Skip to content

'PdoSessionHandler::configureSchema' sets collation 'utf8mb4_bin' without the corresponding charset, causing migration failures#89

Closed
samy-mahmoudi wants to merge 1 commit intosymfony:6.4from
samy-mahmoudi:fix-a-bug-in-the-pdo-session-handler
Closed

'PdoSessionHandler::configureSchema' sets collation 'utf8mb4_bin' without the corresponding charset, causing migration failures#89
samy-mahmoudi wants to merge 1 commit intosymfony:6.4from
samy-mahmoudi:fix-a-bug-in-the-pdo-session-handler

Conversation

@samy-mahmoudi
Copy link
Contributor

Symfony version(s) affected

6.4 (and likely all versions since configureSchema() was introduced)

Description

When using PdoSessionHandler with Doctrine and running make:migration, the generated migration fails with:

SQLSTATE[42000]: Syntax error or access violation: 1253 COLLATION 'utf8mb4_bin' is not valid for CHARACTER SET 'utf8mb3'

This occurs because PdoSessionHandler::configureSchema() sets the collation to utf8mb4_bin but does not set the corresponding charset table option.

How to reproduce

  1. Configure the Doctrine DBAL with charset: utf8 (or leave it unset in DBAL 3.x):

    doctrine:
        dbal:
            charset: utf8
  2. Configure PdoSessionHandler for session storage:

    framework:
        session:
            handler_id: Symfony\Component\HttpFoundation\Session\Storage\Handler\PdoSessionHandler
    
    services:
       Symfony\Component\HttpFoundation\Session\Storage\Handler\PdoSessionHandler:
          arguments:
             - 'mysql:host=%database_host%;port=%database_port%;dbname=%database_name%'
             - { db_username: '%database_user%', db_password: '%database_password%', lock_mode: 2 }
  3. Run php bin/console make:migration

  4. Inspect the generated migration — it contains an incompatible charset-collation combination:

    DEFAULT CHARACTER SET utf8 COLLATE `utf8mb4_bin`
  5. Run php bin/console doctrine:migrations:migrate

  6. Result: Migration fails with charset-collation mismatch error.

Root Cause Analysis

In PdoSessionHandler::configureSchema():

case 'mysql':
    // ...
    $table->addOption('collate', 'utf8mb4_bin');  // <- Sets collation only
    $table->addOption('engine', 'InnoDB');
    break;

The method sets collate but not charset.

The utf8mb4_bin collation is bound to the utf8mb4 character set: no other character set produces a valid MySQL statement. While one might aim for generality and/or optimality by omitting the charset and letting MySQL infer it from the collation, the Doctrine DBAL may supply a charset from its configuration hierarchy, producing an invalid combination like:

DEFAULT CHARACTER SET utf8 COLLATE utf8mb4_bin

Charset Resolution Hierarchy

When building table options, the Doctrine DBAL resolves the charset in this order (highest priority first):

Priority Source Description
1 $table->addOption('charset', ...) Per-table override (e.g., in configureSchema())
2 doctrine.dbal.default_table_options.charset Explicit Doctrine config
3 doctrine.dbal.charset Connection charset (copied to defaultTableOptions by AbstractSchemaManager::createSchemaConfig())
4 Hardcoded default 'utf8' DBAL 3.x only; removed in DBAL 4.x

Behavior Matrix: The Doctrine DBAL 3.x

In DBAL 3.x, if no charset is configured at any level, the hardcoded default 'utf8' is used and a DEFAULT CHARACTER SET clause is always emitted.

Case dbal.charset default_table_options.charset Handler sets charset? Effective Charset Collation MySQL Statement
1 not set not set No utf8 (hardcoded) utf8mb4_bin Invalid
2 not set not set Yes (utf8mb4) utf8mb4 utf8mb4_bin Valid
3 not set utf8mb4 No utf8mb4 utf8mb4_bin Valid
4 not set utf8mb4 Yes utf8mb4 utf8mb4_bin Valid
5 utf8 not set No utf8 (copied) utf8mb4_bin Invalid
6 utf8 not set Yes (utf8mb4) utf8mb4 utf8mb4_bin Valid
7 utf8 utf8mb4 No utf8mb4 utf8mb4_bin Valid
8 utf8 utf8mb4 Yes utf8mb4 utf8mb4_bin Valid
9 utf8mb4 not set No utf8mb4 (copied) utf8mb4_bin Valid
10 utf8mb4 not set Yes utf8mb4 utf8mb4_bin Valid

Behavior Matrix: The Doctrine DBAL 4.x

In DBAL 4.x, the hardcoded 'utf8' default was removed. If no charset is configured at any level, no DEFAULT CHARACTER SET clause is emitted, allowing MySQL to infer the charset from the collation.

Case dbal.charset default_table_options.charset Handler sets charset? Effective Charset Collation MySQL Statement
1 not set not set No none utf8mb4_bin Valid (MySQL infers)
2 not set not set Yes (utf8mb4) utf8mb4 utf8mb4_bin Valid
3 not set utf8mb4 No utf8mb4 utf8mb4_bin Valid
4 not set utf8mb4 Yes utf8mb4 utf8mb4_bin Valid
5 utf8 not set No utf8 (copied) utf8mb4_bin Invalid
6 utf8 not set Yes (utf8mb4) utf8mb4 utf8mb4_bin Valid
7 utf8 utf8mb4 No utf8mb4 utf8mb4_bin Valid
8 utf8 utf8mb4 Yes utf8mb4 utf8mb4_bin Valid
9 utf8mb4 not set No utf8mb4 (copied) utf8mb4_bin Valid
10 utf8mb4 not set Yes utf8mb4 utf8mb4_bin Valid

Summary

DBAL Version Bug Cases Description
3.x Cases 1 & 5 Hardcoded default + charset propagation from connection
4.x Case 5 only Charset propagation from connection (hardcoded default removed)

Proposed Solution

Since utf8mb4_bin is bound to utf8mb4, explicitly setting the charset ensures valid MySQL in all cases:

$table->addOption('charset', 'utf8mb4');
$table->addOption('collate', 'utf8mb4_bin');

Workarounds

Users can work around this by either:

  1. Configuring default_table_options in the Doctrine DBAL to set a compatible charset:

    doctrine:
        dbal:
            default_table_options:
                charset: utf8mb4
                collate: utf8mb4_unicode_ci
  2. Manually editing the generated migration to change the charset to utf8mb4

  3. Using $handler->createTable() directly instead of make:migration (this method uses raw SQL and lets MySQL infer the charset from the collation)

Related Issues

Additional Context

  • The utf8mb4 charset has been the recommended MySQL default since MySQL 5.5.3 (2010) and is required for full Unicode support (including emojis).
  • The Symfony documentation shows SQL examples with COLLATE utf8mb4_bin but does not mention the Doctrine charset requirement.
  • The createTable() method in PdoSessionHandler uses raw SQL (COLLATE utf8mb4_bin without explicit CHARACTER SET) and works correctly because MySQL infers the charset. The configureSchema() method, however, goes through the Doctrine DBAL which may add an explicit CHARACTER SET clause.

…h the Doctrine DBAL

| Q             | A
| ------------- | ---
| Branch?       | 6.4
| Bug fix?      | yes
| New feature?  | no
| Deprecations? | no
| License       | MIT

PdoSessionHandler::configureSchema() was setting `utf8mb4_bin` as the collation
without an explicit charset. The Doctrine DBAL may supply a charset from its
configuration hierarchy, producing an invalid combination like
`DEFAULT CHARACTER SET utf8 COLLATE utf8mb4_bin`.

The `utf8mb4_bin` collation is bound to the `utf8mb4` character set: no other
character set produces a valid MySQL statement. While one might aim for
generality and/or optimality by omitting the charset and letting MySQL infer it,
explicitly setting `charset` to `utf8mb4` ensures valid MySQL in all cases.

A new test verifies that both charset and collate options are correctly set for
MySQL.
@samy-mahmoudi samy-mahmoudi changed the base branch from 8.1 to 6.4 January 20, 2026 14:51
@github-actions
Copy link

Thanks for your Pull Request! We love contributions.

However, you should instead open your PR on the main repository:
https://github.com/symfony/symfony

This repository is what we call a "subtree split": a read-only subset of that main repository.
We're looking forward to your PR there!

@github-actions github-actions bot closed this Jan 20, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant