Skip to content

fix: follow a text set operation through a scalar subquery in the guard's rewrite (#505) - #508

Merged
CaffeinatedCoder merged 2 commits into
mainfrom
fix/505-scalar-subquery-text-set
Oct 26, 2026
Merged

CaffeinatedCoder merged 2 commits into
mainfrom
fix/505-scalar-subquery-text-set

Conversation

@CaffeinatedCoder

Copy link
Copy Markdown
Owner

Closes #505

Problem

ValueSetSqlGuard decides in two places whether a value may be an unsorted text set, and the two disagreed on a scalar subquery.

On {a,c}, ….Select(o => o.Tags.Union({a,b})).FirstOrDefault() == {a,b,c} dropped the row. (r.OptionalTags ?? subquery).Count counted {a,c,a,b} as 4. 10.3.0 threw for the same query, so on 10.x this is a regression in the unreleased 10.4.0. It shipped in no release.

Change

  • CanonicalExpander.IsDerived judges a scalar subquery by its one projection, as the walker does (ValueSetSqlGuard.cs).
  • Seed guard-rewrite-misses-a-scalar-subquery, caught by UnionCanonicalityTests.

No changelog row: the wrong answer shipped in no release. On main, #485's text forms are in 11.0.0-preview.1's Added, which this makes true.

Tests

  • UnionCanonicalityTests (integration) gains two tests, each comparing the server's answer with LINQ over the loaded rows:
    • EqualityOnAUnionFromAScalarSubquery_AnswersAsMemoryDoes: ==, != and == behind a COALESCE, through a correlated scalar subquery, and Add, Intersect and Except the same way.
    • CountOverAUnionFromAScalarSubquery_CountsTheDistinctElements_AsMemoryDoes: Count behind a COALESCE.
  • Proved by reverting: with the arm removed, both new tests fail on the answer (server count against memory's). dotnet run tools/oracles.cs seeds guard-rewrite reports the seed caught (2 of 17 tests fail).
  • Full suite green on PostgreSQL 16: unit, conventions, ASP.NET Core, both EF translation projects and both integration projects.

🤖 Generated with Claude Code

CaffeinatedCoder and others added 2 commits October 26, 2026 17:50
…rd's rewrite (#505)

The guard's walker treats a scalar subquery that projects a text Union,
Add, Intersect or Except as derived, and so lets ==, != and Count over it
through, trusting the expander to write them as mutual containment and
count(DISTINCT). The expander's IsDerived had no arm for a scalar
subquery, so the raw = and cardinality reached the server and compared
the unsorted array_cat: {a,c} ∪ {a,b} == {a,b,c} dropped the row, and
(… ?? subquery).Count counted {a,c,a,b} as 4. 10.3.0 refused the query.

IsDerived now judges a scalar subquery by its one projection, as the
walker does. UnionCanonicalityTests runs ==, != and Count over COALESCE
through a correlated scalar subquery, and Add, Intersect and Except the
same way, against memory over the loaded rows; with the arm removed
both new tests fail on the answer.

Found-By: RV
Defect-Class: SQL

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@CaffeinatedCoder CaffeinatedCoder added this to the 10.4.0 milestone Oct 26, 2026
@CaffeinatedCoder CaffeinatedCoder added bug Something isn't working silent Answers wrongly without throwing — fixes go in the next major, never deferred area/value-sets Value set families and wrapper arities package/efcore Affects CodoMetis.ValueRanges.EFCore.PostgreSQL severity/medium Inconsistency or failure in a narrow scenario labels Oct 26, 2026
@CaffeinatedCoder
CaffeinatedCoder merged commit 3900cc0 into main Oct 26, 2026
14 of 16 checks passed
@CaffeinatedCoder
CaffeinatedCoder deleted the fix/505-scalar-subquery-text-set branch October 26, 2026 17:24
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area/value-sets Value set families and wrapper arities bug Something isn't working package/efcore Affects CodoMetis.ValueRanges.EFCore.PostgreSQL severity/medium Inconsistency or failure in a narrow scenario silent Answers wrongly without throwing — fixes go in the next major, never deferred

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Text set ==, != and Count through a scalar subquery skip #485's rewrite and compare the unsorted array_cat

1 participant