Skip to content

Commit

Permalink
Fixed #31275 -- Optimized sql_flush() without resetting sequences on …
Browse files Browse the repository at this point in the history
…MySQL.

Co-Authored-By: Simon Charette <charettes@users.noreply.github.com>
  • Loading branch information
2 people authored and felixxm committed Apr 2, 2020
1 parent 75520e1 commit 8903287
Show file tree
Hide file tree
Showing 3 changed files with 44 additions and 14 deletions.
1 change: 1 addition & 0 deletions AUTHORS
Original file line number Diff line number Diff line change
Expand Up @@ -585,6 +585,7 @@ answer newbie questions, and generally made Django that much better:
Martin von Gagern <gagern@google.com>
Mart Sõmermaa <http://mrts.pri.ee/>
Marty Alchin <gulopine@gamemusic.org>
Masashi Shibata <m.shibata1020@gmail.com>
masonsimon+django@gmail.com
Massimiliano Ravelli <massimiliano.ravelli@gmail.com>
Massimo Scamarcia <massimo.scamarcia@gmail.com>
Expand Down
47 changes: 33 additions & 14 deletions django/db/backends/mysql/operations.py
Original file line number Diff line number Diff line change
Expand Up @@ -194,21 +194,40 @@ def return_insert_columns(self, fields):
return 'RETURNING %s' % ', '.join(columns), ()

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('SET FOREIGN_KEY_CHECKS = 1;')
sql.extend(self.sequence_reset_by_name_sql(style, sequences))
return sql
else:
if not tables:
return []
sql = ['SET FOREIGN_KEY_CHECKS = 0;']
tables = set(tables)
with_sequences = set(s['table'] for s in sequences)
# It's faster to TRUNCATE tables that require a sequence reset since
# ALTER TABLE AUTO_INCREMENT is slower than TRUNCATE.
sql.extend(
'%s %s;' % (
style.SQL_KEYWORD('TRUNCATE'),
style.SQL_FIELD(self.quote_name(table_name)),
) for table_name in tables.intersection(with_sequences)
)
# Otherwise issue a simple DELETE since it's faster than TRUNCATE
# and preserves sequences.
sql.extend(
'%s %s %s;' % (
style.SQL_KEYWORD('DELETE'),
style.SQL_KEYWORD('FROM'),
style.SQL_FIELD(self.quote_name(table_name)),
) for table_name in tables.difference(with_sequences)
)
sql.append('SET FOREIGN_KEY_CHECKS = 1;')
return sql

def sequence_reset_by_name_sql(self, style, sequences):
return [
'%s %s %s %s = 1;' % (
style.SQL_KEYWORD('ALTER'),
style.SQL_KEYWORD('TABLE'),
style.SQL_FIELD(self.quote_name(sequence_info['table'])),
style.SQL_FIELD('AUTO_INCREMENT'),
) for sequence_info in sequences
]

def validate_autopk_value(self, value):
# MySQLism: zero in AUTO_INCREMENT field does not work. Refs #17653.
Expand Down
10 changes: 10 additions & 0 deletions docs/releases/3.1.txt
Original file line number Diff line number Diff line change
Expand Up @@ -347,6 +347,10 @@ Models
* :meth:`.QuerySet.bulk_create` now sets the primary key on objects when using
MariaDB 10.5+.

* The ``DatabaseOperations.sql_flush()`` method now generates more efficient
SQL on MySQL by using ``DELETE`` instead of ``TRUNCATE`` statements for
tables which don't require resetting sequences.

Pagination
~~~~~~~~~~

Expand Down Expand Up @@ -415,6 +419,12 @@ Tests
* :class:`~django.test.runner.DiscoverRunner` now skips running the system
checks on databases not :ref:`referenced by tests<testing-multi-db>`.

* :class:`~django.test.TransactionTestCase` teardown is now faster on MySQL
due to :djadmin:`flush` command improvements. As a side effect the latter
doesn't automatically reset sequences on teardown anymore. Enable
:attr:`.TransactionTestCase.reset_sequences` if your tests require this
feature.

URLs
~~~~

Expand Down

0 comments on commit 8903287

Please sign in to comment.