sea_query/table/
create.rs

1use inherent::inherent;
2
3use crate::{
4    ColumnDef, IntoColumnDef, SchemaStatementBuilder, backend::SchemaBuilder, foreign_key::*,
5    index::*, table::constraint::Check, types::*,
6};
7
8/// Create a table
9///
10/// # Examples
11///
12/// ```
13/// use sea_query::{*, tests_cfg::*};
14///
15/// let table = Table::create()
16///     .table(Char::Table)
17///     .if_not_exists()
18///     .comment("table's comment")
19///     .col(ColumnDef::new(Char::Id).integer().not_null().auto_increment().primary_key())
20///     .col(ColumnDef::new(Char::FontSize).integer().not_null().comment("font's size"))
21///     .col(ColumnDef::new(Char::Character).string().not_null())
22///     .col(ColumnDef::new(Char::SizeW).integer().not_null())
23///     .col(ColumnDef::new(Char::SizeH).integer().not_null())
24///     .col(ColumnDef::new(Char::FontId).integer().default(Value::Int(None)))
25///     .foreign_key(
26///         ForeignKey::create()
27///             .name("FK_2e303c3a712662f1fc2a4d0aad6")
28///             .from(Char::Table, Char::FontId)
29///             .to(Font::Table, Font::Id)
30///             .on_delete(ForeignKeyAction::Cascade)
31///             .on_update(ForeignKeyAction::Cascade)
32///     )
33///     .to_owned();
34///
35/// assert_eq!(
36///     table.to_string(MysqlQueryBuilder),
37///     [
38///         r#"CREATE TABLE IF NOT EXISTS `character` ("#,
39///             r#"`id` int NOT NULL AUTO_INCREMENT PRIMARY KEY,"#,
40///             r#"`font_size` int NOT NULL COMMENT 'font\'s size',"#,
41///             r#"`character` varchar(255) NOT NULL,"#,
42///             r#"`size_w` int NOT NULL,"#,
43///             r#"`size_h` int NOT NULL,"#,
44///             r#"`font_id` int DEFAULT NULL,"#,
45///             r#"CONSTRAINT `FK_2e303c3a712662f1fc2a4d0aad6`"#,
46///                 r#"FOREIGN KEY (`font_id`) REFERENCES `font` (`id`)"#,
47///                 r#"ON DELETE CASCADE ON UPDATE CASCADE"#,
48///         r#") COMMENT 'table\'s comment'"#,
49///     ].join(" ")
50/// );
51/// assert_eq!(
52///     table.to_string(PostgresQueryBuilder),
53///     [
54///         r#"CREATE TABLE IF NOT EXISTS "character" ("#,
55///             r#""id" integer GENERATED BY DEFAULT AS IDENTITY NOT NULL PRIMARY KEY,"#,
56///             r#""font_size" integer NOT NULL,"#,
57///             r#""character" varchar NOT NULL,"#,
58///             r#""size_w" integer NOT NULL,"#,
59///             r#""size_h" integer NOT NULL,"#,
60///             r#""font_id" integer DEFAULT NULL,"#,
61///             r#"CONSTRAINT "FK_2e303c3a712662f1fc2a4d0aad6""#,
62///                 r#"FOREIGN KEY ("font_id") REFERENCES "font" ("id")"#,
63///                 r#"ON DELETE CASCADE ON UPDATE CASCADE"#,
64///         r#")"#,
65///     ].join(" ")
66/// );
67/// assert_eq!(
68///     table.to_string(SqliteQueryBuilder),
69///     [
70///        r#"CREATE TABLE IF NOT EXISTS "character" ("#,
71///            r#""id" integer NOT NULL PRIMARY KEY AUTOINCREMENT,"#,
72///            r#""font_size" integer NOT NULL,"#,
73///            r#""character" varchar NOT NULL,"#,
74///            r#""size_w" integer NOT NULL,"#,
75///            r#""size_h" integer NOT NULL,"#,
76///            r#""font_id" integer DEFAULT NULL,"#,
77///            r#"FOREIGN KEY ("font_id") REFERENCES "font" ("id") ON DELETE CASCADE ON UPDATE CASCADE"#,
78///        r#")"#,
79///     ].join(" ")
80/// );
81/// ```
82#[derive(Default, Debug, Clone)]
83pub struct TableCreateStatement {
84    pub(crate) table: Option<TableRef>,
85    pub(crate) columns: Vec<ColumnDef>,
86    pub(crate) options: Vec<TableOpt>,
87    pub(crate) partitions: Vec<TablePartition>,
88    pub(crate) indexes: Vec<IndexCreateStatement>,
89    pub(crate) foreign_keys: Vec<ForeignKeyCreateStatement>,
90    pub(crate) if_not_exists: bool,
91    pub(crate) check: Vec<Check>,
92    pub(crate) comment: Option<String>,
93    pub(crate) extra: Option<String>,
94    pub(crate) temporary: bool,
95}
96
97/// All available table options
98#[derive(Debug, Clone)]
99#[non_exhaustive]
100pub enum TableOpt {
101    Engine(String),
102    Collate(String),
103    CharacterSet(String),
104}
105
106/// All available table partition options
107#[derive(Debug, Clone)]
108#[non_exhaustive]
109pub enum TablePartition {}
110
111impl TableCreateStatement {
112    /// Construct create table statement
113    pub fn new() -> Self {
114        Self::default()
115    }
116
117    /// Create table if table not exists
118    pub fn if_not_exists(&mut self) -> &mut Self {
119        self.if_not_exists = true;
120        self
121    }
122
123    /// Set table name
124    pub fn table<T>(&mut self, table: T) -> &mut Self
125    where
126        T: IntoTableRef,
127    {
128        self.table = Some(table.into_table_ref());
129        self
130    }
131
132    /// Set table comment
133    pub fn comment<T>(&mut self, comment: T) -> &mut Self
134    where
135        T: Into<String>,
136    {
137        self.comment = Some(comment.into());
138        self
139    }
140
141    /// Add a new table column
142    pub fn col<C: IntoColumnDef>(&mut self, column: C) -> &mut Self {
143        let mut column = column.into_column_def();
144        column.table.clone_from(&self.table);
145        self.columns.push(column);
146        self
147    }
148
149    pub fn check<T>(&mut self, value: T) -> &mut Self
150    where
151        T: Into<Check>,
152    {
153        self.check.push(value.into());
154        self
155    }
156
157    /// Add an index. MySQL only.
158    ///
159    /// # Examples
160    ///
161    /// ```
162    /// use sea_query::{tests_cfg::*, *};
163    ///
164    /// assert_eq!(
165    ///     Table::create()
166    ///         .table(Glyph::Table)
167    ///         .col(ColumnDef::new(Glyph::Id).integer().not_null())
168    ///         .index(Index::create().unique().name("idx-glyph-id").col(Glyph::Id))
169    ///         .to_string(MysqlQueryBuilder),
170    ///     [
171    ///         "CREATE TABLE `glyph` (",
172    ///         "`id` int NOT NULL,",
173    ///         "UNIQUE KEY `idx-glyph-id` (`id`)",
174    ///         ")",
175    ///     ]
176    ///     .join(" ")
177    /// );
178    /// ```
179    pub fn index(&mut self, index: &mut IndexCreateStatement) -> &mut Self {
180        self.indexes.push(index.take());
181        self
182    }
183
184    /// Add an primary key.
185    ///
186    /// # Examples
187    ///
188    /// ```
189    /// use sea_query::{tests_cfg::*, *};
190    ///
191    /// let mut statement = Table::create();
192    /// statement
193    ///     .table(Glyph::Table)
194    ///     .col(ColumnDef::new(Glyph::Id).integer().not_null())
195    ///     .col(ColumnDef::new(Glyph::Image).string().not_null())
196    ///     .primary_key(Index::create().col(Glyph::Id).col(Glyph::Image));
197    /// assert_eq!(
198    ///     statement.to_string(MysqlQueryBuilder),
199    ///     [
200    ///         "CREATE TABLE `glyph` (",
201    ///         "`id` int NOT NULL,",
202    ///         "`image` varchar(255) NOT NULL,",
203    ///         "PRIMARY KEY (`id`, `image`)",
204    ///         ")",
205    ///     ]
206    ///     .join(" ")
207    /// );
208    /// assert_eq!(
209    ///     statement.to_string(PostgresQueryBuilder),
210    ///     [
211    ///         "CREATE TABLE \"glyph\" (",
212    ///         "\"id\" integer NOT NULL,",
213    ///         "\"image\" varchar NOT NULL,",
214    ///         "PRIMARY KEY (\"id\", \"image\")",
215    ///         ")",
216    ///     ]
217    ///     .join(" ")
218    /// );
219    /// assert_eq!(
220    ///     statement.to_string(SqliteQueryBuilder),
221    ///     [
222    ///         r#"CREATE TABLE "glyph" ("#,
223    ///         r#""id" integer NOT NULL,"#,
224    ///         r#""image" varchar NOT NULL,"#,
225    ///         r#"PRIMARY KEY ("id", "image")"#,
226    ///         r#")"#,
227    ///     ]
228    ///     .join(" ")
229    /// );
230    /// ```
231    pub fn primary_key(&mut self, index: &mut IndexCreateStatement) -> &mut Self {
232        let mut index = index.take();
233        index.primary = true;
234        self.indexes.push(index);
235        self
236    }
237
238    /// Add a foreign key
239    pub fn foreign_key(&mut self, foreign_key: &mut ForeignKeyCreateStatement) -> &mut Self {
240        self.foreign_keys.push(foreign_key.take());
241        self
242    }
243
244    /// Set database engine. MySQL only.
245    pub fn engine<T>(&mut self, string: T) -> &mut Self
246    where
247        T: Into<String>,
248    {
249        self.opt(TableOpt::Engine(string.into()));
250        self
251    }
252
253    /// Set database collate. MySQL only.
254    pub fn collate<T>(&mut self, string: T) -> &mut Self
255    where
256        T: Into<String>,
257    {
258        self.opt(TableOpt::Collate(string.into()));
259        self
260    }
261
262    /// Set database character set. MySQL only.
263    pub fn character_set<T>(&mut self, name: T) -> &mut Self
264    where
265        T: Into<String>,
266    {
267        self.opt(TableOpt::CharacterSet(name.into()));
268        self
269    }
270
271    fn opt(&mut self, option: TableOpt) -> &mut Self {
272        self.options.push(option);
273        self
274    }
275
276    #[allow(dead_code)]
277    fn partition(&mut self, partition: TablePartition) -> &mut Self {
278        self.partitions.push(partition);
279        self
280    }
281
282    pub fn get_table_name(&self) -> Option<&TableRef> {
283        self.table.as_ref()
284    }
285
286    pub fn get_columns(&self) -> &Vec<ColumnDef> {
287        self.columns.as_ref()
288    }
289
290    pub fn get_comment(&self) -> Option<&String> {
291        self.comment.as_ref()
292    }
293
294    pub fn get_foreign_key_create_stmts(&self) -> &Vec<ForeignKeyCreateStatement> {
295        self.foreign_keys.as_ref()
296    }
297
298    pub fn get_indexes(&self) -> &Vec<IndexCreateStatement> {
299        self.indexes.as_ref()
300    }
301
302    /// Rewriting extra param. You should take care self about concat extra params. Add extra after options.
303    /// Example for PostgresSQL [Citus](https://github.com/citusdata/citus) extension:
304    /// ```
305    /// use sea_query::{tests_cfg::*, *};
306    /// let table = Table::create()
307    ///     .table(Char::Table)
308    ///     .col(
309    ///         ColumnDef::new(Char::Id)
310    ///             .uuid()
311    ///             .extra("DEFAULT uuid_generate_v4()")
312    ///             .primary_key()
313    ///             .not_null(),
314    ///     )
315    ///     .col(
316    ///         ColumnDef::new(Char::CreatedAt)
317    ///             .timestamp_with_time_zone()
318    ///             .extra("DEFAULT NOW()")
319    ///             .not_null(),
320    ///     )
321    ///     .col(ColumnDef::new(Char::UserData).json_binary().not_null())
322    ///     .extra("USING columnar")
323    ///     .take();
324    /// assert_eq!(
325    ///     table.to_string(PostgresQueryBuilder),
326    ///     [
327    ///         r#"CREATE TABLE "character" ("#,
328    ///         r#""id" uuid DEFAULT uuid_generate_v4() PRIMARY KEY NOT NULL,"#,
329    ///         r#""created_at" timestamp with time zone DEFAULT NOW() NOT NULL,"#,
330    ///         r#""user_data" jsonb NOT NULL"#,
331    ///         r#") USING columnar"#,
332    ///     ]
333    ///     .join(" ")
334    /// );
335    /// ```
336    pub fn extra<T>(&mut self, extra: T) -> &mut Self
337    where
338        T: Into<String>,
339    {
340        self.extra = Some(extra.into());
341        self
342    }
343
344    pub fn get_extra(&self) -> Option<&String> {
345        self.extra.as_ref()
346    }
347
348    /// Create temporary table
349    ///
350    /// Ref:
351    /// - PostgreSQL: https://www.postgresql.org/docs/17/sql-createtable.html#SQL-CREATETABLE-TEMPORARY
352    /// - MySQL: https://dev.mysql.com/doc/refman/9.2/en/create-temporary-table.html
353    /// - MariaDB: https://mariadb.com/kb/en/create-table/#create-temporary-table
354    /// - SQLite: https://sqlite.org/lang_createtable.html
355    ///
356    /// # Examples
357    ///
358    /// ```
359    /// use sea_query::{tests_cfg::*, *};
360    ///
361    /// let statement = Table::create()
362    ///     .table(Font::Table)
363    ///     .temporary()
364    ///     .col(
365    ///         ColumnDef::new(Font::Id)
366    ///             .integer()
367    ///             .not_null()
368    ///             .primary_key()
369    ///             .auto_increment(),
370    ///     )
371    ///     .col(ColumnDef::new(Font::Name).string().not_null())
372    ///     .take();
373    ///
374    /// assert_eq!(
375    ///     statement.to_string(MysqlQueryBuilder),
376    ///     [
377    ///         "CREATE TEMPORARY TABLE `font` (",
378    ///         "`id` int NOT NULL PRIMARY KEY AUTO_INCREMENT,",
379    ///         "`name` varchar(255) NOT NULL",
380    ///         ")",
381    ///     ]
382    ///     .join(" ")
383    /// );
384    /// assert_eq!(
385    ///     statement.to_string(PostgresQueryBuilder),
386    ///     [
387    ///         r#"CREATE TEMPORARY TABLE "font" ("#,
388    ///         r#""id" integer GENERATED BY DEFAULT AS IDENTITY NOT NULL PRIMARY KEY,"#,
389    ///         r#""name" varchar NOT NULL"#,
390    ///         r#")"#,
391    ///     ]
392    ///     .join(" ")
393    /// );
394    /// assert_eq!(
395    ///     statement.to_string(SqliteQueryBuilder),
396    ///     [
397    ///         r#"CREATE TEMPORARY TABLE "font" ("#,
398    ///         r#""id" integer NOT NULL PRIMARY KEY AUTOINCREMENT,"#,
399    ///         r#""name" varchar NOT NULL"#,
400    ///         r#")"#,
401    ///     ]
402    ///     .join(" ")
403    /// );
404    /// ```
405    pub fn temporary(&mut self) -> &mut Self {
406        self.temporary = true;
407        self
408    }
409
410    pub fn take(&mut self) -> Self {
411        Self {
412            table: self.table.take(),
413            columns: std::mem::take(&mut self.columns),
414            options: std::mem::take(&mut self.options),
415            partitions: std::mem::take(&mut self.partitions),
416            indexes: std::mem::take(&mut self.indexes),
417            foreign_keys: std::mem::take(&mut self.foreign_keys),
418            if_not_exists: self.if_not_exists,
419            check: std::mem::take(&mut self.check),
420            comment: std::mem::take(&mut self.comment),
421            extra: std::mem::take(&mut self.extra),
422            temporary: self.temporary,
423        }
424    }
425}
426
427#[inherent]
428impl SchemaStatementBuilder for TableCreateStatement {
429    pub fn build<T: SchemaBuilder>(&self, schema_builder: T) -> String {
430        let mut sql = String::with_capacity(256);
431        schema_builder.prepare_table_create_statement(self, &mut sql);
432        sql
433    }
434
435    pub fn build_any(&self, schema_builder: &dyn SchemaBuilder) -> String {
436        let mut sql = String::with_capacity(256);
437        schema_builder.prepare_table_create_statement(self, &mut sql);
438        sql
439    }
440
441    pub fn to_string<T: SchemaBuilder>(&self, schema_builder: T) -> String;
442}