Skip to main content

schema_guard/
column.rs

1use serde::Serialize;
2use yaml_rust::Yaml;
3
4use crate::loader::PgColumnDfn;
5use crate::utils::Named;
6
7impl Named for Column {
8    fn get_name(&self) -> String {
9        self.name.clone()
10    }
11}
12
13impl Named for Trig {
14    fn get_name(&self) -> String {
15        self.name.clone()
16    }
17}
18
19impl Default for Column {
20    fn default() -> Self {
21        Column {
22            name: "".to_string(),
23            column_type: "".to_string(),
24            default_value: None,
25            constraint: None,
26            description: "".to_string(),
27            sql: "".to_string(),
28            index: None,
29        }
30    }
31}
32
33#[derive(Debug, Clone, Serialize)]
34pub struct Column {
35    pub name: String,
36    #[serde(rename = "type")]
37    pub column_type: String,
38    #[serde(rename = "defaultValue", skip_serializing_if = "Option::is_none")]
39    pub default_value: Option<String>,
40    //
41    #[serde(skip_serializing_if = "Option::is_none")]
42    pub constraint: Option<Constr>,
43    #[serde(skip_serializing_if = "String::is_empty")]
44    pub description: String,
45    #[serde(skip_serializing_if = "String::is_empty")]
46    pub sql: String,
47    #[serde(skip_serializing_if = "Option::is_none")]
48    pub index: Option<Index>,
49}
50
51#[derive(Debug, Clone, Serialize)]
52pub struct Index {
53    #[serde(skip_serializing_if = "String::is_empty")]
54    pub name: String,
55    #[serde(skip_serializing_if = "String::is_empty")]
56    pub sql: String,
57}
58
59#[derive(Debug, Clone, Serialize)]
60pub struct Constr {
61    #[serde(rename = "primaryKey", skip_serializing_if = "Option::is_none")]
62    pub primary_key: Option<bool>,
63    pub nullable: bool,
64    #[serde(rename = "foreignKey", skip_serializing_if = "Option::is_none")]
65    pub foreign_key: Option<ForeignKey>,
66}
67
68#[derive(Debug, Clone, Serialize)]
69pub struct ForeignKey {
70    pub references: String,
71    //fk_table
72    #[serde(skip_serializing_if = "String::is_empty")]
73    pub sql: String, //-- some SQL suffix on new FK create- on delete no action on update no action
74}
75
76#[derive(Debug, Clone, Serialize)]
77pub struct Trig {
78    pub name: String,
79    //uniq_name_of_trigger
80    pub event: String,
81    // before update
82    pub when: String,
83    //  for each row
84    pub proc: String,  //  -- p()
85}
86
87impl Column {
88    pub(crate) fn new(input: &Yaml) -> Self {
89        let constraint = &input["constraint"];
90        let foreign_key = &constraint["foreignKey"];
91        let references = crate::utils::as_str_esc(foreign_key, "references");
92        let (foreign_key, fk_set) = if references.len() == 0 {
93            (None, false)
94        } else {
95            (
96                Some(ForeignKey {
97                    references,
98                    sql: crate::utils::as_str_esc(foreign_key, "sql"),
99                }),
100                true,
101            )
102        };
103        let primary_key = crate::utils::as_bool(constraint, "primaryKey", false);
104        let nullable = crate::utils::as_bool(constraint, "nullable", true);
105        let constraint = if primary_key || !nullable || fk_set {
106            Some(Constr {
107                primary_key: if primary_key { Some(true) } else { None },
108                nullable,
109                foreign_key,
110            })
111        } else {
112            None
113        };
114        let index = &input["index"];
115
116        Column {
117            name: crate::utils::safe_sql_name(crate::utils::as_str_esc(input, "name")),
118            column_type: crate::utils::as_str_esc(input, "type"),
119            default_value: input["defaultValue"].as_str().map(|s| crate::utils::as_esc(s)),
120            description: crate::utils::as_str_esc(input, "description"),
121            sql: crate::utils::as_str_esc(input, "sql"),
122            constraint,
123            index: if index.is_null() {
124                None
125            } else {
126                Some(Index::new(index))
127            },
128        }
129    }
130
131    // #[cfg(test)]
132    pub fn newt(name: &str, ctype: &str, primary_key: bool, nullable: bool) -> Self {
133        let constraint = if primary_key || !nullable {
134            Some(Constr {
135                primary_key: if primary_key { Some(true) } else { None },
136                nullable,
137                foreign_key: None,
138            })
139        } else {
140            None
141        };
142        Column {
143            name: name.to_string(),
144            column_type: ctype.to_string(),
145            default_value: None,
146            description: "".to_string(),
147            sql: "".to_string(),
148            constraint,
149            index: None,
150        }
151    }
152
153    pub fn is_pk(&self) -> bool {
154        match &self.constraint {
155            None => false,
156            Some(c) => c.primary_key.unwrap_or(false),
157        }
158    }
159
160    #[inline]
161    pub(crate) fn column_def(
162        &self,
163        schema: &String,
164        table_name: &String,
165        file: &str,
166    ) -> Result<PgColumnDfn, String> {
167        if self.column_type.len() == 0 {
168            Err(format!(
169                "Error column definition on table: {}.{} column {} file {}",
170                schema, table_name, self.name, file
171            ))
172        } else {
173            let c = self.constraint.clone();
174            Ok(PgColumnDfn {
175                column_name: self.name.clone(),
176                column_type: self.column_type.clone(),
177                column_default: self.default_value.clone(),
178                sql: Some(self.sql.trim().into()),
179                pk: c.as_ref().map_or(false, |c| c.primary_key.unwrap_or(false)),
180                nullable: c.as_ref().map_or(true, |c| c.nullable),
181                fk: c.map_or(None, |cs| cs.foreign_key
182                    .map_or(None, |fk| Some((fk.references.trim().into(), fk.sql.trim().into())))),
183                sort_order: 0,
184                column_comment: None,
185            })
186        }
187    }
188}
189
190impl Trig {
191    pub(crate) fn new(input: &Yaml) -> Self {
192        Trig {
193            name: crate::utils::safe_sql_name(crate::utils::as_str_esc(input, "name")),
194            event: crate::utils::as_str_esc(input, "event"),
195            when: crate::utils::as_str_esc(input, "when"),
196            proc: crate::utils::as_str_esc(input, "proc"),
197        }
198    }
199
200    /// column defenition to SQL string
201    #[inline]
202    pub(crate) fn trig_def(&self, schema: &String, table_name: &String) -> Option<String> {
203        // let proc = as_str_esc(input, "proc");
204        if self.proc.len() > 0 {
205            Some(format!(
206                "CREATE TRIGGER {} {} ON {}.{} {} EXECUTE PROCEDURE {};",
207                self.name, self.event, schema, table_name, self.when, self.proc
208            ))
209        } else {
210            None
211        }
212    }
213}
214
215impl Index {
216    pub(crate) fn new(input: &Yaml) -> Self {
217        Index {
218            name: crate::utils::as_str_esc(input, "name"),
219            sql: crate::utils::as_str_esc(input, "sql"),
220        }
221    }
222}