gluesql_core/ast/
mod.rs

1mod ast_literal;
2mod data_type;
3mod ddl;
4mod expr;
5mod function;
6mod operator;
7mod query;
8
9pub use {
10    ast_literal::{AstLiteral, DateTimeField, TrimWhereField},
11    data_type::DataType,
12    ddl::*,
13    expr::Expr,
14    function::{Aggregate, CountArgExpr, Function},
15    operator::*,
16    query::*,
17};
18
19use {
20    serde::{Deserialize, Serialize},
21    strum_macros::Display,
22};
23
24pub trait ToSql {
25    fn to_sql(&self) -> String;
26}
27
28pub trait ToSqlUnquoted {
29    fn to_sql_unquoted(&self) -> String;
30}
31
32#[derive(PartialEq, Debug, Clone, Eq, Hash, Serialize, Deserialize)]
33pub struct ForeignKey {
34    pub name: String,
35    pub referencing_column_name: String,
36    pub referenced_table_name: String,
37    pub referenced_column_name: String,
38    pub on_delete: ReferentialAction,
39    pub on_update: ReferentialAction,
40}
41
42#[derive(PartialEq, Debug, Clone, Eq, Hash, Serialize, Deserialize, Display)]
43pub enum ReferentialAction {
44    #[strum(to_string = "NO ACTION")]
45    NoAction,
46}
47
48#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
49pub enum Statement {
50    ShowColumns {
51        table_name: String,
52    },
53    /// SELECT, VALUES
54    Query(Query),
55    /// INSERT
56    Insert {
57        /// TABLE
58        table_name: String,
59        /// COLUMNS
60        columns: Vec<String>,
61        /// A SQL query that specifies what to insert
62        source: Query,
63    },
64    /// UPDATE
65    Update {
66        /// TABLE
67        table_name: String,
68        /// Column assignments
69        assignments: Vec<Assignment>,
70        /// WHERE
71        selection: Option<Expr>,
72    },
73    /// DELETE
74    Delete {
75        /// FROM
76        table_name: String,
77        /// WHERE
78        selection: Option<Expr>,
79    },
80    /// CREATE TABLE
81    CreateTable {
82        if_not_exists: bool,
83        /// Table name
84        name: String,
85        /// Optional schema
86        columns: Option<Vec<ColumnDef>>,
87        source: Option<Box<Query>>,
88        engine: Option<String>,
89        foreign_keys: Vec<ForeignKey>,
90        comment: Option<String>,
91    },
92    /// CREATE FUNCTION
93    CreateFunction {
94        or_replace: bool,
95        name: String,
96        /// Optional schema
97        args: Vec<OperateFunctionArg>,
98        return_: Expr,
99    },
100    /// ALTER TABLE
101    AlterTable {
102        /// Table name
103        name: String,
104        operation: AlterTableOperation,
105    },
106    /// DROP TABLE
107    DropTable {
108        /// An optional `IF EXISTS` clause. (Non-standard.)
109        if_exists: bool,
110        /// One or more objects to drop. (ANSI SQL requires exactly one.)
111        names: Vec<String>,
112        /// An optional `CASCADE` clause for dropping dependent constructs.
113        cascade: bool,
114    },
115    /// DROP FUNCTION
116    DropFunction {
117        /// An optional `IF EXISTS` clause. (Non-standard.)
118        if_exists: bool,
119        /// One or more objects to drop. (ANSI SQL requires exactly one.)
120        names: Vec<String>,
121    },
122    /// CREATE INDEX
123    CreateIndex {
124        name: String,
125        table_name: String,
126        column: OrderByExpr,
127    },
128    /// DROP INDEX
129    DropIndex {
130        name: String,
131        table_name: String,
132    },
133    /// START TRANSACTION, BEGIN
134    StartTransaction,
135    /// COMMIT
136    Commit,
137    /// ROLLBACK
138    Rollback,
139    /// SHOW VARIABLE
140    ShowVariable(Variable),
141    ShowIndexes(String),
142}
143
144#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
145pub struct Assignment {
146    pub id: String,
147    pub value: Expr,
148}
149
150#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
151pub enum Variable {
152    Tables,
153    Functions,
154    Version,
155}
156
157impl ToSql for Statement {
158    fn to_sql(&self) -> String {
159        match self {
160            Statement::ShowColumns { table_name } => {
161                format!("SHOW COLUMNS FROM {table_name};")
162            }
163            Statement::Insert {
164                table_name,
165                columns,
166                source,
167            } => {
168                let columns = match columns.is_empty() {
169                    true => "".to_owned(),
170                    false => format!("({}) ", columns.join(", ")),
171                };
172
173                format!("INSERT INTO {table_name} {columns}{};", source.to_sql())
174            }
175            Statement::Update {
176                table_name,
177                assignments,
178                selection,
179            } => {
180                let assignments = assignments
181                    .iter()
182                    .map(ToSql::to_sql)
183                    .collect::<Vec<_>>()
184                    .join(", ");
185                match selection {
186                    Some(expr) => {
187                        format!(
188                            r#"UPDATE "{table_name}" SET {assignments} WHERE {};"#,
189                            expr.to_sql()
190                        )
191                    }
192                    None => format!(r#"UPDATE "{table_name}" SET {assignments};"#),
193                }
194            }
195            Statement::Delete {
196                table_name,
197                selection,
198            } => match selection {
199                Some(expr) => format!(r#"DELETE FROM "{table_name}" WHERE {};"#, expr.to_sql()),
200                None => format!(r#"DELETE FROM "{table_name}";"#),
201            },
202            Statement::CreateTable {
203                if_not_exists,
204                name,
205                columns,
206                source,
207                engine,
208                foreign_keys,
209                comment,
210            } => {
211                let if_not_exists = if_not_exists.then_some("IF NOT EXISTS");
212                let body = match (source, columns) {
213                    (Some(query), _) => Some(format!("AS {}", query.to_sql())),
214                    (None, None) => None,
215                    (None, Some(columns)) => {
216                        let foreign_keys = foreign_keys.iter().map(ToSql::to_sql);
217                        let body = columns
218                            .iter()
219                            .map(ToSql::to_sql)
220                            .chain(foreign_keys)
221                            .collect::<Vec<_>>()
222                            .join(", ");
223
224                        Some(format!("({body})"))
225                    }
226                };
227                let engine = engine.as_ref().map(|engine| format!("ENGINE = {engine}"));
228                let comment = comment
229                    .as_ref()
230                    .map(|comment| format!("COMMENT = '{comment}'"));
231                let sql = vec![
232                    Some("CREATE TABLE"),
233                    if_not_exists,
234                    Some(&format! {r#""{name}""#}),
235                    body.as_deref(),
236                    engine.as_deref(),
237                    comment.as_deref(),
238                ]
239                .into_iter()
240                .flatten()
241                .collect::<Vec<&str>>()
242                .join(" ");
243
244                format!("{sql};")
245            }
246            Statement::CreateFunction {
247                or_replace,
248                name,
249                args,
250                return_,
251                ..
252            } => {
253                let or_replace = or_replace.then_some(" OR REPLACE").unwrap_or("");
254                let args = args
255                    .iter()
256                    .map(ToSql::to_sql)
257                    .collect::<Vec<_>>()
258                    .join(", ");
259                let return_ = format!(" RETURN {}", return_.to_sql());
260                format!("CREATE{or_replace} FUNCTION {name}({args}){return_};")
261            }
262            Statement::AlterTable { name, operation } => {
263                format!(r#"ALTER TABLE "{name}" {};"#, operation.to_sql())
264            }
265            Statement::DropTable {
266                if_exists,
267                names,
268                cascade,
269            } => {
270                let if_exists = if_exists.then_some("IF EXISTS").unwrap_or_default();
271                let names = names
272                    .iter()
273                    .map(|name| format!(r#""{name}""#))
274                    .collect::<Vec<_>>()
275                    .join(", ");
276                let cascade = cascade.then_some("CASCADE").unwrap_or_default();
277
278                vec!["DROP TABLE", if_exists, &names, cascade]
279                    .into_iter()
280                    .filter(|s| !s.is_empty())
281                    .collect::<Vec<_>>()
282                    .join(" ")
283                    + ";"
284            }
285            Statement::DropFunction { if_exists, names } => {
286                let names = names.join(", ");
287                match if_exists {
288                    true => format!("DROP FUNCTION IF EXISTS {};", names),
289                    false => format!("DROP FUNCTION {};", names),
290                }
291            }
292            Statement::CreateIndex {
293                name,
294                table_name,
295                column,
296            } => {
297                format!(
298                    r#"CREATE INDEX "{name}" ON "{table_name}" ({});"#,
299                    column.to_sql()
300                )
301            }
302            Statement::DropIndex { name, table_name } => {
303                format!("DROP INDEX {table_name}.{name};")
304            }
305            Statement::StartTransaction => "START TRANSACTION;".to_owned(),
306            Statement::Commit => "COMMIT;".to_owned(),
307            Statement::Rollback => "ROLLBACK;".to_owned(),
308            Statement::ShowVariable(variable) => match variable {
309                Variable::Tables => "SHOW TABLES;".to_owned(),
310                Variable::Functions => "SHOW FUNCTIONS;".to_owned(),
311                Variable::Version => "SHOW VERSIONS;".to_owned(),
312            },
313            Statement::ShowIndexes(object_name) => {
314                format!(r#"SHOW INDEXES FROM "{object_name}";"#)
315            }
316            _ => "(..statement..)".to_owned(),
317        }
318    }
319}
320
321impl ToSql for Assignment {
322    fn to_sql(&self) -> String {
323        format!(r#""{}" = {}"#, self.id, self.value.to_sql())
324    }
325}
326
327impl ToSql for ForeignKey {
328    fn to_sql(&self) -> String {
329        let ForeignKey {
330            referencing_column_name,
331            referenced_table_name,
332            referenced_column_name,
333            name,
334            on_delete,
335            on_update,
336        } = self;
337
338        format!(
339            r#"CONSTRAINT "{}" FOREIGN KEY ("{}") REFERENCES "{}" ("{}") ON DELETE {} ON UPDATE {}"#,
340            name,
341            referencing_column_name,
342            referenced_table_name,
343            referenced_column_name,
344            on_delete,
345            on_update
346        )
347    }
348}
349
350#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
351pub struct Array {
352    pub elem: Vec<Expr>,
353    pub named: bool,
354}
355
356#[cfg(test)]
357mod tests {
358    use {
359        crate::ast::{
360            AlterTableOperation, Assignment, AstLiteral, BinaryOperator, ColumnDef, DataType, Expr,
361            ForeignKey, OperateFunctionArg, OrderByExpr, Query, ReferentialAction, Select,
362            SelectItem, SetExpr, Statement, TableFactor, TableWithJoins, ToSql, Values, Variable,
363        },
364        bigdecimal::BigDecimal,
365        std::str::FromStr,
366    };
367
368    #[test]
369    fn to_sql_show_columns() {
370        assert_eq!(
371            "SHOW COLUMNS FROM Bar;",
372            Statement::ShowColumns {
373                table_name: "Bar".into()
374            }
375            .to_sql()
376        )
377    }
378
379    #[test]
380    fn to_sql_insert() {
381        assert_eq!(
382            "INSERT INTO Test (id, num, name) VALUES (1, 2, 'Hello');",
383            Statement::Insert {
384                table_name: "Test".into(),
385                columns: vec!["id".to_owned(), "num".to_owned(), "name".to_owned()],
386                source: Query {
387                    body: SetExpr::Values(Values(vec![vec![
388                        Expr::Literal(AstLiteral::Number(BigDecimal::from_str("1").unwrap())),
389                        Expr::Literal(AstLiteral::Number(BigDecimal::from_str("2").unwrap())),
390                        Expr::Literal(AstLiteral::QuotedString("Hello".to_owned()))
391                    ]])),
392                    order_by: vec![],
393                    limit: None,
394                    offset: None
395                }
396            }
397            .to_sql()
398        );
399    }
400
401    #[test]
402    fn to_sql_update() {
403        assert_eq!(
404            r#"UPDATE "Foo" SET "id" = 4, "color" = 'blue';"#,
405            Statement::Update {
406                table_name: "Foo".into(),
407                assignments: vec![
408                    Assignment {
409                        id: "id".to_owned(),
410                        value: Expr::Literal(AstLiteral::Number(
411                            BigDecimal::from_str("4").unwrap()
412                        ))
413                    },
414                    Assignment {
415                        id: "color".to_owned(),
416                        value: Expr::Literal(AstLiteral::QuotedString("blue".to_owned()))
417                    }
418                ],
419                selection: None
420            }
421            .to_sql()
422        );
423
424        assert_eq!(
425            r#"UPDATE "Foo" SET "name" = 'first' WHERE "a" > "b";"#,
426            Statement::Update {
427                table_name: "Foo".into(),
428                assignments: vec![Assignment {
429                    id: "name".to_owned(),
430                    value: Expr::Literal(AstLiteral::QuotedString("first".to_owned()))
431                }],
432                selection: Some(Expr::BinaryOp {
433                    left: Box::new(Expr::Identifier("a".to_owned())),
434                    op: BinaryOperator::Gt,
435                    right: Box::new(Expr::Identifier("b".to_owned()))
436                })
437            }
438            .to_sql()
439        )
440    }
441
442    #[test]
443    fn to_sql_delete() {
444        assert_eq!(
445            r#"DELETE FROM "Foo";"#,
446            Statement::Delete {
447                table_name: "Foo".into(),
448                selection: None
449            }
450            .to_sql()
451        );
452
453        assert_eq!(
454            r#"DELETE FROM "Foo" WHERE "item" = 'glue';"#,
455            Statement::Delete {
456                table_name: "Foo".into(),
457                selection: Some(Expr::BinaryOp {
458                    left: Box::new(Expr::Identifier("item".to_owned())),
459                    op: BinaryOperator::Eq,
460                    right: Box::new(Expr::Literal(AstLiteral::QuotedString("glue".to_owned())))
461                })
462            }
463            .to_sql()
464        );
465    }
466
467    #[test]
468    fn to_sql_create_table() {
469        assert_eq!(
470            r#"CREATE TABLE IF NOT EXISTS "Foo";"#,
471            Statement::CreateTable {
472                if_not_exists: true,
473                name: "Foo".into(),
474                columns: None,
475                source: None,
476                engine: None,
477                foreign_keys: Vec::new(),
478                comment: None,
479            }
480            .to_sql()
481        );
482
483        assert_eq!(
484            r#"CREATE TABLE "Foo";"#,
485            Statement::CreateTable {
486                if_not_exists: false,
487                name: "Foo".into(),
488                columns: None,
489                source: None,
490                engine: None,
491                foreign_keys: Vec::new(),
492                comment: None,
493            }
494            .to_sql()
495        );
496
497        assert_eq!(
498            r#"CREATE TABLE IF NOT EXISTS "Foo" ("id" BOOLEAN NOT NULL) COMMENT = 'this is comment';"#,
499            Statement::CreateTable {
500                if_not_exists: true,
501                name: "Foo".into(),
502                columns: Some(vec![ColumnDef {
503                    name: "id".to_owned(),
504                    data_type: DataType::Boolean,
505                    nullable: false,
506                    default: None,
507                    unique: None,
508                    comment: None,
509                },]),
510                source: None,
511                engine: None,
512                foreign_keys: Vec::new(),
513                comment: Some("this is comment".to_owned()),
514            }
515            .to_sql()
516        );
517
518        assert_eq!(
519            r#"CREATE TABLE "Foo" ("id" INT NOT NULL, "num" INT NULL, "name" TEXT NOT NULL);"#,
520            Statement::CreateTable {
521                if_not_exists: false,
522                name: "Foo".into(),
523                columns: Some(vec![
524                    ColumnDef {
525                        name: "id".to_owned(),
526                        data_type: DataType::Int,
527                        nullable: false,
528                        default: None,
529                        unique: None,
530                        comment: None,
531                    },
532                    ColumnDef {
533                        name: "num".to_owned(),
534                        data_type: DataType::Int,
535                        nullable: true,
536                        default: None,
537                        unique: None,
538                        comment: None,
539                    },
540                    ColumnDef {
541                        name: "name".to_owned(),
542                        data_type: DataType::Text,
543                        nullable: false,
544                        default: None,
545                        unique: None,
546                        comment: None,
547                    }
548                ]),
549                source: None,
550                engine: None,
551                foreign_keys: Vec::new(),
552                comment: None,
553            }
554            .to_sql()
555        );
556    }
557
558    #[test]
559    fn to_sql_create_table_as() {
560        assert_eq!(
561            r#"CREATE TABLE "Foo" AS SELECT "id", "count" FROM "Bar";"#,
562            Statement::CreateTable {
563                if_not_exists: false,
564                name: "Foo".into(),
565                columns: None,
566                source: Some(Box::new(Query {
567                    body: SetExpr::Select(Box::new(Select {
568                        projection: vec![
569                            SelectItem::Expr {
570                                expr: Expr::Identifier("id".to_owned()),
571                                label: "".to_owned()
572                            },
573                            SelectItem::Expr {
574                                expr: Expr::Identifier("count".to_owned()),
575                                label: "".to_owned()
576                            }
577                        ],
578                        from: TableWithJoins {
579                            relation: TableFactor::Table {
580                                name: "Bar".to_owned(),
581                                alias: None,
582                                index: None
583                            },
584                            joins: vec![]
585                        },
586                        selection: None,
587                        group_by: vec![],
588                        having: None
589                    })),
590                    order_by: vec![],
591                    limit: None,
592                    offset: None
593                })),
594                engine: None,
595                foreign_keys: Vec::new(),
596                comment: None,
597            }
598            .to_sql()
599        );
600
601        assert_eq!(
602            r#"CREATE TABLE IF NOT EXISTS "Foo" AS VALUES (TRUE);"#,
603            Statement::CreateTable {
604                if_not_exists: true,
605                name: "Foo".into(),
606                columns: None,
607                source: Some(Box::new(Query {
608                    body: SetExpr::Values(Values(vec![vec![Expr::Literal(AstLiteral::Boolean(
609                        true
610                    ))]])),
611                    order_by: vec![],
612                    limit: None,
613                    offset: None
614                })),
615                engine: None,
616                foreign_keys: Vec::new(),
617                comment: None,
618            }
619            .to_sql()
620        );
621    }
622
623    #[test]
624    fn to_sql_create_table_with_engine() {
625        assert_eq!(
626            r#"CREATE TABLE "Foo" ENGINE = MEMORY;"#,
627            Statement::CreateTable {
628                if_not_exists: false,
629                name: "Foo".into(),
630                columns: None,
631                source: None,
632                engine: Some("MEMORY".to_owned()),
633                foreign_keys: Vec::new(),
634                comment: None,
635            }
636            .to_sql()
637        );
638
639        assert_eq!(
640            r#"CREATE TABLE "Foo" ("id" BOOLEAN NOT NULL) ENGINE = SLED;"#,
641            Statement::CreateTable {
642                if_not_exists: false,
643                name: "Foo".into(),
644                columns: Some(vec![ColumnDef {
645                    name: "id".to_owned(),
646                    data_type: DataType::Boolean,
647                    nullable: false,
648                    default: None,
649                    unique: None,
650                    comment: None,
651                },]),
652                source: None,
653                engine: Some("SLED".to_owned()),
654                foreign_keys: Vec::new(),
655                comment: None,
656            }
657            .to_sql()
658        );
659    }
660
661    #[test]
662    fn to_sql_insert_function() {
663        assert_eq!(
664            r#"CREATE FUNCTION add("num" INT DEFAULT 0) RETURN "num";"#,
665            Statement::CreateFunction {
666                or_replace: false,
667                name: "add".into(),
668                args: vec![OperateFunctionArg {
669                    name: "num".into(),
670                    data_type: DataType::Int,
671                    default: Some(Expr::Literal(AstLiteral::Number(
672                        BigDecimal::from_str("0").unwrap()
673                    ))),
674                }],
675                return_: Expr::Identifier("num".to_owned())
676            }
677            .to_sql()
678        );
679        assert_eq!(
680            "CREATE OR REPLACE FUNCTION add() RETURN 1;",
681            Statement::CreateFunction {
682                or_replace: true,
683                name: "add".into(),
684                args: vec![],
685                return_: Expr::Literal(AstLiteral::Number(BigDecimal::from_str("1").unwrap()))
686            }
687            .to_sql()
688        );
689    }
690
691    #[test]
692    fn to_sql_alter_table() {
693        assert_eq!(
694            r#"ALTER TABLE "Foo" ADD COLUMN "amount" INT NOT NULL DEFAULT 10;"#,
695            Statement::AlterTable {
696                name: "Foo".into(),
697                operation: AlterTableOperation::AddColumn {
698                    column_def: ColumnDef {
699                        name: "amount".to_owned(),
700                        data_type: DataType::Int,
701                        nullable: false,
702                        default: Some(Expr::Literal(AstLiteral::Number(
703                            BigDecimal::from_str("10").unwrap()
704                        ))),
705                        unique: None,
706                        comment: None,
707                    }
708                }
709            }
710            .to_sql()
711        );
712
713        assert_eq!(
714            r#"ALTER TABLE "Foo" DROP COLUMN "something";"#,
715            Statement::AlterTable {
716                name: "Foo".into(),
717                operation: AlterTableOperation::DropColumn {
718                    column_name: "something".to_owned(),
719                    if_exists: false
720                }
721            }
722            .to_sql()
723        );
724
725        assert_eq!(
726            r#"ALTER TABLE "Foo" DROP COLUMN IF EXISTS "something";"#,
727            Statement::AlterTable {
728                name: "Foo".into(),
729                operation: AlterTableOperation::DropColumn {
730                    column_name: "something".to_owned(),
731                    if_exists: true
732                }
733            }
734            .to_sql()
735        );
736
737        assert_eq!(
738            r#"ALTER TABLE "Bar" RENAME COLUMN "id" TO "new_id";"#,
739            Statement::AlterTable {
740                name: "Bar".into(),
741                operation: AlterTableOperation::RenameColumn {
742                    old_column_name: "id".to_owned(),
743                    new_column_name: "new_id".to_owned()
744                }
745            }
746            .to_sql()
747        );
748
749        assert_eq!(
750            r#"ALTER TABLE "Foo" RENAME TO "Bar";"#,
751            Statement::AlterTable {
752                name: "Foo".to_owned(),
753                operation: AlterTableOperation::RenameTable {
754                    table_name: "Bar".to_owned(),
755                }
756            }
757            .to_sql()
758        );
759    }
760
761    #[test]
762    fn to_sql_drop_table() {
763        assert_eq!(
764            r#"DROP TABLE "Test";"#,
765            Statement::DropTable {
766                if_exists: false,
767                names: vec!["Test".into()],
768                cascade: false,
769            }
770            .to_sql()
771        );
772
773        assert_eq!(
774            r#"DROP TABLE IF EXISTS "Test";"#,
775            Statement::DropTable {
776                if_exists: true,
777                names: vec!["Test".into()],
778                cascade: false,
779            }
780            .to_sql()
781        );
782
783        assert_eq!(
784            r#"DROP TABLE "Foo", "Bar";"#,
785            Statement::DropTable {
786                if_exists: false,
787                names: vec!["Foo".into(), "Bar".into(),],
788                cascade: false,
789            }
790            .to_sql()
791        );
792    }
793
794    #[test]
795    fn to_sql_delete_function() {
796        assert_eq!(
797            "DROP FUNCTION Test;",
798            Statement::DropFunction {
799                if_exists: false,
800                names: vec!["Test".into()]
801            }
802            .to_sql()
803        );
804
805        assert_eq!(
806            "DROP FUNCTION IF EXISTS Test;",
807            Statement::DropFunction {
808                if_exists: true,
809                names: vec!["Test".into()]
810            }
811            .to_sql()
812        );
813
814        assert_eq!(
815            "DROP FUNCTION Foo, Bar;",
816            Statement::DropFunction {
817                if_exists: false,
818                names: vec!["Foo".into(), "Bar".into(),]
819            }
820            .to_sql()
821        );
822    }
823
824    #[test]
825    fn to_sql_create_index() {
826        assert_eq!(
827            r#"CREATE INDEX "idx_name" ON "Test" ("LastName");"#,
828            Statement::CreateIndex {
829                name: "idx_name".into(),
830                table_name: "Test".into(),
831                column: OrderByExpr {
832                    expr: Expr::Identifier("LastName".to_owned()),
833                    asc: None
834                }
835            }
836            .to_sql()
837        );
838    }
839
840    #[test]
841    fn to_sql_drop_index() {
842        assert_eq!(
843            "DROP INDEX Test.idx_id;",
844            Statement::DropIndex {
845                name: "idx_id".into(),
846                table_name: "Test".into(),
847            }
848            .to_sql()
849        )
850    }
851
852    #[test]
853    fn to_sql_transaction() {
854        assert_eq!("START TRANSACTION;", Statement::StartTransaction.to_sql());
855        assert_eq!("COMMIT;", Statement::Commit.to_sql());
856        assert_eq!("ROLLBACK;", Statement::Rollback.to_sql());
857    }
858
859    #[test]
860    fn to_sql_show_variable() {
861        assert_eq!(
862            "SHOW TABLES;",
863            Statement::ShowVariable(Variable::Tables).to_sql()
864        );
865        assert_eq!(
866            "SHOW FUNCTIONS;",
867            Statement::ShowVariable(Variable::Functions).to_sql()
868        );
869        assert_eq!(
870            "SHOW VERSIONS;",
871            Statement::ShowVariable(Variable::Version).to_sql()
872        );
873    }
874
875    #[test]
876    fn to_sql_show_indexes() {
877        assert_eq!(
878            r#"SHOW INDEXES FROM "Test";"#,
879            Statement::ShowIndexes("Test".into()).to_sql()
880        );
881    }
882
883    #[test]
884    fn to_sql_assignment() {
885        assert_eq!(
886            r#""count" = 5"#,
887            Assignment {
888                id: "count".to_owned(),
889                value: Expr::Literal(AstLiteral::Number(BigDecimal::from_str("5").unwrap()))
890            }
891            .to_sql()
892        )
893    }
894
895    #[test]
896    fn to_sql_foreign_key() {
897        assert_eq!(
898            r#"CONSTRAINT "fk_id" FOREIGN KEY ("id") REFERENCES "Test" ("id") ON DELETE NO ACTION ON UPDATE NO ACTION"#,
899            ForeignKey {
900                name: "fk_id".into(),
901                referencing_column_name: "id".into(),
902                referenced_table_name: "Test".into(),
903                referenced_column_name: "id".into(),
904                on_delete: ReferentialAction::NoAction,
905                on_update: ReferentialAction::NoAction,
906            }
907            .to_sql()
908        )
909    }
910}