Skip to content

Fix NOT_FOUND_COLUMN_IN_BLOCK for GROUP BY ALL over a tuple with ORDER BY#110206

Merged
alexey-milovidov merged 10 commits into
masterfrom
fix-group-by-all-tuple-order-by
Jul 16, 2026
Merged

Fix NOT_FOUND_COLUMN_IN_BLOCK for GROUP BY ALL over a tuple with ORDER BY#110206
alexey-milovidov merged 10 commits into
masterfrom
fix-group-by-all-tuple-order-by

Conversation

@alexey-milovidov

@alexey-milovidov alexey-milovidov commented Jul 13, 2026

Copy link
Copy Markdown
Member

Closes: #83433

GROUP BY ALL over a tuple projection combined with ORDER BY threw NOT_FOUND_COLUMN_IN_BLOCK with the analyzer:

SELECT ((v0.c0, v0.c1)) FROM (SELECT 1 c0, 2 c1) v0 GROUP BY ALL ORDER BY ALL
    SETTINGS optimize_injective_functions_in_group_by = 0;
-- Code: 10. DB::Exception: Not found column __table1.c0 in block
-- tuple(__table1.c0, __table1.c1) (NOT_FOUND_COLUMN_IN_BLOCK)

GROUP BY ALL adds the SELECT expressions as grouping keys in expandGroupByAll, which runs after resolveGroupByNode has already unwrapped tuples in the GROUP BY list. So a projection like tuple(a, b) became a single grouping key, unlike an explicit GROUP BY tuple(a, b) (or GROUP BY (a, b)), which expandTuplesInList turns into the separate keys a and b.

OrderByTupleEliminationPass then rewrites ORDER BY tuple(a, b) into ORDER BY a, b. With the tuple kept whole as a grouping key, only the tuple column is available after aggregation, so the rewritten ORDER BY referenced the missing elements a and b and threw the exception. This only reproduced with optimize_injective_functions_in_group_by = 0; with the setting enabled, OptimizeGroupByInjectiveFunctionsPass unwraps the tuple key anyway.

The fix unwraps tuples in the GROUP BY ALL keys too, so GROUP BY ALL produces the same normalized grouping keys as an explicit GROUP BY.

Changelog category (leave one):

  • Bug Fix (user-visible misbehavior in an official stable release)

Changelog entry (a user-readable short description of the changes that goes into CHANGELOG.md):

Fixed a NOT_FOUND_COLUMN_IN_BLOCK error when using GROUP BY ALL over a tuple expression together with ORDER BY.

Documentation entry for user-facing changes

  • Documentation is written (mandatory for new features)

Version info

  • Merged into: 26.7.1.1037 (included in 26.7 and later)

…R BY

`GROUP BY ALL` adds the SELECT expressions as grouping keys in `expandGroupByAll`,
which runs after `resolveGroupByNode` has already unwrapped tuples in the GROUP BY
list. So a projection like `tuple(a, b)` became a single grouping key, unlike an
explicit `GROUP BY tuple(a, b)` (or `GROUP BY (a, b)`) which `expandTuplesInList`
turns into the separate keys `a` and `b`.

`OrderByTupleEliminationPass` later rewrites `ORDER BY tuple(a, b)` into
`ORDER BY a, b`. With the tuple kept whole as a grouping key, only the tuple column
is available after aggregation, so the rewritten `ORDER BY` referenced the missing
elements `a` and `b` and threw `NOT_FOUND_COLUMN_IN_BLOCK`. This only reproduced
with the analyzer and with `optimize_injective_functions_in_group_by = 0` (with the
setting enabled, `OptimizeGroupByInjectiveFunctionsPass` unwraps the tuple key
anyway).

Unwrap tuples in the `GROUP BY ALL` keys too, so `GROUP BY ALL` produces the same
normalized grouping keys as an explicit `GROUP BY`.

Closes: #83433

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@clickhouse-gh

clickhouse-gh Bot commented Jul 13, 2026

Copy link
Copy Markdown
Contributor

Workflow [PR], commit [9d67075]

Summary:


AI Review

Summary

This PR makes the new analyzer normalize GROUP BY ALL tuple projections the same way as an explicit GROUP BY tuple(...), and the follow-up commit restores the missing validateGroupByKeyType check on the expanded keys. I did not find any new correctness or safety issues in the current diff; the remaining open thread is a separate pre-existing GROUP BY ALL + group_by_use_nulls divergence that this PR does not introduce and does not claim to fix.

Final Verdict

Status: ✅ Approve

@clickhouse-gh clickhouse-gh Bot added the pr-bugfix Pull request with bugfix, not backported by default label Jul 13, 2026
alexey-milovidov and others added 2 commits July 13, 2026 06:52
The Style check `test_numbers_check` reported `Gap (4515, 5020) > 100`:
the new stateless test was numbered `05020`, far above the current
maximum on `master` (`04515`), which the numbering gap check rejects.
Renumber it to `04518` (within the allowed gap and clear of the numbers
reserved by other in-flight pull requests) so the workflow no longer
aborts at the Style check, letting the downstream Bugfix validation jobs
run.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
/// rewrites `ORDER BY tuple(a, b)` into `ORDER BY a, b`) would reference columns missing from the
/// aggregated block. See https://github.com/ClickHouse/ClickHouse/issues/83433.
if (was_group_by_all)
expandTuplesInList(query_node_typed.getGroupBy().getNodes());

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.

Late tuple expansion here still leaves GROUP BY ALL different from an explicit GROUP BY tuple(...) for the supported WITH ROLLUP / WITH CUBE forms. resolveGroupByNode is where group_by_use_nulls registers nullable versions of the keys and forces projection/order-by re-resolution; by the time we reach this block those steps have already run against an empty GROUP BY list.

That means queries such as:

SELECT (number, number + 1), count()
FROM numbers(1)
GROUP BY ALL WITH ROLLUP
SETTINGS enable_analyzer = 1, group_by_use_nulls = 1;

still diverge from explicit GROUP BY (number, number + 1): the projection keeps non-nullable children, but validateAggregates later compares them against nullable rollup keys and rejects the query. To make GROUP BY ALL actually match explicit GROUP BY, the expansion needs to happen before resolveGroupByNode, or we need to rerun the same key-validation / nullable-key bookkeeping after expanding here.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

The diagnosis of the mechanism is correct — for GROUP BY ALL the GROUP BY list is empty until expandGroupByAll, so hasGroupBy() is false and resolveGroupByNode (where group_by_use_nulls registers the nullable keys and forces the projection/ORDER BY re-resolution) is skipped entirely. But this is a separate, pre-existing bug, unrelated to this PR's change and unrelated to tuples.

It reproduces on master (no tuple, plain scalar key), with the same NOT_AN_AGGREGATE rejection:

SELECT number, count() FROM numbers(1)
GROUP BY ALL WITH ROLLUP
SETTINGS enable_analyzer = 1, group_by_use_nulls = 1;
-- Code: 215. DB::Exception: Column 'number' is not under aggregate function
-- and not in GROUP BY keys ... (NOT_AN_AGGREGATE)

while the explicit GROUP BY number WITH ROLLUP form works. So the divergence is GROUP BY ALL + group_by_use_nulls, not tuple expansion: it fires with a scalar key and without any tuple, and this PR's added code (expandTuplesInList gated on was_group_by_all) runs after the nullable-key machinery and only touches tuple keys, so it neither causes nor could fix it.

This PR is a minimal, targeted fix for #83433, whose repro uses neither group_by_use_nulls nor WITH ROLLUP/CUBE. Making GROUP BY ALL participate in the full resolveGroupByNode bookkeeping is a genuinely separate change: expandGroupByAll reads the resolved projection, and with group_by_use_nulls the projection is only resolved after GROUP BY is processed, so the expansion cannot simply be moved before resolveGroupByNode — it needs a second nullable-key-registration + re-resolution pass, which warrants its own PR and dedicated group_by_use_nulls regression coverage rather than being folded into this surgical fix.

@alexey-milovidov alexey-milovidov left a comment

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

LGTM

@alexey-milovidov alexey-milovidov self-assigned this Jul 13, 2026
@alexey-milovidov

Copy link
Copy Markdown
Member Author

Merged master and pushed to refresh CI.

On the previous run the only red was the Stateless tests (amd_llvm_coverage, old analyzer, s3 storage, DBReplicated, WasmEdge, parallel, 2/3) shard, whose 8 failures were all one server-crash cascade: 03915_exchange_tables_race fired the logical error

<Fatal> : Logical error: 'Arguments of 'multiply' have incorrect data types: 'n' of type 'Int256', '0.123' of type 'Float64''.

which aborts the server in the coverage build, so the following tests failed with Server does not respond to health check and the run was flagged with the Unknown error / log_check blocker. This is the known flaky tracked in #110133 — it fires on master and many unrelated PRs, only in this shard, and on the old-analyzer path. This PR only changes the new analyzer (QueryAnalyzer::resolveQuery), so it is unrelated.

Locally after merging master: clickhouse builds clean and 04518_group_by_all_tuple_order_by passes.

@alexey-milovidov

Copy link
Copy Markdown
Member Author

The only red on the previous run, Stress test (arm_msan) (report), is the known Block structure mismatch in JoinStep logical error from decorrelated correlated subqueries with a duplicate column name — a JOIN-planning bug unrelated to this PR (which only touches GROUP BY ALL grouping-key expansion in the analyzer).

Merged master and pushed to refresh CI. Local incremental build is clean and 04518_group_by_all_tuple_order_by passes against the reference.

@alexey-milovidov

Copy link
Copy Markdown
Member Author

Merged master (was 198 commits behind) and pushed to refresh CI. The branch builds clean locally and the test 04518_group_by_all_tuple_order_by matches its reference.

Both reds on the previous run are known master-side flakes, unrelated to this analyzer-only change:

Comment thread src/Analyzer/Resolve/QueryAnalyzer.cpp
@groeneai

Copy link
Copy Markdown
Contributor

Investigated #110133. This is a regression from #108728 (merged 2026-07-07 20:55 UTC), not a long-standing flake: CIDB shows 03915_exchange_tables_race went from ~0 hits to a spike starting 2026-07-08 (06-22: 1, 07-07: 1, 07-08: 5, 07-09: 30, steady since), almost entirely on the amd_llvm_coverage, old analyzer, s3 storage, DBReplicated, WasmEdge, parallel, 2/3 shard.

Root cause matches your #110048 analysis: #108728 added freshness checks to the per-query storage cache (Context::getOrCacheStorage) that defeat the #97411 name-pinning, so on the old analyzer a query re-resolves the exchanged table between analysis and pipeline build and executes multiply on the swapped column type.

A fix is already in progress, so no new PR is needed:

`GROUP BY ALL` expands the SELECT expressions into grouping keys in
`expandGroupByAll`, which runs after `resolveGroupByNode` already
validated the equivalent explicit `GROUP BY` keys via
`validateGroupByKeyType`. So a suspicious key type such as
`Dynamic`/`Variant` -- rejected by an explicit `GROUP BY` under
`allow_suspicious_types_in_group_by = 0` -- was silently accepted by
`GROUP BY ALL`, including for such a type nested inside a tuple
grouping key. Run the same `validateGroupByKeyType` check over the
expanded `GROUP BY ALL` keys, so both forms reject the same types.

Addresses review feedback on this PR.
@clickhouse-gh

clickhouse-gh Bot commented Jul 16, 2026

Copy link
Copy Markdown
Contributor

LLVM Coverage Report

Metric Baseline Current Δ
Lines 85.90% 85.90% +0.00%
Functions 92.70% 92.70% +0.00%
Branches 78.10% 78.10% +0.00%

Changed lines: Changed C/C++ lines covered: 9/9 (100.00%) · Uncovered code

Full report · Diff report

@alexey-milovidov
alexey-milovidov added this pull request to the merge queue Jul 16, 2026
Merged via the queue into master with commit e86fae3 Jul 16, 2026
180 checks passed
@alexey-milovidov
alexey-milovidov deleted the fix-group-by-all-tuple-order-by branch July 16, 2026 15:04
@clickgapai

Copy link
Copy Markdown
Contributor

Found via ClickGap automated review. Please close or comment if this is incorrect or needs adjustment.

TL;DR

Fix NOT_FOUND_COLUMN_IN_BLOCK for GROUP BY ALL over a tuple with ORDER BY

ClickGap verified: reproduced it with the script below on 26.6; tested 7 version(s) — 6 affected, 1 clean (see Affects); present since the feature was introduced in v24.8 (not a recent regression).

Reproduction

SET enable_analyzer = 1;
SELECT ((v0.c0, v0.c1)) FROM (SELECT 1 c0, 2 c1) v0 GROUP BY ALL ORDER BY ALL
    SETTINGS optimize_injective_functions_in_group_by = 0;
-- Expected: (1,2)
-- Before the fix: Code: 10. DB::Exception: Not found column __table1.c0 in block tuple(__table1.c0, __table1.c1) (NOT_FOUND_COLUMN_IN_BLOCK)

▶ Run on ClickHouse Fiddle

Bisect

  • The reproducer requires enable_analyzer, introduced in v24.8 — so this is not pre-existing: it was introduced no earlier than v24.8, and the introducing change is in the 24.8master range (most likely the change that added the feature). Every release line down to there reproduces.

Affects

  • Fix is on master — this PR's change fixes the bug on master. Branches below still have the bug and need the backport.
  • Reproduces on: 26.6, 26.5, 26.4, 26.3, 26.2, 26.1
  • Does NOT reproduce on: master
  • Backport needed: 26.6, 26.5, 26.4, 26.3, 26.2, 26.1

Suggested next step

The fix has landed on master; the affected release branches above still need the backport.

Component: comp-query-analyzer
Severity: P2


ClickGap · Finding: phase_d_pr110206

Triage metadata

  • Primary component: comp-query-analyzer
  • Secondary components: comp-testing
  • Component owners (comp-query-analyzer): @KochetovNicolai @novikd

@robot-clickhouse-ci-2 robot-clickhouse-ci-2 added the pr-synced-to-cloud The PR is synced to the cloud repo label Jul 16, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

pr-bugfix Pull request with bugfix, not backported by default pr-synced-to-cloud The PR is synced to the cloud repo

Projects

None yet

Development

Successfully merging this pull request may close these issues.

NOT_FOUND_COLUMN_IN_BLOCK with optimize_injective_functions_in_group_by disabled

4 participants