Support per-element aggregation of `Tuple` columns in `SummingMergeTree`, `AggregatingMergeTree` and `CoalescingMergeTree` by JingYanchao · Pull Request #98039 · ClickHouse/ClickHouse · GitHub
Skip to content

Support per-element aggregation of Tuple columns in SummingMergeTree, AggregatingMergeTree and CoalescingMergeTree#98039

Merged
nihalzp merged 53 commits into
ClickHouse:masterfrom
JingYanchao:johnjing/subcolumn-aggregate-tuple
Jun 11, 2026
Merged

Support per-element aggregation of Tuple columns in SummingMergeTree, AggregatingMergeTree and CoalescingMergeTree#98039
nihalzp merged 53 commits into
ClickHouse:masterfrom
JingYanchao:johnjing/subcolumn-aggregate-tuple

Conversation

@JingYanchao

@JingYanchao JingYanchao commented Feb 26, 2026

Copy link
Copy Markdown
Contributor

Changelog category (leave one):

  • New Feature

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

Add a new immutable MergeTree setting allow_tuple_element_aggregation, disabled by default. When enabled, SummingMergeTree, AggregatingMergeTree and CoalescingMergeTree recursively flatten Tuple columns and aggregate each leaf element independently during merges, exactly as if it were a top-level column — SummingMergeTree sums it, AggregatingMergeTree merges its aggregate-function state, and CoalescingMergeTree keeps its last non-NULL value. The setting must be specified at table creation time and is silently ignored by engines that do not support it.

Example:

CREATE TABLE t
(
    id   UInt64,
    data Tuple(
        id  UInt64,
        val SimpleAggregateFunction(sum, Float64)
    )
)
ENGINE = AggregatingMergeTree
ORDER BY id
SETTINGS allow_tuple_element_aggregation = 1;

Subcolumn data.val will be summed during merges, just as a top-level SimpleAggregateFunction column would be.


Note

Medium Risk
Changes core MergeTree merge algorithms to optionally flatten/reconstruct Tuple columns during merges, which can affect aggregation correctness and column ordering if edge cases slip through. Risk is mitigated by being gated behind a new immutable table setting and covered by new stateless tests.

Overview
Adds a new immutable MergeTree table setting allow_tuple_element_aggregation (default off) that enables recursive aggregation of Tuple leaf elements for SummingMergeTree, AggregatingMergeTree, and CoalescingMergeTree during merges.

When enabled, merge algorithms now flatten tuple columns (including unnamed and nested tuples, and materializing ColumnConst tuples) before selecting/processing aggregate columns, then reconstruct the original tuple structure after merging; the pipeline is plumbed through ReadFromMergeTree, MergeTask, and MergeTreeDataWriter. The PR also validates configuration by rejecting unsupported engines and disallowing plain Tuple columns in the sorting key under this mode, and updates ColumnAggregateFunction::isDefaultAt to return false instead of throwing.

Includes extensive new stateless tests covering SimpleAggregateFunction/AggregateFunction tuple elements, nested/unnamed tuples, overflow, and prior column-indexing edge cases.

Written by Cursor Bugbot for commit 71792f7. This will update automatically on new commits. Configure here.

Version info

  • Merged into: 26.6.1.628

@CLAassistant

CLAassistant commented Feb 26, 2026

Copy link
Copy Markdown

@JingYanchao JingYanchao changed the title Support tuple element aggregation and summing Recursively Support tuple element aggregation and summing recursively Feb 26, 2026
Comment thread src/Columns/ColumnAggregateFunction.h
Comment thread src/Storages/MergeTree/MergeTreeSettings.cpp Outdated
@amosbird amosbird added the can be tested Allows running workflows for external contributors label Feb 26, 2026
@clickhouse-gh

clickhouse-gh Bot commented Feb 26, 2026

Copy link
Copy Markdown
Contributor

Workflow [PR], commit [fe61ffb]

Summary:


AI Review

Summary

This PR adds opt-in recursive Tuple leaf aggregation for SummingMergeTree, AggregatingMergeTree, and CoalescingMergeTree, with docs, schema validation, and stateless coverage. The remaining blocker is that the AggregatingMergeTree merge path does not protect tuple leaves used by PARTITION BY, so the new aggregation can mutate partition-key values during FINAL, OPTIMIZE, or merge-on-insert.

Findings

❌ Blockers

  • [src/Processors/Merges/Algorithms/AggregatingSortedAlgorithm.cpp:24] AggregatingSortedAlgorithm decides aggregate vs copied tuple leaves using only the sorting key. The SummingMergeTree and CoalescingMergeTree paths pass partition+sorting required columns, but the AggregatingMergeTree call sites do not, so an opt-in table with metrics.part SimpleAggregateFunction(sum, UInt64) inside a Tuple, PARTITION BY metrics.part, and ORDER BY id will aggregate metrics.part for rows in the same partition. That can leave a part named/minmaxed for partition 10 containing a row whose partition leaf became 20, breaking partition metadata and pruning assumptions. Pass partition+sorting required columns through ReadFromMergeTree, MergeTask, MergeTreeDataWriter, AggregatingSortedTransform, and AggregatingSortedAlgorithm, then exclude matching leaves or ancestors from aggregation.
Tests
  • ⚠️ Add a focused AggregatingMergeTree regression with allow_tuple_element_aggregation = 1, a tuple SimpleAggregateFunction leaf in PARTITION BY, and duplicate ORDER BY keys, verifying both FINAL and OPTIMIZE TABLE ... FINAL preserve the partition leaf while aggregating non-key leaves.
Final Verdict

Status: ❌ Block

Minimum required action: fix the AggregatingMergeTree partition-key exclusion and add the regression above.

@clickhouse-gh clickhouse-gh Bot added the pr-improvement Pull request with some product improvements label Feb 26, 2026
@JingYanchao JingYanchao force-pushed the johnjing/subcolumn-aggregate-tuple branch 2 times, most recently from c5f285a to 661b6bd Compare February 28, 2026 04:00
Comment thread src/Processors/Merges/Algorithms/AggregatingSortedAlgorithm.cpp
Comment thread src/Processors/Merges/Algorithms/SummingSortedAlgorithm.cpp
Comment thread src/Columns/ColumnAggregateFunction.h
Comment thread src/Storages/MergeTree/MergeTreeData.cpp Outdated
Comment thread src/Storages/MergeTree/MergeTreeData.cpp
Comment thread src/DataTypes/NestedUtils.cpp
Comment thread src/Processors/Merges/Algorithms/AggregatingSortedAlgorithm.h
Comment thread src/Processors/Merges/Algorithms/SummingSortedAlgorithm.h
Comment thread tests/queries/0_stateless/03802_aggregating_merge_tree_tuple_element.sql Outdated
Comment thread tests/queries/0_stateless/03802_tuple_element_aggregation_settings_validation.sql Outdated
@JingYanchao JingYanchao force-pushed the johnjing/subcolumn-aggregate-tuple branch from 661b6bd to 7a3c9de Compare March 11, 2026 08:48
Comment thread src/Processors/Merges/Algorithms/AggregatingSortedAlgorithm.cpp
Comment thread src/Processors/Merges/Algorithms/SummingSortedAlgorithm.cpp Outdated
Comment thread src/Processors/Merges/Algorithms/AggregatingSortedAlgorithm.cpp Outdated
Comment thread src/Processors/Merges/Algorithms/SummingSortedAlgorithm.cpp Outdated
Comment thread src/Processors/Merges/Algorithms/SummingSortedAlgorithm.cpp
fix flatten bug

optimize code

optimize test cases

optimize test cases

optimize settings

optimize comment

optimize code review issue

optimize code review issue

# Conflicts:
#	src/Storages/MergeTree/MergeTreeData.cpp

optimize review code

fix bug

fix review bug

fix review bug
@JingYanchao JingYanchao force-pushed the johnjing/subcolumn-aggregate-tuple branch from 3e6351c to bf9176f Compare March 11, 2026 11:39

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Cursor Bugbot has reviewed your changes and found 2 potential issues.

Comment thread src/DataTypes/NestedUtils.cpp Outdated
Comment thread src/Processors/Merges/Algorithms/SummingSortedAlgorithm.cpp
@nihalzp nihalzp self-assigned this Mar 19, 2026

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

Thanks for working on this!

I was trying this query but it does not sum:

CREATE TABLE t (key UInt32, value Tuple(a UInt64, b UInt64))
ENGINE = SummingMergeTree(value) ORDER BY key
SETTINGS allow_tuple_element_aggregation = 1;

INSERT INTO t VALUES (1, (10, 20));
INSERT INTO t VALUES (1, (30, 40));
OPTIMIZE TABLE t FINAL;
SELECT * FROM t;
1       (10,20)

@alexey-milovidov

Copy link
Copy Markdown
Member

The Stress test (arm_msan) failure is fixed by #101239, which should be merged first. After it is merged, please update the branch to include the fix.

@JingYanchao JingYanchao requested a review from amosbird April 7, 2026 06:28
@JingYanchao

JingYanchao commented Apr 7, 2026

Copy link
Copy Markdown
Contributor Author

Thanks for working on this!

I was trying this query but it does not sum:

CREATE TABLE t (key UInt32, value Tuple(a UInt64, b UInt64))
ENGINE = SummingMergeTree(value) ORDER BY key
SETTINGS allow_tuple_element_aggregation = 1;

INSERT INTO t VALUES (1, (10, 20));
INSERT INTO t VALUES (1, (30, 40));
OPTIMIZE TABLE t FINAL;
SELECT * FROM t;
1       (10,20)

Thank you very much for pointing out this issue. The root cause was that, after the tuple was flattened, the resulting names could not be matched against the source column names recorded in column_names_to_sum, leading to an inability to identify them. I am now resolving this issue by flatten column_names_to_sum alongside the block. Additionally, I have added corresponding test cases to ensure adequate coverage.

@JingYanchao JingYanchao requested a review from nihalzp April 7, 2026 06:56
add coalescing table test cases

flatten column_names_to_sum

fix code space
Comment thread src/Processors/Merges/Algorithms/SummingSortedAlgorithm.cpp
@clickhouse-gh clickhouse-gh Bot added pr-feature Pull request with new product feature and removed pr-improvement Pull request with some product improvements labels Jun 10, 2026
Comment thread src/Storages/MergeTree/MergeTreeData.cpp

static AggregatingSortedAlgorithm::ColumnsDefinition defineColumns(
const Block & header, const SortDescription & description)
const Block & header, const SortDescription & description, bool allow_tuple_element_aggregation)

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.

AggregatingSortedAlgorithm now flattens tuple leaves and aggregates every AggregateFunction / SimpleAggregateFunction leaf that is not in the sorting key, but it still has no visibility into PARTITION BY required columns. Unlike the SummingMergeTree and CoalescingMergeTree paths, the AggregatingMergeTree call sites do not pass metadata.getPartitionKey().expression->getRequiredColumns, so an opt-in table can use a tuple leaf such as metrics.part SimpleAggregateFunction(sum, UInt64) in PARTITION BY metrics.part and ORDER BY id; merging rows in that partition will aggregate metrics.part from 10 to 20 while the part still belongs to partition 10 (partition id and minmax are computed before merge-on-insert too). Please pass partition+sorting required columns into AggregatingSortedAlgorithm, mark matching leaves or ancestors as column_numbers_not_to_aggregate, and add a regression covering FINAL and OPTIMIZE.

@nihalzp nihalzp Jun 10, 2026

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.

This is an existing issue/bug so out of scope for this PR.

CREATE TABLE t (id UInt64, part SimpleAggregateFunction(sum, UInt64))
ENGINE = AggregatingMergeTree PARTITION BY part ORDER BY id;

INSERT INTO t VALUES (1, 10);
INSERT INTO t VALUES (1, 10);
OPTIMIZE TABLE t FINAL;

SELECT * FROM t; -- 1	20; partition column summed

@clickhouse-gh

clickhouse-gh Bot commented Jun 10, 2026

Copy link
Copy Markdown
Contributor

LLVM Coverage Report

Metric Baseline Current Δ
Lines 84.60% 84.60% +0.00%
Functions 92.30% 92.30% +0.00%
Branches 77.20% 77.20% +0.00%

Changed lines: Changed C/C++ lines covered by tests: 387/401 (96.51%) | Lost baseline coverage (was covered on master, now uncovered in this PR): 3 line(s) · Uncovered code

Full report · Diff report

@nihalzp nihalzp added this pull request to the merge queue Jun 11, 2026
Merged via the queue into ClickHouse:master with commit 3376a37 Jun 11, 2026
327 of 328 checks passed
@robot-ch-test-poll3 robot-ch-test-poll3 added the pr-synced-to-cloud The PR is synced to the cloud repo label Jun 11, 2026
@clickgapai

Copy link
Copy Markdown
Contributor

Ergus pushed a commit to Ergus/ClickHouse that referenced this pull request Jun 11, 2026
govtech42 pushed a commit to mirror81/ClickHouse that referenced this pull request Jun 22, 2026
…use#101937)

Cover the exact ClickHouse#101937 reproducer: a CoalescingMergeTree with the canonical
.key/.value nested subcolumns (Array(String) key, Array(UInt32) value) merged
at query time via SELECT ... FINAL. This is a distinct branch from the existing
.id/.val OPTIMIZE FINAL case in the same test.

Verified bidirectionally: with the old *Map name-suffix special-casing in
SummingSortedAlgorithm::defineColumns reinstated the SELECT FINAL aborts with
Assertion 'px != 0' failed (null ColumnPtr); on current master it returns the
coalesced last value. The crash itself was fixed on master by ClickHouse#98039.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
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-feature Pull request with new product feature 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.

7 participants