keelson_core/clause/
having.rs1use crate::expr::{Expr, IntoExpr};
2use crate::writer::{Expression, SqlWriter};
3
4#[derive(Debug, Clone, Default)]
10pub struct Having {
11 pub conditions: Vec<Expr>,
13}
14
15impl Having {
16 pub fn append_having(&mut self, condition: impl IntoExpr) {
18 self.conditions.push(condition.into_expr());
19 }
20
21 pub fn is_empty(&self) -> bool {
23 self.conditions.is_empty()
24 }
25}
26
27impl Expression for Having {
28 fn write_sql(&self, w: &mut SqlWriter<'_>) {
29 w.write_slice(&self.conditions, "HAVING ", " AND ", "");
30 }
31}
32
33pub trait HasHaving {
35 fn having_mut(&mut self) -> &mut Having;
37}
38
39impl HasHaving for Having {
40 fn having_mut(&mut self) -> &mut Having {
41 self
42 }
43}
44
45#[cfg(test)]
46mod tests {
47 use keelson_sqlcheck::testing::assert_frag_sql;
48
49 use super::*;
50 use crate::dialect::testing::Numbered;
51 use crate::expr::{Chain, arg};
52 use crate::value::Value;
53 use crate::writer::build;
54
55 const FRAME: &str = "SELECT count(*) FROM users {}";
59
60 #[test]
61 fn an_empty_having_writes_nothing() {
62 assert_frag_sql(FRAME, &build(&Numbered, &Having::default()).unwrap().0, "");
63 assert!(Having::default().is_empty());
64 }
65
66 #[test]
67 fn conditions_are_and_joined() {
68 let mut h = Having::default();
69 h.append_having(Expr::func("count", "1").gt(arg(1i32)));
70 h.append_having("sum(age) < 9");
71 let (sql, args) = build(&Numbered, &h).unwrap();
72 assert_frag_sql(FRAME, &sql, "HAVING (count(1) > $1) AND sum(age) < 9");
73 assert_eq!(args, vec![Value::I32(1)]);
74 }
75}