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 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 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 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 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 test(r"SPLICE(list, 3, 5, values)", &["list", "3", "5", "values"]);
355
356 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 test(
382 r#"CONCAT_WS(",", "gluesql", "is", "cool")"#,
383 &[r#"",""#, r#""gluesql""#, r#""is""#, r#""cool""#],
384 );
385 }
386}