summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMariusz Felisiak <felisiak.mariusz@gmail.com>2022-02-11 22:21:58 +0100
committerGitHub <noreply@github.com>2022-02-11 22:21:58 +0100
commit3702819227fd0cdd9b581cd99e11d1561d51cbeb (patch)
treedbb187f9d7c3c34ac647b4ddc614c8e67b813a89
parentd113b5a837f726d1c638d76c4e88445e6cd59fd5 (diff)
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.
-rw-r--r--django/db/backends/sqlite3/features.py2
-rw-r--r--django/db/backends/sqlite3/schema.py10
-rw-r--r--tests/schema/tests.py18
3 files changed, 30 insertions, 0 deletions
diff --git a/django/db/backends/sqlite3/features.py b/django/db/backends/sqlite3/features.py
index d0c9dc5391..9161ae3133 100644
--- a/django/db/backends/sqlite3/features.py
+++ b/django/db/backends/sqlite3/features.py
@@ -28,6 +28,8 @@ class DatabaseFeatures(BaseDatabaseFeatures):
has_case_insensitive_like = True
# Is "ALTER TABLE ... RENAME COLUMN" supported?
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
# Deferred constraint checks can be emulated on SQLite < 3.20 but not in a
# reasonably performant way.
diff --git a/django/db/backends/sqlite3/schema.py b/django/db/backends/sqlite3/schema.py
index 18449026bd..b3a2f4282b 100644
--- a/django/db/backends/sqlite3/schema.py
+++ b/django/db/backends/sqlite3/schema.py
@@ -18,6 +18,7 @@ class DatabaseSchemaEditor(BaseDatabaseSchemaEditor):
"REFERENCES %(to_table)s (%(to_column)s) DEFERRABLE INITIALLY DEFERRED"
)
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_delete_unique = "DROP INDEX %(name)s"
@@ -400,6 +401,15 @@ class DatabaseSchemaEditor(BaseDatabaseSchemaEditor):
if field.remote_field.through._meta.auto_created:
self.delete_model(field.remote_field.through)
# 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.
else:
# It might not actually have a column behind it
diff --git a/tests/schema/tests.py b/tests/schema/tests.py
index 208421eb64..37f2946006 100644
--- a/tests/schema/tests.py
+++ b/tests/schema/tests.py
@@ -789,6 +789,24 @@ class SchemaTests(TransactionTestCase):
# Introspection treats BLOBs as TextFields
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):
"""
Tests simple altering of fields