Skip to main content

reinhardt_query/query/
create_table.rs

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