reinhardt_query/query/alter_table.rs
1//! ALTER TABLE statement builder
2//!
3//! This module provides the `AlterTableStatement` type for building SQL ALTER TABLE queries.
4
5use crate::{
6 backend::QueryBuilder,
7 types::{ColumnDef, DynIden, ForeignKeyAction, IntoIden, IntoTableRef, TableRef},
8};
9
10use super::traits::{QueryBuilderTrait, QueryStatementBuilder, QueryStatementWriter};
11
12/// ALTER TABLE statement builder
13///
14/// This struct provides a fluent API for constructing ALTER TABLE queries.
15///
16/// # Examples
17///
18/// ```rust,ignore
19/// use reinhardt_query::prelude::*;
20/// use reinhardt_query::types::ddl::{ColumnDef, ColumnType};
21///
22/// let query = Query::alter_table()
23/// .table("users")
24/// .add_column(
25/// ColumnDef::new("age")
26/// .column_type(ColumnType::Integer)
27/// );
28/// ```
29#[derive(Debug, Clone)]
30pub struct AlterTableStatement {
31 pub(crate) table: Option<TableRef>,
32 pub(crate) operations: Vec<AlterTableOperation>,
33}
34
35/// ALTER TABLE operation
36///
37/// This enum represents the various operations that can be performed with ALTER TABLE.
38#[derive(Debug, Clone)]
39#[non_exhaustive]
40pub enum AlterTableOperation {
41 /// ADD COLUMN
42 AddColumn(ColumnDef),
43 /// DROP COLUMN
44 DropColumn {
45 /// Column name to drop
46 name: DynIden,
47 /// IF EXISTS clause
48 if_exists: bool,
49 },
50 /// RENAME COLUMN
51 RenameColumn {
52 /// Old column name
53 old: DynIden,
54 /// New column name
55 new: DynIden,
56 },
57 /// MODIFY COLUMN / ALTER COLUMN (type or constraints)
58 ModifyColumn(ColumnDef),
59 /// ADD CONSTRAINT
60 AddConstraint(crate::types::TableConstraint),
61 /// DROP CONSTRAINT
62 DropConstraint {
63 /// Constraint name
64 name: DynIden,
65 /// IF EXISTS clause
66 if_exists: bool,
67 },
68 /// RENAME TABLE
69 RenameTable(DynIden),
70}
71
72impl AlterTableStatement {
73 /// Create a new ALTER TABLE statement
74 pub fn new() -> Self {
75 Self {
76 table: None,
77 operations: Vec::new(),
78 }
79 }
80
81 /// Take the ownership of data in the current [`AlterTableStatement`]
82 pub fn take(&mut self) -> Self {
83 Self {
84 table: self.table.take(),
85 operations: std::mem::take(&mut self.operations),
86 }
87 }
88
89 /// Set the table to alter
90 ///
91 /// # Examples
92 ///
93 /// ```rust,ignore
94 /// use reinhardt_query::prelude::*;
95 ///
96 /// let query = Query::alter_table()
97 /// .table("users");
98 /// ```
99 pub fn table<T>(&mut self, tbl: T) -> &mut Self
100 where
101 T: IntoTableRef,
102 {
103 self.table = Some(tbl.into_table_ref());
104 self
105 }
106
107 /// Add a column
108 ///
109 /// # Examples
110 ///
111 /// ```rust,ignore
112 /// use reinhardt_query::prelude::*;
113 /// use reinhardt_query::types::ddl::{ColumnDef, ColumnType};
114 ///
115 /// let query = Query::alter_table()
116 /// .table("users")
117 /// .add_column(
118 /// ColumnDef::new("age")
119 /// .column_type(ColumnType::Integer)
120 /// .not_null(false)
121 /// );
122 /// ```
123 pub fn add_column(&mut self, column: ColumnDef) -> &mut Self {
124 self.operations.push(AlterTableOperation::AddColumn(column));
125 self
126 }
127
128 /// Drop a column
129 ///
130 /// # Examples
131 ///
132 /// ```rust,ignore
133 /// use reinhardt_query::prelude::*;
134 ///
135 /// let query = Query::alter_table()
136 /// .table("users")
137 /// .drop_column("age");
138 /// ```
139 pub fn drop_column<C>(&mut self, column: C) -> &mut Self
140 where
141 C: IntoIden,
142 {
143 self.operations.push(AlterTableOperation::DropColumn {
144 name: column.into_iden(),
145 if_exists: false,
146 });
147 self
148 }
149
150 /// Drop a column with IF EXISTS
151 ///
152 /// # Examples
153 ///
154 /// ```rust,ignore
155 /// use reinhardt_query::prelude::*;
156 ///
157 /// let query = Query::alter_table()
158 /// .table("users")
159 /// .drop_column_if_exists("age");
160 /// ```
161 pub fn drop_column_if_exists<C>(&mut self, column: C) -> &mut Self
162 where
163 C: IntoIden,
164 {
165 self.operations.push(AlterTableOperation::DropColumn {
166 name: column.into_iden(),
167 if_exists: true,
168 });
169 self
170 }
171
172 /// Rename a column
173 ///
174 /// # Examples
175 ///
176 /// ```rust,ignore
177 /// use reinhardt_query::prelude::*;
178 ///
179 /// let query = Query::alter_table()
180 /// .table("users")
181 /// .rename_column("old_name", "new_name");
182 /// ```
183 pub fn rename_column<C1, C2>(&mut self, old: C1, new: C2) -> &mut Self
184 where
185 C1: IntoIden,
186 C2: IntoIden,
187 {
188 self.operations.push(AlterTableOperation::RenameColumn {
189 old: old.into_iden(),
190 new: new.into_iden(),
191 });
192 self
193 }
194
195 /// Modify a column (type or constraints)
196 ///
197 /// # Examples
198 ///
199 /// ```rust,ignore
200 /// use reinhardt_query::prelude::*;
201 /// use reinhardt_query::types::ddl::{ColumnDef, ColumnType};
202 ///
203 /// let query = Query::alter_table()
204 /// .table("users")
205 /// .modify_column(
206 /// ColumnDef::new("age")
207 /// .column_type(ColumnType::BigInteger)
208 /// );
209 /// ```
210 pub fn modify_column(&mut self, column: ColumnDef) -> &mut Self {
211 self.operations
212 .push(AlterTableOperation::ModifyColumn(column));
213 self
214 }
215
216 /// Add a constraint
217 ///
218 /// # Examples
219 ///
220 /// ```rust,ignore
221 /// use reinhardt_query::prelude::*;
222 /// use reinhardt_query::types::ddl::TableConstraint;
223 ///
224 /// let query = Query::alter_table()
225 /// .table("users")
226 /// .add_constraint(TableConstraint::Unique {
227 /// name: Some("uq_email".into()),
228 /// columns: vec!["email".into()],
229 /// });
230 /// ```
231 pub fn add_constraint(&mut self, constraint: crate::types::TableConstraint) -> &mut Self {
232 self.operations
233 .push(AlterTableOperation::AddConstraint(constraint));
234 self
235 }
236
237 /// Drop a constraint
238 ///
239 /// # Examples
240 ///
241 /// ```rust,ignore
242 /// use reinhardt_query::prelude::*;
243 ///
244 /// let query = Query::alter_table()
245 /// .table("users")
246 /// .drop_constraint("uq_email");
247 /// ```
248 pub fn drop_constraint<C>(&mut self, constraint: C) -> &mut Self
249 where
250 C: IntoIden,
251 {
252 self.operations.push(AlterTableOperation::DropConstraint {
253 name: constraint.into_iden(),
254 if_exists: false,
255 });
256 self
257 }
258
259 /// Drop a constraint with IF EXISTS
260 ///
261 /// # Examples
262 ///
263 /// ```rust,ignore
264 /// use reinhardt_query::prelude::*;
265 ///
266 /// let query = Query::alter_table()
267 /// .table("users")
268 /// .drop_constraint_if_exists("uq_email");
269 /// ```
270 pub fn drop_constraint_if_exists<C>(&mut self, constraint: C) -> &mut Self
271 where
272 C: IntoIden,
273 {
274 self.operations.push(AlterTableOperation::DropConstraint {
275 name: constraint.into_iden(),
276 if_exists: true,
277 });
278 self
279 }
280
281 /// Rename table
282 ///
283 /// # Examples
284 ///
285 /// ```rust,ignore
286 /// use reinhardt_query::prelude::*;
287 ///
288 /// let query = Query::alter_table()
289 /// .table("users")
290 /// .rename_table("accounts");
291 /// ```
292 pub fn rename_table<T>(&mut self, new_name: T) -> &mut Self
293 where
294 T: IntoIden,
295 {
296 self.operations
297 .push(AlterTableOperation::RenameTable(new_name.into_iden()));
298 self
299 }
300
301 /// Add a primary key constraint
302 ///
303 /// This is a convenience method for adding a PRIMARY KEY constraint.
304 ///
305 /// # Examples
306 ///
307 /// ```rust,ignore
308 /// use reinhardt_query::prelude::*;
309 ///
310 /// let query = Query::alter_table()
311 /// .table("users")
312 /// .add_primary_key(vec!["id"]);
313 /// ```
314 pub fn add_primary_key<I, C>(&mut self, columns: I) -> &mut Self
315 where
316 I: IntoIterator<Item = C>,
317 C: IntoIden,
318 {
319 self.operations.push(AlterTableOperation::AddConstraint(
320 crate::types::TableConstraint::PrimaryKey {
321 name: None,
322 columns: columns.into_iter().map(|c| c.into_iden()).collect(),
323 },
324 ));
325 self
326 }
327
328 /// Add a unique constraint
329 ///
330 /// This is a convenience method for adding a UNIQUE constraint.
331 ///
332 /// # Examples
333 ///
334 /// ```rust,ignore
335 /// use reinhardt_query::prelude::*;
336 ///
337 /// let query = Query::alter_table()
338 /// .table("users")
339 /// .add_unique(vec!["email"]);
340 /// ```
341 pub fn add_unique<I, C>(&mut self, columns: I) -> &mut Self
342 where
343 I: IntoIterator<Item = C>,
344 C: IntoIden,
345 {
346 self.operations.push(AlterTableOperation::AddConstraint(
347 crate::types::TableConstraint::Unique {
348 name: None,
349 columns: columns.into_iter().map(|c| c.into_iden()).collect(),
350 },
351 ));
352 self
353 }
354
355 /// Add a foreign key constraint
356 ///
357 /// This is a convenience method for adding a FOREIGN KEY constraint.
358 ///
359 /// # Examples
360 ///
361 /// ```rust,ignore
362 /// use reinhardt_query::prelude::*;
363 /// use reinhardt_query::types::ddl::ForeignKeyAction;
364 ///
365 /// let query = Query::alter_table()
366 /// .table("posts")
367 /// .add_foreign_key(
368 /// vec!["user_id"],
369 /// "users",
370 /// vec!["id"],
371 /// Some(ForeignKeyAction::Cascade),
372 /// None,
373 /// );
374 /// ```
375 pub fn add_foreign_key<I1, C1, T, I2, C2>(
376 &mut self,
377 columns: I1,
378 ref_table: T,
379 ref_columns: I2,
380 on_delete: Option<ForeignKeyAction>,
381 on_update: Option<ForeignKeyAction>,
382 ) -> &mut Self
383 where
384 I1: IntoIterator<Item = C1>,
385 C1: IntoIden,
386 T: IntoTableRef,
387 I2: IntoIterator<Item = C2>,
388 C2: IntoIden,
389 {
390 self.operations.push(AlterTableOperation::AddConstraint(
391 crate::types::TableConstraint::ForeignKey {
392 name: None,
393 columns: columns.into_iter().map(|c| c.into_iden()).collect(),
394 ref_table: Box::new(ref_table.into_table_ref()),
395 ref_columns: ref_columns.into_iter().map(|c| c.into_iden()).collect(),
396 on_delete,
397 on_update,
398 },
399 ));
400 self
401 }
402}
403
404impl Default for AlterTableStatement {
405 fn default() -> Self {
406 Self::new()
407 }
408}
409
410impl QueryStatementBuilder for AlterTableStatement {
411 fn build_any(&self, query_builder: &dyn QueryBuilderTrait) -> (String, crate::value::Values) {
412 // Downcast to concrete QueryBuilder type
413 use std::any::Any;
414 if let Some(builder) =
415 (query_builder as &dyn Any).downcast_ref::<crate::backend::PostgresQueryBuilder>()
416 {
417 return builder.build_alter_table(self);
418 }
419 if let Some(builder) =
420 (query_builder as &dyn Any).downcast_ref::<crate::backend::MySqlQueryBuilder>()
421 {
422 return builder.build_alter_table(self);
423 }
424 if let Some(builder) =
425 (query_builder as &dyn Any).downcast_ref::<crate::backend::SqliteQueryBuilder>()
426 {
427 return builder.build_alter_table(self);
428 }
429 panic!("Unsupported query builder type");
430 }
431}
432
433impl QueryStatementWriter for AlterTableStatement {}