1#include "duckdb/planner/expression_binder/order_binder.hpp"
2
3#include "duckdb/parser/expression/columnref_expression.hpp"
4#include "duckdb/parser/expression/constant_expression.hpp"
5#include "duckdb/parser/query_node/select_node.hpp"
6#include "duckdb/planner/expression_binder.hpp"
7
8using namespace std;
9
10namespace duckdb {
11
12OrderBinder::OrderBinder(vector<Binder *> binders, idx_t projection_index, unordered_map<string, idx_t> &alias_map,
13 expression_map_t<idx_t> &projection_map, idx_t max_count)
14 : binders(move(binders)), projection_index(projection_index), max_count(max_count), extra_list(nullptr),
15 alias_map(alias_map), projection_map(projection_map) {
16}
17OrderBinder::OrderBinder(vector<Binder *> binders, idx_t projection_index, SelectNode &node,
18 unordered_map<string, idx_t> &alias_map, expression_map_t<idx_t> &projection_map)
19 : binders(move(binders)), projection_index(projection_index), alias_map(alias_map), projection_map(projection_map) {
20 this->max_count = node.select_list.size();
21 this->extra_list = &node.select_list;
22}
23
24unique_ptr<Expression> OrderBinder::CreateProjectionReference(ParsedExpression &expr, idx_t index) {
25 return make_unique<BoundColumnRefExpression>(expr.GetName(), TypeId::INVALID,
26 ColumnBinding(projection_index, index));
27}
28
29unique_ptr<Expression> OrderBinder::Bind(unique_ptr<ParsedExpression> expr) {
30 // in the ORDER BY clause we do not bind children
31 // we bind ONLY to the select list
32 // if there is no matching entry in the SELECT list already, we add the expression to the SELECT list and refer the
33 // new expression the new entry will then be bound later during the binding of the SELECT list we also don't do type
34 // resolution here: this only happens after the SELECT list has been bound
35 switch (expr->expression_class) {
36 case ExpressionClass::CONSTANT: {
37 // ORDER BY constant
38 // is the ORDER BY expression a constant integer? (e.g. ORDER BY 1)
39 auto &constant = (ConstantExpression &)*expr;
40 // ORDER BY a constant
41 if (!TypeIsIntegral(constant.value.type)) {
42 // non-integral expression, we just leave the constant here.
43 // ORDER BY <constant> has no effect
44 // CONTROVERSIAL: maybe we should throw an error
45 return nullptr;
46 }
47 // INTEGER constant: we use the integer as an index into the select list (e.g. ORDER BY 1)
48 auto index = (idx_t)constant.value.GetValue<int64_t>();
49 if (index < 1 || index > max_count) {
50 throw BinderException("ORDER term out of range - should be between 1 and %lld", (idx_t)max_count);
51 }
52 return CreateProjectionReference(*expr, index - 1);
53 }
54 case ExpressionClass::COLUMN_REF: {
55 // COLUMN REF expression
56 // check if we can bind it to an alias in the select list
57 auto &colref = (ColumnRefExpression &)*expr;
58 // if there is an explicit table name we can't bind to an alias
59 if (!colref.table_name.empty()) {
60 break;
61 }
62 // check the alias list
63 auto entry = alias_map.find(colref.column_name);
64 if (entry != alias_map.end()) {
65 // it does! point it to that entry
66 return CreateProjectionReference(*expr, entry->second);
67 }
68 break;
69 }
70 default:
71 break;
72 }
73 // general case
74 // first bind the table names of this entry
75 for (auto &binder : binders) {
76 ExpressionBinder::BindTableNames(*binder, *expr);
77 }
78 // first check if the ORDER BY clause already points to an entry in the projection list
79 auto entry = projection_map.find(expr.get());
80 if (entry != projection_map.end()) {
81 if (entry->second == INVALID_INDEX) {
82 throw BinderException("Ambiguous reference to column");
83 }
84 // there is a matching entry in the projection list
85 // just point to that entry
86 return CreateProjectionReference(*expr, entry->second);
87 }
88 if (!extra_list) {
89 // no extra list specified: we cannot push an extra ORDER BY clause
90 throw BinderException("Could not ORDER BY column \"%s\": add the expression/function to every SELECT, or move "
91 "the UNION into a FROM clause.",
92 expr->ToString().c_str());
93 }
94 // otherwise we need to push the ORDER BY entry into the select list
95 auto result = CreateProjectionReference(*expr, extra_list->size());
96 extra_list->push_back(move(expr));
97 return result;
98}
99
100} // namespace duckdb
101