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

View File

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

View File

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

View File

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

View File

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

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