mirror of https://github.com/django/django.git
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:
parent
730711e828
commit
83f55aafdd
|
@ -100,7 +100,7 @@ class BaseDatabaseFeatures:
|
||||||
# The database's limit on the number of query parameters.
|
# The database's limit on the number of query parameters.
|
||||||
max_query_params = None
|
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
|
allows_auto_pk_0 = True
|
||||||
|
|
||||||
# Do we need to NULL a ForeignKey out, or can the constraint check be
|
# Do we need to NULL a ForeignKey out, or can the constraint check be
|
||||||
|
|
|
@ -17,7 +17,6 @@ class DatabaseFeatures(BaseDatabaseFeatures):
|
||||||
supports_index_column_ordering = False
|
supports_index_column_ordering = False
|
||||||
supports_timezones = False
|
supports_timezones = False
|
||||||
requires_explicit_null_ordering_when_grouping = True
|
requires_explicit_null_ordering_when_grouping = True
|
||||||
allows_auto_pk_0 = False
|
|
||||||
can_release_savepoints = True
|
can_release_savepoints = True
|
||||||
atomic_transactions = False
|
atomic_transactions = False
|
||||||
can_clone_databases = True
|
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"
|
"Internal method used in Django tests. Don't rely on this from your code"
|
||||||
return self.connection.mysql_server_data['default_storage_engine']
|
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
|
@cached_property
|
||||||
def update_can_self_select(self):
|
def update_can_self_select(self):
|
||||||
return self.connection.mysql_is_mariadb and self.connection.mysql_version >= (10, 3, 2)
|
return self.connection.mysql_is_mariadb and self.connection.mysql_version >= (10, 3, 2)
|
||||||
|
|
|
@ -227,8 +227,9 @@ class DatabaseOperations(BaseDatabaseOperations):
|
||||||
]
|
]
|
||||||
|
|
||||||
def validate_autopk_value(self, value):
|
def validate_autopk_value(self, value):
|
||||||
# MySQLism: zero in AUTO_INCREMENT field does not work. Refs #17653.
|
# Zero in AUTO_INCREMENT field does not work without the
|
||||||
if value == 0:
|
# 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 '
|
raise ValueError('The database backend does not accept 0 as a '
|
||||||
'value for AutoField.')
|
'value for AutoField.')
|
||||||
return value
|
return value
|
||||||
|
@ -266,6 +267,9 @@ class DatabaseOperations(BaseDatabaseOperations):
|
||||||
def max_name_length(self):
|
def max_name_length(self):
|
||||||
return 64
|
return 64
|
||||||
|
|
||||||
|
def pk_default_value(self):
|
||||||
|
return 'NULL'
|
||||||
|
|
||||||
def bulk_insert_sql(self, fields, placeholder_rows):
|
def bulk_insert_sql(self, fields, placeholder_rows):
|
||||||
placeholder_rows_sql = (", ".join(row) for row in placeholder_rows)
|
placeholder_rows_sql = (", ".join(row) for row in placeholder_rows)
|
||||||
values_sql = ", ".join("(%s)" % sql for sql in placeholder_rows_sql)
|
values_sql = ", ".join("(%s)" % sql for sql in placeholder_rows_sql)
|
||||||
|
|
|
@ -32,3 +32,9 @@ class TestFeatures(TestCase):
|
||||||
database_features = DatabaseFeatures(_connection)
|
database_features = DatabaseFeatures(_connection)
|
||||||
self.assertFalse(database_features.has_select_for_update_skip_locked)
|
self.assertFalse(database_features.has_select_for_update_skip_locked)
|
||||||
self.assertFalse(database_features.has_select_for_update_nowait)
|
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)
|
||||||
|
|
|
@ -806,7 +806,8 @@ class ThreadTests(TransactionTestCase):
|
||||||
class MySQLPKZeroTests(TestCase):
|
class MySQLPKZeroTests(TestCase):
|
||||||
"""
|
"""
|
||||||
Zero as id for AutoField should raise exception in MySQL, because MySQL
|
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')
|
@skipIfDBFeature('allows_auto_pk_0')
|
||||||
def test_zero_as_autoval(self):
|
def test_zero_as_autoval(self):
|
||||||
|
|
|
@ -115,7 +115,8 @@ class BulkCreateTests(TestCase):
|
||||||
def test_zero_as_autoval(self):
|
def test_zero_as_autoval(self):
|
||||||
"""
|
"""
|
||||||
Zero as id for AutoField should raise exception in MySQL, because MySQL
|
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')
|
valid_country = Country(name='Germany', iso_two_letter='DE')
|
||||||
invalid_country = Country(id=0, name='Poland', iso_two_letter='PL')
|
invalid_country = Country(id=0, name='Poland', iso_two_letter='PL')
|
||||||
|
|
|
@ -379,8 +379,8 @@ if connection.features.interprets_empty_strings_as_nulls:
|
||||||
data[3] is None)]
|
data[3] is None)]
|
||||||
|
|
||||||
# Regression test for #8651 -- a FK to an object with PK of 0
|
# 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
|
# This won't work on MySQL without the NO_AUTO_VALUE_ON_ZERO SQL mode since it
|
||||||
# with an autoincrement primary key of 0,
|
# won't let you create an object with an autoincrement primary key of 0.
|
||||||
if connection.features.allows_auto_pk_0:
|
if connection.features.allows_auto_pk_0:
|
||||||
test_data.extend([
|
test_data.extend([
|
||||||
(data_obj, 0, Anchor, "Anchor 0"),
|
(data_obj, 0, Anchor, "Anchor 0"),
|
||||||
|
|
Loading…
Reference in New Issue