1use {
2 super::{Expr, IndexOperator, ToSqlUnquoted},
3 crate::ast::ToSql,
4 itertools::Itertools,
5 serde::{Deserialize, Serialize},
6 strum_macros::Display,
7};
8
9#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
10pub struct Query {
11 pub body: SetExpr,
12 pub order_by: Vec<OrderByExpr>,
13 pub limit: Option<Expr>,
14 pub offset: Option<Expr>,
15}
16
17#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
18pub enum SetExpr {
19 Select(Box<Select>),
20 Values(Values),
21}
22
23#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
24pub struct Select {
25 pub distinct: bool,
26 pub projection: Vec<SelectItem>,
27 pub from: TableWithJoins,
28 pub selection: Option<Expr>,
30 pub group_by: Vec<Expr>,
31 pub having: Option<Expr>,
32}
33
34#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
35pub enum SelectItem {
36 Expr { expr: Expr, label: String },
38 QualifiedWildcard(String),
40 Wildcard,
42}
43
44#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
45pub struct TableWithJoins {
46 pub relation: TableFactor,
47 pub joins: Vec<Join>,
48}
49
50#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
51pub enum IndexItem {
52 PrimaryKey(Expr),
53 NonClustered {
54 name: String,
55 asc: Option<bool>,
56 cmp_expr: Option<(IndexOperator, Expr)>,
57 },
58}
59
60#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
61pub enum TableFactor {
62 Table {
63 name: String,
64 alias: Option<TableAlias>,
65 index: Option<IndexItem>,
67 },
68 Derived {
69 subquery: Query,
70 alias: TableAlias,
71 },
72 Series {
73 alias: TableAlias,
74 size: Expr,
75 },
76 Dictionary {
77 dict: Dictionary,
78 alias: TableAlias,
79 },
80}
81
82#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize, Display)]
83#[strum(serialize_all = "SCREAMING_SNAKE_CASE")]
84pub enum Dictionary {
85 GlueTables,
86 GlueTableColumns,
87 GlueIndexes,
88 GlueObjects,
89}
90
91#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
92pub struct TableAlias {
93 pub name: String,
94 pub columns: Vec<String>,
95}
96
97#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
98pub struct Join {
99 pub relation: TableFactor,
100 pub join_operator: JoinOperator,
101 pub join_executor: JoinExecutor,
102}
103
104#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
105pub enum JoinExecutor {
106 NestedLoop,
107 Hash {
108 key_expr: Expr,
109 value_expr: Expr,
110 where_clause: Option<Expr>,
111 },
112}
113
114#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
115pub enum JoinOperator {
116 Inner(JoinConstraint),
117 LeftOuter(JoinConstraint),
118}
119
120#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
121pub enum JoinConstraint {
122 On(Expr),
123 None,
124}
125
126#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
127pub struct OrderByExpr {
128 pub expr: Expr,
129 pub asc: Option<bool>,
130}
131
132#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
133pub struct Values(pub Vec<Vec<Expr>>);
134
135impl ToSql for Query {
136 fn to_sql(&self) -> String {
137 self.to_sql_with(true)
138 }
139}
140
141impl ToSqlUnquoted for Query {
142 fn to_sql_unquoted(&self) -> String {
143 self.to_sql_with(false)
144 }
145}
146
147impl Query {
148 fn to_sql_with(&self, quoted: bool) -> String {
149 let to_sql = |expr: &Expr| match quoted {
150 true => expr.to_sql(),
151 false => expr.to_sql_unquoted(),
152 };
153
154 let Query {
155 body,
156 order_by,
157 limit,
158 offset,
159 } = self;
160
161 let order_by = if order_by.is_empty() {
162 "".to_owned()
163 } else {
164 format!(
165 "ORDER BY {}",
166 order_by
167 .iter()
168 .map(|expr| expr.to_sql_with(quoted))
169 .join(" ")
170 )
171 };
172
173 let limit = match limit {
174 Some(expr) => format!("LIMIT {}", to_sql(expr)),
175 _ => "".to_owned(),
176 };
177
178 let offset = match offset {
179 Some(expr) => format!("OFFSET {}", to_sql(expr)),
180 _ => "".to_owned(),
181 };
182
183 let string = [order_by, limit, offset]
184 .iter()
185 .filter(|sql| !sql.is_empty())
186 .join(" ");
187
188 if string.is_empty() {
189 body.to_sql_with(quoted)
190 } else {
191 format!("{} {}", body.to_sql_with(quoted), string)
192 }
193 }
194}
195
196impl ToSql for SetExpr {
197 fn to_sql(&self) -> String {
198 self.to_sql_with(true)
199 }
200}
201
202impl ToSqlUnquoted for SetExpr {
203 fn to_sql_unquoted(&self) -> String {
204 self.to_sql_with(false)
205 }
206}
207
208impl SetExpr {
209 fn to_sql_with(&self, quoted: bool) -> String {
210 match (self, quoted) {
211 (SetExpr::Select(select), true) => select.to_sql(),
212 (SetExpr::Select(select), false) => select.to_sql_unquoted(),
213 (SetExpr::Values(values), true) => format!("VALUES {}", values.to_sql()),
214 (SetExpr::Values(values), false) => format!("VALUES {}", values.to_sql_unquoted()),
215 }
216 }
217}
218
219impl ToSql for Select {
220 fn to_sql(&self) -> String {
221 self.to_sql_with(true)
222 }
223}
224
225impl ToSqlUnquoted for Select {
226 fn to_sql_unquoted(&self) -> String {
227 self.to_sql_with(false)
228 }
229}
230
231impl Select {
232 fn to_sql_with(&self, quoted: bool) -> String {
233 let to_sql = |expr: &Expr| match quoted {
234 true => expr.to_sql(),
235 false => expr.to_sql_unquoted(),
236 };
237
238 let Select {
239 distinct,
240 projection,
241 from,
242 selection,
243 group_by,
244 having,
245 } = self;
246 let projection = projection
247 .iter()
248 .map(|item| item.to_sql_with(quoted))
249 .join(", ");
250
251 let selection = match selection {
252 Some(expr) => format!("WHERE {}", to_sql(expr)),
253 None => "".to_owned(),
254 };
255
256 let group_by = if group_by.is_empty() {
257 "".to_owned()
258 } else {
259 format!("GROUP BY {}", group_by.iter().map(to_sql).join(", "))
260 };
261
262 let having = match having {
263 Some(having) => format!("HAVING {}", to_sql(having)),
264 None => "".to_owned(),
265 };
266
267 let condition = [selection, group_by, having]
268 .iter()
269 .filter(|sql| !sql.is_empty())
270 .join(" ");
271
272 let distinct = if *distinct { "DISTINCT " } else { "" };
273
274 if condition.is_empty() {
275 format!(
276 "SELECT {}{projection} FROM {}",
277 distinct,
278 from.to_sql_with(quoted)
279 )
280 } else {
281 format!(
282 "SELECT {}{projection} FROM {} {condition}",
283 distinct,
284 from.to_sql_with(quoted)
285 )
286 }
287 }
288}
289
290impl ToSql for SelectItem {
291 fn to_sql(&self) -> String {
292 self.to_sql_with(true)
293 }
294}
295
296impl ToSqlUnquoted for SelectItem {
297 fn to_sql_unquoted(&self) -> String {
298 self.to_sql_with(false)
299 }
300}
301
302impl SelectItem {
303 fn to_sql_with(&self, quoted: bool) -> String {
304 let to_sql = |expr: &Expr| match quoted {
305 true => expr.to_sql(),
306 false => expr.to_sql_unquoted(),
307 };
308
309 match self {
310 SelectItem::Expr { expr, label } => {
311 let expr = to_sql(expr);
312 match (label.is_empty(), quoted) {
313 (true, _) => expr,
314 (false, true) => format!(r#"{expr} AS "{label}""#),
315 (false, false) => format!("{expr} AS {label}"),
316 }
317 }
318 SelectItem::QualifiedWildcard(obj) => match quoted {
319 true => format!(r#""{obj}".*"#),
320 false => format!("{obj}.*"),
321 },
322 SelectItem::Wildcard => "*".to_owned(),
323 }
324 }
325}
326
327impl ToSql for TableWithJoins {
328 fn to_sql(&self) -> String {
329 self.to_sql_with(true)
330 }
331}
332
333impl ToSqlUnquoted for TableWithJoins {
334 fn to_sql_unquoted(&self) -> String {
335 self.to_sql_with(false)
336 }
337}
338
339impl TableWithJoins {
340 fn to_sql_with(&self, quoted: bool) -> String {
341 let TableWithJoins { relation, joins } = self;
342
343 if joins.is_empty() {
344 relation.to_sql_with(quoted)
345 } else {
346 format!(
347 "{} {}",
348 relation.to_sql_with(quoted),
349 joins.iter().map(|join| join.to_sql_with(quoted)).join(" ")
350 )
351 }
352 }
353}
354
355impl ToSql for TableFactor {
356 fn to_sql(&self) -> String {
357 self.to_sql_with(true)
358 }
359}
360
361impl ToSqlUnquoted for TableFactor {
362 fn to_sql_unquoted(&self) -> String {
363 self.to_sql_with(false)
364 }
365}
366
367impl TableFactor {
368 fn to_sql_with(&self, quoted: bool) -> String {
369 let to_sql = |expr: &Expr| match quoted {
370 true => expr.to_sql(),
371 false => expr.to_sql_unquoted(),
372 };
373
374 match (self, quoted) {
375 (TableFactor::Table { name, alias, .. }, true) => match alias {
376 Some(alias) => format!(r#""{}" {}"#, name, alias.to_sql_with(quoted)),
377 None => format!(r#""{name}""#),
378 },
379 (TableFactor::Table { name, alias, .. }, false) => match alias {
380 Some(alias) => format!("{} {}", name, alias.to_sql_with(quoted)),
381 None => name.to_owned(),
382 },
383 (TableFactor::Derived { subquery, alias }, _) => {
384 format!(
385 "({}) {}",
386 subquery.to_sql_with(quoted),
387 alias.to_sql_with(quoted)
388 )
389 }
390 (TableFactor::Series { alias, size }, _) => {
391 format!("SERIES({}) {}", to_sql(size), alias.to_sql_with(quoted))
392 }
393 (TableFactor::Dictionary { dict, alias }, true) => {
394 format!(r#""{dict}" {}"#, alias.to_sql_with(quoted))
395 }
396 (TableFactor::Dictionary { dict, alias }, false) => {
397 format!("{dict} {}", alias.to_sql_with(quoted))
398 }
399 }
400 }
401}
402
403impl ToSql for TableAlias {
404 fn to_sql(&self) -> String {
405 self.to_sql_with(true)
406 }
407}
408
409impl ToSqlUnquoted for TableAlias {
410 fn to_sql_unquoted(&self) -> String {
411 self.to_sql_with(false)
412 }
413}
414
415impl TableAlias {
416 fn to_sql_with(&self, quoted: bool) -> String {
417 let TableAlias { name, .. } = self;
418
419 match quoted {
420 true => format!(r#"AS "{name}""#),
421 false => format!("AS {name}"),
422 }
423 }
424}
425
426impl ToSql for Join {
427 fn to_sql(&self) -> String {
428 self.to_sql_with(true)
429 }
430}
431
432impl ToSqlUnquoted for Join {
433 fn to_sql_unquoted(&self) -> String {
434 self.to_sql_with(false)
435 }
436}
437
438impl Join {
439 fn to_sql_with(&self, quoted: bool) -> String {
440 let Join {
441 relation,
442 join_operator,
443 join_executor,
444 } = self;
445
446 let (join_operator, join_constraint) = match join_operator {
447 JoinOperator::Inner(join_constraint) => ("INNER JOIN", join_constraint),
448 JoinOperator::LeftOuter(join_constraint) => ("LEFT OUTER JOIN", join_constraint),
449 };
450
451 let (join_constraint, join_executor) = match quoted {
452 true => (join_constraint.to_sql(), join_executor.to_sql()),
453 false => (
454 join_constraint.to_sql_unquoted(),
455 join_executor.to_sql_unquoted(),
456 ),
457 };
458
459 let join_constraints = [join_constraint, join_executor]
460 .iter()
461 .filter(|sql| !sql.is_empty())
462 .join(" AND ");
463
464 if join_constraints.is_empty() {
465 format!("{join_operator} {}", relation.to_sql_with(quoted))
466 } else {
467 format!(
468 "{join_operator} {} ON {join_constraints}",
469 relation.to_sql_with(quoted)
470 )
471 }
472 }
473}
474
475impl ToSql for JoinExecutor {
476 fn to_sql(&self) -> String {
477 self.to_sql_with(true)
478 }
479}
480
481impl ToSqlUnquoted for JoinExecutor {
482 fn to_sql_unquoted(&self) -> String {
483 self.to_sql_with(false)
484 }
485}
486
487impl JoinExecutor {
488 fn to_sql_with(&self, quoted: bool) -> String {
489 let to_sql = |expr: &Expr| match quoted {
490 true => expr.to_sql(),
491 false => expr.to_sql_unquoted(),
492 };
493
494 match self {
495 JoinExecutor::NestedLoop => "".to_owned(),
496 JoinExecutor::Hash {
497 key_expr,
498 value_expr,
499 where_clause,
500 } => {
501 let key_value = format!("{} = {}", to_sql(key_expr), to_sql(value_expr));
502 match where_clause {
503 Some(expr) => format!("{key_value} AND {}", to_sql(expr)),
504 None => key_value,
505 }
506 }
507 }
508 }
509}
510
511impl ToSql for JoinConstraint {
512 fn to_sql(&self) -> String {
513 self.to_sql_with(true)
514 }
515}
516
517impl ToSqlUnquoted for JoinConstraint {
518 fn to_sql_unquoted(&self) -> String {
519 self.to_sql_with(false)
520 }
521}
522
523impl JoinConstraint {
524 fn to_sql_with(&self, quoted: bool) -> String {
525 match (self, quoted) {
526 (JoinConstraint::On(expr), true) => expr.to_sql(),
527 (JoinConstraint::On(expr), false) => expr.to_sql_unquoted(),
528 (JoinConstraint::None, _) => "".to_owned(),
529 }
530 }
531}
532
533impl ToSql for OrderByExpr {
534 fn to_sql(&self) -> String {
535 self.to_sql_with(true)
536 }
537}
538
539impl ToSqlUnquoted for OrderByExpr {
540 fn to_sql_unquoted(&self) -> String {
541 self.to_sql_with(false)
542 }
543}
544
545impl OrderByExpr {
546 fn to_sql_with(&self, quoted: bool) -> String {
547 let OrderByExpr { expr, asc } = self;
548 let expr = match quoted {
549 true => expr.to_sql(),
550 false => expr.to_sql_unquoted(),
551 };
552
553 match asc {
554 Some(true) => format!("{expr} ASC"),
555 Some(false) => format!("{expr} DESC"),
556 None => expr,
557 }
558 }
559}
560
561impl ToSql for Values {
562 fn to_sql(&self) -> String {
563 self.to_sql_with(true)
564 }
565}
566
567impl ToSqlUnquoted for Values {
568 fn to_sql_unquoted(&self) -> String {
569 self.to_sql_with(false)
570 }
571}
572
573impl Values {
574 fn to_sql_with(&self, quoted: bool) -> String {
575 let Values(expr) = self;
576
577 expr.iter()
578 .map(|value| {
579 format!(
580 "({})",
581 value
582 .iter()
583 .map(|expr| match quoted {
584 true => expr.to_sql(),
585 false => expr.to_sql_unquoted(),
586 })
587 .join(", ")
588 )
589 })
590 .join(", ")
591 }
592}
593
594#[cfg(test)]
595mod tests {
596 use {
597 crate::{
598 ast::{
599 AstLiteral, BinaryOperator, Dictionary, Expr, Join, JoinConstraint, JoinExecutor,
600 JoinOperator, OrderByExpr, Query, Select, SelectItem, SetExpr, TableAlias,
601 TableFactor, TableWithJoins, ToSql, ToSqlUnquoted, Values,
602 },
603 parse_sql::parse_expr,
604 translate::translate_expr,
605 },
606 bigdecimal::BigDecimal,
607 std::str::FromStr,
608 };
609
610 fn expr(sql: &str) -> Expr {
611 let parsed = parse_expr(sql).expect(sql);
612
613 translate_expr(&parsed).expect(sql)
614 }
615
616 #[test]
617 fn to_sql_query() {
618 let order_by = vec![OrderByExpr {
619 expr: Expr::Identifier("name".to_owned()),
620 asc: Some(true),
621 }];
622 let actual =
623 r#"SELECT * FROM "FOO" AS "F" ORDER BY "name" ASC LIMIT 10 OFFSET 3"#.to_owned();
624 let expected = Query {
625 body: SetExpr::Select(Box::new(Select {
626 distinct: false,
627 projection: vec![SelectItem::Wildcard],
628 from: TableWithJoins {
629 relation: TableFactor::Table {
630 name: "FOO".to_owned(),
631 alias: Some(TableAlias {
632 name: "F".to_owned(),
633 columns: Vec::new(),
634 }),
635 index: None,
636 },
637 joins: Vec::new(),
638 },
639 selection: None,
640 group_by: Vec::new(),
641 having: None,
642 })),
643 order_by,
644 limit: Some(Expr::Literal(AstLiteral::Number(
645 BigDecimal::from_str("10").unwrap(),
646 ))),
647 offset: Some(Expr::Literal(AstLiteral::Number(
648 BigDecimal::from_str("3").unwrap(),
649 ))),
650 }
651 .to_sql();
652 assert_eq!(actual, expected);
653 }
654
655 #[test]
656 fn to_sql_unquoted_query() {
657 let order_by = vec![OrderByExpr {
658 expr: Expr::Identifier("name".to_owned()),
659 asc: Some(true),
660 }];
661 let actual = "SELECT * FROM FOO AS F ORDER BY name ASC LIMIT 10 OFFSET 3".to_owned();
662 let expected = Query {
663 body: SetExpr::Select(Box::new(Select {
664 distinct: false,
665 projection: vec![SelectItem::Wildcard],
666 from: TableWithJoins {
667 relation: TableFactor::Table {
668 name: "FOO".to_owned(),
669 alias: Some(TableAlias {
670 name: "F".to_owned(),
671 columns: Vec::new(),
672 }),
673 index: None,
674 },
675 joins: Vec::new(),
676 },
677 selection: None,
678 group_by: Vec::new(),
679 having: None,
680 })),
681 order_by,
682 limit: Some(Expr::Literal(AstLiteral::Number(
683 BigDecimal::from_str("10").unwrap(),
684 ))),
685 offset: Some(Expr::Literal(AstLiteral::Number(
686 BigDecimal::from_str("3").unwrap(),
687 ))),
688 }
689 .to_sql_unquoted();
690 assert_eq!(actual, expected);
691 }
692
693 #[test]
694 fn to_sql_set_expr() {
695 let actual = r#"SELECT * FROM "FOO" AS "F" INNER JOIN "PlayerItem""#.to_owned();
696 let expected = SetExpr::Select(Box::new(Select {
697 distinct: false,
698 projection: vec![SelectItem::Wildcard],
699 from: TableWithJoins {
700 relation: TableFactor::Table {
701 name: "FOO".to_owned(),
702 alias: Some(TableAlias {
703 name: "F".to_owned(),
704 columns: Vec::new(),
705 }),
706 index: None,
707 },
708 joins: vec![Join {
709 relation: TableFactor::Table {
710 name: "PlayerItem".to_owned(),
711 alias: None,
712 index: None,
713 },
714 join_operator: JoinOperator::Inner(JoinConstraint::None),
715 join_executor: JoinExecutor::NestedLoop,
716 }],
717 },
718 selection: None,
719 group_by: Vec::new(),
720 having: None,
721 }))
722 .to_sql();
723 assert_eq!(actual, expected);
724
725 let actual = "VALUES (1, 'glue', 3), (2, 'sql', 2)".to_owned();
726 let expected = SetExpr::Values(Values(vec![
727 vec![
728 Expr::Literal(AstLiteral::Number(BigDecimal::from_str("1").unwrap())),
729 Expr::Literal(AstLiteral::QuotedString("glue".to_owned())),
730 Expr::Literal(AstLiteral::Number(BigDecimal::from_str("3").unwrap())),
731 ],
732 vec![
733 Expr::Literal(AstLiteral::Number(BigDecimal::from_str("2").unwrap())),
734 Expr::Literal(AstLiteral::QuotedString("sql".to_owned())),
735 Expr::Literal(AstLiteral::Number(BigDecimal::from_str("2").unwrap())),
736 ],
737 ]))
738 .to_sql();
739 assert_eq!(actual, expected);
740 }
741
742 #[test]
743 fn to_sql_unquoted_set_expr() {
744 let actual = "SELECT * FROM FOO AS F INNER JOIN PlayerItem".to_owned();
745 let expected = SetExpr::Select(Box::new(Select {
746 distinct: false,
747 projection: vec![SelectItem::Wildcard],
748 from: TableWithJoins {
749 relation: TableFactor::Table {
750 name: "FOO".to_owned(),
751 alias: Some(TableAlias {
752 name: "F".to_owned(),
753 columns: Vec::new(),
754 }),
755 index: None,
756 },
757 joins: vec![Join {
758 relation: TableFactor::Table {
759 name: "PlayerItem".to_owned(),
760 alias: None,
761 index: None,
762 },
763 join_operator: JoinOperator::Inner(JoinConstraint::None),
764 join_executor: JoinExecutor::NestedLoop,
765 }],
766 },
767 selection: None,
768 group_by: Vec::new(),
769 having: None,
770 }))
771 .to_sql_unquoted();
772 assert_eq!(actual, expected);
773
774 let actual = "VALUES (1 + 1, 'glue'), (3 - 2, 'sql')".to_owned();
775 let expected = SetExpr::Values(Values(vec![
776 vec![
777 Expr::BinaryOp {
778 left: Box::new(Expr::Literal(AstLiteral::Number(
779 BigDecimal::from_str("1").unwrap(),
780 ))),
781 op: BinaryOperator::Plus,
782 right: Box::new(Expr::Literal(AstLiteral::Number(
783 BigDecimal::from_str("1").unwrap(),
784 ))),
785 },
786 Expr::Literal(AstLiteral::QuotedString("glue".to_owned())),
787 ],
788 vec![
789 Expr::BinaryOp {
790 left: Box::new(Expr::Literal(AstLiteral::Number(
791 BigDecimal::from_str("3").unwrap(),
792 ))),
793 op: BinaryOperator::Minus,
794 right: Box::new(Expr::Literal(AstLiteral::Number(
795 BigDecimal::from_str("2").unwrap(),
796 ))),
797 },
798 Expr::Literal(AstLiteral::QuotedString("sql".to_owned())),
799 ],
800 ]))
801 .to_sql_unquoted();
802 assert_eq!(actual, expected);
803 }
804
805 #[test]
806 fn to_sql_select() {
807 let actual =
808 r#"SELECT * FROM "FOO" AS "F" GROUP BY "name" HAVING "name" = 'glue'"#.to_owned();
809 let expected = Select {
810 distinct: false,
811 projection: vec![SelectItem::Wildcard],
812 from: TableWithJoins {
813 relation: TableFactor::Table {
814 name: "FOO".to_owned(),
815 alias: Some(TableAlias {
816 name: "F".to_owned(),
817 columns: Vec::new(),
818 }),
819 index: None,
820 },
821 joins: Vec::new(),
822 },
823 selection: None,
824 group_by: vec![Expr::Identifier("name".to_owned())],
825 having: Some(Expr::BinaryOp {
826 left: Box::new(Expr::Identifier("name".to_owned())),
827 op: BinaryOperator::Eq,
828 right: Box::new(Expr::Literal(AstLiteral::QuotedString("glue".to_owned()))),
829 }),
830 }
831 .to_sql();
832 assert_eq!(actual, expected);
833
834 let actual = r#"SELECT * FROM "FOO" WHERE "name" = 'glue'"#.to_owned();
835 let expected = Select {
836 distinct: false,
837 projection: vec![SelectItem::Wildcard],
838 from: TableWithJoins {
839 relation: TableFactor::Table {
840 name: "FOO".to_owned(),
841 alias: None,
842 index: None,
843 },
844 joins: Vec::new(),
845 },
846 selection: Some(Expr::BinaryOp {
847 left: Box::new(Expr::Identifier("name".to_owned())),
848 op: BinaryOperator::Eq,
849 right: Box::new(Expr::Literal(AstLiteral::QuotedString("glue".to_owned()))),
850 }),
851 group_by: Vec::new(),
852 having: None,
853 }
854 .to_sql();
855 assert_eq!(actual, expected);
856 }
857
858 #[test]
859 fn to_sql_unquoted_select() {
860 let actual = "SELECT * FROM FOO AS F GROUP BY name HAVING name = 'glue'".to_owned();
861 let expected = Select {
862 distinct: false,
863 projection: vec![SelectItem::Wildcard],
864 from: TableWithJoins {
865 relation: TableFactor::Table {
866 name: "FOO".to_owned(),
867 alias: Some(TableAlias {
868 name: "F".to_owned(),
869 columns: Vec::new(),
870 }),
871 index: None,
872 },
873 joins: Vec::new(),
874 },
875 selection: None,
876 group_by: vec![Expr::Identifier("name".to_owned())],
877 having: Some(Expr::BinaryOp {
878 left: Box::new(Expr::Identifier("name".to_owned())),
879 op: BinaryOperator::Eq,
880 right: Box::new(Expr::Literal(AstLiteral::QuotedString("glue".to_owned()))),
881 }),
882 }
883 .to_sql_unquoted();
884 assert_eq!(actual, expected);
885
886 let actual = "SELECT * FROM FOO WHERE name = 'glue'".to_owned();
887 let expected = Select {
888 distinct: false,
889 projection: vec![SelectItem::Wildcard],
890 from: TableWithJoins {
891 relation: TableFactor::Table {
892 name: "FOO".to_owned(),
893 alias: None,
894 index: None,
895 },
896 joins: Vec::new(),
897 },
898 selection: Some(Expr::BinaryOp {
899 left: Box::new(Expr::Identifier("name".to_owned())),
900 op: BinaryOperator::Eq,
901 right: Box::new(Expr::Literal(AstLiteral::QuotedString("glue".to_owned()))),
902 }),
903 group_by: Vec::new(),
904 having: None,
905 }
906 .to_sql_unquoted();
907 assert_eq!(actual, expected);
908 }
909
910 #[test]
911 fn to_sql_select_item() {
912 let actual = r#""name" AS "n""#.to_owned();
913 let expected = SelectItem::Expr {
914 expr: Expr::Identifier("name".to_owned()),
915 label: "n".to_owned(),
916 }
917 .to_sql();
918 assert_eq!(actual, expected);
919
920 let actual = r#""foo".*"#.to_owned();
921 let expected = SelectItem::QualifiedWildcard("foo".to_owned()).to_sql();
922 assert_eq!(actual, expected);
923
924 let actual = "*".to_owned();
925 let expected = SelectItem::Wildcard.to_sql();
926 assert_eq!(actual, expected);
927 }
928
929 #[test]
930 fn to_sql_unquoted_select_item() {
931 let actual = "name AS n".to_owned();
932 let expected = SelectItem::Expr {
933 expr: Expr::Identifier("name".to_owned()),
934 label: "n".to_owned(),
935 }
936 .to_sql_unquoted();
937 assert_eq!(actual, expected);
938
939 let actual = "foo.*".to_owned();
940 let expected = SelectItem::QualifiedWildcard("foo".to_owned()).to_sql_unquoted();
941 assert_eq!(actual, expected);
942 }
943
944 #[test]
945 fn to_sql_table_with_joins() {
946 let actual = r#""FOO" AS "F""#;
947 let expected = TableWithJoins {
948 relation: TableFactor::Table {
949 name: "FOO".to_owned(),
950 alias: Some(TableAlias {
951 name: "F".to_owned(),
952 columns: Vec::new(),
953 }),
954 index: None,
955 },
956 joins: Vec::new(),
957 }
958 .to_sql();
959 assert_eq!(actual, expected);
960 }
961
962 #[test]
963 fn to_sql_unquoted_table_with_joins() {
964 let actual = "FOO AS F";
965 let expected = TableWithJoins {
966 relation: TableFactor::Table {
967 name: "FOO".to_owned(),
968 alias: Some(TableAlias {
969 name: "F".to_owned(),
970 columns: Vec::new(),
971 }),
972 index: None,
973 },
974 joins: Vec::new(),
975 }
976 .to_sql_unquoted();
977 assert_eq!(actual, expected);
978 }
979
980 #[test]
981 fn to_sql_table_factor() {
982 let actual = r#""FOO" AS "F""#;
983 let expected = TableFactor::Table {
984 name: "FOO".to_owned(),
985 alias: Some(TableAlias {
986 name: "F".to_owned(),
987 columns: Vec::new(),
988 }),
989 index: None,
990 }
991 .to_sql();
992 assert_eq!(actual, expected);
993
994 let actual = r#"(SELECT * FROM "FOO") AS "F""#;
995 let expected = TableFactor::Derived {
996 subquery: Query {
997 body: SetExpr::Select(Box::new(Select {
998 distinct: false,
999 projection: vec![SelectItem::Wildcard],
1000 from: TableWithJoins {
1001 relation: TableFactor::Table {
1002 name: "FOO".to_owned(),
1003 alias: None,
1004 index: None,
1005 },
1006 joins: Vec::new(),
1007 },
1008 selection: None,
1009 group_by: Vec::new(),
1010 having: None,
1011 })),
1012 order_by: Vec::new(),
1013 limit: None,
1014 offset: None,
1015 },
1016 alias: TableAlias {
1017 name: "F".to_owned(),
1018 columns: Vec::new(),
1019 },
1020 }
1021 .to_sql();
1022 assert_eq!(actual, expected);
1023
1024 let actual = r#"SERIES(3) AS "S""#;
1025 let expected = TableFactor::Series {
1026 alias: TableAlias {
1027 name: "S".to_owned(),
1028 columns: Vec::new(),
1029 },
1030 size: Expr::Literal(AstLiteral::Number(BigDecimal::from_str("3").unwrap())),
1031 }
1032 .to_sql();
1033 assert_eq!(actual, expected);
1034
1035 let actual = r#""GLUE_TABLES" AS "glue""#;
1036 let expected = TableFactor::Dictionary {
1037 dict: Dictionary::GlueTables,
1038 alias: TableAlias {
1039 name: "glue".to_owned(),
1040 columns: Vec::new(),
1041 },
1042 }
1043 .to_sql();
1044 assert_eq!(actual, expected);
1045 }
1046
1047 #[test]
1048 fn to_sql_unquoted_table_factor() {
1049 let actual = "FOO AS F";
1050 let expected = TableFactor::Table {
1051 name: "FOO".to_owned(),
1052 alias: Some(TableAlias {
1053 name: "F".to_owned(),
1054 columns: Vec::new(),
1055 }),
1056 index: None,
1057 }
1058 .to_sql_unquoted();
1059 assert_eq!(actual, expected);
1060
1061 let actual = "(SELECT * FROM FOO) AS F";
1062 let expected = TableFactor::Derived {
1063 subquery: Query {
1064 body: SetExpr::Select(Box::new(Select {
1065 distinct: false,
1066 projection: vec![SelectItem::Wildcard],
1067 from: TableWithJoins {
1068 relation: TableFactor::Table {
1069 name: "FOO".to_owned(),
1070 alias: None,
1071 index: None,
1072 },
1073 joins: Vec::new(),
1074 },
1075 selection: None,
1076 group_by: Vec::new(),
1077 having: None,
1078 })),
1079 order_by: Vec::new(),
1080 limit: None,
1081 offset: None,
1082 },
1083 alias: TableAlias {
1084 name: "F".to_owned(),
1085 columns: Vec::new(),
1086 },
1087 }
1088 .to_sql_unquoted();
1089 assert_eq!(actual, expected);
1090
1091 let actual = "SERIES(3) AS S";
1092 let expected = TableFactor::Series {
1093 alias: TableAlias {
1094 name: "S".to_owned(),
1095 columns: Vec::new(),
1096 },
1097 size: Expr::Literal(AstLiteral::Number(BigDecimal::from_str("3").unwrap())),
1098 }
1099 .to_sql_unquoted();
1100 assert_eq!(actual, expected);
1101
1102 let actual = "GLUE_TABLES AS glue";
1103 let expected = TableFactor::Dictionary {
1104 dict: Dictionary::GlueTables,
1105 alias: TableAlias {
1106 name: "glue".to_owned(),
1107 columns: Vec::new(),
1108 },
1109 }
1110 .to_sql_unquoted();
1111 assert_eq!(actual, expected);
1112 }
1113
1114 #[test]
1115 fn to_sql_table_alias() {
1116 let actual = r#"AS "F""#;
1117 let expected = TableAlias {
1118 name: "F".to_owned(),
1119 columns: Vec::new(),
1120 }
1121 .to_sql();
1122 assert_eq!(actual, expected);
1123 }
1124
1125 #[test]
1126 fn to_sql_unquoted_table_alias() {
1127 let actual = "AS F";
1128 let expected = TableAlias {
1129 name: "F".to_owned(),
1130 columns: Vec::new(),
1131 }
1132 .to_sql_unquoted();
1133 assert_eq!(actual, expected);
1134 }
1135
1136 #[test]
1137 fn to_sql_join() {
1138 let actual = r#"INNER JOIN "PlayerItem""#;
1139 let expected = Join {
1140 relation: TableFactor::Table {
1141 name: "PlayerItem".to_owned(),
1142 alias: None,
1143 index: None,
1144 },
1145 join_operator: JoinOperator::Inner(JoinConstraint::None),
1146 join_executor: JoinExecutor::NestedLoop,
1147 }
1148 .to_sql();
1149 assert_eq!(actual, expected);
1150
1151 let actual = r#"INNER JOIN "PlayerItem" ON "PlayerItem"."user_id" = "Player"."id""#;
1152 let expected = Join {
1153 relation: TableFactor::Table {
1154 name: "PlayerItem".to_owned(),
1155 alias: None,
1156 index: None,
1157 },
1158 join_operator: JoinOperator::Inner(JoinConstraint::On(expr(
1159 r#""PlayerItem"."user_id" = "Player"."id""#,
1160 ))),
1161 join_executor: JoinExecutor::NestedLoop,
1162 }
1163 .to_sql();
1164 assert_eq!(actual, expected);
1165
1166 let actual = r#"LEFT OUTER JOIN "PlayerItem""#;
1167 let expected = Join {
1168 relation: TableFactor::Table {
1169 name: "PlayerItem".to_owned(),
1170 alias: None,
1171 index: None,
1172 },
1173 join_operator: JoinOperator::LeftOuter(JoinConstraint::None),
1174 join_executor: JoinExecutor::NestedLoop,
1175 }
1176 .to_sql();
1177 assert_eq!(actual, expected);
1178
1179 let actual = r#"LEFT OUTER JOIN "PlayerItem" ON "PlayerItem"."user_id" = "Player"."id""#;
1180 let expected = Join {
1181 relation: TableFactor::Table {
1182 name: "PlayerItem".to_owned(),
1183 alias: None,
1184 index: None,
1185 },
1186 join_operator: JoinOperator::LeftOuter(JoinConstraint::None),
1187 join_executor: JoinExecutor::Hash {
1188 key_expr: expr("PlayerItem.user_id"),
1189 value_expr: expr("Player.id"),
1190 where_clause: None,
1191 },
1192 }
1193 .to_sql();
1194 assert_eq!(actual, expected);
1195
1196 let actual = r#"LEFT OUTER JOIN "PlayerItem" ON "PlayerItem"."age" > "Player"."age" AND "PlayerItem"."user_id" = "Player"."id" AND "PlayerItem"."amount" > 10 AND "PlayerItem"."amount" * 3 <= 2"#;
1197 let expected = Join {
1198 relation: TableFactor::Table {
1199 name: "PlayerItem".to_owned(),
1200 alias: None,
1201 index: None,
1202 },
1203 join_operator: JoinOperator::LeftOuter(JoinConstraint::On(expr(
1204 r#""PlayerItem"."age" > "Player"."age""#,
1205 ))),
1206 join_executor: JoinExecutor::Hash {
1207 key_expr: expr("PlayerItem.user_id"),
1208 value_expr: expr("Player.id"),
1209 where_clause: Some(expr(
1210 r#""PlayerItem"."amount" > 10 AND "PlayerItem"."amount" * 3 <= 2"#,
1211 )),
1212 },
1213 }
1214 .to_sql();
1215 assert_eq!(actual, expected);
1216 }
1217
1218 #[test]
1219 fn to_sql_unquoted_join() {
1220 let actual = "INNER JOIN PlayerItem";
1221 let expected = Join {
1222 relation: TableFactor::Table {
1223 name: "PlayerItem".to_owned(),
1224 alias: None,
1225 index: None,
1226 },
1227 join_operator: JoinOperator::Inner(JoinConstraint::None),
1228 join_executor: JoinExecutor::NestedLoop,
1229 }
1230 .to_sql_unquoted();
1231 assert_eq!(actual, expected);
1232
1233 let actual = "INNER JOIN PlayerItem ON PlayerItem.user_id = Player.id AND PlayerItem.group_id = Player.group_id";
1234 let expected = Join {
1235 relation: TableFactor::Table {
1236 name: "PlayerItem".to_owned(),
1237 alias: None,
1238 index: None,
1239 },
1240 join_operator: JoinOperator::Inner(JoinConstraint::On(expr(
1241 "PlayerItem.user_id = Player.id",
1242 ))),
1243 join_executor: JoinExecutor::Hash {
1244 key_expr: expr("PlayerItem.group_id"),
1245 value_expr: expr("Player.group_id"),
1246 where_clause: None,
1247 },
1248 }
1249 .to_sql_unquoted();
1250 assert_eq!(actual, expected);
1251
1252 let actual = "LEFT OUTER JOIN PlayerItem";
1253 let expected = Join {
1254 relation: TableFactor::Table {
1255 name: "PlayerItem".to_owned(),
1256 alias: None,
1257 index: None,
1258 },
1259 join_operator: JoinOperator::LeftOuter(JoinConstraint::None),
1260 join_executor: JoinExecutor::NestedLoop,
1261 }
1262 .to_sql_unquoted();
1263 assert_eq!(actual, expected);
1264
1265 let actual = "LEFT OUTER JOIN PlayerItem ON PlayerItem.user_id = Player.id";
1266 let expected = Join {
1267 relation: TableFactor::Table {
1268 name: "PlayerItem".to_owned(),
1269 alias: None,
1270 index: None,
1271 },
1272 join_operator: JoinOperator::LeftOuter(JoinConstraint::None),
1273 join_executor: JoinExecutor::Hash {
1274 key_expr: expr("PlayerItem.user_id"),
1275 value_expr: expr("Player.id"),
1276 where_clause: None,
1277 },
1278 }
1279 .to_sql_unquoted();
1280 assert_eq!(actual, expected);
1281
1282 let actual = "LEFT OUTER JOIN PlayerItem ON PlayerItem.age > Player.age AND PlayerItem.user_id = Player.id AND PlayerItem.amount > 10 AND PlayerItem.amount * 3 <= 2";
1283 let expected = Join {
1284 relation: TableFactor::Table {
1285 name: "PlayerItem".to_owned(),
1286 alias: None,
1287 index: None,
1288 },
1289 join_operator: JoinOperator::LeftOuter(JoinConstraint::On(expr(
1290 "PlayerItem.age > Player.age",
1291 ))),
1292 join_executor: JoinExecutor::Hash {
1293 key_expr: expr("PlayerItem.user_id"),
1294 value_expr: expr("Player.id"),
1295 where_clause: Some(expr(
1296 "PlayerItem.amount > 10 AND PlayerItem.amount * 3 <= 2",
1297 )),
1298 },
1299 }
1300 .to_sql_unquoted();
1301 assert_eq!(actual, expected);
1302 }
1303
1304 #[test]
1305 fn to_sql_order_by_expr() {
1306 let actual = r#""foo" ASC"#;
1307 let expected = OrderByExpr {
1308 expr: Expr::Identifier("foo".to_owned()),
1309 asc: Some(true),
1310 }
1311 .to_sql();
1312 assert_eq!(actual, expected);
1313
1314 let actual = r#""foo" DESC"#;
1315 let expected = OrderByExpr {
1316 expr: Expr::Identifier("foo".to_owned()),
1317 asc: Some(false),
1318 }
1319 .to_sql();
1320 assert_eq!(actual, expected);
1321
1322 let actual = r#""foo""#;
1323 let expected = OrderByExpr {
1324 expr: Expr::Identifier("foo".to_owned()),
1325 asc: None,
1326 }
1327 .to_sql();
1328 assert_eq!(actual, expected);
1329 }
1330
1331 #[test]
1332 fn to_sql_unquoted_order_by_expr() {
1333 let actual = "foo ASC";
1334 let expected = OrderByExpr {
1335 expr: Expr::Identifier("foo".to_owned()),
1336 asc: Some(true),
1337 }
1338 .to_sql_unquoted();
1339 assert_eq!(actual, expected);
1340
1341 let actual = "foo DESC";
1342 let expected = OrderByExpr {
1343 expr: Expr::Identifier("foo".to_owned()),
1344 asc: Some(false),
1345 }
1346 .to_sql_unquoted();
1347 assert_eq!(actual, expected);
1348
1349 let actual = "foo";
1350 let expected = OrderByExpr {
1351 expr: Expr::Identifier("foo".to_owned()),
1352 asc: None,
1353 }
1354 .to_sql_unquoted();
1355 assert_eq!(actual, expected);
1356 }
1357}