You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Translate Clamp on Int32Range, Int64Range and DecimalRange #406
Clamp(value) on a range runs in memory only (docs/efcore.md#what-runs-where). The obstacle the docs gave until #402 ("a CASE per shape") is overstated: GREATEST and LEAST skip NULL, so an unbounded side needs no branch.
Change
Translate Clamp on Int32Range, Int64Range and DecimalRange, matching RangeExtensions.Clamp:
Memory's rule:null for the empty range; the value itself inside; otherwise the finite bound it falls outside of, whatever that bound's inclusiveness (on a continuous type an exclusive bound is returned although the range doesn't contain it). An unbounded side never constrains. The value is first passed through NormalizeElement.
SQL:CASE WHEN isempty(x) THEN NULL ELSE LEAST(GREATEST(v, lower(x)), <upper>) END. <upper> is upper(x) for numrange. For int4range/int8range it is upper(x) - 1, because PostgreSQL canonicalizes to an exclusive upper bound where memory's End is inclusive. upper(x) - 1 can't overflow, since the canonical exclusive bound is at least one above a real value.
Measured on PostgreSQL 16: LEAST(GREATEST(15, lower('(,10]')), upper('(,10]') - 1) is 10.
To check first:DecimalRange's NormalizeElement (scale) against what numeric returns, since Same compares the loaded decimal.
Declared nullability: NULL for a NULL range or a NULL value (rule (c)).
Done when
Every condition on the 10.3.0 epic's parity list holds for the three types, against memory over the loaded rows on a live server. translation-support.json classifies them server (the other range types stay client with their reason), and the docs list the three under "Runs as SQL". main first, then the cherry-pick.
PR #440 on main. Measured on PostgreSQL 16 before writing it, and the issue's "to check first" item is resolved:
DecimalRange keeps the default NormalizeElement (the identity), so memory sees the value as given.
numeric returns the stored scale (2.500), which decimal equality ignores.
A NULL range or value needs its own test, because GREATEST/LEAST skip NULL and would otherwise answer with the other operand. ClampParityTests covers #408's parity list for these three types. The composition sweeps run over DateRange, whose Clamp stays in memory. The 10.x cherry-pick follows the merge.
Activity
CaffeinatedCoder commentedon Oct 25, 2026
PR #440 on
main. Measured on PostgreSQL 16 before writing it, and the issue's "to check first" item is resolved:DecimalRangekeeps the defaultNormalizeElement(the identity), so memory sees the value as given.numericreturns the stored scale (2.500), whichdecimalequality ignores.A NULL range or value needs its own test, because
GREATEST/LEASTskip NULL and would otherwise answer with the other operand.ClampParityTestscovers #408's parity list for these three types. The composition sweeps run overDateRange, whoseClampstays in memory. The 10.x cherry-pick follows the merge.Merge pull request #440 from CaffeinatedCoder/feat/406-translate-clamp
CaffeinatedCoder commentedon Oct 25, 2026
Runs as SQL on
mainsince PR #440 (84dc121). Therelease/10.xcherry-pick is tracked separately (port issue under #408).feat: translate Clamp on Int32Range, Int64Range and DecimalRange (#406)
Merge pull request #442 from CaffeinatedCoder/feat/441-translate-clam…