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