Skip to content

Commit

Permalink
gh-79009: sqlite3.iterdump now correctly handles tables with autoincr…
Browse files Browse the repository at this point in the history
…ement (#9621)

Co-authored-by: Erlend E. Aasland <erlend.aasland@protonmail.com>
  • Loading branch information
itssme and erlend-aasland committed Jun 19, 2022
1 parent 8e08978 commit affa9f2
Show file tree
Hide file tree
Showing 4 changed files with 62 additions and 1 deletion.
14 changes: 13 additions & 1 deletion Lib/sqlite3/dump.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,9 +28,16 @@ def _iterdump(connection):
ORDER BY "name"
"""
schema_res = cu.execute(q)
sqlite_sequence = []
for table_name, type, sql in schema_res.fetchall():
if table_name == 'sqlite_sequence':
yield('DELETE FROM "sqlite_sequence";')
rows = cu.execute('SELECT * FROM "sqlite_sequence";').fetchall()
sqlite_sequence = ['DELETE FROM "sqlite_sequence"']
sqlite_sequence += [
f'INSERT INTO "sqlite_sequence" VALUES(\'{row[0]}\',{row[1]})'
for row in rows
]
continue
elif table_name == 'sqlite_stat1':
yield('ANALYZE "sqlite_master";')
elif table_name.startswith('sqlite_'):
Expand Down Expand Up @@ -67,4 +74,9 @@ def _iterdump(connection):
for name, type, sql in schema_res.fetchall():
yield('{0};'.format(sql))

# gh-79009: Yield statements concerning the sqlite_sequence table at the
# end of the transaction.
for row in sqlite_sequence:
yield('{0};'.format(row))

yield('COMMIT;')
47 changes: 47 additions & 0 deletions Lib/test/test_sqlite3/test_dump.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

import unittest
import sqlite3 as sqlite
from .test_dbapi import memory_database


class DumpTests(unittest.TestCase):
def setUp(self):
Expand Down Expand Up @@ -49,6 +51,51 @@ def test_table_dump(self):
[self.assertEqual(expected_sqls[i], actual_sqls[i])
for i in range(len(expected_sqls))]

def test_dump_autoincrement(self):
expected = [
'CREATE TABLE "t1" (id integer primary key autoincrement);',
'INSERT INTO "t1" VALUES(NULL);',
'CREATE TABLE "t2" (id integer primary key autoincrement);',
]
self.cu.executescript("".join(expected))

# the NULL value should now be automatically be set to 1
expected[1] = expected[1].replace("NULL", "1")
expected.insert(0, "BEGIN TRANSACTION;")
expected.extend([
'DELETE FROM "sqlite_sequence";',
'INSERT INTO "sqlite_sequence" VALUES(\'t1\',1);',
'COMMIT;',
])

actual = [stmt for stmt in self.cx.iterdump()]
self.assertEqual(expected, actual)

def test_dump_autoincrement_create_new_db(self):
self.cu.execute("BEGIN TRANSACTION")
self.cu.execute("CREATE TABLE t1 (id integer primary key autoincrement)")
self.cu.execute("CREATE TABLE t2 (id integer primary key autoincrement)")
self.cu.executemany("INSERT INTO t1 VALUES(?)", ((None,) for _ in range(9)))
self.cu.executemany("INSERT INTO t2 VALUES(?)", ((None,) for _ in range(4)))
self.cx.commit()

with memory_database() as cx2:
query = "".join(self.cx.iterdump())
cx2.executescript(query)
cu2 = cx2.cursor()

dataset = (
("t1", 9),
("t2", 4),
)
for table, seq in dataset:
with self.subTest(table=table, seq=seq):
res = cu2.execute("""
SELECT "seq" FROM "sqlite_sequence" WHERE "name" == ?
""", (table,))
rows = res.fetchall()
self.assertEqual(rows[0][0], seq)

def test_unorderable_row(self):
# iterdump() should be able to cope with unorderable row types (issue #15545)
class UnorderableRow:
Expand Down
1 change: 1 addition & 0 deletions Misc/ACKS
Original file line number Diff line number Diff line change
Expand Up @@ -934,6 +934,7 @@ Ron Klatchko
Reid Kleckner
Carsten Klein
Bastian Kleineidam
Joel Klimont
Bob Kline
Matthias Klose
Jeremy Kloth
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
:meth:`sqlite3.Connection.iterdump` now handles databases that use ``AUTOINCREMENT`` in one or more tables.

0 comments on commit affa9f2

Please sign in to comment.