Skip to content

Release 5.4.1 - #33

Merged
CaffeinatedCoder merged 3 commits into
mainfrom
release/5.4.1
Sep 22, 2026
Merged

CaffeinatedCoder merged 3 commits into
mainfrom
release/5.4.1

Conversation

@CaffeinatedCoder

@CaffeinatedCoder CaffeinatedCoder commented Sep 22, 2026 •

Copy link
Copy Markdown
Owner

One fix, to the index-name check. Both core checks compared names per table on every provider. On SQLite, index names are unique across the whole database, so a name reused on another table scaffolded cleanly and failed when the migration was applied. The 5.4.0 PR listed this under "Not in this PR".

Changes

Index names are checked in the provider's namespace (c26c169)

  • SQLite rejects a second CREATE INDEX under a name used on any other table: index ix_name already exists. Confirmed with sqlite3 3.54.0 and with the engine bundled with Microsoft.Data.Sqlite.
  • EF's SQLite provider keeps a configured schema in the model but leaves it out of the DDL. So the namespace is the whole database, not the schema, and a per-schema check would still miss north.a against south.b.
  • New public enum IndexNameScope { Table, Schema, Database } and a protected virtual IndexNameScope on CustomMigrationsModelDiffer. ValidateUniqueIndexNames and ValidateNoNativeIndexNameCollision both group by it.
  • Core default Database: SQLite's rule, and the widest scope. The core also serves providers without a satellite. Too wide costs a rename the database did not need; too narrow approves a migration that fails when applied.
  • SQL Server: overrides to Table. Reusing a name across tables stays allowed.
  • PostgreSQL: overrides to Schema. The core now reports a clash between two plain indexes first. ValidateNamesAcrossKinds still covers keys, the indexes behind constraints, and a table in the default schema against one naming public, which only it normalizes. Each collision throws once, from whichever check sees it first.
  • Only collisions involving this package's declarations are reported, and only in the target model, so a snapshot holding a clash stays diffable.
  • Version 5.4.1, package-validation baseline 5.4.0.

Roadmap: the planned 5.4.0 features move to 5.5.0 (docs commit)

  • 5.4.0 shipped only fixes. The generic descriptor differ (4.1), the PostgreSQL delete constraint (4.2) and the small items (4.3) move to 5.5.0 unchanged. D0 and step 0 are marked shipped.
  • 4.0 now notes the 5.4.1 index-name scope, which the generic differ's name validation will have to honour for every kind it takes over.

Roadmap: two corrections (635ce57)

  • The sixth MigrationsModelDiffer constructor parameter (logger) is on EF Core's main, which is 12.0.0 alpha. release/11.0 keeps five parameters, so EF Core 11 needs no #if around the differ constructor. Sections 1, 3 (D7) and 8 are corrected.
  • The temporal annotation constants that 4.1 planned to mark [Obsolete] and remove in 6.0.0 are internal, so there's no API break to schedule. They're also still in use: NpgsqlComplexIndexSqlGenerator reads them to render migrations scaffolded before 5.0.2. Removing them would give a fresh database built from those migrations a plain UNIQUE or foreign key without the period, and it would apply cleanly. The item is gone from 4.1, 4.3 and the sequence, and the 6.0 migration guide now keeps the keys.

Consumer-visible effects

  • SQLite: a complex index name reused on another table, even in another configured schema, now fails at migrations add and names both tables. The migration could never have applied.
  • Core only, with a provider that scopes names per table (e.g. MySQL): the same model now asks for a rename the database would have accepted. With the differ registered at runtime (UseComplexIndexes()), Migrate() raises the same error until the rename is made.
  • SQL Server and PostgreSQL: nothing is newly rejected. On PostgreSQL, a clash between two plain indexes across tables now gets the core's message, which names both declarations, instead of the cross-kind one.

Verification

  • Tests: 339 pass on net10.0, including the PostgreSQL 18 integration tests. IndexNameCollisionTests diffs the same models under all three scopes.
  • Guards: the source fix was reverted once, keeping the tests.
    • Reverting fails 5 tests: the 3 SQLite rejections ("no exception was thrown") and 2 PostgreSQL message assertions, which 5.4.0 answered from the cross-kind check.
    • Dropping the SQL Server override fails its 2 reuse tests. Dropping the PostgreSQL override fails "may share a name across schemas", plus 3 message assertions. Defaulting the core to Schema fails "tables in different schemas still share one index namespace".
    • The SQLite engine test and the public-schema test pass either way. They document the premise and the cross-kind check's remaining job.
  • Delivery: dotnet pack -c Release validates cleanly against the 5.4.0 baseline restored from nuget.org, with IndexNameScope as an additive API. The consumer smoke test passed in this PR's CI.
  • Review: migration-safety-review was run over the change. It found the Migrate() effect above, which is now in the changelog.

Not in this PR

  • A complex index named like the primary key or an alternate key on its own SQL Server table is not caught by any check. Not verified; it would fail at apply, loudly.

🤖 Generated with Claude Code

CaffeinatedCoder and others added 3 commits September 22, 2026 21:24
Both core name checks grouped by table, schema and name, which is how
SQL Server scopes index names but not SQLite: SQLite keeps them unique
across the database and leaves configured schemas out of the DDL. Two
tables sharing an index name - two complex indexes, or a complex and a
native one - scaffolded cleanly and failed when applied ("index ...
already exists"), and IndexNameCollisionTests asserted that exact model
was fine.

IndexNameScope (Table, Schema, Database) is now a protected virtual on
the core differ, applied by ValidateUniqueIndexNames and
ValidateNoNativeIndexNameCollision. The core default is Database,
SQLite's rule and the widest scope, since the core also serves providers
without a satellite; SQL Server overrides to Table, PostgreSQL to
Schema. On PostgreSQL the core now reports plain index clashes first;
ValidateNamesAcrossKinds still covers keys, constraint-backed indexes
and an unset schema against an explicit 'public'. Only collisions with
one of this package's declarations are reported, in the target model
only.

Reverting the source fails 5 tests: the 3 SQLite rejections (no
exception thrown) and 2 PostgreSQL message assertions, which 5.4.0
answered from the cross-kind check. Dropping the SQL Server or
PostgreSQL override, or defaulting the core to Schema, each fails the
tests pinning it.

Version 5.4.1, package-validation baseline 5.4.0.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
5.4.0 shipped as a fix release: the 4.0 name registry went out, the
generic descriptor differ, the PostgreSQL delete constraint and the
small items riding along did not. The roadmap still scheduled them for
5.4.0. They move to 5.5.0 unchanged, D0 and step 0 are marked shipped,
and 4.0 notes the 5.4.1 index-name scope, which the generic differ's
name validation has to honour.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
The sixth MigrationsModelDiffer constructor parameter (logger) is on
efcore main, which is 12.0.0 alpha; release/11.0 keeps five. EF Core 11
needs no #if around the differ constructor, so section 1, the D7 list of
build differences and the section 8 bullet now say so.

The temporal annotation constants 4.1 meant to mark [Obsolete] and
remove in 6.0.0 are internal, so there is no CP0002 break to schedule,
and not dead: NpgsqlComplexIndexSqlGenerator reads them to render
migrations scaffolded before 5.0.2. Removing them would let a fresh
database built from those migrations apply a plain UNIQUE or foreign
key without the period. The item is dropped from 4.1, 4.3 and the
sequence, and the 6.0 migration guide keeps the keys with the overrides
that read them.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
@CaffeinatedCoder
CaffeinatedCoder merged commit 85a9e2d into main Sep 22, 2026
9 checks passed
@CaffeinatedCoder
CaffeinatedCoder deleted the release/5.4.1 branch September 22, 2026 19:36
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