Skip to content

os migrate plan omits the datetime storage-convergence work, so the plan understates what apply will do #3954

Description

@os-zhuang

Found while merging #3953 (datetime canonical storage) against the deferred-DDL work from #3917. Filed rather than bundled, because fixing it means changing an exported type and the CLI renderer that #3917 just landed.

The gap

previewDeferredSchemaWork() builds the plan from PendingSchemaWork, whose kind is a closed union:

exportinterfacePendingSchemaWork{table: string;kind: 'create_table'|'add_columns';columns: string[];}

#3953 adds two convergence steps that run inside initObjects alongside the create/alter path:

  • backfillCanonicalDatetimes — a row-rewriting UPDATE per SQLite Field.datetime column.
  • migrateMysqlDatetimeColumnsALTER TABLE … MODIFY … DATETIME(3) per legacy MySQL TIMESTAMP column.

Both correctly respect the deferral (they sit after the if (this.deferredDdl) continue gate, and flushDeferredSchemaDdl re-enters initObjects with it disarmed), so plan performs no work and apply performs all of it — the behaviour is right.

What is wrong is the reporting: neither step can be expressed in PendingSchemaWork, so os migrate plan shows nothing for them. An operator sees a plan listing, say, two added columns, confirms it, and apply additionally rewrites every row of a datetime column or rebuilds a column on a large table. That is exactly the kind of surprise #3917 exists to remove — the plan promises to show what apply will do.

Why it matters more than it looks

A DATETIME(3)ALTER … MODIFY on MySQL is a table rebuild: it takes a metadata lock and can run for a long time on a large table. Being told about it beforehand is the difference between a planned maintenance window and an unexplained stall.

The SQLite backfill is cheaper (one scan, and zero writes once converged) but still worth naming, if only so the operator can correlate the one-time boot cost.

Suggested direction

Widen the union and give each new kind the detail an operator needs to judge cost:

kind: 'create_table'|'add_columns'|'normalize_datetime_storage'|'widen_datetime_columns'

columns already carries the per-column list for both, so the shape may not need more than the new labels plus renderer cases. Worth checking whether the plan should also estimate row counts for the rewriting steps — that is the number that decides whether to run it now or in a window.

Anything that adds work to initObjects' physical path from here on has the same obligation, so it may be worth a short note next to PendingSchemaWork saying so.

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions