gluesql_core/ast/
ddl.rs

1use {
2    super::{DataType, Expr},
3    crate::ast::ToSql,
4    serde::{Deserialize, Serialize},
5};
6
7#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
8pub enum AlterTableOperation {
9    /// `ADD [ COLUMN ] <column_def>`
10    AddColumn { column_def: ColumnDef },
11    /// `DROP [ COLUMN ] [ IF EXISTS ] <column_name> [ CASCADE ]`
12    DropColumn {
13        column_name: String,
14        if_exists: bool,
15    },
16    /// `RENAME [ COLUMN ] <old_column_name> TO <new_column_name>`
17    RenameColumn {
18        old_column_name: String,
19        new_column_name: String,
20    },
21    /// `RENAME TO <table_name>`
22    RenameTable { table_name: String },
23}
24
25#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
26pub struct ColumnDef {
27    pub name: String,
28    pub data_type: DataType,
29    pub nullable: bool,
30    /// `DEFAULT <restricted-expr>`
31    pub default: Option<Expr>,
32    /// `{ PRIMARY KEY | UNIQUE }`
33    pub unique: Option<ColumnUniqueOption>,
34    pub comment: Option<String>,
35}
36
37#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
38pub struct ColumnUniqueOption {
39    pub is_primary: bool,
40}
41
42#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
43pub struct OperateFunctionArg {
44    pub name: String,
45    pub data_type: DataType,
46    /// `DEFAULT <restricted-expr>`
47    pub default: Option<Expr>,
48}
49
50impl ToSql for ColumnDef {
51    fn to_sql(&self) -> String {
52        let ColumnDef {
53            name,
54            data_type,
55            nullable,
56            default,
57            unique,
58            comment,
59        } = self;
60        {
61            let nullable = match nullable {
62                true => "NULL",
63                false => "NOT NULL",
64            };
65            let column_def = format!(r#""{name}" {data_type} {nullable}"#);
66            let default = default
67                .as_ref()
68                .map(|expr| format!("DEFAULT {}", expr.to_sql()));
69            let unique = unique.as_ref().map(ToSql::to_sql);
70            let comment = comment
71                .as_ref()
72                .map(|comment| format!("COMMENT '{comment}'"));
73
74            [Some(column_def), default, unique, comment]
75                .into_iter()
76                .flatten()
77                .collect::<Vec<_>>()
78                .join(" ")
79        }
80    }
81}
82
83impl ToSql for ColumnUniqueOption {
84    fn to_sql(&self) -> String {
85        if self.is_primary {
86            "PRIMARY KEY"
87        } else {
88            "UNIQUE"
89        }
90        .to_owned()
91    }
92}
93
94#[cfg(test)]
95mod tests {
96    use crate::{
97        ast::{ColumnDef, ColumnUniqueOption, DataType, Expr, ToSql},
98        data::Value,
99    };
100
101    #[test]
102    fn to_sql_column_def() {
103        assert_eq!(
104            r#""name" TEXT NOT NULL UNIQUE"#,
105            ColumnDef {
106                name: "name".to_owned(),
107                data_type: DataType::Text,
108                nullable: false,
109                default: None,
110                unique: Some(ColumnUniqueOption { is_primary: false }),
111                comment: None,
112            }
113            .to_sql()
114        );
115
116        assert_eq!(
117            r#""accepted" BOOLEAN NULL"#,
118            ColumnDef {
119                name: "accepted".to_owned(),
120                data_type: DataType::Boolean,
121                nullable: true,
122                default: None,
123                unique: None,
124                comment: None,
125            }
126            .to_sql()
127        );
128
129        assert_eq!(
130            r#""id" INT NOT NULL PRIMARY KEY"#,
131            ColumnDef {
132                name: "id".to_owned(),
133                data_type: DataType::Int,
134                nullable: false,
135                default: None,
136                unique: Some(ColumnUniqueOption { is_primary: true }),
137                comment: None,
138            }
139            .to_sql()
140        );
141
142        assert_eq!(
143            r#""accepted" BOOLEAN NOT NULL DEFAULT FALSE"#,
144            ColumnDef {
145                name: "accepted".to_owned(),
146                data_type: DataType::Boolean,
147                nullable: false,
148                default: Some(Expr::Value(Value::Bool(false))),
149                unique: None,
150                comment: None,
151            }
152            .to_sql()
153        );
154
155        assert_eq!(
156            r#""accepted" BOOLEAN NOT NULL DEFAULT FALSE UNIQUE"#,
157            ColumnDef {
158                name: "accepted".to_owned(),
159                data_type: DataType::Boolean,
160                nullable: false,
161                default: Some(Expr::Value(Value::Bool(false))),
162                unique: Some(ColumnUniqueOption { is_primary: false }),
163                comment: None,
164            }
165            .to_sql()
166        );
167
168        assert_eq!(
169            r#""accepted" BOOLEAN NOT NULL COMMENT 'this is comment'"#,
170            ColumnDef {
171                name: "accepted".to_owned(),
172                data_type: DataType::Boolean,
173                nullable: false,
174                default: None,
175                unique: None,
176                comment: Some("this is comment".to_owned()),
177            }
178            .to_sql()
179        );
180    }
181}