Column 'encoding' option is silently ignored for all types except enum/set
Nobody has claimed this yet.
- Dominant language
- PHP
- Stars
- 134
- Forks
- 122
- Avg merge
- 2d 2h
- Merged PRs (30d)
- 3
Description
This is a (multiple allowed):
-
bug
-
enhancement
-
feature-discussion (RFC)
-
CakePHP Version: 5.4.1
-
Migrations plugin version: 5.2.6 (present since 5.0.0)
-
Bake plugin version (if relevant): n/a
-
Database server: MariaDB 10.11.14 (applies to MySQL as well)
-
PHP Version: 8.2.33
-
Platform / OS: Linux
What you did
Ran a migration that sets the encoding option on a column, without a collation:
public function up(): void
{
$this->table('requests')
->changeColumn('data', 'text', ['encoding' => 'ascii'])
->update();
}
Expected Behavior
The column is created in the ascii character set. The option is documented as supported in columns-and-table-operations.md:
MySQL also supports
collationandencoding.
`data` TEXT CHARACTER SET ascii NOT NULL
Actual Behavior
The option is discarded. The generated definition carries no character set, so the column keeps the table default, and no error or warning is raised:
`data` TEXT NOT NULL
All three paths that render a column definition are affected, since each one goes through MysqlAdapter::columnDefinitionSql(). Reading back information_schema.COLUMNS.CHARACTER_SET_NAME on a utf8mb4 table, with ['encoding' => 'ascii'] on every column:
| Path | Expected | Actual |
|---|---|---|
CREATE TABLE |
ascii |
utf8mb4 |
ALTER TABLE ... ADD (addColumn) |
ascii |
utf8mb4 |
ALTER TABLE ... CHANGE (changeColumn) |
ascii |
utf8mb4 |
Why it has gone unnoticed
A migration that pairs encoding with a collation still produces the right column, because MySQL infers the character set from the collation. Only encoding on its own silently does nothing — which is also why no existing test covers it.
Where the option is lost
Column::getValidOptions() accepts encoding, and Table::getChangedColumnOptions() goes as far as preserving it across a changeColumn when it is not passed explicitly. From there:
Column::toArray()exports'collate'and no character-set key.MysqlSchemaDialect::columnDefinitionSql()in cakephp/database rendersCOLLATE, but has no per-columnCHARACTER SET— only the table-levelDEFAULT CHARSET.MysqlAdapter::columnDefinitionSql()emitsCHARACTER SETonly inside theenum/setbackwards-compatibility branch; every other type returns the dialect's output verbatim.
Earliest affected version
5.0.0. That release already had the enum/set-only branch in MysqlAdapter::columnDefinitionSql(). Up to 4.x the adapter was Phinx's, whose getColumnSqlDefinition() appended CHARACTER SET for every column type, so this is a regression introduced by the move to cakephp/database.
Other backends are not affected: PostgreSQL sets its character set per database, and SQL Server and SQLite have no per-column character set either. All three offer only per-column COLLATE, which works.
A fix with tests is proposed in #1112.
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start with MysqlAdapter::columnDefinitionSql(), then trace Column::toArray() and MysqlSchemaDialect::columnDefinitionSql() to understand where encoding is lost. Review the proposed fix in #1112 and verify coverage for CREATE TABLE, addColumn, and changeColumn using information_schema.COLUMNS. Done means encoding-only columns retain the requested character set without breaking enum/set behavior.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- mariadb, mysql, php
- Domain
- backend, databases
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Stale
- Clarity
- Clearly specified
- Newbie friendliness
- 35/100