duckdb 0.7.2-dev904.0 → 0.7.2-dev982.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (63) hide show
  1. package/package.json +1 -1
  2. package/src/duckdb/src/common/enums/logical_operator_type.cpp +2 -0
  3. package/src/duckdb/src/common/serializer/enum_serializer.cpp +4 -0
  4. package/src/duckdb/src/common/types/value.cpp +46 -0
  5. package/src/duckdb/src/execution/column_binding_resolver.cpp +15 -5
  6. package/src/duckdb/src/execution/operator/join/physical_blockwise_nl_join.cpp +40 -19
  7. package/src/duckdb/src/execution/operator/join/physical_range_join.cpp +2 -0
  8. package/src/duckdb/src/execution/operator/persistent/base_csv_reader.cpp +3 -3
  9. package/src/duckdb/src/execution/operator/persistent/buffered_csv_reader.cpp +5 -13
  10. package/src/duckdb/src/execution/operator/projection/physical_projection.cpp +34 -0
  11. package/src/duckdb/src/execution/physical_plan/plan_asof_join.cpp +97 -0
  12. package/src/duckdb/src/execution/physical_plan_generator.cpp +3 -0
  13. package/src/duckdb/src/function/scalar/math/numeric.cpp +87 -0
  14. package/src/duckdb/src/function/scalar/math_functions.cpp +3 -0
  15. package/src/duckdb/src/function/scalar/string/hex.cpp +201 -0
  16. package/src/duckdb/src/function/scalar/string_functions.cpp +1 -0
  17. package/src/duckdb/src/function/table/read_csv.cpp +46 -0
  18. package/src/duckdb/src/function/table/version/pragma_version.cpp +2 -2
  19. package/src/duckdb/src/include/duckdb/common/enums/joinref_type.hpp +5 -4
  20. package/src/duckdb/src/include/duckdb/common/enums/logical_operator_type.hpp +1 -0
  21. package/src/duckdb/src/include/duckdb/common/string_util.hpp +13 -0
  22. package/src/duckdb/src/include/duckdb/common/types/value.hpp +11 -7
  23. package/src/duckdb/src/include/duckdb/common/vector_operations/unary_executor.hpp +2 -2
  24. package/src/duckdb/src/include/duckdb/execution/operator/join/physical_cross_product.hpp +2 -0
  25. package/src/duckdb/src/include/duckdb/execution/operator/persistent/csv_reader_options.hpp +6 -0
  26. package/src/duckdb/src/include/duckdb/execution/operator/projection/physical_projection.hpp +5 -0
  27. package/src/duckdb/src/include/duckdb/execution/physical_plan_generator.hpp +1 -0
  28. package/src/duckdb/src/include/duckdb/function/scalar/math_functions.hpp +8 -0
  29. package/src/duckdb/src/include/duckdb/function/scalar/string_functions.hpp +4 -0
  30. package/src/duckdb/src/include/duckdb/planner/logical_tokens.hpp +1 -0
  31. package/src/duckdb/src/include/duckdb/planner/operator/list.hpp +1 -0
  32. package/src/duckdb/src/include/duckdb/planner/operator/logical_asof_join.hpp +22 -0
  33. package/src/duckdb/src/include/duckdb/planner/operator/logical_comparison_join.hpp +5 -2
  34. package/src/duckdb/src/include/duckdb.h +1 -1
  35. package/src/duckdb/src/optimizer/column_lifetime_analyzer.cpp +1 -0
  36. package/src/duckdb/src/optimizer/filter_pullup.cpp +3 -1
  37. package/src/duckdb/src/optimizer/filter_pushdown.cpp +3 -1
  38. package/src/duckdb/src/optimizer/join_order/cardinality_estimator.cpp +4 -0
  39. package/src/duckdb/src/optimizer/join_order/join_order_optimizer.cpp +8 -4
  40. package/src/duckdb/src/optimizer/pullup/pullup_from_left.cpp +2 -2
  41. package/src/duckdb/src/optimizer/pushdown/pushdown_cross_product.cpp +1 -1
  42. package/src/duckdb/src/optimizer/pushdown/pushdown_inner_join.cpp +3 -0
  43. package/src/duckdb/src/optimizer/pushdown/pushdown_left_join.cpp +4 -2
  44. package/src/duckdb/src/optimizer/pushdown/pushdown_mark_join.cpp +1 -1
  45. package/src/duckdb/src/optimizer/remove_unused_columns.cpp +1 -0
  46. package/src/duckdb/src/optimizer/statistics/operator/propagate_join.cpp +1 -0
  47. package/src/duckdb/src/optimizer/statistics_propagator.cpp +1 -0
  48. package/src/duckdb/src/parser/tableref/joinref.cpp +4 -0
  49. package/src/duckdb/src/parser/transform/tableref/transform_join.cpp +8 -1
  50. package/src/duckdb/src/planner/binder/tableref/bind_joinref.cpp +10 -3
  51. package/src/duckdb/src/planner/binder/tableref/plan_joinref.cpp +60 -12
  52. package/src/duckdb/src/planner/logical_operator.cpp +3 -0
  53. package/src/duckdb/src/planner/logical_operator_visitor.cpp +1 -0
  54. package/src/duckdb/src/planner/operator/logical_asof_join.cpp +8 -0
  55. package/src/duckdb/src/planner/subquery/flatten_dependent_join.cpp +3 -1
  56. package/src/duckdb/third_party/libpg_query/include/nodes/nodes.hpp +32 -0
  57. package/src/duckdb/third_party/libpg_query/include/nodes/primnodes.hpp +3 -3
  58. package/src/duckdb/third_party/libpg_query/include/parser/gram.hpp +915 -913
  59. package/src/duckdb/third_party/libpg_query/include/parser/kwlist.hpp +1 -0
  60. package/src/duckdb/third_party/libpg_query/src_backend_parser_gram.cpp +17371 -17306
  61. package/src/duckdb/ub_src_execution_physical_plan.cpp +2 -0
  62. package/src/duckdb/ub_src_function_scalar_string.cpp +2 -0
  63. package/src/duckdb/ub_src_planner_operator.cpp +2 -0
@@ -113,7 +113,7 @@ bool JoinOrderOptimizer::ExtractJoinRelations(LogicalOperator &input_op, vector<
113
113
  bool non_reorderable_operation = false;
114
114
  if (op->type == LogicalOperatorType::LOGICAL_UNION || op->type == LogicalOperatorType::LOGICAL_EXCEPT ||
115
115
  op->type == LogicalOperatorType::LOGICAL_INTERSECT || op->type == LogicalOperatorType::LOGICAL_DELIM_JOIN ||
116
- op->type == LogicalOperatorType::LOGICAL_ANY_JOIN) {
116
+ op->type == LogicalOperatorType::LOGICAL_ANY_JOIN || op->type == LogicalOperatorType::LOGICAL_ASOF_JOIN) {
117
117
  // set operation, optimize separately in children
118
118
  non_reorderable_operation = true;
119
119
  }
@@ -184,6 +184,7 @@ bool JoinOrderOptimizer::ExtractJoinRelations(LogicalOperator &input_op, vector<
184
184
  }
185
185
 
186
186
  switch (op->type) {
187
+ case LogicalOperatorType::LOGICAL_ASOF_JOIN:
187
188
  case LogicalOperatorType::LOGICAL_COMPARISON_JOIN:
188
189
  case LogicalOperatorType::LOGICAL_CROSS_PRODUCT: {
189
190
  // inner join or cross product
@@ -866,7 +867,8 @@ JoinOrderOptimizer::GenerateJoins(vector<unique_ptr<LogicalOperator>> &extracted
866
867
  result_operator->children[0] = std::move(comp_join);
867
868
  }
868
869
  } else {
869
- D_ASSERT(node->type == LogicalOperatorType::LOGICAL_COMPARISON_JOIN);
870
+ D_ASSERT(node->type == LogicalOperatorType::LOGICAL_COMPARISON_JOIN ||
871
+ node->type == LogicalOperatorType::LOGICAL_ASOF_JOIN);
870
872
  auto &comp_join = (LogicalComparisonJoin &)*node;
871
873
  comp_join.conditions.push_back(std::move(cond));
872
874
  }
@@ -908,7 +910,8 @@ unique_ptr<LogicalOperator> JoinOrderOptimizer::RewritePlan(unique_ptr<LogicalOp
908
910
  auto op = plan.get();
909
911
  auto parent = plan.get();
910
912
  while (op->type != LogicalOperatorType::LOGICAL_CROSS_PRODUCT &&
911
- op->type != LogicalOperatorType::LOGICAL_COMPARISON_JOIN) {
913
+ op->type != LogicalOperatorType::LOGICAL_COMPARISON_JOIN &&
914
+ op->type != LogicalOperatorType::LOGICAL_ASOF_JOIN) {
912
915
  D_ASSERT(op->children.size() == 1);
913
916
  parent = op;
914
917
  op = op->children[0].get();
@@ -943,7 +946,8 @@ unique_ptr<LogicalOperator> JoinOrderOptimizer::Optimize(unique_ptr<LogicalOpera
943
946
  // filters in the process
944
947
  expression_set_t filter_set;
945
948
  for (auto &f_op : filter_operators) {
946
- if (f_op->type == LogicalOperatorType::LOGICAL_COMPARISON_JOIN) {
949
+ if (f_op->type == LogicalOperatorType::LOGICAL_COMPARISON_JOIN ||
950
+ f_op->type == LogicalOperatorType::LOGICAL_ASOF_JOIN) {
947
951
  auto &join = (LogicalComparisonJoin &)*f_op;
948
952
  D_ASSERT(join.join_type == JoinType::INNER);
949
953
  D_ASSERT(join.expressions.empty());
@@ -6,8 +6,8 @@ namespace duckdb {
6
6
 
7
7
  unique_ptr<LogicalOperator> FilterPullup::PullupFromLeft(unique_ptr<LogicalOperator> op) {
8
8
  D_ASSERT(op->type == LogicalOperatorType::LOGICAL_COMPARISON_JOIN ||
9
- op->type == LogicalOperatorType::LOGICAL_ANY_JOIN || op->type == LogicalOperatorType::LOGICAL_EXCEPT ||
10
- op->type == LogicalOperatorType::LOGICAL_DELIM_JOIN);
9
+ op->type == LogicalOperatorType::LOGICAL_ASOF_JOIN || op->type == LogicalOperatorType::LOGICAL_ANY_JOIN ||
10
+ op->type == LogicalOperatorType::LOGICAL_EXCEPT || op->type == LogicalOperatorType::LOGICAL_DELIM_JOIN);
11
11
 
12
12
  FilterPullup left_pullup(true, can_add_column);
13
13
  FilterPullup right_pullup(false, can_add_column);
@@ -46,7 +46,7 @@ unique_ptr<LogicalOperator> FilterPushdown::PushdownCrossProduct(unique_ptr<Logi
46
46
  right_bindings, join_expressions, conditions,
47
47
  arbitrary_expressions);
48
48
  // create the join from the join conditions
49
- return LogicalComparisonJoin::CreateJoin(JoinType::INNER, std::move(op->children[0]),
49
+ return LogicalComparisonJoin::CreateJoin(JoinType::INNER, JoinRefType::REGULAR, std::move(op->children[0]),
50
50
  std::move(op->children[1]), std::move(conditions),
51
51
  std::move(arbitrary_expressions));
52
52
  } else {
@@ -24,6 +24,9 @@ unique_ptr<LogicalOperator> FilterPushdown::PushdownInnerJoin(unique_ptr<Logical
24
24
  // filter statically evaluates to false, strip tree
25
25
  return make_unique<LogicalEmptyResult>(std::move(op));
26
26
  }
27
+ } else if (op->type == LogicalOperatorType::LOGICAL_ASOF_JOIN) {
28
+ // Don't mess with non-standard condition interpretations
29
+ return FinishPushdown(std::move(op));
27
30
  } else {
28
31
  // comparison join
29
32
  D_ASSERT(op->type == LogicalOperatorType::LOGICAL_COMPARISON_JOIN);
@@ -68,7 +68,9 @@ unique_ptr<LogicalOperator> FilterPushdown::PushdownLeftJoin(unique_ptr<LogicalO
68
68
  // for a comparison join we create a FilterCombiner that checks if we can push conditions on LHS join conditions
69
69
  // into the RHS of the join
70
70
  FilterCombiner filter_combiner(optimizer);
71
- if (op->type == LogicalOperatorType::LOGICAL_COMPARISON_JOIN) {
71
+ const auto isComparison = (op->type == LogicalOperatorType::LOGICAL_COMPARISON_JOIN ||
72
+ op->type == LogicalOperatorType::LOGICAL_ASOF_JOIN);
73
+ if (isComparison) {
72
74
  // add all comparison conditions
73
75
  auto &comparison_join = (LogicalComparisonJoin &)*op;
74
76
  for (auto &cond : comparison_join.conditions) {
@@ -82,7 +84,7 @@ unique_ptr<LogicalOperator> FilterPushdown::PushdownLeftJoin(unique_ptr<LogicalO
82
84
  if (side == JoinSide::LEFT) {
83
85
  // bindings match left side
84
86
  // we can push the filter into the left side
85
- if (op->type == LogicalOperatorType::LOGICAL_COMPARISON_JOIN) {
87
+ if (isComparison) {
86
88
  // we MIGHT be able to push it down the RHS as well, but only if it is a comparison that matches the
87
89
  // join predicates we use the FilterCombiner to figure this out add the expression to the FilterCombiner
88
90
  filter_combiner.AddFilter(filters[i]->filter->Copy());
@@ -13,7 +13,7 @@ unique_ptr<LogicalOperator> FilterPushdown::PushdownMarkJoin(unique_ptr<LogicalO
13
13
  auto &comp_join = (LogicalComparisonJoin &)*op;
14
14
  D_ASSERT(join.join_type == JoinType::MARK);
15
15
  D_ASSERT(op->type == LogicalOperatorType::LOGICAL_COMPARISON_JOIN ||
16
- op->type == LogicalOperatorType::LOGICAL_DELIM_JOIN);
16
+ op->type == LogicalOperatorType::LOGICAL_DELIM_JOIN || op->type == LogicalOperatorType::LOGICAL_ASOF_JOIN);
17
17
 
18
18
  right_bindings.insert(comp_join.mark_index);
19
19
  FilterPushdown left_pushdown(optimizer), right_pushdown(optimizer);
@@ -71,6 +71,7 @@ void RemoveUnusedColumns::VisitOperator(LogicalOperator &op) {
71
71
  remove.VisitOperator(*op.children[0]);
72
72
  return;
73
73
  }
74
+ case LogicalOperatorType::LOGICAL_ASOF_JOIN:
74
75
  case LogicalOperatorType::LOGICAL_DELIM_JOIN:
75
76
  case LogicalOperatorType::LOGICAL_COMPARISON_JOIN: {
76
77
  if (!everything_referenced) {
@@ -195,6 +195,7 @@ unique_ptr<NodeStatistics> StatisticsPropagator::PropagateStatistics(LogicalJoin
195
195
  switch (join.type) {
196
196
  case LogicalOperatorType::LOGICAL_COMPARISON_JOIN:
197
197
  case LogicalOperatorType::LOGICAL_DELIM_JOIN:
198
+ case LogicalOperatorType::LOGICAL_ASOF_JOIN:
198
199
  PropagateStatistics((LogicalComparisonJoin &)join, node_ptr);
199
200
  break;
200
201
  case LogicalOperatorType::LOGICAL_ANY_JOIN:
@@ -36,6 +36,7 @@ unique_ptr<NodeStatistics> StatisticsPropagator::PropagateStatistics(LogicalOper
36
36
  case LogicalOperatorType::LOGICAL_PROJECTION:
37
37
  return PropagateStatistics((LogicalProjection &)node, node_ptr);
38
38
  case LogicalOperatorType::LOGICAL_ANY_JOIN:
39
+ case LogicalOperatorType::LOGICAL_ASOF_JOIN:
39
40
  case LogicalOperatorType::LOGICAL_COMPARISON_JOIN:
40
41
  case LogicalOperatorType::LOGICAL_JOIN:
41
42
  case LogicalOperatorType::LOGICAL_DELIM_JOIN:
@@ -18,6 +18,10 @@ string JoinRef::ToString() const {
18
18
  result += "NATURAL ";
19
19
  result += JoinTypeToString(type) + " JOIN ";
20
20
  break;
21
+ case JoinRefType::ASOF:
22
+ result += "ASOF ";
23
+ result += JoinTypeToString(type) + " JOIN ";
24
+ break;
21
25
  case JoinRefType::CROSS:
22
26
  result += ", ";
23
27
  break;
@@ -44,8 +44,15 @@ unique_ptr<TableRef> Transformer::TransformJoin(duckdb_libpgquery::PGJoinExpr *r
44
44
  // Check the type of left arg and right arg before transform
45
45
  result->left = TransformTableRefNode(root->larg);
46
46
  result->right = TransformTableRefNode(root->rarg);
47
- if (root->isNatural) {
47
+ switch (root->joinreftype) {
48
+ case duckdb_libpgquery::PG_JOIN_NATURAL:
48
49
  result->ref_type = JoinRefType::NATURAL;
50
+ break;
51
+ case duckdb_libpgquery::PG_JOIN_ASOF:
52
+ result->ref_type = JoinRefType::ASOF;
53
+ break;
54
+ default:
55
+ break;
49
56
  }
50
57
  result->query_location = root->location;
51
58
 
@@ -25,11 +25,11 @@ static unique_ptr<ParsedExpression> BindColumn(Binder &binder, ClientContext &co
25
25
 
26
26
  static unique_ptr<ParsedExpression> AddCondition(ClientContext &context, Binder &left_binder, Binder &right_binder,
27
27
  const string &left_alias, const string &right_alias,
28
- const string &column_name) {
28
+ const string &column_name, ExpressionType type) {
29
29
  ExpressionBinder expr_binder(left_binder, context);
30
30
  auto left = BindColumn(left_binder, context, left_alias, column_name);
31
31
  auto right = BindColumn(right_binder, context, right_alias, column_name);
32
- return make_unique<ComparisonExpression>(ExpressionType::COMPARE_EQUAL, std::move(left), std::move(right));
32
+ return make_unique<ComparisonExpression>(type, std::move(left), std::move(right));
33
33
  }
34
34
 
35
35
  bool Binder::TryFindBinding(const string &using_column, const string &join_side, string &result) {
@@ -198,12 +198,14 @@ unique_ptr<BoundTableRef> Binder::Bind(JoinRef &ref) {
198
198
  break;
199
199
  }
200
200
  case JoinRefType::REGULAR:
201
+ case JoinRefType::ASOF:
201
202
  if (!ref.using_columns.empty()) {
202
203
  // USING columns
203
204
  D_ASSERT(!result->condition);
204
205
  extra_using_columns = ref.using_columns;
205
206
  }
206
207
  break;
208
+
207
209
  case JoinRefType::CROSS:
208
210
  case JoinRefType::POSITIONAL:
209
211
  break;
@@ -241,8 +243,13 @@ unique_ptr<BoundTableRef> Binder::Bind(JoinRef &ref) {
241
243
  left_binding = RetrieveUsingBinding(left_binder, left_using_binding, using_column, "left", set.get());
242
244
  right_binding = RetrieveUsingBinding(right_binder, right_using_binding, using_column, "right", set.get());
243
245
 
246
+ // Last column of ASOF JOIN ... USING is >=
247
+ const auto type = (ref.ref_type == JoinRefType::ASOF && i == extra_using_columns.size() - 1)
248
+ ? ExpressionType::COMPARE_GREATERTHANOREQUALTO
249
+ : ExpressionType::COMPARE_EQUAL;
250
+
244
251
  extra_conditions.push_back(
245
- AddCondition(context, left_binder, right_binder, left_binding, right_binding, using_column));
252
+ AddCondition(context, left_binder, right_binder, left_binding, right_binding, using_column, type));
246
253
 
247
254
  AddUsingBindings(*set, left_using_binding, left_binding);
248
255
  AddUsingBindings(*set, right_using_binding, right_binding);
@@ -7,6 +7,7 @@
7
7
  #include "duckdb/planner/expression_iterator.hpp"
8
8
  #include "duckdb/planner/binder.hpp"
9
9
  #include "duckdb/planner/operator/logical_any_join.hpp"
10
+ #include "duckdb/planner/operator/logical_asof_join.hpp"
10
11
  #include "duckdb/planner/operator/logical_comparison_join.hpp"
11
12
  #include "duckdb/planner/operator/logical_cross_product.hpp"
12
13
  #include "duckdb/planner/operator/logical_filter.hpp"
@@ -104,12 +105,43 @@ void LogicalComparisonJoin::ExtractJoinConditions(JoinType type, unique_ptr<Logi
104
105
  return ExtractJoinConditions(type, left_child, right_child, expressions, conditions, arbitrary_expressions);
105
106
  }
106
107
 
107
- unique_ptr<LogicalOperator> LogicalComparisonJoin::CreateJoin(JoinType type, unique_ptr<LogicalOperator> left_child,
108
+ unique_ptr<LogicalOperator> LogicalComparisonJoin::CreateJoin(JoinType type, JoinRefType reftype,
109
+ unique_ptr<LogicalOperator> left_child,
108
110
  unique_ptr<LogicalOperator> right_child,
109
111
  vector<JoinCondition> conditions,
110
112
  vector<unique_ptr<Expression>> arbitrary_expressions) {
113
+ // Validate the conditions
111
114
  bool need_to_consider_arbitrary_expressions = true;
112
- if (type == JoinType::INNER) {
115
+ switch (reftype) {
116
+ case JoinRefType::ASOF: {
117
+ need_to_consider_arbitrary_expressions = false;
118
+ auto asof_idx = conditions.size();
119
+ for (size_t c = 0; c < conditions.size(); ++c) {
120
+ auto &cond = conditions[c];
121
+ switch (cond.comparison) {
122
+ case ExpressionType::COMPARE_EQUAL:
123
+ case ExpressionType::COMPARE_NOT_DISTINCT_FROM:
124
+ break;
125
+ case ExpressionType::COMPARE_GREATERTHANOREQUALTO:
126
+ if (asof_idx < conditions.size()) {
127
+ throw BinderException("Multiple ASOF JOIN inequalities");
128
+ }
129
+ asof_idx = c;
130
+ break;
131
+ default:
132
+ throw BinderException("Invalid ASOF JOIN comparison");
133
+ }
134
+ }
135
+ if (asof_idx == conditions.size()) {
136
+ throw BinderException("Missing ASOF JOIN inequality");
137
+ }
138
+ break;
139
+ }
140
+ default:
141
+ break;
142
+ }
143
+
144
+ if (type == JoinType::INNER && reftype == JoinRefType::REGULAR) {
113
145
  // for inner joins we can push arbitrary expressions as a filter
114
146
  // here we prefer to create a comparison join if possible
115
147
  // that way we can use the much faster hash join to process the main join
@@ -144,7 +176,12 @@ unique_ptr<LogicalOperator> LogicalComparisonJoin::CreateJoin(JoinType type, uni
144
176
  } else {
145
177
  // we successfully converted expressions into JoinConditions
146
178
  // create a LogicalComparisonJoin
147
- auto comp_join = make_unique<LogicalComparisonJoin>(type);
179
+ unique_ptr<LogicalComparisonJoin> comp_join;
180
+ if (reftype == JoinRefType::ASOF) {
181
+ comp_join = make_unique<LogicalAsOfJoin>(type);
182
+ } else {
183
+ comp_join = make_unique<LogicalComparisonJoin>(type);
184
+ }
148
185
  comp_join->conditions = std::move(conditions);
149
186
  comp_join->children.push_back(std::move(left_child));
150
187
  comp_join->children.push_back(std::move(right_child));
@@ -179,15 +216,16 @@ static bool HasCorrelatedColumns(Expression &expression) {
179
216
  return has_correlated_columns;
180
217
  }
181
218
 
182
- unique_ptr<LogicalOperator> LogicalComparisonJoin::CreateJoin(JoinType type, unique_ptr<LogicalOperator> left_child,
219
+ unique_ptr<LogicalOperator> LogicalComparisonJoin::CreateJoin(JoinType type, JoinRefType reftype,
220
+ unique_ptr<LogicalOperator> left_child,
183
221
  unique_ptr<LogicalOperator> right_child,
184
222
  unique_ptr<Expression> condition) {
185
223
  vector<JoinCondition> conditions;
186
224
  vector<unique_ptr<Expression>> arbitrary_expressions;
187
225
  LogicalComparisonJoin::ExtractJoinConditions(type, left_child, right_child, std::move(condition), conditions,
188
226
  arbitrary_expressions);
189
- return LogicalComparisonJoin::CreateJoin(type, std::move(left_child), std::move(right_child), std::move(conditions),
190
- std::move(arbitrary_expressions));
227
+ return LogicalComparisonJoin::CreateJoin(type, reftype, std::move(left_child), std::move(right_child),
228
+ std::move(conditions), std::move(arbitrary_expressions));
191
229
  }
192
230
 
193
231
  unique_ptr<LogicalOperator> Binder::CreatePlan(BoundJoinRef &ref) {
@@ -201,7 +239,8 @@ unique_ptr<LogicalOperator> Binder::CreatePlan(BoundJoinRef &ref) {
201
239
  // we reduce expression depth of all columns in the "ref.correlated_columns" set by 1
202
240
  LateralBinder::ReduceExpressionDepth(*right, ref.correlated_columns);
203
241
  }
204
- if (ref.type == JoinType::RIGHT && ClientConfig::GetConfig(context).enable_optimizer) {
242
+ if (ref.type == JoinType::RIGHT && ref.ref_type != JoinRefType::ASOF &&
243
+ ClientConfig::GetConfig(context).enable_optimizer) {
205
244
  // we turn any right outer joins into left outer joins for optimization purposes
206
245
  // they are the same but with sides flipped, so treating them the same simplifies life
207
246
  ref.type = JoinType::LEFT;
@@ -220,7 +259,8 @@ unique_ptr<LogicalOperator> Binder::CreatePlan(BoundJoinRef &ref) {
220
259
  default:
221
260
  break;
222
261
  }
223
- if (ref.type == JoinType::INNER && (ref.condition->HasSubquery() || HasCorrelatedColumns(*ref.condition))) {
262
+ if (ref.type == JoinType::INNER && (ref.condition->HasSubquery() || HasCorrelatedColumns(*ref.condition)) &&
263
+ ref.ref_type == JoinRefType::REGULAR) {
224
264
  // inner join, generate a cross product + filter
225
265
  // this will be later turned into a proper join by the join order optimizer
226
266
  auto root = LogicalCrossProduct::Create(std::move(left), std::move(right));
@@ -235,8 +275,8 @@ unique_ptr<LogicalOperator> Binder::CreatePlan(BoundJoinRef &ref) {
235
275
  }
236
276
 
237
277
  // now create the join operator from the join condition
238
- auto result =
239
- LogicalComparisonJoin::CreateJoin(ref.type, std::move(left), std::move(right), std::move(ref.condition));
278
+ auto result = LogicalComparisonJoin::CreateJoin(ref.type, ref.ref_type, std::move(left), std::move(right),
279
+ std::move(ref.condition));
240
280
 
241
281
  LogicalOperator *join;
242
282
  if (result->type == LogicalOperatorType::LOGICAL_FILTER) {
@@ -254,7 +294,9 @@ unique_ptr<LogicalOperator> Binder::CreatePlan(BoundJoinRef &ref) {
254
294
  }
255
295
 
256
296
  // we visit the expressions depending on the type of join
257
- if (join->type == LogicalOperatorType::LOGICAL_COMPARISON_JOIN) {
297
+ switch (join->type) {
298
+ case LogicalOperatorType::LOGICAL_ASOF_JOIN:
299
+ case LogicalOperatorType::LOGICAL_COMPARISON_JOIN: {
258
300
  // comparison join
259
301
  // in this join we visit the expressions on the LHS with the LHS as root node
260
302
  // and the expressions on the RHS with the RHS as root node
@@ -263,12 +305,18 @@ unique_ptr<LogicalOperator> Binder::CreatePlan(BoundJoinRef &ref) {
263
305
  PlanSubqueries(&comp_join.conditions[i].left, &comp_join.children[0]);
264
306
  PlanSubqueries(&comp_join.conditions[i].right, &comp_join.children[1]);
265
307
  }
266
- } else if (join->type == LogicalOperatorType::LOGICAL_ANY_JOIN) {
308
+ break;
309
+ }
310
+ case LogicalOperatorType::LOGICAL_ANY_JOIN: {
267
311
  auto &any_join = (LogicalAnyJoin &)*join;
268
312
  // for the any join we just visit the condition
269
313
  if (any_join.condition->HasSubquery()) {
270
314
  throw NotImplementedException("Cannot perform non-inner join on subquery!");
271
315
  }
316
+ break;
317
+ }
318
+ default:
319
+ break;
272
320
  }
273
321
  return result;
274
322
  }
@@ -257,6 +257,9 @@ unique_ptr<LogicalOperator> LogicalOperator::Deserialize(Deserializer &deseriali
257
257
  case LogicalOperatorType::LOGICAL_DELIM_JOIN:
258
258
  result = LogicalDelimJoin::Deserialize(state, reader);
259
259
  break;
260
+ case LogicalOperatorType::LOGICAL_ASOF_JOIN:
261
+ result = LogicalAsOfJoin::Deserialize(state, reader);
262
+ break;
260
263
  case LogicalOperatorType::LOGICAL_COMPARISON_JOIN:
261
264
  result = LogicalComparisonJoin::Deserialize(state, reader);
262
265
  break;
@@ -65,6 +65,7 @@ void LogicalOperatorVisitor::EnumerateExpressions(LogicalOperator &op,
65
65
  }
66
66
  break;
67
67
  }
68
+ case LogicalOperatorType::LOGICAL_ASOF_JOIN:
68
69
  case LogicalOperatorType::LOGICAL_DELIM_JOIN:
69
70
  case LogicalOperatorType::LOGICAL_COMPARISON_JOIN: {
70
71
  if (op.type == LogicalOperatorType::LOGICAL_DELIM_JOIN) {
@@ -0,0 +1,8 @@
1
+ #include "duckdb/planner/operator/logical_asof_join.hpp"
2
+
3
+ namespace duckdb {
4
+
5
+ LogicalAsOfJoin::LogicalAsOfJoin(JoinType type) : LogicalComparisonJoin(type, LogicalOperatorType::LOGICAL_ASOF_JOIN) {
6
+ }
7
+
8
+ } // namespace duckdb
@@ -262,6 +262,7 @@ unique_ptr<LogicalOperator> FlattenDependentJoins::PushDownDependentJoinInternal
262
262
  return std::move(join);
263
263
  }
264
264
  case LogicalOperatorType::LOGICAL_ANY_JOIN:
265
+ case LogicalOperatorType::LOGICAL_ASOF_JOIN:
265
266
  case LogicalOperatorType::LOGICAL_COMPARISON_JOIN: {
266
267
  auto &join = (LogicalJoin &)*plan;
267
268
  D_ASSERT(plan->children.size() == 2);
@@ -334,7 +335,8 @@ unique_ptr<LogicalOperator> FlattenDependentJoins::PushDownDependentJoinInternal
334
335
  auto right = make_unique<BoundColumnRefExpression>(
335
336
  correlated_columns[i].type, ColumnBinding(right_binding.table_index, right_binding.column_index + i));
336
337
 
337
- if (join.type == LogicalOperatorType::LOGICAL_COMPARISON_JOIN) {
338
+ if (join.type == LogicalOperatorType::LOGICAL_COMPARISON_JOIN ||
339
+ join.type == LogicalOperatorType::LOGICAL_ASOF_JOIN) {
338
340
  JoinCondition cond;
339
341
  cond.left = std::move(left);
340
342
  cond.right = std::move(right);
@@ -681,6 +681,38 @@ typedef enum PGJoinType {
681
681
  */
682
682
  } PGJoinType;
683
683
 
684
+ /*
685
+ * PGJoinRefType -
686
+ * enums for the types of implied conditions
687
+ *
688
+ * PGJoinRefType specifies the semantics of interpreting the join conditions.
689
+ * These can be explicit (e.g., REGULAR) implied (e.g., NATURAL)
690
+ * or interpreted in a particular manner (e.g., ASOF)
691
+ *
692
+ * This is a generalisation of the old Postgres isNatural flag.
693
+ */
694
+ typedef enum PGJoinRefType {
695
+ PG_JOIN_REGULAR, /* Join conditions are interpreted as is */
696
+ PG_JOIN_NATURAL, /* Join conditions are inferred from the column names */
697
+
698
+ /*
699
+ * ASOF joins are joins with a single inequality predicate
700
+ * and optional equality predicates.
701
+ * The semantics are equivalent to the following window join:
702
+ * times t
703
+ * <jointype> JOIN (
704
+ * SELECT *,
705
+ * LEAD(begin, 1, 'infinity') OVER ([PARTITION BY key] ORDER BY begin) AS end)
706
+ * FROM events) e
707
+ * ON t.ts >= e.begin AND t.ts < e.end [AND t.key = e.key]
708
+ */
709
+ PG_JOIN_ASOF
710
+
711
+ /*
712
+ * Positional join is a candidate to move here
713
+ */
714
+ } PGJoinRefType;
715
+
684
716
  /*
685
717
  * OUTER joins are those for which pushed-down quals must behave differently
686
718
  * from the join's own quals. This is in fact everything except INNER and
@@ -1324,7 +1324,7 @@ typedef struct PGRangeTblRef {
1324
1324
  /*----------
1325
1325
  * PGJoinExpr - for SQL JOIN expressions
1326
1326
  *
1327
- * isNatural, usingClause, and quals are interdependent. The user can write
1327
+ * joinreftype, usingClause, and quals are interdependent. The user can write
1328
1328
  * only one of NATURAL, USING(), or ON() (this is enforced by the grammar).
1329
1329
  * If he writes NATURAL then parse analysis generates the equivalent USING()
1330
1330
  * list, and from that fills in "quals" with the right equality comparisons.
@@ -1347,7 +1347,7 @@ typedef struct PGRangeTblRef {
1347
1347
  typedef struct PGJoinExpr {
1348
1348
  PGNodeTag type;
1349
1349
  PGJoinType jointype; /* type of join */
1350
- bool isNatural; /* Natural join? Will need to shape table */
1350
+ PGJoinRefType joinreftype; /* Regular/Natural/AsOf join? Will need to shape table */
1351
1351
  PGNode *larg; /* left subtree */
1352
1352
  PGNode *rarg; /* right subtree */
1353
1353
  PGList *usingClause; /* USING clause, if any (list of String) */
@@ -1398,4 +1398,4 @@ typedef struct PGOnConflictExpr {
1398
1398
  PGList *exclRelTlist; /* tlist of the EXCLUDED pseudo relation */
1399
1399
  } PGOnConflictExpr;
1400
1400
 
1401
- }
1401
+ }