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