Coverage Report

Created: 2022-08-24 06:40

/src/duckdb/src/parser/transform/expression/transform_function.cpp
Line
Count
Source (jump to first uncovered line)
1
#include "duckdb/common/string_util.hpp"
2
#include "duckdb/common/to_string.hpp"
3
#include "duckdb/parser/expression/case_expression.hpp"
4
#include "duckdb/parser/expression/cast_expression.hpp"
5
#include "duckdb/parser/expression/function_expression.hpp"
6
7
#include "duckdb/parser/expression/operator_expression.hpp"
8
#include "duckdb/parser/expression/star_expression.hpp"
9
#include "duckdb/parser/expression/window_expression.hpp"
10
#include "duckdb/parser/transformer.hpp"
11
12
namespace duckdb {
13
14
0
static ExpressionType WindowToExpressionType(string &fun_name) {
15
0
  if (fun_name == "rank") {
16
0
    return ExpressionType::WINDOW_RANK;
17
0
  } else if (fun_name == "rank_dense" || fun_name == "dense_rank") {
18
0
    return ExpressionType::WINDOW_RANK_DENSE;
19
0
  } else if (fun_name == "percent_rank") {
20
0
    return ExpressionType::WINDOW_PERCENT_RANK;
21
0
  } else if (fun_name == "row_number") {
22
0
    return ExpressionType::WINDOW_ROW_NUMBER;
23
0
  } else if (fun_name == "first_value" || fun_name == "first") {
24
0
    return ExpressionType::WINDOW_FIRST_VALUE;
25
0
  } else if (fun_name == "last_value" || fun_name == "last") {
26
0
    return ExpressionType::WINDOW_LAST_VALUE;
27
0
  } else if (fun_name == "nth_value" || fun_name == "last") {
28
0
    return ExpressionType::WINDOW_NTH_VALUE;
29
0
  } else if (fun_name == "cume_dist") {
30
0
    return ExpressionType::WINDOW_CUME_DIST;
31
0
  } else if (fun_name == "lead") {
32
0
    return ExpressionType::WINDOW_LEAD;
33
0
  } else if (fun_name == "lag") {
34
0
    return ExpressionType::WINDOW_LAG;
35
0
  } else if (fun_name == "ntile") {
36
0
    return ExpressionType::WINDOW_NTILE;
37
0
  }
38
39
0
  return ExpressionType::WINDOW_AGGREGATE;
40
0
}
41
42
0
void Transformer::TransformWindowDef(duckdb_libpgquery::PGWindowDef *window_spec, WindowExpression *expr) {
43
0
  D_ASSERT(window_spec);
44
0
  D_ASSERT(expr);
45
46
  // next: partitioning/ordering expressions
47
0
  if (window_spec->partitionClause) {
48
0
    TransformExpressionList(*window_spec->partitionClause, expr->partitions);
49
0
  }
50
0
  TransformOrderBy(window_spec->orderClause, expr->orders);
51
0
}
52
53
0
void Transformer::TransformWindowFrame(duckdb_libpgquery::PGWindowDef *window_spec, WindowExpression *expr) {
54
0
  D_ASSERT(window_spec);
55
0
  D_ASSERT(expr);
56
57
  // finally: specifics of bounds
58
0
  expr->start_expr = TransformExpression(window_spec->startOffset);
59
0
  expr->end_expr = TransformExpression(window_spec->endOffset);
60
61
0
  if ((window_spec->frameOptions & FRAMEOPTION_END_UNBOUNDED_PRECEDING) ||
62
0
      (window_spec->frameOptions & FRAMEOPTION_START_UNBOUNDED_FOLLOWING)) {
63
0
    throw InternalException(
64
0
        "Window frames starting with unbounded following or ending in unbounded preceding make no sense");
65
0
  }
66
67
0
  const bool rangeMode = (window_spec->frameOptions & FRAMEOPTION_RANGE) != 0;
68
0
  if (window_spec->frameOptions & FRAMEOPTION_START_UNBOUNDED_PRECEDING) {
69
0
    expr->start = WindowBoundary::UNBOUNDED_PRECEDING;
70
0
  } else if (window_spec->frameOptions & FRAMEOPTION_START_VALUE_PRECEDING) {
71
0
    expr->start = rangeMode ? WindowBoundary::EXPR_PRECEDING_RANGE : WindowBoundary::EXPR_PRECEDING_ROWS;
72
0
  } else if (window_spec->frameOptions & FRAMEOPTION_START_VALUE_FOLLOWING) {
73
0
    expr->start = rangeMode ? WindowBoundary::EXPR_FOLLOWING_RANGE : WindowBoundary::EXPR_FOLLOWING_ROWS;
74
0
  } else if (window_spec->frameOptions & FRAMEOPTION_START_CURRENT_ROW) {
75
0
    expr->start = rangeMode ? WindowBoundary::CURRENT_ROW_RANGE : WindowBoundary::CURRENT_ROW_ROWS;
76
0
  }
77
78
0
  if (window_spec->frameOptions & FRAMEOPTION_END_UNBOUNDED_FOLLOWING) {
79
0
    expr->end = WindowBoundary::UNBOUNDED_FOLLOWING;
80
0
  } else if (window_spec->frameOptions & FRAMEOPTION_END_VALUE_PRECEDING) {
81
0
    expr->end = rangeMode ? WindowBoundary::EXPR_PRECEDING_RANGE : WindowBoundary::EXPR_PRECEDING_ROWS;
82
0
  } else if (window_spec->frameOptions & FRAMEOPTION_END_VALUE_FOLLOWING) {
83
0
    expr->end = rangeMode ? WindowBoundary::EXPR_FOLLOWING_RANGE : WindowBoundary::EXPR_FOLLOWING_ROWS;
84
0
  } else if (window_spec->frameOptions & FRAMEOPTION_END_CURRENT_ROW) {
85
0
    expr->end = rangeMode ? WindowBoundary::CURRENT_ROW_RANGE : WindowBoundary::CURRENT_ROW_ROWS;
86
0
  }
87
88
0
  D_ASSERT(expr->start != WindowBoundary::INVALID && expr->end != WindowBoundary::INVALID);
89
0
  if (((window_spec->frameOptions & (FRAMEOPTION_START_VALUE_PRECEDING | FRAMEOPTION_START_VALUE_FOLLOWING)) &&
90
0
       !expr->start_expr) ||
91
0
      ((window_spec->frameOptions & (FRAMEOPTION_END_VALUE_PRECEDING | FRAMEOPTION_END_VALUE_FOLLOWING)) &&
92
0
       !expr->end_expr)) {
93
0
    throw InternalException("Failed to transform window boundary expression");
94
0
  }
95
0
}
96
97
104
unique_ptr<ParsedExpression> Transformer::TransformFuncCall(duckdb_libpgquery::PGFuncCall *root) {
98
104
  auto name = root->funcname;
99
104
  string schema, function_name;
100
104
  if (name->length == 2) {
101
    // schema + name
102
0
    schema = reinterpret_cast<duckdb_libpgquery::PGValue *>(name->head->data.ptr_value)->val.str;
103
0
    function_name = reinterpret_cast<duckdb_libpgquery::PGValue *>(name->head->next->data.ptr_value)->val.str;
104
104
  } else {
105
    // unqualified name
106
104
    schema = INVALID_SCHEMA;
107
104
    function_name = reinterpret_cast<duckdb_libpgquery::PGValue *>(name->head->data.ptr_value)->val.str;
108
104
  }
109
110
104
  auto lowercase_name = StringUtil::Lower(function_name);
111
112
104
  if (root->over) {
113
0
    const auto win_fun_type = WindowToExpressionType(lowercase_name);
114
0
    if (win_fun_type == ExpressionType::INVALID) {
115
0
      throw InternalException("Unknown/unsupported window function");
116
0
    }
117
118
0
    if (root->agg_distinct) {
119
0
      throw ParserException("DISTINCT is not implemented for window functions!");
120
0
    }
121
122
0
    if (root->agg_order) {
123
0
      throw ParserException("ORDER BY is not implemented for window functions!");
124
0
    }
125
126
0
    if (win_fun_type != ExpressionType::WINDOW_AGGREGATE && root->agg_filter) {
127
0
      throw ParserException("FILTER is not implemented for non-aggregate window functions!");
128
0
    }
129
0
    if (root->export_state) {
130
0
      throw ParserException("EXPORT_STATE is not supported for window functions!");
131
0
    }
132
133
0
    if (win_fun_type == ExpressionType::WINDOW_AGGREGATE && root->agg_ignore_nulls) {
134
0
      throw ParserException("IGNORE NULLS is not supported for windowed aggregates");
135
0
    }
136
137
0
    auto expr = make_unique<WindowExpression>(win_fun_type, schema, lowercase_name);
138
0
    expr->ignore_nulls = root->agg_ignore_nulls;
139
140
0
    if (root->agg_filter) {
141
0
      auto filter_expr = TransformExpression(root->agg_filter);
142
0
      expr->filter_expr = move(filter_expr);
143
0
    }
144
145
0
    if (root->args) {
146
0
      vector<unique_ptr<ParsedExpression>> function_list;
147
0
      TransformExpressionList(*root->args, function_list);
148
149
0
      if (win_fun_type == ExpressionType::WINDOW_AGGREGATE) {
150
0
        for (auto &child : function_list) {
151
0
          expr->children.push_back(move(child));
152
0
        }
153
0
      } else {
154
0
        if (!function_list.empty()) {
155
0
          expr->children.push_back(move(function_list[0]));
156
0
        }
157
0
        if (win_fun_type == ExpressionType::WINDOW_LEAD || win_fun_type == ExpressionType::WINDOW_LAG) {
158
0
          if (function_list.size() > 1) {
159
0
            expr->offset_expr = move(function_list[1]);
160
0
          }
161
0
          if (function_list.size() > 2) {
162
0
            expr->default_expr = move(function_list[2]);
163
0
          }
164
0
          if (function_list.size() > 3) {
165
0
            throw ParserException("Incorrect number of parameters for function %s", lowercase_name);
166
0
          }
167
0
        } else if (win_fun_type == ExpressionType::WINDOW_NTH_VALUE) {
168
0
          if (function_list.size() > 1) {
169
0
            expr->children.push_back(move(function_list[1]));
170
0
          }
171
0
          if (function_list.size() > 2) {
172
0
            throw ParserException("Incorrect number of parameters for function %s", lowercase_name);
173
0
          }
174
0
        } else {
175
0
          if (function_list.size() > 1) {
176
0
            throw ParserException("Incorrect number of parameters for function %s", lowercase_name);
177
0
          }
178
0
        }
179
0
      }
180
0
    }
181
0
    auto window_spec = reinterpret_cast<duckdb_libpgquery::PGWindowDef *>(root->over);
182
0
    if (window_spec->name) {
183
0
      auto it = window_clauses.find(StringUtil::Lower(string(window_spec->name)));
184
0
      if (it == window_clauses.end()) {
185
0
        throw ParserException("window \"%s\" does not exist", window_spec->name);
186
0
      }
187
0
      window_spec = it->second;
188
0
      D_ASSERT(window_spec);
189
0
    }
190
0
    auto window_ref = window_spec;
191
0
    if (window_ref->refname) {
192
0
      auto it = window_clauses.find(StringUtil::Lower(string(window_spec->refname)));
193
0
      if (it == window_clauses.end()) {
194
0
        throw ParserException("window \"%s\" does not exist", window_spec->refname);
195
0
      }
196
0
      window_ref = it->second;
197
0
      D_ASSERT(window_ref);
198
0
    }
199
0
    TransformWindowDef(window_ref, expr.get());
200
0
    TransformWindowFrame(window_spec, expr.get());
201
0
    expr->query_location = root->location;
202
0
    return move(expr);
203
0
  }
204
205
104
  if (root->agg_ignore_nulls) {
206
0
    throw ParserException("IGNORE NULLS is not supported for non-window functions");
207
0
  }
208
209
  //  TransformExpressionList??
210
104
  vector<unique_ptr<ParsedExpression>> children;
211
104
  if (root->args != nullptr) {
212
16
    for (auto node = root->args->head; node != nullptr; node = node->next) {
213
8
      auto child_expr = TransformExpression((duckdb_libpgquery::PGNode *)node->data.ptr_value);
214
8
      children.push_back(move(child_expr));
215
8
    }
216
8
  }
217
104
  unique_ptr<ParsedExpression> filter_expr;
218
104
  if (root->agg_filter) {
219
0
    filter_expr = TransformExpression(root->agg_filter);
220
0
  }
221
222
104
  auto order_bys = make_unique<OrderModifier>();
223
104
  TransformOrderBy(root->agg_order, order_bys->orders);
224
225
  // Ordered aggregates can be either WITHIN GROUP or after the function arguments
226
104
  if (root->agg_within_group) {
227
    //  https://www.postgresql.org/docs/current/functions-aggregate.html#FUNCTIONS-ORDEREDSET-TABLE
228
    //  Since we implement "ordered aggregates" without sorting,
229
    //  we map all the ones we support to the corresponding aggregate function.
230
0
    if (order_bys->orders.size() != 1) {
231
0
      throw ParserException("Cannot use multiple ORDER BY clauses with WITHIN GROUP");
232
0
    }
233
0
    if (lowercase_name == "percentile_cont") {
234
0
      if (children.size() != 1) {
235
0
        throw ParserException("Wrong number of arguments for PERCENTILE_CONT");
236
0
      }
237
0
      lowercase_name = "quantile_cont";
238
0
    } else if (lowercase_name == "percentile_disc") {
239
0
      if (children.size() != 1) {
240
0
        throw ParserException("Wrong number of arguments for PERCENTILE_DISC");
241
0
      }
242
0
      lowercase_name = "quantile_disc";
243
0
    } else if (lowercase_name == "mode") {
244
0
      if (!children.empty()) {
245
0
        throw ParserException("Wrong number of arguments for MODE");
246
0
      }
247
0
      lowercase_name = "mode";
248
0
    } else {
249
0
      throw ParserException("Unknown ordered aggregate \"%s\".", function_name);
250
0
    }
251
0
  }
252
253
  // star gets eaten in the parser
254
104
  if (lowercase_name == "count" && children.empty()) {
255
0
    lowercase_name = "count_star";
256
0
  }
257
258
104
  if (lowercase_name == "if") {
259
0
    if (children.size() != 3) {
260
0
      throw ParserException("Wrong number of arguments to IF.");
261
0
    }
262
0
    auto expr = make_unique<CaseExpression>();
263
0
    CaseCheck check;
264
0
    check.when_expr = move(children[0]);
265
0
    check.then_expr = move(children[1]);
266
0
    expr->case_checks.push_back(move(check));
267
0
    expr->else_expr = move(children[2]);
268
0
    return move(expr);
269
104
  } else if (lowercase_name == "construct_array") {
270
0
    auto construct_array = make_unique<OperatorExpression>(ExpressionType::ARRAY_CONSTRUCTOR);
271
0
    construct_array->children = move(children);
272
0
    return move(construct_array);
273
104
  } else if (lowercase_name == "ifnull") {
274
0
    if (children.size() != 2) {
275
0
      throw ParserException("Wrong number of arguments to IFNULL.");
276
0
    }
277
278
    //  Two-argument COALESCE
279
0
    auto coalesce_op = make_unique<OperatorExpression>(ExpressionType::OPERATOR_COALESCE);
280
0
    coalesce_op->children.push_back(move(children[0]));
281
0
    coalesce_op->children.push_back(move(children[1]));
282
0
    return move(coalesce_op);
283
0
  }
284
285
104
  auto function = make_unique<FunctionExpression>(schema, lowercase_name.c_str(), move(children), move(filter_expr),
286
104
                                                  move(order_bys), root->agg_distinct, false, root->export_state);
287
104
  function->query_location = root->location;
288
289
104
  return move(function);
290
104
}
291
292
0
static string SQLValueOpToString(duckdb_libpgquery::PGSQLValueFunctionOp op) {
293
0
  switch (op) {
294
0
  case duckdb_libpgquery::PG_SVFOP_CURRENT_DATE:
295
0
    return "current_date";
296
0
  case duckdb_libpgquery::PG_SVFOP_CURRENT_TIME:
297
0
    return "get_current_time";
298
0
  case duckdb_libpgquery::PG_SVFOP_CURRENT_TIME_N:
299
0
    return "current_time_n";
300
0
  case duckdb_libpgquery::PG_SVFOP_CURRENT_TIMESTAMP:
301
0
    return "get_current_timestamp";
302
0
  case duckdb_libpgquery::PG_SVFOP_CURRENT_TIMESTAMP_N:
303
0
    return "current_timestamp_n";
304
0
  case duckdb_libpgquery::PG_SVFOP_LOCALTIME:
305
0
    return "current_localtime";
306
0
  case duckdb_libpgquery::PG_SVFOP_LOCALTIME_N:
307
0
    return "current_localtime_n";
308
0
  case duckdb_libpgquery::PG_SVFOP_LOCALTIMESTAMP:
309
0
    return "current_localtimestamp";
310
0
  case duckdb_libpgquery::PG_SVFOP_LOCALTIMESTAMP_N:
311
0
    return "current_localtimestamp_n";
312
0
  case duckdb_libpgquery::PG_SVFOP_CURRENT_ROLE:
313
0
    return "current_role";
314
0
  case duckdb_libpgquery::PG_SVFOP_CURRENT_USER:
315
0
    return "current_user";
316
0
  case duckdb_libpgquery::PG_SVFOP_USER:
317
0
    return "user";
318
0
  case duckdb_libpgquery::PG_SVFOP_SESSION_USER:
319
0
    return "session_user";
320
0
  case duckdb_libpgquery::PG_SVFOP_CURRENT_CATALOG:
321
0
    return "current_catalog";
322
0
  case duckdb_libpgquery::PG_SVFOP_CURRENT_SCHEMA:
323
0
    return "current_schema";
324
0
  default:
325
0
    throw InternalException("Could not find named SQL value function specification " + to_string((int)op));
326
0
  }
327
0
}
328
329
0
unique_ptr<ParsedExpression> Transformer::TransformSQLValueFunction(duckdb_libpgquery::PGSQLValueFunction *node) {
330
0
  D_ASSERT(node);
331
0
  vector<unique_ptr<ParsedExpression>> children;
332
0
  auto fname = SQLValueOpToString(node->op);
333
0
  return make_unique<FunctionExpression>(DEFAULT_SCHEMA, fname, move(children));
334
0
}
335
336
} // namespace duckdb