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
13namespace duckdb {
14
15OrderBinder::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}
20OrderBinder::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
28unique_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
41unique_ptr<Expression> OrderBinder::CreateExtraReference(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
50unique_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
66unique_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