You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Picking this up from #1069 (citext support) — wanted to share what I found while looking at the wider "custom column type" problem, and get your read on the API shape before writing code.
One useful finding: DDL is not actually a blocker
Cake\Database\Schema\PostgresSchemaDialect::columnDefinitionSql() already has a fallback at the bottom of the type map (around line 790):
So a column with an unknown type string like 'hstore' already produces "col" HSTORE correctly, and 'vector' with limit => 1536 produces "col" VECTOR(1536). The only thing blocking these types in Migrations today is isValidColumnType() — no dialect subclass or DDL-fragment API is needed for the common case. That narrows the problem nicely.
Design options I've been weighing
A. addSpecificColumnType(string $type) (your plan)
Simple, matches the existing $specificColumnTypes machinery.
Open question: static vs instance state?
Static (mutate static::$specificColumnTypes): simplest, but tests leak registered types between runs. The property is currently never mutated — introducing mutation makes isolation harder.
Instance (per-adapter list): cleaner, but the registration call has to happen every time an adapter is constructed, which is awkward unless it runs from a central bootstrap.
B. Configure-driven additive list — Configure::write('Migrations.Postgres.extraColumnTypes', ['hstore', 'ltree']) read once in the adapter constructor.
Pros: set once per app, no migration-file noise, no static mutation.
Cons: small coupling to Configure; not discoverable from the migration itself.
C. Literal escape hatch — accept Migrations\Db\Literal in Column::$type, skip validation when it's a Literal.
Pros: zero registration, explicit at call site, handles parameterized types in one shot.
Cons: changes Column::$type from strict string to string|Literal. Every ->getType() caller would need auditing — most use in_array() / string equality, which would silently stop matching for Literal and could mask bugs.
My recommendation
Do A + B as a hybrid: the registration method as the primary API, plus a Configure hook as the "set it and forget it" integration point. Defer C (Literal) to a follow-up unless someone shows up with a concrete parameterized-type need — speculative feature.
Concretely:
// In a migration, or once in Application::bootstrap():PostgresAdapter::addSpecificColumnType('hstore');
// Or app-wide config:Configure::write('Migrations.Postgres.extraColumnTypes', ['hstore', 'ltree']);
// Then:$table->addColumn('data', 'hstore'); // passes validation, Cake dialect emits correct DDL
Things I want your opinion on before coding
Static vs instance vs Configure? Leaning static + Configure hook (with a test teardown helper to reset the registration), but it's a judgment call.
Scope of v1: registration only, no DDL-fragment parameter? I think yes — the dialect fallback covers the 90% case; users who need custom DDL can subclass the adapter.
MySQL/SQL Server parity: keep it Postgres-only for now, or add the same hook shape across all adapters? (MySQL has the $specificColumnTypes pattern too but fewer extension types in the wild.)
After checking this plugin, Adapter instance is created in the Environment::executeMigration() on first migration found.
Adding column to the adapter instance is not "safe", like static method.
Configure is the cleanest and comprehensive way.
Should be set in Migrations. options or Datasource (core) ?
Activity
dereuromark commentedon Apr 17, 2026
Picking this up from #1069 (citext support) — wanted to share what I found while looking at the wider "custom column type" problem, and get your read on the API shape before writing code.
One useful finding: DDL is not actually a blocker
Cake\Database\Schema\PostgresSchemaDialect::columnDefinitionSql()already has a fallback at the bottom of the type map (around line 790):So a column with an unknown type string like
'hstore'already produces"col" HSTOREcorrectly, and'vector'withlimit => 1536produces"col" VECTOR(1536). The only thing blocking these types in Migrations today isisValidColumnType()— no dialect subclass or DDL-fragment API is needed for the common case. That narrows the problem nicely.Design options I've been weighing
A.
addSpecificColumnType(string $type)(your plan)Simple, matches the existing
$specificColumnTypesmachinery.Open question: static vs instance state?
static::$specificColumnTypes): simplest, but tests leak registered types between runs. The property is currently never mutated — introducing mutation makes isolation harder.B. Configure-driven additive list —
Configure::write('Migrations.Postgres.extraColumnTypes', ['hstore', 'ltree'])read once in the adapter constructor.Pros: set once per app, no migration-file noise, no static mutation.
Cons: small coupling to
Configure; not discoverable from the migration itself.C.
Literalescape hatch — acceptMigrations\Db\LiteralinColumn::$type, skip validation when it's a Literal.Usage:
$table->addColumn('data', Literal::from('VECTOR(1536)')).Pros: zero registration, explicit at call site, handles parameterized types in one shot.
Cons: changes
Column::$typefrom strictstringtostring|Literal. Every->getType()caller would need auditing — most usein_array()/ string equality, which would silently stop matching for Literal and could mask bugs.My recommendation
Do A + B as a hybrid: the registration method as the primary API, plus a Configure hook as the "set it and forget it" integration point. Defer C (Literal) to a follow-up unless someone shows up with a concrete parameterized-type need — speculative feature.
Concretely:
Things I want your opinion on before coding
$specificColumnTypespattern too but fewer extension types in the wild.)Erwane commentedon Apr 24, 2026
After checking this plugin, Adapter instance is created in the
Environment::executeMigration()on first migration found.Adding column to the adapter instance is not "safe", like static method.
Configure is the cleanest and comprehensive way.
Should be set in Migrations. options or Datasource (core) ?