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}