Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
77 commits
Select commit Hold shift + click to select a range
04ad089
Backport #102360 to 26.3: fix data race for ast in database memory on…
robot-clickhouse Apr 22, 2026
efce815
Backport #103858 to 26.3: Fix data part check and consistency check f…
robot-clickhouse May 2, 2026
be638bc
Backport #100758 to 26.3: Erase sorted_dynamic_paths entries before d…
robot-clickhouse May 4, 2026
67caa16
Backport #102884 to 26.3: Optimization for deferring row policy and P…
robot-clickhouse May 4, 2026
4849292
Update autogenerated version to 26.3.10.62 and contributors
robot-clickhouse May 7, 2026
1592c74
Backport #104133 to 26.3: Fix numerous semantic inconsistency with `o…
robot-clickhouse May 8, 2026
7f43038
Backport #103708 to 26.3: Fix skip-index matching for ALIAS columns w…
robot-clickhouse May 8, 2026
1269501
Update autogenerated version to 26.3.10.60 and contributors
robot-clickhouse May 8, 2026
10d03b5
Merge pull request #104340 from ClickHouse/backport/26.3/104133
nihalzp May 8, 2026
f07e599
Revert "Update autogenerated version to 26.3.10.60 and contributors"
maxknv May 8, 2026
420f1e8
Merge pull request #104380 from ClickHouse/revert_wrong_release_commit
Felixoid May 8, 2026
c2c7c0a
Backport #103384 to 26.3: Make a decision to skip backup of the targe…
robot-clickhouse May 8, 2026
25a39b7
Backport #101504 to 26.3: Fix join reorder pushing INNER JOIN conditi…
robot-clickhouse May 8, 2026
1cd3cf2
Merge pull request #103902 from ClickHouse/backport/26.3/103858
fm4v May 9, 2026
a19fe3c
Backport #100375 to 26.3: Fix Not-ready Set exception when IN subquer…
robot-clickhouse May 9, 2026
1b5dd29
Backport #104317 to 26.3: Fix projection matching regression from `re…
robot-clickhouse May 9, 2026
41e608b
Merge pull request #104481 from ClickHouse/backport/26.3/104317
fm4v May 10, 2026
95c14f2
Merge pull request #104471 from ClickHouse/backport/26.3/100375
fm4v May 10, 2026
5d832f6
Merge pull request #104384 from ClickHouse/backport/26.3/103384
fm4v May 11, 2026
4665533
Merge pull request #104365 from ClickHouse/backport/26.3/103708
CurtizJ May 11, 2026
b4dfe08
Backport fix for IPv6StringToNumOrDefault to 26.3
Avogar May 11, 2026
68d5adc
Backport #103277 to 26.3: Fix data race in dictionaries
robot-clickhouse May 11, 2026
b8baac1
Backport #104065 to 26.3: Refreshable MV: relax sanity checks in SECO…
robot-clickhouse May 12, 2026
4e2c591
Backport #104229 to 26.3: Fix int32_t overflow in `lowerUTF8`/`upperU…
robot-clickhouse May 12, 2026
5cb0855
Merge pull request #104687 from ClickHouse/backport/26.3/104065
fm4v May 12, 2026
0a9dc9b
Merge pull request #104406 from ClickHouse/backport/26.3/101504
vdimir May 12, 2026
4fbe502
Merge pull request #104026 from ClickHouse/backport/26.3/100758
Avogar May 12, 2026
c72d063
Backport #104678 to 26.3: Revert "Fix data part check and consistency…
robot-clickhouse May 12, 2026
9f48636
Merge pull request #104574 from Avogar/backport/26.3/93543
Avogar May 12, 2026
799bb75
Merge pull request #104702 from ClickHouse/backport/26.3/104229
alexey-milovidov May 12, 2026
f68d64b
Backport #104322 to 26.3: fixing a possible underflow while parsing p…
robot-clickhouse May 12, 2026
07766f8
Merge pull request #104760 from ClickHouse/backport/26.3/104322
grantholly-clickhouse May 13, 2026
3885d75
Merge pull request #104727 from ClickHouse/backport/26.3/104678
nihalzp May 13, 2026
31d9847
Backport #104751 to 26.3: Fix use-after-free in `AvroConfluentRowInpu…
robot-clickhouse May 13, 2026
271f51c
Merge pull request #103343 from ClickHouse/backport/26.3/102360
fm4v May 13, 2026
97fe7b0
Merge pull request #104846 from ClickHouse/backport/26.3/104751
mstetsyuk May 13, 2026
29069be
Merge pull request #104618 from ClickHouse/backport/26.3/103277
fm4v May 14, 2026
965bab7
Backport #101484 to 26.3: Add missing Keeper component tracking guard…
robot-clickhouse May 14, 2026
0fb5eeb
Backport #104673 to 26.3: Fix data race in FutureSetFromTuple
robot-clickhouse May 14, 2026
e993f66
Merge pull request #104938 from ClickHouse/backport/26.3/101484
Algunenano May 15, 2026
d0f294a
Merge pull request #104958 from ClickHouse/backport/26.3/104673
mstetsyuk May 15, 2026
54ccc45
Backport #98827 to 26.3: Implement http header validation for DataLak…
robot-clickhouse May 15, 2026
b442640
Merge pull request #105017 from ClickHouse/backport/26.3/98827
Algunenano May 15, 2026
29e7e94
Backport #104705 to 26.3: Add `defer_partition_pruning_after_final` s…
robot-clickhouse May 15, 2026
37c84de
Backport #103890 to 26.3: Fix use-after-free in KeeperHandlingConsume…
robot-clickhouse May 15, 2026
fe2c36e
Merge pull request #105081 from ClickHouse/backport/26.3/103890
antaljanosbenjamin May 15, 2026
363bc1e
Backport #105048 to 26.3: JIT: register __fixunssfti / __fixunsdfti f…
robot-clickhouse May 16, 2026
1c99a07
Merge pull request #105096 from ClickHouse/backport/26.3/105048
alexey-milovidov May 16, 2026
b844b14
Merge pull request #105063 from ClickHouse/backport/26.3/104705
fm4v May 16, 2026
1ce7df4
Backport #101580 to 26.3: Fix flaky test 03706_statistics_preserve_ch…
robot-clickhouse May 18, 2026
4b6bf90
Backport #103536 to 26.3: Fix heap-use-after-free in `executeAggregat…
robot-clickhouse May 18, 2026
55fb72f
Backport #104663 to 26.3: Fix Keeper termination on get `/keeper/avai…
robot-clickhouse May 18, 2026
fac697a
Backport #104888 to 26.3: Fix data part consistency checks for types …
robot-clickhouse May 18, 2026
173a7cc
Backport #104009 to 26.3: Fix SQL injection in ExternalQueryBuilder v…
robot-ch-test-poll1 May 19, 2026
e195d92
Merge pull request #105236 from ClickHouse/backport/26.3/104663
antonio2368 May 19, 2026
b6efd5e
Merge pull request #105206 from ClickHouse/backport/26.3/101580
Algunenano May 19, 2026
e9cdf9d
Merge pull request #105216 from ClickHouse/backport/26.3/103536
Algunenano May 19, 2026
5c6f600
Merge pull request #104034 from ClickHouse/backport/26.3/102884
yariks5s May 19, 2026
50e2fac
Merge pull request #105271 from ClickHouse/backport/26.3/104888
Avogar May 19, 2026
be8e4da
Backport #104610 to 26.3: Use restore Keeper retries for restored parts
robot-clickhouse May 19, 2026
9b5a0a0
Backport #102064 to 26.3: Fix incorrect results for RIGHT ANY JOIN
robot-clickhouse May 19, 2026
8fc1a97
Backport #102417 to 26.3: Check for stack overflow in Avro reader
robot-clickhouse May 19, 2026
e84fd35
Merge pull request #105343 from ClickHouse/backport/26.3/104610
pamarcos May 20, 2026
692332c
Merge pull request #105364 from ClickHouse/backport/26.3/102064
vdimir May 20, 2026
e6bc9b7
Fix `contrib/avro` submodule pointer for stack overflow protection
Avogar May 20, 2026
b5c068a
Merge pull request #105379 from ClickHouse/backport/26.3/102417
Avogar May 21, 2026
26e3465
Backport #104881 to 26.3: Use explicit flag for secondary on cluster …
robot-clickhouse May 21, 2026
705eb74
Backport #105492 to 26.3: Skip test_numbers_check on release branches
robot-clickhouse May 21, 2026
3a04d41
Backport #103148 to 26.3: Fix backward compatibility break in sendPro…
robot-clickhouse May 22, 2026
ac5a410
Merge pull request #105597 from ClickHouse/backport/26.3/105492
Algunenano May 22, 2026
207874d
Backport #105449 to 26.3: Fix heap-buffer-overflow and null-pointer d…
robot-clickhouse May 22, 2026
fa3aa24
Merge pull request #105611 from ClickHouse/backport/26.3/103148
fm4v May 22, 2026
c13b94c
Fix geoparquet metadata handling in Arrow IPC format
Algunenano May 22, 2026
e44ae6e
Update autogenerated version to 26.3.11.36 and contributors
robot-clickhouse May 22, 2026
d77e3c1
Merge pull request #105575 from ClickHouse/backport/26.3/104881
tavplubix May 22, 2026
d23c753
Merge pull request #105620 from ClickHouse/backport/26.3/105449
Algunenano May 22, 2026
f39026c
Merge tag 'v26.3.12.3-lts' into bump/antalya-26.3/26.3.12
zvonand Jun 4, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 8 additions & 1 deletion ci/jobs/check_style.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
from concurrent.futures import ProcessPoolExecutor
from pathlib import Path

from praktika.info import Info
from praktika.result import Result
from praktika.utils import Shell, Utils

Expand Down Expand Up @@ -573,7 +574,13 @@ def parse_args():
)
)
testname = "test_numbers_check"
if testpattern.lower() in testname.lower():
# Skip on release branches and backport PRs: backports cherry-pick a small
# subset of test files, which legitimately leaves large gaps in the numbering.
info = Info()
release_branch_re = re.compile(r"^\d{2}\.\d+$")
branch_to_check = (info.base_branch or info.git_branch or "").removeprefix("release/")
is_release_branch = bool(release_branch_re.match(branch_to_check))
if testpattern.lower() in testname.lower() and not is_release_branch:
results.append(
Result.from_commands_run(
name=testname,
Expand Down
10 changes: 5 additions & 5 deletions cmake/autogenerated_versions.txt
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,13 @@

# NOTE: VERSION_REVISION has nothing common with DBMS_TCP_PROTOCOL_VERSION,
# only DBMS_TCP_PROTOCOL_VERSION should be incremented on protocol changes.
SET(VERSION_REVISION 54517)
SET(VERSION_REVISION 54519)
SET(VERSION_MAJOR 26)
SET(VERSION_MINOR 3)
SET(VERSION_PATCH 10)
SET(VERSION_GITHASH 0d82c1998e3b4d54e110dd8a77a64d247c4bff4a)
SET(VERSION_DESCRIBE v26.3.10.20001.altinityantalya)
SET(VERSION_STRING 26.3.10.20001.altinityantalya)
SET(VERSION_PATCH 12)
SET(VERSION_GITHASH fa3aa24e79104d29f8bfde078fc586ec6ac3a565)
SET(VERSION_DESCRIBE v26.3.12.20001.altinityantalya)
SET(VERSION_STRING 26.3.12.20001.altinityantalya)
# end of autochange

SET(VERSION_TWEAK 20001)
Expand Down
149 changes: 137 additions & 12 deletions src/Analyzer/Passes/InverseDictionaryLookupPass.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,11 @@

#include <Interpreters/Context.h>

#include <DataTypes/DataTypeLowCardinality.h>
#include <DataTypes/DataTypeNullable.h>
#include <DataTypes/DataTypeTuple.h>

#include <Functions/FunctionFactory.h>
#include <Functions/FunctionsExternalDictionaries.h>

#include <Access/ContextAccess.h>
Expand All @@ -26,6 +31,9 @@ namespace DB

namespace Setting
{
extern const SettingsUInt64 max_bytes_in_set;
extern const SettingsUInt64 max_rows_in_set;
extern const SettingsOverflowMode set_overflow_mode;
extern const SettingsBool optimize_inverse_dictionary_lookup;
extern const SettingsBool rewrite_in_to_join;
}
Expand Down Expand Up @@ -68,8 +76,7 @@ bool isSupportedDictGetFunction(const String & name)
"dictGetDateTime",
"dictGetUUID",
"dictGetIPv4",
"dictGetIPv6",
"dictGetOrNull"};
"dictGetIPv6"};

return supported_functions.contains(name);
}
Expand Down Expand Up @@ -127,6 +134,98 @@ void resolveNode(const Node & node, const ContextPtr & context)
QueryAnalysisPass(/*only_analyze*/ false).run(querytree_node, context);
}

bool hasNullableComponentInComplexKey(const QueryTreeNodePtr & key_expr_node)
{
auto type = removeNullable(key_expr_node->getResultType());
const auto * tuple_type = typeid_cast<const DataTypeTuple *>(type.get());
if (!tuple_type)
return false;

for (const auto & element : tuple_type->getElements())
{
if (isNullableOrLowCardinalityNullable(element))
return true;
}
return false;
}

bool isRewriteSemanticallySafe(
const DataTypePtr & dict_attr_type,
const DataTypePtr & dictget_result_type,
const Field & attr_null_value,
const ConstantNode & const_arg_node,
bool default_is_lhs,
const String & attr_comparison_function_name,
const ContextPtr & context)
{
/// Same underlying type after stripping `Nullable` / `LowCardinality` needed. If attribute `n`
/// is `UInt32`, `dictGetUInt16(..., 'n', id) = 42` throws because the underlying types differ (`UInt32` vs `UInt16`)
const bool stripped_types_match
= removeLowCardinalityAndNullable(dict_attr_type)->equals(*removeLowCardinalityAndNullable(dictget_result_type));
if (!stripped_types_match)
return false;

/// `dictGet` and `IN` don't have the same stored-NULL attribute semantics.
/// Example: if dictionary has `id = 1, name = NULL`, `dictGet(..., 1) = 'x'` gives
/// `NULL`. The `IN` rewrite uses `WHERE name = 'x'`, so the row is filtered out and
/// `1 IN (...)` gives `0`. This is visible in projection or `isNull(predicate)`.
/// Skip optimization when the attribute can contain `NULL`, including
/// `LowCardinality(Nullable(...))`.
if (isNullableOrLowCardinalityNullable(dict_attr_type))
return false;

const DataTypePtr const_arg_type = const_arg_node.getResultType();
const Field & const_arg_value = const_arg_node.getValue();

auto default_column = ColumnWithTypeAndName(dict_attr_type->createColumnConst(1, attr_null_value), dict_attr_type, "default_value");
auto const_arg_column = ColumnWithTypeAndName(const_arg_type->createColumnConst(1, const_arg_value), const_arg_type, "const_value");

ColumnsWithTypeAndName comparison_arguments;
if (default_is_lhs)
comparison_arguments = {std::move(default_column), std::move(const_arg_column)};
else
comparison_arguments = {std::move(const_arg_column), std::move(default_column)};

/// `dictGet` and `IN` don't have the same missing-key default semantics.
/// e.g: `dictGet(..., id) = ''` vs `id IN (SELECT id FROM dictionary(...) WHERE name = '')`
/// Example: if dictionary has one row `id = 1, name = 'x'`, data has `id = 2`, and
/// attribute `DEFAULT` is `''`, `dictGet(..., 2)` returns `''`, so
/// `dictGet(..., id) = ''` is true for `id = 2`. The `IN` rewrite scans only
/// dictionary keys, so the subquery has no `id = 2` and `2 IN (...)` is false.
///
/// One of the alternatives is to add `OR id NOT IN (SELECT id FROM dictionary(...))` when the
/// predicate is true for `DEFAULT`, but it requires another set with all dictionary keys.
/// This can be expensive to materialize, so skip optimization for such case.
///
/// As a result, given the current rewrite, if `const <op> DEFAULT` is false, only then the
/// transformation is semantically correct.
Field comparison_result;
try
{
auto function_resolver = FunctionFactory::instance().get(attr_comparison_function_name, context);
auto comparison_function_base = function_resolver->build(comparison_arguments);
auto comparison_result_column
= comparison_function_base->execute(comparison_arguments, comparison_function_base->getResultType(), 1, /* dry_run = */ false);
comparison_result = (*comparison_result_column)[0];
}
catch (const Exception &)
{
/// The constant fold runs during optimization and can throw for values that runtime
/// would not evaluate. Example: `match('', '(')` throws `CANNOT_COMPILE_REGEXP`, but
/// `id < 0 AND match(dictGetString(...), '(')` can skip the `match` branch due to
/// short-circuit evaluation. If we throw here, the optimization breaks a query that
/// works without it. Skip optimization for such case.
return false;
}

if (comparison_result.isNull())
return false;

/// Check `const <op> DEFAULT` is false
UInt64 comparison_result_uint = 0;
return comparison_result.tryGet<UInt64>(comparison_result_uint) && comparison_result_uint == 0;
}


class InverseDictionaryLookupVisitor : public InDepthQueryTreeVisitorWithContext<InverseDictionaryLookupVisitor>
{
Expand All @@ -142,6 +241,14 @@ class InverseDictionaryLookupVisitor : public InDepthQueryTreeVisitorWithContext
if (getSettings()[Setting::rewrite_in_to_join])
return;

/// We build an `IN` set from the dictionary subquery, which respects `max_rows_in_set`,
/// `max_bytes_in_set` and `set_overflow_mode`. With `set_overflow_mode = 'break'`, the set
/// can be truncated and not contain all required elements, so the optimization can produce
/// wrong results. Skip optimization for such case.
if ((getSettings()[Setting::max_rows_in_set] != 0 || getSettings()[Setting::max_bytes_in_set] != 0)
&& getSettings()[Setting::set_overflow_mode] == OverflowMode::BREAK)
return;

auto * node_function = node->as<FunctionNode>();

if (!node_function)
Expand Down Expand Up @@ -226,12 +333,37 @@ class InverseDictionaryLookupVisitor : public InDepthQueryTreeVisitorWithContext
return;
}

/// For complex-key dictionaries, `dictGet` and `IN` don't have the same `NULL` key semantics.
/// e.g: `dictGet(..., (k1, k2))` vs `(k1, k2) IN (SELECT k1, k2 FROM dictionary(...))`
/// Example: if `k1` is `Nullable(UInt64)` and the dictionary has `(NULL, 'a')`,
/// `dictGet(..., (k1, k2))` can match it, but `(NULL, 'a') IN (...)` is not a match
/// with `transform_null_in = 0`.
/// Single-key dictionaries are not affected. Example: if `id` is `Nullable(UInt64)`,
/// `dictGet(..., id) = 'x'` gives `NULL` for `id = NULL`, and `id IN (...)` also gives
/// `NULL` for `id = NULL`.
if (dict_structure.key && hasNullableComponentInComplexKey(dictget_function_info.key_expr_node))
return;

const String attr_col_name = dictget_function_info.attr_col_name_node->getValue().safeGet<String>();

if (!dict_structure.hasAttribute(attr_col_name))
return;

DataTypePtr dict_attr_col_type = dict_structure.getAttribute(attr_col_name).type;
const DictionaryAttribute & attr = dict_structure.getAttribute(attr_col_name);
DataTypePtr dict_attr_col_type = attr.type;

const auto * const_arg_node = (dict_side == Side::LHS) ? arguments[1]->as<ConstantNode>() : arguments[0]->as<ConstantNode>();

/// Skip rewrites that would change query behavior. Details are in the function.
if (!isRewriteSemanticallySafe(
dict_attr_col_type,
dictget_function_info.return_type,
attr.null_value,
*const_arg_node,
dict_side == Side::LHS,
attr_comparison_function_name,
getContext()))
return;

auto dict_table_function = std::make_shared<TableFunctionNode>("dictionary");
dict_table_function->getArguments().getNodes().push_back(dictget_function_info.dict_name_node);
Expand All @@ -240,23 +372,16 @@ class InverseDictionaryLookupVisitor : public InDepthQueryTreeVisitorWithContext
NameAndTypePair attr_col{attr_col_name, dict_attr_col_type};
auto attr_col_node = std::make_shared<ColumnNode>(attr_col, dict_table_function);

/// Needed for dictGet functions like `dictGetString`, `dictGetInt32`, etc.
QueryTreeNodePtr attr_col_node_casted = attr_col_node;
if (!attr_col_node->getResultType()->equals(*dictget_function_info.return_type))
{
attr_col_node_casted = createCastFunction(attr_col_node, dictget_function_info.return_type, getContext());
}

auto attr_comparison_function_node = std::static_pointer_cast<FunctionNode>(node_function->clone());
attr_comparison_function_node->markAsOperator();

if (dict_side == Side::LHS)
{
attr_comparison_function_node->getArguments().getNodes() = { attr_col_node_casted, arguments[1] };
attr_comparison_function_node->getArguments().getNodes() = { attr_col_node, arguments[1] };
}
else
{
attr_comparison_function_node->getArguments().getNodes() = { arguments[0], attr_col_node_casted };
attr_comparison_function_node->getArguments().getNodes() = { arguments[0], attr_col_node };
}
resolveOrdinaryFunctionNodeByName(*attr_comparison_function_node, attr_comparison_function_name, getContext());

Expand Down
65 changes: 46 additions & 19 deletions src/Backups/BackupEntriesCollector.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,12 @@
#include <Databases/IDatabase.h>
#include <Interpreters/Context.h>
#include <Interpreters/DatabaseCatalog.h>
#include <Interpreters/StorageID.h>
#include <Parsers/ASTCreateQuery.h>
#include <Storages/IStorage.h>
#include <Storages/MergeTree/extractZooKeeperPathFromReplicatedTableDef.h>
#include <Storages/StorageMaterializedView.h>
#include <Common/typeid_cast.h>
#include <base/chrono_io.h>
#include <base/insertAtEnd.h>
#include <base/scope_guard.h>
Expand Down Expand Up @@ -524,6 +527,14 @@ void BackupEntriesCollector::gatherTablesMetadata()
checkIsQueryCancelled();

table_infos.clear();

/// Collect target tables of refreshable materialized views that use the REPLACE
/// refresh strategy (APPEND is excluded) among the tables being backed up.
/// We build this snapshot from the storages we've already found, so the decision
/// in `shouldBackupTableData` doesn't need to query `DatabaseCatalog` again and
/// is scoped to the tables within the backup.
std::unordered_set<StorageID, StorageID::DatabaseAndTableNameHash, StorageID::DatabaseAndTableNameEqual> rmv_replace_target_ids;

for (const auto & [database_name, database_info] : database_infos)
{
std::vector<std::pair<ASTPtr, StoragePtr>> db_tables = findTablesInDatabase(database_name);
Expand Down Expand Up @@ -551,35 +562,48 @@ void BackupEntriesCollector::gatherTablesMetadata()
/ escapeForFileName(table_name_in_backup.table);
}

/// Add information to `table_infos`.
const auto qualified_name = QualifiedTableName{database_name, table_name};
auto & res_table_info = table_infos[qualified_name];
res_table_info.database = database_info.database;
res_table_info.storage = storage;
res_table_info.create_table_query = create_table_query;
res_table_info.metadata_path_in_backup = metadata_path_in_backup;
res_table_info.data_path_in_backup = data_path_in_backup;
res_table_info.should_backup_data = shouldBackupTableData(qualified_name, storage);

if (res_table_info.should_backup_data)
if (const auto * mv = typeid_cast<const StorageMaterializedView *>(storage.get()))
{
auto it = database_info.tables.find(table_name);
if (it != database_info.tables.end())
{
const auto & partitions = it->second.partitions;
if (partitions && storage && !storage->supportsBackupPartition())
{
throw Exception(
ErrorCodes::CANNOT_BACKUP_TABLE,
"Table engine {} doesn't support partitions, cannot backup {}",
storage->getName(),
tableNameWithTypeToString(database_name, table_name, false));
}
res_table_info.partitions = partitions;
}
if (mv->isRefreshable() && !mv->isAppendRefreshStrategy())
rmv_replace_target_ids.insert(mv->getTargetTableId());
}
}
}

/// Second pass: now that we have the full snapshot of tables and RMV targets,
/// decide whether the data of each table should be backed up and validate
/// partition-related constraints.
for (auto & [qualified_name, res_table_info] : table_infos)
{
res_table_info.should_backup_data = shouldBackupTableData(qualified_name, res_table_info.storage, rmv_replace_target_ids);

if (!res_table_info.should_backup_data)
continue;

const auto & database_info = database_infos.at(qualified_name.database);
auto it = database_info.tables.find(qualified_name.table);
if (it == database_info.tables.end())
continue;

const auto & partitions = it->second.partitions;
if (partitions && res_table_info.storage && !res_table_info.storage->supportsBackupPartition())
{
throw Exception(
ErrorCodes::CANNOT_BACKUP_TABLE,
"Table engine {} doesn't support partitions, cannot backup {}",
res_table_info.storage->getName(),
tableNameWithTypeToString(qualified_name.database, qualified_name.table, false));
}
res_table_info.partitions = partitions;
}
}

std::vector<std::pair<ASTPtr, StoragePtr>> BackupEntriesCollector::findTablesInDatabase(const String & database_name) const
Expand Down Expand Up @@ -857,7 +881,10 @@ void BackupEntriesCollector::makeBackupEntriesForTableData(const QualifiedTableN
}
}

bool BackupEntriesCollector::shouldBackupTableData(const QualifiedTableName & table_name, const StoragePtr & storage) const
bool BackupEntriesCollector::shouldBackupTableData(
const QualifiedTableName & table_name,
const StoragePtr & storage,
const std::unordered_set<StorageID, StorageID::DatabaseAndTableNameHash, StorageID::DatabaseAndTableNameEqual> & rmv_replace_target_ids) const
{
if (backup_settings.structure_only)
return false;
Expand All @@ -866,7 +893,7 @@ bool BackupEntriesCollector::shouldBackupTableData(const QualifiedTableName & ta
return true;

if (!backup_settings.backup_data_from_refreshable_materialized_view_targets
&& BackupUtils::isTargetForReplaceRefreshableMaterializedView(storage->getStorageID(), context))
&& rmv_replace_target_ids.contains(storage->getStorageID()))
{
LOG_TRACE(log, "Skipping table data for {} (a target of a refreshable materialized view)", table_name.getFullName());
return false;
Expand Down
7 changes: 5 additions & 2 deletions src/Backups/BackupEntriesCollector.h
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
#include <Backups/BackupSettings.h>
#include <Core/QualifiedTableName.h>
#include <Databases/DDLRenamingVisitor.h>
#include <Interpreters/StorageID.h>
#include <Parsers/ASTBackupQuery.h>
#include <Storages/IStorage_fwd.h>
#include <Storages/TableLockHolder.h>
Expand All @@ -20,7 +21,6 @@ using BackupEntries = std::vector<std::pair<String, BackupEntryPtr>>;
class IBackupCoordination;
class IDatabase;
using DatabasePtr = std::shared_ptr<IDatabase>;
struct StorageID;
struct IAccessEntity;
using AccessEntityPtr = std::shared_ptr<const IAccessEntity>;
class QueryStatus;
Expand Down Expand Up @@ -97,7 +97,10 @@ class BackupEntriesCollector : private boost::noncopyable
void makeBackupEntriesForTablesDefs();
void makeBackupEntriesForTablesData();
void makeBackupEntriesForTableData(const QualifiedTableName & table_name);
bool shouldBackupTableData(const QualifiedTableName & table_name, const StoragePtr & storage) const;
bool shouldBackupTableData(
const QualifiedTableName & table_name,
const StoragePtr & storage,
const std::unordered_set<StorageID, StorageID::DatabaseAndTableNameHash, StorageID::DatabaseAndTableNameEqual> & rmv_replace_target_ids) const;

void addBackupEntryUnlocked(const String & file_name, BackupEntryPtr backup_entry);

Expand Down
Loading
Loading