Repository navigation
Release 5.4.0 - #32
Merged
Merged
Conversation
PostgreSQL uses an expression index only for a query whose expression
matches it. Every JSON member was rendered as "doc" -> 'A' ->> 'B' text
with no cast, which matched Npgsql's query translation only for a
top-level string: indexes on nested members (#>> '{A,B}' in the query)
or typed members (CAST(... AS integer)) applied, enforced uniqueness and
served no query. Members now render exactly as NpgsqlQuerySqlGenerator
renders them (identical in Npgsql 10 and 11), in index parts, typed
expression indexes and index filters.
Parts are resolved at diff time on both sides, so a rendering change
alone never produces a migration. Every declaration now writes
CustomIndex:RenderingVersion onto the model; a snapshot without it is
rendered by the old rules, which turns the fix into one drop-and-create
per affected index. Default names come from the path (NameToken), and
ResolvedIndexPart equality ignores the derived metadata, so nothing is
renamed or rebuilt without a reason. Exclusion constraint filters keep
the old rendering.
Date and time members stay text (their casts are not IMMUTABLE); a
non-unique index leading with one is rejected at migrations add.
Guards: rendering asserted against EF's own ToQueryString(); rollout on
a real compiled snapshot; EXPLAIN on PostgreSQL 18 with seqscan off.
Reverting each of the six parts of the fix turns its tests red.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
PostgreSQL keeps constraint names unique per table and index names unique per schema, including the index behind every primary key, unique, exclusion and temporal constraint. Each kind was checked against its own kind on one table at most, so two temporal constraints with one name, a temporal foreign key named like a foreign key on its table, or a temporal constraint named like an index elsewhere in the schema scaffolded cleanly and failed at apply time (42710, 42P07). Against an exclusion constraint it did not fail at all: the exclusion ADD is preceded by DROP CONSTRAINT IF EXISTS, so the same-named temporal constraint was dropped. Confirmed on PostgreSQL 18: the migration applied clean and only the EXCLUDE constraint remained. ValidateNamesAcrossKinds runs last over the target model's complex indexes, exclusion, temporal and temporal FK constraints plus EF's own indexes, keys, foreign keys and check constraints, and reports a collision when one party is this package's. The snapshot side stays diffable. Removing the check fails the 8 collision tests; the 4 tests for legitimate reuse pass either way, as they guard against the check growing too strict. Co-Authored-By: Claude Opus 5.5 <[email protected]>
Found by the migration-safety review of this release. A json/jsonb
scalar inside a ToJson document (a JsonDocument, say) rendered as
CAST("doc" ->> 'Raw' AS jsonb), while Npgsql's query translation reads
it as "doc" -> 'Raw' (its NpgsqlJsonTypeMapping case, checked before the
string case). The index would not match the query, and the text round
trip fails CREATE INDEX on any document holding a JSON string there.
Reverting the check makes the new test fail with the CAST form.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes to what 5.x already ships, found while testing the packages against EF Core 11 rc.1: JSON-member indexes that no query could use, and constraint names that only failed — or quietly vanished — when the migration was applied. The previously planned 5.4.0 scope (generic descriptor differ, delete constraint) moves to 5.5.0.
Changes
JSON-member indexes are written the way Npgsql's queries read the member (
0b5a8d1,127ff6d)"doc" -> 'A' ->> 'B'text with no cast, which matched only a top-level string. Indexes on nested members (#>> '{A,B}'in the query) or typed members (CAST(… AS integer)) applied, enforced uniqueness, and served no query.NpgsqlQuerySqlGeneratorrenders them (identical in Npgsql 10 and 11):->>/#>>, a cast to the store type unless it is a string mapping,decode(…, 'base64')forbytea,jsonbfor a primitive collection or ajson/jsonbmember. This applies to index parts, typed expression indexes and index filters; exclusion constraint filters keep their rendering.CustomIndex:RenderingVersiononto the model; a snapshot without it is rendered by the old rules, so each affected index is dropped and re-created once, under its existing name.ResolvedIndexPartequality ignores the new internal metadata, so identical SQL never rebuilds.migrations add; unique ones are allowed.Names checked across kinds on PostgreSQL (
3c8e11b, roadmap D0)DROP CONSTRAINT IF EXISTSremoved a same-named temporal constraint. Confirmed on PostgreSQL 18: the migration applied clean and only theEXCLUDEremained.Consumer-visible effects
migrations addrebuilds the affected JSON-member indexes. Until that migration exists,has-pending-model-changesreports changes andMigrate()raises EF Core's pending-model-changes error.IsCreatedConcurrently()before generating that migration. The custom generator rendersCONCURRENTLYoutside a transaction.HasAnnotation("CustomIndex:RenderingVersion", 2)line, which causes no DDL on its own.migrations add: a non-unique index leading with a date/time JSON member, and name collisions.Verification
net10.0, including 9 against PostgreSQL 18.ux_ig_tenants_city is not used: Seq Scan.ToQueryString()for every member type, so a future Npgsql translation change fails a test.EXPLAINwithenable_seqscan = offshows EF's queries using nested, int, decimal, bool and enum member indexes.dotnet pack -c Releasevalidates cleanly against the 5.3.0 baseline.migration-safety-reviewwas run over the branch. It found the jsonb-member case (127ff6d) and confirmed that the stock generator still fails loudly via the sentinel, that the marker alone yields zero operations, and thatreal,character(1),smallint,interval,timestamptzandtimeline up with the query translation.Not in this PR
MigrationsModelDifferctor parameter is EF 12, not 11; the[Obsolete]temporal constants item is moot (the class is internal and the generator still reads them for pre-5.0.2 migrations); and the 5.4.0/5.5.0 split.🤖 Generated with Claude Code