| 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 | |