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 AlterTableOperation {
51 fn to_sql(&self) -> String {
52 match self {
53 AlterTableOperation::AddColumn { column_def } => {
54 format!("ADD COLUMN {}", column_def.to_sql())
55 }
56 AlterTableOperation::DropColumn {
57 column_name,
58 if_exists,
59 } => match if_exists {
60 true => format!(r#"DROP COLUMN IF EXISTS "{column_name}""#),
61 false => format!(r#"DROP COLUMN "{column_name}""#),
62 },
63 AlterTableOperation::RenameColumn {
64 old_column_name,
65 new_column_name,
66 } => format!(r#"RENAME COLUMN "{old_column_name}" TO "{new_column_name}""#),
67 AlterTableOperation::RenameTable { table_name } => {
68 format!(r#"RENAME TO "{table_name}""#)
69 }
70 }
71 }
72}
73
74impl ToSql for ColumnDef {
75 fn to_sql(&self) -> String {
76 let ColumnDef {
77 name,
78 data_type,
79 nullable,
80 default,
81 unique,
82 comment,
83 } = self;
84 {
85 let nullable = match nullable {
86 true => "NULL",
87 false => "NOT NULL",
88 };
89 let column_def = format!(r#""{name}" {data_type} {nullable}"#);
90 let default = default
91 .as_ref()
92 .map(|expr| format!("DEFAULT {}", expr.to_sql()));
93 let unique = unique.as_ref().map(ToSql::to_sql);
94 let comment = comment
95 .as_ref()
96 .map(|comment| format!("COMMENT '{}'", comment));
97
98 [Some(column_def), default, unique, comment]
99 .into_iter()
100 .flatten()
101 .collect::<Vec<_>>()
102 .join(" ")
103 }
104 }
105}
106
107impl ToSql for ColumnUniqueOption {
108 fn to_sql(&self) -> String {
109 if self.is_primary {
110 "PRIMARY KEY"
111 } else {
112 "UNIQUE"
113 }
114 .to_owned()
115 }
116}
117
118impl ToSql for OperateFunctionArg {
119 fn to_sql(&self) -> String {
120 let OperateFunctionArg {
121 name,
122 data_type,
123 default,
124 } = self;
125 let default = default
126 .as_ref()
127 .map(|expr| format!(" DEFAULT {}", expr.to_sql()))
128 .unwrap_or_else(|| "".to_owned());
129 format!(r#""{name}" {data_type}{default}"#)
130 }
131}
132
133#[cfg(test)]
134mod tests {
135 use crate::ast::{
136 AstLiteral, ColumnDef, ColumnUniqueOption, DataType, Expr, OperateFunctionArg, ToSql,
137 };
138
139 #[test]
140 fn to_sql_column_def() {
141 assert_eq!(
142 r#""name" TEXT NOT NULL UNIQUE"#,
143 ColumnDef {
144 name: "name".to_owned(),
145 data_type: DataType::Text,
146 nullable: false,
147 default: None,
148 unique: Some(ColumnUniqueOption { is_primary: false }),
149 comment: None,
150 }
151 .to_sql()
152 );
153
154 assert_eq!(
155 r#""accepted" BOOLEAN NULL"#,
156 ColumnDef {
157 name: "accepted".to_owned(),
158 data_type: DataType::Boolean,
159 nullable: true,
160 default: None,
161 unique: None,
162 comment: None,
163 }
164 .to_sql()
165 );
166
167 assert_eq!(
168 r#""id" INT NOT NULL PRIMARY KEY"#,
169 ColumnDef {
170 name: "id".to_owned(),
171 data_type: DataType::Int,
172 nullable: false,
173 default: None,
174 unique: Some(ColumnUniqueOption { is_primary: true }),
175 comment: None,
176 }
177 .to_sql()
178 );
179
180 assert_eq!(
181 r#""accepted" BOOLEAN NOT NULL DEFAULT FALSE"#,
182 ColumnDef {
183 name: "accepted".to_owned(),
184 data_type: DataType::Boolean,
185 nullable: false,
186 default: Some(Expr::Literal(AstLiteral::Boolean(false))),
187 unique: None,
188 comment: None,
189 }
190 .to_sql()
191 );
192
193 assert_eq!(
194 r#""accepted" BOOLEAN NOT NULL DEFAULT FALSE UNIQUE"#,
195 ColumnDef {
196 name: "accepted".to_owned(),
197 data_type: DataType::Boolean,
198 nullable: false,
199 default: Some(Expr::Literal(AstLiteral::Boolean(false))),
200 unique: Some(ColumnUniqueOption { is_primary: false }),
201 comment: None,
202 }
203 .to_sql()
204 );
205
206 assert_eq!(
207 r#""accepted" BOOLEAN NOT NULL COMMENT 'this is comment'"#,
208 ColumnDef {
209 name: "accepted".to_owned(),
210 data_type: DataType::Boolean,
211 nullable: false,
212 default: None,
213 unique: None,
214 comment: Some("this is comment".to_owned()),
215 }
216 .to_sql()
217 );
218 }
219
220 #[test]
221 fn to_sql_operate_function_arg() {
222 assert_eq!(
223 r#""name" TEXT"#,
224 OperateFunctionArg {
225 name: "name".to_owned(),
226 data_type: DataType::Text,
227 default: None,
228 }
229 .to_sql()
230 );
231
232 assert_eq!(
233 r#""accepted" BOOLEAN DEFAULT FALSE"#,
234 OperateFunctionArg {
235 name: "accepted".to_owned(),
236 data_type: DataType::Boolean,
237 default: Some(Expr::Literal(AstLiteral::Boolean(false))),
238 }
239 .to_sql()
240 );
241 }
242}