Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
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
22 changes: 22 additions & 0 deletions src/Planner/Planner.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,7 @@ namespace DB
{
namespace Setting
{
extern const SettingsMap additional_table_filters;
extern const SettingsUInt64 aggregation_in_order_max_block_bytes;
extern const SettingsUInt64 aggregation_memory_efficient_merge_threads;
extern const SettingsUInt64 allow_experimental_parallel_reading_from_replicas;
Expand All @@ -126,6 +127,7 @@ namespace Setting
extern const SettingsString parallel_replicas_custom_key;
extern const SettingsUInt64 parallel_replicas_min_number_of_rows_per_replica;
extern const SettingsBool query_plan_enable_multithreading_after_window_functions;
extern const SettingsBool serialize_query_plan;
extern const SettingsBool throw_on_unsupported_query_inside_transaction;
extern const SettingsFloat totals_auto_threshold;
extern const SettingsTotalsMode totals_mode;
Expand Down Expand Up @@ -1571,6 +1573,26 @@ void Planner::buildPlanForQueryNode()
}
}

/// `additional_table_filters` keys are resolved against the initiator's session current database,
/// but on followers the rewritten `SELECT` uses fully qualified names and the follower's current
/// database is the initiator's user-default DB, so the filter match is unreliable. Rather than
/// patch the match (which differs case by case), disable the combination on the analyzer path.
/// With `serialize_query_plan` the initiator lowers `additional_table_filters` into an explicit
/// `FilterStep` and ships the serialized plan, so the follower never re-resolves the setting —
/// the combination works there and the check is skipped.
if (query_context->canUseParallelReplicasOnInitiator()
&& !settings[Setting::serialize_query_plan]
&& !settings[Setting::additional_table_filters].value.empty())
{
if (settings[Setting::allow_experimental_parallel_reading_from_replicas] >= 2)
throw Exception(ErrorCodes::SUPPORT_IS_DISABLED,
"additional_table_filters is not supported with parallel and without serialize_query_plan=1");

auto & mutable_context = planner_context->getMutableQueryContext();
mutable_context->setSetting("allow_experimental_parallel_reading_from_replicas", Field(0));
LOG_DEBUG(log, "Disabling parallel replicas to execute a query with additional_table_filters");
}

collectTableExpressionData(query_tree, planner_context);
checkStoragesSupportTransactions(planner_context);

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
3
3
3
62 changes: 62 additions & 0 deletions tests/queries/0_stateless/04207_pr_additional_filters.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
-- Tags: no-parallel
-- ^ failpoint

DROP TABLE IF EXISTS atf_p;
CREATE TABLE atf_p (x UInt64) ENGINE = MergeTree ORDER BY tuple();
INSERT INTO atf_p SELECT number FROM numbers(10);

-- The failpoint disables cancellation of unused replicas after all ranges
-- are assigned, so every replica's contribution lands on the initiator and any
-- missing-filter regression is deterministic regardless of timing.

-- `additional_table_filters` keys are resolved against the initiator's session current
-- database, so they cannot be reliably matched on parallel-replica followers (the
-- rewritten query qualifies the table and the follower's current database differs).
-- On the legacy AST-forwarding path the analyzer rejects the combination instead of
-- silently dropping the filter.
SYSTEM ENABLE FAILPOINT parallel_replicas_wait_for_unused_replicas;
SELECT count() FROM atf_p SETTINGS additional_table_filters = {'atf_p': 'x <= 2'},
enable_analyzer = 1,
enable_parallel_replicas = 2,
max_parallel_replicas = 3,
cluster_for_parallel_replicas = 'test_cluster_one_shard_three_replicas_localhost',
parallel_replicas_for_non_replicated_merge_tree = 1,
parallel_replicas_local_plan = 1,
serialize_query_plan = 0; -- { serverError SUPPORT_IS_DISABLED }

SELECT count() FROM atf_p SETTINGS additional_table_filters = {'atf_p': 'x <= 2'},
enable_analyzer = 0,
enable_parallel_replicas = 2,
max_parallel_replicas = 3,
cluster_for_parallel_replicas = 'test_cluster_one_shard_three_replicas_localhost',
parallel_replicas_for_non_replicated_merge_tree = 1,
parallel_replicas_local_plan = 1,
serialize_query_plan = 0;

-- With `enable_parallel_replicas = 1` (best-effort) parallel replicas is silently
-- disabled and the query runs locally with the filter applied.
SYSTEM ENABLE FAILPOINT parallel_replicas_wait_for_unused_replicas;
SELECT count() FROM atf_p SETTINGS additional_table_filters = {'atf_p': 'x <= 2'},
enable_analyzer = 1,
enable_parallel_replicas = 1,
max_parallel_replicas = 3,
cluster_for_parallel_replicas = 'test_cluster_one_shard_three_replicas_localhost',
parallel_replicas_for_non_replicated_merge_tree = 1,
parallel_replicas_local_plan = 1,
serialize_query_plan = 0;

-- With `serialize_query_plan = 1` the initiator lowers the additional filter into an
-- explicit `FilterStep` and ships the serialized plan, so the follower never
-- re-resolves the setting and the combination works.
SYSTEM ENABLE FAILPOINT parallel_replicas_wait_for_unused_replicas;
SELECT count() FROM atf_p SETTINGS additional_table_filters = {'atf_p': 'x <= 2'},
enable_analyzer = 1,
enable_parallel_replicas = 2,
max_parallel_replicas = 3,
cluster_for_parallel_replicas = 'test_cluster_one_shard_three_replicas_localhost',
parallel_replicas_for_non_replicated_merge_tree = 1,
parallel_replicas_local_plan = 1,
serialize_query_plan = 1;

SYSTEM DISABLE FAILPOINT parallel_replicas_wait_for_unused_replicas;
DROP TABLE atf_p;
Loading