1use sea_query::{
14 ColumnDef as SeaColumnDef, ColumnType, MysqlQueryBuilder, PostgresQueryBuilder,
15 SqliteQueryBuilder, Table as SeaTable,
16};
17
18use crate::pool::Driver;
19
20pub struct Schema {
21 pub statements: Vec<String>,
22 driver: Driver,
23}
24
25impl Default for Schema {
26 fn default() -> Self {
27 Self::new()
28 }
29}
30
31impl Schema {
32 pub fn new() -> Self {
33 Self::for_driver(Driver::Postgres)
34 }
35
36 pub fn for_driver(driver: Driver) -> Self {
37 Self {
38 statements: Vec::new(),
39 driver,
40 }
41 }
42
43 pub fn driver(&self) -> Driver {
44 self.driver
45 }
46
47 pub fn create<F>(&mut self, table: &str, build: F)
49 where
50 F: FnOnce(&mut Table),
51 {
52 let mut t = Table::new(table, TableMode::Create, self.driver);
53 build(&mut t);
54 self.statements.extend(t.into_statements());
55 }
56
57 pub fn table<F>(&mut self, table: &str, build: F)
62 where
63 F: FnOnce(&mut Table),
64 {
65 let mut t = Table::new(table, TableMode::Alter, self.driver);
66 build(&mut t);
67 self.statements.extend(t.into_statements());
68 }
69
70 pub fn drop(&mut self, table: &str) {
71 let sql = match self.driver {
72 Driver::Postgres => format!("DROP TABLE IF EXISTS {} CASCADE", table),
73 Driver::MySql | Driver::Sqlite => format!("DROP TABLE IF EXISTS {}", table),
74 };
75 self.statements.push(sql);
76 }
77
78 pub fn drop_if_exists(&mut self, table: &str) {
79 self.drop(table);
80 }
81
82 pub fn rename(&mut self, from: &str, to: &str) {
84 self.statements
85 .push(format!("ALTER TABLE {from} RENAME TO {to}"));
86 }
87
88 pub fn has_table(&mut self, _table: &str) {
90 }
92
93 pub fn raw(&mut self, sql: impl Into<String>) {
94 self.statements.push(sql.into());
95 }
96}
97
98#[derive(Clone, Copy, PartialEq, Eq)]
99enum TableMode {
100 Create,
101 Alter,
102}
103
104pub struct Table {
106 name: String,
107 mode: TableMode,
108 driver: Driver,
109 columns: Vec<ColumnDef>,
110 indexes: Vec<String>,
111 foreign_keys: Vec<String>,
112 drops: Vec<String>,
113 renames: Vec<(String, String)>,
114 alters: Vec<String>,
115}
116
117impl Table {
118 fn new(name: impl Into<String>, mode: TableMode, driver: Driver) -> Self {
119 Self {
120 name: name.into(),
121 mode,
122 driver,
123 columns: Vec::new(),
124 indexes: Vec::new(),
125 foreign_keys: Vec::new(),
126 drops: Vec::new(),
127 renames: Vec::new(),
128 alters: Vec::new(),
129 }
130 }
131
132 fn push_column(&mut self, name: &str, ty: ColumnType) -> &mut ColumnDef {
133 let sea_def = SeaColumnDef::new_with_type(sea_query::Alias::new(name), ty);
134 self.columns.push(ColumnDef {
135 sea_def,
136 name: name.to_string(),
137 mode: self.mode,
138 });
139 self.columns.last_mut().unwrap()
140 }
141
142 pub fn id(&mut self) -> &mut ColumnDef {
145 let cd = self.push_column("id", ColumnType::BigInteger);
146 cd.sea_def.not_null().primary_key().auto_increment();
147 cd
148 }
149
150 pub fn uuid_id(&mut self) -> &mut ColumnDef {
151 let cd = self.push_column("id", ColumnType::Uuid);
152 cd.sea_def.not_null().primary_key();
153 cd
154 }
155
156 pub fn ulid_id(&mut self) -> &mut ColumnDef {
158 self.uuid_id()
159 }
160
161 pub fn tiny_integer(&mut self, name: &str) -> &mut ColumnDef {
164 self.push_column(name, ColumnType::TinyInteger)
165 }
166
167 pub fn small_integer(&mut self, name: &str) -> &mut ColumnDef {
168 self.push_column(name, ColumnType::SmallInteger)
169 }
170
171 pub fn medium_integer(&mut self, name: &str) -> &mut ColumnDef {
172 self.push_column(name, ColumnType::Integer)
173 }
174
175 pub fn integer(&mut self, name: &str) -> &mut ColumnDef {
176 self.push_column(name, ColumnType::Integer)
177 }
178
179 pub fn big_integer(&mut self, name: &str) -> &mut ColumnDef {
180 self.push_column(name, ColumnType::BigInteger)
181 }
182
183 pub fn unsigned_big_integer(&mut self, name: &str) -> &mut ColumnDef {
186 let check = format!(
187 "ALTER TABLE {} ADD CONSTRAINT {}_{}_unsigned CHECK ({} >= 0)",
188 self.name, self.name, name, name
189 );
190 self.alters.push(check);
191 self.push_column(name, ColumnType::BigInteger)
192 }
193
194 pub fn unsigned_integer(&mut self, name: &str) -> &mut ColumnDef {
195 let check = format!(
196 "ALTER TABLE {} ADD CONSTRAINT {}_{}_unsigned CHECK ({} >= 0)",
197 self.name, self.name, name, name
198 );
199 self.alters.push(check);
200 self.push_column(name, ColumnType::Integer)
201 }
202
203 pub fn decimal(&mut self, name: &str, precision: u32, scale: u32) -> &mut ColumnDef {
204 self.push_column(name, ColumnType::Decimal(Some((precision, scale))))
205 }
206
207 pub fn float(&mut self, name: &str) -> &mut ColumnDef {
208 self.push_column(name, ColumnType::Float)
209 }
210
211 pub fn double(&mut self, name: &str) -> &mut ColumnDef {
212 self.push_column(name, ColumnType::Double)
213 }
214
215 pub fn string(&mut self, name: &str) -> &mut ColumnDef {
218 self.push_column(name, ColumnType::String(sea_query::StringLen::N(255)))
219 }
220
221 pub fn string_with_length(&mut self, name: &str, length: u32) -> &mut ColumnDef {
223 self.push_column(name, ColumnType::String(sea_query::StringLen::N(length)))
224 }
225
226 pub fn text(&mut self, name: &str) -> &mut ColumnDef {
227 self.push_column(name, ColumnType::Text)
228 }
229
230 pub fn long_text(&mut self, name: &str) -> &mut ColumnDef {
231 self.push_column(name, ColumnType::Text)
232 }
233
234 pub fn medium_text(&mut self, name: &str) -> &mut ColumnDef {
235 self.push_column(name, ColumnType::Text)
236 }
237
238 pub fn char(&mut self, name: &str, length: u32) -> &mut ColumnDef {
239 self.push_column(name, ColumnType::Char(Some(length)))
240 }
241
242 pub fn remember_token(&mut self) -> &mut ColumnDef {
244 let cd = self.push_column(
245 "remember_token",
246 ColumnType::String(sea_query::StringLen::N(100)),
247 );
248 cd.sea_def.null();
249 cd
250 }
251
252 pub fn enum_col(&mut self, name: &str, variants: &[&str]) -> &mut ColumnDef {
257 let list = variants
258 .iter()
259 .map(|v| format!("'{}'", v.replace('\'', "''")))
260 .collect::<Vec<_>>()
261 .join(", ");
262 let check = format!(
263 "ALTER TABLE {} ADD CONSTRAINT {}_{}_enum CHECK ({} IN ({}))",
264 self.name, self.name, name, name, list
265 );
266 self.alters.push(check);
267 self.push_column(name, ColumnType::String(sea_query::StringLen::N(64)))
268 }
269
270 pub fn binary(&mut self, name: &str) -> &mut ColumnDef {
271 self.push_column(name, ColumnType::VarBinary(sea_query::StringLen::None))
272 }
273
274 pub fn boolean(&mut self, name: &str) -> &mut ColumnDef {
277 self.push_column(name, ColumnType::Boolean)
278 }
279
280 pub fn timestamp(&mut self, name: &str) -> &mut ColumnDef {
283 self.push_column(name, ColumnType::Timestamp)
284 }
285
286 pub fn timestamp_tz(&mut self, name: &str) -> &mut ColumnDef {
287 self.push_column(name, ColumnType::TimestampWithTimeZone)
288 }
289
290 pub fn date(&mut self, name: &str) -> &mut ColumnDef {
291 self.push_column(name, ColumnType::Date)
292 }
293
294 pub fn time(&mut self, name: &str) -> &mut ColumnDef {
295 self.push_column(name, ColumnType::Time)
296 }
297
298 pub fn date_time(&mut self, name: &str) -> &mut ColumnDef {
299 self.push_column(name, ColumnType::DateTime)
300 }
301
302 pub fn year(&mut self, name: &str) -> &mut ColumnDef {
303 self.push_column(name, ColumnType::Year)
304 }
305
306 pub fn timestamps(&mut self) {
308 self.push_column("created_at", ColumnType::TimestampWithTimeZone)
309 .nullable()
310 .default("CURRENT_TIMESTAMP");
311 self.push_column("updated_at", ColumnType::TimestampWithTimeZone)
312 .nullable()
313 .default("CURRENT_TIMESTAMP");
314 }
315
316 pub fn soft_deletes(&mut self) {
317 self.push_column("deleted_at", ColumnType::TimestampWithTimeZone)
318 .nullable();
319 }
320
321 pub fn json(&mut self, name: &str) -> &mut ColumnDef {
324 self.push_column(name, ColumnType::Json)
325 }
326
327 pub fn jsonb(&mut self, name: &str) -> &mut ColumnDef {
328 self.push_column(name, ColumnType::JsonBinary)
329 }
330
331 pub fn uuid(&mut self, name: &str) -> &mut ColumnDef {
332 self.push_column(name, ColumnType::Uuid)
333 }
334
335 pub fn ip_address(&mut self, name: &str) -> &mut ColumnDef {
336 self.push_column(name, ColumnType::String(sea_query::StringLen::N(45)))
338 }
339
340 pub fn mac_address(&mut self, name: &str) -> &mut ColumnDef {
341 self.push_column(name, ColumnType::String(sea_query::StringLen::N(17)))
342 }
343
344 pub fn morphs(&mut self, name: &str) {
349 self.push_column(&format!("{name}_id"), ColumnType::BigInteger)
350 .not_null();
351 self.push_column(
352 &format!("{name}_type"),
353 ColumnType::String(sea_query::StringLen::N(255)),
354 )
355 .not_null();
356 let idx_name = format!("idx_{}_{}_type_id", self.name, name);
357 let sql = format!(
358 "CREATE INDEX {} ON {} ({}_type, {}_id)",
359 idx_name, self.name, name, name
360 );
361 self.indexes.push(sql);
362 }
363
364 pub fn nullable_morphs(&mut self, name: &str) {
365 self.push_column(&format!("{name}_id"), ColumnType::BigInteger)
366 .nullable();
367 self.push_column(
368 &format!("{name}_type"),
369 ColumnType::String(sea_query::StringLen::N(255)),
370 )
371 .nullable();
372 let idx_name = format!("idx_{}_{}_type_id", self.name, name);
373 let sql = format!(
374 "CREATE INDEX {} ON {} ({}_type, {}_id)",
375 idx_name, self.name, name, name
376 );
377 self.indexes.push(sql);
378 }
379
380 pub fn uuid_morphs(&mut self, name: &str) {
382 self.push_column(&format!("{name}_id"), ColumnType::Uuid)
383 .not_null();
384 self.push_column(
385 &format!("{name}_type"),
386 ColumnType::String(sea_query::StringLen::N(255)),
387 )
388 .not_null();
389 let idx_name = format!("idx_{}_{}_type_id", self.name, name);
390 let sql = format!(
391 "CREATE INDEX {} ON {} ({}_type, {}_id)",
392 idx_name, self.name, name, name
393 );
394 self.indexes.push(sql);
395 }
396
397 pub fn foreign_id_for(&mut self, name: &str, references: &str) -> &mut ColumnDef {
405 let fk_sql = format!(
406 "ALTER TABLE {} ADD CONSTRAINT fk_{}_{} FOREIGN KEY ({}) REFERENCES {} (id) ON DELETE CASCADE",
407 self.name, self.name, name, name, references
408 );
409 self.foreign_keys.push(fk_sql);
410 self.push_column(name, ColumnType::BigInteger)
411 }
412
413 pub fn foreign(&mut self, column: &str) -> ForeignKeyBuilder<'_> {
416 ForeignKeyBuilder {
417 table: &mut self.foreign_keys,
418 table_name: self.name.clone(),
419 column: column.to_string(),
420 ref_col: "id".to_string(),
421 ref_table: String::new(),
422 on_delete: None,
423 on_update: None,
424 }
425 }
426
427 pub fn index(&mut self, columns: &[&str]) -> &mut Self {
430 let idx_name = format!("idx_{}_{}", self.name, columns.join("_"));
431 let sql = format!(
432 "CREATE INDEX {} ON {} ({})",
433 idx_name,
434 self.name,
435 columns.join(", ")
436 );
437 self.indexes.push(sql);
438 self
439 }
440
441 pub fn unique_index(&mut self, columns: &[&str]) -> &mut Self {
442 let idx_name = format!("uq_{}_{}", self.name, columns.join("_"));
443 let sql = format!(
444 "CREATE UNIQUE INDEX {} ON {} ({})",
445 idx_name,
446 self.name,
447 columns.join(", ")
448 );
449 self.indexes.push(sql);
450 self
451 }
452
453 pub fn raw_index(&mut self, sql: impl Into<String>) -> &mut Self {
455 self.indexes.push(sql.into());
456 self
457 }
458
459 pub fn drop_column(&mut self, name: &str) -> &mut Self {
462 self.drops.push(format!(
463 "ALTER TABLE {} DROP COLUMN IF EXISTS {}",
464 self.name, name
465 ));
466 self
467 }
468
469 pub fn rename_column(&mut self, from: &str, to: &str) -> &mut Self {
470 self.renames.push((from.to_string(), to.to_string()));
471 self
472 }
473
474 pub fn drop_index(&mut self, name: &str) -> &mut Self {
475 self.drops.push(format!("DROP INDEX IF EXISTS {}", name));
476 self
477 }
478
479 pub fn drop_foreign(&mut self, constraint: &str) -> &mut Self {
480 self.drops.push(format!(
481 "ALTER TABLE {} DROP CONSTRAINT IF EXISTS {}",
482 self.name, constraint
483 ));
484 self
485 }
486
487 pub fn drop_unique(&mut self, name: &str) -> &mut Self {
488 self.drops.push(format!("DROP INDEX IF EXISTS {}", name));
489 self
490 }
491
492 pub fn drop_timestamps(&mut self) -> &mut Self {
493 self.drop_column("created_at").drop_column("updated_at")
494 }
495
496 pub fn drop_soft_deletes(&mut self) -> &mut Self {
497 self.drop_column("deleted_at")
498 }
499
500 fn into_statements(self) -> Vec<String> {
503 let mut out = Vec::new();
504 match self.mode {
505 TableMode::Create => {
506 let mut t = SeaTable::create();
507 t.table(sea_query::Alias::new(&self.name)).if_not_exists();
508 for col in &self.columns {
509 t.col(col.sea_def.clone());
510 }
511 out.push(build_per_driver(&t, self.driver));
512 }
513 TableMode::Alter => {
514 for col in &self.columns {
516 let mut t = SeaTable::alter();
517 t.table(sea_query::Alias::new(&self.name));
518 t.add_column(col.sea_def.clone());
519 out.push(build_alter_per_driver(&t, self.driver));
520 }
521 }
522 }
523 for (from, to) in &self.renames {
524 out.push(format!(
525 "ALTER TABLE {} RENAME COLUMN {} TO {}",
526 self.name, from, to
527 ));
528 }
529 out.extend(self.drops);
530 out.extend(self.indexes);
531 out.extend(self.foreign_keys);
532 out.extend(self.alters);
533 out
534 }
535}
536
537pub struct ForeignKeyBuilder<'a> {
540 table: &'a mut Vec<String>,
541 table_name: String,
542 column: String,
543 ref_col: String,
544 ref_table: String,
545 on_delete: Option<String>,
546 on_update: Option<String>,
547}
548
549impl<'a> ForeignKeyBuilder<'a> {
550 pub fn references(mut self, column: &str) -> Self {
552 self.ref_col = column.to_string();
553 self
554 }
555
556 pub fn on(mut self, table: &str) -> Self {
558 self.ref_table = table.to_string();
559 self
560 }
561
562 pub fn on_delete(mut self, action: &str) -> Self {
564 self.on_delete = Some(action.to_string());
565 self
566 }
567
568 pub fn on_update(mut self, action: &str) -> Self {
569 self.on_update = Some(action.to_string());
570 self
571 }
572
573 pub fn cascade(self) -> Self {
574 self.on_delete("CASCADE")
575 }
576
577 pub fn set_null(self) -> Self {
578 self.on_delete("SET NULL")
579 }
580
581 pub fn restrict(self) -> Self {
582 self.on_delete("RESTRICT")
583 }
584
585 pub fn commit(self) {
587 drop(self);
589 }
590}
591
592impl<'a> Drop for ForeignKeyBuilder<'a> {
593 fn drop(&mut self) {
594 if self.ref_table.is_empty() {
595 return;
597 }
598 let constraint = format!("fk_{}_{}", self.table_name, self.column);
599 let mut sql = format!(
600 "ALTER TABLE {} ADD CONSTRAINT {} FOREIGN KEY ({}) REFERENCES {} ({})",
601 self.table_name, constraint, self.column, self.ref_table, self.ref_col
602 );
603 if let Some(action) = &self.on_delete {
604 sql.push_str(&format!(" ON DELETE {action}"));
605 }
606 if let Some(action) = &self.on_update {
607 sql.push_str(&format!(" ON UPDATE {action}"));
608 }
609 self.table.push(sql);
610 }
611}
612
613pub struct ColumnDef {
614 sea_def: SeaColumnDef,
615 pub name: String,
616 #[allow(dead_code)]
617 mode: TableMode,
618}
619
620impl ColumnDef {
621 pub fn not_null(&mut self) -> &mut Self {
622 self.sea_def.not_null();
623 self
624 }
625
626 pub fn nullable(&mut self) -> &mut Self {
627 self.sea_def.null();
628 self
629 }
630
631 pub fn unique(&mut self) -> &mut Self {
632 self.sea_def.unique_key();
633 self
634 }
635
636 pub fn primary_key(&mut self) -> &mut Self {
637 self.sea_def.primary_key();
638 self
639 }
640
641 pub fn default(&mut self, value: impl Into<String>) -> &mut Self {
642 self.sea_def.default(sea_query::Expr::cust(value.into()));
643 self
644 }
645
646 pub fn default_value<T>(&mut self, value: T) -> &mut Self
647 where
648 T: Into<sea_query::Value>,
649 {
650 self.sea_def.default(value);
651 self
652 }
653
654 pub fn comment(&mut self, _text: impl Into<String>) -> &mut Self {
656 self
659 }
660
661 pub fn use_current(&mut self) -> &mut Self {
663 self.default("CURRENT_TIMESTAMP")
664 }
665}
666
667fn build_per_driver(t: &sea_query::TableCreateStatement, driver: Driver) -> String {
670 match driver {
671 Driver::Postgres => t.build(PostgresQueryBuilder),
672 Driver::MySql => t.build(MysqlQueryBuilder),
673 Driver::Sqlite => t.build(SqliteQueryBuilder),
674 }
675}
676
677fn build_alter_per_driver(t: &sea_query::TableAlterStatement, driver: Driver) -> String {
678 match driver {
679 Driver::Postgres => t.build(PostgresQueryBuilder),
680 Driver::MySql => t.build(MysqlQueryBuilder),
681 Driver::Sqlite => t.build(SqliteQueryBuilder),
682 }
683}