gluesql_core/ast/
function.rs

1use {
2    super::{ast_literal::TrimWhereField, DataType, DateTimeField, Expr},
3    crate::ast::ToSql,
4    serde::{Deserialize, Serialize},
5    strum_macros::Display,
6};
7
8#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize, Display)]
9#[strum(serialize_all = "SCREAMING_SNAKE_CASE")]
10pub enum Function {
11    Abs(Expr),
12    AddMonth {
13        expr: Expr,
14        size: Expr,
15    },
16    Lower(Expr),
17    Initcap(Expr),
18    Upper(Expr),
19    Left {
20        expr: Expr,
21        size: Expr,
22    },
23    Right {
24        expr: Expr,
25        size: Expr,
26    },
27    Asin(Expr),
28    Acos(Expr),
29    Atan(Expr),
30    Lpad {
31        expr: Expr,
32        size: Expr,
33        fill: Option<Expr>,
34    },
35    Rpad {
36        expr: Expr,
37        size: Expr,
38        fill: Option<Expr>,
39    },
40    Replace {
41        expr: Expr,
42        old: Expr,
43        new: Expr,
44    },
45    Cast {
46        expr: Expr,
47        data_type: DataType,
48    },
49    Ceil(Expr),
50    Coalesce(Vec<Expr>),
51    Concat(Vec<Expr>),
52    ConcatWs {
53        separator: Expr,
54        exprs: Vec<Expr>,
55    },
56    Custom {
57        name: String,
58        exprs: Vec<Expr>,
59    },
60    IfNull {
61        expr: Expr,
62        then: Expr,
63    },
64    Rand(Option<Expr>),
65    Round(Expr),
66    Floor(Expr),
67    Trim {
68        expr: Expr,
69        filter_chars: Option<Expr>,
70        trim_where_field: Option<TrimWhereField>,
71    },
72    Exp(Expr),
73    Extract {
74        field: DateTimeField,
75        expr: Expr,
76    },
77    Ln(Expr),
78    Log {
79        antilog: Expr,
80        base: Expr,
81    },
82    Log2(Expr),
83    Log10(Expr),
84    Div {
85        dividend: Expr,
86        divisor: Expr,
87    },
88    Mod {
89        dividend: Expr,
90        divisor: Expr,
91    },
92    Gcd {
93        left: Expr,
94        right: Expr,
95    },
96    Lcm {
97        left: Expr,
98        right: Expr,
99    },
100    Sin(Expr),
101    Cos(Expr),
102    Tan(Expr),
103    Sqrt(Expr),
104    Power {
105        expr: Expr,
106        power: Expr,
107    },
108    Radians(Expr),
109    Degrees(Expr),
110    Now(),
111    Pi(),
112    LastDay(Expr),
113    Ltrim {
114        expr: Expr,
115        chars: Option<Expr>,
116    },
117    Rtrim {
118        expr: Expr,
119        chars: Option<Expr>,
120    },
121    Reverse(Expr),
122    Repeat {
123        expr: Expr,
124        num: Expr,
125    },
126    Sign(Expr),
127    Substr {
128        expr: Expr,
129        start: Expr,
130        count: Option<Expr>,
131    },
132    Unwrap {
133        expr: Expr,
134        selector: Expr,
135    },
136    GenerateUuid(),
137    Greatest(Vec<Expr>),
138    Format {
139        expr: Expr,
140        format: Expr,
141    },
142    ToDate {
143        expr: Expr,
144        format: Expr,
145    },
146    ToTimestamp {
147        expr: Expr,
148        format: Expr,
149    },
150    ToTime {
151        expr: Expr,
152        format: Expr,
153    },
154    Position {
155        from_expr: Expr,
156        sub_expr: Expr,
157    },
158    FindIdx {
159        from_expr: Expr,
160        sub_expr: Expr,
161        start: Option<Expr>,
162    },
163    Ascii(Expr),
164    Chr(Expr),
165    Md5(Expr),
166    Append {
167        expr: Expr,
168        value: Expr,
169    },
170    Sort {
171        expr: Expr,
172        order: Option<Expr>,
173    },
174    Slice {
175        expr: Expr,
176        start: Expr,
177        length: Expr,
178    },
179    Prepend {
180        expr: Expr,
181        value: Expr,
182    },
183    Skip {
184        expr: Expr,
185        size: Expr,
186    },
187    Take {
188        expr: Expr,
189        size: Expr,
190    },
191    GetX(Expr),
192    GetY(Expr),
193    Point {
194        x: Expr,
195        y: Expr,
196    },
197    CalcDistance {
198        geometry1: Expr,
199        geometry2: Expr,
200    },
201    IsEmpty(Expr),
202    Length(Expr),
203    Entries(Expr),
204    Keys(Expr),
205    Values(Expr),
206    Splice {
207        list_data: Expr,
208        begin_index: Expr,
209        end_index: Expr,
210        values: Option<Expr>,
211    },
212    Dedup(Expr),
213}
214
215impl ToSql for Function {
216    fn to_sql(&self) -> String {
217        match self {
218            Function::Abs(e) => format!("ABS({})", e.to_sql()),
219            Function::AddMonth { expr, size } => {
220                format!("ADD_MONTH({},{})", expr.to_sql(), size.to_sql())
221            }
222            Function::Initcap(e) => format!("INITCAP({})", e.to_sql()),
223            Function::Lower(e) => format!("LOWER({})", e.to_sql()),
224            Function::Upper(e) => format!("UPPER({})", e.to_sql()),
225            Function::Left { expr, size } => format!("LEFT({}, {})", expr.to_sql(), size.to_sql()),
226            Function::Right { expr, size } => {
227                format!("RIGHT({}, {})", expr.to_sql(), size.to_sql())
228            }
229            Function::Asin(e) => format!("ASIN({})", e.to_sql()),
230            Function::Acos(e) => format!("ACOS({})", e.to_sql()),
231            Function::Atan(e) => format!("ATAN({})", e.to_sql()),
232            Function::Lpad { expr, size, fill } => match fill {
233                None => format!("LPAD({}, {})", expr.to_sql(), size.to_sql()),
234                Some(fill) => format!(
235                    "LPAD({}, {}, {})",
236                    expr.to_sql(),
237                    size.to_sql(),
238                    fill.to_sql()
239                ),
240            },
241            Function::Rpad { expr, size, fill } => match fill {
242                None => format!("RPAD({}, {})", expr.to_sql(), size.to_sql()),
243                Some(fill) => format!(
244                    "RPAD({}, {}, {})",
245                    expr.to_sql(),
246                    size.to_sql(),
247                    fill.to_sql()
248                ),
249            },
250            Function::Cast { expr, data_type } => {
251                format!("CAST({} AS {data_type})", expr.to_sql())
252            }
253            Function::Ceil(e) => format!("CEIL({})", e.to_sql()),
254            Function::Coalesce(items) => {
255                let items = items
256                    .iter()
257                    .map(ToSql::to_sql)
258                    .collect::<Vec<_>>()
259                    .join(", ");
260                format!("COALESCE({items})")
261            }
262            Function::Concat(items) => {
263                let items = items
264                    .iter()
265                    .map(ToSql::to_sql)
266                    .collect::<Vec<_>>()
267                    .join(", ");
268                format!("CONCAT({items})")
269            }
270            Function::Custom { name, exprs } => {
271                let exprs = exprs
272                    .iter()
273                    .map(ToSql::to_sql)
274                    .collect::<Vec<_>>()
275                    .join(", ");
276                format!("{name}({exprs})")
277            }
278            Function::ConcatWs { separator, exprs } => {
279                let exprs = exprs
280                    .iter()
281                    .map(ToSql::to_sql)
282                    .collect::<Vec<_>>()
283                    .join(", ");
284                format!("CONCAT_WS({}, {})", separator.to_sql(), exprs)
285            }
286            Function::IfNull { expr, then } => {
287                format!("IFNULL({}, {})", expr.to_sql(), then.to_sql())
288            }
289            Function::Rand(e) => match e {
290                Some(v) => format!("RAND({})", v.to_sql()),
291                None => "RAND()".to_owned(),
292            },
293            Function::Round(e) => format!("ROUND({})", e.to_sql()),
294            Function::Floor(e) => format!("FLOOR({})", e.to_sql()),
295            Function::Trim {
296                expr,
297                filter_chars,
298                trim_where_field,
299            } => {
300                let trim_where_field = match trim_where_field {
301                    None => "".to_owned(),
302                    Some(t) => format!("{t} "),
303                };
304
305                match filter_chars {
306                    None => format!("TRIM({}{})", trim_where_field, expr.to_sql()),
307                    Some(filter_chars) => format!(
308                        "TRIM({}{} FROM {})",
309                        trim_where_field,
310                        filter_chars.to_sql(),
311                        expr.to_sql()
312                    ),
313                }
314            }
315            Function::Exp(e) => format!("EXP({})", e.to_sql()),
316            Function::Ln(e) => format!("LN({})", e.to_sql()),
317            Function::Log { antilog, base } => {
318                format!("LOG({}, {})", antilog.to_sql(), base.to_sql())
319            }
320            Function::Log2(e) => format!("LOG2({})", e.to_sql()),
321            Function::Log10(e) => format!("LOG10({})", e.to_sql()),
322            Function::Div { dividend, divisor } => {
323                format!("DIV({}, {})", dividend.to_sql(), divisor.to_sql())
324            }
325            Function::Mod { dividend, divisor } => {
326                format!("MOD({}, {})", dividend.to_sql(), divisor.to_sql())
327            }
328            Function::Gcd { left, right } => format!("GCD({}, {})", left.to_sql(), right.to_sql()),
329            Function::Lcm { left, right } => format!("LCM({}, {})", left.to_sql(), right.to_sql()),
330            Function::Sin(e) => format!("SIN({})", e.to_sql()),
331            Function::Cos(e) => format!("COS({})", e.to_sql()),
332            Function::Tan(e) => format!("TAN({})", e.to_sql()),
333            Function::Sqrt(e) => format!("SQRT({})", e.to_sql()),
334            Function::Power { expr, power } => {
335                format!("POWER({}, {})", expr.to_sql(), power.to_sql())
336            }
337            Function::Radians(e) => format!("RADIANS({})", e.to_sql()),
338            Function::Degrees(e) => format!("DEGREES({})", e.to_sql()),
339            Function::Now() => "NOW()".to_owned(),
340            Function::Pi() => "PI()".to_owned(),
341            Function::LastDay(expr) => format!("LAST_DAY({})", expr.to_sql()),
342            Function::Ltrim { expr, chars } => match chars {
343                None => format!("LTRIM({})", expr.to_sql()),
344                Some(chars) => format!("LTRIM({}, {})", expr.to_sql(), chars.to_sql()),
345            },
346            Function::Rtrim { expr, chars } => match chars {
347                None => format!("RTRIM({})", expr.to_sql()),
348                Some(chars) => format!("RTRIM({}, {})", expr.to_sql(), chars.to_sql()),
349            },
350            Function::Reverse(e) => format!("REVERSE({})", e.to_sql()),
351            Function::Repeat { expr, num } => {
352                format!("REPEAT({}, {})", expr.to_sql(), num.to_sql())
353            }
354            Function::Replace { expr, old, new } => format!(
355                "REPLACE({},{},{})",
356                expr.to_sql(),
357                old.to_sql(),
358                new.to_sql()
359            ),
360
361            Function::Sign(e) => format!("SIGN({})", e.to_sql()),
362            Function::Substr { expr, start, count } => match count {
363                None => format!("SUBSTR({}, {})", expr.to_sql(), start.to_sql()),
364                Some(count) => format!(
365                    "SUBSTR({}, {}, {})",
366                    expr.to_sql(),
367                    start.to_sql(),
368                    count.to_sql()
369                ),
370            },
371            Function::Unwrap { expr, selector } => {
372                format!("UNWRAP({}, {})", expr.to_sql(), selector.to_sql())
373            }
374            Function::GenerateUuid() => "GENERATE_UUID()".to_owned(),
375            Function::Greatest(items) => {
376                let items = items
377                    .iter()
378                    .map(ToSql::to_sql)
379                    .collect::<Vec<_>>()
380                    .join(", ");
381                format!("GREATEST({})", items)
382            }
383            Function::Format { expr, format } => {
384                format!("FORMAT({}, {})", expr.to_sql(), format.to_sql())
385            }
386            Function::ToDate { expr, format } => {
387                format!("TO_DATE({}, {})", expr.to_sql(), format.to_sql())
388            }
389            Function::ToTimestamp { expr, format } => {
390                format!("TO_TIMESTAMP({}, {})", expr.to_sql(), format.to_sql())
391            }
392            Function::ToTime { expr, format } => {
393                format!("TO_TIME({}, {})", expr.to_sql(), format.to_sql())
394            }
395            Function::Position {
396                from_expr,
397                sub_expr,
398            } => format!("POSITION({} IN {})", sub_expr.to_sql(), from_expr.to_sql()),
399            Function::FindIdx {
400                from_expr,
401                sub_expr,
402                start,
403            } => match start {
404                None => format!("FIND_IDX({}, {})", from_expr.to_sql(), sub_expr.to_sql()),
405                Some(start_expr) => format!(
406                    "FIND_IDX({}, {}, {})",
407                    from_expr.to_sql(),
408                    sub_expr.to_sql(),
409                    start_expr.to_sql()
410                ),
411            },
412            Function::Extract { field, expr } => {
413                format!("EXTRACT({field} FROM {})", expr.to_sql())
414            }
415            Function::Ascii(e) => format!("ASCII({})", e.to_sql()),
416            Function::Chr(e) => format!("CHR({})", e.to_sql()),
417            Function::Md5(e) => format!("MD5({})", e.to_sql()),
418            Function::Append { expr, value } => {
419                format!(
420                    "APPEND({items}, {value})",
421                    items = expr.to_sql(),
422                    value = value.to_sql()
423                )
424            }
425            Function::Prepend { expr, value } => {
426                format! {
427                    "PREPEND({items}, {value})",
428                    items = expr.to_sql(),
429                    value = value.to_sql()
430                }
431            }
432            Function::Skip { expr, size } => {
433                format!("SKIP({}, {})", expr.to_sql(), size.to_sql())
434            }
435            Function::Sort { expr, order } => match order {
436                None => format!("SORT({})", expr.to_sql()),
437                Some(order) => {
438                    format!("SORT({}, {})", expr.to_sql(), order.to_sql())
439                }
440            },
441            Function::Slice {
442                expr,
443                start,
444                length,
445            } => {
446                format!(
447                    "SLICE({}, {}, {})",
448                    expr.to_sql(),
449                    start.to_sql(),
450                    length.to_sql()
451                )
452            }
453            Function::Take { expr, size } => {
454                format!("TAKE({}, {})", expr.to_sql(), size.to_sql())
455            }
456            Function::GetX(e) => format!("GET_X({})", e.to_sql()),
457            Function::GetY(e) => format!("GET_Y({})", e.to_sql()),
458            Function::Point { x, y } => format!("POINT({}, {})", x.to_sql(), y.to_sql()),
459            Function::CalcDistance {
460                geometry1,
461                geometry2,
462            } => {
463                format!(
464                    "CALC_DISTANCE({}, {})",
465                    geometry1.to_sql(),
466                    geometry2.to_sql()
467                )
468            }
469            Function::IsEmpty(e) => format!("IS_EMPTY({})", e.to_sql()),
470            Function::Length(e) => format!("LENGTH({})", e.to_sql()),
471            Function::Entries(e) => format!("ENTRIES({})", e.to_sql()),
472            Function::Keys(e) => format!("KEYS({})", e.to_sql()),
473            Function::Values(e) => format!("VALUES({})", e.to_sql()),
474            Function::Splice {
475                list_data,
476                begin_index,
477                end_index,
478                values,
479            } => match values {
480                Some(v) => format!(
481                    "SPLICE({}, {}, {}, {})",
482                    list_data.to_sql(),
483                    begin_index.to_sql(),
484                    end_index.to_sql(),
485                    v.to_sql()
486                ),
487                None => format!(
488                    "SPLICE({}, {}, {})",
489                    list_data.to_sql(),
490                    begin_index.to_sql(),
491                    end_index.to_sql(),
492                ),
493            },
494            Function::Dedup(list) => format!("DEDUP({})", list.to_sql()),
495        }
496    }
497}
498
499#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
500pub enum Aggregate {
501    Count(CountArgExpr),
502    Sum(Expr),
503    Max(Expr),
504    Min(Expr),
505    Avg(Expr),
506    Variance(Expr),
507    Stdev(Expr),
508}
509
510impl ToSql for Aggregate {
511    fn to_sql(&self) -> String {
512        match self {
513            Aggregate::Count(cae) => format!("COUNT({})", cae.to_sql()),
514            Aggregate::Sum(e) => format!("SUM({})", e.to_sql()),
515            Aggregate::Max(e) => format!("MAX({})", e.to_sql()),
516            Aggregate::Min(e) => format!("MIN({})", e.to_sql()),
517            Aggregate::Avg(e) => format!("AVG({})", e.to_sql()),
518            Aggregate::Variance(e) => format!("VARIANCE({})", e.to_sql()),
519            Aggregate::Stdev(e) => format!("STDEV({})", e.to_sql()),
520        }
521    }
522}
523
524#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
525pub enum CountArgExpr {
526    Expr(Expr),
527    Wildcard,
528}
529
530impl ToSql for CountArgExpr {
531    fn to_sql(&self) -> String {
532        match self {
533            CountArgExpr::Expr(e) => e.to_sql(),
534            CountArgExpr::Wildcard => "*".to_owned(),
535        }
536    }
537}
538
539#[cfg(test)]
540mod tests {
541    use {
542        crate::ast::{
543            Aggregate, AstLiteral, CountArgExpr, DataType, DateTimeField, Expr, Function, ToSql,
544            TrimWhereField,
545        },
546        bigdecimal::BigDecimal,
547        std::str::FromStr,
548    };
549
550    #[test]
551    fn to_sql_function() {
552        assert_eq!(
553            r#"ABS("num")"#,
554            &Expr::Function(Box::new(Function::Abs(Expr::Identifier("num".to_owned())))).to_sql()
555        );
556
557        assert_eq!(
558            "LOWER('Bye')",
559            &Expr::Function(Box::new(Function::Lower(Expr::Literal(
560                AstLiteral::QuotedString("Bye".to_owned())
561            ))))
562            .to_sql()
563        );
564
565        assert_eq!(
566            "INITCAP('Bye')",
567            &Expr::Function(Box::new(Function::Initcap(Expr::Literal(
568                AstLiteral::QuotedString("Bye".to_owned())
569            ))))
570            .to_sql()
571        );
572
573        assert_eq!(
574            "UPPER('Hi')",
575            &Expr::Function(Box::new(Function::Upper(Expr::Literal(
576                AstLiteral::QuotedString("Hi".to_owned())
577            ))))
578            .to_sql()
579        );
580
581        assert_eq!(
582            "LEFT('GlueSQL', 2)",
583            &Expr::Function(Box::new(Function::Left {
584                expr: Expr::Literal(AstLiteral::QuotedString("GlueSQL".to_owned())),
585                size: Expr::Literal(AstLiteral::Number(BigDecimal::from_str("2").unwrap()))
586            }))
587            .to_sql()
588        );
589
590        assert_eq!(
591            "RIGHT('GlueSQL', 3)",
592            &Expr::Function(Box::new(Function::Right {
593                expr: Expr::Literal(AstLiteral::QuotedString("GlueSQL".to_owned())),
594                size: Expr::Literal(AstLiteral::Number(BigDecimal::from_str("3").unwrap()))
595            }))
596            .to_sql()
597        );
598
599        assert_eq!(
600            "ASIN(2)",
601            &Expr::Function(Box::new(Function::Asin(Expr::Literal(AstLiteral::Number(
602                BigDecimal::from_str("2").unwrap()
603            )))))
604            .to_sql()
605        );
606
607        assert_eq!(
608            "ACOS(2)",
609            &Expr::Function(Box::new(Function::Acos(Expr::Literal(AstLiteral::Number(
610                BigDecimal::from_str("2").unwrap()
611            )))))
612            .to_sql()
613        );
614
615        assert_eq!(
616            "ATAN(2)",
617            &Expr::Function(Box::new(Function::Atan(Expr::Literal(AstLiteral::Number(
618                BigDecimal::from_str("2").unwrap()
619            )))))
620            .to_sql()
621        );
622
623        assert_eq!(
624            "LPAD('GlueSQL', 2)",
625            &Expr::Function(Box::new(Function::Lpad {
626                expr: Expr::Literal(AstLiteral::QuotedString("GlueSQL".to_owned())),
627                size: Expr::Literal(AstLiteral::Number(BigDecimal::from_str("2").unwrap())),
628                fill: None
629            }))
630            .to_sql()
631        );
632
633        assert_eq!(
634            "LPAD('GlueSQL', 10, 'Go')",
635            &Expr::Function(Box::new(Function::Lpad {
636                expr: Expr::Literal(AstLiteral::QuotedString("GlueSQL".to_owned())),
637                size: Expr::Literal(AstLiteral::Number(BigDecimal::from_str("10").unwrap())),
638                fill: Some(Expr::Literal(AstLiteral::QuotedString("Go".to_owned())))
639            }))
640            .to_sql()
641        );
642
643        assert_eq!(
644            "RPAD('GlueSQL', 10)",
645            &Expr::Function(Box::new(Function::Rpad {
646                expr: Expr::Literal(AstLiteral::QuotedString("GlueSQL".to_owned())),
647                size: Expr::Literal(AstLiteral::Number(BigDecimal::from_str("10").unwrap())),
648                fill: None
649            }))
650            .to_sql()
651        );
652
653        assert_eq!(
654            "RPAD('GlueSQL', 10, 'Go')",
655            &Expr::Function(Box::new(Function::Rpad {
656                expr: Expr::Literal(AstLiteral::QuotedString("GlueSQL".to_owned())),
657                size: Expr::Literal(AstLiteral::Number(BigDecimal::from_str("10").unwrap())),
658                fill: Some(Expr::Literal(AstLiteral::QuotedString("Go".to_owned())))
659            }))
660            .to_sql()
661        );
662
663        assert_eq!(
664            "CAST(1.0 AS INT)",
665            &Expr::Function(Box::new(Function::Cast {
666                expr: Expr::Literal(AstLiteral::Number(BigDecimal::from_str("1.0").unwrap())),
667                data_type: DataType::Int
668            }))
669            .to_sql()
670        );
671
672        assert_eq!(
673            r#"CEIL("num")"#,
674            &Expr::Function(Box::new(Function::Ceil(Expr::Identifier("num".to_owned())))).to_sql()
675        );
676
677        assert_eq!(
678            r#"CUSTOM_FUNC("Tic", 1, "num", 'abc')"#,
679            &Expr::Function(Box::new(Function::Custom {
680                name: "CUSTOM_FUNC".to_owned(),
681                exprs: vec![
682                    Expr::Identifier("Tic".to_owned()),
683                    Expr::Literal(AstLiteral::Number(BigDecimal::from_str("1").unwrap())),
684                    Expr::Identifier("num".to_owned()),
685                    Expr::Literal(AstLiteral::QuotedString("abc".to_owned()))
686                ]
687            }))
688            .to_sql()
689        );
690        assert_eq!(
691            r#"CUSTOM_FUNC("num")"#,
692            &Expr::Function(Box::new(Function::Custom {
693                name: "CUSTOM_FUNC".to_owned(),
694                exprs: vec![Expr::Identifier("num".to_owned())]
695            }))
696            .to_sql()
697        );
698        assert_eq!(
699            "CUSTOM_FUNC()",
700            &Expr::Function(Box::new(Function::Custom {
701                name: "CUSTOM_FUNC".to_owned(),
702                exprs: vec![]
703            }))
704            .to_sql()
705        );
706
707        assert_eq!(
708            r#"COALESCE("First", NULL, "Last")"#,
709            &Expr::Function(Box::new(Function::Coalesce(vec![
710                Expr::Identifier("First".to_owned()),
711                Expr::Literal(AstLiteral::Null),
712                Expr::Identifier("Last".to_owned()),
713            ])))
714            .to_sql()
715        );
716
717        assert_eq!(
718            "CONCAT(\"Tic\", \"tac\", \"toe\")",
719            &Expr::Function(Box::new(Function::Concat(vec![
720                Expr::Identifier("Tic".to_owned()),
721                Expr::Identifier("tac".to_owned()),
722                Expr::Identifier("toe".to_owned())
723            ])))
724            .to_sql()
725        );
726
727        assert_eq!(
728            r#"CONCAT_WS('-', "Tic", "tac", "toe")"#,
729            &Expr::Function(Box::new(Function::ConcatWs {
730                separator: Expr::Literal(AstLiteral::QuotedString("-".to_owned())),
731                exprs: vec![
732                    Expr::Identifier("Tic".to_owned()),
733                    Expr::Identifier("tac".to_owned()),
734                    Expr::Identifier("toe".to_owned())
735                ]
736            }))
737            .to_sql()
738        );
739
740        assert_eq!(
741            "REPLACE('Mticky GlueMQL','M','S')",
742            &Expr::Function(Box::new(Function::Replace {
743                expr: Expr::Literal(AstLiteral::QuotedString("Mticky GlueMQL".to_owned())),
744                old: Expr::Literal(AstLiteral::QuotedString("M".to_owned())),
745                new: Expr::Literal(AstLiteral::QuotedString("S".to_owned()))
746            }))
747            .to_sql()
748        );
749        assert_eq!(
750            r#"IFNULL("updated_at", "created_at")"#,
751            &Expr::Function(Box::new(Function::IfNull {
752                expr: Expr::Identifier("updated_at".to_owned()),
753                then: Expr::Identifier("created_at".to_owned())
754            }))
755            .to_sql()
756        );
757
758        assert_eq!(
759            "RAND()",
760            &Expr::Function(Box::new(Function::Rand(None))).to_sql()
761        );
762
763        assert_eq!(
764            r#"RAND("num")"#,
765            &Expr::Function(Box::new(Function::Rand(Some(Expr::Identifier(
766                "num".to_owned()
767            )))))
768            .to_sql()
769        );
770
771        assert_eq!(
772            r#"ROUND("num")"#,
773            &Expr::Function(Box::new(Function::Round(Expr::Identifier(
774                "num".to_owned()
775            ))))
776            .to_sql()
777        );
778
779        assert_eq!(
780            r#"FLOOR("num")"#,
781            &Expr::Function(Box::new(Function::Floor(Expr::Identifier(
782                "num".to_owned()
783            ))))
784            .to_sql()
785        );
786
787        assert_eq!(
788            r#"TRIM("name")"#,
789            &Expr::Function(Box::new(Function::Trim {
790                expr: Expr::Identifier("name".to_owned()),
791                filter_chars: None,
792                trim_where_field: None
793            }))
794            .to_sql()
795        );
796
797        assert_eq!(
798            r#"TRIM('*' FROM "name")"#,
799            &Expr::Function(Box::new(Function::Trim {
800                expr: Expr::Identifier("name".to_owned()),
801                filter_chars: Some(Expr::Literal(AstLiteral::QuotedString("*".to_owned()))),
802                trim_where_field: None
803            }))
804            .to_sql()
805        );
806
807        assert_eq!(
808            r#"TRIM(BOTH '*' FROM "name")"#,
809            &Expr::Function(Box::new(Function::Trim {
810                expr: Expr::Identifier("name".to_owned()),
811                filter_chars: Some(Expr::Literal(AstLiteral::QuotedString("*".to_owned()))),
812                trim_where_field: Some(TrimWhereField::Both)
813            }))
814            .to_sql()
815        );
816
817        assert_eq!(
818            r#"TRIM(LEADING '*' FROM "name")"#,
819            &Expr::Function(Box::new(Function::Trim {
820                expr: Expr::Identifier("name".to_owned()),
821                filter_chars: Some(Expr::Literal(AstLiteral::QuotedString("*".to_owned()))),
822                trim_where_field: Some(TrimWhereField::Leading)
823            }))
824            .to_sql()
825        );
826
827        assert_eq!(
828            r#"TRIM(LEADING "name")"#,
829            &Expr::Function(Box::new(Function::Trim {
830                expr: Expr::Identifier("name".to_owned()),
831                filter_chars: None,
832                trim_where_field: Some(TrimWhereField::Leading)
833            }))
834            .to_sql()
835        );
836
837        assert_eq!(
838            "EXP(1)",
839            &Expr::Function(Box::new(Function::Exp(Expr::Literal(AstLiteral::Number(
840                BigDecimal::from_str("1").unwrap()
841            )))))
842            .to_sql()
843        );
844
845        assert_eq!(
846            "LN(1)",
847            &Expr::Function(Box::new(Function::Ln(Expr::Literal(AstLiteral::Number(
848                BigDecimal::from_str("1").unwrap()
849            )))))
850            .to_sql()
851        );
852
853        assert_eq!(
854            "LOG(64, 8)",
855            &Expr::Function(Box::new(Function::Log {
856                antilog: Expr::Literal(AstLiteral::Number(BigDecimal::from_str("64").unwrap())),
857                base: Expr::Literal(AstLiteral::Number(BigDecimal::from_str("8").unwrap()))
858            }))
859            .to_sql()
860        );
861
862        assert_eq!(
863            r#"LOG2("num")"#,
864            &Expr::Function(Box::new(Function::Log2(Expr::Identifier("num".to_owned())))).to_sql()
865        );
866
867        assert_eq!(
868            r#"LOG10("num")"#,
869            &Expr::Function(Box::new(Function::Log10(Expr::Identifier(
870                "num".to_owned()
871            ))))
872            .to_sql()
873        );
874
875        assert_eq!(
876            "DIV(64, 8)",
877            &Expr::Function(Box::new(Function::Div {
878                dividend: Expr::Literal(AstLiteral::Number(BigDecimal::from_str("64").unwrap())),
879                divisor: Expr::Literal(AstLiteral::Number(BigDecimal::from_str("8").unwrap()))
880            }))
881            .to_sql()
882        );
883
884        assert_eq!(
885            "MOD(64, 8)",
886            &Expr::Function(Box::new(Function::Mod {
887                dividend: Expr::Literal(AstLiteral::Number(BigDecimal::from_str("64").unwrap())),
888                divisor: Expr::Literal(AstLiteral::Number(BigDecimal::from_str("8").unwrap()))
889            }))
890            .to_sql()
891        );
892
893        assert_eq!(
894            "GCD(64, 8)",
895            &Expr::Function(Box::new(Function::Gcd {
896                left: Expr::Literal(AstLiteral::Number(BigDecimal::from_str("64").unwrap())),
897                right: Expr::Literal(AstLiteral::Number(BigDecimal::from_str("8").unwrap()))
898            }))
899            .to_sql()
900        );
901
902        assert_eq!(
903            "LCM(64, 8)",
904            &Expr::Function(Box::new(Function::Lcm {
905                left: Expr::Literal(AstLiteral::Number(BigDecimal::from_str("64").unwrap())),
906                right: Expr::Literal(AstLiteral::Number(BigDecimal::from_str("8").unwrap()))
907            }))
908            .to_sql()
909        );
910
911        assert_eq!(
912            "SIN(2)",
913            &Expr::Function(Box::new(Function::Sin(Expr::Literal(AstLiteral::Number(
914                BigDecimal::from_str("2").unwrap()
915            )))))
916            .to_sql()
917        );
918
919        assert_eq!(
920            "COS(2)",
921            &Expr::Function(Box::new(Function::Cos(Expr::Literal(AstLiteral::Number(
922                BigDecimal::from_str("2").unwrap()
923            )))))
924            .to_sql()
925        );
926
927        assert_eq!(
928            "TAN(2)",
929            &Expr::Function(Box::new(Function::Tan(Expr::Literal(AstLiteral::Number(
930                BigDecimal::from_str("2").unwrap()
931            )))))
932            .to_sql()
933        );
934
935        assert_eq!(
936            "SQRT(2)",
937            &Expr::Function(Box::new(Function::Sqrt(Expr::Literal(AstLiteral::Number(
938                BigDecimal::from_str("2").unwrap()
939            )))))
940            .to_sql()
941        );
942
943        assert_eq!(
944            "POWER(2, 10)",
945            &Expr::Function(Box::new(Function::Power {
946                expr: Expr::Literal(AstLiteral::Number(BigDecimal::from_str("2").unwrap())),
947                power: Expr::Literal(AstLiteral::Number(BigDecimal::from_str("10").unwrap())),
948            }))
949            .to_sql()
950        );
951
952        assert_eq!(
953            "RADIANS(1)",
954            &Expr::Function(Box::new(Function::Radians(Expr::Literal(
955                AstLiteral::Number(BigDecimal::from_str("1").unwrap())
956            ))))
957            .to_sql()
958        );
959
960        assert_eq!(
961            "DEGREES(1)",
962            &Expr::Function(Box::new(Function::Degrees(Expr::Literal(
963                AstLiteral::Number(BigDecimal::from_str("1").unwrap())
964            ))))
965            .to_sql()
966        );
967
968        assert_eq!("NOW()", &Expr::Function(Box::new(Function::Now())).to_sql());
969
970        assert_eq!("PI()", &Expr::Function(Box::new(Function::Pi())).to_sql());
971
972        assert_eq!(
973            "LTRIM('   HI ')",
974            &Expr::Function(Box::new(Function::Ltrim {
975                expr: Expr::Literal(AstLiteral::QuotedString("   HI ".to_owned())),
976                chars: None
977            }))
978            .to_sql()
979        );
980
981        assert_eq!(
982            "LTRIM('*IMPORTANT', '*')",
983            &Expr::Function(Box::new(Function::Ltrim {
984                expr: Expr::Literal(AstLiteral::QuotedString("*IMPORTANT".to_owned())),
985                chars: Some(Expr::Literal(AstLiteral::QuotedString("*".to_owned()))),
986            }))
987            .to_sql()
988        );
989
990        assert_eq!(
991            "RTRIM('   HI ')",
992            &Expr::Function(Box::new(Function::Rtrim {
993                expr: Expr::Literal(AstLiteral::QuotedString("   HI ".to_owned())),
994                chars: None
995            }))
996            .to_sql()
997        );
998
999        assert_eq!(
1000            "RTRIM('IMPORTANT*', '*')",
1001            &Expr::Function(Box::new(Function::Rtrim {
1002                expr: Expr::Literal(AstLiteral::QuotedString("IMPORTANT*".to_owned())),
1003                chars: Some(Expr::Literal(AstLiteral::QuotedString("*".to_owned()))),
1004            }))
1005            .to_sql()
1006        );
1007
1008        assert_eq!(
1009            r#"REVERSE("name")"#,
1010            &Expr::Function(Box::new(Function::Reverse(Expr::Identifier(
1011                "name".to_owned()
1012            ))))
1013            .to_sql()
1014        );
1015
1016        assert_eq!(
1017            "REPEAT('Ha', 8)",
1018            &Expr::Function(Box::new(Function::Repeat {
1019                expr: Expr::Literal(AstLiteral::QuotedString("Ha".to_owned())),
1020                num: Expr::Literal(AstLiteral::Number(BigDecimal::from_str("8").unwrap()))
1021            }))
1022            .to_sql()
1023        );
1024
1025        assert_eq!(
1026            "SIGN(1.0)",
1027            &Expr::Function(Box::new(Function::Sign(Expr::Literal(AstLiteral::Number(
1028                BigDecimal::from_str("1.0").unwrap()
1029            )))))
1030            .to_sql()
1031        );
1032
1033        assert_eq!(
1034            "SUBSTR('GlueSQL', 2)",
1035            &Expr::Function(Box::new(Function::Substr {
1036                expr: Expr::Literal(AstLiteral::QuotedString("GlueSQL".to_owned())),
1037                start: Expr::Literal(AstLiteral::Number(BigDecimal::from_str("2").unwrap())),
1038                count: None
1039            }))
1040            .to_sql()
1041        );
1042
1043        assert_eq!(
1044            "SUBSTR('GlueSQL', 1, 3)",
1045            &Expr::Function(Box::new(Function::Substr {
1046                expr: Expr::Literal(AstLiteral::QuotedString("GlueSQL".to_owned())),
1047                start: Expr::Literal(AstLiteral::Number(BigDecimal::from_str("1").unwrap())),
1048                count: Some(Expr::Literal(AstLiteral::Number(
1049                    BigDecimal::from_str("3").unwrap()
1050                )))
1051            }))
1052            .to_sql()
1053        );
1054
1055        assert_eq!(
1056            r#"UNWRAP("nested", 'a.foo')"#,
1057            &Expr::Function(Box::new(Function::Unwrap {
1058                expr: Expr::Identifier("nested".to_owned()),
1059                selector: Expr::Literal(AstLiteral::QuotedString("a.foo".to_owned()))
1060            }))
1061            .to_sql()
1062        );
1063
1064        assert_eq!(
1065            "GENERATE_UUID()",
1066            &Expr::Function(Box::new(Function::GenerateUuid())).to_sql()
1067        );
1068        assert_eq!(
1069            "ADD_MONTH('2023-06-15',1)",
1070            &Expr::Function(Box::new(Function::AddMonth {
1071                expr: Expr::Literal(AstLiteral::QuotedString("2023-06-15".to_owned())),
1072                size: Expr::Literal(AstLiteral::Number(BigDecimal::from_str("1").unwrap()))
1073            }))
1074            .to_sql()
1075        );
1076
1077        assert_eq!(
1078            "GREATEST(16, 9, 7)",
1079            &Expr::Function(Box::new(Function::Greatest(vec![
1080                Expr::Literal(AstLiteral::Number(BigDecimal::from_str("16").unwrap())),
1081                Expr::Literal(AstLiteral::Number(BigDecimal::from_str("9").unwrap())),
1082                Expr::Literal(AstLiteral::Number(BigDecimal::from_str("7").unwrap()))
1083            ])))
1084            .to_sql()
1085        );
1086
1087        assert_eq!(
1088            "FORMAT(DATE '2022-10-12', '%Y-%m')",
1089            &Expr::Function(Box::new(Function::Format {
1090                expr: Expr::TypedString {
1091                    data_type: DataType::Date,
1092                    value: "2022-10-12".to_owned()
1093                },
1094                format: Expr::Literal(AstLiteral::QuotedString("%Y-%m".to_owned()))
1095            }))
1096            .to_sql()
1097        );
1098
1099        assert_eq!(
1100            "LAST_DAY(DATE '2022-10-12')",
1101            &Expr::Function(Box::new(Function::LastDay(Expr::TypedString {
1102                data_type: DataType::Date,
1103                value: "2022-10-12".to_owned()
1104            })))
1105            .to_sql()
1106        );
1107
1108        assert_eq!(
1109            "TO_DATE('2022-10-12', '%Y-%m-%d')",
1110            &Expr::Function(Box::new(Function::ToDate {
1111                expr: Expr::Literal(AstLiteral::QuotedString("2022-10-12".to_owned())),
1112                format: Expr::Literal(AstLiteral::QuotedString("%Y-%m-%d".to_owned()))
1113            }))
1114            .to_sql()
1115        );
1116
1117        assert_eq!(
1118            "TO_TIMESTAMP('2022-10-12 00:34:23', '%Y-%m-%d %H:%M:%S')",
1119            &Expr::Function(Box::new(Function::ToTimestamp {
1120                expr: Expr::Literal(AstLiteral::QuotedString("2022-10-12 00:34:23".to_owned())),
1121                format: Expr::Literal(AstLiteral::QuotedString("%Y-%m-%d %H:%M:%S".to_owned()))
1122            }))
1123            .to_sql()
1124        );
1125
1126        assert_eq!(
1127            "TO_TIME('00:34:23', '%H:%M:%S')",
1128            &Expr::Function(Box::new(Function::ToTime {
1129                expr: Expr::Literal(AstLiteral::QuotedString("00:34:23".to_owned())),
1130                format: Expr::Literal(AstLiteral::QuotedString("%H:%M:%S".to_owned()))
1131            }))
1132            .to_sql()
1133        );
1134
1135        assert_eq!(
1136            "POSITION('cup' IN 'cupcake')",
1137            &Expr::Function(Box::new(Function::Position {
1138                from_expr: Expr::Literal(AstLiteral::QuotedString("cupcake".to_owned())),
1139                sub_expr: Expr::Literal(AstLiteral::QuotedString("cup".to_owned())),
1140            }))
1141            .to_sql()
1142        );
1143
1144        assert_eq!(
1145            "FIND_IDX('noodle', 'o', 2)",
1146            &Expr::Function(Box::new(Function::FindIdx {
1147                from_expr: Expr::Literal(AstLiteral::QuotedString("noodle".to_owned())),
1148                sub_expr: Expr::Literal(AstLiteral::QuotedString("o".to_owned())),
1149                start: Some(Expr::Literal(AstLiteral::Number(
1150                    BigDecimal::from_str("2").unwrap()
1151                )))
1152            }))
1153            .to_sql()
1154        );
1155
1156        assert_eq!(
1157            "FIND_IDX('goat cheese', 'goat')",
1158            &Expr::Function(Box::new(Function::FindIdx {
1159                from_expr: Expr::Literal(AstLiteral::QuotedString("goat cheese".to_owned())),
1160                sub_expr: Expr::Literal(AstLiteral::QuotedString("goat".to_owned())),
1161                start: None
1162            }))
1163            .to_sql()
1164        );
1165
1166        assert_eq!(
1167            "ASCII('H')",
1168            &Expr::Function(Box::new(Function::Ascii(Expr::Literal(
1169                AstLiteral::QuotedString("H".to_owned())
1170            ))))
1171            .to_sql()
1172        );
1173
1174        assert_eq!(
1175            r#"CHR(72)"#,
1176            &Expr::Function(Box::new(Function::Chr(Expr::Literal(AstLiteral::Number(
1177                BigDecimal::from_str("72").unwrap()
1178            )))))
1179            .to_sql()
1180        );
1181
1182        assert_eq!(
1183            "MD5('GlueSQL')",
1184            &Expr::Function(Box::new(Function::Md5(Expr::Literal(
1185                AstLiteral::QuotedString("GlueSQL".to_owned())
1186            ))))
1187            .to_sql()
1188        );
1189
1190        assert_eq!(
1191            r#"EXTRACT(MINUTE FROM '2022-05-05 01:02:03')"#,
1192            &Expr::Function(Box::new(Function::Extract {
1193                field: DateTimeField::Minute,
1194                expr: Expr::Literal(AstLiteral::QuotedString("2022-05-05 01:02:03".to_owned()))
1195            }))
1196            .to_sql()
1197        );
1198
1199        assert_eq!(
1200            r#"APPEND("list", "value")"#,
1201            &Expr::Function(Box::new(Function::Append {
1202                expr: Expr::Identifier("list".to_owned()),
1203                value: Expr::Identifier("value".to_owned())
1204            }))
1205            .to_sql()
1206        );
1207
1208        assert_eq!(
1209            r#"PREPEND("list", "value")"#,
1210            &Expr::Function(Box::new(Function::Prepend {
1211                expr: Expr::Identifier("list".to_owned()),
1212                value: Expr::Identifier("value".to_owned())
1213            }))
1214            .to_sql()
1215        );
1216
1217        assert_eq!(
1218            r#"SKIP("list", 2)"#,
1219            &Expr::Function(Box::new(Function::Skip {
1220                expr: Expr::Identifier("list".to_owned()),
1221                size: Expr::Literal(AstLiteral::Number(BigDecimal::from_str("2").unwrap()))
1222            }))
1223            .to_sql()
1224        );
1225
1226        assert_eq!(
1227            r#"SORT("list")"#,
1228            &Expr::Function(Box::new(Function::Sort {
1229                expr: Expr::Identifier("list".to_owned()),
1230                order: None
1231            }))
1232            .to_sql()
1233        );
1234
1235        assert_eq!(
1236            r#"SORT("list", 'ASC')"#,
1237            &Expr::Function(Box::new(Function::Sort {
1238                expr: Expr::Identifier("list".to_owned()),
1239                order: Some(Expr::Literal(AstLiteral::QuotedString("ASC".to_owned())))
1240            }))
1241            .to_sql()
1242        );
1243
1244        assert_eq!(
1245            r#"SLICE("list", 1, 2)"#,
1246            &Expr::Function(Box::new(Function::Slice {
1247                expr: (Expr::Identifier("list".to_owned())),
1248                start: (Expr::Literal(AstLiteral::Number(BigDecimal::from_str("1").unwrap()))),
1249                length: (Expr::Literal(AstLiteral::Number(BigDecimal::from_str("2").unwrap())))
1250            }))
1251            .to_sql()
1252        );
1253
1254        assert_eq!(
1255            r#"TAKE("list", 3)"#,
1256            &Expr::Function(Box::new(Function::Take {
1257                expr: Expr::Identifier("list".to_owned()),
1258                size: Expr::Literal(AstLiteral::Number(BigDecimal::from_str("3").unwrap()))
1259            }))
1260            .to_sql()
1261        );
1262
1263        assert_eq!(
1264            "GET_X(\"point\")",
1265            &Expr::Function(Box::new(Function::GetX(Expr::Identifier(
1266                "point".to_owned()
1267            ))))
1268            .to_sql()
1269        );
1270
1271        assert_eq!(
1272            "GET_Y(\"point\")",
1273            &Expr::Function(Box::new(Function::GetY(Expr::Identifier(
1274                "point".to_owned()
1275            ))))
1276            .to_sql()
1277        );
1278
1279        assert_eq!(
1280            "POINT(0.1, 0.2)",
1281            &Expr::Function(Box::new(Function::Point {
1282                x: Expr::Literal(AstLiteral::Number(BigDecimal::from_str("0.1").unwrap())),
1283                y: Expr::Literal(AstLiteral::Number(BigDecimal::from_str("0.2").unwrap()))
1284            }))
1285            .to_sql()
1286        );
1287
1288        assert_eq!(
1289            "CALC_DISTANCE(POINT(1.1, 2.3), POINT(1.4, 3.6))",
1290            &Expr::Function(Box::new(Function::CalcDistance {
1291                geometry1: Expr::Function(Box::new(Function::Point {
1292                    x: Expr::Literal(AstLiteral::Number(BigDecimal::from_str("1.1").unwrap())),
1293                    y: Expr::Literal(AstLiteral::Number(BigDecimal::from_str("2.3").unwrap()))
1294                })),
1295                geometry2: Expr::Function(Box::new(Function::Point {
1296                    x: Expr::Literal(AstLiteral::Number(BigDecimal::from_str("1.4").unwrap())),
1297                    y: Expr::Literal(AstLiteral::Number(BigDecimal::from_str("3.6").unwrap()))
1298                }))
1299            }))
1300            .to_sql()
1301        );
1302
1303        assert_eq!(
1304            r#"IS_EMPTY("list")"#,
1305            &Expr::Function(Box::new(Function::IsEmpty(Expr::Identifier(
1306                "list".to_owned()
1307            ))))
1308            .to_sql()
1309        );
1310
1311        assert_eq!(
1312            r#"LENGTH("GlueSQL")"#,
1313            &Expr::Function(Box::new(Function::Length(Expr::Identifier(
1314                "GlueSQL".to_owned()
1315            ))))
1316            .to_sql()
1317        );
1318
1319        assert_eq!(
1320            r#"ENTRIES("map")"#,
1321            &Expr::Function(Box::new(Function::Entries(Expr::Identifier(
1322                "map".to_owned()
1323            ))))
1324            .to_sql()
1325        );
1326
1327        assert_eq!(
1328            r#"KEYS("map")"#,
1329            &Expr::Function(Box::new(Function::Keys(Expr::Identifier("map".to_owned())))).to_sql()
1330        );
1331
1332        assert_eq!(
1333            r#"VALUES("map")"#,
1334            &Expr::Function(Box::new(Function::Values(Expr::Identifier(
1335                "map".to_owned()
1336            ))))
1337            .to_sql()
1338        );
1339
1340        assert_eq!(
1341            r#"SPLICE("list", 2, 4)"#,
1342            &Expr::Function(Box::new(Function::Splice {
1343                list_data: Expr::Identifier("list".to_owned()),
1344                begin_index: Expr::Literal(AstLiteral::Number(BigDecimal::from_str("2").unwrap())),
1345                end_index: Expr::Literal(AstLiteral::Number(BigDecimal::from_str("4").unwrap())),
1346                values: None
1347            }))
1348            .to_sql()
1349        );
1350
1351        assert_eq!(
1352            r#"SPLICE("list", 2, 4, "values")"#,
1353            &Expr::Function(Box::new(Function::Splice {
1354                list_data: Expr::Identifier("list".to_owned()),
1355                begin_index: Expr::Literal(AstLiteral::Number(BigDecimal::from_str("2").unwrap())),
1356                end_index: Expr::Literal(AstLiteral::Number(BigDecimal::from_str("4").unwrap())),
1357                values: Some(Expr::Identifier("values".to_owned()))
1358            }))
1359            .to_sql()
1360        );
1361
1362        assert_eq!(
1363            r#"DEDUP("list")"#,
1364            &Expr::Function(Box::new(Function::Dedup(Expr::Identifier(
1365                "list".to_owned()
1366            ))))
1367            .to_sql(),
1368        )
1369    }
1370
1371    #[test]
1372    fn to_sql_aggregate() {
1373        assert_eq!(
1374            r#"MAX("id")"#,
1375            Expr::Aggregate(Box::new(Aggregate::Max(Expr::Identifier("id".to_owned())))).to_sql()
1376        );
1377
1378        assert_eq!(
1379            "COUNT(*)",
1380            Expr::Aggregate(Box::new(Aggregate::Count(CountArgExpr::Wildcard))).to_sql()
1381        );
1382
1383        assert_eq!(
1384            r#"MIN("id")"#,
1385            Expr::Aggregate(Box::new(Aggregate::Min(Expr::Identifier("id".to_owned())))).to_sql()
1386        );
1387
1388        assert_eq!(
1389            r#"SUM("price")"#,
1390            &Expr::Aggregate(Box::new(Aggregate::Sum(Expr::Identifier(
1391                "price".to_owned()
1392            ))))
1393            .to_sql()
1394        );
1395
1396        assert_eq!(
1397            r#"AVG("pay")"#,
1398            &Expr::Aggregate(Box::new(Aggregate::Avg(Expr::Identifier("pay".to_owned())))).to_sql()
1399        );
1400        assert_eq!(
1401            r#"VARIANCE("pay")"#,
1402            &Expr::Aggregate(Box::new(Aggregate::Variance(Expr::Identifier(
1403                "pay".to_owned()
1404            ))))
1405            .to_sql()
1406        );
1407        assert_eq!(
1408            r#"STDEV("total")"#,
1409            &Expr::Aggregate(Box::new(Aggregate::Stdev(Expr::Identifier(
1410                "total".to_owned()
1411            ))))
1412            .to_sql()
1413        );
1414    }
1415}