From c1d90b4605e51346a7ecfc2202ea2c821fae9efd Mon Sep 17 00:00:00 2001 From: dnwpark Date: Fri, 15 Mar 2024 11:54:17 -0400 Subject: [PATCH 01/10] Make escaping strings more consistent. --- edb/edgeql/quote.py | 12 +++++------ tests/edgeql/__init__.py | 26 ++++++++++++++++++++++ tests/edgeql/test_quote.py | 44 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 75 insertions(+), 7 deletions(-) create mode 100644 tests/edgeql/__init__.py create mode 100644 tests/edgeql/test_quote.py diff --git a/edb/edgeql/quote.py b/edb/edgeql/quote.py index 5455cc5dcf9..28390ac53ea 100644 --- a/edb/edgeql/quote.py +++ b/edb/edgeql/quote.py @@ -36,13 +36,11 @@ def escape_string(s: str) -> str: - split = re.split(r"(\n|\\\\|\\')", s) - - if len(split) == 1: - return s.replace(r"'", r"\'") - - return ''.join((r if i % 2 else r.replace(r"'", r"\'")) - for i, r in enumerate(split)) + result = s + result = result.replace('\\', '\\\\') + result = result.replace("'", "\\'") + result = result.replace('"', '\\"') + return result def quote_literal(string: str) -> str: diff --git a/tests/edgeql/__init__.py b/tests/edgeql/__init__.py new file mode 100644 index 00000000000..8de1eeb735c --- /dev/null +++ b/tests/edgeql/__init__.py @@ -0,0 +1,26 @@ +# +# This source file is part of the EdgeDB open source project. +# +# Copyright 2016-present MagicStack Inc. and the EdgeDB authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + + +import unittest + + +def suite(): + test_loader = unittest.TestLoader() + test_suite = test_loader.discover('.', pattern='test_*.py') + return test_suite diff --git a/tests/edgeql/test_quote.py b/tests/edgeql/test_quote.py new file mode 100644 index 00000000000..d7e53e6236f --- /dev/null +++ b/tests/edgeql/test_quote.py @@ -0,0 +1,44 @@ +# +# This source file is part of the EdgeDB open source project. +# +# Copyright 2018-present MagicStack Inc. and the EdgeDB authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +import unittest + +import edb.edgeql.quote as qlquote + +class QuoteTests(unittest.TestCase): + + def test_escape_string(self): + self.assertEqual(qlquote.escape_string(''), '') + self.assertEqual(qlquote.escape_string('abc'), 'abc') + self.assertEqual(qlquote.escape_string('\n'), '\n') + self.assertEqual(qlquote.escape_string('\t'), '\t') + self.assertEqual(qlquote.escape_string('\\'), '\\\\') + self.assertEqual(qlquote.escape_string('\\n'), '\\\\n') + self.assertEqual(qlquote.escape_string('\\t'), '\\\\t') + self.assertEqual(qlquote.escape_string('\\\\'), '\\\\\\\\') + self.assertEqual(qlquote.escape_string('"'), '\\"') + self.assertEqual(qlquote.escape_string("'"), "\\'") + self.assertEqual(qlquote.escape_string('\\"'), '\\\\\\"') + self.assertEqual(qlquote.escape_string("\\'"), "\\\\\\'") + self.assertEqual(qlquote.escape_string( + 'abc"efg\nhij\'klm\\nop"'), + 'abc\\"efg\nhij\\\'klm\\\\nop\\"') + + def test_quote_string(self): + self.assertEqual(qlquote.quote_literal("abc"), "'abc'") + self.assertEqual(qlquote.quote_literal("abc\\\n"), "'abc\\\\\n'") From 9593b23de8e010ef624cf58903148f7c12318bd9 Mon Sep 17 00:00:00 2001 From: dnwpark Date: Fri, 15 Mar 2024 13:32:11 -0400 Subject: [PATCH 02/10] Add unescape_string and fix migration tests. --- edb/common/string.py | 58 +++++++++++++++++++++++++++++ edb/edgeql/quote.py | 9 +---- tests/common/test_string.py | 51 +++++++++++++++++++++++++ tests/edgeql/test_quote.py | 19 +--------- tests/test_edgeql_data_migration.py | 3 +- 5 files changed, 113 insertions(+), 27 deletions(-) create mode 100644 edb/common/string.py create mode 100644 tests/common/test_string.py diff --git a/edb/common/string.py b/edb/common/string.py new file mode 100644 index 00000000000..cb8d2a714bc --- /dev/null +++ b/edb/common/string.py @@ -0,0 +1,58 @@ +# +# This source file is part of the EdgeDB open source project. +# +# Copyright 2009-present MagicStack Inc. and the EdgeDB authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + + +import re + + +def escape_string(s: str) -> str: + # characters escaped according to + # https://www.postgresql.org/docs/current/sql-syntax-lexical.html + # 4.1.2.2 String Constants with C-Style Escapes + result = s + + # escape backslash first + result = result.replace('\\', '\\\\') + + result = result.replace('\'', '\\\'') + result = result.replace('\b', '\\b') + result = result.replace('\f', '\\f') + result = result.replace('\n', '\\n') + result = result.replace('\r', '\\r') + result = result.replace('\t', '\\t') + + return result + + +def unescape_string(s: str) -> str: + split = s.split('\\\\') + + def unescape_non_backslash(s: str) -> str: + result = s + + result = result.replace('\\\'', '\'') + result = result.replace('\\b', '\b') + result = result.replace('\\f', '\f') + result = result.replace('\\n', '\n') + result = result.replace('\\r', '\r') + result = result.replace('\\t', '\t') + + return result + + return '\\'.join(unescape_non_backslash(r) + for r in split) diff --git a/edb/edgeql/quote.py b/edb/edgeql/quote.py index 28390ac53ea..e21d5c69538 100644 --- a/edb/edgeql/quote.py +++ b/edb/edgeql/quote.py @@ -21,6 +21,7 @@ import re +from edb.common.string import escape_string from .parser.grammar import keywords @@ -35,14 +36,6 @@ ''') -def escape_string(s: str) -> str: - result = s - result = result.replace('\\', '\\\\') - result = result.replace("'", "\\'") - result = result.replace('"', '\\"') - return result - - def quote_literal(string: str) -> str: return "'" + escape_string(string) + "'" diff --git a/tests/common/test_string.py b/tests/common/test_string.py new file mode 100644 index 00000000000..4bd429cd692 --- /dev/null +++ b/tests/common/test_string.py @@ -0,0 +1,51 @@ +# +# This source file is part of the EdgeDB open source project. +# +# Copyright 2018-present MagicStack Inc. and the EdgeDB authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +import unittest + +import edb.common.string as string + +class StringTests(unittest.TestCase): + unescaped_escaped_strings = [ + ('', ''), + ('abc', 'abc'), + ('"', '"'), + ('\b', '\\b'), + ('\f', '\\f'), + ('\n', '\\n'), + ('\r', '\\r'), + ('\t', '\\t'), + ('\'', '\\\''), + ('\\', '\\\\'), + ('\\b', '\\\\b'), + ('\\f', '\\\\f'), + ('\\n', '\\\\n'), + ('\\r', '\\\\r'), + ('\\t', '\\\\t'), + ('\\\'', '\\\\\\\''), + ('\\\\', '\\\\\\\\'), + ('abc"efg\nhij\'klm\\nop', 'abc"efg\\nhij\\\'klm\\\\nop'), + ] + + def test_escape_string(self): + for unescaped, escaped in StringTests.unescaped_escaped_strings: + self.assertEqual(string.escape_string(unescaped), escaped) + + def test_unescape_string(self): + for unescaped, escaped in StringTests.unescaped_escaped_strings: + self.assertEqual(string.unescape_string(escaped), unescaped) diff --git a/tests/edgeql/test_quote.py b/tests/edgeql/test_quote.py index d7e53e6236f..0c762c2a962 100644 --- a/tests/edgeql/test_quote.py +++ b/tests/edgeql/test_quote.py @@ -22,23 +22,6 @@ class QuoteTests(unittest.TestCase): - def test_escape_string(self): - self.assertEqual(qlquote.escape_string(''), '') - self.assertEqual(qlquote.escape_string('abc'), 'abc') - self.assertEqual(qlquote.escape_string('\n'), '\n') - self.assertEqual(qlquote.escape_string('\t'), '\t') - self.assertEqual(qlquote.escape_string('\\'), '\\\\') - self.assertEqual(qlquote.escape_string('\\n'), '\\\\n') - self.assertEqual(qlquote.escape_string('\\t'), '\\\\t') - self.assertEqual(qlquote.escape_string('\\\\'), '\\\\\\\\') - self.assertEqual(qlquote.escape_string('"'), '\\"') - self.assertEqual(qlquote.escape_string("'"), "\\'") - self.assertEqual(qlquote.escape_string('\\"'), '\\\\\\"') - self.assertEqual(qlquote.escape_string("\\'"), "\\\\\\'") - self.assertEqual(qlquote.escape_string( - 'abc"efg\nhij\'klm\\nop"'), - 'abc\\"efg\nhij\\\'klm\\\\nop\\"') - def test_quote_string(self): self.assertEqual(qlquote.quote_literal("abc"), "'abc'") - self.assertEqual(qlquote.quote_literal("abc\\\n"), "'abc\\\\\n'") + self.assertEqual(qlquote.quote_literal("abc\\\n"), "'abc\\\\\\n'") diff --git a/tests/test_edgeql_data_migration.py b/tests/test_edgeql_data_migration.py index d67b9995594..58040fadf38 100644 --- a/tests/test_edgeql_data_migration.py +++ b/tests/test_edgeql_data_migration.py @@ -28,6 +28,7 @@ import edgedb from edb.common import assert_data_shape +from edb.common.string import unescape_string from edb.testbase import server as tb from edb.testbase import serutils @@ -140,7 +141,7 @@ async def fast_forward_describe_migration( interpolations[var_name] = var_value for stmt in mig['proposed']['statements']: - curddl = stmt['text'] + curddl = unescape_string(stmt['text']) if interpolations: def _replace(match, interpolations=interpolations): From 459995b8ca705ec8bd071b17d0bd483d0d73670c Mon Sep 17 00:00:00 2001 From: dnwpark Date: Fri, 15 Mar 2024 16:08:34 -0400 Subject: [PATCH 03/10] Fix json when selecting migration description. --- edb/server/compiler/ddl.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/edb/server/compiler/ddl.py b/edb/server/compiler/ddl.py index db63446c909..e36b8a981fa 100644 --- a/edb/server/compiler/ddl.py +++ b/edb/server/compiler/ddl.py @@ -725,8 +725,12 @@ def _describe_current_migration( .decode('utf-8') ) + # json dumps already produces escaped json text + # need to additionally escape single quotes + desc = "'{}'".format(desc.replace("'", "\\'")) + desc_ql = edgeql.parse_query( - f'SELECT to_json({qlquote.quote_literal(desc)})' + f'SELECT to_json({desc})' ) return compiler._compile_ql_query( ctx, From 717749cb40d183f1cfb0d48f46558b396ca1848c Mon Sep 17 00:00:00 2001 From: dnwpark Date: Fri, 15 Mar 2024 16:29:47 -0400 Subject: [PATCH 04/10] Fix formatting. --- edb/common/string.py | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/edb/common/string.py b/edb/common/string.py index cb8d2a714bc..bf7a46658a6 100644 --- a/edb/common/string.py +++ b/edb/common/string.py @@ -17,13 +17,10 @@ # -import re - - def escape_string(s: str) -> str: # characters escaped according to # https://www.postgresql.org/docs/current/sql-syntax-lexical.html - # 4.1.2.2 String Constants with C-Style Escapes + # 4.1.2.2 String Constants with C-Style Escapes result = s # escape backslash first From 949e5d4e43b8d8df767df22360334289c7a3e0ce Mon Sep 17 00:00:00 2001 From: dnwpark Date: Fri, 15 Mar 2024 17:56:32 -0400 Subject: [PATCH 05/10] Remove python string escaping json description. --- edb/server/compiler/ddl.py | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/edb/server/compiler/ddl.py b/edb/server/compiler/ddl.py index e36b8a981fa..f41cd50c09f 100644 --- a/edb/server/compiler/ddl.py +++ b/edb/server/compiler/ddl.py @@ -721,16 +721,10 @@ def _describe_current_migration( **extra, } ) - .encode('unicode_escape') - .decode('utf-8') ) - # json dumps already produces escaped json text - # need to additionally escape single quotes - desc = "'{}'".format(desc.replace("'", "\\'")) - desc_ql = edgeql.parse_query( - f'SELECT to_json({desc})' + f'SELECT to_json({qlquote.quote_literal(desc)})' ) return compiler._compile_ql_query( ctx, From 76e7cb6478a3c805b0a169de435c719c49ef9577 Mon Sep 17 00:00:00 2001 From: dnwpark Date: Fri, 15 Mar 2024 18:03:11 -0400 Subject: [PATCH 06/10] Add escape for double quote. --- edb/common/string.py | 2 ++ tests/common/test_string.py | 5 +++-- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/edb/common/string.py b/edb/common/string.py index bf7a46658a6..b8e5a707624 100644 --- a/edb/common/string.py +++ b/edb/common/string.py @@ -27,6 +27,7 @@ def escape_string(s: str) -> str: result = result.replace('\\', '\\\\') result = result.replace('\'', '\\\'') + result = result.replace('\"', '\\\"') result = result.replace('\b', '\\b') result = result.replace('\f', '\\f') result = result.replace('\n', '\\n') @@ -43,6 +44,7 @@ def unescape_non_backslash(s: str) -> str: result = s result = result.replace('\\\'', '\'') + result = result.replace('\\\"', '\"') result = result.replace('\\b', '\b') result = result.replace('\\f', '\f') result = result.replace('\\n', '\n') diff --git a/tests/common/test_string.py b/tests/common/test_string.py index 4bd429cd692..2c2627a680f 100644 --- a/tests/common/test_string.py +++ b/tests/common/test_string.py @@ -24,13 +24,13 @@ class StringTests(unittest.TestCase): unescaped_escaped_strings = [ ('', ''), ('abc', 'abc'), - ('"', '"'), ('\b', '\\b'), ('\f', '\\f'), ('\n', '\\n'), ('\r', '\\r'), ('\t', '\\t'), ('\'', '\\\''), + ('\"', '\\\"'), ('\\', '\\\\'), ('\\b', '\\\\b'), ('\\f', '\\\\f'), @@ -38,8 +38,9 @@ class StringTests(unittest.TestCase): ('\\r', '\\\\r'), ('\\t', '\\\\t'), ('\\\'', '\\\\\\\''), + ('\\\"', '\\\\\\\"'), ('\\\\', '\\\\\\\\'), - ('abc"efg\nhij\'klm\\nop', 'abc"efg\\nhij\\\'klm\\\\nop'), + ('abc"efg\nhij\'klm\\nop', 'abc\\"efg\\nhij\\\'klm\\\\nop'), ] def test_escape_string(self): From 251d3c95ba1c7a02f0d221cff72a0ddd404aec56 Mon Sep 17 00:00:00 2001 From: dnwpark Date: Fri, 15 Mar 2024 18:16:24 -0400 Subject: [PATCH 07/10] Remove unescape_string. --- edb/common/string.py | 20 -------------------- tests/common/test_string.py | 4 ---- tests/test_edgeql_data_migration.py | 3 +-- 3 files changed, 1 insertion(+), 26 deletions(-) diff --git a/edb/common/string.py b/edb/common/string.py index b8e5a707624..6cb56cdb134 100644 --- a/edb/common/string.py +++ b/edb/common/string.py @@ -35,23 +35,3 @@ def escape_string(s: str) -> str: result = result.replace('\t', '\\t') return result - - -def unescape_string(s: str) -> str: - split = s.split('\\\\') - - def unescape_non_backslash(s: str) -> str: - result = s - - result = result.replace('\\\'', '\'') - result = result.replace('\\\"', '\"') - result = result.replace('\\b', '\b') - result = result.replace('\\f', '\f') - result = result.replace('\\n', '\n') - result = result.replace('\\r', '\r') - result = result.replace('\\t', '\t') - - return result - - return '\\'.join(unescape_non_backslash(r) - for r in split) diff --git a/tests/common/test_string.py b/tests/common/test_string.py index 2c2627a680f..df8986630fe 100644 --- a/tests/common/test_string.py +++ b/tests/common/test_string.py @@ -46,7 +46,3 @@ class StringTests(unittest.TestCase): def test_escape_string(self): for unescaped, escaped in StringTests.unescaped_escaped_strings: self.assertEqual(string.escape_string(unescaped), escaped) - - def test_unescape_string(self): - for unescaped, escaped in StringTests.unescaped_escaped_strings: - self.assertEqual(string.unescape_string(escaped), unescaped) diff --git a/tests/test_edgeql_data_migration.py b/tests/test_edgeql_data_migration.py index 58040fadf38..d67b9995594 100644 --- a/tests/test_edgeql_data_migration.py +++ b/tests/test_edgeql_data_migration.py @@ -28,7 +28,6 @@ import edgedb from edb.common import assert_data_shape -from edb.common.string import unescape_string from edb.testbase import server as tb from edb.testbase import serutils @@ -141,7 +140,7 @@ async def fast_forward_describe_migration( interpolations[var_name] = var_value for stmt in mig['proposed']['statements']: - curddl = unescape_string(stmt['text']) + curddl = stmt['text'] if interpolations: def _replace(match, interpolations=interpolations): From 714fd4d90b07684fc7ac10d0daf6b11296b93a5b Mon Sep 17 00:00:00 2001 From: dnwpark Date: Fri, 15 Mar 2024 18:57:54 -0400 Subject: [PATCH 08/10] Remove separate escape_string function. --- edb/common/string.py | 37 ---------------------------- edb/edgeql/quote.py | 20 +++++++++++++++- tests/common/test_string.py | 48 ------------------------------------- tests/edgeql/test_quote.py | 21 +++++++++++++++- 4 files changed, 39 insertions(+), 87 deletions(-) delete mode 100644 edb/common/string.py delete mode 100644 tests/common/test_string.py diff --git a/edb/common/string.py b/edb/common/string.py deleted file mode 100644 index 6cb56cdb134..00000000000 --- a/edb/common/string.py +++ /dev/null @@ -1,37 +0,0 @@ -# -# This source file is part of the EdgeDB open source project. -# -# Copyright 2009-present MagicStack Inc. and the EdgeDB authors. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# - - -def escape_string(s: str) -> str: - # characters escaped according to - # https://www.postgresql.org/docs/current/sql-syntax-lexical.html - # 4.1.2.2 String Constants with C-Style Escapes - result = s - - # escape backslash first - result = result.replace('\\', '\\\\') - - result = result.replace('\'', '\\\'') - result = result.replace('\"', '\\\"') - result = result.replace('\b', '\\b') - result = result.replace('\f', '\\f') - result = result.replace('\n', '\\n') - result = result.replace('\r', '\\r') - result = result.replace('\t', '\\t') - - return result diff --git a/edb/edgeql/quote.py b/edb/edgeql/quote.py index e21d5c69538..91795fd5e79 100644 --- a/edb/edgeql/quote.py +++ b/edb/edgeql/quote.py @@ -21,7 +21,6 @@ import re -from edb.common.string import escape_string from .parser.grammar import keywords @@ -37,6 +36,25 @@ def quote_literal(string: str) -> str: + + def escape_string(s: str) -> str: + # characters escaped according to + # https://www.edgedb.com/docs/reference/edgeql/lexical#strings + result = s + + # escape backslash first + result = result.replace('\\', '\\\\') + + result = result.replace('\'', '\\\'') + result = result.replace('\"', '\\\"') + result = result.replace('\b', '\\b') + result = result.replace('\f', '\\f') + result = result.replace('\n', '\\n') + result = result.replace('\r', '\\r') + result = result.replace('\t', '\\t') + + return result + return "'" + escape_string(string) + "'" diff --git a/tests/common/test_string.py b/tests/common/test_string.py deleted file mode 100644 index df8986630fe..00000000000 --- a/tests/common/test_string.py +++ /dev/null @@ -1,48 +0,0 @@ -# -# This source file is part of the EdgeDB open source project. -# -# Copyright 2018-present MagicStack Inc. and the EdgeDB authors. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# - -import unittest - -import edb.common.string as string - -class StringTests(unittest.TestCase): - unescaped_escaped_strings = [ - ('', ''), - ('abc', 'abc'), - ('\b', '\\b'), - ('\f', '\\f'), - ('\n', '\\n'), - ('\r', '\\r'), - ('\t', '\\t'), - ('\'', '\\\''), - ('\"', '\\\"'), - ('\\', '\\\\'), - ('\\b', '\\\\b'), - ('\\f', '\\\\f'), - ('\\n', '\\\\n'), - ('\\r', '\\\\r'), - ('\\t', '\\\\t'), - ('\\\'', '\\\\\\\''), - ('\\\"', '\\\\\\\"'), - ('\\\\', '\\\\\\\\'), - ('abc"efg\nhij\'klm\\nop', 'abc\\"efg\\nhij\\\'klm\\\\nop'), - ] - - def test_escape_string(self): - for unescaped, escaped in StringTests.unescaped_escaped_strings: - self.assertEqual(string.escape_string(unescaped), escaped) diff --git a/tests/edgeql/test_quote.py b/tests/edgeql/test_quote.py index 0c762c2a962..13306328855 100644 --- a/tests/edgeql/test_quote.py +++ b/tests/edgeql/test_quote.py @@ -23,5 +23,24 @@ class QuoteTests(unittest.TestCase): def test_quote_string(self): + self.assertEqual(qlquote.quote_literal(""), "''"), self.assertEqual(qlquote.quote_literal("abc"), "'abc'") - self.assertEqual(qlquote.quote_literal("abc\\\n"), "'abc\\\\\\n'") + self.assertEqual(qlquote.quote_literal("\b"), "'\\b'") + self.assertEqual(qlquote.quote_literal("\f"), "'\\f'") + self.assertEqual(qlquote.quote_literal("\n"), "'\\n'") + self.assertEqual(qlquote.quote_literal("\r"), "'\\r'") + self.assertEqual(qlquote.quote_literal("\t"), "'\\t'") + self.assertEqual(qlquote.quote_literal("\'"), "'\\\''") + self.assertEqual(qlquote.quote_literal("\""), "'\\\"'") + self.assertEqual(qlquote.quote_literal("\\"), "'\\\\'") + self.assertEqual(qlquote.quote_literal("\\b"), "'\\\\b'") + self.assertEqual(qlquote.quote_literal("\\f"), "'\\\\f'") + self.assertEqual(qlquote.quote_literal("\\n"), "'\\\\n'") + self.assertEqual(qlquote.quote_literal("\\r"), "'\\\\r'") + self.assertEqual(qlquote.quote_literal("\\t"), "'\\\\t'") + self.assertEqual(qlquote.quote_literal("\\\'"), "'\\\\\\\''") + self.assertEqual(qlquote.quote_literal("\\\""), "'\\\\\\\"'") + self.assertEqual(qlquote.quote_literal("\\\\"), "'\\\\\\\\'") + self.assertEqual(qlquote.quote_literal( + "abc\"efg\nhij\'klm\\nop"), + "'abc\\\"efg\\nhij\\\'klm\\\\nop'") From b79c7b2a9ec32a7a9bd8054fa2c5ec5d915289f7 Mon Sep 17 00:00:00 2001 From: dnwpark Date: Tue, 19 Mar 2024 14:23:21 -0400 Subject: [PATCH 09/10] Remove double quote escape. --- edb/edgeql/quote.py | 1 - 1 file changed, 1 deletion(-) diff --git a/edb/edgeql/quote.py b/edb/edgeql/quote.py index 91795fd5e79..2859fa64d32 100644 --- a/edb/edgeql/quote.py +++ b/edb/edgeql/quote.py @@ -46,7 +46,6 @@ def escape_string(s: str) -> str: result = result.replace('\\', '\\\\') result = result.replace('\'', '\\\'') - result = result.replace('\"', '\\\"') result = result.replace('\b', '\\b') result = result.replace('\f', '\\f') result = result.replace('\n', '\\n') From a10f2282befdeafd32a5efe9e14a1ef2d9907233 Mon Sep 17 00:00:00 2001 From: dnwpark Date: Wed, 20 Mar 2024 13:29:59 -0400 Subject: [PATCH 10/10] Fix test. --- tests/edgeql/test_quote.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/tests/edgeql/test_quote.py b/tests/edgeql/test_quote.py index 13306328855..a8924972aa4 100644 --- a/tests/edgeql/test_quote.py +++ b/tests/edgeql/test_quote.py @@ -25,13 +25,13 @@ class QuoteTests(unittest.TestCase): def test_quote_string(self): self.assertEqual(qlquote.quote_literal(""), "''"), self.assertEqual(qlquote.quote_literal("abc"), "'abc'") + self.assertEqual(qlquote.quote_literal("\""), "'\"'") self.assertEqual(qlquote.quote_literal("\b"), "'\\b'") self.assertEqual(qlquote.quote_literal("\f"), "'\\f'") self.assertEqual(qlquote.quote_literal("\n"), "'\\n'") self.assertEqual(qlquote.quote_literal("\r"), "'\\r'") self.assertEqual(qlquote.quote_literal("\t"), "'\\t'") self.assertEqual(qlquote.quote_literal("\'"), "'\\\''") - self.assertEqual(qlquote.quote_literal("\""), "'\\\"'") self.assertEqual(qlquote.quote_literal("\\"), "'\\\\'") self.assertEqual(qlquote.quote_literal("\\b"), "'\\\\b'") self.assertEqual(qlquote.quote_literal("\\f"), "'\\\\f'") @@ -39,8 +39,7 @@ def test_quote_string(self): self.assertEqual(qlquote.quote_literal("\\r"), "'\\\\r'") self.assertEqual(qlquote.quote_literal("\\t"), "'\\\\t'") self.assertEqual(qlquote.quote_literal("\\\'"), "'\\\\\\\''") - self.assertEqual(qlquote.quote_literal("\\\""), "'\\\\\\\"'") self.assertEqual(qlquote.quote_literal("\\\\"), "'\\\\\\\\'") self.assertEqual(qlquote.quote_literal( "abc\"efg\nhij\'klm\\nop"), - "'abc\\\"efg\\nhij\\\'klm\\\\nop'") + "'abc\"efg\\nhij\\\'klm\\\\nop'")