1 | #include "duckdb/planner/expression_binder/order_binder.hpp" |
2 | |
3 | #include "duckdb/parser/expression/columnref_expression.hpp" |
4 | #include "duckdb/parser/expression/positional_reference_expression.hpp" |
5 | #include "duckdb/parser/expression/constant_expression.hpp" |
6 | #include "duckdb/parser/expression/star_expression.hpp" |
7 | #include "duckdb/parser/query_node/select_node.hpp" |
8 | #include "duckdb/planner/expression_binder.hpp" |
9 | #include "duckdb/parser/expression/parameter_expression.hpp" |
10 | #include "duckdb/planner/expression/bound_parameter_expression.hpp" |
11 | #include "duckdb/planner/binder.hpp" |
12 | |
13 | namespace duckdb { |
14 | |
15 | OrderBinder::OrderBinder(vector<Binder *> binders, idx_t projection_index, case_insensitive_map_t<idx_t> &alias_map, |
16 | parsed_expression_map_t<idx_t> &projection_map, idx_t max_count) |
17 | : binders(std::move(binders)), projection_index(projection_index), max_count(max_count), extra_list(nullptr), |
18 | alias_map(alias_map), projection_map(projection_map) { |
19 | } |
20 | OrderBinder::OrderBinder(vector<Binder *> binders, idx_t projection_index, SelectNode &node, |
21 | case_insensitive_map_t<idx_t> &alias_map, parsed_expression_map_t<idx_t> &projection_map) |
22 | : binders(std::move(binders)), projection_index(projection_index), alias_map(alias_map), |
23 | projection_map(projection_map) { |
24 | this->max_count = node.select_list.size(); |
25 | this->extra_list = &node.select_list; |
26 | } |
27 | |
28 | unique_ptr<Expression> OrderBinder::CreateProjectionReference(ParsedExpression &expr, idx_t index) { |
29 | string alias; |
30 | if (extra_list && index < extra_list->size()) { |
31 | alias = extra_list->at(n: index)->ToString(); |
32 | } else { |
33 | if (!expr.alias.empty()) { |
34 | alias = expr.alias; |
35 | } |
36 | } |
37 | return make_uniq<BoundColumnRefExpression>(args: std::move(alias), args: LogicalType::INVALID, |
38 | args: ColumnBinding(projection_index, index)); |
39 | } |
40 | |
41 | unique_ptr<Expression> OrderBinder::(unique_ptr<ParsedExpression> expr) { |
42 | if (!extra_list) { |
43 | throw InternalException("CreateExtraReference called without extra_list" ); |
44 | } |
45 | auto result = CreateProjectionReference(expr&: *expr, index: extra_list->size()); |
46 | extra_list->push_back(x: std::move(expr)); |
47 | return result; |
48 | } |
49 | |
50 | unique_ptr<Expression> OrderBinder::BindConstant(ParsedExpression &expr, const Value &val) { |
51 | // ORDER BY a constant |
52 | if (!val.type().IsIntegral()) { |
53 | // non-integral expression, we just leave the constant here. |
54 | // ORDER BY <constant> has no effect |
55 | // CONTROVERSIAL: maybe we should throw an error |
56 | return nullptr; |
57 | } |
58 | // INTEGER constant: we use the integer as an index into the select list (e.g. ORDER BY 1) |
59 | auto index = (idx_t)val.GetValue<int64_t>(); |
60 | if (index < 1 || index > max_count) { |
61 | throw BinderException("ORDER term out of range - should be between 1 and %lld" , (idx_t)max_count); |
62 | } |
63 | return CreateProjectionReference(expr, index: index - 1); |
64 | } |
65 | |
66 | unique_ptr<Expression> OrderBinder::Bind(unique_ptr<ParsedExpression> expr) { |
67 | // in the ORDER BY clause we do not bind children |
68 | // we bind ONLY to the select list |
69 | // if there is no matching entry in the SELECT list already, we add the expression to the SELECT list and refer the |
70 | // new expression the new entry will then be bound later during the binding of the SELECT list we also don't do type |
71 | // resolution here: this only happens after the SELECT list has been bound |
72 | switch (expr->expression_class) { |
73 | case ExpressionClass::CONSTANT: { |
74 | // ORDER BY constant |
75 | // is the ORDER BY expression a constant integer? (e.g. ORDER BY 1) |
76 | auto &constant = expr->Cast<ConstantExpression>(); |
77 | return BindConstant(expr&: *expr, val: constant.value); |
78 | } |
79 | case ExpressionClass::COLUMN_REF: { |
80 | // COLUMN REF expression |
81 | // check if we can bind it to an alias in the select list |
82 | auto &colref = expr->Cast<ColumnRefExpression>(); |
83 | // if there is an explicit table name we can't bind to an alias |
84 | if (colref.IsQualified()) { |
85 | break; |
86 | } |
87 | // check the alias list |
88 | auto entry = alias_map.find(x: colref.column_names[0]); |
89 | if (entry != alias_map.end()) { |
90 | // it does! point it to that entry |
91 | return CreateProjectionReference(expr&: *expr, index: entry->second); |
92 | } |
93 | break; |
94 | } |
95 | case ExpressionClass::POSITIONAL_REFERENCE: { |
96 | auto &posref = expr->Cast<PositionalReferenceExpression>(); |
97 | if (posref.index < 1 || posref.index > max_count) { |
98 | throw BinderException("ORDER term out of range - should be between 1 and %lld" , (idx_t)max_count); |
99 | } |
100 | return CreateProjectionReference(expr&: *expr, index: posref.index - 1); |
101 | } |
102 | case ExpressionClass::PARAMETER: { |
103 | throw ParameterNotAllowedException("Parameter not supported in ORDER BY clause" ); |
104 | } |
105 | default: |
106 | break; |
107 | } |
108 | // general case |
109 | // first bind the table names of this entry |
110 | for (auto &binder : binders) { |
111 | ExpressionBinder::QualifyColumnNames(binder&: *binder, expr); |
112 | } |
113 | // first check if the ORDER BY clause already points to an entry in the projection list |
114 | auto entry = projection_map.find(x: *expr); |
115 | if (entry != projection_map.end()) { |
116 | if (entry->second == DConstants::INVALID_INDEX) { |
117 | throw BinderException("Ambiguous reference to column" ); |
118 | } |
119 | // there is a matching entry in the projection list |
120 | // just point to that entry |
121 | return CreateProjectionReference(expr&: *expr, index: entry->second); |
122 | } |
123 | if (!extra_list) { |
124 | // no extra list specified: we cannot push an extra ORDER BY clause |
125 | throw BinderException("Could not ORDER BY column \"%s\": add the expression/function to every SELECT, or move " |
126 | "the UNION into a FROM clause." , |
127 | expr->ToString()); |
128 | } |
129 | // otherwise we need to push the ORDER BY entry into the select list |
130 | return CreateExtraReference(expr: std::move(expr)); |
131 | } |
132 | |
133 | } // namespace duckdb |
134 | |