Add support for dictionary for approx_distinct - #24646
Conversation
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #24646 +/- ##
==========================================
- Coverage 81.63% 81.58% -0.06%
==========================================
Files 1123 1123
Lines 410298 410674 +376
Branches 410298 410674 +376
==========================================
+ Hits 334965 335040 +75
- Misses 55642 55916 +274
- Partials 19691 19718 +27 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
kumarUjjawal
left a comment
There was a problem hiding this comment.
Thank you @mkleen this looks good overall. I had two comments please check.
| | DataType::Map(_, _) | ||
| | DataType::Struct(_) | ||
| | DataType::Union(_, _) | ||
| | DataType::Dictionary(_, _) |
There was a problem hiding this comment.
Would it work to match on the value type instead, so a dictionary is supported exactly when its values would be? Something like DataType::Dictionary(_, value_type)
There was a problem hiding this comment.
Good point. Floats will be unsupported see #23084, So we should exclude them in all container types.
There was a problem hiding this comment.
i still feel we can support floats here if we already support it in regular distinct count 🤔
also for reference seems duckdb supports:
memory D select approx_count_distinct(a) from values (1.5::double), (1.5), ('nan'::double), (null), (0) t(a);
┌──────────────────────────┐
│ approx_count_distinct(a) │
│ int64 │
├──────────────────────────┤
│ 3 │
└──────────────────────────┘There was a problem hiding this comment.
Ok makes sense. I will look into this.
There was a problem hiding this comment.
The unconditional arm accepts dictionaries whose value type remains intentionally unsupported.
For example, Dictionary(Int32, Float64) reaches HLLAccumulator, whose hashing path supports floats, although plain Float64 returns NotImplemented. Grouped aggregation also bypasses the restriction through GroupsAccumulatorAdapter.
We can validate the value type recursively with a shared supported-type predicate and add a negative float-dictionary regression.
| | DataType::Map(_, _) | ||
| | DataType::Struct(_) | ||
| | DataType::Union(_, _) | ||
| | DataType::Dictionary(_, _) |
There was a problem hiding this comment.
would it make sense to have both dictionary arms on the value type, so Dictionary(_, value_type) is accepted only when is_hll_groups_type(value_type) is true?
There was a problem hiding this comment.
That's a good idea. Thank you!
Rich-T-kid
left a comment
There was a problem hiding this comment.
you beat me to it #24731 😆.
f2593d7 to
a3520c7
Compare
a3520c7 to
5f60729
Compare
c898a95 to
9f9aa29
Compare
| /// [`HllGroupsAccumulator`]. The fixed-domain types (booleans / small ints) and | ||
| /// `Null` fall back to the per-group [`Accumulator`] path. | ||
| fn is_hll_groups_type(data_type: &DataType) -> bool { | ||
| if let DataType::Dictionary(_, value_type) = data_type { |
There was a problem hiding this comment.
Recursing here returns false for dictionaries containing Boolean, small integers, or Null, because those plain types use specialized scalar accumulators. The grouped executor then creates one HLLAccumulator per group through GroupsAccumulatorAdapter; each accumulator embeds a 16 KiB sketch. A dictionary-encoded Boolean with 100,000 groups can therefore consume about 1.5 GiB for sketches alone. Please route every supported dictionary through HllGroupsAccumulator, or provide a compact dictionary-aware fallback, and add a path-sensitive regression for a fixed-domain dictionary.
| | DataType::Map(_, _) | ||
| | DataType::Struct(_) | ||
| | DataType::Union(_, _) | ||
| | DataType::Dictionary(_, _) |
There was a problem hiding this comment.
The unconditional arm accepts dictionaries whose value type remains intentionally unsupported.
For example, Dictionary(Int32, Float64) reaches HLLAccumulator, whose hashing path supports floats, although plain Float64 returns NotImplemented. Grouped aggregation also bypasses the restriction through GroupsAccumulatorAdapter.
We can validate the value type recursively with a shared supported-type predicate and add a negative float-dictionary regression.
Which issue does this PR close?
approx_distinctfunction #22989 but does not close it. More types are coming.Rationale for this change
Dictionaryforapprox_distinctWhat changes are included in this PR?
HLLAccumulatorandHllGroupsAccumulatorto supportDictionaryAre these changes tested?
Yes
Are there any user-facing changes?
Yes,
approx_distinctsupports nowDictionarybut no breaking changes.