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