mirror of
https://github.com/ClickHouse/ClickHouse.git
synced 2024-12-03 21:12:28 +00:00
Correctrly enable optimize_or_like_chain
This commit is contained in:
parent
a4614903cd
commit
5295cb0e94
@ -1,8 +1,13 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include <optional>
|
||||||
|
#include <utility>
|
||||||
|
#include <Common/SettingsChanges.h>
|
||||||
#include <Common/Exception.h>
|
#include <Common/Exception.h>
|
||||||
|
#include <Core/Settings.h>
|
||||||
|
|
||||||
#include <Analyzer/IQueryTreeNode.h>
|
#include <Analyzer/IQueryTreeNode.h>
|
||||||
|
#include <Analyzer/QueryNode.h>
|
||||||
|
|
||||||
|
|
||||||
namespace DB
|
namespace DB
|
||||||
@ -84,4 +89,86 @@ private:
|
|||||||
template <typename Derived>
|
template <typename Derived>
|
||||||
using ConstInDepthQueryTreeVisitor = InDepthQueryTreeVisitor<Derived, true /*const_visitor*/>;
|
using ConstInDepthQueryTreeVisitor = InDepthQueryTreeVisitor<Derived, true /*const_visitor*/>;
|
||||||
|
|
||||||
|
template <typename Impl>
|
||||||
|
class OptionalInDepthQueryTreeVisitor : Impl
|
||||||
|
{
|
||||||
|
friend struct StateSwitcher;
|
||||||
|
|
||||||
|
Settings settings;
|
||||||
|
bool is_enabled = false;
|
||||||
|
|
||||||
|
struct StateSwitcher
|
||||||
|
{
|
||||||
|
StateSwitcher(OptionalInDepthQueryTreeVisitor * visitor_, SettingsChanges rollback_changes_, bool value)
|
||||||
|
: visitor(visitor_)
|
||||||
|
, rollback_changes(std::move(rollback_changes_))
|
||||||
|
, previous_value(visitor_->is_enabled)
|
||||||
|
{
|
||||||
|
visitor->is_enabled = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
StateSwitcher(StateSwitcher &&) noexcept = default;
|
||||||
|
|
||||||
|
~StateSwitcher()
|
||||||
|
{
|
||||||
|
visitor->is_enabled = previous_value;
|
||||||
|
for (const auto & change : rollback_changes)
|
||||||
|
visitor->settings.set(change.name, change.value);
|
||||||
|
}
|
||||||
|
|
||||||
|
OptionalInDepthQueryTreeVisitor * visitor;
|
||||||
|
SettingsChanges rollback_changes;
|
||||||
|
bool previous_value;
|
||||||
|
};
|
||||||
|
|
||||||
|
std::optional<StateSwitcher> updateState(QueryTreeNodePtr & node)
|
||||||
|
{
|
||||||
|
auto * query = node->as<QueryNode>();
|
||||||
|
if (!query)
|
||||||
|
return {};
|
||||||
|
|
||||||
|
SettingsChanges rollback_changes;
|
||||||
|
for (const auto & change : query->getSettingsChanges())
|
||||||
|
{
|
||||||
|
rollback_changes.push_back(SettingChange{ change.name, settings.get(change.name) });
|
||||||
|
settings.set(change.name, change.value);
|
||||||
|
}
|
||||||
|
return std::make_optional<StateSwitcher>(this, std::move(rollback_changes), Impl::isEnabled(settings));
|
||||||
|
}
|
||||||
|
|
||||||
|
void visitChildren(QueryTreeNodePtr & node)
|
||||||
|
{
|
||||||
|
for (auto & child : node->getChildren())
|
||||||
|
{
|
||||||
|
if (!child)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
visit(child);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
template <typename ...Args>
|
||||||
|
explicit OptionalInDepthQueryTreeVisitor(const Settings & settings_, Args && ...args)
|
||||||
|
: Impl(std::forward<Args>(args)...)
|
||||||
|
, settings(settings_)
|
||||||
|
{}
|
||||||
|
|
||||||
|
bool needVisit(QueryTreeNodePtr & node)
|
||||||
|
{
|
||||||
|
return is_enabled && Impl::needVisit(node);
|
||||||
|
}
|
||||||
|
|
||||||
|
void visit(QueryTreeNodePtr & node)
|
||||||
|
{
|
||||||
|
auto switcher = updateState(node);
|
||||||
|
|
||||||
|
if (needVisit(node))
|
||||||
|
Impl::visitImpl(node);
|
||||||
|
|
||||||
|
visitChildren(node);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -9,6 +9,7 @@
|
|||||||
#include <DataTypes/DataTypesNumber.h>
|
#include <DataTypes/DataTypesNumber.h>
|
||||||
#include <Functions/FunctionFactory.h>
|
#include <Functions/FunctionFactory.h>
|
||||||
#include <Functions/likePatternToRegexp.h>
|
#include <Functions/likePatternToRegexp.h>
|
||||||
|
#include <Interpreters/Context.h>
|
||||||
|
|
||||||
namespace DB
|
namespace DB
|
||||||
{
|
{
|
||||||
@ -16,17 +17,27 @@ namespace DB
|
|||||||
namespace
|
namespace
|
||||||
{
|
{
|
||||||
|
|
||||||
class ConvertOrLikeChainVisitor : public InDepthQueryTreeVisitor<ConvertOrLikeChainVisitor>
|
class ConvertOrLikeChainImpl
|
||||||
{
|
{
|
||||||
using FunctionNodes = std::vector<std::shared_ptr<FunctionNode>>;
|
using FunctionNodes = std::vector<std::shared_ptr<FunctionNode>>;
|
||||||
|
|
||||||
const FunctionOverloadResolverPtr match_function_ref;
|
const FunctionOverloadResolverPtr match_function_ref;
|
||||||
public:
|
public:
|
||||||
|
|
||||||
explicit ConvertOrLikeChainVisitor(FunctionOverloadResolverPtr _match_function_ref)
|
explicit ConvertOrLikeChainImpl(FunctionOverloadResolverPtr _match_function_ref)
|
||||||
: match_function_ref(_match_function_ref)
|
: match_function_ref(_match_function_ref)
|
||||||
{}
|
{}
|
||||||
|
|
||||||
|
bool needVisit(QueryTreeNodePtr &) { return true; }
|
||||||
|
|
||||||
|
bool isEnabled(const Settings & settings)
|
||||||
|
{
|
||||||
|
return settings.optimize_or_like_chain
|
||||||
|
&& settings.allow_hyperscan
|
||||||
|
&& settings.max_hyperscan_regexp_length == 0
|
||||||
|
&& settings.max_hyperscan_regexp_total_length == 0;
|
||||||
|
}
|
||||||
|
|
||||||
void visitImpl(QueryTreeNodePtr & node)
|
void visitImpl(QueryTreeNodePtr & node)
|
||||||
{
|
{
|
||||||
auto * function_node = node->as<FunctionNode>();
|
auto * function_node = node->as<FunctionNode>();
|
||||||
@ -98,11 +109,13 @@ public:
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
using ConvertOrLikeChainVisitor = OptionalInDepthQueryTreeVisitor<ConvertOrLikeChainImpl>;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void ConvertOrLikeChainPass::run(QueryTreeNodePtr query_tree_node, ContextPtr context)
|
void ConvertOrLikeChainPass::run(QueryTreeNodePtr query_tree_node, ContextPtr context)
|
||||||
{
|
{
|
||||||
ConvertOrLikeChainVisitor visitor(FunctionFactory::instance().get("multiMatchAny", context));
|
ConvertOrLikeChainVisitor visitor(context->getSettingsRef(), FunctionFactory::instance().get("multiMatchAny", context));
|
||||||
visitor.visit(query_tree_node);
|
visitor.visit(query_tree_node);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -17,6 +17,7 @@
|
|||||||
#include <Parsers/ASTSetQuery.h>
|
#include <Parsers/ASTSetQuery.h>
|
||||||
|
|
||||||
#include <Analyzer/Utils.h>
|
#include <Analyzer/Utils.h>
|
||||||
|
#include <fmt/core.h>
|
||||||
|
|
||||||
namespace DB
|
namespace DB
|
||||||
{
|
{
|
||||||
@ -179,6 +180,16 @@ void QueryNode::dumpTreeImpl(WriteBuffer & buffer, FormatState & format_state, s
|
|||||||
buffer << '\n' << std::string(indent + 2, ' ') << "OFFSET\n";
|
buffer << '\n' << std::string(indent + 2, ' ') << "OFFSET\n";
|
||||||
getOffset()->dumpTreeImpl(buffer, format_state, indent + 4);
|
getOffset()->dumpTreeImpl(buffer, format_state, indent + 4);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (hasSettingsChanges())
|
||||||
|
{
|
||||||
|
buffer << '\n' << std::string(indent + 2, ' ') << "SETTINGS";
|
||||||
|
for (const auto & change : settings_changes)
|
||||||
|
{
|
||||||
|
buffer << fmt::format(" {}={}", change.name, toString(change.value));
|
||||||
|
}
|
||||||
|
buffer << '\n';
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool QueryNode::isEqualImpl(const IQueryTreeNode & rhs) const
|
bool QueryNode::isEqualImpl(const IQueryTreeNode & rhs) const
|
||||||
|
@ -214,11 +214,7 @@ void addQueryTreePasses(QueryTreePassManager & manager)
|
|||||||
if (settings.optimize_if_transform_strings_to_enum)
|
if (settings.optimize_if_transform_strings_to_enum)
|
||||||
manager.addPass(std::make_unique<IfTransformStringsToEnumPass>());
|
manager.addPass(std::make_unique<IfTransformStringsToEnumPass>());
|
||||||
|
|
||||||
if (settings.optimize_or_like_chain
|
manager.addPass(std::make_unique<ConvertOrLikeChainPass>());
|
||||||
&& settings.allow_hyperscan
|
|
||||||
&& settings.max_hyperscan_regexp_length == 0
|
|
||||||
&& settings.max_hyperscan_regexp_total_length == 0)
|
|
||||||
manager.addPass(std::make_unique<ConvertOrLikeChainPass>());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -1,9 +1,74 @@
|
|||||||
SELECT materialize(\'Привет, World\') AS s
|
SELECT materialize(\'Привет, World\') AS s
|
||||||
WHERE (s LIKE \'hell%\') OR (s ILIKE \'%привет%\') OR (s ILIKE \'world%\')
|
WHERE (s LIKE \'hell%\') OR (s ILIKE \'%привет%\') OR (s ILIKE \'world%\')
|
||||||
SETTINGS optimize_or_like_chain = 0
|
SETTINGS optimize_or_like_chain = 0
|
||||||
|
QUERY id: 0
|
||||||
|
PROJECTION COLUMNS
|
||||||
|
s String
|
||||||
|
PROJECTION
|
||||||
|
LIST id: 1, nodes: 1
|
||||||
|
FUNCTION id: 2, function_name: materialize, function_type: ordinary, result_type: String
|
||||||
|
ARGUMENTS
|
||||||
|
LIST id: 3, nodes: 1
|
||||||
|
CONSTANT id: 4, constant_value: \'Привет, World\', constant_value_type: String
|
||||||
|
JOIN TREE
|
||||||
|
TABLE id: 5, table_name: system.one
|
||||||
|
WHERE
|
||||||
|
FUNCTION id: 6, function_name: or, function_type: ordinary, result_type: UInt8
|
||||||
|
ARGUMENTS
|
||||||
|
LIST id: 7, nodes: 3
|
||||||
|
FUNCTION id: 8, function_name: like, function_type: ordinary, result_type: UInt8
|
||||||
|
ARGUMENTS
|
||||||
|
LIST id: 9, nodes: 2
|
||||||
|
FUNCTION id: 2, function_name: materialize, function_type: ordinary, result_type: String
|
||||||
|
ARGUMENTS
|
||||||
|
LIST id: 3, nodes: 1
|
||||||
|
CONSTANT id: 4, constant_value: \'Привет, World\', constant_value_type: String
|
||||||
|
CONSTANT id: 10, constant_value: \'hell%\', constant_value_type: String
|
||||||
|
FUNCTION id: 11, function_name: ilike, function_type: ordinary, result_type: UInt8
|
||||||
|
ARGUMENTS
|
||||||
|
LIST id: 12, nodes: 2
|
||||||
|
FUNCTION id: 2, function_name: materialize, function_type: ordinary, result_type: String
|
||||||
|
ARGUMENTS
|
||||||
|
LIST id: 3, nodes: 1
|
||||||
|
CONSTANT id: 4, constant_value: \'Привет, World\', constant_value_type: String
|
||||||
|
CONSTANT id: 13, constant_value: \'%привет%\', constant_value_type: String
|
||||||
|
FUNCTION id: 14, function_name: ilike, function_type: ordinary, result_type: UInt8
|
||||||
|
ARGUMENTS
|
||||||
|
LIST id: 15, nodes: 2
|
||||||
|
FUNCTION id: 2, function_name: materialize, function_type: ordinary, result_type: String
|
||||||
|
ARGUMENTS
|
||||||
|
LIST id: 3, nodes: 1
|
||||||
|
CONSTANT id: 4, constant_value: \'Привет, World\', constant_value_type: String
|
||||||
|
CONSTANT id: 16, constant_value: \'world%\', constant_value_type: String
|
||||||
|
SETTINGS optimize_or_like_chain=0 allow_experimental_analyzer=1
|
||||||
SELECT materialize(\'Привет, World\') AS s
|
SELECT materialize(\'Привет, World\') AS s
|
||||||
WHERE multiMatchAny(s, [\'^hell\', \'(?i)привет\', \'(?i)^world\']) OR false
|
WHERE multiMatchAny(s, [\'^hell\', \'(?i)привет\', \'(?i)^world\']) OR false
|
||||||
SETTINGS optimize_or_like_chain = 1
|
SETTINGS optimize_or_like_chain = 1
|
||||||
|
QUERY id: 0
|
||||||
|
PROJECTION COLUMNS
|
||||||
|
s String
|
||||||
|
PROJECTION
|
||||||
|
LIST id: 1, nodes: 1
|
||||||
|
FUNCTION id: 2, function_name: materialize, function_type: ordinary, result_type: String
|
||||||
|
ARGUMENTS
|
||||||
|
LIST id: 3, nodes: 1
|
||||||
|
CONSTANT id: 4, constant_value: \'Привет, World\', constant_value_type: String
|
||||||
|
JOIN TREE
|
||||||
|
TABLE id: 5, table_name: system.one
|
||||||
|
WHERE
|
||||||
|
FUNCTION id: 6, function_name: or, function_type: ordinary, result_type: UInt8
|
||||||
|
ARGUMENTS
|
||||||
|
LIST id: 7, nodes: 2
|
||||||
|
FUNCTION id: 8, function_name: multiMatchAny, function_type: ordinary, result_type: UInt8
|
||||||
|
ARGUMENTS
|
||||||
|
LIST id: 9, nodes: 2
|
||||||
|
FUNCTION id: 2, function_name: materialize, function_type: ordinary, result_type: String
|
||||||
|
ARGUMENTS
|
||||||
|
LIST id: 3, nodes: 1
|
||||||
|
CONSTANT id: 4, constant_value: \'Привет, World\', constant_value_type: String
|
||||||
|
CONSTANT id: 10, constant_value: Array_[\'^hell\', \'(?i)привет\', \'(?i)^world\'], constant_value_type: Array(String)
|
||||||
|
CONSTANT id: 11, constant_value: UInt64_0, constant_value_type: Bool
|
||||||
|
SETTINGS optimize_or_like_chain=1 allow_experimental_analyzer=1
|
||||||
SELECT
|
SELECT
|
||||||
materialize(\'Привет, World\') AS s1,
|
materialize(\'Привет, World\') AS s1,
|
||||||
materialize(\'Привет, World\') AS s2
|
materialize(\'Привет, World\') AS s2
|
||||||
@ -30,8 +95,12 @@ SELECT
|
|||||||
WHERE multiMatchAny(s1, [\'^hell\', \'(?i)^world\']) OR multiMatchAny(s2, [\'(?i)привет\']) OR (s1 = \'Привет\')
|
WHERE multiMatchAny(s1, [\'^hell\', \'(?i)^world\']) OR multiMatchAny(s2, [\'(?i)привет\']) OR (s1 = \'Привет\')
|
||||||
SETTINGS optimize_or_like_chain = 1
|
SETTINGS optimize_or_like_chain = 1
|
||||||
Привет, optimized World
|
Привет, optimized World
|
||||||
|
Привет, optimized World
|
||||||
|
Привет, World
|
||||||
Привет, World
|
Привет, World
|
||||||
Привет, optimized World
|
Привет, optimized World
|
||||||
|
Привет, optimized World
|
||||||
|
Привет, World
|
||||||
Привет, World
|
Привет, World
|
||||||
SELECT
|
SELECT
|
||||||
(materialize(\'Привет, World\') AS s) LIKE \'hell%\' AS test,
|
(materialize(\'Привет, World\') AS s) LIKE \'hell%\' AS test,
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
EXPLAIN SYNTAX SELECT materialize('Привет, World') AS s WHERE (s LIKE 'hell%') OR (s ILIKE '%привет%') OR (s ILIKE 'world%') SETTINGS optimize_or_like_chain = 0;
|
EXPLAIN SYNTAX SELECT materialize('Привет, World') AS s WHERE (s LIKE 'hell%') OR (s ILIKE '%привет%') OR (s ILIKE 'world%') SETTINGS optimize_or_like_chain = 0;
|
||||||
|
EXPLAIN QUERY TREE run_passes=1 SELECT materialize('Привет, World') AS s WHERE (s LIKE 'hell%') OR (s ILIKE '%привет%') OR (s ILIKE 'world%') SETTINGS optimize_or_like_chain = 0, allow_experimental_analyzer = 1;
|
||||||
EXPLAIN SYNTAX SELECT materialize('Привет, World') AS s WHERE (s LIKE 'hell%') OR (s ILIKE '%привет%') OR (s ILIKE 'world%') SETTINGS optimize_or_like_chain = 1;
|
EXPLAIN SYNTAX SELECT materialize('Привет, World') AS s WHERE (s LIKE 'hell%') OR (s ILIKE '%привет%') OR (s ILIKE 'world%') SETTINGS optimize_or_like_chain = 1;
|
||||||
|
EXPLAIN QUERY TREE run_passes=1 SELECT materialize('Привет, World') AS s WHERE (s LIKE 'hell%') OR (s ILIKE '%привет%') OR (s ILIKE 'world%') SETTINGS optimize_or_like_chain = 1, allow_experimental_analyzer = 1;
|
||||||
|
|
||||||
EXPLAIN SYNTAX SELECT materialize('Привет, World') AS s1, materialize('Привет, World') AS s2 WHERE (s1 LIKE 'hell%') OR (s2 ILIKE '%привет%') OR (s1 ILIKE 'world%') SETTINGS optimize_or_like_chain = 1;
|
EXPLAIN SYNTAX SELECT materialize('Привет, World') AS s1, materialize('Привет, World') AS s2 WHERE (s1 LIKE 'hell%') OR (s2 ILIKE '%привет%') OR (s1 ILIKE 'world%') SETTINGS optimize_or_like_chain = 1;
|
||||||
EXPLAIN SYNTAX SELECT materialize('Привет, World') AS s1, materialize('Привет, World') AS s2 WHERE (s1 LIKE 'hell%') OR (s2 ILIKE '%привет%') OR (s1 ILIKE 'world%') SETTINGS optimize_or_like_chain = 1 SETTINGS allow_hyperscan = 0;
|
EXPLAIN SYNTAX SELECT materialize('Привет, World') AS s1, materialize('Привет, World') AS s2 WHERE (s1 LIKE 'hell%') OR (s2 ILIKE '%привет%') OR (s1 ILIKE 'world%') SETTINGS optimize_or_like_chain = 1 SETTINGS allow_hyperscan = 0;
|
||||||
@ -10,9 +11,16 @@ EXPLAIN SYNTAX SELECT materialize('Привет, World') AS s1, materialize('П
|
|||||||
|
|
||||||
|
|
||||||
SELECT materialize('Привет, optimized World') AS s WHERE (s LIKE 'hell%') OR (s LIKE '%привет%') OR (s ILIKE '%world') SETTINGS optimize_or_like_chain = 1;
|
SELECT materialize('Привет, optimized World') AS s WHERE (s LIKE 'hell%') OR (s LIKE '%привет%') OR (s ILIKE '%world') SETTINGS optimize_or_like_chain = 1;
|
||||||
|
SELECT materialize('Привет, optimized World') AS s WHERE (s LIKE 'hell%') OR (s LIKE '%привет%') OR (s ILIKE '%world') SETTINGS optimize_or_like_chain = 1, allow_experimental_analyzer = 1;
|
||||||
|
|
||||||
SELECT materialize('Привет, World') AS s WHERE (s LIKE 'hell%') OR (s LIKE '%привет%') OR (s ILIKE '%world') SETTINGS optimize_or_like_chain = 0;
|
SELECT materialize('Привет, World') AS s WHERE (s LIKE 'hell%') OR (s LIKE '%привет%') OR (s ILIKE '%world') SETTINGS optimize_or_like_chain = 0;
|
||||||
|
SELECT materialize('Привет, World') AS s WHERE (s LIKE 'hell%') OR (s LIKE '%привет%') OR (s ILIKE '%world') SETTINGS optimize_or_like_chain = 0, allow_experimental_analyzer = 1;
|
||||||
|
|
||||||
SELECT materialize('Привет, optimized World') AS s WHERE (s LIKE 'hell%') OR (s ILIKE '%привет%') OR (s LIKE 'world%') SETTINGS optimize_or_like_chain = 1;
|
SELECT materialize('Привет, optimized World') AS s WHERE (s LIKE 'hell%') OR (s ILIKE '%привет%') OR (s LIKE 'world%') SETTINGS optimize_or_like_chain = 1;
|
||||||
|
SELECT materialize('Привет, optimized World') AS s WHERE (s LIKE 'hell%') OR (s ILIKE '%привет%') OR (s LIKE 'world%') SETTINGS optimize_or_like_chain = 1, allow_experimental_analyzer = 1;
|
||||||
|
|
||||||
SELECT materialize('Привет, World') AS s WHERE (s LIKE 'hell%') OR (s ILIKE '%привет%') OR (s LIKE 'world%') SETTINGS optimize_or_like_chain = 0;
|
SELECT materialize('Привет, World') AS s WHERE (s LIKE 'hell%') OR (s ILIKE '%привет%') OR (s LIKE 'world%') SETTINGS optimize_or_like_chain = 0;
|
||||||
|
SELECT materialize('Привет, World') AS s WHERE (s LIKE 'hell%') OR (s ILIKE '%привет%') OR (s LIKE 'world%') SETTINGS optimize_or_like_chain = 0, allow_experimental_analyzer = 1;
|
||||||
|
|
||||||
-- Aliases
|
-- Aliases
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user