ClickHouse
236 строк · 9.5 Кб
1#include <Processors/QueryPlan/Optimizations/actionsDAGUtils.h>
2
3#include <Core/Field.h>
4#include <Functions/IFunction.h>
5#include <Columns/ColumnConst.h>
6
7#include <stack>
8
9namespace DB
10{
11MatchedTrees::Matches matchTrees(const ActionsDAG::NodeRawConstPtrs & inner_dag, const ActionsDAG & outer_dag, bool check_monotonicity)
12{
13using Parents = std::set<const ActionsDAG::Node *>;
14std::unordered_map<const ActionsDAG::Node *, Parents> inner_parents;
15std::unordered_map<std::string_view, const ActionsDAG::Node *> inner_inputs;
16
17{
18std::stack<const ActionsDAG::Node *> stack;
19for (const auto * out : inner_dag)
20{
21if (inner_parents.contains(out))
22continue;
23
24stack.push(out);
25inner_parents.emplace(out, Parents());
26while (!stack.empty())
27{
28const auto * node = stack.top();
29stack.pop();
30
31if (node->type == ActionsDAG::ActionType::INPUT)
32inner_inputs.emplace(node->result_name, node);
33
34for (const auto * child : node->children)
35{
36auto [it, inserted] = inner_parents.emplace(child, Parents());
37it->second.emplace(node);
38
39if (inserted)
40stack.push(child);
41}
42}
43}
44}
45
46struct Frame
47{
48const ActionsDAG::Node * node;
49ActionsDAG::NodeRawConstPtrs mapped_children;
50};
51
52MatchedTrees::Matches matches;
53std::stack<Frame> stack;
54
55for (const auto & node : outer_dag.getNodes())
56{
57if (matches.contains(&node))
58continue;
59
60stack.push(Frame{&node, {}});
61while (!stack.empty())
62{
63auto & frame = stack.top();
64frame.mapped_children.reserve(frame.node->children.size());
65
66while (frame.mapped_children.size() < frame.node->children.size())
67{
68const auto * child = frame.node->children[frame.mapped_children.size()];
69auto it = matches.find(child);
70if (it == matches.end())
71{
72/// If match map does not contain a child, it was not visited.
73stack.push(Frame{child, {}});
74break;
75}
76/// A node from found match may be nullptr.
77/// It means that node is visited, but no match was found.
78if (it->second.monotonicity)
79/// Ignore a match with monotonicity.
80frame.mapped_children.push_back(nullptr);
81else
82frame.mapped_children.push_back(it->second.node);
83
84}
85
86if (frame.mapped_children.size() < frame.node->children.size())
87continue;
88
89/// Create an empty match for current node.
90/// match.node will be set if match is found.
91auto & match = matches[frame.node];
92
93if (frame.node->type == ActionsDAG::ActionType::INPUT)
94{
95const ActionsDAG::Node * mapped = nullptr;
96if (auto it = inner_inputs.find(frame.node->result_name); it != inner_inputs.end())
97mapped = it->second;
98
99match.node = mapped;
100}
101else if (frame.node->type == ActionsDAG::ActionType::ALIAS)
102{
103match = matches[frame.node->children.at(0)];
104}
105else if (frame.node->type == ActionsDAG::ActionType::FUNCTION)
106{
107//std::cerr << "... Processing " << frame.node->function_base->getName() << std::endl;
108
109bool found_all_children = true;
110const ActionsDAG::Node * any_child = nullptr;
111size_t num_children = frame.node->children.size();
112for (size_t i = 0; i < num_children; ++i)
113{
114if (frame.mapped_children[i])
115any_child = frame.mapped_children[i];
116else if (!frame.node->children[i]->column || !isColumnConst(*frame.node->children[i]->column))
117found_all_children = false;
118}
119
120if (found_all_children && any_child)
121{
122Parents container;
123Parents * intersection = &inner_parents[any_child];
124
125if (frame.mapped_children.size() > 1)
126{
127std::vector<Parents *> other_parents;
128size_t mapped_children_size = frame.mapped_children.size();
129other_parents.reserve(mapped_children_size);
130for (size_t i = 1; i < mapped_children_size; ++i)
131if (frame.mapped_children[i])
132other_parents.push_back(&inner_parents[frame.mapped_children[i]]);
133
134for (const auto * parent : *intersection)
135{
136bool is_common = true;
137for (const auto * set : other_parents)
138{
139if (!set->contains(parent))
140{
141is_common = false;
142break;
143}
144}
145
146if (is_common)
147container.insert(parent);
148}
149
150intersection = &container;
151}
152
153//std::cerr << ".. Candidate parents " << intersection->size() << std::endl;
154
155if (!intersection->empty())
156{
157auto func_name = frame.node->function_base->getName();
158for (const auto * parent : *intersection)
159{
160//std::cerr << ".. candidate " << parent->result_name << std::endl;
161if (parent->type == ActionsDAG::ActionType::FUNCTION && func_name == parent->function_base->getName())
162{
163const auto & children = parent->children;
164if (children.size() == num_children)
165{
166bool all_children_matched = true;
167for (size_t i = 0; all_children_matched && i < num_children; ++i)
168{
169if (frame.mapped_children[i] == nullptr)
170{
171all_children_matched = children[i]->column && isColumnConst(*children[i]->column)
172&& children[i]->result_type->equals(*frame.node->children[i]->result_type)
173&& assert_cast<const ColumnConst &>(*children[i]->column).getField() == assert_cast<const ColumnConst &>(*frame.node->children[i]->column).getField();
174}
175else
176all_children_matched = frame.mapped_children[i] == children[i];
177}
178
179if (all_children_matched)
180{
181match.node = parent;
182break;
183}
184}
185}
186}
187}
188}
189
190if (!match.node && check_monotonicity && frame.node->function_base->hasInformationAboutMonotonicity())
191{
192size_t num_const_args = 0;
193const ActionsDAG::Node * monotonic_child = nullptr;
194for (const auto * child : frame.node->children)
195{
196if (child->column)
197++num_const_args;
198else
199monotonic_child = child;
200}
201
202if (monotonic_child && num_const_args + 1 == frame.node->children.size())
203{
204const auto & child_match = matches[monotonic_child];
205if (child_match.node)
206{
207auto info = frame.node->function_base->getMonotonicityForRange(*monotonic_child->result_type, {}, {});
208if (info.is_monotonic)
209{
210MatchedTrees::Monotonicity monotonicity;
211monotonicity.direction *= info.is_positive ? 1 : -1;
212monotonicity.strict = info.is_strict;
213
214if (child_match.monotonicity)
215{
216monotonicity.direction *= child_match.monotonicity->direction;
217if (!child_match.monotonicity->strict)
218monotonicity.strict = false;
219}
220
221match.node = child_match.node;
222match.monotonicity = monotonicity;
223}
224}
225}
226}
227}
228
229stack.pop();
230}
231}
232
233return matches;
234}
235
236}
237