Description
bake migration_snapshot silently drops ON UPDATE CURRENT_TIMESTAMP from datetime / timestamp columns. The generated migration omits the clause entirely, so any database rebuilt from the snapshot loses it.
The update column option is already documented and supported on the write path (docs/en/guides/writing-migrations/columns-and-table-operations.md), and Table::addTimestamps() emits it. So a user can write the clause in a migration, but bake cannot read it back.
Reproduction
Against MySQL:
CREATE TABLE on_update_proof (
id INT NOT NULL AUTO_INCREMENT,
modified DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (id)
);
bin/cake bake migration_snapshot OnUpdateProof
Expected — the snapshot preserves the clause:
->addColumn('modified', 'datetime', [
'default' => 'CURRENT_TIMESTAMP',
'limit' => null,
'null' => false,
'update' => 'CURRENT_TIMESTAMP',
])
Actual — the update option is missing:
->addColumn('modified', 'datetime', [
'default' => 'CURRENT_TIMESTAMP',
'limit' => null,
'null' => false,
])
Running the generated migration produces a column with no ON UPDATE clause.
Impact
Databases rebuilt from a snapshot stop auto-updating those columns on any write that does not go through the ORM (raw SQL, bulk updates, other services sharing the database). The column simply retains its insert-time value.
It makes the snapshot unusable as a migration baseline, which is its primary use case: squashing a long migration history into a snapshot silently rewrites the schema.
Observed in a CakePHP 5.3 / migrations 5.0.0 application on four columns.
Root cause
MigrationHelper filters reflected column attributes through two independent allowlists, and onUpdate is absent from both:
MigrationHelper::attributes() — $validOptions gates what the snapshot path can see. onUpdate is dropped here first, so it never reaches the next stage.
MigrationHelper::getColumnOption() — $wantedOptions gates what both the snapshot and diff paths render.
Both need the attribute, and it additionally needs translating from CakePHP's onUpdate key to Phinx's update option, since Column::setUpdate() is the setter behind the update option and there is no onUpdate alias.
This mirrors how the fixed attribute was added in #1014 (both allowlists) and corrected in #1048 (filtering the null case), and how collate is translated to collation in getColumnOption().
Versions
- migrations: 5.x
- CakePHP: 5.3
- Database: MySQL / MariaDB (the clause is MySQL-specific; core only reflects
onUpdate in MysqlSchemaDialect)
Description
bake migration_snapshotsilently dropsON UPDATE CURRENT_TIMESTAMPfromdatetime/timestampcolumns. The generated migration omits the clause entirely, so any database rebuilt from the snapshot loses it.The
updatecolumn option is already documented and supported on the write path (docs/en/guides/writing-migrations/columns-and-table-operations.md), andTable::addTimestamps()emits it. So a user can write the clause in a migration, but bake cannot read it back.Reproduction
Against MySQL:
Expected — the snapshot preserves the clause:
Actual — the
updateoption is missing:Running the generated migration produces a column with no
ON UPDATEclause.Impact
Databases rebuilt from a snapshot stop auto-updating those columns on any write that does not go through the ORM (raw SQL, bulk updates, other services sharing the database). The column simply retains its insert-time value.
It makes the snapshot unusable as a migration baseline, which is its primary use case: squashing a long migration history into a snapshot silently rewrites the schema.
Observed in a CakePHP 5.3 / migrations 5.0.0 application on four columns.
Root cause
MigrationHelperfilters reflected column attributes through two independent allowlists, andonUpdateis absent from both:MigrationHelper::attributes()—$validOptionsgates what the snapshot path can see.onUpdateis dropped here first, so it never reaches the next stage.MigrationHelper::getColumnOption()—$wantedOptionsgates what both the snapshot and diff paths render.Both need the attribute, and it additionally needs translating from CakePHP's
onUpdatekey to Phinx'supdateoption, sinceColumn::setUpdate()is the setter behind theupdateoption and there is noonUpdatealias.This mirrors how the
fixedattribute was added in #1014 (both allowlists) and corrected in #1048 (filtering thenullcase), and howcollateis translated tocollationingetColumnOption().Versions
onUpdateinMysqlSchemaDialect)