Skip to content

Translate Length on Int32Range and Int64Range #405

Closed

Description

@CaffeinatedCoder

Problem

Length on Int32Range and Int64Range runs in memory only (docs/efcore.md#what-runs-where), so a query can't filter or order by it. The obstacle the docs gave until #402 (that int4range overflows before a cast can widen it) doesn't hold: casting first works.

Change

Translate Length on both types to a scalar the server evaluates, matching memory exactly:

  • Int32Range (long?): CASE WHEN isempty(x) THEN 0 ELSE upper(x)::bigint - lower(x)::bigint END. An unbounded side gives NULL because upper/lower do, which is memory's null. Measured on PostgreSQL 16: 4294967295 for [-2147483648,2147483647), 0 for empty, NULL for [1,). PostgreSQL's canonical [) form makes upper - lower the count memory returns ([1,10] measures 10).
  • Int64Range (long?): memory computes in decimal and returns null when the count exceeds long.MaxValue. The SQL has to do the same: numeric arithmetic, and NULL above 9223372036854775807, never an int8 overflow error or a wrapped value.
  • Declared nullability: NULL for a NULL range too (rule (c), a value-producing member over NULL).

Done when

Every condition on the 10.3.0 epic's parity list holds for both types: empty and unbounded shapes, the domain extremes, a nullable column and an outer-join operand, Where, OrderBy, a final Select (which ran in memory before) and a composed predicate (Length > n with &&/!), each against memory over the loaded rows on a live server. translation-support.json (#384) classifies both server, and the docs move the row to "Runs as SQL". main first, then the cherry-pick to release/10.x.

Activity

  1. added this to the 10.3.0 milestone on Oct 25, 2026
  2. added
    enhancementNew feature or request
    area/rangesRange unions, variants and the shape engines
    package/efcoreAffects CodoMetis.ValueRanges.EFCore.PostgreSQL
    on Oct 25, 2026
  3. CaffeinatedCoder commented on Oct 25, 2026

    @CaffeinatedCoder
    OwnerAuthor

    PR #437 on main. Measured on PostgreSQL 16 before writing it, over columns, parameters and inlined constants:

    • int4range: [-2147483648,2147483646] gives 4294967295. The empty range gives 0, an unbounded side NULL, and a NULL range NULL.
    • int8range: [0, long.MaxValue - 1] and [long.MinValue, -2] give long.MaxValue. One more, as in [-1, long.MaxValue - 1], gives NULL, and constants too large to count raise no planning error.

    The composition sweeps run over the DateRange family only, so they don't reach these members. IntegerLengthParityTests covers #408's parity list for them instead. The 10.x cherry-pick follows the merge.

  4. added a commit that references this issue on Oct 25, 2026
  5. CaffeinatedCoder commented on Oct 25, 2026

    @CaffeinatedCoder
    OwnerAuthor

    Runs as SQL on main since PR #437 (67628e2). The release/10.x cherry-pick is tracked separately (port issue under #408).

  6. added a commit that references this issue on Oct 25, 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/rangesRange unions, variants and the shape enginesenhancementNew feature or requestpackage/efcoreAffects CodoMetis.ValueRanges.EFCore.PostgreSQL

    Projects

    No projects

      Milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions