Description
bake migration_snapshot silently drops table level options from the generated $this->table() statement. Only the primary key portion is rendered, so collation and engine are omitted entirely and any database rebuilt from the snapshot loses them.
Table options are already documented and supported on the write path — Creating a Table lists comment, collation, row_format, engine, signed, and limit, and MysqlAdapter::createTable() consumes them. So a user can write a table's collation or engine in a migration, but bake cannot read it back.
Reproduction
Against MySQL, with a database whose default collation is utf8mb4_0900_ai_ci:
CREATE TABLE events (
id INT NOT NULL AUTO_INCREMENT,
title VARCHAR(255) NULL,
PRIMARY KEY (id)
) COLLATE=utf8mb3_hungarian_ci;
CREATE TABLE parts (
id INT NOT NULL AUTO_INCREMENT,
name VARCHAR(255) NOT NULL,
PRIMARY KEY (id)
) ENGINE=MyISAM;
bin/cake bake migration_snapshot TableOptionsProof
Expected — the snapshot preserves the options:
$this->table('events', ['collation' => 'utf8_hungarian_ci'])
->addColumn('title', 'string', [...])
->create();
$this->table('parts', ['engine' => 'MyISAM'])
->addColumn('name', 'string', [...])
->create();
Actual — both options are missing:
$this->table('events')
->addColumn('title', 'string', [...])
->create();
$this->table('parts')
->addColumn('name', 'string', [...])
->create();
Running the generated migration produces events with the database default collation and parts with the InnoDB engine.
Impact
A table whose collation or engine differs from the connection defaults is rebuilt from the snapshot with the connection defaults instead of its real ones.
Snapshots rebuild fresh databases (CI, test, new environments) while existing databases keep their real schema, so the source and the rebuilt database diverge. Collation determines string comparison semantics and index key length.
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 eight tables declared with 'collation' => 'utf8_unicode_ci', which regenerate as utf8mb4_unicode_ci.
Root cause
This is not a reflection gap. The values are present on the TableSchema at bake time — MysqlSchemaDialect::describeOptions() returns engine and collation, and SchemaDialect::describe() applies them via setOptions().
They are dropped at render time. templates/bake/element/create-tables.twig builds the table options array from the primary key alone and never reads TableSchema::getOptions().
Column collation is affected as a consequence: MigrationHelper::attributes() strips a column's collate when it equals the table's collation, which is only sound if the table collation is rendered.
Table options are reflected for MySQL only; the other dialects return [] from describeOptions().
Versions
- migrations: 5.x
- CakePHP: 5.3
- Database: MySQL / MariaDB (table options are only reflected in
MysqlSchemaDialect)
Description
bake migration_snapshotsilently drops table level options from the generated$this->table()statement. Only the primary key portion is rendered, socollationandengineare omitted entirely and any database rebuilt from the snapshot loses them.Table options are already documented and supported on the write path — Creating a Table lists
comment,collation,row_format,engine,signed, andlimit, andMysqlAdapter::createTable()consumes them. So a user can write a table's collation or engine in a migration, but bake cannot read it back.Reproduction
Against MySQL, with a database whose default collation is
utf8mb4_0900_ai_ci:Expected — the snapshot preserves the options:
Actual — both options are missing:
Running the generated migration produces
eventswith the database default collation andpartswith the InnoDB engine.Impact
A table whose collation or engine differs from the connection defaults is rebuilt from the snapshot with the connection defaults instead of its real ones.
Snapshots rebuild fresh databases (CI, test, new environments) while existing databases keep their real schema, so the source and the rebuilt database diverge. Collation determines string comparison semantics and index key length.
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 eight tables declared with
'collation' => 'utf8_unicode_ci', which regenerate asutf8mb4_unicode_ci.Root cause
This is not a reflection gap. The values are present on the
TableSchemaat bake time —MysqlSchemaDialect::describeOptions()returnsengineandcollation, andSchemaDialect::describe()applies them viasetOptions().They are dropped at render time.
templates/bake/element/create-tables.twigbuilds the table options array from the primary key alone and never readsTableSchema::getOptions().Column collation is affected as a consequence:
MigrationHelper::attributes()strips a column'scollatewhen it equals the table's collation, which is only sound if the table collation is rendered.Table options are reflected for MySQL only; the other dialects return
[]fromdescribeOptions().Versions
MysqlSchemaDialect)