1use super::schema::{CheckExpr, Column};
16use super::types::ColumnType;
17
18#[derive(Debug, Clone)]
20pub enum AlterOp {
21 AddColumn(Column),
23 DropColumn {
25 name: String,
27 cascade: bool,
29 },
30 RenameColumn {
32 from: String,
34 to: String,
36 },
37 AlterType {
39 column: String,
41 new_type: ColumnType,
43 using: Option<String>,
45 },
46 SetNotNull(String),
48 DropNotNull(String),
50 SetDefault {
52 column: String,
54 expr: String,
56 },
57 DropDefault(String),
59 AddConstraint {
61 name: String,
63 constraint: TableConstraint,
65 },
66 DropConstraint {
68 name: String,
70 cascade: bool,
72 },
73 RenameTable(String),
75 SetSchema(String),
77 SetRowLevelSecurity(bool),
79 ForceRowLevelSecurity(bool),
81}
82
83#[derive(Debug, Clone)]
85pub enum TableConstraint {
86 PrimaryKey(Vec<String>),
88 Unique(Vec<String>),
90 Check(CheckExpr),
92 ForeignKey {
94 columns: Vec<String>,
96 ref_table: String,
98 ref_columns: Vec<String>,
100 },
101 Exclude {
103 method: String,
105 elements: Vec<String>,
107 },
108}
109
110#[derive(Debug, Clone)]
112pub struct AlterTable {
113 pub table: String,
115 pub ops: Vec<AlterOp>,
117 pub only: bool,
119 pub if_exists: bool,
121}
122
123impl AlterTable {
124 pub fn new(table: impl Into<String>) -> Self {
126 Self {
127 table: table.into(),
128 ops: Vec::new(),
129 only: false,
130 if_exists: false,
131 }
132 }
133
134 pub fn only(mut self) -> Self {
136 self.only = true;
137 self
138 }
139
140 pub fn if_exists(mut self) -> Self {
142 self.if_exists = true;
143 self
144 }
145
146 pub fn add_column(mut self, col: Column) -> Self {
148 self.ops.push(AlterOp::AddColumn(col));
149 self
150 }
151
152 pub fn drop_column(mut self, name: impl Into<String>) -> Self {
154 self.ops.push(AlterOp::DropColumn {
155 name: name.into(),
156 cascade: false,
157 });
158 self
159 }
160
161 pub fn drop_column_cascade(mut self, name: impl Into<String>) -> Self {
163 self.ops.push(AlterOp::DropColumn {
164 name: name.into(),
165 cascade: true,
166 });
167 self
168 }
169
170 pub fn rename_column(mut self, from: impl Into<String>, to: impl Into<String>) -> Self {
172 self.ops.push(AlterOp::RenameColumn {
173 from: from.into(),
174 to: to.into(),
175 });
176 self
177 }
178
179 pub fn set_type(mut self, column: impl Into<String>, new_type: ColumnType) -> Self {
181 self.ops.push(AlterOp::AlterType {
182 column: column.into(),
183 new_type,
184 using: None,
185 });
186 self
187 }
188
189 pub fn set_type_using(
191 mut self,
192 column: impl Into<String>,
193 new_type: ColumnType,
194 using: impl Into<String>,
195 ) -> Self {
196 self.ops.push(AlterOp::AlterType {
197 column: column.into(),
198 new_type,
199 using: Some(using.into()),
200 });
201 self
202 }
203
204 pub fn set_not_null(mut self, column: impl Into<String>) -> Self {
206 self.ops.push(AlterOp::SetNotNull(column.into()));
207 self
208 }
209
210 pub fn drop_not_null(mut self, column: impl Into<String>) -> Self {
212 self.ops.push(AlterOp::DropNotNull(column.into()));
213 self
214 }
215
216 pub fn set_default(mut self, column: impl Into<String>, expr: impl Into<String>) -> Self {
218 self.ops.push(AlterOp::SetDefault {
219 column: column.into(),
220 expr: expr.into(),
221 });
222 self
223 }
224
225 pub fn drop_default(mut self, column: impl Into<String>) -> Self {
227 self.ops.push(AlterOp::DropDefault(column.into()));
228 self
229 }
230
231 pub fn add_constraint(mut self, name: impl Into<String>, constraint: TableConstraint) -> Self {
233 self.ops.push(AlterOp::AddConstraint {
234 name: name.into(),
235 constraint,
236 });
237 self
238 }
239
240 pub fn drop_constraint(mut self, name: impl Into<String>) -> Self {
242 self.ops.push(AlterOp::DropConstraint {
243 name: name.into(),
244 cascade: false,
245 });
246 self
247 }
248
249 pub fn drop_constraint_cascade(mut self, name: impl Into<String>) -> Self {
251 self.ops.push(AlterOp::DropConstraint {
252 name: name.into(),
253 cascade: true,
254 });
255 self
256 }
257
258 pub fn rename_to(mut self, name: impl Into<String>) -> Self {
260 self.ops.push(AlterOp::RenameTable(name.into()));
261 self
262 }
263
264 pub fn set_schema(mut self, schema: impl Into<String>) -> Self {
266 self.ops.push(AlterOp::SetSchema(schema.into()));
267 self
268 }
269
270 pub fn enable_rls(mut self) -> Self {
272 self.ops.push(AlterOp::SetRowLevelSecurity(true));
273 self
274 }
275
276 pub fn disable_rls(mut self) -> Self {
278 self.ops.push(AlterOp::SetRowLevelSecurity(false));
279 self
280 }
281
282 pub fn force_rls(mut self) -> Self {
284 self.ops.push(AlterOp::ForceRowLevelSecurity(true));
285 self
286 }
287
288 pub fn no_force_rls(mut self) -> Self {
290 self.ops.push(AlterOp::ForceRowLevelSecurity(false));
291 self
292 }
293}
294
295#[cfg(test)]
296mod tests {
297 use super::*;
298 use crate::migrate::types::ColumnType;
299
300 #[test]
301 fn test_alter_table_builder() {
302 let alter = AlterTable::new("users")
303 .add_column(Column::new("bio", ColumnType::Text))
304 .drop_column("legacy")
305 .rename_column("username", "handle")
306 .set_not_null("email");
307
308 assert_eq!(alter.table, "users");
309 assert_eq!(alter.ops.len(), 4);
310 }
311
312 #[test]
313 fn test_alter_type_with_using() {
314 let alter = AlterTable::new("users").set_type_using("age", ColumnType::Int, "age::integer");
315
316 match &alter.ops[0] {
317 AlterOp::AlterType { column, using, .. } => {
318 assert_eq!(column, "age");
319 assert_eq!(using.as_ref().unwrap(), "age::integer");
320 }
321 _ => panic!("Expected AlterType"),
322 }
323 }
324
325 #[test]
326 fn test_add_constraint() {
327 let alter = AlterTable::new("users")
328 .add_constraint("pk_users", TableConstraint::PrimaryKey(vec!["id".into()]));
329
330 assert_eq!(alter.ops.len(), 1);
331 }
332}