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    AddColumn { column_def: ColumnDef },
11    DropColumn {
13        column_name: String,
14        if_exists: bool,
15    },
16    RenameColumn {
18        old_column_name: String,
19        new_column_name: String,
20    },
21    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    pub default: Option<Expr>,
32    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    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::ast::{AstLiteral, ColumnDef, ColumnUniqueOption, DataType, Expr, ToSql};
97
98    #[test]
99    fn to_sql_column_def() {
100        assert_eq!(
101            r#""name" TEXT NOT NULL UNIQUE"#,
102            ColumnDef {
103                name: "name".to_owned(),
104                data_type: DataType::Text,
105                nullable: false,
106                default: None,
107                unique: Some(ColumnUniqueOption { is_primary: false }),
108                comment: None,
109            }
110            .to_sql()
111        );
112
113        assert_eq!(
114            r#""accepted" BOOLEAN NULL"#,
115            ColumnDef {
116                name: "accepted".to_owned(),
117                data_type: DataType::Boolean,
118                nullable: true,
119                default: None,
120                unique: None,
121                comment: None,
122            }
123            .to_sql()
124        );
125
126        assert_eq!(
127            r#""id" INT NOT NULL PRIMARY KEY"#,
128            ColumnDef {
129                name: "id".to_owned(),
130                data_type: DataType::Int,
131                nullable: false,
132                default: None,
133                unique: Some(ColumnUniqueOption { is_primary: true }),
134                comment: None,
135            }
136            .to_sql()
137        );
138
139        assert_eq!(
140            r#""accepted" BOOLEAN NOT NULL DEFAULT FALSE"#,
141            ColumnDef {
142                name: "accepted".to_owned(),
143                data_type: DataType::Boolean,
144                nullable: false,
145                default: Some(Expr::Literal(AstLiteral::Boolean(false))),
146                unique: None,
147                comment: None,
148            }
149            .to_sql()
150        );
151
152        assert_eq!(
153            r#""accepted" BOOLEAN NOT NULL DEFAULT FALSE UNIQUE"#,
154            ColumnDef {
155                name: "accepted".to_owned(),
156                data_type: DataType::Boolean,
157                nullable: false,
158                default: Some(Expr::Literal(AstLiteral::Boolean(false))),
159                unique: Some(ColumnUniqueOption { is_primary: false }),
160                comment: None,
161            }
162            .to_sql()
163        );
164
165        assert_eq!(
166            r#""accepted" BOOLEAN NOT NULL COMMENT 'this is comment'"#,
167            ColumnDef {
168                name: "accepted".to_owned(),
169                data_type: DataType::Boolean,
170                nullable: false,
171                default: None,
172                unique: None,
173                comment: Some("this is comment".to_owned()),
174            }
175            .to_sql()
176        );
177    }
178}