Export partition - check name and pos of columns in partition key. - #2134
Export partition - check name and pos of columns in partition key.#2134k-morozov wants to merge 18 commits into
Conversation
Signed-off-by: Konstantin Morozov <just.morozov.k@gmail.com>
Signed-off-by: Konstantin Morozov <just.morozov.k@gmail.com>
9e63fd6 to
5322e48
Compare
Signed-off-by: Konstantin Morozov <just.morozov.k@gmail.com>
|
@codex review |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: faef463617
ℹ️ 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".
| auto partition_key_columns = source_metadata->getColumnsRequiredForPartitionKey(); | ||
| const std::unordered_set<String> partition_key_column_set( | ||
| std::make_move_iterator(partition_key_columns.begin()), | ||
| std::make_move_iterator(partition_key_columns.end())); |
There was a problem hiding this comment.
Normalize subcolumns before checking partition positions
When PARTITION BY references a subcolumn such as t.a, getColumnsRequiredForPartitionKey returns the subcolumn name, while source_columns contains only the top-level readable column t; consequently, this set never matches and the new positional check is skipped. For example, source columns (t Tuple(a UInt32), u Tuple(a UInt32)) and destination columns (u Tuple(a UInt32), t Tuple(a UInt32)), both partitioned by t.a, have positionally compatible types and pass validation, but export source u into destination t while constructing the partition from source t.a, silently mispartitioning the data. Map required subcolumns back to their top-level storage columns before constructing this set.
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
It's not a problem. Source-id here https://github.com/Altinity/ClickHouse/blob/antalya-26.3/src/Storages/ObjectStorage/DataLakes/Iceberg/Utils.cpp#L985 mapped only for columns (not for subcolumns).
up: not quite.
There was a problem hiding this comment.
If it is not a problem, can you write a test for it?
| const auto & source_column = source_columns[i]; | ||
| const auto & destination_column = destination_columns[i]; | ||
|
|
||
| if (partition_key_column_set.contains(source_column.name) && source_column.name != destination_column.name) |
There was a problem hiding this comment.
What if partitioning is for several columns, and destination has same columns but in different order?
Both have columns 'a' and 'b', in same order, but source with partition by a,b, and destination partition by b,a.
There was a problem hiding this comment.
Or when destination has more columns in partition list than source.
There was a problem hiding this comment.
I added the tests for simular cases:
What if partitioning is for several columns, and destination has same columns but in different order?
https://github.com/Altinity/ClickHouse/pull/2134/changes#diff-28d3e30160a3442e2b9ef01e3bf0b10b4c27de6a39efea3175ccec0a3f2d8949R1786
Or when destination has more columns in partition list than source.
https://github.com/Altinity/ClickHouse/pull/2134/changes#diff-28d3e30160a3442e2b9ef01e3bf0b10b4c27de6a39efea3175ccec0a3f2d8949R1799
|
|
||
| namespace | ||
| { | ||
| std::optional<String> getDateTimeTimeZoneName(const DataTypePtr & type) |
There was a problem hiding this comment.
I found method getExplicitTimeZoneOfDateTimeArgument like this.
And fragment from method extractTimeZoneNameFromFunctionArguments.
May be possible to reuse something?
There was a problem hiding this comment.
Seems they are a slightly defferent. But I could use checkAndGetDataType from them.
Signed-off-by: Konstantin Morozov <just.morozov.k@gmail.com>
mkmkme
left a comment
There was a problem hiding this comment.
The code indeed does what is expected from the description.
However I'm a bit hesitant still to just approve it because I'm not sure whether the description matches to what we actually want to do.
Let's say we have source columns (a, b, c, d, e) with partition by (a, b).
As expected, destination having (e, d, c, b, a) or even (b, a, c, d, e) will be rejected.
However, situations like (a, b, e, d, c) will be accepted (assuming matching types). And sometimes, especially considering how easy it can be to make a typo/mistake, it can lead to quite surprising results.
Overall, for me this fix looks good, but I would like to continue the discussion about all possible outcomes tomorrow. If we decide that this solution is fine for now I'll approve it :)
|
Here's the output of Note the first one. It's actually the very same as was reported by codex and then dismissed. Could you double-check that please whether this is something we can simply dismiss or need to fix? |
|
As discussed on the call, this change is good to go. Still, @k-morozov could you double-check the audit report on whether its first "High" issue is actually bogus and inapplicable? |
| i, | ||
| destination_column.name); | ||
|
|
||
| if (partition_key_column_set.contains(source_column.name)) |
There was a problem hiding this comment.
This is not very related to this PR, is it? It seems like a fix for #2138. I pushed a similar fix to it in my pr, but a more permissive one since the goal on my pr is to allow the monotonic stuff.
I think it is ok to keep this change here, and then I'll modify it in my pr. Your pr is likely going to be merged first.
| } | ||
| } | ||
|
|
||
| void verifyMergeTreePartitionCompatibility( |
There was a problem hiding this comment.
I would rename this to something like assertPartitionKeyASTAreEqual
Signed-off-by: Konstantin Morozov <just.morozov.k@gmail.com>
Signed-off-by: Konstantin Morozov <just.morozov.k@gmail.com>
Signed-off-by: Konstantin Morozov <just.morozov.k@gmail.com>
|
@codex review |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 9ce7987f46
ℹ️ 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".
Signed-off-by: Konstantin Morozov <just.morozov.k@gmail.com>
Signed-off-by: Konstantin Morozov <just.morozov.k@gmail.com>
Signed-off-by: Konstantin Morozov <just.morozov.k@gmail.com>
|
@codex review |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 2d7f3b2e68
ℹ️ 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".
| const auto source_time_zone = getDateTimeTimeZoneName(source_resolved->type); | ||
| const auto destination_time_zone = getDateTimeTimeZoneName(destination_resolved->type); |
There was a problem hiding this comment.
Inspect nested DateTime types in partition columns
When the partition key is a composite column, such as t Tuple(ts DateTime('UTC')), and the destination declares the same t column with DateTime('Asia/Tokyo'), these calls pass the whole Tuple type to getDateTimeTimeZoneName, which returns no timezone. canBeSafelyCast also accepts the tuple because DateTime::equals ignores timezone, so the export bypasses the new guard; with the wildcard strategy, WildcardPartitionStrategy computes the path using toString(t), causing the nested timestamp to be rendered in the destination timezone and silently changing the partition identifier. Recursively compare nested DateTime/DateTime64 members of partition-key columns.
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
The comment isn't related to the current changes — yes, we have a problem with DateTime (and more), but I'm adding name and position checks for the column in the partition by expression.
Signed-off-by: Konstantin Morozov <just.morozov.k@gmail.com>
Signed-off-by: Konstantin Morozov <just.morozov.k@gmail.com>
Signed-off-by: Konstantin Morozov <just.morozov.k@gmail.com>
|
@codex review |
|
Codex Review: Didn't find any major issues. Hooray! Reviewed commit: ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
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". |
@mkmkme Regarding first comment - in this description we don;t have a problem. It is safely use |
Signed-off-by: Konstantin Morozov <just.morozov.k@gmail.com>
Closes: #2123
Changelog category (leave one):
Changelog entry (a user-readable short description of the changes that goes to CHANGELOG.md):
Fixed
ALTER TABLE ... EXPORT PARTandEXPORT PARTITIONschema validation to prevent silently writing values into incorrect destination columns.The export now verifies, by name and position only, that the top-level column owning each
PARTITION BYcolumn or subcolumn occupies the same position in the source and destination schemas. If that column contains a namedTuple, its element names must also be declared in the same order - recursively through nested tuples and through container types such asArrayandMap(both the key and value type of aMapare checked). This protects both by-name subcolumn access (PARTITION BY t.a) and positional expressions (PARTITION BY tupleElement(t, 1),PARTITION BY tupleElement(arr[1], 'a')) from silently changing meaning after the positional cast.This check only compares names, positions, and
Tupleelement layout of the owning columns - it does not verify that the corresponding types are compatible (e.g. it does not detect aDateTime/DateTime64partition key columnusing different time zones in the source and destination tables, nor a
Tupleelement whose name matches but whose type differs). Such type mismatches, like all non-partition-key type mismatches, are still caught separately bythe existing positional cast check (rejected unless
export_merge_tree_part_allow_lossy_cast = 1is set).Documentation entry for user-facing changes
...
CI/CD Options
Exclude tests:
Regression jobs to run: