diff --git a/src/Processors/QueryPlan/ReadFromMergeTree.cpp b/src/Processors/QueryPlan/ReadFromMergeTree.cpp index d7dae54bb1e..67ea36aeeef 100644 --- a/src/Processors/QueryPlan/ReadFromMergeTree.cpp +++ b/src/Processors/QueryPlan/ReadFromMergeTree.cpp @@ -865,6 +865,7 @@ MergeTreeDataSelectAnalysisResultPtr ReadFromMergeTree::selectRangesToRead( size_t total_parts = parts.size(); + /// TODO Support row_policy_filter and additional_filters auto part_values = MergeTreeDataSelectExecutor::filterPartsByVirtualColumns(data, parts, query_info.query, context); if (part_values && part_values->empty()) return std::make_shared(MergeTreeDataSelectAnalysisResult{.result = std::move(result)}); @@ -923,6 +924,9 @@ MergeTreeDataSelectAnalysisResultPtr ReadFromMergeTree::selectRangesToRead( } LOG_DEBUG(log, "Key condition: {}", key_condition->toString()); + if (key_condition->alwaysFalse()) + return std::make_shared(MergeTreeDataSelectAnalysisResult{.result = std::move(result)}); + const auto & select = query_info.query->as(); size_t total_marks_pk = 0; diff --git a/src/Storages/MergeTree/KeyCondition.cpp b/src/Storages/MergeTree/KeyCondition.cpp index b42fe49a1d0..04007ce9356 100644 --- a/src/Storages/MergeTree/KeyCondition.cpp +++ b/src/Storages/MergeTree/KeyCondition.cpp @@ -2463,7 +2463,6 @@ BoolMask KeyCondition::checkInHyperrectangle( return rpn_stack[0]; } - bool KeyCondition::mayBeTrueInRange( size_t used_key_size, const FieldRef * left_keys, @@ -2474,6 +2473,7 @@ bool KeyCondition::mayBeTrueInRange( } String KeyCondition::RPNElement::toString() const { return toString("column " + std::to_string(key_column), false); } + String KeyCondition::RPNElement::toString(std::string_view column_name, bool print_constants) const { auto print_wrapped_column = [this, &column_name, print_constants](WriteBuffer & buf) @@ -2563,10 +2563,12 @@ bool KeyCondition::alwaysUnknownOrTrue() const { return unknownOrAlwaysTrue(false); } + bool KeyCondition::anyUnknownOrAlwaysTrue() const { return unknownOrAlwaysTrue(true); } + bool KeyCondition::unknownOrAlwaysTrue(bool unknown_any) const { std::vector rpn_stack; @@ -2627,6 +2629,80 @@ bool KeyCondition::unknownOrAlwaysTrue(bool unknown_any) const return rpn_stack[0]; } +bool KeyCondition::alwaysFalse() const +{ + /// 0: always_false, 1: always_true, 2: non_const + std::vector rpn_stack; + + for (const auto & element : rpn) + { + if (element.function == RPNElement::ALWAYS_TRUE) + { + rpn_stack.push_back(1); + } + else if (element.function == RPNElement::ALWAYS_FALSE) + { + rpn_stack.push_back(0); + } + else if (element.function == RPNElement::FUNCTION_NOT_IN_RANGE + || element.function == RPNElement::FUNCTION_IN_RANGE + || element.function == RPNElement::FUNCTION_IN_SET + || element.function == RPNElement::FUNCTION_NOT_IN_SET + || element.function == RPNElement::FUNCTION_IS_NULL + || element.function == RPNElement::FUNCTION_IS_NOT_NULL + || element.function == RPNElement::FUNCTION_UNKNOWN) + { + rpn_stack.push_back(2); + } + else if (element.function == RPNElement::FUNCTION_NOT) + { + assert(!rpn_stack.empty()); + + auto & arg = rpn_stack.back(); + if (arg == 0) + arg = 1; + else if (arg == 1) + arg = 0; + } + else if (element.function == RPNElement::FUNCTION_AND) + { + assert(!rpn_stack.empty()); + + auto arg1 = rpn_stack.back(); + rpn_stack.pop_back(); + auto arg2 = rpn_stack.back(); + + if (arg1 == 0 || arg2 == 0) + rpn_stack.back() = 0; + else if (arg1 == 1 && arg2 == 1) + rpn_stack.back() = 1; + else + rpn_stack.back() = 2; + } + else if (element.function == RPNElement::FUNCTION_OR) + { + assert(!rpn_stack.empty()); + + auto arg1 = rpn_stack.back(); + rpn_stack.pop_back(); + auto arg2 = rpn_stack.back(); + + if (arg1 == 1 || arg2 == 1) + rpn_stack.back() = 1; + else if (arg1 == 0 && arg2 == 0) + rpn_stack.back() = 0; + else + rpn_stack.back() = 2; + } + else + throw Exception("Unexpected function type in KeyCondition::RPNElement", ErrorCodes::LOGICAL_ERROR); + } + + if (rpn_stack.size() != 1) + throw Exception("Unexpected stack size in KeyCondition::alwaysFalse", ErrorCodes::LOGICAL_ERROR); + + return rpn_stack[0] == 0; +} size_t KeyCondition::getMaxKeyColumn() const { diff --git a/src/Storages/MergeTree/KeyCondition.h b/src/Storages/MergeTree/KeyCondition.h index 586bc43f791..d00a25a1077 100644 --- a/src/Storages/MergeTree/KeyCondition.h +++ b/src/Storages/MergeTree/KeyCondition.h @@ -279,6 +279,8 @@ public: /// Does not allow any FUNCTION_UNKNOWN (will instantly return true). bool anyUnknownOrAlwaysTrue() const; + bool alwaysFalse() const; + /// Get the maximum number of the key element used in the condition. size_t getMaxKeyColumn() const; diff --git a/tests/queries/0_stateless/02416_row_policy_always_false_index.reference b/tests/queries/0_stateless/02416_row_policy_always_false_index.reference new file mode 100644 index 00000000000..e69de29bb2d diff --git a/tests/queries/0_stateless/02416_row_policy_always_false_index.sql b/tests/queries/0_stateless/02416_row_policy_always_false_index.sql new file mode 100644 index 00000000000..c233e99ec29 --- /dev/null +++ b/tests/queries/0_stateless/02416_row_policy_always_false_index.sql @@ -0,0 +1,17 @@ +drop table if exists tbl; + +create table tbl (s String, i int) engine MergeTree order by i; + +insert into tbl values ('123', 123); + +drop row policy if exists filter on tbl; + +create row policy filter on tbl using 0 to all; + +set max_rows_to_read = 0; + +select * from tbl; + +drop row policy filter on tbl; + +drop table tbl;