OmniSciDB  72c90bc290
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
anonymous_namespace{FromTableReordering.cpp} Namespace Reference

Classes

class  SchedulingDependencyTracking
 
struct  TraversalEdge
 

Typedefs

using cost_t = unsigned
 
using node_t = size_t
 

Functions

static std::unordered_map
< SQLTypes, cost_t >
std::tuple< cost_t, cost_t,
InnerQualDecision
get_join_qual_cost (const Analyzer::Expr *qual, const Executor *executor)
 
std::vector< std::map< node_t,
cost_t > > 
build_join_cost_graph (const JoinQualsPerNestingLevel &left_deep_join_quals, const std::vector< InputTableInfo > &table_infos, const Executor *executor, std::vector< std::map< node_t, InnerQualDecision >> &qual_detection_res)
 
SchedulingDependencyTracking build_dependency_tracking (const JoinQualsPerNestingLevel &left_deep_join_quals, const std::vector< std::map< node_t, cost_t >> &join_cost_graph)
 
std::vector< node_ttraverse_join_cost_graph (const std::vector< std::map< node_t, cost_t >> &join_cost_graph, const std::vector< InputTableInfo > &table_infos, const std::function< bool(const node_t lhs_nest_level, const node_t rhs_nest_level)> &compare_node, const std::function< bool(const TraversalEdge &, const TraversalEdge &)> &compare_edge, const JoinQualsPerNestingLevel &left_deep_join_quals, std::vector< std::map< node_t, InnerQualDecision >> &qual_normalization_res)
 

Variables

static std::unordered_map
< SQLTypes, cost_t
GEO_TYPE_COSTS {kPOINT, 60}
 

Typedef Documentation

using anonymous_namespace{FromTableReordering.cpp}::cost_t = typedef unsigned

Definition at line 29 of file FromTableReordering.cpp.

using anonymous_namespace{FromTableReordering.cpp}::node_t = typedef size_t

Definition at line 30 of file FromTableReordering.cpp.

Function Documentation

SchedulingDependencyTracking anonymous_namespace{FromTableReordering.cpp}::build_dependency_tracking ( const JoinQualsPerNestingLevel left_deep_join_quals,
const std::vector< std::map< node_t, cost_t >> &  join_cost_graph 
)

Definition at line 266 of file FromTableReordering.cpp.

References anonymous_namespace{FromTableReordering.cpp}::SchedulingDependencyTracking::addEdge(), LEFT, and run_benchmark_import::type.

Referenced by traverse_join_cost_graph().

268  {
269  SchedulingDependencyTracking dependency_tracking(left_deep_join_quals.size() + 1);
270  // Add directed graph edges for left join dependencies.
271  // See also start_it inside traverse_join_cost_graph(). These
272  // edges prevent start_it from pointing to a table with a
273  // left join dependency on another table.
274  for (size_t level_idx = 0; level_idx < left_deep_join_quals.size(); ++level_idx) {
275  if (left_deep_join_quals[level_idx].type == JoinType::LEFT) {
276  dependency_tracking.addEdge(level_idx, level_idx + 1);
277  }
278  }
279  return dependency_tracking;
280 }

+ Here is the call graph for this function:

+ Here is the caller graph for this function:

std::vector<std::map<node_t, cost_t> > anonymous_namespace{FromTableReordering.cpp}::build_join_cost_graph ( const JoinQualsPerNestingLevel left_deep_join_quals,
const std::vector< InputTableInfo > &  table_infos,
const Executor executor,
std::vector< std::map< node_t, InnerQualDecision >> &  qual_detection_res 
)

Definition at line 190 of file FromTableReordering.cpp.

References CHECK_EQ, CHECK_GE, get_join_qual_cost(), and ScalarExprVisitor< T >::visit().

Referenced by get_node_input_permutation().

194  {
195  CHECK_EQ(left_deep_join_quals.size() + 1, table_infos.size());
196  std::vector<std::map<node_t, cost_t>> join_cost_graph(table_infos.size());
198  // Build the constraints graph: nodes are nest levels, edges are the existence of
199  // qualifiers between levels.
200  for (const auto& current_level_join_conditions : left_deep_join_quals) {
201  for (const auto& qual : current_level_join_conditions.quals) {
202  std::set<int> qual_nest_levels = visitor.visit(qual.get());
203  if (qual_nest_levels.size() != 2) {
204  continue;
205  }
206  int lhs_nest_level = *qual_nest_levels.begin();
207  CHECK_GE(lhs_nest_level, 0);
208  qual_nest_levels.erase(qual_nest_levels.begin());
209  int rhs_nest_level = *qual_nest_levels.begin();
210  CHECK_GE(rhs_nest_level, 0);
211 
212  // Get the {lhs, rhs} cost for the qual
213  const auto qual_costing = get_join_qual_cost(qual.get(), executor);
214  qual_detection_res[lhs_nest_level][rhs_nest_level] = std::get<2>(qual_costing);
215  qual_detection_res[rhs_nest_level][lhs_nest_level] = std::get<2>(qual_costing);
216  const auto edge_it = join_cost_graph[lhs_nest_level].find(rhs_nest_level);
217  auto rhs_cost = std::get<1>(qual_costing);
218  if (edge_it == join_cost_graph[lhs_nest_level].end() ||
219  edge_it->second > rhs_cost) {
220  auto lhs_cost = std::get<0>(qual_costing);
221  join_cost_graph[lhs_nest_level][rhs_nest_level] = rhs_cost;
222  join_cost_graph[rhs_nest_level][lhs_nest_level] = lhs_cost;
223  }
224  }
225  }
226  return join_cost_graph;
227 }
#define CHECK_EQ(x, y)
Definition: Logger.h:301
static std::unordered_map< SQLTypes, cost_t > std::tuple< cost_t, cost_t, InnerQualDecision > get_join_qual_cost(const Analyzer::Expr *qual, const Executor *executor)
#define CHECK_GE(x, y)
Definition: Logger.h:306
T visit(const Analyzer::Expr *expr) const

+ Here is the call graph for this function:

+ Here is the caller graph for this function:

static std::unordered_map<SQLTypes, cost_t> std::tuple<cost_t, cost_t, InnerQualDecision> anonymous_namespace{FromTableReordering.cpp}::get_join_qual_cost ( const Analyzer::Expr qual,
const Executor executor 
)

Definition at line 42 of file FromTableReordering.cpp.

References CHECK, CHECK_NE, GEO_TYPE_COSTS, get_table_cardinality(), Analyzer::Expr::get_type_info(), Analyzer::FunctionOper::getArg(), GeospatialFunctionFinder::getGeoArgCvs(), GeospatialFunctionFinder::getGeoFunctionName(), GeospatialFunctionFinder::getJoinTableKeyPair(), IGNORE, HashJoinFail::inner_qual_decision, is_constructed_point(), IS_EQUIVALENCE, BoundingBoxIntersectJoinSupportedFunction::is_point_poly_rewrite_target_func(), BoundingBoxIntersectJoinSupportedFunction::is_poly_point_rewrite_target_func(), kPOINT, LHS, HashJoin::normalizeColumnPairs(), RHS, UNKNOWN, ScalarExprVisitor< T >::visit(), and VLOG.

Referenced by build_join_cost_graph().

44  {
45  if (executor) {
46  GeospatialFunctionFinder geo_func_finder;
47  geo_func_finder.visit(qual);
48  if (auto table_key_pair = geo_func_finder.getJoinTableKeyPair()) {
49  auto const inner_table_key = (*table_key_pair).inner_table_key;
50  auto const outer_table_key = (*table_key_pair).outer_table_key;
51  // try to find a chance to swap tables in the binary join
52  // note that self-join does not need to be swapped
53  CHECK_NE(inner_table_key, outer_table_key);
54  const auto& target_geo_func_name = geo_func_finder.getGeoFunctionName();
55  auto const inner_table_cardinality =
56  get_table_cardinality(inner_table_key, executor);
57  auto const outer_table_cardinality =
58  get_table_cardinality(outer_table_key, executor);
59  auto inner_qual_decision = inner_table_cardinality > outer_table_cardinality
62  // detect the case when table reordering by cardinality incurs unexpected overhead
63  // i.e., SELECT ... FROM R, S where ST_Interesects(S.poly, R.pt) where |R| > |S|
64  // but |R| is not that larger than |S|, i.e., |R| / |S| < 10.0
65  // in this case, it might be better if keeping the existing ordering
66  // to exploit bounding-box intersection w/ hash join framework instead of loop join
67  const auto& geo_args = geo_func_finder.getGeoArgCvs();
68  const auto inner_cv_it =
69  std::find_if(geo_args.begin(),
70  geo_args.end(),
71  [&inner_table_key](const Analyzer::ColumnVar* cv) {
72  return cv->getTableKey() == inner_table_key;
73  });
74  CHECK(inner_cv_it != geo_args.end());
75  const auto inner_cv = *inner_cv_it;
76  bool needs_table_reordering = inner_table_cardinality < outer_table_cardinality;
77  const auto outer_inner_card_ratio =
78  outer_table_cardinality / static_cast<double>(inner_table_cardinality);
80  target_geo_func_name) ||
82  target_geo_func_name)) {
83  // the goal of this is to maximize the chance of using bounding-box intersection
84  // (aka. bbox-intersection) to filter out unnecessary pair of geometries before
85  // computing geo function to achieve this, point column has zero for its rte_idx
86  // (so we build a hash table based on poly column which has rte_idx = 1) but if
87  // it's cardinality is smaller than that of polygon table more than 10x we try to
88  // fall back to loop join to avoid too expensive bbox-intersection
89  if (inner_cv->get_rte_idx() == 0 &&
90  (inner_cv->get_type_info().get_type() == kPOINT)) {
91  // outer is poly, and we can use hash join w/ bbox-intersection
92  if (needs_table_reordering && outer_inner_card_ratio > 10.0 &&
93  inner_table_cardinality < 10000) {
94  // but current pt table is small enough and hash table is larger than
95  // the pt table at least 10 times, then we fall back to loop join
96  // to avoid too expensive hash join
97  // so let's try to set inner table as poly table to invalidate
98  // rte index requirement
99  return {200, 200, InnerQualDecision::RHS};
100  } else {
101  // otherwise, try to keep the existing ordering
102  return {180, 190, InnerQualDecision::IGNORE};
103  }
104  } else {
105  // poly is the inner table, so we need to reorder tables to use
106  // bbox-intersection
107  if (needs_table_reordering) {
108  // outer point table is larger than inner poly table, so let's reorder them
109  // by table cardinality
110  return {200, 200, InnerQualDecision::RHS};
111  } else {
112  // otherwise, try to keep the existing ordering
113  return {180, 190, InnerQualDecision::IGNORE};
114  }
115  }
116  }
117  // rest of bbox-intersection-available and unavailable geo functions
118  // can reach here, and they are reordered by table cardinality
119  // specifically, bbox-intersection-available geo join functions are satisfied one of
120  // followings: ST_INTERSECTSBOX_sv and is_poly_mpoly_rewrite_target_func we can use
121  // bbox-intersection hash join for those functions regardless of table ordering see
122  // rewrite_bbox_intersection function in ExpressionRewrite.cpp
123  VLOG(2) << "Detect geo join operator, initial_inner_table(db_id: "
124  << inner_table_key.db_id << ", table_id: " << inner_table_key.table_id
125  << "), cardinality: " << inner_table_cardinality
126  << "), initial_outer_table(db_id: " << outer_table_key.db_id
127  << ", table_id: " << outer_table_key.table_id
128  << "), cardinality: " << outer_table_cardinality
129  << "), inner_qual_decision: " << inner_qual_decision;
130  return {200, 200, inner_qual_decision};
131 
132  } else {
133  // let's fall back to the old strategy by ordering tables by types of geometries
134  const auto func_oper = dynamic_cast<const Analyzer::FunctionOper*>(qual);
135  if (func_oper) {
136  std::vector<SQLTypes> geo_types_for_func;
137  for (size_t i = 0; i < func_oper->getArity(); i++) {
138  const auto arg_expr = func_oper->getArg(i);
139  const auto& ti = arg_expr->get_type_info();
140  if (ti.is_geometry() || is_constructed_point(arg_expr)) {
141  geo_types_for_func.push_back(ti.get_type());
142  }
143  }
144  std::regex geo_func_regex("ST_[\\w]*");
145  std::smatch geo_func_match;
146  const auto& func_name = func_oper->getName();
147  if (geo_types_for_func.size() == 2 &&
148  std::regex_match(func_name, geo_func_match, geo_func_regex)) {
149  const auto rhs_cost = GEO_TYPE_COSTS[geo_types_for_func[0]];
150  const auto lhs_cost = GEO_TYPE_COSTS[geo_types_for_func[1]];
151  return {lhs_cost, rhs_cost, InnerQualDecision::IGNORE};
152  }
153  return {200, 200, InnerQualDecision::IGNORE};
154  }
155  }
156  }
157 
158  const auto bin_oper = dynamic_cast<const Analyzer::BinOper*>(qual);
159  if (!bin_oper || !IS_EQUIVALENCE(bin_oper->get_optype())) {
160  return {200, 200, InnerQualDecision::IGNORE};
161  }
162  InnerQualDecision inner_qual_decision = InnerQualDecision::UNKNOWN;
163  if (executor) {
164  try {
165  const auto normalized_bin_oper =
166  HashJoin::normalizeColumnPairs(bin_oper, executor->getTemporaryTables());
167  const auto& inner_outer = normalized_bin_oper.first;
168  // normalization success, so we need to figure out which cv becomes an inner
169  auto lhs = bin_oper->get_left_operand();
170  if (auto lhs_tuple = dynamic_cast<const Analyzer::ExpressionTuple*>(
171  bin_oper->get_left_operand())) {
172  lhs = lhs_tuple->getTuple().front().get();
173  }
174  CHECK(lhs);
175  if (lhs == inner_outer.front().first) {
176  inner_qual_decision = InnerQualDecision::LHS;
177  } else if (lhs == inner_outer.front().second) {
178  inner_qual_decision = InnerQualDecision::RHS;
179  }
180  } catch (const HashJoinFail& e) {
181  return {200, 200, e.inner_qual_decision};
182  } catch (...) {
183  return {200, 200, inner_qual_decision};
184  }
185  }
186  return {100, 100, inner_qual_decision};
187 }
static bool is_point_poly_rewrite_target_func(std::string_view target_func_name)
#define IS_EQUIVALENCE(X)
Definition: sqldefs.h:69
bool is_constructed_point(const Analyzer::Expr *expr)
Definition: Execute.h:1699
size_t get_table_cardinality(shared::TableKey const &table_key, Executor const *executor)
const std::vector< const Analyzer::ColumnVar * > & getGeoArgCvs() const
T visit(const Analyzer::Expr *expr) const
static std::unordered_map< SQLTypes, cost_t > GEO_TYPE_COSTS
#define CHECK_NE(x, y)
Definition: Logger.h:302
const std::string & getGeoFunctionName() const
const SQLTypeInfo & get_type_info() const
Definition: Analyzer.h:79
const Analyzer::Expr * getArg(const size_t i) const
Definition: Analyzer.h:2617
const std::optional< GeoJoinOperandsTableKeyPair > getJoinTableKeyPair() const
#define CHECK(condition)
Definition: Logger.h:291
static std::pair< std::vector< InnerOuter >, std::vector< InnerOuterStringOpInfos > > normalizeColumnPairs(const Analyzer::BinOper *condition, const TemporaryTables *temporary_tables)
Definition: HashJoin.cpp:1015
static bool is_poly_point_rewrite_target_func(std::string_view target_func_name)
InnerQualDecision
Definition: HashJoin.h:61
InnerQualDecision inner_qual_decision
Definition: HashJoin.h:77
#define VLOG(n)
Definition: Logger.h:388

+ Here is the call graph for this function:

+ Here is the caller graph for this function:

std::vector<node_t> anonymous_namespace{FromTableReordering.cpp}::traverse_join_cost_graph ( const std::vector< std::map< node_t, cost_t >> &  join_cost_graph,
const std::vector< InputTableInfo > &  table_infos,
const std::function< bool(const node_t lhs_nest_level, const node_t rhs_nest_level)> &  compare_node,
const std::function< bool(const TraversalEdge &, const TraversalEdge &)> &  compare_edge,
const JoinQualsPerNestingLevel left_deep_join_quals,
std::vector< std::map< node_t, InnerQualDecision >> &  qual_normalization_res 
)

Definition at line 285 of file FromTableReordering.cpp.

References build_dependency_tracking(), CHECK, CHECK_EQ, Analyzer::ColumnVar::colvar_comp(), g_trivial_loop_join_threshold, Analyzer::BinOper::get_own_left_operand(), IGNORE, gpu_enabled::iota(), anonymous_namespace{FromTableReordering.cpp}::TraversalEdge::join_cost, LHS, anonymous_namespace{FromTableReordering.cpp}::TraversalEdge::nest_level, RHS, and VLOG.

Referenced by get_node_input_permutation().

292  {
293  std::vector<node_t> all_nest_levels(table_infos.size());
294  std::iota(all_nest_levels.begin(), all_nest_levels.end(), 0);
295  std::vector<node_t> input_permutation;
296  std::unordered_set<node_t> visited;
297  auto dependency_tracking =
298  build_dependency_tracking(left_deep_join_quals, join_cost_graph);
299  auto schedulable_node = [&dependency_tracking, &visited](const node_t node) {
300  const auto nodes_ready = dependency_tracking.getRoots();
301  return nodes_ready.find(node) != nodes_ready.end() &&
302  visited.find(node) == visited.end();
303  };
304  while (visited.size() < table_infos.size()) {
305  // Filter out nest levels which are already visited or have pending dependencies.
306  std::vector<node_t> remaining_nest_levels;
307  std::copy_if(all_nest_levels.begin(),
308  all_nest_levels.end(),
309  std::back_inserter(remaining_nest_levels),
310  schedulable_node);
311  CHECK(!remaining_nest_levels.empty());
312  // Start with the table with most tuples.
313  const auto start_it = std::max_element(
314  remaining_nest_levels.begin(), remaining_nest_levels.end(), compare_node);
315  CHECK(start_it != remaining_nest_levels.end());
316  std::priority_queue<TraversalEdge, std::vector<TraversalEdge>, decltype(compare_edge)>
317  worklist(compare_edge);
318  // look at all edges, compare the
319  // cost of our edge vs theirs, and pick the best start edge
320  node_t start = *start_it;
321  // we adaptively switch the inner and outer when we have a chance to exploit
322  // hash join framework for a query with a single binary join
323  TraversalEdge start_edge{start, 0};
324 
325  // when we have a single binary join in the query, we can analyze the qual and apply
326  // more smart table reordering logic that maximizes the chance of exploiting hash join
327  // todo (yoonmin) : generalize this for an arbitrary join pipeline
328  if (remaining_nest_levels.size() == 2 && qual_normalization_res[start].size() == 1) {
329  auto inner_qual_decision = qual_normalization_res[start].begin()->second;
330  auto join_qual = left_deep_join_quals.begin()->quals;
331  using ColvarSet =
332  std::set<const Analyzer::ColumnVar*,
333  bool (*)(const Analyzer::ColumnVar*, const Analyzer::ColumnVar*)>;
334 
335  auto set_new_rte_idx = [](ColvarSet& cv_set, int new_rte) {
336  std::for_each(
337  cv_set.begin(), cv_set.end(), [new_rte](const Analyzer::ColumnVar* cv) {
338  const_cast<Analyzer::ColumnVar*>(cv)->set_rte_idx(new_rte);
339  });
340  };
341 
342  // IGNORE: use the existing table reordering logic
343  // KEEP: return the existing table permutation and related cvs (column variables)
344  // SWAP: change the starting table of the table reordering logic and relevant
345  // columns' rte index
346  enum class Decision { IGNORE, KEEP, SWAP };
347 
348  auto analyze_join_qual = [&start,
349  &remaining_nest_levels,
350  &inner_qual_decision,
351  &table_infos,
352  compare_node](const std::shared_ptr<Analyzer::Expr>& lhs,
353  ColvarSet& lhs_colvar_set,
354  const std::shared_ptr<Analyzer::Expr>& rhs,
355  ColvarSet& rhs_colvar_set) {
356  if (!lhs || !rhs || lhs_colvar_set.empty() || rhs_colvar_set.empty()) {
357  return std::make_pair(Decision::IGNORE, start);
358  }
359 
360  auto alternative_it = std::find_if(
361  remaining_nest_levels.begin(),
362  remaining_nest_levels.end(),
363  [start](const size_t nest_level) { return start != nest_level; });
364  CHECK(alternative_it != remaining_nest_levels.end());
365  auto alternative_rte = *alternative_it;
366 
367  Decision decision = Decision::IGNORE;
368  // inner col's rte should be larger than outer col
369  int inner_rte = -1;
370  int outer_rte = -1;
371  bool is_outer_col_valid = false;
372  auto check_expr_is_valid_col = [&is_outer_col_valid](const Analyzer::Expr* expr) {
373  if (auto expr_tuple = dynamic_cast<const Analyzer::ExpressionTuple*>(expr)) {
374  for (auto& inner_expr : expr_tuple->getTuple()) {
375  auto cv_from_expr =
376  HashJoin::getHashJoinColumn<Analyzer::ColumnVar>(inner_expr.get());
377  if (!cv_from_expr) {
378  is_outer_col_valid = false;
379  return;
380  }
381  }
382  } else {
383  auto cv_from_expr = HashJoin::getHashJoinColumn<Analyzer::ColumnVar>(expr);
384  if (!cv_from_expr) {
385  is_outer_col_valid = false;
386  return;
387  }
388  }
389  is_outer_col_valid = true;
390  };
391  if (inner_qual_decision == InnerQualDecision::LHS) {
392  inner_rte = (*lhs_colvar_set.begin())->get_rte_idx();
393  outer_rte = (*rhs_colvar_set.begin())->get_rte_idx();
394  check_expr_is_valid_col(rhs.get());
395  } else if (inner_qual_decision == InnerQualDecision::RHS) {
396  inner_rte = (*rhs_colvar_set.begin())->get_rte_idx();
397  outer_rte = (*lhs_colvar_set.begin())->get_rte_idx();
398  check_expr_is_valid_col(lhs.get());
399  }
400  if (inner_rte >= 0 && outer_rte >= 0) {
401  const auto inner_cardinality =
402  table_infos[inner_rte].info.getNumTuplesUpperBound();
403  const auto outer_cardinality =
404  table_infos[outer_rte].info.getNumTuplesUpperBound();
405  if (inner_cardinality > g_trivial_loop_join_threshold) {
406  if (inner_rte == static_cast<int>(start)) {
407  // inner is driving the join loop but also has a valid join column
408  // which is available for building a hash table
409  // but ignore swapping when inner's cardinality is larger than that of
410  // outer's / otherwise swap inner and outer (to use the valid inner)
411  decision = is_outer_col_valid && inner_cardinality > outer_cardinality
412  ? Decision::IGNORE
413  : Decision::SWAP;
414  } else {
415  CHECK_EQ(inner_rte, static_cast<int>(alternative_rte));
416  // now, a valid inner column is outer table
417  if (compare_node(inner_rte, start)) {
418  // but outer table is larger than the current inner
419  // so we can exploit the existing table reordering logic
420  decision = Decision::IGNORE;
421  } else {
422  // and outer table is smaller than the current inner
423  // so we do not need to reorder the table starting from the inner
424  decision = Decision::KEEP;
425  }
426  }
427  }
428  }
429 
430  if (decision == Decision::KEEP) {
431  return std::make_pair(decision, start);
432  } else if (decision == Decision::SWAP) {
433  return std::make_pair(decision, alternative_rte);
434  }
435  return std::make_pair(Decision::IGNORE, start);
436  };
437 
438  auto collect_colvars = [](const std::shared_ptr<Analyzer::Expr> expr,
439  ColvarSet& cv_set) {
440  expr->collect_column_var(cv_set, false);
441  };
442 
443  auto adjust_reordering_logic = [&start, &start_edge, &start_it, set_new_rte_idx](
444  Decision decision,
445  int alternative_rte,
446  ColvarSet& lhs_colvar_set,
447  ColvarSet& rhs_colvar_set) {
448  CHECK(decision == Decision::SWAP);
449  start = alternative_rte;
450  set_new_rte_idx(lhs_colvar_set, alternative_rte);
451  set_new_rte_idx(rhs_colvar_set, *start_it);
452  start_edge.join_cost = 0;
453  start_edge.nest_level = start;
454  };
455 
456  auto bin_op = dynamic_cast<Analyzer::BinOper*>(join_qual.begin()->get());
457  if (bin_op) {
458  auto lhs = bin_op->get_own_left_operand();
459  auto rhs = bin_op->get_own_right_operand();
460  if (auto lhs_exp = dynamic_cast<Analyzer::ExpressionTuple*>(lhs.get())) {
461  // retrieve the decision and info for adjusting reordering by referring the
462  // first cv and apply them to the rest of cvs
463  auto rhs_exp = dynamic_cast<Analyzer::ExpressionTuple*>(rhs.get());
464  CHECK(rhs_exp);
465  auto& lhs_exprs = lhs_exp->getTuple();
466  auto& rhs_exprs = rhs_exp->getTuple();
467  CHECK_EQ(lhs_exprs.size(), rhs_exprs.size());
468  for (size_t i = 0; i < lhs_exprs.size(); ++i) {
469  Decision decision{Decision::IGNORE};
470  int alternative_rte_idx = -1;
471  ColvarSet lhs_colvar_set(Analyzer::ColumnVar::colvar_comp);
472  ColvarSet rhs_colvar_set(Analyzer::ColumnVar::colvar_comp);
473  collect_colvars(lhs_exprs.at(i), lhs_colvar_set);
474  collect_colvars(rhs_exprs.at(i), rhs_colvar_set);
475  if (i == 0) {
476  auto investigation_res =
477  analyze_join_qual(lhs, lhs_colvar_set, rhs, rhs_colvar_set);
478  decision = investigation_res.first;
479  if (decision == Decision::KEEP) {
480  return remaining_nest_levels;
481  }
482  alternative_rte_idx = investigation_res.second;
483  }
484  if (decision == Decision::SWAP) {
485  adjust_reordering_logic(
486  decision, alternative_rte_idx, lhs_colvar_set, rhs_colvar_set);
487  }
488  }
489  } else {
490  ColvarSet lhs_colvar_set(Analyzer::ColumnVar::colvar_comp);
491  ColvarSet rhs_colvar_set(Analyzer::ColumnVar::colvar_comp);
492  collect_colvars(lhs, lhs_colvar_set);
493  collect_colvars(rhs, rhs_colvar_set);
494  auto investigation_res =
495  analyze_join_qual(lhs, lhs_colvar_set, rhs, rhs_colvar_set);
496  if (investigation_res.first == Decision::KEEP) {
497  return remaining_nest_levels;
498  } else if (investigation_res.first == Decision::SWAP) {
499  adjust_reordering_logic(investigation_res.first,
500  investigation_res.second,
501  lhs_colvar_set,
502  rhs_colvar_set);
503  }
504  }
505  }
506  }
507 
508  VLOG(2) << "Table reordering starting with nest level " << start;
509  for (const auto& graph_edge : join_cost_graph[*start_it]) {
510  const node_t succ = graph_edge.first;
511  if (!schedulable_node(succ)) {
512  continue;
513  }
514  const TraversalEdge succ_edge{succ, graph_edge.second};
515  for (const auto& successor_edge : join_cost_graph[succ]) {
516  if (successor_edge.first == start) {
517  start_edge.join_cost = successor_edge.second;
518  // lhs cost / num tuples less than rhs cost if compare edge is true, swap nest
519  // levels
520  if (compare_edge(start_edge, succ_edge)) {
521  VLOG(2) << "Table reordering changing start nest level from " << start
522  << " to " << succ;
523  start = succ;
524  start_edge = succ_edge;
525  }
526  }
527  }
528  }
529  VLOG(2) << "Table reordering picked start nest level " << start << " with cost "
530  << start_edge.join_cost;
531  CHECK_EQ(start, start_edge.nest_level);
532  worklist.push(start_edge);
533  const auto it_ok = visited.insert(start);
534  CHECK(it_ok.second);
535  while (!worklist.empty()) {
536  // Extract a node and add it to the permutation.
537  TraversalEdge crt = worklist.top();
538  worklist.pop();
539  VLOG(1) << "Insert input permutation, idx: " << input_permutation.size()
540  << ", nest_level: " << crt.nest_level;
541  input_permutation.push_back(crt.nest_level);
542  dependency_tracking.removeNode(crt.nest_level);
543  // Add successors which are ready and not visited yet to the queue.
544  for (const auto& graph_edge : join_cost_graph[crt.nest_level]) {
545  const node_t succ = graph_edge.first;
546  if (!schedulable_node(succ)) {
547  continue;
548  }
549  worklist.push(TraversalEdge{succ, graph_edge.second});
550  const auto it_ok = visited.insert(succ);
551  CHECK(it_ok.second);
552  }
553  }
554  }
555  return input_permutation;
556 }
#define CHECK_EQ(x, y)
Definition: Logger.h:301
static bool colvar_comp(const ColumnVar *l, const ColumnVar *r)
Definition: Analyzer.h:215
SchedulingDependencyTracking build_dependency_tracking(const JoinQualsPerNestingLevel &left_deep_join_quals, const std::vector< std::map< node_t, cost_t >> &join_cost_graph)
unsigned g_trivial_loop_join_threshold
Definition: Execute.cpp:92
DEVICE void iota(ARGS &&...args)
Definition: gpu_enabled.h:69
#define CHECK(condition)
Definition: Logger.h:291
AccessManager::Decision Decision
const std::shared_ptr< Analyzer::Expr > get_own_left_operand() const
Definition: Analyzer.h:457
#define VLOG(n)
Definition: Logger.h:388

+ Here is the call graph for this function:

+ Here is the caller graph for this function:

Variable Documentation

std::unordered_map<SQLTypes, cost_t> anonymous_namespace{FromTableReordering.cpp}::GEO_TYPE_COSTS {kPOINT, 60}
static

Definition at line 32 of file FromTableReordering.cpp.

Referenced by get_join_qual_cost().