#33899 closed Bug (fixed)
RemoveField on indexed fields crashes on SQLite 3.35.5+
Description (last modified by ) ¶
Description ¶
I encountered the following error with django 4.1 in my Gitlab CI/CD Pipeline. When I bumped django versions from 4.0.7 to 4.1. my pipeline broke during the testing stage; specifically during db migrations. I have not changed any other source code.
Steps to reproduce ¶
Minimal example attached. Run make green
to see that it works with 4.0.7, run make red
to see that it does not work with 4.1. It will build and exercise a docker container which installs all dependencies in isolation and sets up an example django app and run migrations.
Manual steps:
- Install django 4.1
- Create a new project
- Create an app
- Install app in project
- Create a model
- Add field on model, set
db_index=True
- Make migrations:
$ python manage.py makemigrations
- Remove field from model
- Make migrations:
$ python manage.py makemigrations
- Apply migrations:
$ python manage.py migrate
The migration fails with the following error (for an app called web
, with a model called Entity
with a field called attribute
for example):
Running migrations: Applying contenttypes.0001_initial... OK ... Applying sessions.0001_initial... OK Applying web.0001_initial... OK Applying web.0002_remove_entity_attribute...Traceback (most recent call last): File "/usr/local/lib/python3.10/site-packages/django/db/backends/utils.py", line 89, in _execute return self.cursor.execute(sql, params) File "/usr/local/lib/python3.10/site-packages/django/db/backends/sqlite3/base.py", line 357, in execute return Database.Cursor.execute(self, query, params) sqlite3.OperationalError: error in index web_entity_attribute_d22c3fcb after drop column: no such column: attribute
Details ¶
The above steps create a set of migrations where at the end a RemoveField
migration is produced. Applying this migration fails for fields which had db_index=True
. The example I attached uses a SlugField where db_index
defaults to True
, setting this parameter to False will apply the migration without this error.
I reproduced the error with the following field types: TextField, IntegerField, SlugField, CharField, URLField
Change History (15)
by , 3 years ago
Attachment: | django-bug-33899.tgz added |
---|
comment:1 by , 3 years ago
Description: | modified (diff) |
---|
follow-up: 3 comment:2 by , 3 years ago
I'm really puzzled. I cannot reproduce this issue manually, but make red
crashes.
comment:3 by , 3 years ago
Replying to Mariusz Felisiak:
I'm really puzzled. I cannot reproduce this issue manually, but
make red
crashes.
Thank you for giving this a try. I investigated this some further and realized that my manual description is not precise enough to reproduce the error. The example I attached uses a SlugField, but the error does not occur with other field types, such as CharFields. I noticed that SlugFields set db_index=True
by default (See Link 1), and could reproduce the bug with other fields when setting db_index=True
. I will change the bug description accordingly.
Links:
comment:4 by , 3 years ago
Easy pickings: | set |
---|---|
Has patch: | set |
Needs tests: | set |
Triage Stage: | Unreviewed → Accepted |
The issue is valid. I have written the regression test of the exact test case. And found the first bad commit as below:
3702819227fd0cdd9b581cd99e11d1561d51cbeb is the first bad commit commit 3702819227fd0cdd9b581cd99e11d1561d51cbeb Author: Mariusz Felisiak <felisiak.mariusz@gmail.com> Date: Fri Feb 11 22:21:58 2022 +0100 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. django/db/backends/sqlite3/features.py | 2 ++ django/db/backends/sqlite3/schema.py | 10 ++++++++++ tests/schema/tests.py | 18 ++++++++++++++++++ 3 files changed, 30 insertions(+) bisect found first bad commit
comment:5 by , 3 years ago
Description: | modified (diff) |
---|---|
Summary: | migrations.RemoveField causes OperationalError "no such column" upon migration → migrations.RemoveField causes OperationalError "no such column" upon migration when db_index=True |
comment:6 by , 3 years ago
Description: | modified (diff) |
---|
comment:7 by , 3 years ago
Easy pickings: | unset |
---|---|
Owner: | changed from | to
Status: | new → assigned |
comment:8 by , 3 years ago
Has patch: | unset |
---|---|
Keywords: | Docker removed |
Needs tests: | unset |
Severity: | Normal → Release blocker |
Summary: | migrations.RemoveField causes OperationalError "no such column" upon migration when db_index=True → RemoveField on indexed fields crashes on SQLite 3.35.5+ |
Thanks! It should be enough to avoid this optimization on indexes fields:
-
TabularUnified django/db/backends/sqlite3/schema.py
diff --git a/django/db/backends/sqlite3/schema.py b/django/db/backends/sqlite3/schema.py index 55fdf5fbfe..6c106ae868 100644
a b class DatabaseSchemaEditor(BaseDatabaseSchemaEditor): 408 408 # For explicit "through" M2M fields, do nothing 409 409 elif ( 410 410 self.connection.features.can_alter_table_drop_column 411 # Primary keys, unique fields, and foreign keys are not412 # supported in ALTER TABLE DROP COLUMN.411 # Primary keys, unique fields, indexed fields, and foreign keys are 412 # not supported in ALTER TABLE DROP COLUMN. 413 413 and not field.primary_key 414 414 and not field.unique 415 and not field.db_index 415 416 and not (field.remote_field and field.db_constraint) 416 417 ): 417 418 super().remove_field(model, field)
According to SQLite docs:
Possible reasons why the DROP COLUMN command can fail include':'
...
- The column is indexed.
Regression in 3702819227fd0cdd9b581cd99e11d1561d51cbeb.
comment:9 by , 3 years ago
Has patch: | set |
---|
Please review the PR and approve workflows if any one of you is the maintainer.
https://github.com/django/django/pull/15925
comment:10 by , 3 years ago
Triage Stage: | Accepted → Ready for checkin |
---|
Minimal Example