• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

gluesql / gluesql / 22275856923

22 Feb 2026 11:01AM UTC coverage: 98.17% (+0.09%) from 98.085%
22275856923

Pull #1883

github

web-flow
Merge 6026cc70f into 80a446e87
Pull Request #1883: Add patch coverage diff summary to PR coverage bot comment

21 of 29 new or added lines in 11 files covered. (72.41%)

67 existing lines in 24 files now uncovered.

42750 of 43547 relevant lines covered (98.17%)

66356.37 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

98.8
/core/src/plan/expr/plan_expr/function.rs
1
use {
2
    crate::ast::{Expr, Function},
3
    std::iter::{empty, once},
4
};
5

6
impl Function {
7
    pub fn as_exprs(&self) -> impl Iterator<Item = &Expr> {
1,059,949✔
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 {
13,162✔
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()),
20,123✔
27
            Self::Lower(expr)
18,426✔
28
            | Self::Length(expr)
11,938✔
29
            | Self::Initcap(expr)
5,265✔
30
            | Self::Upper(expr)
10,529✔
31
            | Self::Sin(expr)
7,897✔
32
            | Self::Cos(expr)
7,897✔
33
            | Self::Tan(expr)
7,897✔
34
            | Self::Asin(expr)
5,265✔
35
            | Self::Acos(expr)
6,581✔
36
            | Self::Atan(expr)
6,581✔
37
            | Self::Radians(expr)
15,793✔
38
            | Self::Degrees(expr)
13,161✔
39
            | Self::Ceil(expr)
13,161✔
40
            | Self::Rand(Some(expr))
7,897✔
41
            | Self::Round(expr)
13,162✔
42
            | Self::Trunc(expr)
13,161✔
43
            | Self::Floor(expr)
13,161✔
44
            | Self::Exp(expr)
6,581✔
45
            | Self::Ln(expr)
6,581✔
46
            | Self::Log2(expr)
6,581✔
47
            | Self::Log10(expr)
6,581✔
48
            | Self::Sqrt(expr)
7,897✔
49
            | Self::Abs(expr)
23,694✔
50
            | Self::Sign(expr)
21,062✔
51
            | Self::Ascii(expr)
13,160✔
52
            | Self::Chr(expr)
6,580✔
53
            | Self::Md5(expr)
×
54
            | Self::Hex(expr)
1✔
55
            | Self::LastDay(expr)
2,632✔
56
            | Self::Ltrim { expr, chars: None }
5,265✔
57
            | Self::Rtrim { expr, chars: None }
3,949✔
58
            | Self::Trim {
59
                expr,
10,529✔
60
                filter_chars: None,
61
                ..
62
            }
63
            | Self::Reverse(expr)
3,949✔
64
            | Self::Cast { expr, .. }
193,928✔
65
            | Self::Extract { expr, .. }
22,377✔
66
            | Self::GetX(expr)
5,264✔
67
            | Self::GetY(expr)
5,264✔
68
            | Self::IsEmpty(expr)
6,581✔
69
            | Self::Sort { expr, order: None }
3,948✔
70
            | Self::Dedup(expr)
3,949✔
71
            | Self::Entries(expr)
2,632✔
72
            | Self::Keys(expr)
5,264✔
73
            | Self::Values(expr) => Exprs::Single([expr].into_iter()),
558,602✔
74
            Self::Left { expr, size: expr2 }
10,529✔
75
            | Self::Right { expr, size: expr2 }
5,265✔
76
            | Self::Lpad {
77
                expr,
2,633✔
78
                size: expr2,
2,633✔
79
                fill: None,
80
            }
81
            | Self::Rpad {
82
                expr,
2,633✔
83
                size: expr2,
2,633✔
84
                fill: None,
85
            }
86
            | Self::Trim {
87
                expr,
15,795✔
88
                filter_chars: Some(expr2),
15,795✔
89
                ..
90
            }
91
            | Self::Log {
92
                antilog: expr,
9,213✔
93
                base: expr2,
9,213✔
94
            }
95
            | Self::Div {
96
                dividend: expr,
10,529✔
97
                divisor: expr2,
10,529✔
98
            }
99
            | Self::Mod {
100
                dividend: expr,
6,581✔
101
                divisor: expr2,
6,581✔
102
            }
103
            | Self::Gcd {
104
                left: expr,
10,529✔
105
                right: expr2,
10,529✔
106
            }
107
            | Self::Lcm {
108
                left: expr,
10,529✔
109
                right: expr2,
10,529✔
110
            }
111
            | Self::Format {
112
                expr,
17,108✔
113
                format: expr2,
17,108✔
114
            }
115
            | Self::ToDate {
116
                expr,
13,160✔
117
                format: expr2,
13,160✔
118
            }
119
            | Self::ToTimestamp {
120
                expr,
13,160✔
121
                format: expr2,
13,160✔
122
            }
123
            | Self::ToTime {
124
                expr,
6,580✔
125
                format: expr2,
6,580✔
126
            }
127
            | Self::Power { expr, power: expr2 }
14,477✔
128
            | Self::Ltrim {
129
                expr,
9,213✔
130
                chars: Some(expr2),
9,213✔
131
            }
132
            | Self::Rtrim {
133
                expr,
13,161✔
134
                chars: Some(expr2),
13,161✔
135
            }
136
            | Self::Repeat { expr, num: expr2 }
5,266✔
137
            | Self::Substr {
138
                expr,
30,268✔
139
                start: expr2,
30,268✔
140
                count: None,
141
            }
142
            | Self::IfNull { expr, then: expr2 }
27,636✔
143
            | Self::NullIf { expr1: expr, expr2 }
7,896✔
144
            | Self::Unwrap {
145
                expr,
17,109✔
146
                selector: expr2,
17,109✔
147
            }
148
            | Self::Position {
149
                from_expr: expr2,
5,266✔
150
                sub_expr: expr,
5,266✔
151
            }
152
            | Self::FindIdx {
153
                from_expr: expr,
6,581✔
154
                sub_expr: expr2,
6,581✔
155
                start: None,
156
            }
157
            | Self::Append { expr, value: expr2 }
3,948✔
158
            | Self::Prepend { expr, value: expr2 }
3,948✔
159
            | Self::Skip { expr, size: expr2 }
9,213✔
160
            | Self::Sort {
161
                expr,
9,212✔
162
                order: Some(expr2),
9,212✔
163
            }
164
            | Self::Take { expr, size: expr2 }
11,845✔
165
            | Self::Point { x: expr, y: expr2 }
5,264✔
166
            | Self::CalcDistance {
167
                geometry1: expr,
3,948✔
168
                geometry2: expr2,
3,948✔
169
            }
170
            | Self::AddMonth { expr, size: expr2 } => Exprs::Double([expr, expr2].into_iter()),
332,971✔
171

172
            Self::Lpad {
173
                expr,
10,529✔
174
                size: expr2,
10,529✔
175
                fill: Some(expr3),
10,529✔
176
            }
177
            | Self::Rpad {
178
                expr,
10,529✔
179
                size: expr2,
10,529✔
180
                fill: Some(expr3),
10,529✔
181
            }
182
            | Self::Substr {
183
                expr,
36,849✔
184
                start: expr2,
36,849✔
185
                count: Some(expr3),
36,849✔
186
            }
187
            | Self::Replace {
188
                expr,
5,264✔
189
                old: expr2,
5,264✔
190
                new: expr3,
5,264✔
191
            }
192
            | Self::Slice {
193
                expr,
15,792✔
194
                start: expr2,
15,792✔
195
                length: expr3,
15,792✔
196
            }
197
            | Self::FindIdx {
198
                from_expr: expr,
5,264✔
199
                sub_expr: expr2,
5,264✔
200
                start: Some(expr3),
5,264✔
201
            }
202
            | Self::Splice {
203
                list_data: expr,
2,633✔
204
                begin_index: expr2,
2,633✔
205
                end_index: expr3,
2,633✔
206
                values: None,
207
            } => Exprs::Triple([expr, expr2, expr3].into_iter()),
86,860✔
208
            Self::Custom { name: _, exprs }
849✔
209
            | Self::Coalesce(exprs)
26,322✔
210
            | Self::Concat(exprs)
9,216✔
211
            | Self::Greatest(exprs) => Exprs::VariableArgs(exprs.iter()),
46,915✔
212
            Self::ConcatWs { separator, exprs } => {
9,213✔
213
                Exprs::VariableArgsWithSingle(once(separator).chain(exprs.iter()))
9,213✔
214
            }
215
            Self::Splice {
216
                list_data: expr,
5,265✔
217
                begin_index: expr2,
5,265✔
218
                end_index: expr3,
5,265✔
219
                values: Some(expr4),
5,265✔
220
            } => Exprs::Quadruple([expr, expr2, expr3, expr4].into_iter()),
5,265✔
221
        }
222
    }
1,059,949✔
223
}
224

225
#[cfg(test)]
226
mod 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 {
207✔
234
        let parsed = parse_expr(sql).expect(sql);
207✔
235

236
        translate_expr(&parsed, NO_PARAMS).expect(sql)
207✔
237
    }
207✔
238

239
    fn test(sql: &str, expected: &[&str]) {
85✔
240
        let function = match expr(sql) {
85✔
241
            Expr::Function(function) => *function,
85✔
242
            _ => unreachable!("only for function tests"),
×
243
        };
244
        let actual = function.as_exprs();
85✔
245
        let actual = actual.collect::<Vec<_>>();
85✔
246

247
        assert_eq!(actual.len(), expected.len(), "{sql}");
85✔
248

249
        for (expected, actual) in expected.iter().zip(actual.into_iter()) {
122✔
250
            assert_eq!(actual, &expr(expected), "{sql}");
122✔
251
        }
252
    }
85✔
253

254
    #[test]
255
    fn as_exprs() {
1✔
256
        if std::env::var_os("GLUESQL_COVERAGE_BOT_MISS").is_some() {
1✔
NEW
257
            std::hint::black_box(1_u8);
×
258
        }
1✔
259
        // Empty
260
        test("NOW()", &[]);
1✔
261
        test("CURRENT_DATE()", &[]);
1✔
262
        test("CURRENT_TIME()", &[]);
1✔
263
        test("CURRENT_TIMESTAMP()", &[]);
1✔
264
        test("PI()", &[]);
1✔
265
        test("GENERATE_UUID()", &[]);
1✔
266
        test("RAND()", &[]);
1✔
267
        test("CUSTOM_FUNC()", &[]);
1✔
268

269
        // Single
270
        test("LOWER(id)", &["id"]);
1✔
271
        test("INITCAP(id)", &["id"]);
1✔
272
        test(r#"UPPER("Hello")"#, &[r#""Hello""#]);
1✔
273
        test("SIN(3.14)", &["3.14"]);
1✔
274
        test("COS(3.14)", &["3.14"]);
1✔
275
        test("TAN(3.14)", &["3.14"]);
1✔
276
        test("ASIN(3.14)", &["3.14"]);
1✔
277
        test("ACOS(3.14)", &["3.14"]);
1✔
278
        test("ATAN(3.14)", &["3.14"]);
1✔
279
        test("RADIANS(180)", &["180"]);
1✔
280
        test("DEGREES(3.14)", &["3.14"]);
1✔
281
        test("CEIL(1.23)", &["1.23"]);
1✔
282
        test("Rand(1.23)", &["1.23"]);
1✔
283
        test("ROUND(1.23)", &["1.23"]);
1✔
284
        test("TRUNC(1.23)", &["1.23"]);
1✔
285
        test("FLOOR(1.23)", &["1.23"]);
1✔
286
        test("EXP(1.23)", &["1.23"]);
1✔
287
        test("LN(col + 1)", &["col + 1"]);
1✔
288
        test("LOG2(16)", &["16"]);
1✔
289
        test("LOG10(150 - 50)", &["150 - 50"]);
1✔
290
        test("SQRT(144)", &["144"]);
1✔
291
        test("LASTDAY(DATE '2020-01-01')", &[r"DATE '2020-01-01'"]);
1✔
292
        test(r#"LTRIM("  hello")"#, &[r#""  hello""#]);
1✔
293
        test(r#"RTRIM("world  ")"#, &[r#""world  ""#]);
1✔
294
        test(r#"TRIM("  rust  ")"#, &[r#""  rust  ""#]);
1✔
295
        test(r#"REVERSE("abcde")"#, &[r#""abcde""#]);
1✔
296
        test(r"CAST(1 AS BOOLEAN)", &["1"]);
1✔
297
        test(r"IS_EMPTY(col)", &["col"]);
1✔
298
        test(r"VALUES(col)", &["col"]);
1✔
299
        test(r"HEX(10)", &["10"]);
1✔
300

301
        test(r"ABS(1)", &["1"]);
1✔
302
        test(r"ABS(-1)", &["-1"]);
1✔
303
        test(r"ABS(2)", &["2"]);
1✔
304
        test(r"ABS(-2)", &["-2"]);
1✔
305
        test(r"ABS(3.0)", &["3.0"]);
1✔
306
        test(r"ABS(-3.0)", &["-3.0"]);
1✔
307

308
        test(r"SIGN(1)", &["1"]);
1✔
309
        test(r"SIGN(-1)", &["-1"]);
1✔
310
        test(r"SIGN(2)", &["2"]);
1✔
311
        test(r"SIGN(-2)", &["-2"]);
1✔
312
        test(r"SIGN(3.0)", &["3.0"]);
1✔
313
        test(r"SIGN(-3.0)", &["-3.0"]);
1✔
314

315
        test(r"DEDUP(list)", &["list"]);
1✔
316

317
        // Double
318
        test(r#"LEFT("hello", 2)"#, &[r#""hello""#, "2"]);
1✔
319
        test(r#"RIGHT("hello", 2)"#, &[r#""hello""#, "2"]);
1✔
320
        test(r#"FIND_IDX("Calzone", "zone")"#, &[r"Calzone", r"zone"]);
1✔
321
        test(r"TAKE(list, 3)", &[r"list", r"3"]);
1✔
322
        test(r"LPAD(value, 5)", &["value", "5"]);
1✔
323
        test(r"RPAD(value, 5)", &["value", "5"]);
1✔
324
        test(
1✔
325
            r#"TRIM(LEADING "_" FROM "__hello")"#,
1✔
326
            &[r#""__hello""#, r#""_""#],
1✔
327
        );
328
        test("LOG(rate, 2)", &["rate", "2"]);
1✔
329
        test("DIV(6, 2)", &["6", "2"]);
1✔
330
        test("MOD(6, 2)", &["6", "2"]);
1✔
331
        test("GCD(6, 2)", &["6", "2"]);
1✔
332
        test("LCM(6, 2)", &["6", "2"]);
1✔
333
        test("POWER(base, 10)", &["base", "10"]);
1✔
334
        test(r#"LTRIM(name, "xyz")"#, &["name", r#""xyz""#]);
1✔
335
        test(r#"RTRIM(name, "xyz")"#, &["name", r#""xyz""#]);
1✔
336
        test("REPEAT(col || col2, 3)", &["col || col2", "3"]);
1✔
337
        test("REPEAT(column, 2)", &["column", "2"]);
1✔
338
        test(r#"UNWRAP(field, "foo.1")"#, &["field", r#""foo.1""#]);
1✔
339
        test(r"SKIP(list, 2)", &[r#""list""#, r"2"]);
1✔
340

341
        // Triple
342
        test(
1✔
343
            r#"LPAD(name, 20, '>")++++<')"#,
1✔
344
            &["name", "20", r#"'>")++++<'"#],
1✔
345
        );
346
        test(
1✔
347
            r#"RPAD(name, 20, '>")++++<')"#,
1✔
348
            &["name", "20", r#"'>")++++<'"#],
1✔
349
        );
350
        test(
1✔
351
            r#"SUBSTR('   >++++("<   ', 3, 11)"#,
1✔
352
            &[r#"'   >++++("<   '"#, "3", "11"],
1✔
353
        );
354
        test(r"SPLICE(list, 2, 4)", &["list", "2", "4"]);
1✔
355

356
        // Quadruple
357
        test(r"SPLICE(list, 3, 5, values)", &["list", "3", "5", "values"]);
1✔
358

359
        //VariableArgs
360
        test(r#"COALESCE("test")"#, &[r#""test""#]);
1✔
361

362
        test(r#"COALESCE(NULL, "test")"#, &["NULL", r#""test""#]);
1✔
363

364
        test(r#"CONCAT("abc")"#, &[r#""abc""#]);
1✔
365

366
        test(r#"CONCAT("abc", "123")"#, &[r#""abc""#, r#""123""#]);
1✔
367

368
        test(r#"CONCAT("a", "b", "c")"#, &[r#""a""#, r#""b""#, r#""c""#]);
1✔
369

370
        test(
1✔
371
            r#"CUSTOM_FUNC("a", "b", "c")"#,
1✔
372
            &[r#""a""#, r#""b""#, r#""c""#],
1✔
373
        );
374

375
        test(
1✔
376
            r#"CONCAT("gluesql", " ", "is", " ", "cool")"#,
1✔
377
            &[r#""gluesql""#, r#"" ""#, r#""is""#, r#"" ""#, r#""cool""#],
1✔
378
        );
379

380
        test(r#"POSITION("men" IN "ramen")"#, &[r#""men""#, r#""ramen""#]);
1✔
381
        test(r#"POSITION("men" IN ramen)"#, &[r#""men""#, "ramen"]);
1✔
382

383
        //TypedStringVariableArgs
384
        test(
1✔
385
            r#"CONCAT_WS(",", "gluesql", "is", "cool")"#,
1✔
386
            &[r#"",""#, r#""gluesql""#, r#""is""#, r#""cool""#],
1✔
387
        );
388
    }
1✔
389
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc