| 1 | #include "duckdb/parser/expression/list.hpp" |
| 2 | #include "duckdb/parser/transformer.hpp" |
| 3 | #include "duckdb/parser/query_node/select_node.hpp" |
| 4 | #include "duckdb/parser/tableref/subqueryref.hpp" |
| 5 | |
| 6 | namespace duckdb { |
| 7 | |
| 8 | unique_ptr<ParsedExpression> Transformer::TransformSubquery(duckdb_libpgquery::PGSubLink &root) { |
| 9 | auto subquery_expr = make_uniq<SubqueryExpression>(); |
| 10 | |
| 11 | subquery_expr->subquery = TransformSelect(node: root.subselect); |
| 12 | D_ASSERT(subquery_expr->subquery); |
| 13 | D_ASSERT(subquery_expr->subquery->node->GetSelectList().size() > 0); |
| 14 | |
| 15 | switch (root.subLinkType) { |
| 16 | case duckdb_libpgquery::PG_EXISTS_SUBLINK: { |
| 17 | subquery_expr->subquery_type = SubqueryType::EXISTS; |
| 18 | break; |
| 19 | } |
| 20 | case duckdb_libpgquery::PG_ANY_SUBLINK: |
| 21 | case duckdb_libpgquery::PG_ALL_SUBLINK: { |
| 22 | // comparison with ANY() or ALL() |
| 23 | subquery_expr->subquery_type = SubqueryType::ANY; |
| 24 | subquery_expr->child = TransformExpression(node: root.testexpr); |
| 25 | // get the operator name |
| 26 | if (!root.operName) { |
| 27 | // simple IN |
| 28 | subquery_expr->comparison_type = ExpressionType::COMPARE_EQUAL; |
| 29 | } else { |
| 30 | auto operator_name = |
| 31 | string((PGPointerCast<duckdb_libpgquery::PGValue>(ptr: root.operName->head->data.ptr_value))->val.str); |
| 32 | subquery_expr->comparison_type = OperatorToExpressionType(op: operator_name); |
| 33 | } |
| 34 | if (subquery_expr->comparison_type != ExpressionType::COMPARE_EQUAL && |
| 35 | subquery_expr->comparison_type != ExpressionType::COMPARE_NOTEQUAL && |
| 36 | subquery_expr->comparison_type != ExpressionType::COMPARE_GREATERTHAN && |
| 37 | subquery_expr->comparison_type != ExpressionType::COMPARE_GREATERTHANOREQUALTO && |
| 38 | subquery_expr->comparison_type != ExpressionType::COMPARE_LESSTHAN && |
| 39 | subquery_expr->comparison_type != ExpressionType::COMPARE_LESSTHANOREQUALTO) { |
| 40 | throw ParserException("ANY and ALL operators require one of =,<>,>,<,>=,<= comparisons!" ); |
| 41 | } |
| 42 | if (root.subLinkType == duckdb_libpgquery::PG_ALL_SUBLINK) { |
| 43 | // ALL sublink is equivalent to NOT(ANY) with inverted comparison |
| 44 | // e.g. [= ALL()] is equivalent to [NOT(<> ANY())] |
| 45 | // first invert the comparison type |
| 46 | subquery_expr->comparison_type = NegateComparisonExpression(type: subquery_expr->comparison_type); |
| 47 | return make_uniq<OperatorExpression>(args: ExpressionType::OPERATOR_NOT, args: std::move(subquery_expr)); |
| 48 | } |
| 49 | break; |
| 50 | } |
| 51 | case duckdb_libpgquery::PG_EXPR_SUBLINK: { |
| 52 | // return a single scalar value from the subquery |
| 53 | // no child expression to compare to |
| 54 | subquery_expr->subquery_type = SubqueryType::SCALAR; |
| 55 | break; |
| 56 | } |
| 57 | case duckdb_libpgquery::PG_ARRAY_SUBLINK: { |
| 58 | auto subquery_table_alias = "__subquery" ; |
| 59 | auto subquery_column_alias = "__arr_element" ; |
| 60 | |
| 61 | // ARRAY expression |
| 62 | // wrap subquery into "SELECT CASE WHEN ARRAY_AGG(i) IS NULL THEN [] ELSE ARRAY_AGG(i) END FROM (...) tbl(i)" |
| 63 | auto select_node = make_uniq<SelectNode>(); |
| 64 | |
| 65 | // ARRAY_AGG(i) |
| 66 | vector<unique_ptr<ParsedExpression>> children; |
| 67 | children.push_back( |
| 68 | x: make_uniq_base<ParsedExpression, ColumnRefExpression>(args&: subquery_column_alias, args&: subquery_table_alias)); |
| 69 | auto aggr = make_uniq<FunctionExpression>(args: "array_agg" , args: std::move(children)); |
| 70 | // ARRAY_AGG(i) IS NULL |
| 71 | auto agg_is_null = make_uniq<OperatorExpression>(args: ExpressionType::OPERATOR_IS_NULL, args: aggr->Copy()); |
| 72 | // empty list |
| 73 | vector<unique_ptr<ParsedExpression>> list_children; |
| 74 | auto empty_list = make_uniq<FunctionExpression>(args: "list_value" , args: std::move(list_children)); |
| 75 | // CASE |
| 76 | auto case_expr = make_uniq<CaseExpression>(); |
| 77 | CaseCheck check; |
| 78 | check.when_expr = std::move(agg_is_null); |
| 79 | check.then_expr = std::move(empty_list); |
| 80 | case_expr->case_checks.push_back(x: std::move(check)); |
| 81 | case_expr->else_expr = std::move(aggr); |
| 82 | |
| 83 | select_node->select_list.push_back(x: std::move(case_expr)); |
| 84 | |
| 85 | // FROM (...) tbl(i) |
| 86 | auto child_subquery = make_uniq<SubqueryRef>(args: std::move(subquery_expr->subquery), args&: subquery_table_alias); |
| 87 | child_subquery->column_name_alias.emplace_back(args&: subquery_column_alias); |
| 88 | select_node->from_table = std::move(child_subquery); |
| 89 | |
| 90 | auto new_subquery = make_uniq<SelectStatement>(); |
| 91 | new_subquery->node = std::move(select_node); |
| 92 | subquery_expr->subquery = std::move(new_subquery); |
| 93 | |
| 94 | subquery_expr->subquery_type = SubqueryType::SCALAR; |
| 95 | break; |
| 96 | } |
| 97 | default: |
| 98 | throw NotImplementedException("Subquery of type %d not implemented\n" , (int)root.subLinkType); |
| 99 | } |
| 100 | subquery_expr->query_location = root.location; |
| 101 | return std::move(subquery_expr); |
| 102 | } |
| 103 | |
| 104 | } // namespace duckdb |
| 105 | |