sea_query/table/
create.rs

1use inherent::inherent;
2
3use crate::{
4    backend::SchemaBuilder, foreign_key::*, index::*, types::*, ColumnDef, IntoColumnDef,
5    SchemaStatementBuilder, SimpleExpr,
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" serial 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<SimpleExpr>,
92    pub(crate) comment: Option<String>,
93    pub(crate) extra: Option<String>,
94}
95
96/// All available table options
97#[derive(Debug, Clone)]
98pub enum TableOpt {
99    Engine(String),
100    Collate(String),
101    CharacterSet(String),
102}
103
104/// All available table partition options
105#[derive(Debug, Clone)]
106pub enum TablePartition {}
107
108impl TableCreateStatement {
109    /// Construct create table statement
110    pub fn new() -> Self {
111        Self::default()
112    }
113
114    /// Create table if table not exists
115    pub fn if_not_exists(&mut self) -> &mut Self {
116        self.if_not_exists = true;
117        self
118    }
119
120    /// Set table name
121    pub fn table<T>(&mut self, table: T) -> &mut Self
122    where
123        T: IntoTableRef,
124    {
125        self.table = Some(table.into_table_ref());
126        self
127    }
128
129    /// Set table comment
130    pub fn comment<T>(&mut self, comment: T) -> &mut Self
131    where
132        T: Into<String>,
133    {
134        self.comment = Some(comment.into());
135        self
136    }
137
138    /// Add a new table column
139    pub fn col<C: IntoColumnDef>(&mut self, column: C) -> &mut Self {
140        let mut column = column.into_column_def();
141        column.table.clone_from(&self.table);
142        self.columns.push(column);
143        self
144    }
145
146    pub fn check(&mut self, value: SimpleExpr) -> &mut Self {
147        self.check.push(value);
148        self
149    }
150
151    /// Add an index. MySQL only.
152    ///
153    /// # Examples
154    ///
155    /// ```
156    /// use sea_query::{tests_cfg::*, *};
157    ///
158    /// assert_eq!(
159    ///     Table::create()
160    ///         .table(Glyph::Table)
161    ///         .col(ColumnDef::new(Glyph::Id).integer().not_null())
162    ///         .index(Index::create().unique().name("idx-glyph-id").col(Glyph::Id))
163    ///         .to_string(MysqlQueryBuilder),
164    ///     [
165    ///         "CREATE TABLE `glyph` (",
166    ///         "`id` int NOT NULL,",
167    ///         "UNIQUE KEY `idx-glyph-id` (`id`)",
168    ///         ")",
169    ///     ]
170    ///     .join(" ")
171    /// );
172    /// ```
173    pub fn index(&mut self, index: &mut IndexCreateStatement) -> &mut Self {
174        self.indexes.push(index.take());
175        self
176    }
177
178    /// Add an primary key.
179    ///
180    /// # Examples
181    ///
182    /// ```
183    /// use sea_query::{tests_cfg::*, *};
184    ///
185    /// let mut statement = Table::create();
186    /// statement
187    ///     .table(Glyph::Table)
188    ///     .col(ColumnDef::new(Glyph::Id).integer().not_null())
189    ///     .col(ColumnDef::new(Glyph::Image).string().not_null())
190    ///     .primary_key(Index::create().col(Glyph::Id).col(Glyph::Image));
191    /// assert_eq!(
192    ///     statement.to_string(MysqlQueryBuilder),
193    ///     [
194    ///         "CREATE TABLE `glyph` (",
195    ///         "`id` int NOT NULL,",
196    ///         "`image` varchar(255) NOT NULL,",
197    ///         "PRIMARY KEY (`id`, `image`)",
198    ///         ")",
199    ///     ]
200    ///     .join(" ")
201    /// );
202    /// assert_eq!(
203    ///     statement.to_string(PostgresQueryBuilder),
204    ///     [
205    ///         "CREATE TABLE \"glyph\" (",
206    ///         "\"id\" integer NOT NULL,",
207    ///         "\"image\" varchar NOT NULL,",
208    ///         "PRIMARY KEY (\"id\", \"image\")",
209    ///         ")",
210    ///     ]
211    ///     .join(" ")
212    /// );
213    /// assert_eq!(
214    ///     statement.to_string(SqliteQueryBuilder),
215    ///     [
216    ///         r#"CREATE TABLE "glyph" ("#,
217    ///         r#""id" integer NOT NULL,"#,
218    ///         r#""image" varchar NOT NULL,"#,
219    ///         r#"PRIMARY KEY ("id", "image")"#,
220    ///         r#")"#,
221    ///     ]
222    ///     .join(" ")
223    /// );
224    /// ```
225    pub fn primary_key(&mut self, index: &mut IndexCreateStatement) -> &mut Self {
226        let mut index = index.take();
227        index.primary = true;
228        self.indexes.push(index);
229        self
230    }
231
232    /// Add a foreign key
233    pub fn foreign_key(&mut self, foreign_key: &mut ForeignKeyCreateStatement) -> &mut Self {
234        self.foreign_keys.push(foreign_key.take());
235        self
236    }
237
238    /// Set database engine. MySQL only.
239    pub fn engine<T>(&mut self, string: T) -> &mut Self
240    where
241        T: Into<String>,
242    {
243        self.opt(TableOpt::Engine(string.into()));
244        self
245    }
246
247    /// Set database collate. MySQL only.
248    pub fn collate<T>(&mut self, string: T) -> &mut Self
249    where
250        T: Into<String>,
251    {
252        self.opt(TableOpt::Collate(string.into()));
253        self
254    }
255
256    /// Set database character set. MySQL only.
257    pub fn character_set<T>(&mut self, name: T) -> &mut Self
258    where
259        T: Into<String>,
260    {
261        self.opt(TableOpt::CharacterSet(name.into()));
262        self
263    }
264
265    fn opt(&mut self, option: TableOpt) -> &mut Self {
266        self.options.push(option);
267        self
268    }
269
270    #[allow(dead_code)]
271    fn partition(&mut self, partition: TablePartition) -> &mut Self {
272        self.partitions.push(partition);
273        self
274    }
275
276    pub fn get_table_name(&self) -> Option<&TableRef> {
277        self.table.as_ref()
278    }
279
280    pub fn get_columns(&self) -> &Vec<ColumnDef> {
281        self.columns.as_ref()
282    }
283
284    pub fn get_comment(&self) -> Option<&String> {
285        self.comment.as_ref()
286    }
287
288    pub fn get_foreign_key_create_stmts(&self) -> &Vec<ForeignKeyCreateStatement> {
289        self.foreign_keys.as_ref()
290    }
291
292    pub fn get_indexes(&self) -> &Vec<IndexCreateStatement> {
293        self.indexes.as_ref()
294    }
295
296    /// Rewriting extra param. You should take care self about concat extra params. Add extra after options.
297    /// Example for PostgresSQL [Citus](https://github.com/citusdata/citus) extension:
298    /// ```
299    /// use sea_query::{tests_cfg::*, *};
300    /// let table = Table::create()
301    ///     .table(Char::Table)
302    ///     .col(
303    ///         ColumnDef::new(Char::Id)
304    ///             .uuid()
305    ///             .extra("DEFAULT uuid_generate_v4()")
306    ///             .primary_key()
307    ///             .not_null(),
308    ///     )
309    ///     .col(
310    ///         ColumnDef::new(Char::CreatedAt)
311    ///             .timestamp_with_time_zone()
312    ///             .extra("DEFAULT NOW()")
313    ///             .not_null(),
314    ///     )
315    ///     .col(ColumnDef::new(Char::UserData).json_binary().not_null())
316    ///     .extra("USING columnar")
317    ///     .to_owned();
318    /// assert_eq!(
319    ///     table.to_string(PostgresQueryBuilder),
320    ///     [
321    ///         r#"CREATE TABLE "character" ("#,
322    ///         r#""id" uuid DEFAULT uuid_generate_v4() PRIMARY KEY NOT NULL,"#,
323    ///         r#""created_at" timestamp with time zone DEFAULT NOW() NOT NULL,"#,
324    ///         r#""user_data" jsonb NOT NULL"#,
325    ///         r#") USING columnar"#,
326    ///     ]
327    ///     .join(" ")
328    /// );
329    /// ```
330    pub fn extra<T>(&mut self, extra: T) -> &mut Self
331    where
332        T: Into<String>,
333    {
334        self.extra = Some(extra.into());
335        self
336    }
337
338    pub fn get_extra(&self) -> Option<&String> {
339        self.extra.as_ref()
340    }
341
342    pub fn take(&mut self) -> Self {
343        Self {
344            table: self.table.take(),
345            columns: std::mem::take(&mut self.columns),
346            options: std::mem::take(&mut self.options),
347            partitions: std::mem::take(&mut self.partitions),
348            indexes: std::mem::take(&mut self.indexes),
349            foreign_keys: std::mem::take(&mut self.foreign_keys),
350            if_not_exists: self.if_not_exists,
351            check: std::mem::take(&mut self.check),
352            comment: std::mem::take(&mut self.comment),
353            extra: std::mem::take(&mut self.extra),
354        }
355    }
356}
357
358#[inherent]
359impl SchemaStatementBuilder for TableCreateStatement {
360    pub fn build<T: SchemaBuilder>(&self, schema_builder: T) -> String {
361        let mut sql = String::with_capacity(256);
362        schema_builder.prepare_table_create_statement(self, &mut sql);
363        sql
364    }
365
366    pub fn build_any(&self, schema_builder: &dyn SchemaBuilder) -> String {
367        let mut sql = String::with_capacity(256);
368        schema_builder.prepare_table_create_statement(self, &mut sql);
369        sql
370    }
371
372    pub fn to_string<T: SchemaBuilder>(&self, schema_builder: T) -> String;
373}