Skip to content

Typed filters double braces in string literals: x.Code != "{draft}" renders '{{draft}}' #70

Closed

Description

@CaffeinatedCoder

Problem

A string literal containing { or } in a typed filter is stored with the braces doubled, and filters never undo that: ResolveFilter deliberately has no {{ escape, because '{{1,2},{3,4}}' is a valid PostgreSQL array literal.

b.HasComplexIndex(x => x.Code, x => x.Code != "{draft}", indexName: "ix_code_final");

scaffolds

CREATE INDEX "ix_code_final" ON ... ("Code") WHERE "Code" <> '{{draft}}';

On PostgreSQL SELECT '{draft}' <> '{{draft}}' is true, so rows whose code is {draft} are indexed, and constrained if the index is unique, while the model says they are excluded. No error at migrations add or at apply. The same goes for HasFilter<T>, AddComplexIndexFilter<T> and typed exclusion-constraint filters.

The doubling is right for typed expression indexes (NpgsqlLinqIndexTranslator.Translate), whose templates are unescaped when resolved; it is wrong for predicates, which go through the filter resolver.

Fix

Predicate literals keep their braces: TranslatePredicate must not escape braces inside string literals. ResolveFilter already skips everything inside single quotes, so an unescaped brace there can never be read as a placeholder.

Changed answer

A typed filter with a brace in a string literal re-creates its index or constraint on the first migrations add after upgrading. Ships in a minor with a ### Changed answers row.

Found by the 5.5.0 pre-tag analysis (code-analyst, PostgreSQL satellite). Present since typed filters shipped in 5.2.0.

Activity

  1. added this to the 5.5.0 milestone on Oct 19, 2026
  2. added
    bugSomething isn't working
    silentAnswers wrongly without throwing — fixes go in the next major, never deferred
    severity/mediumInconsistency or failure in a narrow scenario
    package/postgresqlAffects EFCore.ComplexIndexes.PostgreSQL
    area/indexesComplex-property, composite, unique and filtered indexes
    area/constraintsTemporal (WITHOUT OVERLAPS, PERIOD) and exclusion constraints
    on Oct 19, 2026
  3. added a commit that references this issue on Oct 20, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    area/constraintsTemporal (WITHOUT OVERLAPS, PERIOD) and exclusion constraintsarea/indexesComplex-property, composite, unique and filtered indexesbugSomething isn't workingpackage/postgresqlAffects EFCore.ComplexIndexes.PostgreSQLseverity/mediumInconsistency or failure in a narrow scenariosilentAnswers wrongly without throwing — fixes go in the next major, never deferred

    Projects

    No projects

      Milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions