1#include "duckdb/execution/column_binding_resolver.hpp"
2
3#include "duckdb/planner/operator/logical_comparison_join.hpp"
4#include "duckdb/planner/operator/logical_create_index.hpp"
5#include "duckdb/planner/operator/logical_delim_join.hpp"
6
7#include "duckdb/planner/expression/bound_columnref_expression.hpp"
8#include "duckdb/planner/expression/bound_reference_expression.hpp"
9
10#include "duckdb/catalog/catalog_entry/table_catalog_entry.hpp"
11
12using namespace duckdb;
13using namespace std;
14
15ColumnBindingResolver::ColumnBindingResolver() {
16}
17
18void ColumnBindingResolver::VisitOperator(LogicalOperator &op) {
19 if (op.type == LogicalOperatorType::COMPARISON_JOIN || op.type == LogicalOperatorType::DELIM_JOIN) {
20 // special case: comparison join
21 auto &comp_join = (LogicalComparisonJoin &)op;
22 // first get the bindings of the LHS and resolve the LHS expressions
23 VisitOperator(*comp_join.children[0]);
24 for (auto &cond : comp_join.conditions) {
25 VisitExpression(&cond.left);
26 }
27 if (op.type == LogicalOperatorType::DELIM_JOIN) {
28 // visit the duplicate eliminated columns on the LHS, if any
29 auto &delim_join = (LogicalDelimJoin &)op;
30 for (auto &expr : delim_join.duplicate_eliminated_columns) {
31 VisitExpression(&expr);
32 }
33 }
34 // then get the bindings of the RHS and resolve the RHS expressions
35 VisitOperator(*comp_join.children[1]);
36 for (auto &cond : comp_join.conditions) {
37 VisitExpression(&cond.right);
38 }
39 // finally update the bindings with the result bindings of the join
40 bindings = op.GetColumnBindings();
41 return;
42 } else if (op.type == LogicalOperatorType::ANY_JOIN) {
43 // ANY join, this join is different because we evaluate the expression on the bindings of BOTH join sides at
44 // once i.e. we set the bindings first to the bindings of the entire join, and then resolve the expressions of
45 // this operator
46 VisitOperatorChildren(op);
47 bindings = op.GetColumnBindings();
48 VisitOperatorExpressions(op);
49 return;
50 } else if (op.type == LogicalOperatorType::CREATE_INDEX) {
51 // CREATE INDEX statement, add the columns of the table with table index 0 to the binding set
52 // afterwards bind the expressions of the CREATE INDEX statement
53 auto &create_index = (LogicalCreateIndex &)op;
54 bindings = LogicalOperator::GenerateColumnBindings(0, create_index.table.columns.size());
55 VisitOperatorExpressions(op);
56 return;
57 } else if (op.type == LogicalOperatorType::GET) {
58 //! We first need to update the current set of bindings and then visit operator expressions
59 bindings = op.GetColumnBindings();
60 VisitOperatorExpressions(op);
61 return;
62 }
63 // general case
64 // first visit the children of this operator
65 VisitOperatorChildren(op);
66 // now visit the expressions of this operator to resolve any bound column references
67 VisitOperatorExpressions(op);
68 // finally update the current set of bindings to the current set of column bindings
69 bindings = op.GetColumnBindings();
70}
71
72unique_ptr<Expression> ColumnBindingResolver::VisitReplace(BoundColumnRefExpression &expr,
73 unique_ptr<Expression> *expr_ptr) {
74 assert(expr.depth == 0);
75 // check the current set of column bindings to see which index corresponds to the column reference
76 for (idx_t i = 0; i < bindings.size(); i++) {
77 if (expr.binding == bindings[i]) {
78 return make_unique<BoundReferenceExpression>(expr.alias, expr.return_type, i);
79 }
80 }
81 // could not bind the column reference, this should never happen and indicates a bug in the code
82 // generate an error message
83 string bound_columns = "[";
84 for (idx_t i = 0; i < bindings.size(); i++) {
85 if (i != 0) {
86 bound_columns += " ";
87 }
88 bound_columns += to_string(bindings[i].table_index) + "." + to_string(bindings[i].column_index);
89 }
90 bound_columns += "]";
91
92 throw InternalException("Failed to bind column reference \"%s\" [%d.%d] (bindings: %s)", expr.alias.c_str(),
93 (int)expr.binding.table_index, (int)expr.binding.column_index, bound_columns.c_str());
94}
95