Skip to content

Export partition - allow non matching partition expressions in case we can prove the destination expression does not split the data - #2074

Open
arthurpassos wants to merge 46 commits into
antalya-26.3from
feature/antalya-26.3/export-partition-monotonic-compat
Open

Export partition - allow non matching partition expressions in case we can prove the destination expression does not split the data#2074
arthurpassos wants to merge 46 commits into
antalya-26.3from
feature/antalya-26.3/export-partition-monotonic-compat

Conversation

@arthurpassos

@arthurpassos arthurpassos commented Jul 17, 2026

Copy link
Copy Markdown
Collaborator

Changelog category (leave one):

  • Improvement

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

Allow export partition through different partition expressions as long as the destination expression does not repartition the data. This is validated at schedule time through two mechanisms:

  1. Structural match: in case the source and destination are identical, the destination expression is a subset of the source expression or the destination expression can be entirely computed using only constants and the exact values guaranteed (pinned) by the source.
  2. Dynamic proof: the destination expression is monotonic over the source partition min/max range.

Documentation entry for user-facing changes

...

CI/CD Options

Exclude tests:

  • Fast test
  • Integration Tests
  • Stateless tests
  • Stateful tests
  • Performance tests
  • Aarch64 tests
  • All with ASAN
  • All with TSAN
  • All with MSAN
  • All with UBSAN
  • All with Coverage
  • All Regression
  • Disable CI Cache

Regression jobs to run:

  • Fast suites (mostly <1h)
  • Aggregate Functions (2h)
  • Alter (1.5h)
  • Benchmark (30m)
  • ClickHouse Keeper (1h)
  • Iceberg (2h)
  • LDAP (1h)
  • OAuth (5m)
  • Parquet (1.5h)
  • RBAC (1.5h)
  • SSL Server (1h)
  • S3 (2h)
  • S3 Export (2h)
  • Swarms (30m)
  • Tiered Storage (2h)

@github-actions

github-actions Bot commented Jul 17, 2026

Copy link
Copy Markdown

Workflow [PR], commit [9a1cb91]

@arthurpassos

Copy link
Copy Markdown
Collaborator Author

@codex review

@chatgpt-codex-connector chatgpt-codex-connector 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 3f3af7c4fc

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src/Storages/MergeTree/ExportPartitionUtils.cpp Outdated
Comment thread src/Storages/MergeTree/ExportPartitionUtils.cpp Outdated
@ilejn

ilejn commented Jul 21, 2026

Copy link
Copy Markdown
Collaborator

Consider mentioning 'export' in the title and/or description

@arthurpassos arthurpassos changed the title Allow non matching partition expressions iff monotonicity is proved and data is not repartitioned Export partition - allow non matching partition expressions iff monotonicity is proved and data is not repartitioned Jul 21, 2026
@arthurpassos

Copy link
Copy Markdown
Collaborator Author

Consider mentioning 'export' in the title and/or description

Done, I'll soon add the description

@arthurpassos

Copy link
Copy Markdown
Collaborator Author

@codex review

@chatgpt-codex-connector chatgpt-codex-connector 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: ca43cf1f6d

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src/Storages/MergeTree/ExportPartitionUtils.cpp
@arthurpassos arthurpassos added port-antalya PRs to be ported to all new Antalya releases antalya-26.3 labels Aug 5, 2026
@arthurpassos

Copy link
Copy Markdown
Collaborator Author

Hey @arthurpassos - separate from the positional/name column-matching issue. I want to confirm the intended behaviour here before I pin it in a test.

What happens

A destination with an extra column is rejected on column count:

CREATE TABLE src (id Int64, a Int32)
ENGINE = ReplicatedMergeTree('/clickhouse/tables/shard0/src', '{replica}')
ORDER BY tuple() PARTITION BY a;

CREATE TABLE dst (id Int64, a Int32, b Int32)
ENGINE = S3(..., format='Parquet', partition_strategy='hive') PARTITION BY a;

INSERT INTO src VALUES (1, 42);

ALTER TABLE src EXPORT PARTITION ID '42' TO TABLE dst;
-- Code: 20. Number of columns doesn't match (source: 2 and result: 3).

That one seems right to me.

But giving the extra column a DEFAULT changes nothing:

CREATE TABLE dst (id Int64, a Int32, b Int32 DEFAULT 42)
ENGINE = S3(..., format='Parquet', partition_strategy='hive') PARTITION BY a;

ALTER TABLE src EXPORT PARTITION ID '42' TO TABLE dst;
-- Code: 20. Number of columns doesn't match (source: 2 and result: 3).

Same error, same counts - the DEFAULT is not taken into account.

What I'd expect

I'd expect the second case to succeed, with b filled from its default:

SELECT id, a, b FROM dst;
-- 1  42  42

which is what

INSERT INTO dst (id, a)
SELECT id, a
FROM src;

would produce.

A DEFAULT column is exactly the case where the destination can supply the value itself, so requiring the source to provide it seems off.

Is the current behaviour intended? There's no data-loss risk either way since it's a loud rejection - I just want the test to assert the intended behaviour rather than whichever one I guess at.

I think this deserves a separate issue

@arthurpassos

Copy link
Copy Markdown
Collaborator Author

I suggest adding setting to turn on/off this functionality.

Export partition is already experimental and back by a setting the user must opt in

@arthurpassos

Copy link
Copy Markdown
Collaborator Author

Can you check #2138 - this does seem like we should not be allowing the export here or have some other fix in this case.

it is not related to this PR, but I did push a fix: 3e60b54

@arthurpassos

Copy link
Copy Markdown
Collaborator Author

@Selfeer I would appreciate if you could "humanize" a bit more those AI reports. For example, the following issue is very hard to understand and doesn't explain what is happening: 1. 🔴 High — Argument order lost, monotonicity proof evaluates the wrong expression.

It says "anchor" pointing to some code location. Then it says the impact. Then the trigger. It is too much to read to understand the real problem. Brain energy required to process this is very high.

Instead, if you could understand the issue yourself first and then give me a few SQL instructions that repro the case and a human explanation of what's going on, that would be 100 times better.

@Selfeer

Selfeer commented Aug 6, 2026

Copy link
Copy Markdown
Collaborator

@Selfeer I would appreciate if you could "humanize" a bit more those AI reports. For example, the following issue is very hard to understand and doesn't explain what is happening: 1. 🔴 High — Argument order lost, monotonicity proof evaluates the wrong expression.

It says "anchor" pointing to some code location. Then it says the impact. Then the trigger. It is too much to read to understand the real problem. Brain energy required to process this is very high.

Instead, if you could understand the issue yourself first and then give me a few SQL instructions that repro the case and a human explanation of what's going on, that would be 100 times better.

I agree. I'd much rather do it the way you described: perform my own investigation on the findings and raise issues as needed-and we actually do that. But even in that case, I would still have to post this exact message first.

The purpose of the audit review has always been to perform a quick review of the PR without running any tests first, share the findings with the developer, and let you decide whether they are actual issues. If they are, we then raise separate issues afterward.

I can update the skills we use for the audit review to make the output easier to read, but overall, the audit review has always been a separate part of the verification process, separate from our actual testing.

@arthurpassos arthurpassos changed the title Export partition - allow non matching partition expressions iff monotonicity is proved and data is not repartitioned Export partition - allow non matching partition expressions in case we can prove the destination expression does not split the data Aug 20, 2026
@arthurpassos

Copy link
Copy Markdown
Collaborator Author

@k-morozov hi, I have made some refactorings, fixed conflicts and updated the docs. Could you please re-review it?

@arthurpassos

Copy link
Copy Markdown
Collaborator Author

@Selfeer regarding the 5 AI findings you posted

  1. Should be fixed
  2. Seems like AI gibberish. At the very least, not related to this pr
  3. Should be fixed
  4. Outdated, that code path does not exist anymore
  5. Not related to this pr

"To allow its usage, enable the setting `allow_insert_into_iceberg`.");
}

ExportPartitionUtils::verifyExportSchemaCastable(

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Calls ExportPartitionUtils::verifyExportSchemaCastable twice here and below.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Thanks, bad rebase

)


def test_export_part_unnamed_tuple_partition_key_owner_matching_named_destination_is_allowed(cluster):

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

What's the reason?

/// so the same expression over the two types can produce different partitions.
bool isSameTypeForPartitioning(const DataTypePtr & lhs, const DataTypePtr & rhs)
{
return lhs->getName() == rhs->getName();

@k-morozov k-morozov Aug 21, 2026

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

What about unnamed Tuple, Array, etc?
Does it related with test test_export_part_unnamed_tuple_partition_key_owner_matching_named_destination_is_allowed that was removed?
up: maybe haveSameTupleElementLayout will be usefull.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Answering #2074 (comment) here as it relates to this question.

The main reason I removed that test, and yes, it is affected by this name check, is to draw a line of "good enough" and move on. This PR needs to get merged so that we fix the broader and more common use cases, we can worry about named tuples later.


/// A NULL value forms its own destination partition, so a Nullable column may split the source
/// partition; min/max cannot rule that out. Require a structural match for such columns.
if (isNullableOrLowCardinalityNullable(source_type))

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Seems that this behauvor should be in documentation (and motivation maybe?) Also I need to find tests for this condition..


/// Compute the global min/max index of the parts
IMergeTreeDataPart::MinMaxIndex minmax;
for (const auto & part : parts)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

As I can see, we prepare mimax every time even if we have an equal partition key. But we really need it only for verifyOutputMapsToSinglePartition.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Right, but verifyOutputMapsToSinglePartition is called within a for loop. What is your suggestion?

@k-morozov k-morozov Aug 21, 2026

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

For example, create 2 loops:
1 for happy path.
2. for verifyOutputMapsToSinglePartition.

So we could create minmax between 1 and 2 steps.

Another way - to fill minmax within the current loop only once before verifyOutputMapsToSinglePartition.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Imo, that's somewhat of an unecessary "optimization" that will not give us any speed up with the cost of more code. I vote for keeping it as is unless you strongly object.

if (parquet_row_group_size_bytes)
json.set("parquet_row_group_size_bytes", *parquet_row_group_size_bytes);
if (iceberg_partition_timezone)
json.set("iceberg_partition_timezone", *iceberg_partition_timezone);

@k-morozov k-morozov Aug 21, 2026

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

We try to set this setting for export. But if we have several columns with different timezones, we would set the value from the setting for all of them, right?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

I think that's how this setting was designed, @ianton-ru can confirm. AFAIK, this setting was created due to a misconception on the Apache Iceberg specs (the spec itself is poorly written) when it was believed we could tweak the timezone of an apache iceberg column. When in reality, according to 3rd party docs, the types are always UTC.

Comment thread src/Storages/MergeTree/ExportPartitionUtils.cpp
k-morozov
k-morozov previously approved these changes Aug 21, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

antalya antalya-26.3 port-antalya PRs to be ported to all new Antalya releases

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants