Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 12 additions & 4 deletions Lib/sqlite3/dump.py
Original file line number Diff line number Diff line change
Expand Up @@ -78,13 +78,21 @@ def _iterdump(connection, *, filter=None):

# Build the insert statement for each row of the current table
table_name_ident = _quote_name(table_name)
# When the identifier (which may contain single quotes) is embedded
# inside a single-quoted SQL string literal below, any single quotes
# must be doubled so the string literal remains valid. Use
# table_name_ident for identifier contexts (PRAGMA, FROM), and
# table_name_literal for the string-literal context.
table_name_literal = table_name_ident.replace("'", "''")
res = cu.execute(f'PRAGMA table_info({table_name_ident})')
column_names = [str(table_info[1]) for table_info in res.fetchall()]
q = "SELECT 'INSERT INTO {0} VALUES('{1}')' FROM {0};".format(
cols = "','".join(
"||quote({0})||".format(_quote_name(col)) for col in column_names
)
q = "SELECT 'INSERT INTO {1} VALUES('{2}')' FROM {0};".format(
table_name_ident,
"','".join(
"||quote({0})||".format(_quote_name(col)) for col in column_names
)
table_name_literal,
cols,
)
query_res = cu.execute(q)
for row in query_res:
Expand Down
22 changes: 22 additions & 0 deletions Lib/test/test_sqlite3/test_dump.py
Original file line number Diff line number Diff line change
Expand Up @@ -228,6 +228,28 @@ def test_dump_virtual_tables(self):
actual = list(self.cx.iterdump())
self.assertEqual(expected, actual)

def test_dump_table_with_single_quote_in_name(self):
# Table names containing single quotes must be round-trippable.
self.cu.execute("CREATE TABLE \"a'b\" (x);")
self.cu.execute("INSERT INTO \"a'b\" VALUES (1);")
dump_sqls = list(self.cx.iterdump())
with memory_database() as cx2:
cx2.executescript("".join(dump_sqls))
cu2 = cx2.cursor()
res = cu2.execute("SELECT * FROM \"a'b\";").fetchall()
self.assertEqual(res, [(1,)])

def test_dump_table_with_double_quotes_in_name(self):
# Regression test: names with double quotes already handled.
self.cu.execute('CREATE TABLE "a""b" (x)')
self.cu.execute('INSERT INTO "a""b" VALUES (2)')
dump_sqls = list(self.cx.iterdump())
with memory_database() as cx2:
cx2.executescript("".join(dump_sqls))
cu2 = cx2.cursor()
res = cu2.execute('SELECT * FROM "a""b";').fetchall()
self.assertEqual(res, [(2,)])


if __name__ == "__main__":
unittest.main()
3 changes: 3 additions & 0 deletions Misc/NEWS.d/153658.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
bpo-153658: sqlite3.Connection.iterdump() now correctly handles table and column names that contain single quotes (apostrophes). Identifiers with apostrophes are properly escaped when embedded in single-quoted SQL string literals, so dumps produced by iterdump() can be replayed with executescript().

Contributed by @joshiaradhya.
Loading