mirror of
https://github.com/ClickHouse/ClickHouse.git
synced 2024-12-11 08:52:06 +00:00
Correctrly enable optimize_or_like_chain
This commit is contained in:
parent
a4614903cd
commit
5295cb0e94
@ -1,8 +1,13 @@
|
||||
#pragma once
|
||||
|
||||
#include <optional>
|
||||
#include <utility>
|
||||
#include <Common/SettingsChanges.h>
|
||||
#include <Common/Exception.h>
|
||||
#include <Core/Settings.h>
|
||||
|
||||
#include <Analyzer/IQueryTreeNode.h>
|
||||
#include <Analyzer/QueryNode.h>
|
||||
|
||||
|
||||
namespace DB
|
||||
@ -84,4 +89,86 @@ private:
|
||||
template <typename Derived>
|
||||
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 <Functions/FunctionFactory.h>
|
||||
#include <Functions/likePatternToRegexp.h>
|
||||
#include <Interpreters/Context.h>
|
||||
|
||||
namespace DB
|
||||
{
|
||||
@ -16,17 +17,27 @@ namespace DB
|
||||
namespace
|
||||
{
|
||||
|
||||
class ConvertOrLikeChainVisitor : public InDepthQueryTreeVisitor<ConvertOrLikeChainVisitor>
|
||||
class ConvertOrLikeChainImpl
|
||||
{
|
||||
using FunctionNodes = std::vector<std::shared_ptr<FunctionNode>>;
|
||||
|
||||
const FunctionOverloadResolverPtr match_function_ref;
|
||||
public:
|
||||
|
||||
explicit ConvertOrLikeChainVisitor(FunctionOverloadResolverPtr _match_function_ref)
|
||||
explicit ConvertOrLikeChainImpl(FunctionOverloadResolverPtr _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)
|
||||
{
|
||||
auto * function_node = node->as<FunctionNode>();
|
||||
@ -98,11 +109,13 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
using ConvertOrLikeChainVisitor = OptionalInDepthQueryTreeVisitor<ConvertOrLikeChainImpl>;
|
||||
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
|
@ -17,6 +17,7 @@
|
||||
#include <Parsers/ASTSetQuery.h>
|
||||
|
||||
#include <Analyzer/Utils.h>
|
||||
#include <fmt/core.h>
|
||||
|
||||
namespace DB
|
||||
{
|
||||
@ -179,6 +180,16 @@ void QueryNode::dumpTreeImpl(WriteBuffer & buffer, FormatState & format_state, s
|
||||
buffer << '\n' << std::string(indent + 2, ' ') << "OFFSET\n";
|
||||
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
|
||||
|
@ -214,11 +214,7 @@ void addQueryTreePasses(QueryTreePassManager & manager)
|
||||
if (settings.optimize_if_transform_strings_to_enum)
|
||||
manager.addPass(std::make_unique<IfTransformStringsToEnumPass>());
|
||||
|
||||
if (settings.optimize_or_like_chain
|
||||
&& settings.allow_hyperscan
|
||||
&& settings.max_hyperscan_regexp_length == 0
|
||||
&& settings.max_hyperscan_regexp_total_length == 0)
|
||||
manager.addPass(std::make_unique<ConvertOrLikeChainPass>());
|
||||
manager.addPass(std::make_unique<ConvertOrLikeChainPass>());
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -1,9 +1,74 @@
|
||||
SELECT materialize(\'Привет, World\') AS s
|
||||
WHERE (s LIKE \'hell%\') OR (s ILIKE \'%привет%\') OR (s ILIKE \'world%\')
|
||||
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
|
||||
WHERE multiMatchAny(s, [\'^hell\', \'(?i)привет\', \'(?i)^world\']) OR false
|
||||
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
|
||||
materialize(\'Привет, World\') AS s1,
|
||||
materialize(\'Привет, World\') AS s2
|
||||
@ -30,8 +95,12 @@ SELECT
|
||||
WHERE multiMatchAny(s1, [\'^hell\', \'(?i)^world\']) OR multiMatchAny(s2, [\'(?i)привет\']) OR (s1 = \'Привет\')
|
||||
SETTINGS optimize_or_like_chain = 1
|
||||
Привет, optimized World
|
||||
Привет, optimized World
|
||||
Привет, World
|
||||
Привет, World
|
||||
Привет, optimized World
|
||||
Привет, optimized World
|
||||
Привет, World
|
||||
Привет, World
|
||||
SELECT
|
||||
(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 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 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 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, 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, 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, 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, allow_experimental_analyzer = 1;
|
||||
|
||||
-- Aliases
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user