1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
//! CASE expression evaluation in aggregate context
use super::super::super::builder::SelectExecutor;
use crate::{
errors::ExecutorError,
evaluator::{CombinedExpressionEvaluator, ExpressionEvaluator},
};
/// Evaluate CASE expression with potential aggregates in operand/conditions/results
///
/// Handles both:
/// - Simple CASE: CASE COUNT(*) WHEN 5 THEN 'five' END
/// - Searched CASE: CASE WHEN COUNT(*) > 5 THEN 'many' END
pub(super) fn evaluate(
executor: &SelectExecutor,
operand: &Option<Box<vibesql_ast::Expression>>,
when_clauses: &[vibesql_ast::CaseWhen],
else_result: &Option<Box<vibesql_ast::Expression>>,
group_rows: &[vibesql_storage::Row],
group_key: &[vibesql_types::SqlValue],
evaluator: &CombinedExpressionEvaluator,
) -> Result<vibesql_types::SqlValue, ExecutorError> {
match operand {
// Simple CASE: CASE operand WHEN value THEN result ...
Some(operand_expr) => {
// Evaluate operand (may contain aggregates like COUNT(*))
let operand_value = executor.evaluate_with_aggregates(
operand_expr,
group_rows,
group_key,
evaluator,
)?;
for when_clause in when_clauses {
// Check if ANY condition matches (OR logic)
for condition_expr in &when_clause.conditions {
// Evaluate condition (may contain aggregates)
let when_value = executor.evaluate_with_aggregates(
condition_expr,
group_rows,
group_key,
evaluator,
)?;
// Use IS NOT DISTINCT FROM semantics (NULL = NULL is TRUE)
if ExpressionEvaluator::values_are_equal(&operand_value, &when_value) {
// Evaluate result (may contain aggregates)
return executor.evaluate_with_aggregates(
&when_clause.result,
group_rows,
group_key,
evaluator,
);
}
}
}
// No match - evaluate ELSE clause if present
if let Some(else_expr) = else_result {
executor.evaluate_with_aggregates(else_expr, group_rows, group_key, evaluator)
} else {
Ok(vibesql_types::SqlValue::Null)
}
}
// Searched CASE: CASE WHEN condition THEN result ...
None => {
for when_clause in when_clauses {
// Each when_clause can have multiple conditions (OR logic within a clause)
for condition_expr in &when_clause.conditions {
// Evaluate condition (may contain aggregates)
let condition_value = executor.evaluate_with_aggregates(
condition_expr,
group_rows,
group_key,
evaluator,
)?;
// Check if condition is TRUE (not FALSE or NULL)
let is_true = match condition_value {
vibesql_types::SqlValue::Boolean(true) => true,
vibesql_types::SqlValue::Boolean(false) | vibesql_types::SqlValue::Null => {
false
}
// SQLLogicTest compatibility: treat integers as truthy/falsy
vibesql_types::SqlValue::Integer(0) => false,
vibesql_types::SqlValue::Integer(_) => true,
vibesql_types::SqlValue::Smallint(0) => false,
vibesql_types::SqlValue::Smallint(_) => true,
vibesql_types::SqlValue::Bigint(0) => false,
vibesql_types::SqlValue::Bigint(_) => true,
vibesql_types::SqlValue::Float(0.0) => false,
vibesql_types::SqlValue::Float(_) => true,
vibesql_types::SqlValue::Real(0.0) => false,
vibesql_types::SqlValue::Real(_) => true,
vibesql_types::SqlValue::Double(0.0) => false,
vibesql_types::SqlValue::Double(_) => true,
other => {
return Err(ExecutorError::UnsupportedExpression(format!(
"CASE condition must evaluate to boolean, got: {:?}",
other
)))
}
};
if is_true {
// Evaluate result (may contain aggregates)
return executor.evaluate_with_aggregates(
&when_clause.result,
group_rows,
group_key,
evaluator,
);
}
}
}
// No match - evaluate ELSE clause if present
if let Some(else_expr) = else_result {
executor.evaluate_with_aggregates(else_expr, group_rows, group_key, evaluator)
} else {
Ok(vibesql_types::SqlValue::Null)
}
}
}
}