An aggregate's grouping sets are ordered, and the spec ties meaning to that order: "an aggregate relation with more than one grouping set receives an extra i32 column on the right-hand side. The value of this field will be the zero-based index of the grouping set that yielded the record" (logical relations, spec v0.101.0). Converting to Calcite discards it. Calcite's Aggregate constructor requires its groupSets strictly ordered by ImmutableBitSet.ORDERING, and RelBuilder.aggregate funnels them through an ImmutableSortedMultiset(ImmutableBitSet.COMPARATOR), so whatever order the producer chose is normalized to ascending and the trip back returns the sets sorted.
Measured on main at 7310fc8, for aggregates over foo(a i64, b i64, c string) with no measures:
declared sets round-tripped sets
{c}, {a} -> {a}, {c}
{a}, {a,b} -> {a,b}, {a}
The first converts to LogicalAggregate(group=[{0, 2}], groups=[[{0}, {2}]]) — already sorted, with nothing left to record that the producer declared {c} first. original.equals(roundTripped) is false in both cases, and the round-tripped aggregate's emit mapping comes back as the identity with the grouping-set index dropped.
Because the index column holds the set's zero-based index, reordering the sets changes the value every row reports: rows grouped by c report 0 before the trip and 1 after. A consumer that filters or branches on that column selects the wrong rows.
ComplexAggregateTest.anAggregateOverOutOfOrderGroupingSetsRoundTrips compares only subList(0, 2) of the record types and never getGroupings() or the emit mapping, so it passes while the sets are swapped.
Distinct from #1162, which is the index column's type, and #1182, which is the value the conversion puts in it — this is the set order that value indexes into. Found while reviewing #1161.
An aggregate's grouping sets are ordered, and the spec ties meaning to that order: "an aggregate relation with more than one grouping set receives an extra
i32column on the right-hand side. The value of this field will be the zero-based index of the grouping set that yielded the record" (logical relations, spec v0.101.0). Converting to Calcite discards it. Calcite'sAggregateconstructor requires itsgroupSetsstrictly ordered byImmutableBitSet.ORDERING, andRelBuilder.aggregatefunnels them through anImmutableSortedMultiset(ImmutableBitSet.COMPARATOR), so whatever order the producer chose is normalized to ascending and the trip back returns the sets sorted.Measured on
mainat 7310fc8, for aggregates overfoo(a i64, b i64, c string)with no measures:The first converts to
LogicalAggregate(group=[{0, 2}], groups=[[{0}, {2}]])— already sorted, with nothing left to record that the producer declared{c}first.original.equals(roundTripped)is false in both cases, and the round-tripped aggregate's emit mapping comes back as the identity with the grouping-set index dropped.Because the index column holds the set's zero-based index, reordering the sets changes the value every row reports: rows grouped by
creport 0 before the trip and 1 after. A consumer that filters or branches on that column selects the wrong rows.ComplexAggregateTest.anAggregateOverOutOfOrderGroupingSetsRoundTripscompares onlysubList(0, 2)of the record types and nevergetGroupings()or the emit mapping, so it passes while the sets are swapped.Distinct from #1162, which is the index column's type, and #1182, which is the value the conversion puts in it — this is the set order that value indexes into. Found while reviewing #1161.