Repository navigation
Conversation
zhuqi-lucas
approved these changes
Nov 6, 2026
zhuqi-lucas
left a comment
Contributor
There was a problem hiding this comment.
LGTM, thanks @rkrishn7!
xudong963
approved these changes
Nov 6, 2026
xudong963
left a comment
Member
There was a problem hiding this comment.
How about adding a regression test through FirstValue::groups_accumulator_supported/create_groups_accumulator with no ORDER BY.
The current tests instantiate the new private accumulator directly, so restoring the old ordering gate would make the optimization unreachable while every new test remained green.
rkrishn7
force-pushed
the
unordered-first-value-groups-accumulator
branch
from
November 6, 2026 20:52
874690b to
c36da6e
Compare
Contributor
Author
Added in c36da6e |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Which issue does this PR close?
GroupsAccumulatorfor unorderedfirst_value#27149Rationale for this change
first_valueonly has aGroupsAccumulatorwhen it has anORDER BY. Without one, grouped aggregation usesGroupsAccumulatorAdapter, which keeps one boxedTrivialFirstValueAccumulator, holding aScalarValue, per group.The planner produces unordered
first_valuefor everyDISTINCT ONwithoutORDER BY:ReplaceDistinctWithAggregateturnsSELECT DISTINCT ON (keys) colsinto an aggregate grouped bykeyswith onefirst_value(col)per selected column. With many distinct keys, the per-group accumulators dominate both time and memory.What changes are included in this PR?
UnorderedFirstValueGroupsAccumulator: aGroupsAccumulatorforfirst_valuewithoutORDER BY. It stores values in the existingValueStateimplementations (PrimitiveValueState,BytesValueState,GenericValueState) and keeps oneis_setbit per group.FirstValue::groups_accumulator_supportedno longer requires an ordering.LastValueand orderedFirstValueare unchanged.A group takes the first row that passes the
FILTERclause (and, withIGNORE NULLS, is not null), and is never written again. That is the rowTrivialFirstValueAccumulatorkeeps: rows within a batch in input order, and batches in arrival order.merge_batchapplies the same rule to rows whoseis_setflag is true.The intermediate state is
[first_value, is_set], the same layout asTrivialFirstValueAccumulator, so partial and final aggregation see the same schema as before.convert_to_statesetsis_setfrom the filter andIGNORE NULLS, so skipped partial aggregation keeps the same semantics.A batch costs one bit test per row plus one value copy per newly decided group. Nothing visits groups the batch has no rows for.
What is the testing strategy for this PR?
Yes. New unit tests in
first_last.rscover:RESPECT NULLSkeeping a leading null, and later batches not overwriting a group's valueIGNORE NULLSFILTERpredicate that is null (excluded)merge_batchkeeping existing values, skipping rows withis_set = false, and filling groups out of orderconvert_to_statefoldingFILTERandIGNORE NULLSintois_setEmitTo::FirstBenchmarks
ClickBench
hits.parquet(./benchmarks/bench.sh data clickbench_1),datafusion-clirelease builds ofmain(b6b2e0e) and this PR, Apple M4 Pro (14 cores, 24 GB). Mean of 5 runs after 1 warmup (hyperfine); peak memory is the process peak footprint from/usr/bin/time -l, median of 3 runs.mainmain→ PRDISTINCT ON ("UserID"), 5 columnsfirst_value("URL")byUserIDfirst_value("WatchID")byUserIDDISTINCT ON ("OS"), 5 columnsOn
main, Q1 peaks close to the machine's 24 GB, so part of its time and variance is likely memory pressure. Q4 has few groups and shows no regression.Queries:
The outer
MAXs keep everyfirst_valuein the plan while printing one row. Results of unorderedfirst_valuedepend on partition scheduling on bothmainand this PR; withtarget_partitions = 1, both return identical results.Are there any user-facing changes?
No API changes. Queries using
first_valuewithoutORDER BY, includingDISTINCT ONwithoutORDER BY, use less time and memory. Which row is "first" is unchanged: as before, it depends on input order.