Skip to content

fix(clickhouse): restore label hash suffix to avoid Code 215 on virtual datasets - #42793

Closed
Abdulrehman-PIAIC80387 wants to merge 2 commits into
apache:masterfrom
Abdulrehman-PIAIC80387:fix/clickhouse-virtual-alias-group-by-40289
Closed

fix(clickhouse): restore label hash suffix to avoid Code 215 on virtual datasets#42793
Abdulrehman-PIAIC80387 wants to merge 2 commits into
apache:masterfrom
Abdulrehman-PIAIC80387:fix/clickhouse-virtual-alias-group-by-40289

Conversation

@Abdulrehman-PIAIC80387

@Abdulrehman-PIAIC80387 Abdulrehman-PIAIC80387 commented Aug 5, 2026

Copy link
Copy Markdown
Contributor

SUMMARY

Fixes #40289. Adds a _mutate_label override on ClickHouseBaseEngineSpec that suffixes column aliases with a 6-character hash of the label — the shape of the override removed in #38280, now placed on the shared base so both drivers (ClickHouseEngineSpec for clickhouse-sqlalchemy and ClickHouseConnectEngineSpec for clickhouse-connect) get the fix. Code 215 is a server-side ClickHouse behavior, not driver-specific, so both drivers need coverage.

Also addresses @ivkhokhlachev's related regression on #38280 where time-range filters silently narrow when the granularity changes (same root cause: alias == subquery column name → ClickHouse's prefer_column_name_to_alias=0 substitutes the aliased expression into the WHERE clause).

THE BUG

Charts on virtual (SQL-defined) ClickHouse datasets with a time-grain groupby (e.g. monthly) fail with:

Code: 215. DB::Exception: Column ... is not under aggregate function and not in GROUP BY

on ClickHouse 25.3+, even though the SELECT and GROUP BY expressions are lexically identical. Confirmed regression from 4.1.1 by three independent reporters (@manyyy, @cpassnat, @cizara).

Superset generates:

SELECT toStartOfMonth(t.create_time) AS create_time,
       COUNT(*) AS count
FROM (SELECT create_time FROM raw) AS t
GROUP BY toStartOfMonth(t.create_time)

When the alias (create_time) collides lexically with a column name inside the subquery, ClickHouse 25.3+'s aggregate checker fails to correlate the outer alias with the GROUP BY expression and raises Code 215.

The _mutate_label override used to suffix every alias with a 6-character hash (create_time_b09621 under the default HASH_ALGORITHM = "sha256") so this collision never happened. #38280 removed it on the theory that clickhouse-connect>=0.13.0 made it unnecessary — but the workaround was actually guarding against server-side ClickHouse behavior that has since tightened.

WHY THIS OVER prefer_column_name_to_alias=1?

On the #38280 thread, @villebro floated defaulting connect_args to {"settings": {"prefer_column_name_to_alias": 1}} with a deep-merge of user overrides. That would fix the same class of bug via a server-side setting rather than a client-side alias suffix. Both are legitimate; this PR takes the _mutate_label path because:

  1. It covers both drivers — the setting approach only reliably reaches clickhouse-connect's engine params, not clickhouse-sqlalchemy.
  2. It doesn't require touching every existing user connection's connect_args (or introducing deep-merge logic in the connection layer to preserve user overrides).
  3. It's a targeted revert-and-widen of a known-good behavior.

Happy to layer the setting default on top as a follow-up PR if maintainers prefer that as belt-and-braces.

BEHAVIOR MATRIX

Scenario Before After
Chart on physical ClickHouse table (any grain) ✓ works ✓ works (aliases now suffixed)
Chart on virtual ClickHouse dataset (Day/Week grain) ✓ works ✓ works
Chart on virtual ClickHouse dataset (Month grain, CH 25.3+) ❌ Code: 215 ✓ works
Time-range filter narrowing on granularity change ❌ silently narrows ✓ works
Non-ClickHouse engines — (unchanged)
Exported CSV/XLSX column names create_time create_time_b09621 (documented in UPDATING.md)

TESTING INSTRUCTIONS

Manual (requires ClickHouse 25.3+):

  1. Create a virtual dataset: SELECT create_time, value FROM some_table
  2. Chart with time grain = Month, groupby = create_time
  3. Before: Code: 215 error
  4. After: chart renders correctly

Automated:

pytest tests/unit_tests/db_engine_specs/test_clickhouse.py -v -k mutate_label

Three regression tests:

  • test_clickhouse_mutate_label_suffixes_hash — pins the exact sha256 digest so drift in algo, input, or suffix length is caught
  • test_clickhouse_mutate_label_is_deterministic — locks in determinism and verifies the suffix was applied (fails against identity _mutate_label)
  • test_clickhouse_mutate_label_is_unique_across_inputs — same for uniqueness

BREAKING CHANGE NOTE

Exported CSV/XLSX column names for ClickHouse charts will regain the _XXXXXX hash suffix (same as pre-#38280 / pre-6.0 behavior). Cached results keyed on the old (no-suffix) aliases miss the cache once on upgrade. Downstream consumers that parse column headers (external clients, notebooks, alerts) see the new names. Documented in UPDATING.md.

ADDITIONAL INFORMATION

@dosubot dosubot Bot added data:connect:clickhouse Related to Clickhouse risk:breaking-change Issues or PRs that will introduce breaking changes labels Aug 5, 2026
@bito-code-review

bito-code-review Bot commented Aug 5, 2026

Copy link
Copy Markdown
Contributor

Code Review Agent Run #5cdd8a

Actionable Suggestions - 0
Review Details
  • Files reviewed - 2 · Commit Range: ee55751..ee55751
    • superset/db_engine_specs/clickhouse.py
    • tests/unit_tests/db_engine_specs/test_clickhouse.py
  • Files skipped - 1
    • UPDATING.md - Reason: Filter setting
  • Tools
    • MyPy (Static Code Analysis) - ✔︎ Successful
    • Astral Ruff (Static Code Analysis) - ✔︎ Successful
    • Whispers (Secret Scanner) - ✔︎ Successful
    • Detect-secrets (Secret Scanner) - ✔︎ Successful

Bito Usage Guide

Commands

Type the following command in the pull request comment and save the comment.

  • /review - Manually triggers a full AI review.

  • /pause - Pauses automatic reviews on this pull request.

  • /resume - Resumes automatic reviews.

  • /resolve - Marks all Bito-posted review comments as resolved.

  • /abort - Cancels all in-progress reviews.

Refer to the documentation for additional commands.

Configuration

This repository uses Superset You can customize the agent settings here or contact your Bito workspace admin at evan@preset.io.

Documentation & Help

AI Code Review powered by Bito Logo

@codecov

codecov Bot commented Aug 5, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 75.00000% with 1 line in your changes missing coverage. Please review.
✅ Project coverage is 66.40%. Comparing base (b98e157) to head (56d7521).
⚠️ Report is 1 commits behind head on master.

Files with missing lines Patch % Lines
superset/db_engine_specs/clickhouse.py 75.00% 1 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##           master   #42793      +/-   ##
==========================================
- Coverage   66.41%   66.40%   -0.02%     
==========================================
  Files        2861     2860       -1     
  Lines      161610   161519      -91     
  Branches    37218    37202      -16     
==========================================
- Hits       107341   107252      -89     
+ Misses      52227    52225       -2     
  Partials     2042     2042              
Flag Coverage Δ
hive 38.21% <75.00%> (+<0.01%) ⬆️
mysql 57.76% <75.00%> (+<0.01%) ⬆️
postgres 57.81% <75.00%> (+<0.01%) ⬆️
presto 40.18% <75.00%> (+<0.01%) ⬆️
python 59.20% <75.00%> (+<0.01%) ⬆️
sqlite 57.43% <75.00%> (+<0.01%) ⬆️
unit 100.00% <ø> (ø)

Flags with carried forward coverage won't be shown. Click here to find out more.

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

Comment thread tests/unit_tests/db_engine_specs/test_clickhouse.py Outdated
@bito-code-review

Copy link
Copy Markdown
Contributor

The review suggestion is correct. Testing the implementation by re-calculating the expected value using the same function (hash_from_str) as the production code creates a circular dependency, meaning the test would pass even if the hashing logic itself were broken. To resolve this, you should use a hardcoded, pre-computed hash value for the test inputs to ensure the implementation remains stable and correct.

Here is how you can update the test to use fixed expected values:

def test_clickhouse_mutate_label_suffixes_hash() -> None:
    from superset.db_engine_specs.clickhouse import ClickHouseBaseEngineSpec

    # Pre-computed hashes for the labels
    assert ClickHouseBaseEngineSpec._mutate_label("create_time") == "create_time_b16a62"
    assert ClickHouseBaseEngineSpec._mutate_label("revenue") == "revenue_a1b2c3"
    assert ClickHouseBaseEngineSpec._mutate_label("sum(A)/sum(B)") == "sum(A)/sum(B)_d4e5f6"

(Note: Please replace the placeholder hashes b16a62, a1b2c3, and d4e5f6 with the actual correct hash outputs for those strings.)

tests/unit_tests/db_engine_specs/test_clickhouse.py

# Pre-computed hashes for the labels
    assert ClickHouseBaseEngineSpec._mutate_label("create_time") == "create_time_b16a62"
    assert ClickHouseBaseEngineSpec._mutate_label("revenue") == "revenue_a1b2c3"
    assert ClickHouseBaseEngineSpec._mutate_label("sum(A)/sum(B)") == "sum(A)/sum(B)_d4e5f6"

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

Thanks for tracking down a real ClickHouse bug — the Code 215 collision on virtual datasets is well diagnosed and a deterministic hash suffix on the alias is the right fix. Findings below; the actual merge blocker is the branch conflict, not the code.

Blast radius — verified ClickHouse-only, but broader than a "restore"

_mutate_label is a per-spec override; the base implementation (BaseEngineSpec._mutate_label, base.py:2387) is identity and every customizing spec defines its own. This change sits on ClickHouseBaseEngineSpec, so it affects both ClickHouseEngineSpec (native) and ClickHouseConnectEngineSpec, and no non-ClickHouse spec (grepped _mutate_label across db_engine_specs/: athena, bigquery, databend, dremio, drill, elasticsearch, redshift, datastore each keep their own). Good scoping.

One nuance for the wording: the method removed in #38280 lived on the leaf ClickHouseConnectEngineSpec, whereas this one sits on the base — so native ClickHouseEngineSpec now gets suffixed aliases it never had, pre- or post-#38280. Arguably more correct (Code 215 is server-side, not driver-specific), but "restores the pre-#38280 behavior" undersells it; consider noting the scope widened to all ClickHouse drivers.

Determinism, disambiguation, truncation — verified sound

hash_from_str returns an md5/sha256 hexdigest, stable across processes and runs (not Python's salted hash()), so aliases and any SQL-derived cache keys stay stable. The suffix disambiguates the outer alias from the inner subquery column because only the outer SELECT alias flows through _mutate_label while the inner column keeps its raw name, so create_time_xxxxxx != create_time resolves the 215. ClickHouse leaves max_column_name_length = None, so make_label_compatible never truncates and the suffix always survives — no suffix-vs-truncation hazard.

Backward compatibility

Re-adding the suffix changes emitted SQL for existing ClickHouse charts on upgrade: cached results keyed on the old (no-suffix) alias will miss and re-run, and exported CSV/XLSX column names change. UPDATING.md documents the CSV/XLSX effect, which is the right call — flagging only that the impact is broader than exports (cache invalidation, any downstream consumer keyed on column names).

UPDATING.md example doesn't match the default config

hash_from_str uses HASH_ALGORITHM, which defaults to sha256 (config.py:259), so under a default deployment create_timecreate_time_b09621. The documented create_time_b16a62 is the md5 digest — only correct when an operator sets HASH_ALGORITHM="md5". (This also means the suffix isn't byte-identical to the pre-#38280 md5 suffix under default config — another reason "restore" is slightly off.) Inline suggestion below.

Tests — endorsing codeant's tautology finding

As codeant noted on test_clickhouse.py:636, test_clickhouse_mutate_label_suffixes_hash builds expected with the same f"{label}_{hash_from_str(label)[:6]}" formula production uses, so it can't detect a wrong hash algorithm, input, or suffix length — only a fully dropped suffix. I verified with a revert (removed the production _mutate_label): this is the only one of the three new tests that fails. test_..._is_deterministic and test_..._is_unique_across_inputs both still pass against an identity _mutate_label (a no-op is deterministic and "a" != "b" trivially holds), so those two guard essentially nothing. Recommend asserting a fixed literal for the default algorithm (e.g. create_time_b09621) or an implementation-independent invariant, and tightening the other two so they'd fail on a no-op.

Merge blocker

The PR is currently CONFLICTING / DIRTY and needs a rebase on master before it can merge (re-checked at review time).

Happy to re-look once the doc example and the branch conflict are sorted.

Comment thread UPDATING.md Outdated
Comment thread tests/unit_tests/db_engine_specs/test_clickhouse.py Outdated
@Abdulrehman-PIAIC80387
Abdulrehman-PIAIC80387 force-pushed the fix/clickhouse-virtual-alias-group-by-40289 branch from ee55751 to 123746c Compare August 7, 2026 11:20
@pull-request-size pull-request-size Bot added size/L and removed size/M labels Aug 7, 2026
@Abdulrehman-PIAIC80387

Copy link
Copy Markdown
Contributor Author

Thanks for the deep read @aminghadersohi. Pushed 123746ca1e addressing all four findings plus a self-noticed weakness in the other two tests:

  1. Rebased on master to clear the CONFLICTING state.
  2. UPDATING.md now shows the sha256-default digest (create_time_b09621, not the md5 b16a62) and calls out cache invalidation + downstream consumers alongside CSV/XLSX headers.
  3. Test Add pandas-highcharts as requirements #1 now uses fixed sha256 literals + patched get_hash_algorithm; tests Fix documentation #2 and Implementing my own highcharts wrapper #3 also hardened with startswith(label + "_") checks so they no longer pass against an identity _mutate_label (per your own observation).
  4. PR body / commit message reframed from "restore" to "add … widened to both drivers" per your point about the leaf-vs-base placement — Code 215 is server-side, so both ClickHouseEngineSpec and ClickHouseConnectEngineSpec need coverage.

Also proactively added a "why not prefer_column_name_to_alias=1" section in the PR body, since @villebro floated that alternative on the #38280 thread. TL;DR: both are valid; this PR takes the client-side path because it covers the sqlalchemy driver too and doesn't require touching existing connect_args. Happy to layer the setting default as a follow-up if maintainers prefer belt-and-braces.

Note on CI: the failing checks are a master-wide alembic branching issue (Multiple head revisions are present), not this PR — every open PR is hitting it. Will re-run once master lands the merge.

@bito-code-review

bito-code-review Bot commented Aug 7, 2026

Copy link
Copy Markdown
Contributor

Code Review Agent Run #f65f1e

Actionable Suggestions - 0
Review Details
  • Files reviewed - 2 · Commit Range: 123746c..123746c
    • superset/db_engine_specs/clickhouse.py
    • tests/unit_tests/db_engine_specs/test_clickhouse.py
  • Files skipped - 1
    • UPDATING.md - Reason: Filter setting
  • Tools
    • MyPy (Static Code Analysis) - ✔︎ Successful
    • Astral Ruff (Static Code Analysis) - ✔︎ Successful
    • Whispers (Secret Scanner) - ✔︎ Successful
    • Detect-secrets (Secret Scanner) - ✔︎ Successful

Bito Usage Guide

Commands

Type the following command in the pull request comment and save the comment.

  • /review - Manually triggers a full AI review.

  • /pause - Pauses automatic reviews on this pull request.

  • /resume - Resumes automatic reviews.

  • /resolve - Marks all Bito-posted review comments as resolved.

  • /abort - Cancels all in-progress reviews.

Refer to the documentation for additional commands.

Configuration

This repository uses Superset You can customize the agent settings here or contact your Bito workspace admin at evan@preset.io.

Documentation & Help

AI Code Review powered by Bito Logo

Applies the pre-apache#38280 6-char hash suffix to column aliases on both
``ClickHouseEngineSpec`` (clickhouse-sqlalchemy) and
``ClickHouseConnectEngineSpec`` by placing the ``_mutate_label``
override on the shared ``ClickHouseBaseEngineSpec``. Fixes ClickHouse
25.3+ raising ``Code: 215`` on charts against virtual datasets when the
outer alias collides lexically with a subquery column name.

Also addresses @ivkhokhlachev's related regression on the apache#38280 thread
where time-range filters silently narrow when the granularity changes
(same root cause: alias == subquery column name → ClickHouse's
``prefer_column_name_to_alias=0`` substitutes the aliased expression
into the WHERE clause).

Documented in UPDATING.md: cached results keyed on the old aliases will
miss the cache once on upgrade, exported CSV/XLSX column names for
ClickHouse charts include the suffix, and any downstream consumer that
parses column headers will see the new names.

Fixes apache#40289
@Abdulrehman-PIAIC80387
Abdulrehman-PIAIC80387 force-pushed the fix/clickhouse-virtual-alias-group-by-40289 branch from 123746c to 2c17cd8 Compare August 10, 2026 06:06
@bito-code-review

bito-code-review Bot commented Aug 10, 2026

Copy link
Copy Markdown
Contributor

Code Review Agent Run #842aff

Actionable Suggestions - 0
Review Details
  • Files reviewed - 2 · Commit Range: 2c17cd8..2c17cd8
    • superset/db_engine_specs/clickhouse.py
    • tests/unit_tests/db_engine_specs/test_clickhouse.py
  • Files skipped - 1
    • UPDATING.md - Reason: Filter setting
  • Tools
    • MyPy (Static Code Analysis) - ✔︎ Successful
    • Astral Ruff (Static Code Analysis) - ✔︎ Successful
    • Whispers (Secret Scanner) - ✔︎ Successful
    • Detect-secrets (Secret Scanner) - ✔︎ Successful

Bito Usage Guide

Commands

Type the following command in the pull request comment and save the comment.

  • /review - Manually triggers a full AI review.

  • /pause - Pauses automatic reviews on this pull request.

  • /resume - Resumes automatic reviews.

  • /resolve - Marks all Bito-posted review comments as resolved.

  • /abort - Cancels all in-progress reviews.

Refer to the documentation for additional commands.

Configuration

This repository uses Superset You can customize the agent settings here or contact your Bito workspace admin at evan@preset.io.

Documentation & Help

AI Code Review powered by Bito Logo

@villebro
villebro self-requested a review August 10, 2026 17:12
@villebro

Copy link
Copy Markdown
Member

@joe-clickhouse can you take a look? Reverting back to the hash prefix would not be great, and should be left as a last resort only if this is not fixable more cleanly.

@rusackas

Copy link
Copy Markdown
Member

Seeing about opening an upstream PR for this one... would love a review when I do :D

@rusackas

Copy link
Copy Markdown
Member

Filed this upstream too: ClickHouse/ClickHouse#114200. Confirmed against a live 26.7.3 instance, it isolates cleanly to whether the inner subquery column reference is qualified (t.create_time, works) or not (create_time, Code 215), even though SELECT and GROUP BY are otherwise identical. Doesn't change this PR, just good to have the root cause tracked now.

@joe-clickhouse

joe-clickhouse commented Aug 10, 2026

Copy link
Copy Markdown
Contributor

Hi @Abdulrehman-PIAIC80387, which clickhouse-connect version did you reproduce this with? I ran this end to end with #38280 in place, clickhouse-connect 1.6.0, and ClickHouse 25.3, 26.1 and 26.6, and could not reproduce a 215, including Month grain on a virtual dataset. I could only surface it by downgrading to clickhouse-connect 0.11.0.

I think there are two separate problems being conflated here:

  1. The Code 215 only occurs when GROUP BY repeats the full unqualified expression. That is the kind of query clickhouse-connect emitted before 0.13.0. Since Add GROUP BY alias rendering to ClickHouse SQLAlchemy compiler ClickHouse/clickhouse-connect#655 the dialect renders GROUP BY with the label alias, which succeeds on every server version I tested. All three repros on ClickHouse Error 215: Column not in GROUP BY when using virtual tables in charts (Superset 6.1.0 + ClickHouse 25.3.1.2703) #40289 show the old style, which happen in pre 0.13.0 drivers. The server side inconsistency is now tracked in GROUP BY / SELECT alias colliding with subquery column name causes false Code: 215 (NOT_AN_AGGREGATE) ClickHouse/ClickHouse#114200, thanks @rusackas.

  2. The time filter narrowing @ivkhokhlachev mentioned does exist on current stacks. The unqualified WHERE ref gets the alias expression substituted in by the server, so the range applies to the truncated value. I reproduced it through a live chart data request. The targeted fix is qualifying the filter column refs, which I verified are immune to the substitution. Restoring the hash suffix would mask it but re-breaks When using clickhouse connect, label mutations do not allow using aliases and virtual columns #33551.

Also note that I discovered prefer_column_name_to_alias=1 is not viable either becuase the GROUP BY alias rendering in clickhouse-connect 0.13+ depends on the default resolution mode and with the setting at 1 grain grouping degrades to one group per raw value.

Can you please rerun your repro on a current stack and confirm the driver version? I expect (hope) the 215 disappears and what remains is the time filter narrowing, which should then get its own targeted fix rather than restoring the label mutation.

@villebro villebro left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I'm going to mark this PR as "Request changes" until we have a proposal that does not cause a regression to previous issues. I'm also happy to jump on a sync call to discuss a path forward if this causes friction.

@Abdulrehman-PIAIC80387

Copy link
Copy Markdown
Contributor Author

Ran the driver-emission comparison locally per @joe-clickhouse's analysis. Data below — confirms this is a driver-version story.

Test setup

Superset-shaped SQLAlchemy query mimicking the exact structure TableColumn.get_sqla_col builds for a virtual dataset with a time-grain groupby — unqualified column("create_time") reference (matching Superset's actual emission, not SQLAlchemy's auto-qualified subquery.c.create_time). Same query, same label, same GROUP BY object; only the pinned clickhouse-connect version differs.

Results

driver GROUP BY emitted Fires Code 215?
clickhouse-connect==0.11.0 (SQLA 1.4) GROUP BY toStartOfMonth(toDateTime(`create_time`)) ✅ yes
clickhouse-connect==0.13.0 (SQLA 1.4) GROUP BY `create_time` ❌ no
clickhouse-connect==1.6.0 (SQLA 2.0) GROUP BY `create_time` ❌ no

Emission SQL for the 0.11.0 case matches the reporter's failing query on #40289 byte-for-byte modulo backticks. So the split @joe-clickhouse outlined is empirically confirmed:

  1. Code 215 — only fires on the pre-0.13.0 emission shape. Since Superset already requires >=0.13.0 per fix(clickhouse): remove _mutate_label workaround and bump clickhouse-connect to >=0.13.0 #38280, users hitting this today are on an outdated driver and a driver upgrade resolves.
  2. @ivkhokhlachev's time-filter narrowing — real on current stacks; root cause is the WHERE-clause unqualified ref being substituted with the aliased expression under prefer_column_name_to_alias=0. Targeted fix is qualifying the filter column references. Distinct from Code 215.
  3. Underlying server-side ambiguity — real, reproducible without any driver, tracked upstream in @rusackas's GROUP BY / SELECT alias colliding with subquery column name causes false Code: 215 (NOT_AN_AGGREGATE) ClickHouse/ClickHouse#114200.

Given (1) is a driver-upgrade path and (2)/(3) need different fixes, closing this PR in favor of that plan.

Follow-up offer

Happy to pick up the filter-qualification fix for (2) if you can point me at the WHERE-clause builder site. Thanks for the careful review.

@Abdulrehman-PIAIC80387

Copy link
Copy Markdown
Contributor Author

See final comment above — closing per @joe-clickhouse's driver-emission analysis (empirically confirmed). Code 215 is a pre-clickhouse-connect 0.13.0 emission shape; a driver upgrade resolves. The remaining current-stack bug (@ivkhokhlachev's filter narrowing) needs its own targeted fix.

Abdulrehman-PIAIC80387 added a commit to Abdulrehman-PIAIC80387/superset that referenced this pull request Aug 11, 2026
Per review on apache#42976:

1. **marginal_distributions guard** — combining ``show_values_as`` with
   ``marginal_distributions`` would include the ``All`` margin row/col
   in the row/col/grand-total denominators, producing wrong percentages.
   Raise ``InvalidPostProcessingError`` explicitly rather than silently
   returning wrong numbers. Combining the two needs a first-class design
   (probably compute on the non-margin subset then re-insert the
   margins), out of scope here.

2. **Flat-multi-metric percent_total** — a multi-metric pivot with no
   ``columns`` groupby produces a flat column index where each column
   IS a metric. Verified with pandas 2.3.3 that the previous code summed
   across metrics for the grand total (metric a's magnitude leaking
   into metric b's percentages). Now iterate each column as its own
   single-metric block, matching the MultiIndex per-metric semantics.

3. **NaN numerator test was broken** — the previous fixture used
   ``operator="sum"`` on a value that included ``NaN``; ``pandas``
   ``.sum(skipna=True)`` on a single-element ``[NaN]`` group returns
   ``0.0``, not ``NaN``, so the test never actually exercised
   ``_div_preserving_nan``'s NaN-preservation path. Rewrote the fixture
   to use a missing (row, col) combination, which ``pivot_table`` fills
   with a genuine ``NaN`` cell — verified empirically.

4. **Falsy-string validation** — the previous
   ``if show_values_as and show_values_as != "actual"`` guard skipped
   validation for empty strings, silently no-oping bad input. Now
   an explicit sentinel check ``if show_values_as not in (None, "",
   "actual")`` rejects unknown modes uniformly and lets both ``None``
   and ``""`` route to the no-op path.

5. **New zero-row/col-denominator tests** — pin the guard against
   division-by-zero producing ``Infinity`` in row/column modes (already
   handled for grand-total mode). Explicit tests for both axes.

6. **Pandas-3 compat (found while fixing)** — ``df.groupby(level=0,
   axis=1)`` is deprecated in pandas 2.3 (FutureWarning) and removed in
   pandas 3.x. Refactored to iterate ``columns.get_level_values(0)``
   explicitly with ``df.xs``, avoiding the deprecated call and keeping
   the fix forward-compatible.

All six fix paths verified against pandas 2.3.3 with a standalone repro
script before writing tests, mirroring the process discipline learned
from apache#42793.
@joe-clickhouse

Copy link
Copy Markdown
Contributor

Sounds good to me! Thanks for the followup. I'm not super familiar with superset internals, but a quick search shows that the get_time_filter function is what builds the range condition and can be found here:

def get_time_filter( # pylint: disable=too-many-arguments # noqa: C901

and the no-grain path looks like it goes through TableColumn.get_sqla_col and can be found here:

def get_sqla_col(

^ it looks like this is what emits the unqualified column(self.column_name) ref which is what the server substitutes. The fix has to resolve against the from clause in get_from_clause and it should be scoped to plain columns since the expression columns render as literal_columns and can't be auto-qualified.

Let me know if that's enough context or if you need more info or discover something else or decide there's a better way to fix. Happy to help!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

data:connect:clickhouse Related to Clickhouse risk:breaking-change Issues or PRs that will introduce breaking changes size/L

Projects

None yet

Development

Successfully merging this pull request may close these issues.

ClickHouse Error 215: Column not in GROUP BY when using virtual tables in charts (Superset 6.1.0 + ClickHouse 25.3.1.2703)

5 participants