Repository navigation
Fixes #37167: keep the entity_extension.extension charset when widening it in 2.0.3 - #37181
Conversation
| -- The column keeps its character set and collation: changing either makes MySQL copy the whole | ||
| -- table and block writes to it, while widening alone is done in place. | ||
| SET @entity_extension_widen_ddl = ( | ||
| SELECT IF( | ||
| c.CHARACTER_MAXIMUM_LENGTH < 512, | ||
| CONCAT( | ||
| 'ALTER TABLE entity_extension MODIFY COLUMN extension VARCHAR(512) CHARACTER SET ', | ||
| c.CHARACTER_SET_NAME, ' COLLATE ', c.COLLATION_NAME, ' NOT NULL'), | ||
| 'SELECT 1' | ||
| ) | ||
| FROM information_schema.columns c | ||
| WHERE c.table_schema = DATABASE() | ||
| AND c.table_name = 'entity_extension' | ||
| AND c.column_name = 'extension' | ||
| ); |
There was a problem hiding this comment.
⚠️ Quality: Editing shipped 2.0.3 migration and split charset rule are unrecorded
This PR changes a statement in an already-released migration (2.0.3). The migrations rule says migrations are append-only. The only recorded exception, 2026-11-02-the-1-13-0-quartz-purge-is-one-locked-transaction.md, says it "does not license editing statements". The safety argument here is that the runner sees the new block's checksum as unrecorded, re-runs the 2.0.3 folder on the next migrate, and the guard makes it a no-op for columns already at 512. That argument and the rule that follows from it appear only in the PR description. The repository squash-merges, so neither will reach git log.
The PR also creates a lasting difference between databases: entity_extension.extension stays ascii_bin where the old statement ran and keeps utf8mb4 everywhere else, until #37180. Collation decides key comparison in the composite primary key (ascii_bin is case-sensitive, utf8mb4_0900_ai_ci is not), so future code and migrations need to know about it.
A record should assert, from the description: "Widening alone is enough for #29493" / the column "keeps the column's current character set and collation". It should also say that a shipped MySQL statement may be replaced by an idempotent guarded block because "they run the 2.0.3 folder again on their next upgrade", and that databases that already ran the old statement keep ascii_bin until #37180.
Was this helpful? React with 👍 / 👎
There was a problem hiding this comment.
Intended. The new block's text is new, so databases that already ran 2.0.3 run it again on their next upgrade. Its guard makes it do nothing when the column is already 512, so their ascii_bin column is left for #37180 to realign. Databases that never ran 2.0.3 get the widening in place, with no table copy.
There was a problem hiding this comment.
Checked the block in 2.0.3/mysql/schemaChanges.sql (lines 14-28). Your explanation matches the code. The guard only builds an ALTER when CHARACTER_MAXIMUM_LENGTH < 512, and it reuses the column's current CHARACTER_SET_NAME and COLLATION_NAME, so databases that already widened the column run SELECT 1 and keep their ascii_bin, and databases that never ran 2.0.3 are widened in place without a table copy.
That behavior is intended, but the finding is about where it is written down. Right now the reasoning lives in the PR description, which will not survive a squash merge. The file comment covers only the "keeps its character set and collation" part. These points are not recorded anywhere in the repo:
- A shipped MySQL statement may be replaced by an idempotent guarded block, because its new text has a new checksum and the 2.0.3 folder reruns on the next
migrate. The quartz ADR says its append-only exception "does not license editing statements" (that sentence is outside the part I read), so this needs its own record. - Databases that ran the old statement keep
ascii_binonentity_extension.extension, and the others keeputf8mb4, until MySQL: realign entity_extension.extension on databases already switched to ascii by 2.0.3 #37180 realigns them.ascii_binis case-sensitive in the composite primary key andutf8mb4_0900_ai_ciis not, so later code and migrations need to know about the split.
To resolve the finding, add one ADR under docs/decisions/ covering both points (it can be the same one as for the other finding), or a comment in the SQL file that names #37180 and the charset split. I can write either if you ask.
There was a problem hiding this comment.
Recorded in the SQL comment above the block: it replaces the statement 2.0.3 first shipped, its new text makes it run again where the runner reprocesses 2.0.3, it does nothing where the column is already 512, and those databases stay ascii_bin until #37180 aligns the column with its table.
There was a problem hiding this comment.
Checked the latest head. The SQL comment now records that the block replaces the originally shipped statement, that databases which ran the old one stay ascii_bin until #37180, and that widening alone is done in place. That is the substance of the finding.
Not recorded there: the rule that a shipped MySQL statement may be replaced by an idempotent guarded block, and the case-sensitivity difference between ascii_bin and utf8mb4 in the composite primary key. The first is the reasoning that would let someone repeat this edit elsewhere, and a file comment is easy to miss from other migrations. If you prefer to keep this to the comment, that is a reasonable call for a one-off, but the finding stays open on those two points.
Code Review ✅ Approved 2 closed / 2 findings🔴 High risk · The MySQL migration widens entity_extension.extension in a schema change; an unexpected DDL outcome could disrupt writes or leave upgrades incomplete. Keeps the ✅ 2 closed✅ Quality: New SET/PREPARE guard in schemaChanges.sql contradicts an Accepted ADR
✅ Quality: Editing shipped 2.0.3 migration and split charset rule are unrecorded
VerificationRules No rules evaluated Functional validation Not enabledAuto-approval Not enabledFor more verification details, comment OptionsDisplay: compact → Showing compact verification results. Comment with these commands to change the behavior for this request:
Was this helpful? React with 👍 / 👎 | Powered by Gitar — free for open source |
…hen widening it in 2.0.3 (#37182)
Fixes #37167
Databases that already ran the old statement are handled separately in #37180.
Why
The 2.0.3 MySQL migration for #29493 widens
entity_extension.extensiontoVARCHAR(512), but it also switches the column toCHARACTER SET ascii COLLATE ascii_bin. Real databases have this column asutf8mb4with the table's default collation, so MySQL cannot do the change in place: it copies the whole table and blocks writes to it until the copy ends. On a production database with a 446 GB table this took about 7 hours. It can also fail (strict mode) or replace characters with?(non-strict mode) when a key holds non-ASCII characters, such as an older custom property name.Widening alone is enough for #29493, and MySQL does it in place.
What changes
The MySQL statement in
2.0.3/mysql/schemaChanges.sqlbecomes a guarded block:VARCHAR(512)and keeps the column's current character set and collation, read frominformation_schema. Nothing else about the column changes, so MySQL only updates the table definition.Postgres is unchanged: its statement only widened the column.
Why keeping
utf8mb4is safeThe
asciicharacter set was there to keepextensionusable in the table's primary key,(id, extension). That isn't needed:utf8mb4, MySQL counts 4 bytes per character, so the primary key needs 36 × 4 + 512 × 4 = 2192 bytes.COMPACTrow format. A database could only have created it with theDYNAMICrow format (MySQL's default since 5.7), whose limit is 3072 bytes.VARCHARstores the actual bytes, and a plain ASCII character takes 1 byte inutf8mb4too, so the hashed keys take the same space either way.The tests below widen a
utf8mb4column that is part of the primary key, on 300k rows, without errors.Tests
Run against the column states real databases are in (MySQL 8.0, 300k rows). An unchanged InnoDB table id means MySQL did not rebuild the table.
varchar(256)utf8mb4_0900_ai_ci(never ran 2.0.3)varchar(512)utf8mb4_0900_ai_ci, 0.07 svarchar(512)ascii_bin(ran the old statement)varchar(256)utf8mb4_general_ci(older default collation)varchar(512)utf8mb4_general_ciReal upgrades with the release images and the migration runner, using a 2.0.7 image that carries this file:
validatevarchar(512)ascii_binvarchar(512)utf8mb4_0900_ai_ciascii) to 2.0.7 with this fixvarchar(512)ascii_bin, unchangedIn the last case the runner re-ran the 2.0.3 folder and recorded the new block under 2.0.3, so the block ran and left the column alone.
Unit tests:
MySqlGuardedBlockNamesTest,MySqlMigrationUnitsTest,GuardedBlockMySqlExecutionTest,MigrationWorkflowTestandMigrationWorkflowReprocessingTestpass.