Fixed #31275 -- Optimized sql_flush() without resetting sequences on MySQL.

Co-Authored-By: Simon Charette <charettes@users.noreply.github.com>
This commit is contained in:
c-bata 2020-03-27 01:51:30 +09:00 committed by Mariusz Felisiak
parent 75520e1767
commit 89032876f4
3 changed files with 44 additions and 14 deletions

View File

@ -585,6 +585,7 @@ answer newbie questions, and generally made Django that much better:
Martin von Gagern <gagern@google.com> Martin von Gagern <gagern@google.com>
Mart Sõmermaa <http://mrts.pri.ee/> Mart Sõmermaa <http://mrts.pri.ee/>
Marty Alchin <gulopine@gamemusic.org> Marty Alchin <gulopine@gamemusic.org>
Masashi Shibata <m.shibata1020@gmail.com>
masonsimon+django@gmail.com masonsimon+django@gmail.com
Massimiliano Ravelli <massimiliano.ravelli@gmail.com> Massimiliano Ravelli <massimiliano.ravelli@gmail.com>
Massimo Scamarcia <massimo.scamarcia@gmail.com> Massimo Scamarcia <massimo.scamarcia@gmail.com>

View File

@ -194,21 +194,40 @@ class DatabaseOperations(BaseDatabaseOperations):
return 'RETURNING %s' % ', '.join(columns), () return 'RETURNING %s' % ', '.join(columns), ()
def sql_flush(self, style, tables, sequences, allow_cascade=False): def sql_flush(self, style, tables, sequences, allow_cascade=False):
# NB: The generated SQL below is specific to MySQL if not tables:
# '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:
return [] 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): def validate_autopk_value(self, value):
# MySQLism: zero in AUTO_INCREMENT field does not work. Refs #17653. # MySQLism: zero in AUTO_INCREMENT field does not work. Refs #17653.

View File

@ -347,6 +347,10 @@ Models
* :meth:`.QuerySet.bulk_create` now sets the primary key on objects when using * :meth:`.QuerySet.bulk_create` now sets the primary key on objects when using
MariaDB 10.5+. 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 Pagination
~~~~~~~~~~ ~~~~~~~~~~
@ -415,6 +419,12 @@ Tests
* :class:`~django.test.runner.DiscoverRunner` now skips running the system * :class:`~django.test.runner.DiscoverRunner` now skips running the system
checks on databases not :ref:`referenced by tests<testing-multi-db>`. 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 URLs
~~~~ ~~~~