Fix ChangeField failure with index+column type changes on MySQL.
Review Request #14922 — Created March 17, 2026 and updated
When a
ChangeFieldmutation changes both the field type and
unique/db_indexattributes, MySQL'sMODIFY COLUMNruns before the
index is dropped. MySQL rejects this forTEXTorBLOBcolumns with
the error "BLOB/TEXT column used in key specification without a key
length".This happened because
ChangeField.mutate()returns early after
change_column_type()whenchange_column_type_sets_attrsisTrue
(MySQL), skipping thechange_column_attrs()call that handles index
drops.
change_column_type()now processesdb_index/uniquechanges from
new_attrswhenchange_column_type_sets_attrsisTrue, emitting
index drops as pre-SQL (before theMODIFY) and index adds as post-SQL
(after). The SQLite3 backend also calls the new helper to keep in-memory
database state in sync.
- Ran unit tests without the fix and verified that the new tests failed with
the same issue I saw in the wild. - Ran unit tests with the fix and verified that it was now fixed.
- Ran dbtests for mysql, sqlite, and postgresql.
- Verified that this fixes the upgrade issue I was running into.
| Summary | ID |
|---|---|
| qnonyqrrwwqqowzwqpqlwpvvslzxpvum |
| Description | From | Last Updated |
|---|---|---|
|
Testing doesn't say, but I assume this also fixed the issue found in the wild? |
|
|
|
continuation line over-indented for visual indent Column: 48 Error code: E127 |
|
|
|
Versions are listed in descending order. |
|
|
|
Mind breaking the "This is necessary" into a new paragraph? There's a lot to process in this paragraph. |
|
|
|
This function has the following possible states: Old column New column Result Unique Unique No change Unique Indexed Remove unique, … |
|
|
|
Can we pull database_state out? This is used multiple times. |
|
|
|
Can you use keyword arguments here? |
|
|
|
Can you use keyword arguments here? I have no idea what True or None relate to. |
|
- Commits:
-
Summary ID qnonyqrrwwqqowzwqpqlwpvvslzxpvum qnonyqrrwwqqowzwqpqlwpvvslzxpvum
Checks run (2 succeeded)
-
This seems like the right approach. Went through the logic carefully and have some notes mostly to help ensure no future issues.
-
-
-
Mind breaking the "This is necessary" into a new paragraph? There's a lot to process in this paragraph.
-
This function has the following possible states:
Old column New column Result Unique Unique No change Unique Indexed Remove unique, add index Unique Non-indexed/unique Remove unique Unique (missing index) Unique No change Unique (missing index) Indexed Create index Unique (missing index) Non-indexed/unique No change Indexed Unique Drop index, add unique Indexed Indexed No change Indexed Non-indexed/unique Drop index Non-indexed/unique Unique Add unique Non-indexed/unique Indexed Add index Non-indexed/unique Non-indexed/unique No change I think I have that right.
Some of these are implicitly covered by other tests, but we should ensure we have tests covering all combinations of unique/indexed states so there are no surprises now or as code evolves.
It would also be great to have a truth table like this in the code showing the expectations for future us.
Notable above, the "Unique (missing index) -> Unique" does nothing, which is probably right, but perhaps worth calling out because it won't result in a database index being installed.
-
-
-
- Testing Done:
-
- Ran unit tests without the fix and verified that the new tests failed with
the same issue I saw in the wild.
- Ran unit tests with the fix and verified that it was now fixed.
- Ran dbtests for mysql, sqlite, and postgresql.
+ - Verified that this fixes the upgrade issue I was running into.
- Ran unit tests without the fix and verified that the new tests failed with
- Commits:
-
Summary ID qnonyqrrwwqqowzwqpqlwpvvslzxpvum qnonyqrrwwqqowzwqpqlwpvvslzxpvum