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