gluesql_core/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 } => Exprs::VariableArgs(exprs.iter()),
209            Self::Coalesce(exprs) => Exprs::VariableArgs(exprs.iter()),
210            Self::Concat(exprs) => Exprs::VariableArgs(exprs.iter()),
211            Self::ConcatWs { separator, exprs } => {
212                Exprs::VariableArgsWithSingle(once(separator).chain(exprs.iter()))
213            }
214            Self::Greatest(exprs) => Exprs::VariableArgs(exprs.iter()),
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::{ast::Expr, parse_sql::parse_expr, translate::translate_expr};
228
229    fn expr(sql: &str) -> Expr {
230        let parsed = parse_expr(sql).expect(sql);
231
232        translate_expr(&parsed).expect(sql)
233    }
234
235    fn test(sql: &str, expected: &[&str]) {
236        let function = match expr(sql) {
237            Expr::Function(function) => *function,
238            _ => unreachable!("only for function tests"),
239        };
240        let actual = function.as_exprs();
241        let actual = actual.collect::<Vec<_>>();
242
243        assert_eq!(actual.len(), expected.len(), "{sql}");
244
245        for (expected, actual) in expected.iter().zip(actual.into_iter()) {
246            assert_eq!(actual, &expr(expected), "{sql}");
247        }
248    }
249
250    #[test]
251    fn as_exprs() {
252        // Empty
253        test("NOW()", &[]);
254        test("CURRENT_DATE()", &[]);
255        test("CURRENT_TIME()", &[]);
256        test("CURRENT_TIMESTAMP()", &[]);
257        test("PI()", &[]);
258        test("GENERATE_UUID()", &[]);
259        test("RAND()", &[]);
260        test("CUSTOM_FUNC()", &[]);
261
262        // Single
263        test("LOWER(id)", &["id"]);
264        test("INITCAP(id)", &["id"]);
265        test(r#"UPPER("Hello")"#, &[r#""Hello""#]);
266        test("SIN(3.14)", &["3.14"]);
267        test("COS(3.14)", &["3.14"]);
268        test("TAN(3.14)", &["3.14"]);
269        test("ASIN(3.14)", &["3.14"]);
270        test("ACOS(3.14)", &["3.14"]);
271        test("ATAN(3.14)", &["3.14"]);
272        test("RADIANS(180)", &["180"]);
273        test("DEGREES(3.14)", &["3.14"]);
274        test("CEIL(1.23)", &["1.23"]);
275        test("Rand(1.23)", &["1.23"]);
276        test("ROUND(1.23)", &["1.23"]);
277        test("TRUNC(1.23)", &["1.23"]);
278        test("FLOOR(1.23)", &["1.23"]);
279        test("EXP(1.23)", &["1.23"]);
280        test("LN(col + 1)", &["col + 1"]);
281        test("LOG2(16)", &["16"]);
282        test("LOG10(150 - 50)", &["150 - 50"]);
283        test("SQRT(144)", &["144"]);
284        test("LASTDAY(DATE '2020-01-01')", &[r#"DATE '2020-01-01'"#]);
285        test(r#"LTRIM("  hello")"#, &[r#""  hello""#]);
286        test(r#"RTRIM("world  ")"#, &[r#""world  ""#]);
287        test(r#"TRIM("  rust  ")"#, &[r#""  rust  ""#]);
288        test(r#"REVERSE("abcde")"#, &[r#""abcde""#]);
289        test(r#"CAST(1 AS BOOLEAN)"#, &["1"]);
290        test(r#"IS_EMPTY(col)"#, &["col"]);
291        test(r#"VALUES(col)"#, &["col"]);
292        test(r#"HEX(10)"#, &["10"]);
293
294        test(r#"ABS(1)"#, &["1"]);
295        test(r#"ABS(-1)"#, &["-1"]);
296        test(r#"ABS(2)"#, &["2"]);
297        test(r#"ABS(-2)"#, &["-2"]);
298        test(r#"ABS(3.0)"#, &["3.0"]);
299        test(r#"ABS(-3.0)"#, &["-3.0"]);
300
301        test(r#"SIGN(1)"#, &["1"]);
302        test(r#"SIGN(-1)"#, &["-1"]);
303        test(r#"SIGN(2)"#, &["2"]);
304        test(r#"SIGN(-2)"#, &["-2"]);
305        test(r#"SIGN(3.0)"#, &["3.0"]);
306        test(r#"SIGN(-3.0)"#, &["-3.0"]);
307
308        test(r#"DEDUP(list)"#, &["list"]);
309
310        // Double
311        test(r#"LEFT("hello", 2)"#, &[r#""hello""#, "2"]);
312        test(r#"RIGHT("hello", 2)"#, &[r#""hello""#, "2"]);
313        test(r#"FIND_IDX("Calzone", "zone")"#, &[r#"Calzone"#, r#"zone"#]);
314        test(r#"TAKE(list, 3)"#, &[r#"list"#, r#"3"#]);
315        test(r#"LPAD(value, 5)"#, &["value", "5"]);
316        test(r#"RPAD(value, 5)"#, &["value", "5"]);
317        test(
318            r#"TRIM(LEADING "_" FROM "__hello")"#,
319            &[r#""__hello""#, r#""_""#],
320        );
321        test("LOG(rate, 2)", &["rate", "2"]);
322        test("DIV(6, 2)", &["6", "2"]);
323        test("MOD(6, 2)", &["6", "2"]);
324        test("GCD(6, 2)", &["6", "2"]);
325        test("LCM(6, 2)", &["6", "2"]);
326        test("POWER(base, 10)", &["base", "10"]);
327        test(r#"LTRIM(name, "xyz")"#, &["name", r#""xyz""#]);
328        test(r#"RTRIM(name, "xyz")"#, &["name", r#""xyz""#]);
329        test("REPEAT(col || col2, 3)", &["col || col2", "3"]);
330        test("REPEAT(column, 2)", &["column", "2"]);
331        test(r#"UNWRAP(field, "foo.1")"#, &["field", r#""foo.1""#]);
332        test(r#"SKIP(list, 2)"#, &[r#""list""#, r#"2"#]);
333
334        // Triple
335        test(
336            r#"LPAD(name, 20, '>")++++<')"#,
337            &["name", "20", r#"'>")++++<'"#],
338        );
339        test(
340            r#"RPAD(name, 20, '>")++++<')"#,
341            &["name", "20", r#"'>")++++<'"#],
342        );
343        test(
344            r#"SUBSTR('   >++++("<   ', 3, 11)"#,
345            &[r#"'   >++++("<   '"#, "3", "11"],
346        );
347        test(r#"SPLICE(list, 2, 4)"#, &["list", "2", "4"]);
348
349        // Quadruple
350        test(
351            r#"SPLICE(list, 3, 5, values)"#,
352            &["list", "3", "5", "values"],
353        );
354
355        //VariableArgs
356        test(r#"COALESCE("test")"#, &[r#""test""#]);
357
358        test(r#"COALESCE(NULL, "test")"#, &["NULL", r#""test""#]);
359
360        test(r#"CONCAT("abc")"#, &[r#""abc""#]);
361
362        test(r#"CONCAT("abc", "123")"#, &[r#""abc""#, r#""123""#]);
363
364        test(r#"CONCAT("a", "b", "c")"#, &[r#""a""#, r#""b""#, r#""c""#]);
365
366        test(
367            r#"CUSTOM_FUNC("a", "b", "c")"#,
368            &[r#""a""#, r#""b""#, r#""c""#],
369        );
370
371        test(
372            r#"CONCAT("gluesql", " ", "is", " ", "cool")"#,
373            &[r#""gluesql""#, r#"" ""#, r#""is""#, r#"" ""#, r#""cool""#],
374        );
375
376        test(r#"POSITION("men" IN "ramen")"#, &[r#""men""#, r#""ramen""#]);
377        test(r#"POSITION("men" IN ramen)"#, &[r#""men""#, "ramen"]);
378
379        //TypedStringVariableArgs
380        test(
381            r#"CONCAT_WS(",", "gluesql", "is", "cool")"#,
382            &[r#"",""#, r#""gluesql""#, r#""is""#, r#""cool""#],
383        );
384    }
385}