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