diff --git a/packages/django-google-spanner/django_spanner/schema.py b/packages/django-google-spanner/django_spanner/schema.py index da57122bb73d..905e5843fe51 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("'", "''") + # GoogleSQL string literals use backslash escaping; '' quote + # doubling is not recognized, so escape the backslash first and + # then the quote (matching the db_default/generated inlining 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..0ce848f9afe3 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_escapes_string(self): + """ + String literals must be backslash-escaped for GoogleSQL. A quote or + backslash in the value must not be able to terminate 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="\\'; DROP TABLE t; --"), + "'\\\\\\'; DROP TABLE t; --'", + ) + def test_skip_default(self): """ Tries skipping default as Cloud spanner doesn't support it.