1use crate::{types::SqlType, ColumnDef, ColumnName, TableName};
2
3#[derive(Debug, PartialEq, Clone)]
4pub struct TableDef {
5 pub name: TableName,
6
7 pub comment: Option<String>,
9
10 pub columns: Vec<ColumnDef>,
12
13 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 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 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 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 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 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 pub fn get_column(&self, column_name: &ColumnName) -> Option<&ColumnDef> {
174 self.columns.iter().find(|c| c.name == *column_name)
175 }
176}
177
178#[derive(Debug, PartialEq, Clone)]
190pub struct ForeignKey {
191 pub name: Option<String>,
192 pub columns: Vec<ColumnName>,
194 pub foreign_table: TableName,
196 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}