Skip to content

Fix NUMBER_OF_COLUMNS_DOESNT_MATCH for duplicate ALIAS columns in a subquery on a Distributed table#108725

Merged
alexey-milovidov merged 7 commits into
ClickHouse:masterfrom
groeneai:fix-distributed-alias-subquery-collapse
Jun 28, 2026
Merged

Fix NUMBER_OF_COLUMNS_DOESNT_MATCH for duplicate ALIAS columns in a subquery on a Distributed table#108725
alexey-milovidov merged 7 commits into
ClickHouse:masterfrom
groeneai:fix-distributed-alias-subquery-collapse

Conversation

@groeneai

@groeneai groeneai commented Jun 28, 2026

Copy link
Copy Markdown
Contributor

Related: #107815
Related: #107913

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):

Fix NUMBER_OF_COLUMNS_DOESNT_MATCH error when a subquery on a Distributed table reads two or more ALIAS columns that expand to the same expression (for example a1 String ALIAS toString(x), a2 String ALIAS toString(x)) and the subquery feeds an outer query, e.g. SELECT count() FROM (SELECT a1, a2 FROM dist GROUP BY a1, a2).

Description

PR #107913 added buildShardCollapseFanOut to reconstruct duplicate-ALIAS columns that a shard deduplicates into one. It matches the expected (initiator) columns to the shard columns by their inlined action-node names. Those names embed a disambiguating __tableN table alias.

The shard query tree is renumbered independently from the initiator's: buildQueryTreeForShard runs createUniqueAliasesIfNecessary, which restarts the __tableN aliases at 1. When the distributed read is nested inside a subquery, the same source column is named __table1.x on the shard but __tableK.x (K > 1) in the initiator's tree. The name match then fails, the fan-out is skipped, and the positional fallback cannot reconcile the differing column counts, so the query throws NUMBER_OF_COLUMNS_DOESNT_MATCH. The top-level form works only because the distributed table is __table1 on both sides.

The fix realigns the two numberings before matching: it zips the distinct __tableN indices of the shard header and of the initiator-side inlined names in ascending order (both trees number tables in the same DFS pre-order, so the i-th initiator table is the i-th shard table) and rewrites the initiator indices to the shard ones. Realignment is applied only when both sides expose the same number of table indices (the single distributed-read scope); otherwise the original names are kept and the existing bijection guard rejects any mismatch, so the behavior is unchanged for multi-table scopes.

Regression cases (subquery-wrapped duplicate-ALIAS collapse, including a non-collapsed column flowing alongside the collapsed pair) are added to 04357_distributed_alias_same_expression.

Version info

  • Merged into: 26.7.1.212 (included in 26.7 and later)
  • Backported to: 26.6.2.45

…ubquery on a Distributed table

buildShardCollapseFanOut (added in ClickHouse#107913) reconstructs duplicate-ALIAS
columns that a shard deduplicates into one, matching the expected columns to
the shard columns by their inlined action-node names. Those names embed a
__tableN table alias.

The shard query tree is renumbered independently from the initiator's:
buildQueryTreeForShard runs createUniqueAliasesIfNecessary, which restarts the
__tableN aliases at 1. When the distributed read is nested inside a subquery,
the same source column is named __table1.x on the shard but __tableK.x (K > 1)
in the initiator's tree, so the name match fails, the fan-out is skipped, and
the positional fallback cannot reconcile the differing column counts, throwing
NUMBER_OF_COLUMNS_DOESNT_MATCH. The top-level form works only because the
distributed table is __table1 on both sides.

Realign the two numberings before matching by zipping the distinct __tableN
indices of each side in ascending order (both trees number tables in the same
DFS pre-order) and rewriting the initiator indices to the shard ones. This is
applied only when both sides expose the same number of table indices; otherwise
the original names are kept and the existing bijection guard rejects any
mismatch, so multi-table scopes are unchanged.

Add subquery-wrapped regression cases to 04357_distributed_alias_same_expression.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@groeneai

Copy link
Copy Markdown
Contributor Author
Pre-PR validation gate (click to expand)
# Question Answer
a Deterministic repro? Yes. SELECT count() FROM (SELECT a1, a2 FROM dist GROUP BY a1, a2) (dist = Distributed over a MergeTree with a1 String ALIAS toString(x), a2 String ALIAS toString(x)) fails 100% on current master with Code 20 NUMBER_OF_COLUMNS_DOESNT_MATCH (source: 1, result: 2).
b Root cause explained? Yes. buildShardCollapseFanOut matches expected columns to shard columns by inlined action-node name, and those names embed a __tableN alias. The shard query tree is renumbered independently by createUniqueAliasesIfNecessary (restarts __tableN at 1), so in a subquery scope the same column is __table1.x on the shard but __table2.x on the initiator. The name lookup fails, the fan-out is skipped, and the positional fallback cannot reconcile 1 vs 2 columns. The top-level form works only because dist is __table1 on both sides.
c Fix matches root cause? Yes. It realigns the initiator-side __tableN indices to the shard-side ones (ascending zip of the distinct indices on each side) before the name lookup, directly addressing the renumbering skew.
d Test intent preserved / new tests added? Yes. No existing assertion changed. Added subquery-wrapped duplicate-ALIAS cases (incl. a non-collapsed column flowing alongside the collapsed pair) to 04357_distributed_alias_same_expression.
e Both directions demonstrated? Yes. 04357 FAILS at the new line on a clean-master binary (Code 20) and PASSES with the fix (15/15 runs with randomization enabled).
f Fix is general across code paths? Yes. The realignment lives in the single buildShardCollapseFanOut reconciliation point used by the Distributed read; verified across single-table, nested-subquery, multiple-distinct-alias-groups, and non-collapse pass-through cases. The multi-table GLOBAL JOIN subquery variant is a pre-existing, separate failure (fails identically on master); the fix is a deliberate no-op there (table-index counts differ → no realign), so no regression.
g Fix generalizes across inputs? Yes. Realignment is keyed on table-index count, not on specific datatypes/wrappers. Verified with multiple ALIAS groups (toString(x) and x+1), 3-way duplicates, double-nested subqueries, and a non-collapsed column; remapping is full-token-only so __table1 is never matched inside __table12, and indices are paired numerically (not lexicographically) so e.g. __table9/__table10 order correctly.
h Backward compatible? Yes. No setting default, on-disk/wire/replication format, or new validation is changed. The only behavior change is that a previously-throwing query now returns the correct result.
i Invariants and contracts preserved? Yes. Realignment is applied only when both sides expose the same number of distinct table indices; the existing bijection guard (every shard column must be used exactly once) still runs and rejects any wrong alignment by returning nullopt and falling back. Multi-table scopes keep the original exact-name matching unchanged.

Session id: cron:clickhouse-worker-slot-2:20260628-001700

@groeneai

Copy link
Copy Markdown
Contributor Author

cc @yakov-olkhovskiy @vdimir — could you review this? It extends the duplicate-ALIAS shard-collapse reconciliation from #107913 to the subquery case: when the distributed read is nested in a subquery, the shard tree's __tableN aliases (renumbered from 1 by createUniqueAliasesIfNecessary) no longer match the initiator's, so buildShardCollapseFanOut skips the fan-out and the query throws NUMBER_OF_COLUMNS_DOESNT_MATCH. The fix realigns the table indices before matching; the bijection guard still rejects any wrong alignment.

@alexey-milovidov alexey-milovidov added the can be tested Allows running workflows for external contributors label Jun 28, 2026
@clickhouse-gh

clickhouse-gh Bot commented Jun 28, 2026

Copy link
Copy Markdown
Contributor

Workflow [PR], commit [7c87465]

Summary:


AI Review

Summary

This PR tries to fix NUMBER_OF_COLUMNS_DOESNT_MATCH for duplicate ALIAS columns on Distributed reads nested under a subquery by normalizing analyzer-generated __tableN qualifier numbering before matching the shard header to the initiator header. The main subquery case is covered, but the current text-based normalization still treats some unstructured lambda argument text as a genuine table qualifier, so a valid duplicate-ALIAS shape remains on the old failing path.

Missing context / blind spots
  • ⚠️ The local .git metadata is read-only and the checkout was at 54d7a697, while the PR head is 7c87465a; I reviewed the current PR diff and raw head files from GitHub instead of switching the local worktree. A writable checkout at 7c87465a would close this gap for local repro runs.
Findings

⚠️ Majors

  • [src/Storages/buildQueryTreeForShard.cpp:1015] The normalizer still cannot distinguish all user text from real analyzer qualifiers. Lambda argument names are rendered into action names unquoted, so an argument named __table1.x has tail x; because x is also collected as a genuine column tail, this branch rewrites the lambda argument text as if it were a table qualifier. Two distinct non-collapsed shard columns using lambda argument names __table1.x and __table2.x then normalize to the same name, emplace keeps only one shard column, the unused-column guard rejects the fan-out, and the query falls back to NUMBER_OF_COLUMNS_DOESNT_MATCH next to the duplicate ALIAS pair. Fix by deriving the exact normalization spans from structured column identifiers, or by making the rendered-name scan skip lambda argument declarations as well as quoted spans.
Tests
  • ⚠️ Please add the smallest regression where the duplicate ALIAS collapse is nested in a subquery and two lambda-based ALIAS expressions use argument names with a real column tail, e.g. __table1.x and __table2.x. The current 04357 coverage only uses __table1.y, where y is not a real column tail, so it does not prove this remaining collision case.
Final Verdict

Status: ⚠️ Request changes

Minimum required action: fix the lambda-argument real-tail normalization case and add a focused regression for it.

@clickhouse-gh clickhouse-gh Bot added the pr-bugfix Pull request with bugfix, not backported by default label Jun 28, 2026
Comment thread src/Storages/buildQueryTreeForShard.cpp Outdated
groeneai and others added 3 commits June 28, 2026 03:15
The duplicate-ALIAS shard-collapse reconstruction realigns the shard and
initiator __tableN numbering by scanning inlined action-name text. The
previous scan matched any __table followed by digits, so user-visible text
that merely looks like a qualifier could perturb it: a column named
__table9, a string constant such as concat('__table9', x), or an over-long
__table999... literal (the last could also overflow parse<UInt64>).

Match only a genuine analyzer qualifier __tableN. (with the trailing dot
that buildColumnIdentifier always emits, and a non-word left boundary), and
use overflow-safe tryParse. Add regression cases to
04357_distributed_alias_same_expression.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The duplicate-ALIAS shard collapse reconstruction realigns the
initiator/shard __tableN numbering by scanning column action-name text.
The previous matcher required a `__tableN.` shape (trailing dot), but a
string constant such as `'__table1.'` also satisfies it: the opening
quote is a non-word boundary and the dot is part of the literal. That
literal's id then collides with the shard's renumbered real __table1
alias, the two index sets differ in size, the realignment is disabled,
and a collapsed pair nested in a subquery still fails with
NUMBER_OF_COLUMNS_DOESNT_MATCH. A backquoted column identifier
containing a dot (e.g. `__table1.k`) has the same problem.

Action names quote user text in exactly two ways: `'...'` for string
constants (FieldVisitorToString) and backquoted `...` for identifiers
(backQuoteIfNeed), both backslash-escaped (writeAnyEscapedString). Make
collectTableIds and remapTableIds skip those spans, so __tableN.-looking
text inside a quoted span is treated as user data, not a qualifier. The
same quote-skipping runs on both the shard header names and the inlined
initiator names, keeping the two index sets symmetric for the zip.

Regression cases added to 04357: a `'__table1.'` string constant and a
backquoted `__table1.k` column riding along a duplicate-ALIAS collapse
nested in a subquery. Both fail before this change and pass after.
The duplicate-ALIAS shard collapse reconstruction matched shard columns to
the initiator's expected columns by parsing the __tableN table-qualifier
indices out of the inlined action-name text and remapping one numbering onto
the other. That text scan could not reliably tell a real analyzer qualifier
apart from user text of the same shape: a string constant '__table1.', a
column literally named __table9, or a lambda argument name like __table1. or
__table1.y are all emitted into the action name and look like qualifiers. A
look-alike token's index polluted the per-side index sets, so the two sides
disagreed on the number of tables, the remap was disabled, and the collapsed
pair still failed with NUMBER_OF_COLUMNS_DOESNT_MATCH.

Replace the parse-and-remap with a symmetric canonicalization: blank the index
of every __table<digits>. qualifier (rewrite __table<digits>. to __table.) on
both the shard column names and the initiator's expected names, then match by
equality. The only systematic difference between the two sides is the
independent __tableN renumbering that createUniqueAliasesIfNecessary applies on
the shard; every other part of the name, including any qualifier-shaped user
text, is produced identically on both sides and cancels out under the equality
comparison. This removes the need to decide whether a given token is a real
qualifier, so string constants, oddly named columns, and lambda argument names
no longer perturb the match. Ambiguous collisions (two shard columns sharing a
canonical name) are still rejected by the existing every-shard-column-used
guard, which falls back to the default reconciliation rather than producing a
wrong result. The digits are no longer parsed into an integer, so an over-long
run cannot overflow.

Extends the 04357 regression test with lambda-argument ALIAS columns whose
argument names are __table1. and __table1.y.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Comment thread src/Storages/buildQueryTreeForShard.cpp
…iers

buildShardCollapseFanOut matches the initiator's expected columns to the
shard's deduplicated columns by name, after removing the independent __tableN
alias renumbering. The previous approach blanked the number of every
__table<digits>. token in the action-name text, which also mangled user text
that merely looks like a qualifier: two distinct expressions such as
concat('__table1.', toString(x)) and concat('__table2.', toString(x)) both
canonicalized to the same name, so one shard column was dropped as a duplicate
and the query failed with NUMBER_OF_COLUMNS_DOESNT_MATCH.

Drive the normalization from the query tree instead. Collect the set of genuine
column-name tails of __tableN. qualifiers from the structured column identifiers
(PlannerContext::getColumnNodeIdentifierOrNull), and erase a __table<digits>.
number only when the following tail is one of those known column names. Quoted
spans (string constants and backquoted identifiers) are skipped, and a
backquoted column tail that itself contains a dot (e.g. `__table1.k`) is matched
as a whole. User text (string literals, lambda argument names, columns named
like __table9) is therefore left intact and distinct look-alike values no longer
collapse onto one another. Matching stays by name, not position, so it tolerates
the shard returning its deduplicated columns in a different order than the
initiator expects.

Add regression cases to 04357_distributed_alias_same_expression.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
else
while (tail_end < name.size() && isWordCharASCII(name[tail_end]))
++tail_end;
if (tail_end > tail_begin && genuine_tails.contains(name.substr(tail_begin, tail_end - tail_begin)))

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 still treats some user text as a genuine qualifier. Lambda argument names are emitted into action names unquoted, so if the argument name has a real column tail, for example:

arrayMap(`__table1.x` -> toString(x), [0])
arrayMap(`__table2.x` -> toString(x), [0])

then this check sees tail x in genuine_tails and normalizes both argument names to __table.x. Those are two distinct non-collapsed shard columns, but shard_normalized_to_index.emplace keeps only one; the unused-column guard returns nullopt, and the caller falls back to the old positional conversion with fewer shard columns than the expected header, producing NUMBER_OF_COLUMNS_DOESNT_MATCH next to the duplicate-ALIAS pair. Please make the text scan skip lambda argument declarations too, or derive the normalization spans from structured column identifiers instead of matching every rendered __tableN.<tail> token.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

You are right that this exact shape is not reconstructed, and I now believe it cannot be at this call site. I want to lay out why, with the rendered strings, and ask a maintainer to weigh in on accepting it as a documented limit.

Why no text rule on the shard side can separate the two. At this point we only have the shard Block header text (query_plan.getCurrentHeader()); the shard query tree built by buildQueryTreeForShard is serialized into ReadFromRemote and is not reachable here. For your example, the shard column is:

arrayMap(__table1.x UInt8 -> toString(__table1.x), _CAST([0]_Array(UInt8), 'Array(UInt8)'_String))

The lambda argument name (__table1.x) is not renumbered (it is fixed user text rendered via getColumnName()), while the genuine body qualifier is renumbered (createUniqueAliasesIfNecessary restarts at 1 on the shard, so the body is __table1.x on the shard but __table2.x on the initiator). So the same literal __table1.x appears twice in one shard name, once as the arg (must be preserved) and once as a genuine qualifier (must be normalized), and they are byte-identical. With only the shard text and no shard tree, no token set, token equality, or "skip lambda arg declarations" rule can normalize one occurrence and not the other. I tried the lambda-arg-position grammar too: genuine qualifiers also render followed by <Type>/keywords in sort and window action names (e.g. __table1.y ORDER BY __table1.x ASC ..., __table1.x UInt8), so a flat-text "arg declaration" scan is not sound either.

This case is not a regression. I built the pre-PR base (exact-name fan-out, the buildShardCollapseFanOut from #107913) and ran your shape on a forced two-server remote cluster:

  • base (no this PR): Code 20 NUMBER_OF_COLUMNS_DOESNT_MATCH (source 3, result 4)
  • this PR: Code 20 NUMBER_OF_COLUMNS_DOESNT_MATCH (source 3, result 4) (identical)

The normalize collision is caught by the existing "every shard column used" guard, so the fan-out returns nullopt and the caller falls back to positional conversion exactly as before this PR. It never produces a wrong result. When the lambda argument is actually referenced in the body (e.g. arrayMap(\__table1.x` -> `__table1.x` + 100, [5])), the two columns stay distinct after normalization and the fan-out reconstructs them correctly; the only unreconstructed shape is an *unused* argument whose backquoted name equals __table.`, which is not produced by normal queries or by the analyzer.

So the PR fixes the reported bug (#107815) and the residual look-alike case is a safe fall-back identical to the pre-PR behavior. A complete fix would need the genuine-qualifier byte spans known at the shard header, which would mean transmitting that span metadata from the shard, well beyond this PR.

cc @vdimir (you reviewed #107913 which introduced this function) for a maintainer call on accepting this as a documented limitation versus a larger follow-up.

groeneai and others added 2 commits June 28, 2026 09:50
clang-tidy modernize-use-starts-ends-with (-warnings-as-errors) flagged the
compare(0, prefix.size(), prefix) prefix check in CollectGenuineQualifierTailsVisitor.
Equivalent rewrite to std::string::starts_with; no behavior change.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The lambda argument name `__table1.` (a backquoted identifier ending in a
dot) makes the targeted AST fuzzer mutate the test table into a column whose
ALIAS default type mismatches, which forces error-message formatting to
convert the lambda back to an AST. IdentifierNode::toASTImpl then builds an
ASTIdentifier whose name parts include an empty trailing part (from splitting
`__table1.` on the dot), tripping chassert(!part.empty()) in the ASTIdentifier
constructor (pre-existing engine assertion, unrelated to this PR's fix; also
seen on unrelated PRs ClickHouse#104436 and ClickHouse#99877).

The `__table1.y` lambda-arg cases already cover the same code path in
buildShardCollapseFanOut (an unquoted lambda-arg name that looks like a
`__tableN.` qualifier but whose tail is not a real column, so its numbering
must be left intact), without producing an empty identifier part. Keep those
and drop the redundant trailing-dot variant so the test no longer surfaces
the unrelated assertion under fuzzing.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@groeneai

Copy link
Copy Markdown
Contributor Author

The two AST-fuzzer/stress findings on the previous commit (9c57191) are both pre-existing engine bugs, not regressions from this PR. Pushed 7c87465 to stop this PR's test from surfacing one of them; both engine bugs are tracked for separate fixes.

STID 2508-2ef2 '!part.empty()' (AST fuzzer targeted, Stress arm_debug). The targeted fuzzer mutated this PR's new loc_adv table column lam0 ALIAS arrayMap(__table1. -> ...) into a type-mismatched ALIAS, which makes error-message formatting convert the lambda back to AST: IdentifierNode::toASTImpl -> ASTIdentifier ctor asserts !part.empty() and aborts, because the backquoted name __table1. splits on . into parts ["__table1", ""]. Pre-existing (ASTIdentifier.cpp:48 on master; also hit unrelated PRs 104436 and 99877 (no link, to avoid cross-refs)). 7c87465 drops the __table1. (trailing-dot) lambda cases and keeps the equivalent __table1.y ones, which cover the same buildShardCollapseFanOut branch (an unquoted lambda-arg name shaped like __tableN.<tail> whose tail is not a real column) without producing an empty identifier part.

STID 3926-38e4 'Cannot find sharding key column' (Stress arm_release). The crashing query SELECT ... FROM remote('127.{1..4}', view(SELECT toInt32(id) AS id FROM data), if(2 AS a, toInt32(id), t0)) ... is a fuzzer mutation of the unrelated existing test 01930_optimize_skip_unused_shards_rewrite_in.sql (the unmutated toInt32(id) sharding key works fine), in StorageDistributed::skipUnusedShardsWithAnalyzer. Unrelated to this PR's code or test; pre-existing (also hit PR 103383). Tracked separately.

The fix itself is unchanged and the regression test passes (verified locally, randomization on).

@groeneai

Copy link
Copy Markdown
Contributor Author

Fixing PR for the ASTIdentifier !part.empty() abort (STID 2508-2ef2) created: #108735

@groeneai

Copy link
Copy Markdown
Contributor Author

Fixing PR for the AST-fuzzer finding Cannot find sharding key column (STID 3926-38e4, a mutation of 01930_optimize_skip_unused_shards_rewrite_in, unrelated to this PR's change) created: #108737

@clickhouse-gh

clickhouse-gh Bot commented Jun 28, 2026

Copy link
Copy Markdown
Contributor

LLVM Coverage Report

Metric Baseline Current Δ
Lines 85.30% 85.40% +0.10%
Functions 92.60% 92.60% +0.00%
Branches 77.50% 77.60% +0.10%

Changed lines: Changed C/C++ lines covered by tests: 74/78 (94.87%) | Lost baseline coverage: none · Uncovered code

Full report · Diff report

@alexey-milovidov alexey-milovidov 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.

It's quite unfortunate that we have to do this. Approved.

@alexey-milovidov alexey-milovidov self-assigned this Jun 28, 2026
@alexey-milovidov
alexey-milovidov added this pull request to the merge queue Jun 28, 2026
Merged via the queue into ClickHouse:master with commit 36b98b9 Jun 28, 2026
169 checks passed
@robot-ch-test-poll4 robot-ch-test-poll4 added the pr-synced-to-cloud The PR is synced to the cloud repo label Jun 28, 2026
@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 NUMBER_OF_COLUMNS_DOESNT_MATCH for duplicate ALIAS columns in a subquery on a Distributed table

ClickGap verified: reproduced it with the script below on 26.5; tested 6 version(s) — 5 affected, 1 clean (see Affects); long-standing (not a recent regression); introducing PR not yet pinned.

Reproduction

-- Needs a clickhouse-server (the bug is in distributed query planning on the initiator).
-- The cluster test_cluster_two_shards_localhost ships in the standard test server config.
CREATE TABLE local_same_expr (x UInt8, a1 String ALIAS toString(x), a2 String ALIAS toString(x))
    ENGINE = MergeTree ORDER BY x;
CREATE TABLE dist_same_expr (x UInt8, a1 String ALIAS toString(x), a2 String ALIAS toString(x))
    ENGINE = Distributed(test_cluster_two_shards_localhost, currentDatabase(), local_same_expr, rand());
INSERT INTO local_same_expr (x) VALUES (7);
SET enable_analyzer = 1;
-- Without the fix this throws NUMBER_OF_COLUMNS_DOESNT_MATCH (Code 20); with it returns 1.
SELECT count() FROM (SELECT a1, a2 FROM dist_same_expr GROUP BY a1, a2);

Bisect

  • Reproduces on every release line tested down to v24.8.14.39 (the oldest with a downloadable binary for this repro). The introducing change predates v24.8.14.39, so it can't be pinned from release binaries — this is a long-standing issue, not a recent regression. A maintainer with full git history can git blame the relevant file/line to find the exact change.

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.5, 26.4, 26.3, 26.2, 26.1
  • Does NOT reproduce on: master
  • Backport needed: 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-distributed
Severity: P2

cc fix PR author: @groeneai
cc fix PR reviewer: @alexey-milovidov


ClickGap · Finding: phase_d_pr108725

Triage metadata

  • Primary component: comp-distributed
  • Secondary components: comp-testing
  • Component owners (comp-distributed): @azat @devcrafter

@robot-ch-test-poll3 robot-ch-test-poll3 added the pr-must-backport-synced The `*-must-backport` labels are synced into the cloud Sync PR label Jul 7, 2026
clickhouse-gh Bot pushed a commit that referenced this pull request Jul 7, 2026
…icate ALIAS columns in a subquery on a Distributed table
clickhouse-gh Bot added a commit that referenced this pull request Jul 7, 2026
Backport #108725 to 26.6: Fix NUMBER_OF_COLUMNS_DOESNT_MATCH for duplicate ALIAS columns in a subquery on a Distributed table
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

can be tested Allows running workflows for external contributors pr-backports-created Backport PRs are successfully created, it won't be processed by CI script anymore pr-bugfix Pull request with bugfix, not backported by default pr-must-backport-synced The `*-must-backport` labels are synced into the cloud Sync PR pr-synced-to-cloud The PR is synced to the cloud repo v26.6-must-backport

Projects

None yet

Development

Successfully merging this pull request may close these issues.

7 participants