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
9namespace duckdb {
10
11HavingBinder::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
18BindResult 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
44BindResult 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