Skip to main content

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 {}