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 | |
8 | using namespace std; |
9 | |
10 | namespace duckdb { |
11 | |
12 | OrderBinder::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 | } |
17 | OrderBinder::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 | |
24 | unique_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 | |
29 | unique_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 | |