rustorm/
table.rs

1use crate::{types::SqlType, ColumnDef, ColumnName, TableName};
2
3#[derive(Debug, PartialEq, Clone)]
4pub struct TableDef {
5    pub name: TableName,
6
7    /// comment of this table
8    pub comment: Option<String>,
9
10    /// columns of this table
11    pub columns: Vec<ColumnDef>,
12
13    /// views can also be generated
14    pub is_view: bool,
15
16    pub table_key: Vec<TableKey>,
17}
18
19impl TableDef {
20    pub fn complete_name(&self) -> String {
21        self.name.complete_name()
22    }
23
24    pub fn safe_name(&self) -> String {
25        self.name.safe_name()
26    }
27
28    pub fn safe_complete_name(&self) -> String {
29        self.name.safe_complete_name()
30    }
31
32    pub fn get_primary_column_names(&self) -> Vec<&ColumnName> {
33        let mut primary: Vec<&ColumnName> = vec![];
34        for key in &self.table_key {
35            if let TableKey::PrimaryKey(ref pk) = key {
36                for col in &pk.columns {
37                    primary.push(col)
38                }
39            }
40        }
41        primary.sort_by(|a, b| a.name.cmp(&b.name));
42        primary
43    }
44
45    pub fn get_non_primary_columns(&self) -> Vec<&ColumnDef> {
46        let primary = self.get_primary_columns();
47        self.columns
48            .iter()
49            .filter(|c| !primary.contains(c))
50            .collect()
51    }
52
53    pub fn get_primary_columns(&self) -> Vec<&ColumnDef> {
54        self.get_primary_column_names()
55            .iter()
56            .filter_map(|column_name| self.get_column(column_name))
57            .collect()
58    }
59
60    pub fn is_primary_column(&self, column: &ColumnDef) -> bool {
61        self.get_primary_columns().contains(&column)
62    }
63
64    pub fn get_primary_column_types(&self) -> Vec<&SqlType> {
65        self.get_primary_columns()
66            .iter()
67            .map(|column| &column.specification.sql_type)
68            .collect()
69    }
70
71    /// return the foreignkyes of this table
72    pub fn get_foreign_keys(&self) -> Vec<&ForeignKey> {
73        let mut foreign: Vec<&ForeignKey> = vec![];
74        for key in &self.table_key {
75            if let TableKey::ForeignKey(ref fk) = key {
76                foreign.push(fk)
77            }
78        }
79        foreign
80    }
81
82    /// return the table names which is foreign to this table
83    pub fn get_foreign_tables(&self) -> Vec<&TableName> {
84        self.get_foreign_keys()
85            .iter()
86            .map(|foreign| &foreign.foreign_table)
87            .collect()
88    }
89
90    pub fn get_foreign_key_to_table(&self, table_name: &TableName) -> Option<&ForeignKey> {
91        let foreign_keys: Vec<&ForeignKey> = self.get_foreign_keys();
92        for fk in foreign_keys {
93            if fk.foreign_table == *table_name {
94                return Some(fk);
95            }
96        }
97        None
98    }
99
100    /// get the (local_columns, foreign_columns) to the table
101    pub fn get_local_foreign_columns_pair_to_table(
102        &self,
103        table_name: &TableName,
104    ) -> Vec<(&ColumnName, &ColumnName)> {
105        let foreign_keys: Vec<&ForeignKey> = self.get_foreign_keys();
106        for fk in foreign_keys {
107            if fk.foreign_table == *table_name {
108                let mut container = vec![];
109                for (local_column, referred_column) in
110                    fk.columns.iter().zip(fk.referred_columns.iter())
111                {
112                    container.push((local_column, referred_column));
113                }
114                return container;
115            }
116        }
117        vec![]
118    }
119
120    fn get_foreign_columns_to_table(&self, table_name: &TableName) -> Vec<&ColumnDef> {
121        self.get_foreign_column_names_to_table(table_name)
122            .iter()
123            .filter_map(|column_name| self.get_column(column_name))
124            .collect()
125    }
126
127    pub fn get_foreign_column_types_to_table(&self, table_name: &TableName) -> Vec<&SqlType> {
128        self.get_foreign_columns_to_table(table_name)
129            .iter()
130            .map(|column| &column.specification.sql_type)
131            .collect()
132    }
133
134    pub fn get_foreign_column_names_to_table(&self, table_name: &TableName) -> Vec<&ColumnName> {
135        let mut foreign_columns = vec![];
136        let foreign_keys = self.get_foreign_key_to_table(table_name);
137        for fk in &foreign_keys {
138            for fk_column in &fk.columns {
139                foreign_columns.push(fk_column);
140            }
141        }
142        foreign_columns
143    }
144
145    /// get the column names of this table
146    pub fn get_foreign_column_names(&self) -> Vec<&ColumnName> {
147        let mut foreign_columns = vec![];
148        let foreign_keys = self.get_foreign_keys();
149        for fk in &foreign_keys {
150            for fk_column in &fk.columns {
151                foreign_columns.push(fk_column);
152            }
153        }
154        foreign_columns
155    }
156
157    /// return the local columns of this table
158    /// that is referred by the argument table name
159    pub fn get_referred_columns_to_table(
160        &self,
161        table_name: &TableName,
162    ) -> Option<&Vec<ColumnName>> {
163        let foreign_keys: Vec<&ForeignKey> = self.get_foreign_keys();
164        for fk in foreign_keys {
165            if fk.foreign_table == *table_name {
166                return Some(&fk.referred_columns);
167            }
168        }
169        None
170    }
171
172    /// find the column which matches this `column_name`
173    pub fn get_column(&self, column_name: &ColumnName) -> Option<&ColumnDef> {
174        self.columns.iter().find(|c| c.name == *column_name)
175    }
176}
177
178/// example:
179///     category { id, name }
180///     product { product_id, name, category_id }
181///
182/// if the table in context is product and the foreign table is category
183/// ForeignKey{
184///     name: product_category_fkey
185///     columns: _category_id_
186///     foreign_table: category
187///     referred_columns: _id_
188/// }
189#[derive(Debug, PartialEq, Clone)]
190pub struct ForeignKey {
191    pub name: Option<String>,
192    // the local columns of this table local column = foreign_column
193    pub columns: Vec<ColumnName>,
194    // referred foreign table
195    pub foreign_table: TableName,
196    // referred column of the foreign table
197    // this is most likely the primary key of the table in context
198    pub referred_columns: Vec<ColumnName>,
199}
200
201#[derive(Debug, PartialEq, Clone)]
202pub struct Key {
203    pub name: Option<String>,
204    pub columns: Vec<ColumnName>,
205}
206
207#[derive(Debug, PartialEq, Clone)]
208pub enum TableKey {
209    PrimaryKey(Key),
210    UniqueKey(Key),
211    Key(Key),
212    ForeignKey(ForeignKey),
213}
214
215#[derive(Debug)]
216pub struct SchemaContent {
217    pub schema: String,
218    pub tablenames: Vec<TableName>,
219    pub views: Vec<TableName>,
220}
221
222#[cfg(test)]
223#[cfg(feature = "with-postgres")]
224mod test {
225    use crate::{table::*, *};
226    use log::*;
227
228    #[test]
229    fn film_table_info() {
230        let db_url = "postgres://postgres:p0stgr3s@localhost:5432/sakila";
231        let mut pool = Pool::new();
232        let mut em = pool.em(db_url).expect("must be ok");
233        let table = em
234            .get_table(&TableName::from("public.film"))
235            .expect("must have a table");
236        println!("table: {:#?}", table);
237    }
238
239    #[test]
240    fn test_foreign_tables() {
241        let db_url = "postgres://postgres:p0stgr3s@localhost:5432/sakila";
242        let mut pool = Pool::new();
243        let mut em = pool.em(db_url).expect("must be ok");
244        let table = em
245            .get_table(&TableName::from("public.film_actor"))
246            .expect("must be ok")
247            .expect("must have a table");
248
249        println!("table: {:#?}", table);
250        let foreign_tables = table.get_foreign_tables();
251        println!("foreign_tables: {:#?}", foreign_tables);
252        assert_eq!(
253            foreign_tables,
254            vec![
255                &TableName::from("public.actor"),
256                &TableName::from("public.film"),
257            ]
258        );
259    }
260
261    #[test]
262    fn referred_columns() {
263        let db_url = "postgres://postgres:p0stgr3s@localhost:5432/sakila";
264        let mut pool = Pool::new();
265        let em = pool.em(db_url);
266        let mut db = pool.db(db_url).unwrap();
267        assert!(em.is_ok());
268        let film_tablename = TableName::from("public.film");
269        let film = db
270            .get_table(&film_tablename)
271            .expect("must be ok")
272            .expect("must have a table");
273        let film_actor_tablename = TableName::from("public.film_actor");
274        let film_actor = db
275            .get_table(&film_actor_tablename)
276            .expect("must be ok")
277            .expect("must have a table");
278        let rc = film_actor.get_referred_columns_to_table(&film.name);
279        info!("rc: {:#?}", rc);
280        assert_eq!(
281            rc,
282            Some(&vec![ColumnName {
283                name: "film_id".to_string(),
284                table: None,
285                alias: None,
286            }])
287        );
288    }
289
290    #[test]
291    fn referred_columns_hero_id() {
292        let db_url = "postgres://postgres:p0stgr3s@localhost:5432/dota";
293        let mut pool = Pool::new();
294        let mut em = pool.em(db_url).expect("must be ok");
295        let hero_tablename = TableName::from("public.hero");
296        let hero = em
297            .get_table(&hero_tablename)
298            .expect("must be ok")
299            .expect("must have a table");
300
301        let hero_ability_tablename = TableName::from("public.hero_ability");
302        let hero_ability = em
303            .get_table(&hero_ability_tablename)
304            .expect("must be ok")
305            .expect("must have a table");
306
307        info!("hero {:#?}", hero);
308        info!("hero ability {:#?}", hero_ability);
309        let rc = hero_ability.get_referred_columns_to_table(&hero.name);
310        info!("rc: {:#?}", rc);
311        assert_eq!(
312            rc,
313            Some(&vec![ColumnName {
314                name: "id".to_string(),
315                table: None,
316                alias: None,
317            }])
318        );
319        let foreign_key = hero_ability.get_foreign_key_to_table(&hero.name);
320        info!("foreign_key: {:#?}", foreign_key);
321        assert_eq!(
322            foreign_key,
323            Some(&ForeignKey {
324                name: Some("hero_id_fkey".to_string()),
325                columns: vec![ColumnName {
326                    name: "hero_id".to_string(),
327                    table: None,
328                    alias: None,
329                }],
330                foreign_table: TableName {
331                    name: "hero".to_string(),
332                    schema: Some("public".to_string()),
333                    alias: None,
334                },
335                referred_columns: vec![ColumnName {
336                    name: "id".to_string(),
337                    table: None,
338                    alias: None,
339                }],
340            })
341        );
342    }
343}