Skip to content

[v3-3-test] Fix cursor pagination dropping rows when sorting by a nullable column (#68869) - #70739

Merged
pierrejeambrun merged 2 commits into
v3-3-testfrom
backport-3b13e59-v3-3-test
Jul 30, 2026
Merged

[v3-3-test] Fix cursor pagination dropping rows when sorting by a nullable column (#68869)#70739
pierrejeambrun merged 2 commits into
v3-3-testfrom
backport-3b13e59-v3-3-test

Conversation

@vatsrahul1001

Copy link
Copy Markdown
Contributor

Backport of #68869 to v3-3-test for the Airflow 3.3.1 patch release. Clean git cherry-pick -x (no conflicts).


Was generative AI tooling used to co-author this PR?
  • Yes — Claude Code (Opus 4.8)

Generated-by: Claude Code (Opus 4.8) following the guidelines — automated cherry-pick backport; code is the original author's, unchanged.

…#68869)

* Fix cursor pagination dropping rows when sorting by a nullable column

Cursor (keyset) paginated REST list endpoints silently dropped rows when the
sort column was nullable. The keyset predicate and the generated ORDER BY
disagreed on where NULLs sort, so once a page boundary fell on the NULL/non-NULL
edge every row on one side of it was skipped with no error. This is silent data
loss from the public API under a common sort such as start_date.

Cross-dialect NULLS FIRST/LAST is not portable (unsupported on MySQL and old
SQLite), so the cursor path now pins NULL placement with a portable CASE-based
null-rank key shared by both the keyset ORDER BY and the keyset predicate, so the
two can no longer disagree on any backend. The rank follows the column's sort
direction, so NULLs order as the largest value (last when ascending, first when
descending), matching PostgreSQL's default. PostgreSQL result order is therefore
unchanged; SQLite and MySQL NULL ordering shifts to align with it. The cursor
token format is unchanged (the rank is derived, not encoded) and offset
pagination is untouched.

* Add newsfragment for nullable-column cursor pagination fix

* Preserve index usage for nullable-column cursor pagination

The keyset ORDER BY ranked NULLs with a portable CASE expression so NULL
placement was uniform across backends. A computed ORDER BY key cannot use a
B-tree index, so large lists fall back to a full sort. Match each backend's
native NULL placement in the keyset predicate instead and keep the ORDER BY a
bare column, preserving the index; the fix still returns every row exactly once.
EXPLAIN on Postgres confirms an Index Only Scan rather than a Sort.

(cherry picked from commit 3b13e59)
@vatsrahul1001 vatsrahul1001 added this to the Airflow 3.3.1 milestone Jul 30, 2026
@vatsrahul1001 vatsrahul1001 added the type:bug-fix Changelog: Bug Fixes label Jul 30, 2026
@boring-cyborg boring-cyborg Bot added the area:API Airflow's REST/HTTP API label Jul 30, 2026
@pierrejeambrun
pierrejeambrun merged commit c183d07 into v3-3-test Jul 30, 2026
71 checks passed
@pierrejeambrun
pierrejeambrun deleted the backport-3b13e59-v3-3-test branch July 30, 2026 16:20
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area:API Airflow's REST/HTTP API type:bug-fix Changelog: Bug Fixes

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants