1use crate::ast::{AggregateFunc, BinaryOp, Condition, Expr, Operator, Value};
17
18pub fn col(name: &str) -> Expr {
22 Expr::Named(name.to_string())
23}
24
25pub fn star() -> Expr {
27 Expr::Star
28}
29
30pub fn count() -> AggregateBuilder {
34 AggregateBuilder {
35 col: "*".to_string(),
36 func: AggregateFunc::Count,
37 distinct: false,
38 filter: None,
39 alias: None,
40 }
41}
42
43pub fn count_distinct(column: &str) -> AggregateBuilder {
45 AggregateBuilder {
46 col: column.to_string(),
47 func: AggregateFunc::Count,
48 distinct: true,
49 filter: None,
50 alias: None,
51 }
52}
53
54pub fn count_filter(conditions: Vec<Condition>) -> AggregateBuilder {
56 AggregateBuilder {
57 col: "*".to_string(),
58 func: AggregateFunc::Count,
59 distinct: false,
60 filter: Some(conditions),
61 alias: None,
62 }
63}
64
65pub fn sum(column: &str) -> AggregateBuilder {
67 AggregateBuilder {
68 col: column.to_string(),
69 func: AggregateFunc::Sum,
70 distinct: false,
71 filter: None,
72 alias: None,
73 }
74}
75
76pub fn avg(column: &str) -> AggregateBuilder {
78 AggregateBuilder {
79 col: column.to_string(),
80 func: AggregateFunc::Avg,
81 distinct: false,
82 filter: None,
83 alias: None,
84 }
85}
86
87pub fn min(column: &str) -> AggregateBuilder {
89 AggregateBuilder {
90 col: column.to_string(),
91 func: AggregateFunc::Min,
92 distinct: false,
93 filter: None,
94 alias: None,
95 }
96}
97
98pub fn max(column: &str) -> AggregateBuilder {
100 AggregateBuilder {
101 col: column.to_string(),
102 func: AggregateFunc::Max,
103 distinct: false,
104 filter: None,
105 alias: None,
106 }
107}
108
109#[derive(Debug, Clone)]
111pub struct AggregateBuilder {
112 col: String,
113 func: AggregateFunc,
114 distinct: bool,
115 filter: Option<Vec<Condition>>,
116 alias: Option<String>,
117}
118
119impl AggregateBuilder {
120 pub fn distinct(mut self) -> Self {
122 self.distinct = true;
123 self
124 }
125
126 pub fn filter(mut self, conditions: Vec<Condition>) -> Self {
128 self.filter = Some(conditions);
129 self
130 }
131
132 pub fn alias(mut self, name: &str) -> Expr {
134 self.alias = Some(name.to_string());
135 self.build()
136 }
137
138 pub fn build(self) -> Expr {
140 Expr::Aggregate {
141 col: self.col,
142 func: self.func,
143 distinct: self.distinct,
144 filter: self.filter,
145 alias: self.alias,
146 }
147 }
148}
149
150impl From<AggregateBuilder> for Expr {
151 fn from(builder: AggregateBuilder) -> Self {
152 builder.build()
153 }
154}
155
156pub fn now() -> Expr {
160 Expr::FunctionCall {
161 name: "NOW".to_string(),
162 args: vec![],
163 alias: None,
164 }
165}
166
167pub fn interval(duration: &str) -> Expr {
169 Expr::SpecialFunction {
170 name: "INTERVAL".to_string(),
171 args: vec![(None, Box::new(Expr::Named(format!("'{}'", duration))))],
172 alias: None,
173 }
174}
175
176pub fn now_minus(duration: &str) -> Expr {
178 Expr::Binary {
179 left: Box::new(now()),
180 op: BinaryOp::Sub,
181 right: Box::new(interval(duration)),
182 alias: None,
183 }
184}
185
186pub fn now_plus(duration: &str) -> Expr {
188 Expr::Binary {
189 left: Box::new(now()),
190 op: BinaryOp::Add,
191 right: Box::new(interval(duration)),
192 alias: None,
193 }
194}
195
196pub fn cast(expr: impl Into<Expr>, target_type: &str) -> CastBuilder {
200 CastBuilder {
201 expr: expr.into(),
202 target_type: target_type.to_string(),
203 alias: None,
204 }
205}
206
207#[derive(Debug, Clone)]
209pub struct CastBuilder {
210 expr: Expr,
211 target_type: String,
212 alias: Option<String>,
213}
214
215impl CastBuilder {
216 pub fn alias(mut self, name: &str) -> Expr {
218 self.alias = Some(name.to_string());
219 self.build()
220 }
221
222 pub fn build(self) -> Expr {
224 Expr::Cast {
225 expr: Box::new(self.expr),
226 target_type: self.target_type,
227 alias: self.alias,
228 }
229 }
230}
231
232impl From<CastBuilder> for Expr {
233 fn from(builder: CastBuilder) -> Self {
234 builder.build()
235 }
236}
237
238pub fn case_when(condition: Condition, then_expr: impl Into<Expr>) -> CaseBuilder {
242 CaseBuilder {
243 when_clauses: vec![(condition, Box::new(then_expr.into()))],
244 else_value: None,
245 alias: None,
246 }
247}
248
249#[derive(Debug, Clone)]
251pub struct CaseBuilder {
252 when_clauses: Vec<(Condition, Box<Expr>)>,
253 else_value: Option<Box<Expr>>,
254 alias: Option<String>,
255}
256
257impl CaseBuilder {
258 pub fn when(mut self, condition: Condition, then_expr: impl Into<Expr>) -> Self {
260 self.when_clauses.push((condition, Box::new(then_expr.into())));
261 self
262 }
263
264 pub fn otherwise(mut self, else_expr: impl Into<Expr>) -> Self {
266 self.else_value = Some(Box::new(else_expr.into()));
267 self
268 }
269
270 pub fn alias(mut self, name: &str) -> Expr {
272 self.alias = Some(name.to_string());
273 self.build()
274 }
275
276 pub fn build(self) -> Expr {
278 Expr::Case {
279 when_clauses: self.when_clauses,
280 else_value: self.else_value,
281 alias: self.alias,
282 }
283 }
284}
285
286impl From<CaseBuilder> for Expr {
287 fn from(builder: CaseBuilder) -> Self {
288 builder.build()
289 }
290}
291
292pub fn binary(left: impl Into<Expr>, op: BinaryOp, right: impl Into<Expr>) -> BinaryBuilder {
296 BinaryBuilder {
297 left: left.into(),
298 op,
299 right: right.into(),
300 alias: None,
301 }
302}
303
304#[derive(Debug, Clone)]
306pub struct BinaryBuilder {
307 left: Expr,
308 op: BinaryOp,
309 right: Expr,
310 alias: Option<String>,
311}
312
313impl BinaryBuilder {
314 pub fn alias(mut self, name: &str) -> Expr {
316 self.alias = Some(name.to_string());
317 self.build()
318 }
319
320 pub fn build(self) -> Expr {
322 Expr::Binary {
323 left: Box::new(self.left),
324 op: self.op,
325 right: Box::new(self.right),
326 alias: self.alias,
327 }
328 }
329}
330
331impl From<BinaryBuilder> for Expr {
332 fn from(builder: BinaryBuilder) -> Self {
333 builder.build()
334 }
335}
336
337fn make_condition(column: &str, op: Operator, value: Value) -> Condition {
341 Condition {
342 left: Expr::Named(column.to_string()),
343 op,
344 value,
345 is_array_unnest: false,
346 }
347}
348
349pub fn eq(column: &str, value: impl Into<Value>) -> Condition {
351 make_condition(column, Operator::Eq, value.into())
352}
353
354pub fn ne(column: &str, value: impl Into<Value>) -> Condition {
356 make_condition(column, Operator::Ne, value.into())
357}
358
359pub fn gt(column: &str, value: impl Into<Value>) -> Condition {
361 make_condition(column, Operator::Gt, value.into())
362}
363
364pub fn gte(column: &str, value: impl Into<Value>) -> Condition {
366 make_condition(column, Operator::Gte, value.into())
367}
368
369pub fn lt(column: &str, value: impl Into<Value>) -> Condition {
371 make_condition(column, Operator::Lt, value.into())
372}
373
374pub fn lte(column: &str, value: impl Into<Value>) -> Condition {
376 make_condition(column, Operator::Lte, value.into())
377}
378
379pub fn is_in<V: Into<Value>>(column: &str, values: impl IntoIterator<Item = V>) -> Condition {
381 let vals: Vec<Value> = values.into_iter().map(|v| v.into()).collect();
382 make_condition(column, Operator::In, Value::Array(vals))
383}
384
385pub fn not_in<V: Into<Value>>(column: &str, values: impl IntoIterator<Item = V>) -> Condition {
387 let vals: Vec<Value> = values.into_iter().map(|v| v.into()).collect();
388 make_condition(column, Operator::NotIn, Value::Array(vals))
389}
390
391pub fn is_null(column: &str) -> Condition {
393 make_condition(column, Operator::IsNull, Value::Null)
394}
395
396pub fn is_not_null(column: &str) -> Condition {
398 make_condition(column, Operator::IsNotNull, Value::Null)
399}
400
401pub fn like(column: &str, pattern: &str) -> Condition {
403 make_condition(column, Operator::Like, Value::String(pattern.to_string()))
404}
405
406pub fn ilike(column: &str, pattern: &str) -> Condition {
408 make_condition(column, Operator::ILike, Value::String(pattern.to_string()))
409}
410
411pub fn func(name: &str, args: Vec<Expr>) -> FunctionBuilder {
415 FunctionBuilder {
416 name: name.to_string(),
417 args,
418 alias: None,
419 }
420}
421
422pub fn coalesce(args: Vec<Expr>) -> FunctionBuilder {
424 func("COALESCE", args)
425}
426
427pub fn nullif(a: impl Into<Expr>, b: impl Into<Expr>) -> FunctionBuilder {
429 func("NULLIF", vec![a.into(), b.into()])
430}
431
432#[derive(Debug, Clone)]
434pub struct FunctionBuilder {
435 name: String,
436 args: Vec<Expr>,
437 alias: Option<String>,
438}
439
440impl FunctionBuilder {
441 pub fn alias(mut self, name: &str) -> Expr {
443 self.alias = Some(name.to_string());
444 self.build()
445 }
446
447 pub fn build(self) -> Expr {
449 Expr::FunctionCall {
450 name: self.name,
451 args: self.args,
452 alias: self.alias,
453 }
454 }
455}
456
457impl From<FunctionBuilder> for Expr {
458 fn from(builder: FunctionBuilder) -> Self {
459 builder.build()
460 }
461}
462
463pub fn int(value: i64) -> Expr {
467 Expr::Named(value.to_string())
468}
469
470pub fn float(value: f64) -> Expr {
472 Expr::Named(value.to_string())
473}
474
475pub fn text(value: &str) -> Expr {
477 Expr::Named(format!("'{}'", value))
478}
479
480pub trait ExprExt {
484 fn as_alias(self, alias: &str) -> Expr;
486}
487
488impl ExprExt for Expr {
489 fn as_alias(self, alias: &str) -> Expr {
490 match self {
491 Expr::Named(name) => Expr::Aliased { name, alias: alias.to_string() },
492 Expr::Aggregate { col, func, distinct, filter, .. } => {
493 Expr::Aggregate { col, func, distinct, filter, alias: Some(alias.to_string()) }
494 }
495 Expr::Cast { expr, target_type, .. } => {
496 Expr::Cast { expr, target_type, alias: Some(alias.to_string()) }
497 }
498 Expr::Case { when_clauses, else_value, .. } => {
499 Expr::Case { when_clauses, else_value, alias: Some(alias.to_string()) }
500 }
501 Expr::FunctionCall { name, args, .. } => {
502 Expr::FunctionCall { name, args, alias: Some(alias.to_string()) }
503 }
504 Expr::Binary { left, op, right, .. } => {
505 Expr::Binary { left, op, right, alias: Some(alias.to_string()) }
506 }
507 other => other, }
509 }
510}
511
512#[cfg(test)]
513mod tests {
514 use super::*;
515
516 #[test]
517 fn test_count_filter() {
518 let expr = count_filter(vec![
519 eq("direction", "outbound"),
520 ]).alias("sent_count");
521
522 assert!(matches!(expr, Expr::Aggregate { alias: Some(a), .. } if a == "sent_count"));
523 }
524
525 #[test]
526 fn test_now_minus() {
527 let expr = now_minus("24 hours");
528 assert!(matches!(expr, Expr::Binary { op: BinaryOp::Sub, .. }));
529 }
530
531 #[test]
532 fn test_case_when() {
533 let expr = case_when(gt("x", 0), int(1))
534 .otherwise(int(0))
535 .alias("result");
536
537 assert!(matches!(expr, Expr::Case { alias: Some(a), .. } if a == "result"));
538 }
539
540 #[test]
541 fn test_cast() {
542 let expr = cast(col("value"), "float8").alias("value_f");
543 assert!(matches!(expr, Expr::Cast { target_type, .. } if target_type == "float8"));
544 }
545}