Skip to content

Cursor paging: null-aware seek for nullable sort keys - #5836

Merged
jeremydmiller merged 1 commit into
masterfrom
stoat/nov-5-releases/marten-5832
Nov 5, 2026
Merged

jeremydmiller merged 1 commit into
masterfrom
stoat/nov-5-releases/marten-5832

Conversation

@jeremydmiller

Copy link
Copy Markdown
Member

Fixes #5832

Cursor paging dropped null rows ascending and threw Invalid cursor descending for a nullable leading key holding nulls. Reproduced first (24 of 56 new cases red on ascending), then fixed:

  • BuildSeekPredicate: per key, the 'after the boundary' term now follows PostgreSQL's ordering. Ascending non-null boundary: k > v OR k IS NULL; ascending null boundary: nothing follows, only tie-breakers; descending null boundary: k IS NOT NULL; descending non-null: k < v. Equality on a null boundary uses IS NULL.
  • DecodeCursor: a JSON null boundary decodes to null for nullable/reference keys (still Invalid cursor for a non-nullable key).

Tests: int?, DateTime?, string, two nullable keys, and nullable strong-typed GuidId?, ascending and descending, nulls leading/inside/trailing/all-null/with ties, pageSize 2, compared with the unpaged query. Existing cursor suites (DocumentDbTests, ValueTypeTests, NodaTime) pass on net10.0.

🤖 Generated with Claude Code

NULL never satisfies a comparison, so ascending paging dropped null rows
and a descending cursor with a null boundary was rejected as invalid.
Match PostgreSQL's NULLS LAST (ASC) / NULLS FIRST (DESC) ordering in the
seek predicate and accept a null boundary when decoding the cursor.

Fixes #5832

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
@jeremydmiller
jeremydmiller merged commit 06c20f2 into master Nov 5, 2026
31 of 32 checks passed
@jeremydmiller
jeremydmiller deleted the stoat/nov-5-releases/marten-5832 branch November 5, 2026 22:10
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Cursor paging silently drops rows (ascending) or throws Invalid cursor (descending) for a nullable sort key with null rows

1 participant