1#![allow(clippy::missing_errors_doc, clippy::doc_markdown)]
2pub mod aggregation;
3pub mod comparison;
4pub mod destructured_query;
5pub mod error;
6pub mod filter;
7pub mod query_metadata;
8pub mod support;
9pub mod table;
10
11#[cfg(test)]
12mod tests {
13
14 use crate::query_metadata::QueryMetadata;
15 use crate::table::TabIdent;
16 use crate::{internal, malformed_query, unsupported};
17
18 use super::aggregation::{Aggregation, KoronFunction};
19 use super::comparison::CompareOp;
20 use super::error::ParseError;
21 use super::filter::Filter;
22
23 fn sample_sum() -> Aggregation {
24 Aggregation {
25 function: KoronFunction::Sum,
26 column: "test_column_2".to_string(),
27 alias: None,
28 }
29 }
30
31 fn sample_tab_ident() -> TabIdent {
32 TabIdent {
33 db: Some("test_db".to_string()),
34 schema: Some("test_schema".to_string()),
35 table: "test_table_1".to_string(),
36 }
37 }
38
39 #[test]
40 fn basic_aggregation() {
41 let cases = [
42 ("SUM(test_column_2)", KoronFunction::Sum),
43 ("COUNT(test_column_2)", KoronFunction::Count),
44 ("AVG(test_column_2)", KoronFunction::Average),
45 ("MEDIAN(test_column_2)", KoronFunction::Median),
46 ("VARIANCE(test_column_2)", KoronFunction::Variance),
47 ("STDDEV(test_column_2)", KoronFunction::StandardDeviation),
48 ("MIN(test_column_2)", KoronFunction::Min),
49 ("MAX(test_column_2)", KoronFunction::Max),
50 ];
51
52 for (projection, function) in cases {
53 let query = &format!("SELECT {projection} FROM test_db.test_schema.test_table_1");
54
55 let data_aggregation_query = if function == KoronFunction::Median {
56 None
57 } else {
58 Some(format!(
59 "SELECT CAST({projection} AS TEXT) FROM test_db.test_schema.test_table_1"
60 ))
61 };
62
63 let expected = Ok(QueryMetadata {
64 table: sample_tab_ident(),
65 aggregation: Aggregation {
66 function,
67 column: "test_column_2".to_string(),
68 alias: None,
69 },
70 filter: None,
71 data_extraction_query: String::from(
72 "SELECT test_column_2 FROM test_db.test_schema.test_table_1",
73 ),
74 data_aggregation_query,
75 });
76 assert_eq!(
77 QueryMetadata::parse(query, None),
78 expected,
79 "\nfailed for aggregation {projection}",
80 );
81 }
82 }
83
84 #[test]
85 fn parenthesized_query() {
86 let query = "(((SELECT SUM(test_column_2) FROM test_db.test_schema.test_table_1)))";
87 let expected = Ok(QueryMetadata {
88 table: sample_tab_ident(),
89 aggregation: sample_sum(),
90 filter: None,
91 data_extraction_query: String::from(
92 "SELECT test_column_2 FROM test_db.test_schema.test_table_1",
93 ),
94 data_aggregation_query: Some(String::from(
95 "SELECT CAST(SUM(test_column_2) AS TEXT) FROM test_db.test_schema.test_table_1",
96 )),
97 });
98 assert_eq!(QueryMetadata::parse(query, None), expected);
99 }
100
101 #[test]
102 fn parenthesized_function() {
103 let query = "SELECT (((SUM(test_column_2)))) FROM test_db.test_schema.test_table_1";
104 let expected = Ok(QueryMetadata {
105 table: sample_tab_ident(),
106 aggregation: sample_sum(),
107 filter: None,
108 data_extraction_query:String::from("SELECT test_column_2 FROM test_db.test_schema.test_table_1"),
109 data_aggregation_query: Some(String::from("SELECT CAST((((SUM(test_column_2)))) AS TEXT) FROM test_db.test_schema.test_table_1")),
110 });
111 assert_eq!(QueryMetadata::parse(query, None), expected);
112 }
113
114 #[test]
115 fn parenthesized_column() {
116 let query = "SELECT SUM((((test_column_2)))) FROM test_db.test_schema.test_table_1";
117 let expected = Ok(QueryMetadata {
118 table: sample_tab_ident(),
119 aggregation: sample_sum(),
120 filter: None,
121 data_extraction_query:String::from("SELECT test_column_2 FROM test_db.test_schema.test_table_1"),
122 data_aggregation_query: Some(String::from("SELECT CAST(SUM((((test_column_2)))) AS TEXT) FROM test_db.test_schema.test_table_1")),
123 });
124 assert_eq!(QueryMetadata::parse(query, None), expected);
125 }
126
127 #[test]
128 fn result_alias() {
129 let query = "SELECT SUM(test_column_2) AS s FROM test_db.test_schema.test_table_1";
130 let expected = Ok(QueryMetadata {
131 table: sample_tab_ident(),
132 aggregation: Aggregation {
133 function: KoronFunction::Sum,
134 column: "test_column_2".to_string(),
135 alias: Some("s".to_string()),
136 },
137 filter: None,
138 data_extraction_query:String::from("SELECT test_column_2 FROM test_db.test_schema.test_table_1"),
139 data_aggregation_query: Some(String::from("SELECT CAST(SUM(test_column_2) AS TEXT) AS s FROM test_db.test_schema.test_table_1")),
140 });
141 assert_eq!(QueryMetadata::parse(query, None), expected);
142 }
143
144 #[test]
145 fn table_alias() {
146 let query = "SELECT SUM(test_column_2) FROM test_db.test_schema.test_table_1 AS t";
147 let expected = Ok(QueryMetadata {
148 table: sample_tab_ident(),
149 aggregation: sample_sum(),
150 filter: None,
151 data_extraction_query:String::from("SELECT test_column_2 FROM test_db.test_schema.test_table_1"),
152 data_aggregation_query: Some(String::from("SELECT CAST(SUM(test_column_2) AS TEXT) FROM test_db.test_schema.test_table_1 AS t")),
153 });
154 assert_eq!(QueryMetadata::parse(query, None), expected);
155 }
156
157 #[test]
158 fn unquoted_function_case_insensitive() {
159 let query = "SELECT sum(test_column_2) FROM test_db.test_schema.test_table_1";
160 let expected = Ok(QueryMetadata {
161 table: sample_tab_ident(),
162 aggregation: sample_sum(),
163 filter: None,
164 data_extraction_query: String::from(
165 "SELECT test_column_2 FROM test_db.test_schema.test_table_1",
166 ),
167 data_aggregation_query: Some(String::from(
168 "SELECT CAST(sum(test_column_2) AS TEXT) FROM test_db.test_schema.test_table_1",
169 )),
170 });
171 assert_eq!(QueryMetadata::parse(query, None), expected);
172 }
173
174 #[test]
175 fn quoted_function_case_sensitive() {
176 let query = "SELECT \"SUM\"(test_column_2) FROM test_db.test_schema.test_table_1";
177 let expected = Err(unsupported!(
178 "unrecognized or unsupported function: \"SUM\".".to_string()
179 ));
180 assert_eq!(QueryMetadata::parse(query, None), expected);
181 }
182
183 #[test]
184 fn unquoted_result_alias_case_insensitive() {
185 let query = "SELECT SUM(test_column_2) AS S FROM test_db.test_schema.test_table_1";
186 let expected = Ok(QueryMetadata {
187 table: sample_tab_ident(),
188 aggregation: Aggregation {
189 function: KoronFunction::Sum,
190 column: "test_column_2".to_string(),
191 alias: Some("s".to_string()),
192 },
193 filter: None,
194 data_extraction_query:String::from("SELECT test_column_2 FROM test_db.test_schema.test_table_1"),
195 data_aggregation_query: Some(String::from("SELECT CAST(SUM(test_column_2) AS TEXT) AS S FROM test_db.test_schema.test_table_1")),
196 });
197 assert_eq!(QueryMetadata::parse(query, None), expected);
198 }
199
200 #[test]
201 fn quoted_result_alias_case_sensitive() {
202 let query = "SELECT SUM(test_column_2) AS \"S\" FROM test_db.test_schema.test_table_1";
203 let expected = Ok(QueryMetadata {
204 table: sample_tab_ident(),
205 aggregation: Aggregation {
206 function: KoronFunction::Sum,
207 column: "test_column_2".to_string(),
208 alias: Some("S".to_string()),
209 },
210 filter: None,
211 data_extraction_query:String::from("SELECT test_column_2 FROM test_db.test_schema.test_table_1"),
212 data_aggregation_query: Some(String::from("SELECT CAST(SUM(test_column_2) AS TEXT) AS \"S\" FROM test_db.test_schema.test_table_1")),
213 });
214 assert_eq!(QueryMetadata::parse(query, None), expected);
215 }
216
217 #[test]
218 fn quoted_table_alias_case_sensitive() {
219 for (column, alias, extracted_alias) in [
220 ("t.test_column_2", "\"T\"", "T"),
221 ("\"T\".test_column_2", "\"t\"", "t"),
222 ] {
223 let query =
224 &format!("SELECT SUM({column}) FROM test_db.test_schema.test_table_1 AS {alias}");
225
226 let expected = Err(malformed_query!(format!(
227 "the {column} column is not part of \
228 the table that's listed in the FROM clause ({extracted_alias}).",
229 )));
230 assert_eq!(
231 QueryMetadata::parse(query, None),
232 expected,
233 "\nfailed for query {query:?}",
234 );
235 }
236 }
237
238 #[test]
239 fn qualified_column_from_different_table() {
240 for column in [
241 "\"test_table_2\".test_column_2",
242 "\"test_schema\".test_table_2.test_column_2",
243 ] {
244 let query = &format!("SELECT SUM({column}) FROM test_db.test_schema.test_table_1");
245
246 let expected = Err(malformed_query!(format!(
247 "the {column} column is not part of \
248 the table that's listed in the FROM clause (test_db.test_schema.test_table_1).",
249 )));
250 assert_eq!(
251 QueryMetadata::parse(query, None),
252 expected,
253 "\nfailed for query {query:?}",
254 );
255 }
256 }
257
258 #[test]
259 fn qualified_column_not_from_table_alias() {
260 for column in [
261 "test_table_1.test_column_2",
262 "test_schema.test_table_1.test_column_2",
263 ] {
264 let query = &format!("SELECT SUM({column}) FROM test_db.test_schema.test_table_1 AS t");
265 let expected = Err(malformed_query!(format!(
266 "the {column} column is not part of \
267 the table that's listed in the FROM clause (t).",
268 )));
269 assert_eq!(
270 QueryMetadata::parse(query, None),
271 expected,
272 "\nfailed for query {query:?}",
273 );
274 }
275 }
276
277 #[test]
278 fn sql_syntax_error() {
279 let query = "SELECT * FROM";
280 let expected = Err(malformed_query!(
281 "sql parser error: Expected identifier, found: EOF".to_string()
282 ));
283 assert_eq!(QueryMetadata::parse(query, None), expected);
284 }
285
286 #[test]
287 fn table_name_too_many_name_parts() {
288 let query = "SELECT SUM(test_column_2) FROM x.test_db.test_schema.test_table_1";
289 let expected = Err(internal!("found too many ident in table name (i.e., x.test_db.test_schema.test_table_1) in query AST.".to_string()));
290 assert_eq!(QueryMetadata::parse(query, None), expected);
291 }
292
293 #[test]
294 fn column_name_too_many_name_parts() {
295 let query = "SELECT SUM(x.test_db.test_schema.test_table_1.test_column_2) FROM test_db.test_schema.test_table_1";
296 let expected = Err(internal!("found too many ident in column name (i.e., x.test_db.test_schema.test_table_1.test_column_2)."
297 .to_string()));
298 assert_eq!(QueryMetadata::parse(query, None), expected);
299 }
300
301 #[test]
302 fn wrong_number_of_arguments() {
303 let cases = [
304 (
305 "SUM()",
306 "the SUM function takes exactly 1 argument, but 0 are provided.",
307 ),
308 (
309 "SUM(test_column_2, test_column_2)",
310 "the SUM function takes exactly 1 argument, but 2 are provided.",
311 ),
312 ];
313
314 for (projection, reason) in cases {
315 let query = &format!("SELECT {projection} FROM test_db.test_schema.test_table_1");
316 let expected = Err(malformed_query!(reason.to_string()));
317 assert_eq!(
318 QueryMetadata::parse(query, None),
319 expected,
320 "\nfailed for aggregation {projection}",
321 );
322 }
323 }
324
325 #[test]
326 fn unsupported_sql_features() {
327 let cases = [
328 (
329 "SELECT * FROM test_db.test_schema.test_table_1; SELECT * FROM test_db.test_schema.test_table_1",
330 "statements different from single SELECT statement.",
331 ),
332 (
333 "DELETE FROM test_db.test_schema.test_table_1",
334 "statements different from single SELECT statement.",
335 ),
336 (
337 "WITH t AS (SELECT 1) SELECT SUM(test_column_2) FROM test_db.test_schema.test_table_1",
338 "CTEs (i.e., WITH clause).",
339 ),
340 (
341 "SELECT SUM(test_column_2) FROM test_db.test_schema.test_table_1 ORDER BY SUM",
342 "ORDER BY.",
343 ),
344 (
345 "SELECT SUM(test_column_2) FROM test_db.test_schema.test_table_1 LIMIT 1",
346 "LIMIT.",
347 ),
348 (
349 "SELECT SUM(test_column_2) FROM test_db.test_schema.test_table_1 OFFSET 1",
350 "OFFSET.",
351 ),
352 (
353 "SELECT SUM(test_column_2) FROM test_db.test_schema.test_table_1 FETCH FIRST 1 ROW ONLY",
354 "FETCH.",
355 ),
356 (
357 "SELECT SUM(test_column_2) FROM test_db.test_schema.test_table_1 drda FOR UPDATE",
358 "locking clauses (i.e., FOR UPDATE).",
359 ),
360 (
361 "SELECT SUM(test_column_2) FROM test_db.test_schema.test_table_1 \
362 UNION \
363 SELECT SUM(test_column_2) FROM test_db.test_schema.test_table_1",
364 "set operations (i.e., UNION).",
365 ),
366 ("VALUES (1)", "VALUES."),
367 (
368 "INSERT INTO test_table_1(test_column_2) VALUES(1)",
369 "statements different from single SELECT statement."
370 ),
371 (
372 "SELECT DISTINCT SUM(test_column_2) FROM test_db.test_schema.test_table_1",
373 "DISTINCT.",
374 ),
375 (
377 "SELECT TOP 1 SUM(test_column_2) FROM test_db.test_schema.test_table_1",
378 "TOP.",
379 ),
380 (
381 "SELECT SUM(test_column_2) INTO t FROM test_db.test_schema.test_table_1",
382 "SELECT INTO.",
383 ),
384 (
386 "SELECT SUM(test_column_2) FROM test_db.test_schema.test_table_1 LATERAL VIEW (SELECT 1) t",
387 "LATERAL VIEW.",
388 ),
389 (
390 "SELECT SUM(test_column_2) FROM test_db.test_schema.test_table_1 GROUP BY SUM",
391 "GROUP BY.",
392 ),
393 (
395 "SELECT SUM(test_column_2) FROM test_db.test_schema.test_table_1 CLUSTER BY SUM",
396 "CLUSTER BY.",
397 ),
398 (
400 "SELECT SUM(test_column_2) FROM test_db.test_schema.test_table_1 DISTRIBUTE BY SUM",
401 "DISTRIBUTE BY.",
402 ),
403 (
405 "SELECT SUM(test_column_2) FROM test_db.test_schema.test_table_1 SORT BY SUM",
406 "SORT BY.",
407 ),
408 (
409 "SELECT SUM(test_column_2) FROM test_db.test_schema.test_table_1 HAVING sum > 0",
410 "HAVING.",
411 ),
412 (
413 "SELECT SUM(test_column_2) FROM test_db.test_schema.test_table_1, treasury.attachment",
414 "the FROM clause has multiple tables (no JOINs, subqueries or functions allowed).",
415 ),
416 (
417 "SELECT SUM(test_column_2) FROM test_db.test_schema.test_table_1 CROSS JOIN treasury.attachment",
418 "the FROM clause has multiple tables (no JOINs, subqueries or functions allowed).",
419 ),
420 (
421 "SELECT SUM(test_column_2) FROM f('arg')",
422 "the FROM clause has multiple tables (no JOINs, subqueries or functions allowed).",
423 ),
424 (
426 "SELECT SUM(test_column_2) FROM test_db.test_schema.test_table_1 WITH (NOLOCK)",
427 "table hints (WITH in FROM clauses).",
428 ),
429 (
430 "SELECT SUM(test_column_2) FROM (SELECT * FROM test_db.test_schema.test_table_1)",
431 "the FROM clause has multiple tables (no JOINs, subqueries or functions allowed).",
432 ),
433 (
434 "SELECT SUM(test_column_2) FROM TABLE(f())",
435 "the FROM clause has multiple tables (no JOINs, subqueries or functions allowed).",
436 ),
437 (
438 "SELECT SUM(test_column_2) FROM (test_schema.test_table_1 CROSS JOIN treasury.attachment)",
439 "the FROM clause has multiple tables (no JOINs, subqueries or functions allowed).",
440 ),
441 (
442 "SELECT SUM(f) FROM test_db.test_schema.test_table_1 AS d (f, g)",
443 "table aliases with columns (such as d (f, g)).",
444 ),
445 (
446 "SELECT SUM(test_column_2), AVG(test_column_2) FROM test_db.test_schema.test_table_1",
447 "the SELECT clause must contain exactly one aggregation / analytic function. Nothing else is accepted.",
448 ),
449 (
450 "SELECT drda.* FROM test_db.test_schema.test_table_1",
451 "the SELECT clause must contain exactly one aggregation / analytic function. Nothing else is accepted.",
452 ),
453 (
454 "SELECT * FROM test_db.test_schema.test_table_1",
455 "the SELECT clause must contain exactly one aggregation / analytic function. Nothing else is accepted.",
456 ),
457 (
458 "SELECT id FROM test_db.test_schema.test_table_1",
459 "the SELECT clause must contain exactly one aggregation / analytic function. Nothing else is accepted.",
460 ),
461 (
462 "SELECT SUM(test_column_2) OVER (PARTITION BY id) FROM test_db.test_schema.test_table_1",
463 "window functions (OVER).",
464 ),
465 (
466 "SELECT SUM(DISTINCT test_column_2) FROM test_db.test_schema.test_table_1",
467 "DISTINCT.",
468 ),
469 (
470 "SELECT custom.aggregation(test_column_2) FROM test_db.test_schema.test_table_1",
471 "unrecognized or unsupported function: custom.aggregation.",
472 ),
473 (
474 "SELECT SUM(x => test_column_2) FROM test_db.test_schema.test_table_1",
475 "named function arguments (such as x => test_column_2).",
476 ),
477 (
478 "SELECT SUM(1) FROM test_db.test_schema.test_table_1",
479 "only a column name is supported as the argument of the SUM function.",
480 ),
481 (
482 "SELECT SUM(test_table_1.*) FROM test_db.test_schema.test_table_1",
483 "only a column name is supported as the argument of the SUM function.",
484 ),
485 (
486 "SELECT SUM(*) FROM test_db.test_schema.test_table_1",
487 "only a column name is supported as the argument of the SUM function.",
488 ),
489 (
490 "INSERT INTO test_table_1 SELECT * FROM test_db.test_schema.test_table_1",
491 "statements different from single SELECT statement.",
492 ),
493 (
494 "CREATE TABLE test_table_1 AS SELECT * FROM test_db.test_schema.test_table_1",
495 "statements different from single SELECT statement.",
496 ),
497 (
498 "SELECT SUM(test_column_2) FROM test_db.test_schema.test_table_1 WHERE test_column_2 BETWEEN 1 AND 2",
499 "unsupported expression in the WHERE clause: test_column_2 BETWEEN 1 AND 2.",
500 ),
501 (
502 "SELECT SUM(test_column_2) FROM test_db.test_schema.test_table_1 WHERE 2 < 1",
503 "2 < 1. Only comparisons between a column and a constant are supported.",
504 ),
505 (
506 "SELECT SUM(test_column_2) FROM test_db.test_schema.test_table_1 WHERE test_column_2 < test_column_3",
507 "test_column_2 < test_column_3. Only comparisons between a column and a constant are supported.",
508 ),
509 (
511 "SELECT KTHELEMENT(test_column_2, 3) FROM test_db.test_schema.test_table_1;",
512 "unrecognized or unsupported function: KTHELEMENT."
513 )
514 ];
515
516 for (query, reason) in cases {
517 let expected = Err(unsupported!(reason.to_string()));
518 assert_eq!(
519 QueryMetadata::parse(query, None),
520 expected,
521 "\nfailed for query {query:?}",
522 );
523 }
524 }
525
526 #[test]
527 fn aggregation_with_single_where() {
528 let cases = [
529 (
530 "test_column_2 < 1",
531 Filter {
532 column: "test_column_2".to_string(),
533 comparison: CompareOp::Lt {
534 value: "1".to_string(),
535 },
536 },
537 ),
538 (
539 "1 < test_column_2",
540 Filter {
541 column: "test_column_2".to_string(),
542 comparison: CompareOp::Gt {
543 value: "1".to_string(),
544 },
545 },
546 ),
547 (
548 "test_column_2 <= 1",
549 Filter {
550 column: "test_column_2".to_string(),
551 comparison: CompareOp::LtEq {
552 value: "1".to_string(),
553 },
554 },
555 ),
556 (
557 "1 <= test_column_2",
558 Filter {
559 column: "test_column_2".to_string(),
560 comparison: CompareOp::GtEq {
561 value: "1".to_string(),
562 },
563 },
564 ),
565 (
566 "test_column_2 > 1",
567 Filter {
568 column: "test_column_2".to_string(),
569 comparison: CompareOp::Gt {
570 value: "1".to_string(),
571 },
572 },
573 ),
574 (
575 "1 > test_column_2",
576 Filter {
577 column: "test_column_2".to_string(),
578 comparison: CompareOp::Lt {
579 value: "1".to_string(),
580 },
581 },
582 ),
583 (
584 "test_column_2 >= 1",
585 Filter {
586 column: "test_column_2".to_string(),
587 comparison: CompareOp::GtEq {
588 value: "1".to_string(),
589 },
590 },
591 ),
592 (
593 "1 >= test_column_2",
594 Filter {
595 column: "test_column_2".to_string(),
596 comparison: CompareOp::LtEq {
597 value: "1".to_string(),
598 },
599 },
600 ),
601 (
602 "test_column_3 > '2021-04-02T05:02:16.04+03:00'",
603 Filter {
604 column: "test_column_3".to_string(),
605 comparison: CompareOp::Gt {
606 value: "2021-04-02T05:02:16.04+03:00".to_string(),
607 },
608 },
609 ),
610 (
611 "-1 >= test_column_4",
612 Filter {
613 column: "test_column_4".to_string(),
614 comparison: CompareOp::LtEq {
615 value: "-1".to_string(),
616 },
617 },
618 ),
619 (
620 "+1 >= test_column_2",
621 Filter {
622 column: "test_column_2".to_string(),
623 comparison: CompareOp::LtEq {
624 value: "1".to_string(),
625 },
626 },
627 ),
628 (
629 "+1 = test_column_2",
630 Filter {
631 column: "test_column_2".to_string(),
632 comparison: CompareOp::Eq {
633 value: "1".to_string(),
634 },
635 },
636 ),
637 (
638 "+1 <> test_column_2",
639 Filter {
640 column: "test_column_2".to_string(),
641 comparison: CompareOp::NotEq {
642 value: "1".to_string(),
643 },
644 },
645 ),
646 (
647 "test_column_2 IS NULL",
648 Filter {
649 column: "test_column_2".to_string(),
650 comparison: CompareOp::IsNull,
651 },
652 ),
653 (
654 "test_column_2 IS NOT NULL",
655 Filter {
656 column: "test_column_2".to_string(),
657 comparison: CompareOp::IsNotNull,
658 },
659 ),
660 (
661 "test_column_1 = NULL",
662 Filter {
663 column: "test_column_1".to_string(),
664 comparison: CompareOp::Eq {
665 value: "Null".to_string(),
666 },
667 },
668 ),
669 (
670 "test_column_2 = NULL",
671 Filter {
672 column: "test_column_2".to_string(),
673 comparison: CompareOp::Eq {
674 value: "Null".to_string(),
675 },
676 },
677 ),
678 (
679 "test_column_3 = NULL",
680 Filter {
681 column: "test_column_3".to_string(),
682 comparison: CompareOp::Eq {
683 value: "Null".to_string(),
684 },
685 },
686 ),
687 (
688 "test_column_4 = NULL",
689 Filter {
690 column: "test_column_4".to_string(),
691 comparison: CompareOp::Eq {
692 value: "Null".to_string(),
693 },
694 },
695 ),
696 (
697 "test_column_5 IS TRUE",
698 Filter {
699 column: "test_column_5".to_string(),
700 comparison: CompareOp::IsTrue,
701 },
702 ),
703 (
704 "test_column_5 IS NOT TRUE",
705 Filter {
706 column: "test_column_5".to_string(),
707 comparison: CompareOp::IsNotTrue,
708 },
709 ),
710 (
711 "test_column_5 = true",
712 Filter {
713 column: "test_column_5".to_string(),
714 comparison: CompareOp::Eq {
715 value: "true".to_string(),
716 },
717 },
718 ),
719 (
720 "test_column_5 <> true",
721 Filter {
722 column: "test_column_5".to_string(),
723 comparison: CompareOp::NotEq {
724 value: "true".to_string(),
725 },
726 },
727 ),
728 (
729 "test_column_5 IS FALSE",
730 Filter {
731 column: "test_column_5".to_string(),
732 comparison: CompareOp::IsFalse,
733 },
734 ),
735 (
736 "test_column_5 IS NOT FALSE",
737 Filter {
738 column: "test_column_5".to_string(),
739 comparison: CompareOp::IsNotFalse,
740 },
741 ),
742 (
743 "test_column_5 = false",
744 Filter {
745 column: "test_column_5".to_string(),
746 comparison: CompareOp::Eq {
747 value: "false".to_string(),
748 },
749 },
750 ),
751 (
752 "test_column_5 <> false",
753 Filter {
754 column: "test_column_5".to_string(),
755 comparison: CompareOp::NotEq {
756 value: "false".to_string(),
757 },
758 },
759 ),
760 ];
761
762 let analytical_functions = [("SUM", KoronFunction::Sum), ("COUNT", KoronFunction::Count)];
763
764 let test_cases = |enum_fn: KoronFunction, query: &String| {
765 for (selection, filter) in cases.clone() {
766 let query = &format!("{query} WHERE {selection}");
767 let mut aggregation = sample_sum();
768 aggregation.function = enum_fn;
769 let expected_query = if &filter.column == "test_column_2" {
770 "SELECT test_column_2 FROM test_db.test_schema.test_table_1".to_string()
771 } else {
772 format!(
773 "SELECT test_column_2, {} FROM test_db.test_schema.test_table_1",
774 filter.column
775 )
776 };
777 let expected = QueryMetadata {
778 table: sample_tab_ident(),
779 aggregation,
780 filter: Some(filter.clone()),
781 data_extraction_query: expected_query,
782 data_aggregation_query: None,
783 };
784 let result = QueryMetadata::parse(query, None).unwrap();
785 assert_eq!(
786 result.aggregation, expected.aggregation,
787 "\nfailed for selection {selection:?}",
788 );
789 assert_eq!(
790 result.table, expected.table,
791 "\nfailed for selection {selection:?}",
792 );
793 assert_eq!(
794 result.filter, expected.filter,
795 "\nfailed for selection {selection:?}",
796 );
797 assert_eq!(
798 result.data_extraction_query, expected.data_extraction_query,
799 "\nfailed for selection {selection:?}",
800 );
801 }
802 };
803
804 for (function, enum_fn) in analytical_functions {
805 let query =
806 format!("SELECT {function}(test_column_2) FROM test_db.test_schema.test_table_1");
807 test_cases(enum_fn, &query);
808 }
809 }
810}