Fix group key generation when optimizeMaxInitialResultHolderCapacity shrinks the cardinality product - #19379
Open
xiangfu0 wants to merge 1 commit into
Open
Conversation
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## master #19379 +/- ##
============================================
+ Coverage 57.71% 67.54% +9.83%
- Complexity 7 1430 +1423
============================================
Files 2686 3486 +800
Lines 163987 224163 +60176
Branches 26627 35381 +8754
============================================
+ Hits 94640 151406 +56766
+ Misses 61352 60714 -638
- Partials 7995 12043 +4048
Flags with carried forward coverage won't be shown. Click here to find out more. ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
xiangfu0
requested review from
Jackie-Jiang,
gortiz and
yashmayya
and
a lite review from Copilot
August 28, 2026 17:36
…shrinks the cardinality product With optimizeMaxInitialResultHolderCapacity=true, DictionaryBasedGroupKeyGenerator used the IN/EQ-predicate-shrunk cardinality product both for holder type selection and for the group id upper bound. ArrayBasedHolder uses raw dictionary-id mixed-radix products as group ids, so any matching dictionary id beyond the shrunk bound threw ArrayIndexOutOfBoundsException, and resetting longOverflow could downgrade the holder to int/long raw keys that overflow for the full cardinalities, silently colliding distinct groups. Holder type selection now always uses the full cardinality product; the predicate-derived value only caps the dense group id upper bound, and ArrayBasedHolder falls back to IntMapBasedHolder when the bound shrinks below the product. Multi-value group-by expressions are excluded from the predicate-derived bound (every value inside a matching row becomes a group), enforced both in DefaultGroupByExecutor for all generators and inside DictionaryBasedGroupKeyGenerator.
xiangfu0
force-pushed
the
fix-optimized-groupby-holder-selection
branch
from
August 29, 2026 09:07
009f03b to
dbe1598
Compare
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.
Problem
With the query option
optimizeMaxInitialResultHolderCapacity=true,DictionaryBasedGroupKeyGeneratorshrinks the group-by cardinality product to the IN/EQ predicate sizes and then uses the shrunk value both for holder-type selection and for_globalGroupIdUpperBound. This is unsound in three ways:ArrayIndexOutOfBoundsException:ArrayBasedHolderuses raw dictionary-id mixed-radix products as group ids directly, so its_flagsarray (sized to the shrunk bound) is indexed by ids up to the full cardinality product. Repro: dict INT columndIntwith cardinality 1000,SET optimizeMaxInitialResultHolderCapacity=true; SELECT dInt, COUNT(*) FROM t WHERE dInt IN (1,2,3,4,5) GROUP BY dIntthrowsAIOOBE: Index 5 out of bounds for length 5inArrayBasedHolder.markGroups.longOverflow = falseand shrinking the product can downgrade the holder toIntMapBasedHolder/LongMapBasedHolder, whose raw-key arithmetic still uses the full cardinalities — the keys overflow int/long and distinct groups collide.Fix
_globalGroupIdUpperBound, which is a valid dense group-count bound for the map-based holders.ArrayBasedHolderfalls back toIntMapBasedHolder, which maps the sparse raw keys onto dense group ids. Inside theArrayBasedHolderbranch,_globalGroupIdUpperBound == cardinalityProductnow holds by construction. This deliberately trades a hash lookup per row for the smaller result holders the opt-in option asks for; the alternative (keepArrayBasedHolderwith the full bound whenever the product fits underarrayBasedThreshold) would silently turn the option into a no-op for every small-cardinality group-by, changing its documented, tested behavior. With the option off (the default), holder selection is bit-identical to before.DefaultGroupByExecutor#getGroupByExpressionSizesFromPredicatesexcludes multi-value group-by expressions from the predicate-size map, covering all three group key generators, andDictionaryBasedGroupKeyGeneratoralso self-enforces the exclusion (it computes the bound from its own_isSingleValueColumn/_cardinalitiesarrays, which additionally removes the per-querycardinalityMapallocation and keeps the bound comparable to the cardinality product when the same expression appears multiple times in the GROUP BY).Known adjacent follow-up (not in this PR):
NoDictionaryMultiColumnGroupKeyGeneratordoes not cap its optimized upper bound bynumGroupsLimit(not a correctness issue — the limit is enforced separately at group creation — but it oversizes result holder max capacity), andNoDictionaryGroupKeyGeneratorTesthas no coverage with a non-null predicate-size map.Tests
DictionaryBasedGroupKeyGeneratorTestadditions (each verified to fail before the fix):testOptimizedUpperBoundSmallerThanCardinalityProduct— the AIOOBE repro; now served byIntMapBasedHolder.testOptimizedUpperBoundKeepsLongMapBasedHolder/testOptimizedUpperBoundKeepsArrayMapBasedHolder— the optimization must not downgrade the holder type across the int/long overflow boundaries.testOptimizedUpperBoundMatchingCardinalityProductKeepsArrayBasedHolder— no deoptimization when the predicates do not prove fewer groups.testOptimizedUpperBoundIgnoresMultiValuePredicates— MV predicate sizes must not shrink the bound.testGetGroupByResultHolderCapacitynow also runsprocess()over the block, so all 12 capacity cases exercise actual key generation (8 of them threw AIOOBE before the fix).