1use crate::alloc_prelude::*;
7
8use super::{
9 CheckConstraint, Column, Enum, ForeignKey, Generated, GeneratedType, Identity, IdentityType,
10 Index, IndexColumnDef, Policy, PrimaryKey, Sequence, Table, UniqueConstraint, View,
11};
12
13#[derive(Clone, Debug)]
19pub struct TableSql<'a> {
20 pub table: &'a Table,
21 pub columns: &'a [Column],
22 pub primary_key: Option<&'a PrimaryKey>,
23 pub foreign_keys: &'a [ForeignKey],
24 pub unique_constraints: &'a [UniqueConstraint],
25 pub check_constraints: &'a [CheckConstraint],
26 pub indexes: &'a [Index],
27 pub policies: &'a [Policy],
28}
29
30impl<'a> TableSql<'a> {
31 pub fn new(table: &'a Table) -> Self {
33 Self {
34 table,
35 columns: &[],
36 primary_key: None,
37 foreign_keys: &[],
38 unique_constraints: &[],
39 check_constraints: &[],
40 indexes: &[],
41 policies: &[],
42 }
43 }
44
45 pub fn columns(mut self, columns: &'a [Column]) -> Self {
47 self.columns = columns;
48 self
49 }
50
51 pub fn primary_key(mut self, pk: Option<&'a PrimaryKey>) -> Self {
53 self.primary_key = pk;
54 self
55 }
56
57 pub fn foreign_keys(mut self, fks: &'a [ForeignKey]) -> Self {
59 self.foreign_keys = fks;
60 self
61 }
62
63 pub fn unique_constraints(mut self, uniques: &'a [UniqueConstraint]) -> Self {
65 self.unique_constraints = uniques;
66 self
67 }
68
69 pub fn check_constraints(mut self, checks: &'a [CheckConstraint]) -> Self {
71 self.check_constraints = checks;
72 self
73 }
74
75 pub fn indexes(mut self, indexes: &'a [Index]) -> Self {
77 self.indexes = indexes;
78 self
79 }
80
81 pub fn policies(mut self, policies: &'a [Policy]) -> Self {
83 self.policies = policies;
84 self
85 }
86
87 fn schema_prefix(&self) -> String {
88 if self.table.schema() != "public" {
89 format!("\"{}\".", self.table.schema())
90 } else {
91 String::new()
92 }
93 }
94
95 pub fn create_table_sql(&self) -> String {
97 let schema_prefix = self.schema_prefix();
98 let mut sql = format!(
99 "CREATE TABLE {}\"{}\" (\n",
100 schema_prefix,
101 self.table.name()
102 );
103
104 let mut lines = Vec::new();
105
106 for column in self.columns {
108 lines.push(format!("\t{}", column.to_column_sql()));
109 }
110
111 if let Some(pk) = &self.primary_key {
113 let cols = pk
114 .columns
115 .iter()
116 .map(|c| format!("\"{}\"", c))
117 .collect::<Vec<_>>()
118 .join(", ");
119 if pk.name_explicit {
120 lines.push(format!(
121 "\tCONSTRAINT \"{}\" PRIMARY KEY({})",
122 pk.name(),
123 cols
124 ));
125 } else {
126 lines.push(format!("\tPRIMARY KEY({})", cols));
127 }
128 }
129
130 for fk in self.foreign_keys {
132 lines.push(format!("\t{}", fk.to_constraint_sql()));
133 }
134
135 for unique in self.unique_constraints {
137 let cols = unique
138 .columns
139 .iter()
140 .map(|c| format!("\"{}\"", c))
141 .collect::<Vec<_>>()
142 .join(", ");
143 lines.push(format!(
144 "\tCONSTRAINT \"{}\" UNIQUE({})",
145 unique.name(),
146 cols
147 ));
148 }
149
150 for check in self.check_constraints {
152 lines.push(format!(
153 "\tCONSTRAINT \"{}\" CHECK ({})",
154 check.name(),
155 &check.value
156 ));
157 }
158
159 sql.push_str(&lines.join(",\n"));
160 sql.push_str("\n);");
161
162 sql
163 }
164
165 pub fn drop_table_sql(&self) -> String {
167 let schema_prefix = self.schema_prefix();
168 format!("DROP TABLE {}\"{}\";", schema_prefix, self.table.name())
169 }
170
171 pub fn create_indexes_sql(&self) -> Vec<String> {
173 self.indexes.iter().map(|i| i.create_index_sql()).collect()
174 }
175
176 pub fn enable_rls_sql(&self) -> Option<String> {
178 if self.table.is_rls_enabled.unwrap_or(false) {
179 let schema_prefix = self.schema_prefix();
180 Some(format!(
181 "ALTER TABLE {}\"{}\" ENABLE ROW LEVEL SECURITY;",
182 schema_prefix,
183 self.table.name()
184 ))
185 } else {
186 None
187 }
188 }
189
190 pub fn create_policies_sql(&self) -> Vec<String> {
192 self.policies
193 .iter()
194 .map(|p| p.create_policy_sql())
195 .collect()
196 }
197}
198
199impl Column {
204 pub fn to_column_sql(&self) -> String {
206 let mut sql = format!("\"{}\" {}", self.name(), self.sql_type());
207
208 if let Some(identity) = &self.identity {
210 sql.push_str(&identity.to_sql());
211 }
212
213 if let Some(generated) = &self.generated {
215 sql.push_str(&generated.to_sql());
216 }
217
218 if self.identity.is_none()
220 && self.generated.is_none()
221 && let Some(default) = self.default.as_ref()
222 {
223 sql.push_str(&format!(" DEFAULT {}", default));
224 }
225
226 if self.not_null {
228 sql.push_str(" NOT NULL");
229 }
230
231 sql
232 }
233
234 pub fn add_column_sql(&self) -> String {
236 let schema_prefix = if self.schema() != "public" {
237 format!("\"{}\".", self.schema())
238 } else {
239 String::new()
240 };
241 format!(
242 "ALTER TABLE {}\"{}\" ADD COLUMN {};",
243 schema_prefix,
244 self.table(),
245 self.to_column_sql()
246 )
247 }
248
249 pub fn drop_column_sql(&self) -> String {
251 let schema_prefix = if self.schema() != "public" {
252 format!("\"{}\".", self.schema())
253 } else {
254 String::new()
255 };
256 format!(
257 "ALTER TABLE {}\"{}\" DROP COLUMN \"{}\";",
258 schema_prefix,
259 self.table(),
260 self.name()
261 )
262 }
263}
264
265impl Identity {
270 pub fn to_sql(&self) -> String {
272 let identity_type = match self.type_ {
273 IdentityType::Always => "ALWAYS",
274 IdentityType::ByDefault => "BY DEFAULT",
275 };
276
277 let mut sql = format!(" GENERATED {} AS IDENTITY", identity_type);
278
279 let mut options = Vec::new();
281
282 if let Some(increment) = self.increment.as_ref() {
283 options.push(format!("INCREMENT BY {}", increment));
284 }
285 if let Some(min) = self.min_value.as_ref() {
286 options.push(format!("MINVALUE {}", min));
287 }
288 if let Some(max) = self.max_value.as_ref() {
289 options.push(format!("MAXVALUE {}", max));
290 }
291 if let Some(start) = self.start_with.as_ref() {
292 options.push(format!("START WITH {}", start));
293 }
294 if let Some(cache) = self.cache {
295 options.push(format!("CACHE {}", cache));
296 }
297 if self.cycle.unwrap_or(false) {
298 options.push("CYCLE".to_string());
299 }
300
301 if !options.is_empty() {
302 sql.push_str(&format!(" ({})", options.join(" ")));
303 }
304
305 sql
306 }
307}
308
309impl Generated {
314 pub fn to_sql(&self) -> String {
316 let gen_type = match self.gen_type {
317 GeneratedType::Stored => "STORED",
318 };
319 format!(" GENERATED ALWAYS AS ({}) {}", self.expression, gen_type)
320 }
321}
322
323impl ForeignKey {
328 pub fn to_constraint_sql(&self) -> String {
330 let from_cols = self
331 .columns
332 .iter()
333 .map(|c| format!("\"{}\"", c))
334 .collect::<Vec<_>>()
335 .join(", ");
336
337 let to_cols = self
338 .columns_to
339 .iter()
340 .map(|c| format!("\"{}\"", c))
341 .collect::<Vec<_>>()
342 .join(", ");
343
344 let to_schema_prefix = if self.schema_to() != "public" {
345 format!("\"{}\".", self.schema_to())
346 } else {
347 String::new()
348 };
349
350 let mut sql = format!(
351 "CONSTRAINT \"{}\" FOREIGN KEY ({}) REFERENCES {}\"{}\"({})",
352 self.name(),
353 from_cols,
354 to_schema_prefix,
355 self.table_to(),
356 to_cols
357 );
358
359 if let Some(on_update) = self.on_update.as_ref()
360 && on_update != "NO ACTION"
361 {
362 sql.push_str(&format!(" ON UPDATE {}", on_update.to_uppercase()));
363 }
364
365 if let Some(on_delete) = self.on_delete.as_ref()
366 && on_delete != "NO ACTION"
367 {
368 sql.push_str(&format!(" ON DELETE {}", on_delete.to_uppercase()));
369 }
370
371 sql
372 }
373
374 pub fn add_fk_sql(&self) -> String {
376 let schema_prefix = if self.schema() != "public" {
377 format!("\"{}\".", self.schema())
378 } else {
379 String::new()
380 };
381 format!(
382 "ALTER TABLE {}\"{}\" ADD {};",
383 schema_prefix,
384 self.table(),
385 self.to_constraint_sql()
386 )
387 }
388
389 pub fn drop_fk_sql(&self) -> String {
391 let schema_prefix = if self.schema() != "public" {
392 format!("\"{}\".", self.schema())
393 } else {
394 String::new()
395 };
396 format!(
397 "ALTER TABLE {}\"{}\" DROP CONSTRAINT \"{}\";",
398 schema_prefix,
399 self.table(),
400 self.name()
401 )
402 }
403}
404
405impl Index {
410 pub fn create_index_sql(&self) -> String {
412 let unique = if self.is_unique { "UNIQUE " } else { "" };
413
414 let concurrently = if self.concurrently {
415 "CONCURRENTLY "
416 } else {
417 ""
418 };
419
420 let schema_prefix = if self.schema() != "public" {
421 format!("\"{}\".", self.schema())
422 } else {
423 String::new()
424 };
425
426 let columns = self
427 .columns
428 .iter()
429 .map(|c| c.to_sql())
430 .collect::<Vec<_>>()
431 .join(", ");
432
433 let using = self
434 .method
435 .as_ref()
436 .map(|m| format!(" USING {}", m))
437 .unwrap_or_default();
438
439 let mut sql = format!(
440 "CREATE {}{}INDEX \"{}\" ON {}\"{}\"{}({});",
441 unique,
442 concurrently,
443 self.name(),
444 schema_prefix,
445 self.table(),
446 using,
447 columns
448 );
449
450 if let Some(where_clause) = self.where_clause.as_ref() {
451 sql.pop();
453 sql.push_str(&format!(" WHERE {};", where_clause));
454 }
455
456 sql
457 }
458
459 pub fn drop_index_sql(&self) -> String {
461 let schema_prefix = if self.schema() != "public" {
462 format!("\"{}\".", self.schema())
463 } else {
464 String::new()
465 };
466 format!("DROP INDEX {}\"{}\";", schema_prefix, self.name())
467 }
468}
469
470impl IndexColumnDef {
471 pub fn to_sql(&self) -> String {
473 let mut sql = if self.is_expression {
474 format!("({})", self.value)
475 } else {
476 format!("\"{}\"", self.value)
477 };
478
479 if let Some(op) = self.opclass.as_ref() {
480 sql.push_str(&format!(" {}", op));
481 }
482
483 if !self.asc {
484 sql.push_str(" DESC");
485 }
486
487 if self.nulls_first {
488 sql.push_str(" NULLS FIRST");
489 }
490
491 sql
492 }
493}
494
495impl Enum {
500 pub fn create_enum_sql(&self) -> String {
502 let schema_prefix = if self.schema() != "public" {
503 format!("\"{}\".", self.schema())
504 } else {
505 String::new()
506 };
507 let values = self
508 .values
509 .iter()
510 .map(|v| format!("'{}'", v))
511 .collect::<Vec<_>>()
512 .join(", ");
513 format!(
514 "CREATE TYPE {}\"{}\" AS ENUM ({});",
515 schema_prefix,
516 self.name(),
517 values
518 )
519 }
520
521 pub fn drop_enum_sql(&self) -> String {
523 let schema_prefix = if self.schema() != "public" {
524 format!("\"{}\".", self.schema())
525 } else {
526 String::new()
527 };
528 format!("DROP TYPE {}\"{}\";", schema_prefix, self.name())
529 }
530
531 pub fn add_value_sql(&self, value: &str, before: Option<&str>) -> String {
533 let schema_prefix = if self.schema() != "public" {
534 format!("\"{}\".", self.schema())
535 } else {
536 String::new()
537 };
538 if let Some(before_value) = before {
539 format!(
540 "ALTER TYPE {}\"{}\" ADD VALUE '{}' BEFORE '{}';",
541 schema_prefix,
542 self.name(),
543 value,
544 before_value
545 )
546 } else {
547 format!(
548 "ALTER TYPE {}\"{}\" ADD VALUE '{}';",
549 schema_prefix,
550 self.name(),
551 value
552 )
553 }
554 }
555}
556
557impl Sequence {
562 pub fn create_sequence_sql(&self) -> String {
564 let schema_prefix = if self.schema() != "public" {
565 format!("\"{}\".", self.schema())
566 } else {
567 String::new()
568 };
569
570 let mut sql = format!("CREATE SEQUENCE {}\"{}\"", schema_prefix, self.name());
571
572 if let Some(inc) = self.increment_by.as_ref() {
573 sql.push_str(&format!(" INCREMENT BY {}", inc));
574 }
575 if let Some(min) = self.min_value.as_ref() {
576 sql.push_str(&format!(" MINVALUE {}", min));
577 }
578 if let Some(max) = self.max_value.as_ref() {
579 sql.push_str(&format!(" MAXVALUE {}", max));
580 }
581 if let Some(start) = self.start_with.as_ref() {
582 sql.push_str(&format!(" START WITH {}", start));
583 }
584 if let Some(cache) = self.cache_size {
585 sql.push_str(&format!(" CACHE {}", cache));
586 }
587 if self.cycle.unwrap_or(false) {
588 sql.push_str(" CYCLE");
589 }
590
591 sql.push(';');
592 sql
593 }
594
595 pub fn drop_sequence_sql(&self) -> String {
597 let schema_prefix = if self.schema() != "public" {
598 format!("\"{}\".", self.schema())
599 } else {
600 String::new()
601 };
602 format!("DROP SEQUENCE {}\"{}\";", schema_prefix, self.name())
603 }
604}
605
606impl View {
611 pub fn create_view_sql(&self) -> String {
613 let schema_prefix = if self.schema() != "public" {
614 format!("\"{}\".", self.schema())
615 } else {
616 String::new()
617 };
618
619 let materialized = if self.materialized {
620 "MATERIALIZED "
621 } else {
622 ""
623 };
624
625 let Some(def) = self.definition.as_ref() else {
626 return format!(
627 "-- {}View {}\"{}\" has no definition",
628 materialized,
629 schema_prefix,
630 self.name()
631 );
632 };
633
634 use core::fmt::Write;
635
636 let mut sql = String::with_capacity(def.len() + 64);
637 let _ = write!(
638 sql,
639 "CREATE {}VIEW {}\"{}\"",
640 materialized,
641 schema_prefix,
642 self.name(),
643 );
644
645 if let Some(using) = self.using.as_ref() {
646 let _ = write!(sql, " USING {}", using);
647 }
648
649 let mut check_option_clause = None;
650 if let Some(with_opts) = self.with.as_ref() {
651 let mut options = String::new();
652 let mut has_option = false;
653
654 macro_rules! push_option {
655 ($name:expr, $value:expr) => {{
656 if has_option {
657 options.push_str(", ");
658 } else {
659 has_option = true;
660 }
661 let _ = write!(options, "{} = {}", $name, $value);
662 }};
663 }
664
665 if let Some(check_option) = with_opts.check_option.as_deref() {
666 check_option_clause = Some(check_option.to_ascii_uppercase());
667 }
668
669 if let Some(value) = with_opts.security_barrier {
670 push_option!("security_barrier", value);
671 }
672 if let Some(value) = with_opts.security_invoker {
673 push_option!("security_invoker", value);
674 }
675 if let Some(value) = with_opts.fillfactor {
676 push_option!("fillfactor", value);
677 }
678 if let Some(value) = with_opts.toast_tuple_target {
679 push_option!("toast_tuple_target", value);
680 }
681 if let Some(value) = with_opts.parallel_workers {
682 push_option!("parallel_workers", value);
683 }
684 if let Some(value) = with_opts.autovacuum_enabled {
685 push_option!("autovacuum_enabled", value);
686 }
687 if let Some(value) = with_opts.vacuum_index_cleanup.as_ref() {
688 push_option!("vacuum_index_cleanup", value);
689 }
690 if let Some(value) = with_opts.vacuum_truncate {
691 push_option!("vacuum_truncate", value);
692 }
693 if let Some(value) = with_opts.autovacuum_vacuum_threshold {
694 push_option!("autovacuum_vacuum_threshold", value);
695 }
696 if let Some(value) = with_opts.autovacuum_vacuum_scale_factor {
697 push_option!("autovacuum_vacuum_scale_factor", value);
698 }
699 if let Some(value) = with_opts.autovacuum_vacuum_cost_delay {
700 push_option!("autovacuum_vacuum_cost_delay", value);
701 }
702 if let Some(value) = with_opts.autovacuum_vacuum_cost_limit {
703 push_option!("autovacuum_vacuum_cost_limit", value);
704 }
705 if let Some(value) = with_opts.autovacuum_freeze_min_age {
706 push_option!("autovacuum_freeze_min_age", value);
707 }
708 if let Some(value) = with_opts.autovacuum_freeze_max_age {
709 push_option!("autovacuum_freeze_max_age", value);
710 }
711 if let Some(value) = with_opts.autovacuum_freeze_table_age {
712 push_option!("autovacuum_freeze_table_age", value);
713 }
714 if let Some(value) = with_opts.autovacuum_multixact_freeze_min_age {
715 push_option!("autovacuum_multixact_freeze_min_age", value);
716 }
717 if let Some(value) = with_opts.autovacuum_multixact_freeze_max_age {
718 push_option!("autovacuum_multixact_freeze_max_age", value);
719 }
720 if let Some(value) = with_opts.autovacuum_multixact_freeze_table_age {
721 push_option!("autovacuum_multixact_freeze_table_age", value);
722 }
723 if let Some(value) = with_opts.log_autovacuum_min_duration {
724 push_option!("log_autovacuum_min_duration", value);
725 }
726 if let Some(value) = with_opts.user_catalog_table {
727 push_option!("user_catalog_table", value);
728 }
729
730 if has_option {
731 let _ = write!(sql, " WITH ({})", options);
732 }
733 }
734
735 if let Some(tablespace) = self.tablespace.as_ref() {
736 let _ = write!(sql, " TABLESPACE \"{}\"", tablespace);
737 }
738
739 sql.push_str(" AS ");
740 sql.push_str(def);
741
742 if let Some(check_option) = check_option_clause {
743 let _ = write!(sql, " WITH {} CHECK OPTION", check_option);
744 }
745
746 if self.materialized && matches!(self.with_no_data, Some(true)) {
747 sql.push_str(" WITH NO DATA");
748 }
749
750 sql.push(';');
751 sql
752 }
753
754 pub fn drop_view_sql(&self) -> String {
756 let schema_prefix = if self.schema() != "public" {
757 format!("\"{}\".", self.schema())
758 } else {
759 String::new()
760 };
761 let materialized = if self.materialized {
762 "MATERIALIZED "
763 } else {
764 ""
765 };
766 format!(
767 "DROP {}VIEW {}\"{}\";",
768 materialized,
769 schema_prefix,
770 self.name()
771 )
772 }
773}
774
775impl Policy {
780 pub fn create_policy_sql(&self) -> String {
782 let schema_prefix = if self.schema() != "public" {
783 format!("\"{}\".", self.schema())
784 } else {
785 String::new()
786 };
787
788 let mut sql = format!(
789 "CREATE POLICY \"{}\" ON {}\"{}\"",
790 self.name(),
791 schema_prefix,
792 self.table()
793 );
794
795 if let Some(r#for) = self.for_clause.as_ref() {
796 sql.push_str(&format!(" FOR {}", r#for.to_uppercase()));
797 }
798
799 if let Some(to) = self.to.as_ref()
800 && !to.is_empty()
801 {
802 let to_roles = to
803 .iter()
804 .map(|r| {
805 if *r == "public" {
806 "PUBLIC".to_string()
807 } else {
808 format!("\"{}\"", r)
809 }
810 })
811 .collect::<Vec<_>>()
812 .join(", ");
813 sql.push_str(&format!(" TO {}", to_roles));
814 }
815
816 if let Some(using) = self.using.as_ref() {
817 sql.push_str(&format!(" USING ({})", using));
818 }
819
820 if let Some(with_check) = self.with_check.as_ref() {
821 sql.push_str(&format!(" WITH CHECK ({})", with_check));
822 }
823
824 sql.push(';');
825 sql
826 }
827
828 pub fn drop_policy_sql(&self) -> String {
830 let schema_prefix = if self.schema() != "public" {
831 format!("\"{}\".", self.schema())
832 } else {
833 String::new()
834 };
835 format!(
836 "DROP POLICY \"{}\" ON {}\"{}\";",
837 self.name(),
838 schema_prefix,
839 self.table()
840 )
841 }
842}
843
844impl Table {
849 pub fn drop_table_sql(&self) -> String {
851 let schema_prefix = if self.schema() != "public" {
852 format!("\"{}\".", self.schema())
853 } else {
854 String::new()
855 };
856 format!("DROP TABLE {}\"{}\";", schema_prefix, self.name())
857 }
858
859 pub fn rename_table_sql(&self, new_name: &str) -> String {
861 let schema_prefix = if self.schema() != "public" {
862 format!("\"{}\".", self.schema())
863 } else {
864 String::new()
865 };
866 format!(
867 "ALTER TABLE {}\"{}\" RENAME TO \"{}\";",
868 schema_prefix,
869 self.name(),
870 new_name
871 )
872 }
873}
874
875impl PrimaryKey {
880 pub fn to_constraint_sql(&self) -> String {
882 let cols = self
883 .columns
884 .iter()
885 .map(|c| format!("\"{}\"", c))
886 .collect::<Vec<_>>()
887 .join(", ");
888
889 format!("CONSTRAINT \"{}\" PRIMARY KEY({})", self.name(), cols)
890 }
891
892 pub fn add_pk_sql(&self) -> String {
894 let schema_prefix = if self.schema() != "public" {
895 format!("\"{}\".", self.schema())
896 } else {
897 String::new()
898 };
899 format!(
900 "ALTER TABLE {}\"{}\" ADD {};",
901 schema_prefix,
902 self.table(),
903 self.to_constraint_sql()
904 )
905 }
906
907 pub fn drop_pk_sql(&self) -> String {
909 let schema_prefix = if self.schema() != "public" {
910 format!("\"{}\".", self.schema())
911 } else {
912 String::new()
913 };
914 format!(
915 "ALTER TABLE {}\"{}\" DROP CONSTRAINT \"{}\";",
916 schema_prefix,
917 self.table(),
918 self.name()
919 )
920 }
921}
922
923impl UniqueConstraint {
928 pub fn to_constraint_sql(&self) -> String {
930 let cols = self
931 .columns
932 .iter()
933 .map(|c| format!("\"{}\"", c))
934 .collect::<Vec<_>>()
935 .join(", ");
936
937 format!("CONSTRAINT \"{}\" UNIQUE({})", self.name(), cols)
938 }
939
940 pub fn add_unique_sql(&self) -> String {
942 let schema_prefix = if self.schema() != "public" {
943 format!("\"{}\".", self.schema())
944 } else {
945 String::new()
946 };
947 format!(
948 "ALTER TABLE {}\"{}\" ADD {};",
949 schema_prefix,
950 self.table(),
951 self.to_constraint_sql()
952 )
953 }
954
955 pub fn drop_unique_sql(&self) -> String {
957 let schema_prefix = if self.schema() != "public" {
958 format!("\"{}\".", self.schema())
959 } else {
960 String::new()
961 };
962 format!(
963 "ALTER TABLE {}\"{}\" DROP CONSTRAINT \"{}\";",
964 schema_prefix,
965 self.table(),
966 self.name()
967 )
968 }
969}
970
971impl CheckConstraint {
976 pub fn to_constraint_sql(&self) -> String {
978 format!("CONSTRAINT \"{}\" CHECK ({})", self.name(), &self.value)
979 }
980
981 pub fn add_check_sql(&self) -> String {
983 let schema_prefix = if self.schema() != "public" {
984 format!("\"{}\".", self.schema())
985 } else {
986 String::new()
987 };
988 format!(
989 "ALTER TABLE {}\"{}\" ADD {};",
990 schema_prefix,
991 self.table(),
992 self.to_constraint_sql()
993 )
994 }
995
996 pub fn drop_check_sql(&self) -> String {
998 let schema_prefix = if self.schema() != "public" {
999 format!("\"{}\".", self.schema())
1000 } else {
1001 String::new()
1002 };
1003 format!(
1004 "ALTER TABLE {}\"{}\" DROP CONSTRAINT \"{}\";",
1005 schema_prefix,
1006 self.table(),
1007 self.name()
1008 )
1009 }
1010}
1011
1012#[cfg(test)]
1013mod tests {
1014 use super::*;
1015 use crate::postgres::ddl::{ColumnDef, PrimaryKeyDef, TableDef};
1016 use std::borrow::Cow;
1017
1018 #[test]
1019 fn test_simple_create_table() {
1020 let table = TableDef::new("public", "users").into_table();
1021 let columns = [
1022 ColumnDef::new("public", "users", "id", "SERIAL")
1023 .not_null()
1024 .into_column(),
1025 ColumnDef::new("public", "users", "name", "TEXT")
1026 .not_null()
1027 .into_column(),
1028 ColumnDef::new("public", "users", "email", "TEXT").into_column(),
1029 ];
1030 const PK_COLS: &[Cow<'static, str>] = &[Cow::Borrowed("id")];
1031 let pk = PrimaryKeyDef::new("public", "users", "users_pkey")
1032 .columns(PK_COLS)
1033 .into_primary_key();
1034
1035 let sql = TableSql::new(&table)
1036 .columns(&columns)
1037 .primary_key(Some(&pk))
1038 .create_table_sql();
1039
1040 assert!(sql.contains("CREATE TABLE \"users\""));
1041 assert!(sql.contains("\"id\" SERIAL NOT NULL"));
1042 assert!(sql.contains("\"name\" TEXT NOT NULL"));
1043 assert!(sql.contains("\"email\" TEXT"));
1044 }
1045
1046 #[test]
1047 fn test_table_with_schema() {
1048 let table = TableDef::new("myschema", "users").into_table();
1049 let sql = TableSql::new(&table).create_table_sql();
1050 assert!(sql.contains("\"myschema\".\"users\""));
1051 }
1052}