Repository navigation
10.x: LowerBound() and UpperBound() in a query: == null becomes WHERE FALSE and ?? loses its COALESCE (port of #349) #354
Description
Problem
The same defect as #349, on release/10.x, which is what consumers on .NET 10 run (10.1.0 from nuget.org is where #349 was measured). BoundFunction in ValueRangesMethodCallTranslator.cs (line 639 there, identical to main before #353) declares lower() and upper() with argumentsPropagateNullability: [true]. So over a required column LowerBound() == null is WHERE FALSE, != null and HasValue filter nothing, ?? fallback loses its COALESCE, and on a nullable column the bound is tested with the column's own IS NULL. This holds for every range type and RangeSet.
Change
- Cherry-pick with
-x, in order: 2c44472 (test: register #349 and #350 as ExecutedQueryTests candidates #352, the twoExecutedQueryTestssweeps, their candidates and claim E70), then the fix: declare lower() and upper() NULL for a non-null range #353 fix commit (the flag, theRangeSetcolumns in the bound sweep, the two translation tests, the nine LowerBound() and UpperBound() in a query: == null becomes WHERE FALSE and ?? max loses its COALESCE, because lower() and upper() are declared NULL only for a NULL argument #349 candidates removed). A null range answers some extensions in memory and throws on others, and the answers it gives disagree with PostgreSQL on a NULL column #350's twelve candidates come along and stay, since A null range answers some extensions in memory and throws on others, and the answers it gives disagree with PostgreSQL on a NULL column #350 applies to 10.x as well. silent, so it ships in a 10.x minor, not a patch: it waits for Open 10.2.0 on release/10.x #331 to open 10.2.0.pr-hygiene.shfails the PR until the[10.2.0]section ofCHANGELOG.mdhas a### Changed answersrow linking the issue the PR closes. Link this issue and LowerBound() and UpperBound() in a query: == null becomes WHERE FALSE and ?? max loses its COALESCE, because lower() and upper() are declared NULL only for a NULL argument #349.docs/migration.mdgets## Changed answers in 10.2.0with the subsectionmainhas under## Migration from v10.x(### Check bound comparisons with null in queries). When the 10.2.0 sections are carried tomain, that heading would exist twice. The carry PR keeps one and links it from the other.- The package changelogs (EF Core, EF Core NodaTime) get the same row.
Done when
On release/10.x the bound sweep passes on PostgreSQL 16 and 18 with no #349 candidates left, and fails on the 234 range and RangeSet rows with the flag reverted. Close this issue by hand after the merge, since Closes only acts on main.
Status
PR #367 against release/10.x (after #366 opened 10.2.0): the two commits cherry-picked with -x, the changelog and migration entries rewritten for [10.2.0] / ## Changed answers in 10.2.0. Locally green on PostgreSQL 16 and 18 and on EF Core 10.0.13 / Npgsql 10.0.3. With the flag reverted the bound sweep fails on the 234 rows.
Activity
CaffeinatedCoder commentedon Oct 24, 2026
Fixed on
release/10.xby #367, merged as 7581ab7:lower()/upper()declaredargumentsPropagateNullability: [false], the bound sweep over the range andRangeSetcolumns of every family, and the### Changed answersrow in[10.2.0]with## Changed answers in 10.2.0indocs/migration.md. Ships in 10.2.0 (#358). Closed by hand, sinceClosesacts only onmain.