From aaed6e04eceef98e9ec433e86b80486ada2a668b Mon Sep 17 00:00:00 2001 From: Adrian Holovaty Date: Mon, 20 Aug 2007 00:15:53 +0000 Subject: [PATCH] Refactored get_sql_flush() to DatabaseOperations.sql_flush(). Refs #5106 git-svn-id: http://code.djangoproject.com/svn/django/trunk@5963 bcc190cf-cafb-0310-a4f2-bffc1f526a37 --- django/core/management/sql.py | 6 +- django/db/backends/__init__.py | 11 ++ django/db/backends/ado_mssql/base.py | 13 --- django/db/backends/dummy/base.py | 1 - django/db/backends/mysql/base.py | 53 ++++----- django/db/backends/mysql_old/base.py | 53 ++++----- django/db/backends/oracle/base.py | 49 ++++---- django/db/backends/postgresql/base.py | 107 +++++++++--------- .../db/backends/postgresql_psycopg2/base.py | 103 +++++++++-------- django/db/backends/sqlite3/base.py | 31 +++-- 10 files changed, 197 insertions(+), 230 deletions(-) diff --git a/django/core/management/sql.py b/django/core/management/sql.py index 09713d278d..f053ec7082 100644 --- a/django/core/management/sql.py +++ b/django/core/management/sql.py @@ -178,9 +178,9 @@ def sql_reset(app, style): return sql_delete(app, style) + sql_all(app, style) def sql_flush(style): - "Returns a list of the SQL statements used to flush the database" - from django.db import backend - statements = backend.get_sql_flush(style, table_list(), sequence_list()) + "Returns a list of the SQL statements used to flush the database." + from django.db import connection + statements = connection.ops.sql_flush(style, table_list(), sequence_list()) return statements def sql_custom(app): diff --git a/django/db/backends/__init__.py b/django/db/backends/__init__.py index 63114db67d..e1122344ec 100644 --- a/django/db/backends/__init__.py +++ b/django/db/backends/__init__.py @@ -139,3 +139,14 @@ class BaseDatabaseOperations(object): Returns a SQL expression that returns a random value. """ return 'RANDOM()' + + def sql_flush(self, style, tables, sequences): + """ + Returns a list of SQL statements required to remove all data from + the given database tables (without actually removing the tables + themselves). + + The `style` argument is a Style object as returned by either + color_style() or no_style() in django.core.management.color. + """ + raise NotImplementedError() diff --git a/django/db/backends/ado_mssql/base.py b/django/db/backends/ado_mssql/base.py index 51ea566218..f514905528 100644 --- a/django/db/backends/ado_mssql/base.py +++ b/django/db/backends/ado_mssql/base.py @@ -109,19 +109,6 @@ def get_start_transaction_sql(): def get_tablespace_sql(tablespace, inline=False): return "ON %s" % quote_name(tablespace) -def get_sql_flush(style, tables, sequences): - """Return a list of SQL statements required to remove all data from - all tables in the database (without actually removing the tables - themselves) and put the database in an empty 'initial' state - """ - # Return a list of 'TRUNCATE x;', 'TRUNCATE y;', 'TRUNCATE z;'... style SQL statements - # TODO - SQL not actually tested against ADO MSSQL yet! - # TODO - autoincrement indices reset required? See other get_sql_flush() implementations - sql_list = ['%s %s;' % \ - (style.SQL_KEYWORD('TRUNCATE'), - style.SQL_FIELD(quote_name(table)) - ) for table in tables] - def get_sql_sequence_reset(style, model_list): "Returns a list of the SQL statements to reset sequences for the given models." # No sequence reset required diff --git a/django/db/backends/dummy/base.py b/django/db/backends/dummy/base.py index f367ebe1aa..19534423ca 100644 --- a/django/db/backends/dummy/base.py +++ b/django/db/backends/dummy/base.py @@ -44,7 +44,6 @@ dictfetchone = complain dictfetchmany = complain dictfetchall = complain get_start_transaction_sql = complain -get_sql_flush = complain get_sql_sequence_reset = complain OPERATOR_MAPPING = {} diff --git a/django/db/backends/mysql/base.py b/django/db/backends/mysql/base.py index 4b4d4eb44a..e09313c585 100644 --- a/django/db/backends/mysql/base.py +++ b/django/db/backends/mysql/base.py @@ -87,6 +87,29 @@ class DatabaseOperations(BaseDatabaseOperations): def random_function_sql(self): return 'RAND()' + def sql_flush(self, style, tables, sequences): + # NB: The generated SQL below is specific to MySQL + # 'TRUNCATE x;', 'TRUNCATE y;', 'TRUNCATE z;'... style SQL statements + # to clear all tables of all data + if tables: + sql = ['SET FOREIGN_KEY_CHECKS = 0;'] + for table in tables: + sql.append('%s %s;' % (style.SQL_KEYWORD('TRUNCATE'), style.SQL_FIELD(quote_name(table)))) + sql.append('SET FOREIGN_KEY_CHECKS = 1;') + + # 'ALTER TABLE table AUTO_INCREMENT = 1;'... style SQL statements + # to reset sequence indices + sql.extend(["%s %s %s %s %s;" % \ + (style.SQL_KEYWORD('ALTER'), + style.SQL_KEYWORD('TABLE'), + style.SQL_TABLE(quote_name(sequence['table'])), + style.SQL_KEYWORD('AUTO_INCREMENT'), + style.SQL_FIELD('= 1'), + ) for sequence in sequences]) + return sql + else: + return [] + class DatabaseWrapper(BaseDatabaseWrapper): ops = DatabaseOperations() @@ -168,36 +191,6 @@ dictfetchall = util.dictfetchall def get_start_transaction_sql(): return "BEGIN;" -def get_sql_flush(style, tables, sequences): - """Return a list of SQL statements required to remove all data from - all tables in the database (without actually removing the tables - themselves) and put the database in an empty 'initial' state - - """ - # NB: The generated SQL below is specific to MySQL - # 'TRUNCATE x;', 'TRUNCATE y;', 'TRUNCATE z;'... style SQL statements - # to clear all tables of all data - if tables: - sql = ['SET FOREIGN_KEY_CHECKS = 0;'] + \ - ['%s %s;' % \ - (style.SQL_KEYWORD('TRUNCATE'), - style.SQL_FIELD(quote_name(table)) - ) for table in tables] + \ - ['SET FOREIGN_KEY_CHECKS = 1;'] - - # 'ALTER TABLE table AUTO_INCREMENT = 1;'... style SQL statements - # to reset sequence indices - sql.extend(["%s %s %s %s %s;" % \ - (style.SQL_KEYWORD('ALTER'), - style.SQL_KEYWORD('TABLE'), - style.SQL_TABLE(quote_name(sequence['table'])), - style.SQL_KEYWORD('AUTO_INCREMENT'), - style.SQL_FIELD('= 1'), - ) for sequence in sequences]) - return sql - else: - return [] - def get_sql_sequence_reset(style, model_list): "Returns a list of the SQL statements to reset sequences for the given models." # No sequence reset required diff --git a/django/db/backends/mysql_old/base.py b/django/db/backends/mysql_old/base.py index 143572a598..38533d5aa0 100644 --- a/django/db/backends/mysql_old/base.py +++ b/django/db/backends/mysql_old/base.py @@ -97,6 +97,29 @@ class DatabaseOperations(BaseDatabaseOperations): def random_function_sql(self): return 'RAND()' + def sql_flush(self, style, tables, sequences): + # NB: The generated SQL below is specific to MySQL + # 'TRUNCATE x;', 'TRUNCATE y;', 'TRUNCATE z;'... style SQL statements + # to clear all tables of all data + if tables: + sql = ['SET FOREIGN_KEY_CHECKS = 0;'] + for table in tables: + sql.append('%s %s;' % (style.SQL_KEYWORD('TRUNCATE'), style.SQL_FIELD(quote_name(table)))) + sql.append('SET FOREIGN_KEY_CHECKS = 1;') + + # 'ALTER TABLE table AUTO_INCREMENT = 1;'... style SQL statements + # to reset sequence indices + sql.extend(["%s %s %s %s %s;" % \ + (style.SQL_KEYWORD('ALTER'), + style.SQL_KEYWORD('TABLE'), + style.SQL_TABLE(quote_name(sequence['table'])), + style.SQL_KEYWORD('AUTO_INCREMENT'), + style.SQL_FIELD('= 1'), + ) for sequence in sequences]) + return sql + else: + return [] + class DatabaseWrapper(BaseDatabaseWrapper): ops = DatabaseOperations() @@ -187,36 +210,6 @@ dictfetchall = util.dictfetchall def get_start_transaction_sql(): return "BEGIN;" -def get_sql_flush(style, tables, sequences): - """Return a list of SQL statements required to remove all data from - all tables in the database (without actually removing the tables - themselves) and put the database in an empty 'initial' state - - """ - # NB: The generated SQL below is specific to MySQL - # 'TRUNCATE x;', 'TRUNCATE y;', 'TRUNCATE z;'... style SQL statements - # to clear all tables of all data - if tables: - sql = ['SET FOREIGN_KEY_CHECKS = 0;'] + \ - ['%s %s;' % \ - (style.SQL_KEYWORD('TRUNCATE'), - style.SQL_FIELD(quote_name(table)) - ) for table in tables] + \ - ['SET FOREIGN_KEY_CHECKS = 1;'] - - # 'ALTER TABLE table AUTO_INCREMENT = 1;'... style SQL statements - # to reset sequence indices - sql.extend(["%s %s %s %s %s;" % \ - (style.SQL_KEYWORD('ALTER'), - style.SQL_KEYWORD('TABLE'), - style.SQL_TABLE(quote_name(sequence['table'])), - style.SQL_KEYWORD('AUTO_INCREMENT'), - style.SQL_FIELD('= 1'), - ) for sequence in sequences]) - return sql - else: - return [] - def get_sql_sequence_reset(style, model_list): "Returns a list of the SQL statements to reset sequences for the given models." # No sequence reset required diff --git a/django/db/backends/oracle/base.py b/django/db/backends/oracle/base.py index a633e8e28f..9fb79bf949 100644 --- a/django/db/backends/oracle/base.py +++ b/django/db/backends/oracle/base.py @@ -73,6 +73,28 @@ class DatabaseOperations(BaseDatabaseOperations): def random_function_sql(self): return "DBMS_RANDOM.RANDOM" + def sql_flush(self, style, tables, sequences): + # Return a list of 'TRUNCATE x;', 'TRUNCATE y;', + # 'TRUNCATE z;'... style SQL statements + if tables: + # Oracle does support TRUNCATE, but it seems to get us into + # FK referential trouble, whereas DELETE FROM table works. + sql = ['%s %s %s;' % \ + (style.SQL_KEYWORD('DELETE'), + style.SQL_KEYWORD('FROM'), + style.SQL_FIELD(quote_name(table)) + ) for table in tables] + # Since we've just deleted all the rows, running our sequence + # ALTER code will reset the sequence to 0. + for sequence_info in sequences: + table_name = sequence_info['table'] + seq_name = get_sequence_name(table_name) + query = _get_sequence_reset_sql() % {'sequence':seq_name, 'table':quote_name(table_name)} + sql.append(query) + return sql + else: + return [] + class DatabaseWrapper(BaseDatabaseWrapper): ops = DatabaseOperations() @@ -218,33 +240,6 @@ def _get_sequence_reset_sql(): END; /""" -def get_sql_flush(style, tables, sequences): - """Return a list of SQL statements required to remove all data from - all tables in the database (without actually removing the tables - themselves) and put the database in an empty 'initial' state - """ - # Return a list of 'TRUNCATE x;', 'TRUNCATE y;', - # 'TRUNCATE z;'... style SQL statements - if tables: - # Oracle does support TRUNCATE, but it seems to get us into - # FK referential trouble, whereas DELETE FROM table works. - sql = ['%s %s %s;' % \ - (style.SQL_KEYWORD('DELETE'), - style.SQL_KEYWORD('FROM'), - style.SQL_FIELD(quote_name(table)) - ) for table in tables] - # Since we've just deleted all the rows, running our sequence - # ALTER code will reset the sequence to 0. - for sequence_info in sequences: - table_name = sequence_info['table'] - seq_name = get_sequence_name(table_name) - query = _get_sequence_reset_sql() % {'sequence':seq_name, - 'table':quote_name(table_name)} - sql.append(query) - return sql - else: - return [] - def get_sequence_name(table): name_length = DatabaseOperations().max_name_length() - 3 return '%s_SQ' % util.truncate_name(table, name_length).upper() diff --git a/django/db/backends/postgresql/base.py b/django/db/backends/postgresql/base.py index 4c5a278daf..d3ad270aac 100644 --- a/django/db/backends/postgresql/base.py +++ b/django/db/backends/postgresql/base.py @@ -73,6 +73,57 @@ class DatabaseOperations(BaseDatabaseOperations): cursor.execute("SELECT CURRVAL('\"%s_%s_seq\"')" % (table_name, pk_name)) return cursor.fetchone()[0] + def sql_flush(self, style, tables, sequences): + if tables: + if postgres_version[0] >= 8 and postgres_version[1] >= 1: + # Postgres 8.1+ can do 'TRUNCATE x, y, z...;'. In fact, it *has to* + # in order to be able to truncate tables referenced by a foreign + # key in any other table. The result is a single SQL TRUNCATE + # statement. + sql = ['%s %s;' % \ + (style.SQL_KEYWORD('TRUNCATE'), + style.SQL_FIELD(', '.join([quote_name(table) for table in tables])) + )] + else: + # Older versions of Postgres can't do TRUNCATE in a single call, so + # they must use a simple delete. + sql = ['%s %s %s;' % \ + (style.SQL_KEYWORD('DELETE'), + style.SQL_KEYWORD('FROM'), + style.SQL_FIELD(quote_name(table)) + ) for table in tables] + + # 'ALTER SEQUENCE sequence_name RESTART WITH 1;'... style SQL statements + # to reset sequence indices + for sequence_info in sequences: + table_name = sequence_info['table'] + column_name = sequence_info['column'] + if column_name and len(column_name)>0: + # sequence name in this case will be __seq + sql.append("%s %s %s %s %s %s;" % \ + (style.SQL_KEYWORD('ALTER'), + style.SQL_KEYWORD('SEQUENCE'), + style.SQL_FIELD(quote_name('%s_%s_seq' % (table_name, column_name))), + style.SQL_KEYWORD('RESTART'), + style.SQL_KEYWORD('WITH'), + style.SQL_FIELD('1') + ) + ) + else: + # sequence name in this case will be
_id_seq + sql.append("%s %s %s %s %s %s;" % \ + (style.SQL_KEYWORD('ALTER'), + style.SQL_KEYWORD('SEQUENCE'), + style.SQL_FIELD(quote_name('%s_id_seq' % table_name)), + style.SQL_KEYWORD('RESTART'), + style.SQL_KEYWORD('WITH'), + style.SQL_FIELD('1') + ) + ) + return sql + else: + return [] + class DatabaseWrapper(BaseDatabaseWrapper): ops = DatabaseOperations() @@ -134,62 +185,6 @@ def dictfetchall(cursor): def get_start_transaction_sql(): return "BEGIN;" -def get_sql_flush(style, tables, sequences): - """Return a list of SQL statements required to remove all data from - all tables in the database (without actually removing the tables - themselves) and put the database in an empty 'initial' state - - """ - if tables: - if postgres_version[0] >= 8 and postgres_version[1] >= 1: - # Postgres 8.1+ can do 'TRUNCATE x, y, z...;'. In fact, it *has to* - # in order to be able to truncate tables referenced by a foreign - # key in any other table. The result is a single SQL TRUNCATE - # statement. - sql = ['%s %s;' % \ - (style.SQL_KEYWORD('TRUNCATE'), - style.SQL_FIELD(', '.join([quote_name(table) for table in tables])) - )] - else: - # Older versions of Postgres can't do TRUNCATE in a single call, so - # they must use a simple delete. - sql = ['%s %s %s;' % \ - (style.SQL_KEYWORD('DELETE'), - style.SQL_KEYWORD('FROM'), - style.SQL_FIELD(quote_name(table)) - ) for table in tables] - - # 'ALTER SEQUENCE sequence_name RESTART WITH 1;'... style SQL statements - # to reset sequence indices - for sequence_info in sequences: - table_name = sequence_info['table'] - column_name = sequence_info['column'] - if column_name and len(column_name)>0: - # sequence name in this case will be
__seq - sql.append("%s %s %s %s %s %s;" % \ - (style.SQL_KEYWORD('ALTER'), - style.SQL_KEYWORD('SEQUENCE'), - style.SQL_FIELD(quote_name('%s_%s_seq' % (table_name, column_name))), - style.SQL_KEYWORD('RESTART'), - style.SQL_KEYWORD('WITH'), - style.SQL_FIELD('1') - ) - ) - else: - # sequence name in this case will be
_id_seq - sql.append("%s %s %s %s %s %s;" % \ - (style.SQL_KEYWORD('ALTER'), - style.SQL_KEYWORD('SEQUENCE'), - style.SQL_FIELD(quote_name('%s_id_seq' % table_name)), - style.SQL_KEYWORD('RESTART'), - style.SQL_KEYWORD('WITH'), - style.SQL_FIELD('1') - ) - ) - return sql - else: - return [] - def get_sql_sequence_reset(style, model_list): "Returns a list of the SQL statements to reset sequences for the given models." from django.db import models diff --git a/django/db/backends/postgresql_psycopg2/base.py b/django/db/backends/postgresql_psycopg2/base.py index 44b80ac820..17df019a01 100644 --- a/django/db/backends/postgresql_psycopg2/base.py +++ b/django/db/backends/postgresql_psycopg2/base.py @@ -35,6 +35,57 @@ class DatabaseOperations(BaseDatabaseOperations): cursor.execute("SELECT CURRVAL('\"%s_%s_seq\"')" % (table_name, pk_name)) return cursor.fetchone()[0] + def sql_flush(self, style, tables, sequences): + if tables: + if postgres_version[0] >= 8 and postgres_version[1] >= 1: + # Postgres 8.1+ can do 'TRUNCATE x, y, z...;'. In fact, it *has to* + # in order to be able to truncate tables referenced by a foreign + # key in any other table. The result is a single SQL TRUNCATE + # statement. + sql = ['%s %s;' % \ + (style.SQL_KEYWORD('TRUNCATE'), + style.SQL_FIELD(', '.join([quote_name(table) for table in tables])) + )] + else: + # Older versions of Postgres can't do TRUNCATE in a single call, so + # they must use a simple delete. + sql = ['%s %s %s;' % \ + (style.SQL_KEYWORD('DELETE'), + style.SQL_KEYWORD('FROM'), + style.SQL_FIELD(quote_name(table)) + ) for table in tables] + + # 'ALTER SEQUENCE sequence_name RESTART WITH 1;'... style SQL statements + # to reset sequence indices + for sequence_info in sequences: + table_name = sequence_info['table'] + column_name = sequence_info['column'] + if column_name and len(column_name)>0: + # sequence name in this case will be
__seq + sql.append("%s %s %s %s %s %s;" % \ + (style.SQL_KEYWORD('ALTER'), + style.SQL_KEYWORD('SEQUENCE'), + style.SQL_FIELD(quote_name('%s_%s_seq' % (table_name, column_name))), + style.SQL_KEYWORD('RESTART'), + style.SQL_KEYWORD('WITH'), + style.SQL_FIELD('1') + ) + ) + else: + # sequence name in this case will be
_id_seq + sql.append("%s %s %s %s %s %s;" % \ + (style.SQL_KEYWORD('ALTER'), + style.SQL_KEYWORD('SEQUENCE'), + style.SQL_FIELD(quote_name('%s_id_seq' % table_name)), + style.SQL_KEYWORD('RESTART'), + style.SQL_KEYWORD('WITH'), + style.SQL_FIELD('1') + ) + ) + return sql + else: + return [] + class DatabaseWrapper(BaseDatabaseWrapper): ops = DatabaseOperations() @@ -88,58 +139,6 @@ dictfetchall = util.dictfetchall def get_start_transaction_sql(): return "BEGIN;" -def get_sql_flush(style, tables, sequences): - """Return a list of SQL statements required to remove all data from - all tables in the database (without actually removing the tables - themselves) and put the database in an empty 'initial' state - """ - if tables: - if postgres_version[0] >= 8 and postgres_version[1] >= 1: - # Postgres 8.1+ can do 'TRUNCATE x, y, z...;'. In fact, it *has to* in order to be able to - # truncate tables referenced by a foreign key in any other table. The result is a - # single SQL TRUNCATE statement - sql = ['%s %s;' % \ - (style.SQL_KEYWORD('TRUNCATE'), - style.SQL_FIELD(', '.join([quote_name(table) for table in tables])) - )] - else: - sql = ['%s %s %s;' % \ - (style.SQL_KEYWORD('DELETE'), - style.SQL_KEYWORD('FROM'), - style.SQL_FIELD(quote_name(table)) - ) for table in tables] - - # 'ALTER SEQUENCE sequence_name RESTART WITH 1;'... style SQL statements - # to reset sequence indices - for sequence in sequences: - table_name = sequence['table'] - column_name = sequence['column'] - if column_name and len(column_name) > 0: - # sequence name in this case will be
__seq - sql.append("%s %s %s %s %s %s;" % \ - (style.SQL_KEYWORD('ALTER'), - style.SQL_KEYWORD('SEQUENCE'), - style.SQL_FIELD(quote_name('%s_%s_seq' % (table_name, column_name))), - style.SQL_KEYWORD('RESTART'), - style.SQL_KEYWORD('WITH'), - style.SQL_FIELD('1') - ) - ) - else: - # sequence name in this case will be
_id_seq - sql.append("%s %s %s %s %s %s;" % \ - (style.SQL_KEYWORD('ALTER'), - style.SQL_KEYWORD('SEQUENCE'), - style.SQL_FIELD(quote_name('%s_id_seq' % table_name)), - style.SQL_KEYWORD('RESTART'), - style.SQL_KEYWORD('WITH'), - style.SQL_FIELD('1') - ) - ) - return sql - else: - return [] - def get_sql_sequence_reset(style, model_list): "Returns a list of the SQL statements to reset sequences for the given models." from django.db import models diff --git a/django/db/backends/sqlite3/base.py b/django/db/backends/sqlite3/base.py index 0ab928dfc4..ce3a0e719a 100644 --- a/django/db/backends/sqlite3/base.py +++ b/django/db/backends/sqlite3/base.py @@ -51,6 +51,19 @@ class DatabaseOperations(BaseDatabaseOperations): def pk_default_value(self): return 'NULL' + def sql_flush(self, style, tables, sequences): + # NB: The generated SQL below is specific to SQLite + # Note: The DELETE FROM... SQL generated below works for SQLite databases + # because constraints don't exist + sql = ['%s %s %s;' % \ + (style.SQL_KEYWORD('DELETE'), + style.SQL_KEYWORD('FROM'), + style.SQL_FIELD(quote_name(table)) + ) for table in tables] + # Note: No requirement for reset of auto-incremented indices (cf. other + # sql_flush() implementations). Just return SQL at this point + return sql + class DatabaseWrapper(BaseDatabaseWrapper): ops = DatabaseOperations() @@ -121,24 +134,6 @@ def _sqlite_extract(lookup_type, dt): def get_start_transaction_sql(): return "BEGIN;" -def get_sql_flush(style, tables, sequences): - """ - Return a list of SQL statements required to remove all data from - all tables in the database (without actually removing the tables - themselves) and put the database in an empty 'initial' state - """ - # NB: The generated SQL below is specific to SQLite - # Note: The DELETE FROM... SQL generated below works for SQLite databases - # because constraints don't exist - sql = ['%s %s %s;' % \ - (style.SQL_KEYWORD('DELETE'), - style.SQL_KEYWORD('FROM'), - style.SQL_FIELD(quote_name(table)) - ) for table in tables] - # Note: No requirement for reset of auto-incremented indices (cf. other - # get_sql_flush() implementations). Just return SQL at this point - return sql - def get_sql_sequence_reset(style, model_list): "Returns a list of the SQL statements to reset sequences for the given models." # No sequence reset required