Skip to content

bake migration_snapshot drops ON UPDATE CURRENT_TIMESTAMP from datetime columns #1102

Description

@jamisonbryant

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:

  1. MigrationHelper::attributes()$validOptions gates what the snapshot path can see. onUpdate is dropped here first, so it never reaches the next stage.
  2. 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)

Metadata

Metadata

Assignees

Labels

Type

No type

Fields

No fields configured for issues without a type.

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions