Skip to content

Changed answers: typed filters over Trim/Substring are re-created too, but the guide and changelog name only expression indexes #170

Closed

Description

@CaffeinatedCoder

Found by a pre-release sweep of main @ eff80b4 (lens: round-trip); reproduced by diffing a real v5.4.1 snapshot, and by a scratch test for the rendering. Holds the tag: the changelog section becomes each package's nuspec release notes, and those can't be changed after the push.

Problem

#72 changed how Trim, TrimStart, TrimEnd and Substring render in the typed translator. The translator serves typed expression indexes and also typed filters (NpgsqlTypedFilterExtensions, for indexes and exclusion constraints). The guide entry (Substring and Trim render as EF Core's queries, docs/upgrading.md:176-192) and the ### Changed answers row in CHANGELOG.md:30 mention typed expression indexes only.

Typed filters are translated at the declaration and stored as the filter string, so a 5.4.1 snapshot holds the old text, and the first migrations add re-creates the index under a changed filter. For Trim, that changes which rows the partial index covers. btrim("Code") <> '' included a row whose Code is only tabs or line breaks. btrim("Code", E' \t\n\r') <> '' leaves it out. A unique filtered index then stops checking those rows, and an exclusion constraint's typed filter is affected in the same way.

Model

b.HasComplexIndex(x => x.Title, x => x.Code.Trim() != "", indexName: "ix_title_trim");
  • v5.4.1 snapshot: filter=btrim("Code") <> ''. v5.4.1's translator renders btrim({target}); see NpgsqlLinqIndexTranslator.cs:166 at that tag.
  • eff80b4: filter=btrim("Code", E' \t\n\r') <> '', from a scratch test using CreateIndexOperation.Filter.
  • The upgrade diff from the real 5.4.1 snapshot gives DropIndex ix_title_trim and then CreateIndex ix_title_trim … filter=btrim("Code", E' \t\n\r') <> ''. No guide section covers that pair. The gate on 934ec55 had the same operation in its output, so "matched the docs exactly" missed it.

The code change is intended and correct, since it matches EF's query. What's missing is the announcement of a changed answer.

Change

In the guide entry and the changelog row (root and PostgreSQL package), say "typed expression indexes and typed filters (on indexes and exclusion constraints)". Add what the change does to the rows a filtered unique index or exclusion constraint checks: rows that are only whitespace now fall outside a Trim() != "" filter.

Test

An upgrade test that diffs a 5.4.1 snapshot and asserts each operation maps to a guide section. At minimum, a test that pins the typed-filter rendering of Trim() next to the typed expression-index one.

Activity

  1. added this to the 5.5.0 milestone on Oct 20, 2026
  2. added
    documentationImprovements or additions to documentation
    package/postgresqlAffects EFCore.ComplexIndexes.PostgreSQL
    area/indexesComplex-property, composite, unique and filtered indexes
    area/expression-indexesExpression, typed LINQ and JSON member indexes
    area/constraintsTemporal (WITHOUT OVERLAPS, PERIOD) and exclusion constraints
    on Oct 20, 2026
  3. CaffeinatedCoder commented on Oct 20, 2026

    @CaffeinatedCoder
    OwnerAuthor

    Measured on a real v5.4.1 snapshot (typed filters over each method on HasComplexIndex, HasComplexCompositeIndex, HasExpressionIndex and HasExclusionConstraint): the first migrations add drops and re-creates every one of them, 8 indexes and 2 exclusion constraints, 21 operations including CREATE EXTENSION IF NOT EXISTS btree_gist. A ToLower filter is untouched and the second diff is empty. Substring filters cover the same rows (substr = substring on PostgreSQL 18); trimmed filters can gain rows (= 'abc' now covers E'abc\t') or lose them (<> '' drops tabs-only values). Fixed in #173.

  4. 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/expression-indexesExpression, typed LINQ and JSON member indexesarea/indexesComplex-property, composite, unique and filtered indexesdocumentationImprovements or additions to documentationpackage/postgresqlAffects EFCore.ComplexIndexes.PostgreSQL

    Projects

    No projects

      Milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions