1 | #include "duckdb/planner/binder.hpp" |
2 | #include "duckdb/planner/expression/bound_cast_expression.hpp" |
3 | #include "duckdb/planner/expression/bound_columnref_expression.hpp" |
4 | #include "duckdb/planner/operator/logical_projection.hpp" |
5 | #include "duckdb/planner/operator/logical_set_operation.hpp" |
6 | #include "duckdb/planner/query_node/bound_set_operation_node.hpp" |
7 | |
8 | namespace duckdb { |
9 | |
10 | // Optionally push a PROJECTION operator |
11 | unique_ptr<LogicalOperator> Binder::CastLogicalOperatorToTypes(vector<LogicalType> &source_types, |
12 | vector<LogicalType> &target_types, |
13 | unique_ptr<LogicalOperator> op) { |
14 | D_ASSERT(op); |
15 | // first check if we even need to cast |
16 | D_ASSERT(source_types.size() == target_types.size()); |
17 | if (source_types == target_types) { |
18 | // source and target types are equal: don't need to cast |
19 | return op; |
20 | } |
21 | // otherwise add casts |
22 | auto node = op.get(); |
23 | if (node->type == LogicalOperatorType::LOGICAL_PROJECTION) { |
24 | // "node" is a projection; we can just do the casts in there |
25 | D_ASSERT(node->expressions.size() == source_types.size()); |
26 | // add the casts to the selection list |
27 | for (idx_t i = 0; i < target_types.size(); i++) { |
28 | if (source_types[i] != target_types[i]) { |
29 | // differing types, have to add a cast |
30 | string alias = node->expressions[i]->alias; |
31 | node->expressions[i] = |
32 | BoundCastExpression::AddCastToType(context, expr: std::move(node->expressions[i]), target_type: target_types[i]); |
33 | node->expressions[i]->alias = alias; |
34 | } |
35 | } |
36 | return op; |
37 | } else { |
38 | // found a non-projection operator |
39 | // push a new projection containing the casts |
40 | |
41 | // fetch the set of column bindings |
42 | auto setop_columns = op->GetColumnBindings(); |
43 | D_ASSERT(setop_columns.size() == source_types.size()); |
44 | |
45 | // now generate the expression list |
46 | vector<unique_ptr<Expression>> select_list; |
47 | for (idx_t i = 0; i < target_types.size(); i++) { |
48 | unique_ptr<Expression> result = make_uniq<BoundColumnRefExpression>(args&: source_types[i], args&: setop_columns[i]); |
49 | if (source_types[i] != target_types[i]) { |
50 | // add a cast only if the source and target types are not equivalent |
51 | result = BoundCastExpression::AddCastToType(context, expr: std::move(result), target_type: target_types[i]); |
52 | } |
53 | select_list.push_back(x: std::move(result)); |
54 | } |
55 | auto projection = make_uniq<LogicalProjection>(args: GenerateTableIndex(), args: std::move(select_list)); |
56 | projection->children.push_back(x: std::move(op)); |
57 | return std::move(projection); |
58 | } |
59 | } |
60 | |
61 | unique_ptr<LogicalOperator> Binder::CreatePlan(BoundSetOperationNode &node) { |
62 | // Generate the logical plan for the left and right sides of the set operation |
63 | node.left_binder->plan_subquery = plan_subquery; |
64 | node.right_binder->plan_subquery = plan_subquery; |
65 | |
66 | auto left_node = node.left_binder->CreatePlan(node&: *node.left); |
67 | auto right_node = node.right_binder->CreatePlan(node&: *node.right); |
68 | |
69 | // Add a new projection to child node |
70 | D_ASSERT(node.left_reorder_exprs.size() == node.right_reorder_exprs.size()); |
71 | if (!node.left_reorder_exprs.empty()) { |
72 | D_ASSERT(node.setop_type == SetOperationType::UNION_BY_NAME); |
73 | vector<LogicalType> left_types; |
74 | vector<LogicalType> right_types; |
75 | // We are going to add a new projection operator, so collect the type |
76 | // of reorder exprs in order to call CastLogicalOperatorToTypes() |
77 | for (idx_t i = 0; i < node.left_reorder_exprs.size(); ++i) { |
78 | left_types.push_back(x: node.left_reorder_exprs[i]->return_type); |
79 | right_types.push_back(x: node.right_reorder_exprs[i]->return_type); |
80 | } |
81 | |
82 | auto left_projection = make_uniq<LogicalProjection>(args: GenerateTableIndex(), args: std::move(node.left_reorder_exprs)); |
83 | left_projection->children.push_back(x: std::move(left_node)); |
84 | left_node = std::move(left_projection); |
85 | |
86 | auto right_projection = make_uniq<LogicalProjection>(args: GenerateTableIndex(), args: std::move(node.right_reorder_exprs)); |
87 | right_projection->children.push_back(x: std::move(right_node)); |
88 | right_node = std::move(right_projection); |
89 | |
90 | left_node = CastLogicalOperatorToTypes(source_types&: left_types, target_types&: node.types, op: std::move(left_node)); |
91 | right_node = CastLogicalOperatorToTypes(source_types&: right_types, target_types&: node.types, op: std::move(right_node)); |
92 | } else { |
93 | left_node = CastLogicalOperatorToTypes(source_types&: node.left->types, target_types&: node.types, op: std::move(left_node)); |
94 | right_node = CastLogicalOperatorToTypes(source_types&: node.right->types, target_types&: node.types, op: std::move(right_node)); |
95 | } |
96 | |
97 | // check if there are any unplanned subqueries left in either child |
98 | has_unplanned_subqueries = |
99 | node.left_binder->has_unplanned_subqueries || node.right_binder->has_unplanned_subqueries; |
100 | |
101 | // create actual logical ops for setops |
102 | LogicalOperatorType logical_type; |
103 | switch (node.setop_type) { |
104 | case SetOperationType::UNION: |
105 | case SetOperationType::UNION_BY_NAME: |
106 | logical_type = LogicalOperatorType::LOGICAL_UNION; |
107 | break; |
108 | case SetOperationType::EXCEPT: |
109 | logical_type = LogicalOperatorType::LOGICAL_EXCEPT; |
110 | break; |
111 | default: |
112 | D_ASSERT(node.setop_type == SetOperationType::INTERSECT); |
113 | logical_type = LogicalOperatorType::LOGICAL_INTERSECT; |
114 | break; |
115 | } |
116 | |
117 | auto root = make_uniq<LogicalSetOperation>(args&: node.setop_index, args: node.types.size(), args: std::move(left_node), |
118 | args: std::move(right_node), args&: logical_type); |
119 | |
120 | return VisitQueryNode(node, root: std::move(root)); |
121 | } |
122 | |
123 | } // namespace duckdb |
124 | |