Correctrly enable optimize_or_like_chain

This commit is contained in:
Dmitry Novik 2022-11-01 22:11:56 +00:00
parent a4614903cd
commit 5295cb0e94
6 changed files with 193 additions and 9 deletions

View File

@ -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);
}
};
} }

View File

@ -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);
} }

View File

@ -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

View File

@ -214,10 +214,6 @@ 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
&& 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>());
} }

View File

@ -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,

View File

@ -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