gluesql_core/plan/expr/plan_expr/
function.rs

1use {
2    crate::ast::{Expr, Function},
3    std::iter::{empty, once},
4};
5
6impl Function {
7    pub fn as_exprs(&self) -> impl Iterator<Item = &Expr> {
8        #[derive(iter_enum::Iterator)]
9        enum Exprs<I0, I1, I2, I3, I4, I5, I6> {
10            Empty(I0),
11            Single(I1),
12            Double(I2),
13            Triple(I3),
14            VariableArgs(I4),
15            VariableArgsWithSingle(I5),
16            Quadruple(I6),
17        }
18
19        match self {
20            Self::Now()
21            | Function::Pi()
22            | Function::GenerateUuid()
23            | Self::Rand(None)
24            | Function::CurrentDate()
25            | Function::CurrentTime()
26            | Function::CurrentTimestamp() => Exprs::Empty(empty()),
27            Self::Lower(expr)
28            | Self::Length(expr)
29            | Self::Initcap(expr)
30            | Self::Upper(expr)
31            | Self::Sin(expr)
32            | Self::Cos(expr)
33            | Self::Tan(expr)
34            | Self::Asin(expr)
35            | Self::Acos(expr)
36            | Self::Atan(expr)
37            | Self::Radians(expr)
38            | Self::Degrees(expr)
39            | Self::Ceil(expr)
40            | Self::Rand(Some(expr))
41            | Self::Round(expr)
42            | Self::Trunc(expr)
43            | Self::Floor(expr)
44            | Self::Exp(expr)
45            | Self::Ln(expr)
46            | Self::Log2(expr)
47            | Self::Log10(expr)
48            | Self::Sqrt(expr)
49            | Self::Abs(expr)
50            | Self::Sign(expr)
51            | Self::Ascii(expr)
52            | Self::Chr(expr)
53            | Self::Md5(expr)
54            | Self::Hex(expr)
55            | Self::LastDay(expr)
56            | Self::Ltrim { expr, chars: None }
57            | Self::Rtrim { expr, chars: None }
58            | Self::Trim {
59                expr,
60                filter_chars: None,
61                ..
62            }
63            | Self::Reverse(expr)
64            | Self::Cast { expr, .. }
65            | Self::Extract { expr, .. }
66            | Self::GetX(expr)
67            | Self::GetY(expr)
68            | Self::IsEmpty(expr)
69            | Self::Sort { expr, order: None }
70            | Self::Dedup(expr)
71            | Self::Entries(expr)
72            | Self::Keys(expr)
73            | Self::Values(expr) => Exprs::Single([expr].into_iter()),
74            Self::Left { expr, size: expr2 }
75            | Self::Right { expr, size: expr2 }
76            | Self::Lpad {
77                expr,
78                size: expr2,
79                fill: None,
80            }
81            | Self::Rpad {
82                expr,
83                size: expr2,
84                fill: None,
85            }
86            | Self::Trim {
87                expr,
88                filter_chars: Some(expr2),
89                ..
90            }
91            | Self::Log {
92                antilog: expr,
93                base: expr2,
94            }
95            | Self::Div {
96                dividend: expr,
97                divisor: expr2,
98            }
99            | Self::Mod {
100                dividend: expr,
101                divisor: expr2,
102            }
103            | Self::Gcd {
104                left: expr,
105                right: expr2,
106            }
107            | Self::Lcm {
108                left: expr,
109                right: expr2,
110            }
111            | Self::Format {
112                expr,
113                format: expr2,
114            }
115            | Self::ToDate {
116                expr,
117                format: expr2,
118            }
119            | Self::ToTimestamp {
120                expr,
121                format: expr2,
122            }
123            | Self::ToTime {
124                expr,
125                format: expr2,
126            }
127            | Self::Power { expr, power: expr2 }
128            | Self::Ltrim {
129                expr,
130                chars: Some(expr2),
131            }
132            | Self::Rtrim {
133                expr,
134                chars: Some(expr2),
135            }
136            | Self::Repeat { expr, num: expr2 }
137            | Self::Substr {
138                expr,
139                start: expr2,
140                count: None,
141            }
142            | Self::IfNull { expr, then: expr2 }
143            | Self::NullIf { expr1: expr, expr2 }
144            | Self::Unwrap {
145                expr,
146                selector: expr2,
147            }
148            | Self::Position {
149                from_expr: expr2,
150                sub_expr: expr,
151            }
152            | Self::FindIdx {
153                from_expr: expr,
154                sub_expr: expr2,
155                start: None,
156            }
157            | Self::Append { expr, value: expr2 }
158            | Self::Prepend { expr, value: expr2 }
159            | Self::Skip { expr, size: expr2 }
160            | Self::Sort {
161                expr,
162                order: Some(expr2),
163            }
164            | Self::Take { expr, size: expr2 }
165            | Self::Point { x: expr, y: expr2 }
166            | Self::CalcDistance {
167                geometry1: expr,
168                geometry2: expr2,
169            }
170            | Self::AddMonth { expr, size: expr2 } => Exprs::Double([expr, expr2].into_iter()),
171
172            Self::Lpad {
173                expr,
174                size: expr2,
175                fill: Some(expr3),
176            }
177            | Self::Rpad {
178                expr,
179                size: expr2,
180                fill: Some(expr3),
181            }
182            | Self::Substr {
183                expr,
184                start: expr2,
185                count: Some(expr3),
186            }
187            | Self::Replace {
188                expr,
189                old: expr2,
190                new: expr3,
191            }
192            | Self::Slice {
193                expr,
194                start: expr2,
195                length: expr3,
196            }
197            | Self::FindIdx {
198                from_expr: expr,
199                sub_expr: expr2,
200                start: Some(expr3),
201            }
202            | Self::Splice {
203                list_data: expr,
204                begin_index: expr2,
205                end_index: expr3,
206                values: None,
207            } => Exprs::Triple([expr, expr2, expr3].into_iter()),
208            Self::Custom { name: _, exprs }
209            | Self::Coalesce(exprs)
210            | Self::Concat(exprs)
211            | Self::Greatest(exprs) => Exprs::VariableArgs(exprs.iter()),
212            Self::ConcatWs { separator, exprs } => {
213                Exprs::VariableArgsWithSingle(once(separator).chain(exprs.iter()))
214            }
215            Self::Splice {
216                list_data: expr,
217                begin_index: expr2,
218                end_index: expr3,
219                values: Some(expr4),
220            } => Exprs::Quadruple([expr, expr2, expr3, expr4].into_iter()),
221        }
222    }
223}
224
225#[cfg(test)]
226mod tests {
227    use crate::{
228        ast::Expr,
229        parse_sql::parse_expr,
230        translate::{NO_PARAMS, translate_expr},
231    };
232
233    fn expr(sql: &str) -> Expr {
234        let parsed = parse_expr(sql).expect(sql);
235
236        translate_expr(&parsed, NO_PARAMS).expect(sql)
237    }
238
239    fn test(sql: &str, expected: &[&str]) {
240        let function = match expr(sql) {
241            Expr::Function(function) => *function,
242            _ => unreachable!("only for function tests"),
243        };
244        let actual = function.as_exprs();
245        let actual = actual.collect::<Vec<_>>();
246
247        assert_eq!(actual.len(), expected.len(), "{sql}");
248
249        for (expected, actual) in expected.iter().zip(actual.into_iter()) {
250            assert_eq!(actual, &expr(expected), "{sql}");
251        }
252    }
253
254    #[test]
255    fn as_exprs() {
256        // Empty
257        test("NOW()", &[]);
258        test("CURRENT_DATE()", &[]);
259        test("CURRENT_TIME()", &[]);
260        test("CURRENT_TIMESTAMP()", &[]);
261        test("PI()", &[]);
262        test("GENERATE_UUID()", &[]);
263        test("RAND()", &[]);
264        test("CUSTOM_FUNC()", &[]);
265
266        // Single
267        test("LOWER(id)", &["id"]);
268        test("INITCAP(id)", &["id"]);
269        test(r#"UPPER("Hello")"#, &[r#""Hello""#]);
270        test("SIN(3.14)", &["3.14"]);
271        test("COS(3.14)", &["3.14"]);
272        test("TAN(3.14)", &["3.14"]);
273        test("ASIN(3.14)", &["3.14"]);
274        test("ACOS(3.14)", &["3.14"]);
275        test("ATAN(3.14)", &["3.14"]);
276        test("RADIANS(180)", &["180"]);
277        test("DEGREES(3.14)", &["3.14"]);
278        test("CEIL(1.23)", &["1.23"]);
279        test("Rand(1.23)", &["1.23"]);
280        test("ROUND(1.23)", &["1.23"]);
281        test("TRUNC(1.23)", &["1.23"]);
282        test("FLOOR(1.23)", &["1.23"]);
283        test("EXP(1.23)", &["1.23"]);
284        test("LN(col + 1)", &["col + 1"]);
285        test("LOG2(16)", &["16"]);
286        test("LOG10(150 - 50)", &["150 - 50"]);
287        test("SQRT(144)", &["144"]);
288        test("LASTDAY(DATE '2020-01-01')", &[r"DATE '2020-01-01'"]);
289        test(r#"LTRIM("  hello")"#, &[r#""  hello""#]);
290        test(r#"RTRIM("world  ")"#, &[r#""world  ""#]);
291        test(r#"TRIM("  rust  ")"#, &[r#""  rust  ""#]);
292        test(r#"REVERSE("abcde")"#, &[r#""abcde""#]);
293        test(r"CAST(1 AS BOOLEAN)", &["1"]);
294        test(r"IS_EMPTY(col)", &["col"]);
295        test(r"VALUES(col)", &["col"]);
296        test(r"HEX(10)", &["10"]);
297
298        test(r"ABS(1)", &["1"]);
299        test(r"ABS(-1)", &["-1"]);
300        test(r"ABS(2)", &["2"]);
301        test(r"ABS(-2)", &["-2"]);
302        test(r"ABS(3.0)", &["3.0"]);
303        test(r"ABS(-3.0)", &["-3.0"]);
304
305        test(r"SIGN(1)", &["1"]);
306        test(r"SIGN(-1)", &["-1"]);
307        test(r"SIGN(2)", &["2"]);
308        test(r"SIGN(-2)", &["-2"]);
309        test(r"SIGN(3.0)", &["3.0"]);
310        test(r"SIGN(-3.0)", &["-3.0"]);
311
312        test(r"DEDUP(list)", &["list"]);
313
314        // Double
315        test(r#"LEFT("hello", 2)"#, &[r#""hello""#, "2"]);
316        test(r#"RIGHT("hello", 2)"#, &[r#""hello""#, "2"]);
317        test(r#"FIND_IDX("Calzone", "zone")"#, &[r"Calzone", r"zone"]);
318        test(r"TAKE(list, 3)", &[r"list", r"3"]);
319        test(r"LPAD(value, 5)", &["value", "5"]);
320        test(r"RPAD(value, 5)", &["value", "5"]);
321        test(
322            r#"TRIM(LEADING "_" FROM "__hello")"#,
323            &[r#""__hello""#, r#""_""#],
324        );
325        test("LOG(rate, 2)", &["rate", "2"]);
326        test("DIV(6, 2)", &["6", "2"]);
327        test("MOD(6, 2)", &["6", "2"]);
328        test("GCD(6, 2)", &["6", "2"]);
329        test("LCM(6, 2)", &["6", "2"]);
330        test("POWER(base, 10)", &["base", "10"]);
331        test(r#"LTRIM(name, "xyz")"#, &["name", r#""xyz""#]);
332        test(r#"RTRIM(name, "xyz")"#, &["name", r#""xyz""#]);
333        test("REPEAT(col || col2, 3)", &["col || col2", "3"]);
334        test("REPEAT(column, 2)", &["column", "2"]);
335        test(r#"UNWRAP(field, "foo.1")"#, &["field", r#""foo.1""#]);
336        test(r"SKIP(list, 2)", &[r#""list""#, r"2"]);
337
338        // Triple
339        test(
340            r#"LPAD(name, 20, '>")++++<')"#,
341            &["name", "20", r#"'>")++++<'"#],
342        );
343        test(
344            r#"RPAD(name, 20, '>")++++<')"#,
345            &["name", "20", r#"'>")++++<'"#],
346        );
347        test(
348            r#"SUBSTR('   >++++("<   ', 3, 11)"#,
349            &[r#"'   >++++("<   '"#, "3", "11"],
350        );
351        test(r"SPLICE(list, 2, 4)", &["list", "2", "4"]);
352
353        // Quadruple
354        test(r"SPLICE(list, 3, 5, values)", &["list", "3", "5", "values"]);
355
356        //VariableArgs
357        test(r#"COALESCE("test")"#, &[r#""test""#]);
358
359        test(r#"COALESCE(NULL, "test")"#, &["NULL", r#""test""#]);
360
361        test(r#"CONCAT("abc")"#, &[r#""abc""#]);
362
363        test(r#"CONCAT("abc", "123")"#, &[r#""abc""#, r#""123""#]);
364
365        test(r#"CONCAT("a", "b", "c")"#, &[r#""a""#, r#""b""#, r#""c""#]);
366
367        test(
368            r#"CUSTOM_FUNC("a", "b", "c")"#,
369            &[r#""a""#, r#""b""#, r#""c""#],
370        );
371
372        test(
373            r#"CONCAT("gluesql", " ", "is", " ", "cool")"#,
374            &[r#""gluesql""#, r#"" ""#, r#""is""#, r#"" ""#, r#""cool""#],
375        );
376
377        test(r#"POSITION("men" IN "ramen")"#, &[r#""men""#, r#""ramen""#]);
378        test(r#"POSITION("men" IN ramen)"#, &[r#""men""#, "ramen"]);
379
380        //TypedStringVariableArgs
381        test(
382            r#"CONCAT_WS(",", "gluesql", "is", "cool")"#,
383            &[r#"",""#, r#""gluesql""#, r#""is""#, r#""cool""#],
384        );
385    }
386}