docs/agents/ddl/07-modify-column.md
This doc focuses on model.ActionModifyColumn, covering ALTER TABLE ... MODIFY COLUMN, ALTER TABLE ... CHANGE COLUMN, and ALTER TABLE ... RENAME COLUMN (implemented as a special case of modify-column).
Modify column is tricky because it can be:
SQL → DDL module:
pkg/executor/ddl.go (type DDLExec, (*DDLExec).Next)pkg/ddl/executor.go:ModifyColumnpkg/ddl/executor.go:ChangeColumnpkg/ddl/executor.go:RenameColumn (builds a model.ActionModifyColumn job)Job creation / args:
pkg/ddl/modify_column.go:GetModifiableColumnJobmodel.ModifyColumnArgs (pkg/meta/model/job_args.go)Job execution (owner worker):
pkg/ddl/job_worker.go (case model.ActionModifyColumn:)pkg/ddl/modify_column.go:onModifyColumnModify-column behavior is driven by args.ModifyColumnType:
pkg/ddl/modify_column.go:getModifyColumnTypepkg/ddl/modify_column.go:noReorgDataStrict (strong check: “no reorg no matter what data is”)High-level rules (read getModifyColumnType for the exact gates):
noReorgDataStrict(...) is true:
ModifyTypeNoReorgModifyTypeNoReorgWithCheck (requires validating existing rows)ModifyTypeReorg (see the FIXME gate in getModifyColumnType)ModifyTypeReorgModifyTypeReorg (needRowReorg)ModifyTypeNoReorgWithCheckModifyTypeIndexReorg (needIndexReorg)Special-case: VARCHAR → CHAR may start as ModifyTypePrecheck and then “downgrade” to a no-reorg type if data is safe:
pkg/ddl/modify_column.go:precheckForVarcharToCharNo-reorg jobs still run through the DDL job framework, but avoid the backfill engine.
pkg/ddl/modify_column.go:doModifyColumnNoCheckpkg/ddl/modify_column.go:doModifyColumnWithCheck
pkg/ddl/modify_column.go:buildCheckSQLFromModifyColumnpkg/ddl/modify_column.go:checkModifyColumnData (runs a restricted SQL SELECT ... LIMIT 1)Finish / schema diff:
pkg/ddl/modify_column.go:finishModifyColumnWithoutReorgThis path is used when row data can stay as-is, but index keys/values must be rewritten (e.g. unsigned/signed integer toggles, collation encoding changes).
pkg/ddl/modify_column.go:doModifyColumnIndexReorgpkg/ddl/modify_column.go:initializeChangingIndexespkg/ddl/index.go:doReorgWorkForCreateIndexState machine (job.SchemaState) is similar to add-index:
StateNone → StateDeleteOnly → StateWriteOnly → StateWriteReorganization → StatePublic → DoneThis is the “full” modify-column pipeline:
pkg/ddl/modify_column.go:doModifyColumnTypeWithDatapkg/ddl/modify_column.go:getChangingColjob.ReorgMeta.Stage (pkg/meta/model/reorg.go):
ReorgStageModifyColumnUpdateColumn (row rewrite)ReorgStageModifyColumnRecreateIndex (index rebuild)ReorgStageModifyColumnCompletedSchema state machine (online compatibility window):
StateNone → StateDeleteOnly → StateWriteOnly → StateWriteReorganization → StatePublicStateWriteOnly → StateDeleteOnly → removedRow rewrite (reorg/backfill) is implemented via the shared reorg engine:
pkg/ddl/modify_column.go:doReorgWorkForModifyColumnpkg/ddl/column.go:updatePhysicalTableRow (uses typeUpdateColumnWorker for ActionModifyColumn)pkg/ddl/column.go:updateColumnWorker (casts and writes new row values)Index recreation reuses the add-index reorg path:
pkg/ddl/index.go:doReorgWorkForCreateIndexDurable progress matters because owner transfer / retry can re-run steps.
Job args (model.ModifyColumnArgs) persist mid-flight identifiers so the job can resume:
OldColumnID and OldColumnNameChangingColumn / ChangingIdxs (full reorg)RedundantIdxs (GC old temp indexes created by earlier modify-column jobs)IndexIDs, PartitionIDs, etc.Reorg checkpoint/progress:
mysql.tidb_ddl_job (job_meta, reorg, processing)mysql.tidb_ddl_reorg (checkpoint range + element)Reorg meta (job.ReorgMeta) persists:
ReorgTp (backfill type selection for index rebuild): pkg/ddl/index.go:initForReorgIndexes → pkg/ddl/index.go:pickBackfillTypeStage (modify-column stage machine): pkg/meta/model/reorg.goAnalyzeState (optional analyze after reorg)Rollback handling depends on the modify type:
pkg/ddl/modify_column.go:rollbackModifyColumnJobpkg/ddl/modify_column.go:rollbackModifyColumnJobWithReorgpkg/ddl/modify_column.go:rollbackModifyColumnJobWithIndexReorgKey invariant: a step may be retried/replayed, so make each step idempotent and ensure all “new identifiers” are persisted before doing irreversible work.
SQL:
ADMIN SHOW DDL JOBSADMIN SHOW DDL JOB QUERIESADMIN CANCEL DDL JOBS <job_id>Tables:
mysql.tidb_ddl_job (queue)mysql.tidb_ddl_history (history)mysql.tidb_ddl_reorg (reorg handle/progress)Failpoints (tests/debug):
github.com/pingcap/tidb/pkg/ddl/mockDelayInModifyColumnTypeWithDatagithub.com/pingcap/tidb/pkg/ddl/afterModifyColumnStateDeleteOnlygithub.com/pingcap/tidb/pkg/ddl/afterReorgWorkForModifyColumngithub.com/pingcap/tidb/pkg/ddl/getModifyColumnTypeTests (good starting points):
pkg/ddl/modify_column_test.gopkg/ddl/column_modify_test.gopkg/ddl/column_change_test.gogetModifyColumnType); don’t assume “fast path” applies.ModifyColumnType and args decoding must remain backward compatible (getModifyColumnType’s compat comments).