Coverage Report

Created: 2026-07-07 07:29

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/duckdb/src/planner/expression_binder/having_binder.cpp
Line
Count
Source
1
#include "duckdb/planner/expression_binder/having_binder.hpp"
2
3
#include "duckdb/parser/expression/columnref_expression.hpp"
4
#include "duckdb/parser/expression/window_expression.hpp"
5
#include "duckdb/planner/binder.hpp"
6
#include "duckdb/common/string_util.hpp"
7
#include "duckdb/planner/query_node/bound_select_node.hpp"
8
#include "duckdb/planner/column_qualifier.hpp"
9
10
namespace duckdb {
11
12
HavingBinder::HavingBinder(Binder &binder, ClientContext &context, BoundSelectNode &node,
13
                           AggregateHandling aggregate_handling)
14
0
    : BaseSelectBinder(binder, context, node), column_alias_binder(node.bind_state),
15
0
      aggregate_handling(aggregate_handling) {
16
0
  target_type = LogicalType(LogicalTypeId::BOOLEAN);
17
0
}
18
19
0
BindResult HavingBinder::BindLambdaReference(LambdaRefExpression &expr, idx_t depth) {
20
0
  D_ASSERT(lambda_bindings && expr.LambdaIndex() < lambda_bindings->size());
21
0
  auto &lambda_ref = expr.Cast<LambdaRefExpression>();
22
0
  return (*lambda_bindings)[expr.LambdaIndex()].Bind(lambda_ref, depth);
23
0
}
24
25
0
BindResult HavingBinder::BindColumnRef(unique_ptr<ParsedExpression> &expr_ptr, idx_t depth, bool root_expression) {
26
  // Keep the original column name to return a meaningful error message.
27
0
  auto col_ref = expr_ptr->Cast<ColumnRefExpression>();
28
0
  const auto &column_name = col_ref.GetColumnName();
29
30
0
  if (!col_ref.IsQualified()) {
31
    // Try binding as a lambda parameter.
32
0
    auto lambda_ref = LambdaRefExpression::FindMatchingBinding(lambda_bindings, col_ref.GetColumnName());
33
0
    if (lambda_ref) {
34
0
      return BindLambdaReference(lambda_ref->Cast<LambdaRefExpression>(), depth);
35
0
    }
36
    // column was not found - check if it is a SQL value function
37
0
    auto value_function = GetSQLValueFunction(col_ref.GetColumnName());
38
0
    if (value_function) {
39
0
      return BindExpression(value_function, depth);
40
0
    }
41
0
  }
42
43
  // Bind the alias.
44
0
  BindResult alias_result;
45
0
  auto found_alias = column_alias_binder.BindAlias(*this, expr_ptr, depth, root_expression, alias_result);
46
0
  if (found_alias) {
47
0
    if (depth > 0) {
48
0
      throw BinderException("Having clause cannot reference alias \"%s\" in correlated subquery", column_name);
49
0
    }
50
0
    return alias_result;
51
0
  }
52
53
0
  if (aggregate_handling != AggregateHandling::FORCE_AGGREGATES) {
54
0
    return BindResult(StringUtil::Format(
55
0
        "column %s must appear in the GROUP BY clause or be used in an aggregate function", column_name));
56
0
  }
57
58
0
  if (depth > 0) {
59
0
    throw BinderException("Having clause cannot reference column \"%s\" in correlated subquery and group by all",
60
0
                          column_name);
61
0
  }
62
63
0
  auto expr = duckdb::BaseSelectBinder::BindColumnRef(expr_ptr, depth, root_expression);
64
0
  if (expr.HasError()) {
65
0
    return expr;
66
0
  }
67
68
  // Return a GROUP BY column reference expression.
69
0
  auto return_type = expr.expression->GetReturnType();
70
0
  auto group_idx = ColumnBinding::PushExpression(node.groups.group_expressions, std::move(expr.expression));
71
0
  auto column_binding = ColumnBinding(node.group_index, group_idx);
72
0
  auto group_ref = make_uniq<BoundColumnRefExpression>(return_type, column_binding);
73
0
  return BindResult(std::move(group_ref));
74
0
}
75
76
0
BindResult HavingBinder::BindWindowExpression(WindowExpression &expr, idx_t depth) {
77
0
  throw BinderException::Unsupported(expr, "HAVING clause cannot contain window functions!");
78
0
}
79
80
0
void ExpressionBinder::QualifyColumnNames(HavingBinder &having_binder, unique_ptr<ParsedExpression> &expr) {
81
0
  ColumnQualifier qualifier(having_binder.binder, having_binder.lambda_bindings, nullptr, having_binder);
82
0
  vector<identifier_set_t> lambda_params;
83
0
  qualifier.QualifyColumnNames(expr, lambda_params);
84
0
}
85
86
} // namespace duckdb