Skip to content

Add conflict resolution for auto-generated FK constraint names#1042

Merged
dereuromark merged 5 commits intofix-auto-generate-fk-constraint-namefrom
fix-fk-name-conflict-resolution
Mar 11, 2026
Merged

Add conflict resolution for auto-generated FK constraint names#1042
dereuromark merged 5 commits intofix-auto-generate-fk-constraint-namefrom
fix-fk-name-conflict-resolution

Conversation

@dereuromark
Copy link
Copy Markdown
Member

@dereuromark dereuromark commented Mar 9, 2026

Summary

I feel like when we now auto generate them instead of the db, we should do this, as there could be conflict somewhere along the migrations.

  • When auto-generating FK constraint names, check if the name already exists
  • Append counter suffix (_2, _3, etc.) if a conflict is detected
  • Prevents duplicate constraint name errors in edge cases

UPDATE:
Also now respects technical max length limit per constraint using truncate.
We could also add hashing instead of just truncate + counter suffix.

This builds on #1041 which added auto-generation of FK constraint names.

Example

// First FK: generates "articles_user_id"
$table->addForeignKey('user_id', 'users', 'id');

// Second FK on same column (unusual but possible): generates "articles_user_id_2"
$table->addForeignKey('user_id', 'admins', 'id');

Changes

All 4 adapters updated with getUniqueForeignKeyName() method:

  • MysqlAdapter
  • SqliteAdapter
  • PostgresAdapter
  • SqlserverAdapter

When auto-generating FK constraint names, check if the name already
exists and append a counter suffix (_2, _3, etc.) if needed.

This prevents duplicate constraint name errors when multiple FKs
are created on the same columns with different references.
Limit auto-generated foreign key constraint names to 125 characters
to ensure the final name (including potential _XX counter suffix)
stays within 128 characters. This prevents identifier length errors
on databases with strict limits (MySQL: 64, PostgreSQL: 63).
- MySQL: 61 chars (64 limit - 3 for _XX suffix)
- PostgreSQL: 60 chars (63 limit - 3 for _XX suffix)
- SQL Server: 125 chars (128 limit - 3 for _XX suffix)
- SQLite: No limit needed
Each adapter now defines its database-specific identifier length limit
as a class constant, making the code more self-documenting.
@dereuromark
Copy link
Copy Markdown
Member Author

Should the common code by moved to Util class? Regarding the

public static function generateUniqueConstraintName(                                                     
      string $baseName,                                                                                    
      array $existingNames,                                                                                
      int $maxLength                                                                                       
  ): string        

@dereuromark dereuromark requested a review from markstory March 9, 2026 20:33
@dereuromark
Copy link
Copy Markdown
Member Author

dereuromark commented Mar 9, 2026

@markstory I would squash-merge this first into the open PR, then we can merge the whole thing squashed for easier up/down merges.

@dereuromark dereuromark merged commit fc56383 into fix-auto-generate-fk-constraint-name Mar 11, 2026
14 checks passed
@dereuromark dereuromark deleted the fix-fk-name-conflict-resolution branch March 11, 2026 17:53
dereuromark added a commit that referenced this pull request Mar 11, 2026
Add upgrade documentation explaining the new auto-generated FK constraint
naming behavior introduced in #1041 and #1042, including:
- New consistent naming pattern across all adapters
- Potential impact on existing migrations with rollbacks
- Conflict resolution with counter suffixes
- Database-specific name length limits
dereuromark added a commit that referenced this pull request Mar 11, 2026
* Auto-generate foreign key constraint names when not provided

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 test comparison files for new FK naming convention

Update the expected FK names in comparison files and schema dumps
to match the new auto-generated naming pattern (tablename_columnname).

* Update test comparison files with index rename operations

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.

* Remove unnecessary index operations from comparison file

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.

* Add conflict resolution for auto-generated FK constraint names (#1042)

* Add conflict resolution for auto-generated FK constraint names

When auto-generating FK constraint names, check if the name already
exists and append a counter suffix (_2, _3, etc.) if needed.

This prevents duplicate constraint name errors when multiple FKs
are created on the same columns with different references.

* Remove unused variable

* Truncate FK constraint names to max 128 characters

Limit auto-generated foreign key constraint names to 125 characters
to ensure the final name (including potential _XX counter suffix)
stays within 128 characters. This prevents identifier length errors
on databases with strict limits (MySQL: 64, PostgreSQL: 63).

* Use database-specific identifier length limits

- MySQL: 61 chars (64 limit - 3 for _XX suffix)
- PostgreSQL: 60 chars (63 limit - 3 for _XX suffix)
- SQL Server: 125 chars (128 limit - 3 for _XX suffix)
- SQLite: No limit needed

* Use IDENTIFIER_MAX_LENGTH class constant for clarity

Each adapter now defines its database-specific identifier length limit
as a class constant, making the code more self-documenting.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant