Skip to content

Auto-generate foreign key constraint names when not provided#1041

Open
dereuromark wants to merge 4 commits into5.xfrom
fix-auto-generate-fk-constraint-name
Open

Auto-generate foreign key constraint names when not provided#1041
dereuromark wants to merge 4 commits into5.xfrom
fix-auto-generate-fk-constraint-name

Conversation

@dereuromark
Copy link
Member

Summary

  • Auto-generate foreign key constraint names when not provided in addForeignKey()
  • MySQL and SQLite adapters now use the pattern {table}_{columns} (matching PostgresAdapter and SqlserverAdapter behavior)

This fixes an issue where unnamed constraints could cause problems with CakePHP's ConnectionHelper::dropTables() which expects string constraint names. When no name was provided, MySQL would auto-generate one, but the schema reflection could store it with integer array keys, causing TypeError in TableSchema::getConstraint().

Fixes #1040

@dereuromark dereuromark added the bug label Mar 8, 2026
@dereuromark dereuromark force-pushed the fix-auto-generate-fk-constraint-name branch 2 times, most recently from c582b85 to 809abc9 Compare March 8, 2026 20:35
When a foreign key is added without an explicit name, the MysqlAdapter
and SqliteAdapter now generate a name following the pattern
'tablename_columnname' (e.g., 'articles_user_id' for a FK on the
user_id column in the articles table).

This matches the behavior of PostgresAdapter and SqlserverAdapter,
which already auto-generate FK names. This ensures constraint names
are always strings and prevents issues with constraint lookup methods
that expect string names.
Update the expected FK names in comparison files and schema dumps
to match the new auto-generated naming pattern (tablename_columnname).
@dereuromark dereuromark force-pushed the fix-auto-generate-fk-constraint-name branch from ed2b2d3 to 629c85e Compare March 8, 2026 21:02
The FK constraint name change from auto-generated (ibfk_N) to
explicit (table_column) also affects the implicit index MySQL
creates for FKs. Update comparison files to reflect the index
rename from user_id to articles_user_id.
With the FK naming changes, both the lock file and database have
the index named articles_user_id, so no diff is needed for this
index. Remove the index rename operations that were incorrectly
added.
if ($foreignKey->getName()) {
$def .= ' CONSTRAINT ' . $this->quoteColumnName((string)$foreignKey->getName());
}
$constraintName = $foreignKey->getName() ?: ($tableName . '_' . implode('_', $foreignKey->getColumns()));
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does mysql have any length limits we have to be concerned about like with postgres?

Copy link
Member Author

@dereuromark dereuromark Mar 9, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I notice the existing PostgreSQL adapter autogenerate doesn't have explicit truncation either - it builds names like {table}_{columns}_fkey without length limiting.

The question is valid. MySQL has a 64-character limit for identifiers (including constraint names). The current implementation:

  $constraintName = $foreignKey->getName() ?: ($tableName . '_' . implode('_',  
  $foreignKey->getColumns()));                                                  

Could exceed 64 characters with long table names or multiple columns.

Copy link
Member Author

@dereuromark dereuromark Mar 9, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We could built this into #1042

For now this PR aims to fix the critical bug and the inconsistency first.
Having a too long index is usually seldom and would often require a custom naming anyway.

@dereuromark
Copy link
Member Author

Updated PR #1042 with database-specific limits:

  • MySQL: 61 chars (64 - 3)
  • PostgreSQL: 60 chars (63 - 3)
  • SQL Server: 125 chars (128 - 3)
  • SQLite: No limit

Copy link
Member

@markstory markstory left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Changing the name of indexes, will change the schema created by a migration history. What used to be automatically named by the database engine will now be given a different name by migrations which could cause index add, index drop/change operations to fail as the name of an index has changed.

I don't think we have a good solution for this, but it should be something we point out in the release notes as a potential upgrade issue.

@dereuromark
Copy link
Member Author

dereuromark commented Mar 9, 2026

After the merge of this and follow up, maybe I can invest some time into shimming possibilities?
Like if it doesnt find by name, maybe looking up by fields and see which one would likely match the most?
This could potentially mitigate of those issues.

  // Pseudocode for dropForeignKey fallback                                                                
  $fk = $this->findForeignKeyByName($name);                                                                
  if (!$fk) {                                                                                              
      $fk = $this->findForeignKeyByColumns($columns);                                                      
      // Log warning about name mismatch                                                                   
  }                                                                                                        

But that would only work if we can reverse engineer the columns from the up() statement.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

addForeignkey() without a constraint/name creates a constraint, which has a integer name

2 participants