Skip to content

feat(core-spec): allow NULLS FIRST and NULLS LAST in window ORDER BY - #577

Merged
jbonofre merged 2 commits into
apache:mainfrom
ekut:window-nulls-ordering
Oct 13, 2026
Merged

jbonofre merged 2 commits into
apache:mainfrom
ekut:window-nulls-ordering

Conversation

@ekut

@ekut ekut commented Oct 13, 2026 •

Copy link
Copy Markdown
Contributor

Adds NULLS FIRST | NULLS LAST to the window ORDER BY grammar, as @christianeu-db suggested in #498. Engines disagree on where NULLs sort by default (table in #498), so without the clause an author cannot write an ordering that gives the same rows everywhere. Parsers of window expressions need to accept the clause.

Where NULLs sort when the clause is omitted is left out of this change and raised on the dev list: https://lists.apache.org/thread/z2jr7q4pllsk5nkzv7d18ro8844n5tgy

The window syntax had no way to say where NULLs go, and engines differ
on it, so an author could not write an ordering that gives the same rows
everywhere. Add the clause to the grammar with its meaning and an
example. The placement when the clause is omitted is left out of this
change (apache#498).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

@christianeu-db christianeu-db left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for putting this together and identifying the spec gap!

If we have a default, I'd lean towards either the nulls_are_small or nulls_are_large approaches - those make nulls feel more like a regular value. I don't have a strong preference between either one of those schemes.

Comment thread core-spec/expression_language.md Outdated
function_name(args) OVER (
[PARTITION BY partition_expr, ...]
[ORDER BY order_expr [ASC|DESC], ...]
[ORDER BY order_expr [ASC|DESC] [NULLS FIRST|NULLS LAST], ...]

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: [NULLS FIRST | NULLS LAST] reads fine, but a grouped form like [NULLS { FIRST | LAST }] makes the pairing explicit

It also might be worth spelling the ordering out as separate productions, roughly how the SQL standard models a sort specification:

<sort specification>  ::= <sort key> [ <ordering spec> ] [ <null ordering> ]
<ordering spec>       ::= ASC | DESC
<null ordering>       ::= NULLS FIRST | NULLS LAST

That makes the three choices (key, direction, null placement) visibly independent and gives us one place to describe each

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done in 3f05af1: the clause is now [NULLS {FIRST|LAST}], and the three parts of an ORDER BY item are listed separately below the block. I kept the SQL-style syntax block rather than productions, since the rest of the document writes syntax that way (the only production is FieldExpr). Happy to switch to productions if you prefer.

Comment thread core-spec/expression_language.md Outdated
)
```

`NULLS FIRST` sorts NULL values of that `order_expr` before non-NULL values, and `NULLS LAST` after them, with either `ASC` or `DESC`:

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The text is a bit ambiguous. It reads as though NULL placement composes with the sort direction - a plausible interpretation is that NULLS FIRST means NULLs come first for ASC and last for DESC. But the two are independent: null placement is fixed regardless of direction.

Maybe something closer to:

NULLS { FIRST | LAST } specifies whether NULL values are ordered before or after non-NULL values, regardless of the sort direction. For example, RANK() OVER (ORDER BY revenue DESC NULLS LAST) ranks non-NULL revenues from high to low and places NULL revenues last.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed, done in 3f05af1: NULL placement is now stated per order_expr and independent of the sort direction, and the example uses your wording.

…ection

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@ekut

ekut commented Oct 13, 2026

Copy link
Copy Markdown
Contributor Author

Thanks for the review. The default stays out of this PR; the question is open on dev@: https://lists.apache.org/thread/z2jr7q4pllsk5nkzv7d18ro8844n5tgy. Your preference for nulls_are_small or nulls_are_large would help there, so it is on the list before a vote.

@jbonofre
jbonofre self-requested a review October 13, 2026 08:39

@jbonofre jbonofre left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks @ekut! The clause is the right fix for the portability gap in #498, and it's purely additive, so existing expressions stay valid.
See also my comments on the dev@ mailing list. A couple of notes for us:

  • #498 suggests a compliance-suite case with NULLs in the ordering column. That could go in a separate PR.
  • The existing examples using ORDER BY order_date have no clause. They may need revisit once the default is decided on dev@

LGTM!

@jbonofre
jbonofre merged commit 7496811 into apache:main Oct 13, 2026
75 checks passed
ekut added a commit to Dactopus/dactopus-ossie-clickhouse that referenced this pull request Oct 13, 2026
apache/ossie#577 added the clause to the spec's window syntax, so a
model that writes it must load. It passes through as written; without it
NULLs still sort last in both directions. WITHIN GROUP keeps rejecting
it, as the spec's syntax there has none.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants