Skip to main content

schema_sql_generator/common/
relation_generator.rs

1use schema_model::model::database_model::DatabaseModel;
2use schema_model::model::relation::Relation;
3use schema_model::model::table::Table;
4use schema_model::model::types::RelationType;
5use crate::common::generator_context::GeneratorContext;
6use crate::common::sql_writer::SqlWriter;
7
8const FK_PREFIX: &str = "fk_";
9
10pub trait RelationGenerator {
11    fn output_relations(&self);
12}
13
14pub struct DefaultRelationGenerator {
15    context: GeneratorContext,
16}
17
18impl DefaultRelationGenerator {
19    pub fn new(context: GeneratorContext) -> Self {
20        Self {
21            context,
22        }
23    }
24
25    pub fn context(&self) -> &GeneratorContext {
26        &self.context
27    }
28
29    fn output_relation_for_table(&self, writer: &mut SqlWriter, table: &Table) {
30        let database_type = self.context.settings().database_type();
31        let database_model = self.context.settings().database_model();
32        let max_key_name_length = database_type.max_key_name_length();
33        let table_name = table.name();
34        let relations = table.relations();
35
36        for (relation_index, relation) in relations.iter().enumerate() {
37            let mut relation_name = format!("{}{}{}", FK_PREFIX, table_name, relation_index + 1);
38
39            if relation_name.len() > max_key_name_length {
40                let truncated_table_name_len = max_key_name_length - FK_PREFIX.len() - 1; // leave space for the index
41                let truncated_table_name = &table_name[..truncated_table_name_len.min(table_name.len())];
42                relation_name = format!("{}{}{}", FK_PREFIX, truncated_table_name, relation_index + 1);
43            }
44
45            self.output_relation(writer, &relation_name.to_lowercase(), database_model, table, relation);
46        }
47    }
48
49    fn output_relation(&self,
50                       writer: &mut SqlWriter,
51                       relation_name: &str,
52                       database_model: &DatabaseModel,
53                       table: &Table,
54                       relation: &Relation) {
55        let operation = self.relation_operation_type(relation.relation_type());
56        let to_table = self.find_table(relation.to_table_name(), database_model);
57
58        writer.print(format!("alter table {}", table.fully_qualified_table_name()).as_str());
59        writer.print(" add constraint ");
60        writer.print(relation_name);
61        writer.print(" foreign key (");
62        writer.print(relation.from_column_name());
63        writer.print(") references ");
64        writer.print(to_table.fully_qualified_table_name().as_str());
65        writer.print("(");
66        writer.print(relation.to_column_name());
67        writer.print(") on delete ");
68        writer.print(operation);
69        writer.println(self.context().settings().statement_separator());
70    }
71
72    fn relation_operation_type(&self, relation_type: RelationType) -> &str {
73        match relation_type {
74            RelationType::Cascade => {"cascade"}
75            RelationType::Enforce => {"no action"}
76            RelationType::SetNull => {"set null"}
77            RelationType::DoNothing => {"no action"}
78        }
79    }
80
81    fn find_table<'a>(&self, to_table_name: &str, database_model: &'a DatabaseModel) -> &'a Table {
82        let parts: Vec<&str> = to_table_name.split('.').collect();
83        let (schema, table_name) = if parts.len() == 2 {
84            (Some(parts[0]), parts[1])
85        } else {
86            (None, to_table_name)
87        };
88
89        database_model.find_table(schema, table_name)
90    }
91}
92
93impl RelationGenerator for DefaultRelationGenerator {
94    fn output_relations(&self) {
95        let database_model = self.context.settings().database_model();
96        let has_relations = database_model.all_tables().iter().any(|table| {!table.relations().is_empty()});
97
98        if has_relations {
99            self.context.with_writer(|writer| {
100                writer.println("/* relations */");
101
102                database_model.all_tables().iter().filter(|table| {
103                    !table.relations().is_empty()
104                }).for_each(|table| {
105                    self.output_relation_for_table(writer, table);
106                });
107
108                writer.newline();
109            });
110        }
111    }
112}