Repository navigation
A temporal foreign key's TPC copy on a long derived table is not shortened, and a 5.4.1 model fails at the first migrations add #168
Description
Found by a pre-release sweep of main @ eff80b4 (lenses: round-trip, sibling-parity); reproduced in a scratch test. Decide before the tag: either shorten the copy or document the refusal. Shortening it later changes the answer for models that work around it.
Problem
#145 shortens the default name of a TPC copy that runs past the identifier limit for complex indexes, exclusion constraints and temporal constraints, but not for temporal foreign keys. In ApplyTemporalConstraints (NpgsqlComplexIndexMigrationsModelDiffer.cs:697-698), the FK calls ThrowIfIdentifierTooLong(…, foreignKey.DependentTable, "name") without the copy's Origin, and foreign keys have no ShortenConstraintCopyNames pass. Exclusion constraints have one at :1191 and temporal constraints at :1331. Commit f4986ed says this was on purpose ("Every other default name keeps the 5.2.0 refusal").
docs/upgrading.md says otherwise. The TPC entry covers "a complex index, exclusion constraint, temporal constraint or temporal foreign key" and says a copy name past the limit "is shortened as EF Core shortens its own". Its list of what "fail[s] at migrations add now" doesn't include this case. A 5.4.1 model that put the FK on the base table only now fails at the first migrations add after the upgrade, with no model change.
Model
mb.Entity<FkRoom>(b => { b.ToTable("rooms"); b.HasTemporalConstraint(x => x.RoomId, x => x.Available); });
mb.Entity<FkBooking>(b =>
{
b.UseTpcMappingStrategy();
b.ToTable("bookings");
b.HasTemporalForeignKey<FkBooking, FkRoom>(x => x.RoomId, x => x.During, x => x.RoomId, x => x.Available);
});
mb.Entity<FkCorpBooking>().ToTable("corporate_bookings_for_customer_accounts_region_xyz");MigrationHarness.NpgsqlDiff(null, model) on eff80b4 throws:
InvalidOperationException: The temporal foreign key name 'FK_corporate_bookings_for_customer_accounts_region_xyz_rooms_RoomId_During' on table 'corporate_bookings_for_customer_accounts_region_xyz' is 74 bytes long, but the provider allows at most 63. … Give the declaration an explicit, shorter name
The advice works: FK names are unique per table, and with name: "fk_booking_room" the whole upgrade applied on PostgreSQL 18 (23503 on the derived table). The failure is loud, and a workaround exists.
Change
Pick one before the tag:
- Pass
foreignKey.Originand shorten FK copies per table, among the table's constraints, as the other kinds are shortened. - Keep the refusal, name the case in the upgrade guide's "fail at
migrations addnow" list, and say in the message that the long name comes from the TPC copy.
Test
For the model above: NpgsqlDiff(null, model) succeeds with a FK_…~ name on the derived table, or the guide lists the refusal and a test pins the message.
Activity
CaffeinatedCoder commentedon Oct 20, 2026
Decided in #176: the first option. A temporal foreign key's default-named copy past the limit is shortened among its table's constraints (
FK_…~), on both diff sides. A 5.4.1 snapshot upgrade adds the copies, and on PostgreSQL 18 the long table rejects with 23503 under the shortened name.Merge pull request #176 from CaffeinatedCoder/fix/167-168-tpc-copy-names