OmniSciDB  eb3a3d0a03
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
QueryPlanDagExtractor.cpp
Go to the documentation of this file.
1 /*
2  * Copyright 2021 OmniSci, Inc.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #include "QueryPlanDagExtractor.h"
18 #include "RexVisitor.h"
20 
21 #include <boost/algorithm/cxx11/any_of.hpp>
22 
23 namespace {
24 struct IsEquivBinOp {
25  bool operator()(std::shared_ptr<Analyzer::Expr> const& qual) {
26  if (auto oper = std::dynamic_pointer_cast<const Analyzer::BinOper>(qual)) {
27  return IS_EQUIVALENCE(oper->get_optype());
28  }
29  return false;
30  }
31 };
32 } // namespace
33 
34 std::vector<InnerOuterOrLoopQual> QueryPlanDagExtractor::normalizeColumnsPair(
35  const Analyzer::BinOper* condition,
37  std::vector<InnerOuterOrLoopQual> result;
38  const auto lhs_tuple_expr =
39  dynamic_cast<const Analyzer::ExpressionTuple*>(condition->get_left_operand());
40  const auto rhs_tuple_expr =
41  dynamic_cast<const Analyzer::ExpressionTuple*>(condition->get_right_operand());
42 
43  CHECK_EQ(static_cast<bool>(lhs_tuple_expr), static_cast<bool>(rhs_tuple_expr));
44  auto do_normalize_inner_outer_pair = [&result, &cat, &condition](
45  const Analyzer::Expr* lhs,
46  const Analyzer::Expr* rhs,
47  const TemporaryTables* temporary_table) {
48  try {
49  auto inner_outer_pair = HashJoin::normalizeColumnPair(
50  lhs, rhs, cat, temporary_table, condition->is_overlaps_oper());
51  InnerOuterOrLoopQual valid_qual{
52  std::make_pair(inner_outer_pair.first, inner_outer_pair.second), false};
53  result.push_back(valid_qual);
54  } catch (HashJoinFail& e) {
55  InnerOuterOrLoopQual invalid_qual{std::make_pair(lhs, rhs), true};
56  result.push_back(invalid_qual);
57  }
58  };
59  if (lhs_tuple_expr) {
60  const auto& lhs_tuple = lhs_tuple_expr->getTuple();
61  const auto& rhs_tuple = rhs_tuple_expr->getTuple();
62  CHECK_EQ(lhs_tuple.size(), rhs_tuple.size());
63  for (size_t i = 0; i < lhs_tuple.size(); ++i) {
64  do_normalize_inner_outer_pair(
65  lhs_tuple[i].get(), rhs_tuple[i].get(), &temporary_tables_);
66  }
67  } else {
68  do_normalize_inner_outer_pair(condition->get_left_operand(),
69  condition->get_right_operand(),
71  }
72  return result;
73 }
74 
75 // To extract query plan DAG, we call this function with root node of the query plan
76 // and some objects required while extracting DAG
77 // We consider a DAG representation of a query plan as a series of "unique" rel node ids
78 // We decide each rel node's node id by searching the cached plan DAG first,
79 // and assign a new id iff there exists no duplicated rel node that can reuse
81  const RelAlgNode*
82  node, /* the root node of the query plan tree we want to extract its DAG */
83  const Catalog_Namespace::Catalog& catalog,
84  std::optional<unsigned> left_deep_tree_id,
85  std::unordered_map<unsigned, JoinQualsPerNestingLevel>& left_deep_tree_infos,
86  const TemporaryTables& temporary_tables,
87  Executor* executor,
88  const RelAlgTranslator& rel_alg_translator) {
89  // check if this plan tree has not supported pattern for DAG extraction
90  if (QueryPlanDagChecker::isNotSupportedDag(node, rel_alg_translator)) {
91  VLOG(1) << "Stop DAG extraction due to not supproed node: " << node->toString();
92  return {node, EMPTY_QUERY_PLAN, nullptr, nullptr, {}, true};
93  }
94 
96  node, catalog, left_deep_tree_id, left_deep_tree_infos, temporary_tables, executor);
97 }
98 
100  const RelAlgNode*
101  node, /* the root node of the query plan tree we want to extract its DAG */
102  const Catalog_Namespace::Catalog& catalog,
103  std::optional<unsigned> left_deep_tree_id,
104  std::unordered_map<unsigned, JoinQualsPerNestingLevel>& left_deep_tree_infos,
105  const TemporaryTables& temporary_tables,
106  Executor* executor) {
107  mapd_unique_lock<mapd_shared_mutex> lock(executor->getDataRecyclerLock());
108 
109  auto& cached_dag = executor->getQueryPlanDagCache();
110  QueryPlanDagExtractor dag_extractor(
111  cached_dag, catalog, left_deep_tree_infos, temporary_tables, executor);
112 
113  // add the root node of this query plan DAG
114  auto res = cached_dag.addNodeIfAbsent(node);
115  if (!res) {
116  VLOG(1) << "Stop DAG extraction while adding node to the DAG node cache: "
117  << node->toString();
118  return {node, EMPTY_QUERY_PLAN, nullptr, nullptr, {}, true};
119  }
120  CHECK(res.has_value());
121  node->setRelNodeDagId(res.value());
122  dag_extractor.extracted_dag_.push_back(res.value());
123 
124  // visit child node if necessary
125  auto num_child_node = node->inputCount();
126  switch (num_child_node) {
127  case 1: // unary op
128  dag_extractor.visit(node, node->getInput(0));
129  break;
130  case 2: // binary op
131  if (auto trans_join_node = dynamic_cast<const RelTranslatedJoin*>(node)) {
132  dag_extractor.visit(trans_join_node, trans_join_node->getLHS());
133  dag_extractor.visit(trans_join_node, trans_join_node->getRHS());
134  break;
135  }
136  VLOG(1) << "Visit an invalid rel node while extracting query plan DAG: "
137  << ::toString(node);
138  return {node, EMPTY_QUERY_PLAN, nullptr, nullptr, {}, true};
139  case 0: // leaf node
140  break;
141  default:
142  // since we replace RelLeftDeepJoin as a set of RelTranslatedJoin
143  // which is a binary op, # child nodes for every rel node should be <= 2
144  UNREACHABLE();
145  }
146 
147  // check whether extracted DAG is available to use
148  if (dag_extractor.extracted_dag_.empty() || dag_extractor.isDagExtractionAvailable()) {
149  return {node, EMPTY_QUERY_PLAN, nullptr, nullptr, {}, true};
150  }
151 
152  return {node,
153  dag_extractor.getExtractedQueryPlanDagStr(),
154  dag_extractor.getTranslatedJoinInfo(),
155  dag_extractor.getPerNestingJoinQualInfo(left_deep_tree_id),
156  dag_extractor.getHashTableBuildDag(),
157  false};
158 }
159 
161  std::ostringstream oss;
163  oss << "N/A";
164  } else {
165  for (auto& dag_node_id : extracted_dag_) {
166  oss << dag_node_id << "|";
167  }
168  }
169  return oss.str();
170 }
171 
173  std::optional<RelNodeId> retrieved_node_id) {
174  if (!retrieved_node_id) {
175  VLOG(1) << "Stop DAG extraction while adding node to the DAG node cache: "
176  << node->toString();
178  extracted_dag_.clear();
179  return false;
180  }
181  CHECK(retrieved_node_id.has_value());
182  node->setRelNodeDagId(retrieved_node_id.value());
183  return true;
184 }
185 
187  const RelAlgNode* parent_node,
188  const RelAlgNode* child_node,
189  std::optional<RelNodeId> retrieved_node_id) {
190  CHECK(parent_node);
191  CHECK(child_node);
192  CHECK(retrieved_node_id.has_value());
193  auto parent_node_id = parent_node->getRelNodeDagId();
194  global_dag_.connectNodes(parent_node_id, retrieved_node_id.value());
195  extracted_dag_.push_back(retrieved_node_id.value());
196  return true;
197 }
198 
199 // we recursively visit each rel node starting from the root
200 // and collect assigned rel node ids and return them as query plan DAG
201 // for join operations we additionally generate additional information
202 // to recycle each hashtable that needs to process a given query
203 void QueryPlanDagExtractor::visit(const RelAlgNode* parent_node,
204  const RelAlgNode* child_node) {
205  if (!child_node || contain_not_supported_rel_node_) {
206  return;
207  }
208  auto register_and_visit = [this](const RelAlgNode* parent_node,
209  const RelAlgNode* child_node) {
210  // This function takes a responsibility for all rel nodes
211  // except 1) RelLeftDeepJoinTree and 2) RelTranslatedJoin
212  auto res = global_dag_.addNodeIfAbsent(child_node);
213  if (validateNodeId(child_node, res) &&
214  registerNodeToDagCache(parent_node, child_node, res)) {
215  for (size_t i = 0; i < child_node->inputCount(); i++) {
216  visit(child_node, child_node->getInput(i));
217  }
218  }
219  };
220  if (auto left_deep_joins = dynamic_cast<const RelLeftDeepInnerJoin*>(child_node)) {
221  if (left_deep_tree_infos_.empty()) {
222  // we should have left_deep_tree_info for input left deep tree node
223  VLOG(1) << "Stop DAG extraction due to not supported join pattern";
225  extracted_dag_.clear();
226  return;
227  }
228  const auto inner_cond = left_deep_joins->getInnerCondition();
229  // we analyze left-deep join tree as per-join qual level, so
230  // when visiting RelLeftDeepInnerJoin we decompose it into individual join node
231  // (RelTranslatedJoin).
232  // Thus, this RelLeftDeepInnerJoin object itself is useless when recycling data
233  // but sometimes it has inner condition that has to consider so we add an extra
234  // RelFilter node containing the condition to keep query semantic correctly
235  if (auto cond = dynamic_cast<const RexOperator*>(inner_cond)) {
236  RexDeepCopyVisitor copier;
237  auto copied_inner_cond = copier.visit(cond);
238  auto dummy_filter = std::make_shared<RelFilter>(copied_inner_cond);
239  register_and_visit(parent_node, dummy_filter.get());
240  handleLeftDeepJoinTree(dummy_filter.get(), left_deep_joins);
241  } else {
242  handleLeftDeepJoinTree(parent_node, left_deep_joins);
243  }
244  } else if (auto translated_join_node =
245  dynamic_cast<const RelTranslatedJoin*>(child_node)) {
246  handleTranslatedJoin(parent_node, translated_join_node);
247  } else {
248  register_and_visit(parent_node, child_node);
249  }
250 }
251 
253  const RelAlgNode* parent_node,
254  const RelTranslatedJoin* rel_trans_join) {
255  // when left-deep tree has multiple joins this rel_trans_join can be revisited
256  // but we need to mark the child query plan to accurately catch the query plan dag
257  // here we do not create new dag id since all rel nodes are visited already
258  CHECK(parent_node);
259  CHECK(rel_trans_join);
260 
261  auto res = global_dag_.addNodeIfAbsent(rel_trans_join);
262  if (!validateNodeId(rel_trans_join, res) ||
263  !registerNodeToDagCache(parent_node, rel_trans_join, res)) {
264  return;
265  }
266 
267  // To extract an access path (query plan DAG) for hashtable is to use a difference of
268  // two query plan DAGs 1) query plan DAG after visiting RHS node and 2) query plan DAG
269  // after visiting LHS node so by comparing 1) and 2) we can extract which query plan DAG
270  // is necessary to project join cols that are used to build a hashtable and we use it as
271  // hashtable access path
272  QueryPlan current_plan_dag, after_rhs_visited, after_lhs_visited;
273  current_plan_dag = getExtractedQueryPlanDagStr();
274  if (rel_trans_join->getRHS()) {
275  visit(rel_trans_join, rel_trans_join->getRHS());
276  after_rhs_visited = getExtractedQueryPlanDagStr();
277  }
278  if (rel_trans_join->getLHS()) {
279  visit(rel_trans_join, rel_trans_join->getLHS());
280  after_lhs_visited = getExtractedQueryPlanDagStr();
281  }
282  if (isEmptyQueryPlanDag(after_lhs_visited) || isEmptyQueryPlanDag(after_rhs_visited)) {
283  VLOG(1) << "Stop DAG extraction while extracting query plan DAG for join qual";
285  extracted_dag_.clear();
286  return;
287  }
288  // after visiting new node, we have added node id(s) which can be used as an access path
289  // so, we extract that node id(s) by splitting the new plan dag by the current plan dag
290  auto outer_table_identifier = split(after_rhs_visited, current_plan_dag)[1];
291  auto hash_table_identfier = split(after_lhs_visited, after_rhs_visited)[1];
292 
293  if (!rel_trans_join->isNestedLoopQual()) {
294  std::ostringstream oss;
295  auto inner_join_cols = rel_trans_join->getJoinCols(true);
296  oss << global_dag_.translateColVarsToInfoString(inner_join_cols, false);
297  auto hash_table_cols_info = oss.str();
298  oss << "|";
299  auto outer_join_cols = rel_trans_join->getJoinCols(false);
300  oss << global_dag_.translateColVarsToInfoString(outer_join_cols, false);
301  auto join_qual_info = oss.str();
302  // hash table join cols info | hash table build plan dag (hashtable identifier or
303  // hashtable access path)
304  auto it = hash_table_query_plan_dag_.find(join_qual_info);
305  if (it == hash_table_query_plan_dag_.end()) {
306  VLOG(2) << "Add hashtable access path"
307  << ", join col info: " << hash_table_cols_info
308  << ", access path: " << hash_table_identfier;
310  join_qual_info, std::make_pair(hash_table_cols_info, hash_table_identfier));
311  }
312  } else {
313  VLOG(2) << "Add loop join access path, for LHS: " << outer_table_identifier
314  << ", for RHS: " << hash_table_identfier;
315  }
316 }
317 
318 namespace {
319 struct OpInfo {
320  std::string type_;
321  std::string qualifier_;
322  std::string typeinfo_;
323 };
324 
325 // Return the input index whose tableId matches the given tbl_id.
326 // If none then return -1.
327 int get_input_idx(const RelLeftDeepInnerJoin* rel_left_deep_join, int const tbl_id) {
328  for (size_t input_idx = 0; input_idx < rel_left_deep_join->inputCount(); ++input_idx) {
329  auto const input_node = rel_left_deep_join->getInput(input_idx);
330  auto const scan_node = dynamic_cast<const RelScan*>(input_node);
331  int const target_table_id = scan_node ? scan_node->getTableDescriptor()->tableId
332  : -1 * input_node->getId(); // temporary table
333  if (target_table_id == tbl_id) {
334  return input_idx;
335  }
336  }
337  return -1;
338 }
339 } // namespace
340 
342  Analyzer::Expr const* col_info) {
343  auto col_var = dynamic_cast<const Analyzer::ColumnVar*>(col_info);
344  if (!col_var) {
345  auto visited_cols = global_dag_.collectColVars(col_info);
346  if (visited_cols.size() == 1) {
347  col_var = dynamic_cast<const Analyzer::ColumnVar*>(visited_cols[0]);
348  }
349  }
350  return col_var;
351 }
352 
353 // we coalesce join quals and related filter conditions into a single RelLeftDeepInnerJoin
354 // node when converting calcite AST to logical query plan, but to recycle hashtable(s) we
355 // need to know access path of each hashtable, so we disassemble it into a set of join
356 // qual and collect hashtable info from there
358  const RelAlgNode* parent_node,
359  const RelLeftDeepInnerJoin* rel_left_deep_join) {
360  CHECK(parent_node);
361  CHECK(rel_left_deep_join);
362 
363  // RelLeftDeepInnerJoin node does not need to be added to DAG since
364  // RelLeftDeepInnerJoin is a logical node and
365  // we add all join nodes of this `RelLeftDeepInnerJoin`
366  // thus, the below `left_deep_tree_id` is not the same as its DAG id
367  // (we do not have a DAG node id for this `RelLeftDeepInnerJoin`)
368  auto left_deep_tree_id = rel_left_deep_join->getId();
369  auto left_deep_join_info = getPerNestingJoinQualInfo(left_deep_tree_id);
370  if (!left_deep_join_info) {
371  // we should have left_deep_tree_info for input left deep tree node
372  VLOG(1) << "Stop DAG extraction due to not supported join pattern";
374  extracted_dag_.clear();
375  return;
376  }
377 
378  // gathering per-join qual level info to correctly recycle each hashtable (and necessary
379  // info) that we created
380  // Here we visit each join qual in bottom-up manner to distinct DAGs among join quals
381  // Let say we have three joins- #1: R.a = S.a / #2: R.b = T.b / #3. R.c = X.c
382  // When we start to visit #1, we cannot determine outer col's dag clearly
383  // since we need to visit #2 and #3 due to the current visitor's behavior
384  // In contrast, when starting from #3, we clearly retrieve both inputs' dag
385  // by skipping already visited nodes
386  // So when we visit #2 after visiting #3, we can skip to consider nodes beloning to
387  // qual #3 so we clearly retrieve DAG only corresponding to #2's
388  for (size_t level_idx = 0; level_idx < left_deep_join_info->size(); ++level_idx) {
389  const auto& current_level_join_conditions = left_deep_join_info->at(level_idx);
390  std::vector<const Analyzer::ColumnVar*> inner_join_cols;
391  std::vector<const Analyzer::ColumnVar*> outer_join_cols;
392  std::vector<std::shared_ptr<const Analyzer::Expr>> filter_ops;
393  int inner_input_idx{-1};
394  int outer_input_idx{-1};
395  OpInfo op_info{"UNDEFINED", "UNDEFINED", "UNDEFINED"};
396  std::unordered_set<std::string> visited_filter_ops;
397 
398  // we first check whether this qual needs nested loop
399  const bool found_eq_join_qual =
400  current_level_join_conditions.type != JoinType::INVALID &&
401  boost::algorithm::any_of(current_level_join_conditions.quals, IsEquivBinOp{});
402  const bool nested_loop = !found_eq_join_qual;
403 
404  RexScalar const* const outer_join_cond =
405  current_level_join_conditions.type == JoinType::LEFT
406  ? rel_left_deep_join->getOuterCondition(level_idx + 1)
407  : nullptr;
408 
409  // collect join col, filter ops, and detailed info of join operation, i.e., op_type,
410  // qualifier, ...
411  // when we have more than one quals, i.e., current_level_join_conditions.quals.size()
412  // > 1, we consider the first qual is used as hashtable building
413  for (const auto& join_qual : current_level_join_conditions.quals) {
414  auto qual_bin_oper = std::dynamic_pointer_cast<const Analyzer::BinOper>(join_qual);
415  auto join_qual_str = ::toString(join_qual);
416  if (qual_bin_oper) {
417  if (join_qual == current_level_join_conditions.quals.front()) {
418  // set op_info based on the first qual
419  op_info = OpInfo{::toString(qual_bin_oper->get_optype()),
420  ::toString(qual_bin_oper->get_qualifier()),
421  qual_bin_oper->get_type_info().to_string()};
422  }
423  for (auto& col_pair_info : normalizeColumnsPair(qual_bin_oper.get(), catalog_)) {
424  if (col_pair_info.loop_join_qual && !found_eq_join_qual) {
425  // we only consider that cur level's join is loop join if we have no
426  // equi-join qual and both lhs and rhs are not col_var,
427  // i.e., lhs: col_var / rhs: constant / bin_op: kGE
428  if (visited_filter_ops.emplace(std::move(join_qual_str)).second) {
429  filter_ops.push_back(join_qual);
430  }
431  } else {
432  // a qual_bin_oper becomes an inner join qual iff both lhs and rhs are col_var
433  // otherwise it becomes a filter qual
434  bool found_valid_col_vars = false;
435  if (col_pair_info.inner_outer.first && col_pair_info.inner_outer.second) {
436  auto const* lhs_col_var = getColVar(col_pair_info.inner_outer.first);
437  auto const* rhs_col_var = getColVar(col_pair_info.inner_outer.second);
438  // this qual is valid and used for join op
439  if (lhs_col_var && rhs_col_var) {
440  found_valid_col_vars = true;
441  if (inner_input_idx == -1) {
442  inner_input_idx =
443  get_input_idx(rel_left_deep_join, lhs_col_var->get_table_id());
444  }
445  if (outer_input_idx == -1) {
446  outer_input_idx =
447  get_input_idx(rel_left_deep_join, rhs_col_var->get_table_id());
448  }
449  inner_join_cols.push_back(lhs_col_var);
450  outer_join_cols.push_back(rhs_col_var);
451  }
452  }
453  if (!found_valid_col_vars &&
454  visited_filter_ops.emplace(std::move(join_qual_str)).second) {
455  filter_ops.push_back(join_qual);
456  }
457  }
458  }
459  } else {
460  if (visited_filter_ops.emplace(std::move(join_qual_str)).second) {
461  filter_ops.push_back(join_qual);
462  }
463  }
464  }
465  if (inner_join_cols.size() != outer_join_cols.size()) {
466  VLOG(1) << "Stop DAG extraction due to inner/outer col mismatch";
468  extracted_dag_.clear();
469  return;
470  }
471 
472  // create RelTranslatedJoin based on the collected info from the join qual
473  // there are total seven types of join query pattern
474  // 1. INNER HASH ONLY
475  // 2. INNER LOOP ONLY (!)
476  // 3. LEFT LOOP ONLY
477  // 4. INNER HASH + INNER LOOP (!)
478  // 5. LEFT LOOP + INNER HASH
479  // 6. LEFT LOOP + INNER LOOP (!)
480  // 7. LEFT LOOP + INNER HASH + INNER LOOP (!)
481  // here, if a query contains INNER LOOP join, its qual has nothing
482  // so, some patterns do not have bin_oper at the specific join nest level
483  // if we find INNER LOOP, corresponding RelTranslatedJoin has nulled LHS and RHS
484  // to mark it as loop join
485  const RelAlgNode* lhs;
486  const RelAlgNode* rhs;
487  if (inner_input_idx != -1 && outer_input_idx != -1) {
488  lhs = rel_left_deep_join->getInput(inner_input_idx);
489  rhs = rel_left_deep_join->getInput(outer_input_idx);
490  } else {
491  if (level_idx == 0) {
492  lhs = rel_left_deep_join->getInput(0);
493  rhs = rel_left_deep_join->getInput(1);
494  } else {
495  lhs = translated_join_info_->rbegin()->get();
496  rhs = rel_left_deep_join->getInput(level_idx + 1);
497  }
498  }
499  CHECK(lhs);
500  CHECK(rhs);
501  auto cur_translated_join_node =
502  std::make_shared<RelTranslatedJoin>(lhs,
503  rhs,
504  inner_join_cols,
505  outer_join_cols,
506  filter_ops,
507  outer_join_cond,
508  nested_loop,
509  current_level_join_conditions.type,
510  op_info.type_,
511  op_info.qualifier_,
512  op_info.typeinfo_);
513  CHECK(cur_translated_join_node);
514  handleTranslatedJoin(parent_node, cur_translated_join_node.get());
515  translated_join_info_->push_back(std::move(cur_translated_join_node));
516  }
517 }
bool isNestedLoopQual() const
#define CHECK_EQ(x, y)
Definition: Logger.h:217
QueryPlanDagCache & global_dag_
std::string cat(Ts &&...args)
std::string toString(const ExtArgumentType &sig_type)
std::vector< InnerOuterOrLoopQual > normalizeColumnsPair(const Analyzer::BinOper *condition, const Catalog_Namespace::Catalog &cat)
class for a per-database catalog. also includes metadata for the current database and the current use...
Definition: Catalog.h:102
std::optional< RelNodeId > addNodeIfAbsent(const RelAlgNode *)
#define IS_EQUIVALENCE(X)
Definition: sqldefs.h:67
void connectNodes(const RelNodeId parent_id, const RelNodeId child_id)
const Catalog_Namespace::Catalog & catalog_
static ExtractedPlanDag extractQueryPlanDag(const RelAlgNode *node, const Catalog_Namespace::Catalog &catalog, std::optional< unsigned > left_deep_tree_id, std::unordered_map< unsigned, JoinQualsPerNestingLevel > &left_deep_tree_infos, const TemporaryTables &temporary_tables, Executor *executor, const RelAlgTranslator &rel_alg_translator)
const RexScalar * getOuterCondition(const size_t nesting_level) const
const Expr * get_right_operand() const
Definition: Analyzer.h:443
void setRelNodeDagId(const size_t id) const
const TemporaryTables & temporary_tables_
#define UNREACHABLE()
Definition: Logger.h:253
std::vector< const Analyzer::ColumnVar * > getJoinCols(bool lhs) const
static bool isNotSupportedDag(const RelAlgNode *rel_alg_node, const RelAlgTranslator &rel_alg_translator)
HashTableBuildDagMap & getHashTableBuildDag()
std::vector< std::string > split(std::string_view str, std::string_view delim, std::optional< size_t > maxsplit)
split apart a string into a vector of substrings
std::unordered_map< int, const ResultSetPtr & > TemporaryTables
Definition: InputMetadata.h:31
virtual T visit(const RexScalar *rex_scalar) const
Definition: RexVisitor.h:27
const RelAlgNode * getRHS() const
bool validateNodeId(const RelAlgNode *node, std::optional< RelNodeId > retrieved_node_id)
unsigned getId() const
std::string getExtractedQueryPlanDagStr()
bool isEmptyQueryPlanDag(const std::string &dag)
void handleTranslatedJoin(const RelAlgNode *, const RelTranslatedJoin *)
const RelAlgNode * getInput(const size_t idx) const
std::vector< size_t > extracted_dag_
bool registerNodeToDagCache(const RelAlgNode *parent_node, const RelAlgNode *child_node, std::optional< RelNodeId > retrieved_node_id)
JoinColumnsInfo translateColVarsToInfoString(std::vector< const Analyzer::ColumnVar * > &col_vars, bool col_id_only) const
HashTableBuildDagMap hash_table_query_plan_dag_
void handleLeftDeepJoinTree(const RelAlgNode *, const RelLeftDeepInnerJoin *)
const RelAlgNode * getLHS() const
void visit(const RelAlgNode *, const RelAlgNode *)
std::shared_ptr< TranslatedJoinInfo > translated_join_info_
static InnerOuter normalizeColumnPair(const Analyzer::Expr *lhs, const Analyzer::Expr *rhs, const Catalog_Namespace::Catalog &cat, const TemporaryTables *temporary_tables, const bool is_overlaps_join=false)
Definition: HashJoin.cpp:587
virtual std::string toString() const =0
constexpr char const * EMPTY_QUERY_PLAN
#define CHECK(condition)
Definition: Logger.h:209
int get_input_idx(const RelLeftDeepInnerJoin *rel_left_deep_join, int const tbl_id)
std::unordered_map< unsigned, JoinQualsPerNestingLevel > & left_deep_tree_infos_
const Expr * get_left_operand() const
Definition: Analyzer.h:442
bool is_overlaps_oper() const
Definition: Analyzer.h:440
static ExtractedPlanDag extractQueryPlanDagImpl(const RelAlgNode *node, const Catalog_Namespace::Catalog &catalog, std::optional< unsigned > left_deep_tree_id, std::unordered_map< unsigned, JoinQualsPerNestingLevel > &left_deep_tree_infos, const TemporaryTables &temporary_tables, Executor *executor)
const size_t inputCount() const
size_t getRelNodeDagId() const
const TableDescriptor * getTableDescriptor() const
std::string QueryPlan
#define VLOG(n)
Definition: Logger.h:303
std::vector< const Analyzer::ColumnVar * > collectColVars(const Analyzer::Expr *target)
const JoinQualsPerNestingLevel * getPerNestingJoinQualInfo(std::optional< unsigned > left_deep_join_tree_id)
bool operator()(std::shared_ptr< Analyzer::Expr > const &qual)
std::shared_ptr< TranslatedJoinInfo > getTranslatedJoinInfo()
Analyzer::ColumnVar const * getColVar(const Analyzer::Expr *col_info)