Fixed #17653 -- Allowed using zero as AutoFields value on MySQL if NO_AUTO_VALUE_ON_ZERO SQL mode is enabled.

This commit is contained in:
Mariusz Felisiak 2020-07-20 09:48:31 +02:00 committed by GitHub
parent 730711e828
commit 83f55aafdd
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 27 additions and 8 deletions

View File

@ -100,7 +100,7 @@ class BaseDatabaseFeatures:
# The database's limit on the number of query parameters.
max_query_params = None
# Can an object have an autoincrement primary key of 0? MySQL says No.
# Can an object have an autoincrement primary key of 0?
allows_auto_pk_0 = True
# Do we need to NULL a ForeignKey out, or can the constraint check be

View File

@ -17,7 +17,6 @@ class DatabaseFeatures(BaseDatabaseFeatures):
supports_index_column_ordering = False
supports_timezones = False
requires_explicit_null_ordering_when_grouping = True
allows_auto_pk_0 = False
can_release_savepoints = True
atomic_transactions = False
can_clone_databases = True
@ -51,6 +50,14 @@ class DatabaseFeatures(BaseDatabaseFeatures):
"Internal method used in Django tests. Don't rely on this from your code"
return self.connection.mysql_server_data['default_storage_engine']
@cached_property
def allows_auto_pk_0(self):
"""
Autoincrement primary key can be set to 0 if it doesn't generate new
autoincrement values.
"""
return 'NO_AUTO_VALUE_ON_ZERO' in self.connection.sql_mode
@cached_property
def update_can_self_select(self):
return self.connection.mysql_is_mariadb and self.connection.mysql_version >= (10, 3, 2)

View File

@ -227,8 +227,9 @@ class DatabaseOperations(BaseDatabaseOperations):
]
def validate_autopk_value(self, value):
# MySQLism: zero in AUTO_INCREMENT field does not work. Refs #17653.
if value == 0:
# Zero in AUTO_INCREMENT field does not work without the
# NO_AUTO_VALUE_ON_ZERO SQL mode.
if value == 0 and not self.connection.features.allows_auto_pk_0:
raise ValueError('The database backend does not accept 0 as a '
'value for AutoField.')
return value
@ -266,6 +267,9 @@ class DatabaseOperations(BaseDatabaseOperations):
def max_name_length(self):
return 64
def pk_default_value(self):
return 'NULL'
def bulk_insert_sql(self, fields, placeholder_rows):
placeholder_rows_sql = (", ".join(row) for row in placeholder_rows)
values_sql = ", ".join("(%s)" % sql for sql in placeholder_rows_sql)

View File

@ -32,3 +32,9 @@ class TestFeatures(TestCase):
database_features = DatabaseFeatures(_connection)
self.assertFalse(database_features.has_select_for_update_skip_locked)
self.assertFalse(database_features.has_select_for_update_nowait)
def test_allows_auto_pk_0(self):
with mock.MagicMock() as _connection:
_connection.sql_mode = {'NO_AUTO_VALUE_ON_ZERO'}
database_features = DatabaseFeatures(_connection)
self.assertIs(database_features.allows_auto_pk_0, True)

View File

@ -806,7 +806,8 @@ class ThreadTests(TransactionTestCase):
class MySQLPKZeroTests(TestCase):
"""
Zero as id for AutoField should raise exception in MySQL, because MySQL
does not allow zero for autoincrement primary key.
does not allow zero for autoincrement primary key if the
NO_AUTO_VALUE_ON_ZERO SQL mode is not enabled.
"""
@skipIfDBFeature('allows_auto_pk_0')
def test_zero_as_autoval(self):

View File

@ -115,7 +115,8 @@ class BulkCreateTests(TestCase):
def test_zero_as_autoval(self):
"""
Zero as id for AutoField should raise exception in MySQL, because MySQL
does not allow zero for automatic primary key.
does not allow zero for automatic primary key if the
NO_AUTO_VALUE_ON_ZERO SQL mode is not enabled.
"""
valid_country = Country(name='Germany', iso_two_letter='DE')
invalid_country = Country(id=0, name='Poland', iso_two_letter='PL')

View File

@ -379,8 +379,8 @@ if connection.features.interprets_empty_strings_as_nulls:
data[3] is None)]
# Regression test for #8651 -- a FK to an object with PK of 0
# This won't work on MySQL since it won't let you create an object
# with an autoincrement primary key of 0,
# This won't work on MySQL without the NO_AUTO_VALUE_ON_ZERO SQL mode since it
# won't let you create an object with an autoincrement primary key of 0.
if connection.features.allows_auto_pk_0:
test_data.extend([
(data_obj, 0, Anchor, "Anchor 0"),