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 let mut sql = format!(
635 "CREATE {}VIEW {}\"{}\"",
636 materialized,
637 schema_prefix,
638 self.name(),
639 );
640
641 if let Some(using) = self.using.as_ref() {
642 sql.push_str(&format!(" USING {}", using));
643 }
644
645 let mut check_option_clause = None;
646 if let Some(with_opts) = self.with.as_ref() {
647 let mut options = Vec::new();
648
649 if let Some(check_option) = with_opts.check_option.as_ref() {
650 check_option_clause = Some(check_option.as_ref().to_uppercase());
651 }
652
653 if let Some(value) = with_opts.security_barrier {
654 options.push(format!("security_barrier = {}", value));
655 }
656 if let Some(value) = with_opts.security_invoker {
657 options.push(format!("security_invoker = {}", value));
658 }
659 if let Some(value) = with_opts.fillfactor {
660 options.push(format!("fillfactor = {}", value));
661 }
662 if let Some(value) = with_opts.toast_tuple_target {
663 options.push(format!("toast_tuple_target = {}", value));
664 }
665 if let Some(value) = with_opts.parallel_workers {
666 options.push(format!("parallel_workers = {}", value));
667 }
668 if let Some(value) = with_opts.autovacuum_enabled {
669 options.push(format!("autovacuum_enabled = {}", value));
670 }
671 if let Some(value) = with_opts.vacuum_index_cleanup.as_ref() {
672 options.push(format!("vacuum_index_cleanup = {}", value));
673 }
674 if let Some(value) = with_opts.vacuum_truncate {
675 options.push(format!("vacuum_truncate = {}", value));
676 }
677 if let Some(value) = with_opts.autovacuum_vacuum_threshold {
678 options.push(format!("autovacuum_vacuum_threshold = {}", value));
679 }
680 if let Some(value) = with_opts.autovacuum_vacuum_scale_factor {
681 options.push(format!("autovacuum_vacuum_scale_factor = {}", value));
682 }
683 if let Some(value) = with_opts.autovacuum_vacuum_cost_delay {
684 options.push(format!("autovacuum_vacuum_cost_delay = {}", value));
685 }
686 if let Some(value) = with_opts.autovacuum_vacuum_cost_limit {
687 options.push(format!("autovacuum_vacuum_cost_limit = {}", value));
688 }
689 if let Some(value) = with_opts.autovacuum_freeze_min_age {
690 options.push(format!("autovacuum_freeze_min_age = {}", value));
691 }
692 if let Some(value) = with_opts.autovacuum_freeze_max_age {
693 options.push(format!("autovacuum_freeze_max_age = {}", value));
694 }
695 if let Some(value) = with_opts.autovacuum_freeze_table_age {
696 options.push(format!("autovacuum_freeze_table_age = {}", value));
697 }
698 if let Some(value) = with_opts.autovacuum_multixact_freeze_min_age {
699 options.push(format!("autovacuum_multixact_freeze_min_age = {}", value));
700 }
701 if let Some(value) = with_opts.autovacuum_multixact_freeze_max_age {
702 options.push(format!("autovacuum_multixact_freeze_max_age = {}", value));
703 }
704 if let Some(value) = with_opts.autovacuum_multixact_freeze_table_age {
705 options.push(format!("autovacuum_multixact_freeze_table_age = {}", value));
706 }
707 if let Some(value) = with_opts.log_autovacuum_min_duration {
708 options.push(format!("log_autovacuum_min_duration = {}", value));
709 }
710 if let Some(value) = with_opts.user_catalog_table {
711 options.push(format!("user_catalog_table = {}", value));
712 }
713
714 if !options.is_empty() {
715 sql.push_str(&format!(" WITH ({})", options.join(", ")));
716 }
717 }
718
719 sql.push_str(" AS ");
720 sql.push_str(def);
721
722 if let Some(check_option) = check_option_clause {
723 sql.push_str(&format!(" WITH {} CHECK OPTION", check_option));
724 }
725
726 if self.materialized && matches!(self.with_no_data, Some(true)) {
727 sql.push_str(" WITH NO DATA");
728 }
729
730 if let Some(tablespace) = self.tablespace.as_ref() {
731 sql.push_str(&format!(" TABLESPACE \"{}\"", tablespace));
732 }
733
734 sql.push(';');
735 sql
736 }
737
738 pub fn drop_view_sql(&self) -> String {
740 let schema_prefix = if self.schema() != "public" {
741 format!("\"{}\".", self.schema())
742 } else {
743 String::new()
744 };
745 let materialized = if self.materialized {
746 "MATERIALIZED "
747 } else {
748 ""
749 };
750 format!(
751 "DROP {}VIEW {}\"{}\";",
752 materialized,
753 schema_prefix,
754 self.name()
755 )
756 }
757}
758
759impl Policy {
764 pub fn create_policy_sql(&self) -> String {
766 let schema_prefix = if self.schema() != "public" {
767 format!("\"{}\".", self.schema())
768 } else {
769 String::new()
770 };
771
772 let mut sql = format!(
773 "CREATE POLICY \"{}\" ON {}\"{}\"",
774 self.name(),
775 schema_prefix,
776 self.table()
777 );
778
779 if let Some(r#for) = self.for_clause.as_ref() {
780 sql.push_str(&format!(" FOR {}", r#for.to_uppercase()));
781 }
782
783 if let Some(to) = self.to.as_ref()
784 && !to.is_empty()
785 {
786 let to_roles = to
787 .iter()
788 .map(|r| {
789 if *r == "public" {
790 "PUBLIC".to_string()
791 } else {
792 format!("\"{}\"", r)
793 }
794 })
795 .collect::<Vec<_>>()
796 .join(", ");
797 sql.push_str(&format!(" TO {}", to_roles));
798 }
799
800 if let Some(using) = self.using.as_ref() {
801 sql.push_str(&format!(" USING ({})", using));
802 }
803
804 if let Some(with_check) = self.with_check.as_ref() {
805 sql.push_str(&format!(" WITH CHECK ({})", with_check));
806 }
807
808 sql.push(';');
809 sql
810 }
811
812 pub fn drop_policy_sql(&self) -> String {
814 let schema_prefix = if self.schema() != "public" {
815 format!("\"{}\".", self.schema())
816 } else {
817 String::new()
818 };
819 format!(
820 "DROP POLICY \"{}\" ON {}\"{}\";",
821 self.name(),
822 schema_prefix,
823 self.table()
824 )
825 }
826}
827
828impl Table {
833 pub fn drop_table_sql(&self) -> String {
835 let schema_prefix = if self.schema() != "public" {
836 format!("\"{}\".", self.schema())
837 } else {
838 String::new()
839 };
840 format!("DROP TABLE {}\"{}\";", schema_prefix, self.name())
841 }
842
843 pub fn rename_table_sql(&self, new_name: &str) -> String {
845 let schema_prefix = if self.schema() != "public" {
846 format!("\"{}\".", self.schema())
847 } else {
848 String::new()
849 };
850 format!(
851 "ALTER TABLE {}\"{}\" RENAME TO \"{}\";",
852 schema_prefix,
853 self.name(),
854 new_name
855 )
856 }
857}
858
859impl PrimaryKey {
864 pub fn to_constraint_sql(&self) -> String {
866 let cols = self
867 .columns
868 .iter()
869 .map(|c| format!("\"{}\"", c))
870 .collect::<Vec<_>>()
871 .join(", ");
872
873 format!("CONSTRAINT \"{}\" PRIMARY KEY({})", self.name(), cols)
874 }
875
876 pub fn add_pk_sql(&self) -> String {
878 let schema_prefix = if self.schema() != "public" {
879 format!("\"{}\".", self.schema())
880 } else {
881 String::new()
882 };
883 format!(
884 "ALTER TABLE {}\"{}\" ADD {};",
885 schema_prefix,
886 self.table(),
887 self.to_constraint_sql()
888 )
889 }
890
891 pub fn drop_pk_sql(&self) -> String {
893 let schema_prefix = if self.schema() != "public" {
894 format!("\"{}\".", self.schema())
895 } else {
896 String::new()
897 };
898 format!(
899 "ALTER TABLE {}\"{}\" DROP CONSTRAINT \"{}\";",
900 schema_prefix,
901 self.table(),
902 self.name()
903 )
904 }
905}
906
907impl UniqueConstraint {
912 pub fn to_constraint_sql(&self) -> String {
914 let cols = self
915 .columns
916 .iter()
917 .map(|c| format!("\"{}\"", c))
918 .collect::<Vec<_>>()
919 .join(", ");
920
921 format!("CONSTRAINT \"{}\" UNIQUE({})", self.name(), cols)
922 }
923
924 pub fn add_unique_sql(&self) -> String {
926 let schema_prefix = if self.schema() != "public" {
927 format!("\"{}\".", self.schema())
928 } else {
929 String::new()
930 };
931 format!(
932 "ALTER TABLE {}\"{}\" ADD {};",
933 schema_prefix,
934 self.table(),
935 self.to_constraint_sql()
936 )
937 }
938
939 pub fn drop_unique_sql(&self) -> String {
941 let schema_prefix = if self.schema() != "public" {
942 format!("\"{}\".", self.schema())
943 } else {
944 String::new()
945 };
946 format!(
947 "ALTER TABLE {}\"{}\" DROP CONSTRAINT \"{}\";",
948 schema_prefix,
949 self.table(),
950 self.name()
951 )
952 }
953}
954
955impl CheckConstraint {
960 pub fn to_constraint_sql(&self) -> String {
962 format!("CONSTRAINT \"{}\" CHECK ({})", self.name(), &self.value)
963 }
964
965 pub fn add_check_sql(&self) -> String {
967 let schema_prefix = if self.schema() != "public" {
968 format!("\"{}\".", self.schema())
969 } else {
970 String::new()
971 };
972 format!(
973 "ALTER TABLE {}\"{}\" ADD {};",
974 schema_prefix,
975 self.table(),
976 self.to_constraint_sql()
977 )
978 }
979
980 pub fn drop_check_sql(&self) -> String {
982 let schema_prefix = if self.schema() != "public" {
983 format!("\"{}\".", self.schema())
984 } else {
985 String::new()
986 };
987 format!(
988 "ALTER TABLE {}\"{}\" DROP CONSTRAINT \"{}\";",
989 schema_prefix,
990 self.table(),
991 self.name()
992 )
993 }
994}
995
996#[cfg(test)]
997mod tests {
998 use super::*;
999 use crate::postgres::ddl::{ColumnDef, PrimaryKeyDef, TableDef};
1000 use std::borrow::Cow;
1001
1002 #[test]
1003 fn test_simple_create_table() {
1004 let table = TableDef::new("public", "users").into_table();
1005 let columns = [
1006 ColumnDef::new("public", "users", "id", "SERIAL")
1007 .not_null()
1008 .into_column(),
1009 ColumnDef::new("public", "users", "name", "TEXT")
1010 .not_null()
1011 .into_column(),
1012 ColumnDef::new("public", "users", "email", "TEXT").into_column(),
1013 ];
1014 const PK_COLS: &[Cow<'static, str>] = &[Cow::Borrowed("id")];
1015 let pk = PrimaryKeyDef::new("public", "users", "users_pkey")
1016 .columns(PK_COLS)
1017 .into_primary_key();
1018
1019 let sql = TableSql::new(&table)
1020 .columns(&columns)
1021 .primary_key(Some(&pk))
1022 .create_table_sql();
1023
1024 assert!(sql.contains("CREATE TABLE \"users\""));
1025 assert!(sql.contains("\"id\" SERIAL NOT NULL"));
1026 assert!(sql.contains("\"name\" TEXT NOT NULL"));
1027 assert!(sql.contains("\"email\" TEXT"));
1028 }
1029
1030 #[test]
1031 fn test_table_with_schema() {
1032 let table = TableDef::new("myschema", "users").into_table();
1033 let sql = TableSql::new(&table).create_table_sql();
1034 assert!(sql.contains("\"myschema\".\"users\""));
1035 }
1036}