Detect Duplicate Unique Indexes in Doctrine DBAL Schema SQL

“Duplicate key name” during a Doctrine schema round-trip can mean one database object was represented as both a unique constraint and a unique index. Do not run the generated DDL against production until you know whether your installed DBAL version emits both definitions.

Last updated: November 5, 2026.

<?php
$manager = $connection->createSchemaManager();
$table = $manager->introspectTableByUnquotedName('customer_email');
$statements = $connection->getDatabasePlatform()->getCreateTableSQL($table);

foreach ($statements as $statement) {
    echo $statement, ";
";
}

// Review the output: one logical unique key should be rendered once.

Run this only against a disposable test table created with a named unique key. Save the original DDL and compare it with the regenerated SQL before changing a migration.

Confirm the round-trip problem

Doctrine’s schema manager documentation explains that introspection builds an in-memory representation from database metadata. MySQL and MariaDB expose uniqueness through index metadata, while DBAL also models constraints. A version-specific mapping error can therefore render the same named object twice.

Create a minimal table containing one unique key, introspect it, and inspect every statement returned by getCreateTableSQL(). Also run composer show doctrine/dbal so the test report records the exact package version, PHP version, driver, and database version. A current Doctrine DBAL issue demonstrates this duplicate constraint/index shape on DBAL 4.5.0.

Keep migrations deterministic

Do not delete an existing production index merely to make generated SQL run. First test the latest compatible DBAL patch release and review its changelog. If the affected release has no verified fix, pin the last tested version temporarily and keep the failing round-trip as an automated regression test.

For a pending migration, define the desired unique key once in your migration source rather than accepting an unreviewed introspection diff. Compare the generated SQL in CI and fail when a constraint and index share the same normalized name and column list. Then apply the migration to an empty database and to a copy of the current schema.

After upgrading DBAL, repeat the test on every supported platform because MySQL and MariaDB metadata are not identical. Continue with Doctrine transaction recovery, safe SaaS migrations, and ALTER TABLE fundamentals.

Related Web Cheat Sheet guides

Sergey Kornilov

Sergey Kornilov