1use super::schema::{CheckExpr, Column};
16use super::types::ColumnType;
17
18#[derive(Debug, Clone)]
20pub enum AlterOp {
21 AddColumn(Column),
22 DropColumn { name: String, cascade: bool },
24 RenameColumn { from: String, to: String },
26 AlterType {
28 column: String,
29 new_type: ColumnType,
30 using: Option<String>,
31 },
32 SetNotNull(String),
33 DropNotNull(String),
34 SetDefault { column: String, expr: String },
36 DropDefault(String),
37 AddConstraint {
39 name: String,
40 constraint: TableConstraint,
41 },
42 DropConstraint { name: String, cascade: bool },
44 RenameTable(String),
46 SetSchema(String),
47 SetRowLevelSecurity(bool),
49}
50
51#[derive(Debug, Clone)]
53pub enum TableConstraint {
54 PrimaryKey(Vec<String>),
55 Unique(Vec<String>),
56 Check(CheckExpr),
57 ForeignKey {
59 columns: Vec<String>,
60 ref_table: String,
61 ref_columns: Vec<String>,
62 },
63 Exclude {
65 method: String,
66 elements: Vec<String>,
67 },
68}
69
70#[derive(Debug, Clone)]
72pub struct AlterTable {
73 pub table: String,
74 pub ops: Vec<AlterOp>,
75 pub only: bool,
76 pub if_exists: bool,
77}
78
79impl AlterTable {
80 pub fn new(table: impl Into<String>) -> Self {
82 Self {
83 table: table.into(),
84 ops: Vec::new(),
85 only: false,
86 if_exists: false,
87 }
88 }
89
90 pub fn only(mut self) -> Self {
92 self.only = true;
93 self
94 }
95
96 pub fn if_exists(mut self) -> Self {
98 self.if_exists = true;
99 self
100 }
101
102 pub fn add_column(mut self, col: Column) -> Self {
104 self.ops.push(AlterOp::AddColumn(col));
105 self
106 }
107
108 pub fn drop_column(mut self, name: impl Into<String>) -> Self {
110 self.ops.push(AlterOp::DropColumn {
111 name: name.into(),
112 cascade: false,
113 });
114 self
115 }
116
117 pub fn drop_column_cascade(mut self, name: impl Into<String>) -> Self {
119 self.ops.push(AlterOp::DropColumn {
120 name: name.into(),
121 cascade: true,
122 });
123 self
124 }
125
126 pub fn rename_column(mut self, from: impl Into<String>, to: impl Into<String>) -> Self {
128 self.ops.push(AlterOp::RenameColumn {
129 from: from.into(),
130 to: to.into(),
131 });
132 self
133 }
134
135 pub fn set_type(mut self, column: impl Into<String>, new_type: ColumnType) -> Self {
137 self.ops.push(AlterOp::AlterType {
138 column: column.into(),
139 new_type,
140 using: None,
141 });
142 self
143 }
144
145 pub fn set_type_using(
147 mut self,
148 column: impl Into<String>,
149 new_type: ColumnType,
150 using: impl Into<String>,
151 ) -> Self {
152 self.ops.push(AlterOp::AlterType {
153 column: column.into(),
154 new_type,
155 using: Some(using.into()),
156 });
157 self
158 }
159
160 pub fn set_not_null(mut self, column: impl Into<String>) -> Self {
162 self.ops.push(AlterOp::SetNotNull(column.into()));
163 self
164 }
165
166 pub fn drop_not_null(mut self, column: impl Into<String>) -> Self {
168 self.ops.push(AlterOp::DropNotNull(column.into()));
169 self
170 }
171
172 pub fn set_default(mut self, column: impl Into<String>, expr: impl Into<String>) -> Self {
174 self.ops.push(AlterOp::SetDefault {
175 column: column.into(),
176 expr: expr.into(),
177 });
178 self
179 }
180
181 pub fn drop_default(mut self, column: impl Into<String>) -> Self {
183 self.ops.push(AlterOp::DropDefault(column.into()));
184 self
185 }
186
187 pub fn add_constraint(
189 mut self,
190 name: impl Into<String>,
191 constraint: TableConstraint,
192 ) -> Self {
193 self.ops.push(AlterOp::AddConstraint {
194 name: name.into(),
195 constraint,
196 });
197 self
198 }
199
200 pub fn drop_constraint(mut self, name: impl Into<String>) -> Self {
202 self.ops.push(AlterOp::DropConstraint {
203 name: name.into(),
204 cascade: false,
205 });
206 self
207 }
208
209 pub fn drop_constraint_cascade(mut self, name: impl Into<String>) -> Self {
211 self.ops.push(AlterOp::DropConstraint {
212 name: name.into(),
213 cascade: true,
214 });
215 self
216 }
217
218 pub fn rename_to(mut self, name: impl Into<String>) -> Self {
220 self.ops.push(AlterOp::RenameTable(name.into()));
221 self
222 }
223
224 pub fn set_schema(mut self, schema: impl Into<String>) -> Self {
226 self.ops.push(AlterOp::SetSchema(schema.into()));
227 self
228 }
229
230 pub fn enable_rls(mut self) -> Self {
232 self.ops.push(AlterOp::SetRowLevelSecurity(true));
233 self
234 }
235
236 pub fn disable_rls(mut self) -> Self {
238 self.ops.push(AlterOp::SetRowLevelSecurity(false));
239 self
240 }
241}
242
243#[cfg(test)]
244mod tests {
245 use super::*;
246 use crate::migrate::types::ColumnType;
247
248 #[test]
249 fn test_alter_table_builder() {
250 let alter = AlterTable::new("users")
251 .add_column(Column::new("bio", ColumnType::Text))
252 .drop_column("legacy")
253 .rename_column("username", "handle")
254 .set_not_null("email");
255
256 assert_eq!(alter.table, "users");
257 assert_eq!(alter.ops.len(), 4);
258 }
259
260 #[test]
261 fn test_alter_type_with_using() {
262 let alter = AlterTable::new("users")
263 .set_type_using("age", ColumnType::Int, "age::integer");
264
265 match &alter.ops[0] {
266 AlterOp::AlterType { column, using, .. } => {
267 assert_eq!(column, "age");
268 assert_eq!(using.as_ref().unwrap(), "age::integer");
269 }
270 _ => panic!("Expected AlterType"),
271 }
272 }
273
274 #[test]
275 fn test_add_constraint() {
276 let alter = AlterTable::new("users").add_constraint(
277 "pk_users",
278 TableConstraint::PrimaryKey(vec!["id".into()]),
279 );
280
281 assert_eq!(alter.ops.len(), 1);
282 }
283}