diff --git a/packages/django-google-spanner/django_spanner/schema.py b/packages/django-google-spanner/django_spanner/schema.py index da57122bb73d..031d39e493c2 100644 --- a/packages/django-google-spanner/django_spanner/schema.py +++ b/packages/django-google-spanner/django_spanner/schema.py @@ -450,7 +450,10 @@ def add_index(self, model, index): def quote_value(self, value): # A more complete implementation isn't currently required. if isinstance(value, str): - return "'%s'" % value.replace("'", "''") + # Cloud Spanner (GoogleSQL) escapes quotes with a backslash, not by + # doubling them, and treats backslash as an escape character. Match + # the escaping used for generated/db_default columns above. + return "'%s'" % value.replace("\\", "\\\\").replace("'", "\\'") if isinstance(value, bool): return "TRUE" if value else "FALSE" return str(value) diff --git a/packages/django-google-spanner/tests/unit/django_spanner/test_schema.py b/packages/django-google-spanner/tests/unit/django_spanner/test_schema.py index b7ef7cec39ec..e9b3ea80eed4 100644 --- a/packages/django-google-spanner/tests/unit/django_spanner/test_schema.py +++ b/packages/django-google-spanner/tests/unit/django_spanner/test_schema.py @@ -40,6 +40,19 @@ def test_quote_value(self): schema_editor = DatabaseSchemaEditor(self.connection) self.assertEqual(schema_editor.quote_value(value=1.1), "1.1") + def test_quote_value_str_escaping(self): + """ + String values must be escaped the GoogleSQL way (backslash), so a + quote or backslash in the value can't break out of the literal. + """ + schema_editor = DatabaseSchemaEditor(self.connection) + self.assertEqual(schema_editor.quote_value(value="O'Brien"), "'O\\'Brien'") + self.assertEqual(schema_editor.quote_value(value="a\\b"), "'a\\\\b'") + self.assertEqual( + schema_editor.quote_value(value="\\' OR 1=1 -- "), + "'\\\\\\' OR 1=1 -- '", + ) + def test_skip_default(self): """ Tries skipping default as Cloud spanner doesn't support it.