#pragma once
#include
#include
#include
#include
#include
#include
#include
namespace DB
{
namespace ErrorCodes
{
extern const int LOGICAL_ERROR;
}
/** Visitor that traverse query tree in depth.
* Derived class must implement `visitImpl` method.
* Additionally subclass can control if child need to be visited using `needChildVisit` method, by
* default all node children are visited.
* By default visitor traverse tree from top to bottom, if bottom to top traverse is required subclass
* can override `shouldTraverseTopToBottom` method.
*
* Usage example:
* class FunctionsVisitor : public InDepthQueryTreeVisitor
* {
* void visitImpl(VisitQueryTreeNodeType & query_tree_node)
* {
* if (query_tree_node->getNodeType() == QueryTreeNodeType::FUNCTION)
* processFunctionNode(query_tree_node);
* }
* }
*/
template
class InDepthQueryTreeVisitor
{
public:
using VisitQueryTreeNodeType = std::conditional_t;
/// Return true if visitor should traverse tree top to bottom, false otherwise
bool shouldTraverseTopToBottom() const
{
return true;
}
/// Return true if visitor should visit child, false otherwise
bool needChildVisit(VisitQueryTreeNodeType & parent [[maybe_unused]], VisitQueryTreeNodeType & child [[maybe_unused]])
{
return true;
}
void visit(VisitQueryTreeNodeType & query_tree_node)
{
bool traverse_top_to_bottom = getDerived().shouldTraverseTopToBottom();
if (!traverse_top_to_bottom)
visitChildren(query_tree_node);
getDerived().visitImpl(query_tree_node);
if (traverse_top_to_bottom)
visitChildren(query_tree_node);
}
private:
Derived & getDerived()
{
return *static_cast(this);
}
const Derived & getDerived() const
{
return *static_cast(this);
}
void visitChildren(VisitQueryTreeNodeType & expression)
{
for (auto & child : expression->getChildren())
{
if (!child)
continue;
bool need_visit_child = getDerived().needChildVisit(expression, child);
if (need_visit_child)
visit(child);
}
}
};
template
using ConstInDepthQueryTreeVisitor = InDepthQueryTreeVisitor;
/** Same as InDepthQueryTreeVisitor (but has a different interface) and additionally keeps track of current scope context.
* This can be useful if your visitor has special logic that depends on current scope context.
*
* To specify behavior of the visitor you can implement following methods in derived class:
* 1. needChildVisit – This methods allows to skip subtree.
* 2. enterImpl – This method is called before children are processed.
* 3. leaveImpl – This method is called after children are processed.
*/
template
class InDepthQueryTreeVisitorWithContext
{
public:
using VisitQueryTreeNodeType = QueryTreeNodePtr;
explicit InDepthQueryTreeVisitorWithContext(ContextPtr context, size_t initial_subquery_depth = 0)
: current_context(std::move(context))
, subquery_depth(initial_subquery_depth)
{}
/// Return true if visitor should visit child, false otherwise
bool needChildVisit(VisitQueryTreeNodeType & parent [[maybe_unused]], VisitQueryTreeNodeType & child [[maybe_unused]])
{
return true;
}
const ContextPtr & getContext() const
{
return current_context;
}
const Settings & getSettings() const
{
return current_context->getSettingsRef();
}
size_t getSubqueryDepth() const
{
return subquery_depth;
}
void visit(VisitQueryTreeNodeType & query_tree_node)
{
auto current_scope_context_ptr = current_context;
SCOPE_EXIT(
current_context = std::move(current_scope_context_ptr);
--subquery_depth;
);
if (auto * query_node = query_tree_node->template as())
current_context = query_node->getContext();
else if (auto * union_node = query_tree_node->template as())
current_context = union_node->getContext();
++subquery_depth;
getDerived().enterImpl(query_tree_node);
visitChildren(query_tree_node);
getDerived().leaveImpl(query_tree_node);
}
void enterImpl(VisitQueryTreeNodeType & node [[maybe_unused]])
{}
void leaveImpl(VisitQueryTreeNodeType & node [[maybe_unused]])
{}
private:
Derived & getDerived()
{
return *static_cast(this);
}
const Derived & getDerived() const
{
return *static_cast(this);
}
void visitChildIfNeeded(
VisitQueryTreeNodeType & parent,
VisitQueryTreeNodeType & child)
{
bool need_visit_child = getDerived().needChildVisit(parent, child);
if (!need_visit_child)
return;
// We do not visit ListNode with arguments of TableFunctionNode directly, because
// we need to know which arguments are in the unresolved state.
// It must be safe because we do not modify table function arguments list in any visitor.
if (auto * table_function_node = parent->as())
{
if (child != table_function_node->getArgumentsNode())
throw Exception(ErrorCodes::LOGICAL_ERROR, "TableFunctionNode is expected to have only one child node");
const auto & unresolved_indexes = table_function_node->getUnresolvedArgumentIndexes();
size_t index = 0;
for (auto & argument : table_function_node->getArguments())
{
if (std::find(unresolved_indexes.begin(),
unresolved_indexes.end(),
index) == unresolved_indexes.end())
visit(argument);
++index;
}
return;
}
visit(child);
}
void visitChildren(VisitQueryTreeNodeType & expression)
{
for (auto & child : expression->getChildren())
{
if (child)
visitChildIfNeeded(expression, child);
}
}
ContextPtr current_context;
size_t subquery_depth = 0;
};
}