Skip to content

Commit 895fe0b

Browse files
[3.13] gh-153658: Fix sqlite3 iterdump() for table names containing a single quote (GH-153659) (#153684)
(cherry picked from commit 2fc3c4d) Co-authored-by: tonghuaroot (童话) <tonghuaroot@gmail.com>
1 parent cd1d07a commit 895fe0b

3 files changed

Lines changed: 29 additions & 4 deletions

File tree

Lib/sqlite3/dump.py

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,12 @@ def _quote_name(name):
1111
return '"{0}"'.format(name.replace('"', '""'))
1212

1313

14+
def _escape_single_quotes(value):
15+
return value.replace("'", "''")
16+
17+
1418
def _quote_value(value):
15-
return "'{0}'".format(value.replace("'", "''"))
19+
return "'{0}'".format(_escape_single_quotes(value))
1620

1721

1822
def _iterdump(connection, *, filter=None):
@@ -80,11 +84,12 @@ def _iterdump(connection, *, filter=None):
8084
table_name_ident = _quote_name(table_name)
8185
res = cu.execute(f'PRAGMA table_info({table_name_ident})')
8286
column_names = [str(table_info[1]) for table_info in res.fetchall()]
83-
q = "SELECT 'INSERT INTO {0} VALUES('{1}')' FROM {0};".format(
84-
table_name_ident,
87+
q = "SELECT 'INSERT INTO {0} VALUES('{1}')' FROM {2};".format(
88+
_escape_single_quotes(table_name_ident),
8589
"','".join(
8690
"||quote({0})||".format(_quote_name(col)) for col in column_names
87-
)
91+
),
92+
table_name_ident,
8893
)
8994
query_res = cu.execute(q)
9095
for row in query_res:

Lib/test/test_sqlite3/test_dump.py

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,24 @@ def test_table_dump(self):
5656
[self.assertEqual(expected_sqls[i], actual_sqls[i])
5757
for i in range(len(expected_sqls))]
5858

59+
def test_dump_single_quote_in_identifier(self):
60+
# A single quote in a table or column name must not break the dump.
61+
self.cu.execute("""CREATE TABLE "a'b" ("c'd" text);""")
62+
self.cu.execute("""INSERT INTO "a'b" VALUES('x''y');""")
63+
expected = [
64+
"BEGIN TRANSACTION;",
65+
"""CREATE TABLE "a'b" ("c'd" text);""",
66+
"""INSERT INTO "a'b" VALUES('x''y');""",
67+
"COMMIT;",
68+
]
69+
actual = list(self.cx.iterdump())
70+
self.assertEqual(expected, actual)
71+
# The dump restores into a fresh database.
72+
with memory_database() as cx2:
73+
cx2.executescript("".join(actual))
74+
row = cx2.execute("""SELECT "c'd" FROM "a'b";""").fetchone()
75+
self.assertEqual(row[0], "x'y")
76+
5977
def test_table_dump_filter(self):
6078
all_table_sqls = [
6179
"""CREATE TABLE "some_table_2" ("id_1" INTEGER);""",
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Fix :meth:`sqlite3.Connection.iterdump` raising :exc:`sqlite3.OperationalError`
2+
when a table name contains a single quote. Patch by tonghuaroot.

0 commit comments

Comments
 (0)