1
0
mirror of https://github.com/django/django.git synced 2024-12-23 01:25:58 +00:00

Refs #32502 -- Avoided table rebuild when removing fields on SQLite 3.35.5+.

ALTER TABLE ... DROP COLUMN was introduced in SQLite 3.35+ however
a data corruption issue was fixed in SQLite 3.35.5.
This commit is contained in:
Mariusz Felisiak 2022-02-11 22:21:58 +01:00 committed by GitHub
parent d113b5a837
commit 3702819227
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 30 additions and 0 deletions

View File

@ -28,6 +28,8 @@ class DatabaseFeatures(BaseDatabaseFeatures):
has_case_insensitive_like = True has_case_insensitive_like = True
# Is "ALTER TABLE ... RENAME COLUMN" supported? # Is "ALTER TABLE ... RENAME COLUMN" supported?
can_alter_table_rename_column = Database.sqlite_version_info >= (3, 25, 0) can_alter_table_rename_column = Database.sqlite_version_info >= (3, 25, 0)
# Is "ALTER TABLE ... DROP COLUMN" supported?
can_alter_table_drop_column = Database.sqlite_version_info >= (3, 35, 5)
supports_parentheses_in_compound = False supports_parentheses_in_compound = False
# Deferred constraint checks can be emulated on SQLite < 3.20 but not in a # Deferred constraint checks can be emulated on SQLite < 3.20 but not in a
# reasonably performant way. # reasonably performant way.

View File

@ -18,6 +18,7 @@ class DatabaseSchemaEditor(BaseDatabaseSchemaEditor):
"REFERENCES %(to_table)s (%(to_column)s) DEFERRABLE INITIALLY DEFERRED" "REFERENCES %(to_table)s (%(to_column)s) DEFERRABLE INITIALLY DEFERRED"
) )
sql_create_column_inline_fk = sql_create_inline_fk sql_create_column_inline_fk = sql_create_inline_fk
sql_delete_column = "ALTER TABLE %(table)s DROP COLUMN %(column)s"
sql_create_unique = "CREATE UNIQUE INDEX %(name)s ON %(table)s (%(columns)s)" sql_create_unique = "CREATE UNIQUE INDEX %(name)s ON %(table)s (%(columns)s)"
sql_delete_unique = "DROP INDEX %(name)s" sql_delete_unique = "DROP INDEX %(name)s"
@ -400,6 +401,15 @@ class DatabaseSchemaEditor(BaseDatabaseSchemaEditor):
if field.remote_field.through._meta.auto_created: if field.remote_field.through._meta.auto_created:
self.delete_model(field.remote_field.through) self.delete_model(field.remote_field.through)
# For explicit "through" M2M fields, do nothing # For explicit "through" M2M fields, do nothing
elif (
self.connection.features.can_alter_table_drop_column
# Primary keys, unique fields, and foreign keys are not
# supported in ALTER TABLE DROP COLUMN.
and not field.primary_key
and not field.unique
and not (field.remote_field and field.db_constraint)
):
super().remove_field(model, field)
# For everything else, remake. # For everything else, remake.
else: else:
# It might not actually have a column behind it # It might not actually have a column behind it

View File

@ -789,6 +789,24 @@ class SchemaTests(TransactionTestCase):
# Introspection treats BLOBs as TextFields # Introspection treats BLOBs as TextFields
self.assertEqual(columns["bits"][0], "TextField") self.assertEqual(columns["bits"][0], "TextField")
def test_remove_field(self):
with connection.schema_editor() as editor:
editor.create_model(Author)
with CaptureQueriesContext(connection) as ctx:
editor.remove_field(Author, Author._meta.get_field("name"))
columns = self.column_classes(Author)
self.assertNotIn("name", columns)
if getattr(connection.features, "can_alter_table_drop_column", True):
# Table is not rebuilt.
self.assertIs(
any("CREATE TABLE" in query["sql"] for query in ctx.captured_queries),
False,
)
self.assertIs(
any("DROP TABLE" in query["sql"] for query in ctx.captured_queries),
False,
)
def test_alter(self): def test_alter(self):
""" """
Tests simple altering of fields Tests simple altering of fields