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