| 1 | #include "duckdb/planner/expression_binder/having_binder.hpp" |
| 2 | |
| 3 | #include "duckdb/parser/expression/columnref_expression.hpp" |
| 4 | #include "duckdb/planner/binder.hpp" |
| 5 | #include "duckdb/planner/expression_binder/aggregate_binder.hpp" |
| 6 | #include "duckdb/common/string_util.hpp" |
| 7 | #include "duckdb/planner/query_node/bound_select_node.hpp" |
| 8 | |
| 9 | namespace duckdb { |
| 10 | |
| 11 | HavingBinder::HavingBinder(Binder &binder, ClientContext &context, BoundSelectNode &node, BoundGroupInformation &info, |
| 12 | case_insensitive_map_t<idx_t> &alias_map, AggregateHandling aggregate_handling) |
| 13 | : BaseSelectBinder(binder, context, node, info), column_alias_binder(node, alias_map), |
| 14 | aggregate_handling(aggregate_handling) { |
| 15 | target_type = LogicalType(LogicalTypeId::BOOLEAN); |
| 16 | } |
| 17 | |
| 18 | BindResult HavingBinder::BindColumnRef(unique_ptr<ParsedExpression> &expr_ptr, idx_t depth, bool root_expression) { |
| 19 | auto &expr = expr_ptr->Cast<ColumnRefExpression>(); |
| 20 | auto alias_result = column_alias_binder.BindAlias(enclosing_binder&: *this, expr, depth, root_expression); |
| 21 | if (!alias_result.HasError()) { |
| 22 | if (depth > 0) { |
| 23 | throw BinderException("Having clause cannot reference alias in correlated subquery" ); |
| 24 | } |
| 25 | return alias_result; |
| 26 | } |
| 27 | if (aggregate_handling == AggregateHandling::FORCE_AGGREGATES) { |
| 28 | if (depth > 0) { |
| 29 | throw BinderException("Having clause cannot reference column in correlated subquery and group by all" ); |
| 30 | } |
| 31 | auto expr = duckdb::BaseSelectBinder::BindExpression(expr_ptr, depth); |
| 32 | if (expr.HasError()) { |
| 33 | return expr; |
| 34 | } |
| 35 | auto group_ref = make_uniq<BoundColumnRefExpression>( |
| 36 | args&: expr.expression->return_type, args: ColumnBinding(node.group_index, node.groups.group_expressions.size())); |
| 37 | node.groups.group_expressions.push_back(x: std::move(expr.expression)); |
| 38 | return BindResult(std::move(group_ref)); |
| 39 | } |
| 40 | return BindResult(StringUtil::Format( |
| 41 | fmt_str: "column %s must appear in the GROUP BY clause or be used in an aggregate function" , params: expr.ToString())); |
| 42 | } |
| 43 | |
| 44 | BindResult HavingBinder::BindExpression(unique_ptr<ParsedExpression> &expr_ptr, idx_t depth, bool root_expression) { |
| 45 | auto &expr = *expr_ptr; |
| 46 | // check if the expression binds to one of the groups |
| 47 | auto group_index = TryBindGroup(expr, depth); |
| 48 | if (group_index != DConstants::INVALID_INDEX) { |
| 49 | return BindGroup(expr, depth, group_index); |
| 50 | } |
| 51 | switch (expr.expression_class) { |
| 52 | case ExpressionClass::WINDOW: |
| 53 | return BindResult("HAVING clause cannot contain window functions!" ); |
| 54 | case ExpressionClass::COLUMN_REF: |
| 55 | return BindColumnRef(expr_ptr, depth, root_expression); |
| 56 | default: |
| 57 | return duckdb::BaseSelectBinder::BindExpression(expr_ptr, depth); |
| 58 | } |
| 59 | } |
| 60 | |
| 61 | } // namespace duckdb |
| 62 | |