Skip to content

docs: a query over a foreign NaN float answers in PostgreSQL's order; CHECK constraints keep it out - #241

Merged
CaffeinatedCoder merged 2 commits into
mainfrom
docs/232-float-check-constraints
Oct 18, 2026
Merged

CaffeinatedCoder merged 2 commits into
mainfrom
docs/232-float-check-constraints

Conversation

@CaffeinatedCoder

Copy link
Copy Markdown
Owner

Closes #232

Problem

docs/why.md and docs/efcore.md implied the finite-only rule protects every query. It protects every value the application creates, binds and loads. Over a row another tool wrote, a query that runs in the database answers in PostgreSQL's order ([0,NaN) matches Contains(1e308)), and only loading the entity throws (#179, part B).

Change

  • docs/efcore.md: a new Keep NaN and infinity out of a float column section. It states the boundary with the issue's three examples and gives one HasCheckConstraint per column kind. The "Only finite values load" bullet links to it. docs/why.md gets the same boundary in "Where is the model stricter than the database?". There are CHANGELOG Docs: lines (root and EF package).
  • Measured on PostgreSQL 16.15 and 18.1, and the reason the constraints differ by kind: the bounds check the issue proposes (lower(x) NOT IN ('NaN','Infinity','-Infinity') AND upper(x) …) is complete for a range but not for a multirange. A multirange's lower/upper are its outer bounds, and they are NULL beside an unbounded side, which a CHECK lets pass. So {(,-Infinity],[1,2)}, {(,-Infinity),[1,2)}, {[1,2),[Infinity,)}, {[1,2),(Infinity,)} and {(,0),[1,Infinity),[NaN,)} all pass it. The documented multirange constraint checks the text instead ("Bands"::text !~* '(nan|inf)'; a finite float never prints those letters). The array constraint is NOT ("Weights" && '{NaN,Infinity,-Infinity}').
  • Claims E60, E61 and W06, held by the new test.

Tests

  • New FloatCheckConstraintTests (integration, PostgreSQL 17 in the suite; the same corpus ran by hand on 16 and 18):
    • Constraints: it reads the three expressions out of the guide's code block, so the SQL a reader pastes is the SQL that runs. It applies them with HasCheckConstraint on float8 and float4 range, multirange and array columns through EnsureCreated. It requires every non-finite literal to fail with 23514 (22 per width, including the five multiranges above) and every finite, unbounded, empty and NULL value to be stored.
    • Boundary: without the constraints, Contains(1e308) over [0,NaN), a range containing a NaN double column, and Count over {1,NaN} answer in PostgreSQL's order, and loading throws ArgumentException.
  • Proof: with the issue's bounds check in place of the documented multirange expression, the test fails, because 10 rows are stored (5 multiranges × float8/float4).
  • Conventions 395, integration 258.

🤖 Generated with Claude Code

…QL's order

The finite-only rule covers what the application creates, binds and loads;
a row another tool writes is answered in PostgreSQL's order by a query
that runs in the database, and only loading it throws. docs/efcore.md and
docs/why.md now say so, and recommend a CHECK constraint per column kind.

Measured on PostgreSQL 16 and 18: the bounds check (lower/upper NOT IN
NaN, Infinity, -Infinity) is complete for a range but not for a
multirange, whose lower and upper are its outer bounds -
{(,-Infinity],[1,2)} and {[1,2),[Infinity,)} pass it. The multirange
constraint checks its text instead; the array one uses &&.

FloatCheckConstraintTests reads the three expressions from the guide's
code block, applies them with HasCheckConstraint on float8 and float4
columns, and requires each non-finite literal refused and each finite,
unbounded, empty and NULL value stored. With the bounds check in place of
the multirange expression, 10 rows are stored and the test fails.

Closes #232

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@CaffeinatedCoder CaffeinatedCoder added this to the 10.0.0 milestone Oct 18, 2026
@CaffeinatedCoder CaffeinatedCoder added documentation Improvements or additions to documentation area/ranges Range unions, variants and the shape engines area/value-sets Value set families and wrapper arities package/efcore Affects CodoMetis.ValueRanges.EFCore.PostgreSQL labels Oct 18, 2026
@CaffeinatedCoder
CaffeinatedCoder merged commit 3e2da56 into main Oct 18, 2026
11 checks passed
@CaffeinatedCoder
CaffeinatedCoder deleted the docs/232-float-check-constraints branch October 18, 2026 20:46
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area/ranges Range unions, variants and the shape engines area/value-sets Value set families and wrapper arities documentation Improvements or additions to documentation package/efcore Affects CodoMetis.ValueRanges.EFCore.PostgreSQL

Projects

None yet

Development

Successfully merging this pull request may close these issues.

A server-side predicate over a stored NaN or infinite float answers in PostgreSQL's order; only loading throws

1 participant