From 51d04d1ba8643d8a0c89d8307bf43697223f31e2 Mon Sep 17 00:00:00 2001 From: Petri Lehtinen Date: Sun, 12 Feb 2012 21:05:31 +0200 Subject: Fix sqlite3.Connection.iterdump on tables/fields with reserved names or quotes Closes #9750 --- Lib/sqlite3/dump.py | 50 +++++++++++++++++++++++++++--------------------- Lib/sqlite3/test/dump.py | 8 ++++++++ Misc/ACKS | 1 + Misc/NEWS | 4 ++++ 4 files changed, 41 insertions(+), 22 deletions(-) diff --git a/Lib/sqlite3/dump.py b/Lib/sqlite3/dump.py index 409a405..da6be68 100644 --- a/Lib/sqlite3/dump.py +++ b/Lib/sqlite3/dump.py @@ -1,6 +1,12 @@ # Mimic the sqlite3 console shell's .dump command # Author: Paul Kippes +# Every identifier in sql is quoted based on a comment in sqlite +# documentation "SQLite adds new keywords from time to time when it +# takes on new features. So to prevent your code from being broken by +# future enhancements, you should normally quote any identifier that +# is an English language word, even if you do not have to." + def _iterdump(connection): """ Returns an iterator to the dump of the database in an SQL text format. @@ -15,49 +21,49 @@ def _iterdump(connection): # sqlite_master table contains the SQL CREATE statements for the database. q = """ - SELECT name, type, sql - FROM sqlite_master - WHERE sql NOT NULL AND - type == 'table' + SELECT "name", "type", "sql" + FROM "sqlite_master" + WHERE "sql" NOT NULL AND + "type" == 'table' """ schema_res = cu.execute(q) - for table_name, type, sql in schema_res.fetchall(): + for table_name, type, sql in sorted(schema_res.fetchall()): if table_name == 'sqlite_sequence': - yield('DELETE FROM sqlite_sequence;') + yield('DELETE FROM "sqlite_sequence";') elif table_name == 'sqlite_stat1': - yield('ANALYZE sqlite_master;') + yield('ANALYZE "sqlite_master";') elif table_name.startswith('sqlite_'): continue # NOTE: Virtual table support not implemented #elif sql.startswith('CREATE VIRTUAL TABLE'): # qtable = table_name.replace("'", "''") # yield("INSERT INTO sqlite_master(type,name,tbl_name,rootpage,sql)"\ - # "VALUES('table','%s','%s',0,'%s');" % - # qtable, + # "VALUES('table','{0}','{0}',0,'{1}');".format( # qtable, - # sql.replace("''")) + # sql.replace("''"))) else: - yield('%s;' % sql) + yield('{0};'.format(sql)) # Build the insert statement for each row of the current table - res = cu.execute("PRAGMA table_info('%s')" % table_name) + table_name_ident = table_name.replace('"', '""') + res = cu.execute('PRAGMA table_info("{0}")'.format(table_name_ident)) column_names = [str(table_info[1]) for table_info in res.fetchall()] - q = "SELECT 'INSERT INTO \"%(tbl_name)s\" VALUES(" - q += ",".join(["'||quote(" + col + ")||'" for col in column_names]) - q += ")' FROM '%(tbl_name)s'" - query_res = cu.execute(q % {'tbl_name': table_name}) + q = """SELECT 'INSERT INTO "{0}" VALUES({1})' FROM "{0}";""".format( + table_name_ident, + ",".join("""'||quote("{0}")||'""".format(col.replace('"', '""')) for col in column_names)) + query_res = cu.execute(q) for row in query_res: - yield("%s;" % row[0]) + yield("{0};".format(row[0])) # Now when the type is 'index', 'trigger', or 'view' q = """ - SELECT name, type, sql - FROM sqlite_master - WHERE sql NOT NULL AND - type IN ('index', 'trigger', 'view') + SELECT "name", "type", "sql" + FROM "sqlite_master" + WHERE "sql" NOT NULL AND + "type" IN ('index', 'trigger', 'view') """ schema_res = cu.execute(q) for name, type, sql in schema_res.fetchall(): - yield('%s;' % sql) + yield('{0};'.format(sql)) yield('COMMIT;') diff --git a/Lib/sqlite3/test/dump.py b/Lib/sqlite3/test/dump.py index f40876a..b200333 100644 --- a/Lib/sqlite3/test/dump.py +++ b/Lib/sqlite3/test/dump.py @@ -13,6 +13,14 @@ class DumpTests(unittest.TestCase): def CheckTableDump(self): expected_sqls = [ + """CREATE TABLE "index"("index" blob);""" + , + """INSERT INTO "index" VALUES(X'01');""" + , + """CREATE TABLE "quoted""table"("quoted""field" text);""" + , + """INSERT INTO "quoted""table" VALUES('quoted''value');""" + , "CREATE TABLE t1(id integer primary key, s1 text, " \ "t1_i1 integer not null, i2 integer, unique (s1), " \ "constraint t1_idx1 unique (i2));" diff --git a/Misc/ACKS b/Misc/ACKS index 313aed5..f79eced 100644 --- a/Misc/ACKS +++ b/Misc/ACKS @@ -503,6 +503,7 @@ Lenny Kneler Pat Knight Greg Kochanski Damon Kohler +Marko Kohtala Vlad Korolev Joseph Koshy Maksim Kozyarchuk diff --git a/Misc/NEWS b/Misc/NEWS index 73bdf82..a00331d 100644 --- a/Misc/NEWS +++ b/Misc/NEWS @@ -113,6 +113,10 @@ Core and Builtins Library ------- +- Issue #9750: Fix sqlite3.Connection.iterdump on tables and fields + with a name that is a keyword or contains quotes. Patch by Marko + Kohtala. + - Issue #10287: nntplib now queries the server's CAPABILITIES again after authenticating (since the result may change, according to RFC 4643). Patch by Hynek Schlawack. -- cgit v0.12