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
8namespace duckdb {
9
10// Optionally push a PROJECTION operator
11unique_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
61unique_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