Commit 13b7f299 authored by Aymeric Augustin's avatar Aymeric Augustin
Browse files

Added a stealth option to flush to allow cascades.

This allows using flush on a subset of the tables without having to
manually cascade to all tables with foreign keys to the tables being
truncated, when they're known to be empty.

On databases where truncate is implemented with DELETE FROM, this
doesn't make a difference. The cascade is allowed, not mandatory.
parent 7a65c95d
Loading
Loading
Loading
Loading
+5 −2
Original line number Diff line number Diff line
@@ -32,8 +32,9 @@ class Command(NoArgsCommand):
        connection = connections[db]
        verbosity = int(options.get('verbosity'))
        interactive = options.get('interactive')
        # 'reset_sequences' is a stealth option
        # 'reset_sequences' and 'allow_cascade' are stealth options
        reset_sequences = options.get('reset_sequences', True)
        allow_cascade = options.get('allow_cascade', False)

        self.style = no_style()

@@ -45,7 +46,9 @@ class Command(NoArgsCommand):
            except ImportError:
                pass

        sql_list = sql_flush(self.style, connection, only_django=True, reset_sequences=reset_sequences)
        sql_list = sql_flush(self.style, connection, only_django=True,
                             reset_sequences=reset_sequences,
                             allow_cascade=allow_cascade)

        if interactive:
            confirm = input("""You have requested a flush of the database.
+2 −2
Original line number Diff line number Diff line
@@ -102,7 +102,7 @@ def sql_delete(app, style, connection):
    return output[::-1]  # Reverse it, to deal with table dependencies.


def sql_flush(style, connection, only_django=False, reset_sequences=True):
def sql_flush(style, connection, only_django=False, reset_sequences=True, allow_cascade=False):
    """
    Returns a list of the SQL statements used to flush the database.

@@ -114,7 +114,7 @@ def sql_flush(style, connection, only_django=False, reset_sequences=True):
    else:
        tables = connection.introspection.table_names()
    seqs = connection.introspection.sequence_list() if reset_sequences else ()
    statements = connection.ops.sql_flush(style, tables, seqs)
    statements = connection.ops.sql_flush(style, tables, seqs, allow_cascade)
    return statements


+6 −2
Original line number Diff line number Diff line
@@ -947,7 +947,7 @@ class BaseDatabaseOperations(object):
        """
        return ''

    def sql_flush(self, style, tables, sequences):
    def sql_flush(self, style, tables, sequences, allow_cascade=False):
        """
        Returns a list of SQL statements required to remove all data from
        the given database tables (without actually removing the tables
@@ -958,6 +958,10 @@ class BaseDatabaseOperations(object):

        The `style` argument is a Style object as returned by either
        color_style() or no_style() in django.core.management.color.

        The `allow_cascade` argument determines whether truncation may cascade
        to tables with foreign keys pointing the tables being truncated.
        PostgreSQL requires a cascade even if these tables are empty.
        """
        raise NotImplementedError()

+5 −2
Original line number Diff line number Diff line
@@ -298,14 +298,17 @@ class DatabaseOperations(BaseDatabaseOperations):
    def random_function_sql(self):
        return 'RAND()'

    def sql_flush(self, style, tables, sequences):
    def sql_flush(self, style, tables, sequences, allow_cascade=False):
        # 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(self.quote_name(table))))
                sql.append('%s %s;' % (
                    style.SQL_KEYWORD('TRUNCATE'),
                    style.SQL_FIELD(self.quote_name(table)),
                ))
            sql.append('SET FOREIGN_KEY_CHECKS = 1;')
            sql.extend(self.sequence_reset_by_name_sql(style, sequences))
            return sql
+6 −6
Original line number Diff line number Diff line
@@ -339,17 +339,17 @@ WHEN (new.%(col_name)s IS NULL)
    def savepoint_rollback_sql(self, sid):
        return convert_unicode("ROLLBACK TO SAVEPOINT " + self.quote_name(sid))

    def sql_flush(self, style, tables, sequences):
    def sql_flush(self, style, tables, sequences, allow_cascade=False):
        # 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'),
            sql = ['%s %s %s;' % (
                style.SQL_KEYWORD('DELETE'),
                style.SQL_KEYWORD('FROM'),
                     style.SQL_FIELD(self.quote_name(table)))
                    for table in tables]
                style.SQL_FIELD(self.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.
            sql.extend(self.sequence_reset_by_name_sql(style, sequences))
Loading