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}