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