Skip to content

Add support for dictionary for approx_distinct - #24646

Merged
kumarUjjawal merged 4 commits into
apache:mainfrom
mkleen:dictionary_approx_distinct
Sep 4, 2026
Merged

Add support for dictionary for approx_distinct#24646
kumarUjjawal merged 4 commits into
apache:mainfrom
mkleen:dictionary_approx_distinct

Conversation

@mkleen

Copy link
Copy Markdown
Contributor

Which issue does this PR close?

Rationale for this change

  • Support the Arrow type Dictionary for approx_distinct

What changes are included in this PR?

  • Enable HLLAccumulator and HllGroupsAccumulator to support Dictionary
  • Tests

Are these changes tested?

Yes

Are there any user-facing changes?

Yes, approx_distinct supports now Dictionary but no breaking changes.

@github-actionsgithub-actionsBot added sqllogictest SQL Logic Tests (.slt) functions Changes to functions implementation labels Aug 25, 2026
@mkleen
mkleen marked this pull request as ready for review August 25, 2026 07:58
@codecov-commenter

codecov-commenter commented Aug 25, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 81.59%. Comparing base (5980374) to head (e5132c7).
⚠️ Report is 6 commits behind head on main.

Additional details and impacted files
@@ Coverage Diff @@## main #24646 +/- ##
==========================================
- Coverage 81.63% 81.59% -0.05% 
==========================================
Files 1123 1123 Lines 410298 410970 +672 Branches 410298 410970 +672 ==========================================
+ Hits 334965 335333 +368 - Misses 55642 55893 +251 - Partials 19691 19744 +53 

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@mkleen

Copy link
Copy Markdown
ContributorAuthor

@Jefffrey

@kumarUjjawalkumarUjjawal 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.

Thank you @mkleen this looks good overall. I had two comments please check.

| DataType::Map(_, _)
| DataType::Struct(_)
| DataType::Union(_, _)
| DataType::Dictionary(_, _)

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.

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)

@mkleenmkleenAug 25, 2026

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

Good point. Floats will be unsupported see #23084, So we should exclude them in all container types.

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.

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) fromvalues (1.5::double), (1.5), ('nan'::double), (null), (0) t(a);
┌──────────────────────────┐
│ approx_count_distinct(a) │
│ int64 │
├──────────────────────────┤
│ 3 │
└──────────────────────────┘

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

Ok makes sense. I will look into this.

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 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(_, _)

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.

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?

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.

this is a good point

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

That's a good idea. Thank you!

@Rich-T-kidRich-T-kid 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.

you beat me to it #24731 😆.

@mkleen
mkleenforce-pushed the dictionary_approx_distinct branch 2 times, most recently from f2593d7 to a3520c7CompareSeptember 4, 2026 01:45
@mkleen
mkleenforce-pushed the dictionary_approx_distinct branch from a3520c7 to 5f60729CompareSeptember 4, 2026 01:46
@mkleen
mkleenforce-pushed the dictionary_approx_distinct branch from c898a95 to 9f9aa29CompareSeptember 4, 2026 02:39
/// [`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 {

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.

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(_, _)

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 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.

@mkleen

Copy link
Copy Markdown
ContributorAuthor

@kumarUjjawal Thanks for the review. Could you do one more pass please?

@kumarUjjawalkumarUjjawal 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.

Thank you @mkleen for all the iterations.

@kumarUjjawal
kumarUjjawal added this pull request to the merge queueSep 4, 2026
Merged via the queue into apache:main with commit 39d36f0Sep 4, 2026
38 checks passed
Rich-T-kid pushed a commit to DataDog/datafusion that referenced this pull request Sep 4, 2026
…24646)
Resolves merge conflict between HEAD's type-specific HLL accumulators and
the upstream dictionary support commit. Implements dictionary handling via
a DictionaryAccumulator wrapper that casts to the value type before
delegating to the appropriate inner accumulator.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Rich-T-kid pushed a commit to DataDog/datafusion that referenced this pull request Sep 4, 2026
…24646)
Resolves merge conflict between HEAD's type-specific HLL accumulators and
the upstream dictionary support commit. Implements dictionary handling via
a DictionaryAccumulator wrapper that casts to the value type before
delegating to the appropriate inner accumulator.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
gh-worker-dd-mergequeue-cf854dBot added a commit to DataDog/datafusion that referenced this pull request Sep 4, 2026
…anch
[cherry-pick] Add support for dictionary for approx_distinct (apache#24646)
Co-authored-by: Rich-T-kid <richard.baah@datadoghq.com>
Co-authored-by: mkleen <mkleen@gmail.com>
Rich-T-kid added a commit to DataDog/datafusion that referenced this pull request Sep 4, 2026
…anch-55
[Cherry-pick] Add support for dictionary for approx_distinct (apache#24646)
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

functionsChanges to functions implementationsqllogictestSQL Logic Tests (.slt)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants

@mkleen@codecov-commenter@kumarUjjawal@Jefffrey@Rich-T-kid