use crate::{
types::SqlType,
ColumnDef,
ColumnName,
TableName,
};
#[derive(Debug, PartialEq, Clone)]
pub struct TableDef {
pub name: TableName,
pub comment: Option<String>,
pub columns: Vec<ColumnDef>,
pub is_view: bool,
pub table_key: Vec<TableKey>,
}
impl TableDef {
pub fn complete_name(&self) -> String { self.name.complete_name() }
pub fn safe_name(&self) -> String { self.name.safe_name() }
pub fn safe_complete_name(&self) -> String { self.name.safe_complete_name() }
pub fn get_primary_column_names(&self) -> Vec<&ColumnName> {
let mut primary: Vec<&ColumnName> = vec![];
for key in &self.table_key {
if let TableKey::PrimaryKey(ref pk) = key {
for col in &pk.columns {
primary.push(col)
}
}
}
primary.sort_by(|a, b| a.name.cmp(&b.name));
primary
}
pub fn get_non_primary_columns(&self) -> Vec<&ColumnDef> {
let primary = self.get_primary_columns();
self.columns
.iter()
.filter(|c| !primary.contains(c))
.collect()
}
pub fn get_primary_columns(&self) -> Vec<&ColumnDef> {
self.get_primary_column_names()
.iter()
.filter_map(|column_name| self.get_column(column_name))
.collect()
}
pub fn is_primary_column(&self, column: &ColumnDef) -> bool {
self.get_primary_columns().contains(&column)
}
pub fn get_primary_column_types(&self) -> Vec<&SqlType> {
self.get_primary_columns()
.iter()
.map(|column| &column.specification.sql_type)
.collect()
}
pub fn get_foreign_keys(&self) -> Vec<&ForeignKey> {
let mut foreign: Vec<&ForeignKey> = vec![];
for key in &self.table_key {
if let TableKey::ForeignKey(ref fk) = key {
foreign.push(fk)
}
}
foreign
}
pub fn get_foreign_tables(&self) -> Vec<&TableName> {
self.get_foreign_keys()
.iter()
.map(|foreign| &foreign.foreign_table)
.collect()
}
pub fn get_foreign_key_to_table(&self, table_name: &TableName) -> Option<&ForeignKey> {
let foreign_keys: Vec<&ForeignKey> = self.get_foreign_keys();
for fk in foreign_keys {
if fk.foreign_table == *table_name {
return Some(fk);
}
}
None
}
pub fn get_local_foreign_columns_pair_to_table(
&self,
table_name: &TableName,
) -> Vec<(&ColumnName, &ColumnName)> {
let foreign_keys: Vec<&ForeignKey> = self.get_foreign_keys();
for fk in foreign_keys {
if fk.foreign_table == *table_name {
let mut container = vec![];
for (local_column, referred_column) in
fk.columns.iter().zip(fk.referred_columns.iter())
{
container.push((local_column, referred_column));
}
return container;
}
}
vec![]
}
fn get_foreign_columns_to_table(&self, table_name: &TableName) -> Vec<&ColumnDef> {
self.get_foreign_column_names_to_table(table_name)
.iter()
.filter_map(|column_name| self.get_column(column_name))
.collect()
}
pub fn get_foreign_column_types_to_table(&self, table_name: &TableName) -> Vec<&SqlType> {
self.get_foreign_columns_to_table(table_name)
.iter()
.map(|column| &column.specification.sql_type)
.collect()
}
pub fn get_foreign_column_names_to_table(&self, table_name: &TableName) -> Vec<&ColumnName> {
let mut foreign_columns = vec![];
let foreign_keys = self.get_foreign_key_to_table(table_name);
for fk in &foreign_keys {
for fk_column in &fk.columns {
foreign_columns.push(fk_column);
}
}
foreign_columns
}
pub fn get_foreign_column_names(&self) -> Vec<&ColumnName> {
let mut foreign_columns = vec![];
let foreign_keys = self.get_foreign_keys();
for fk in &foreign_keys {
for fk_column in &fk.columns {
foreign_columns.push(fk_column);
}
}
foreign_columns
}
pub fn get_referred_columns_to_table(
&self,
table_name: &TableName,
) -> Option<&Vec<ColumnName>> {
let foreign_keys: Vec<&ForeignKey> = self.get_foreign_keys();
for fk in foreign_keys {
if fk.foreign_table == *table_name {
return Some(&fk.referred_columns);
}
}
None
}
pub fn get_column(&self, column_name: &ColumnName) -> Option<&ColumnDef> {
self.columns.iter().find(|c| c.name == *column_name)
}
}
#[derive(Debug, PartialEq, Clone)]
pub struct ForeignKey {
pub name: Option<String>,
pub columns: Vec<ColumnName>,
pub foreign_table: TableName,
pub referred_columns: Vec<ColumnName>,
}
#[derive(Debug, PartialEq, Clone)]
pub struct Key {
pub name: Option<String>,
pub columns: Vec<ColumnName>,
}
#[derive(Debug, PartialEq, Clone)]
pub enum TableKey {
PrimaryKey(Key),
UniqueKey(Key),
Key(Key),
ForeignKey(ForeignKey),
}
#[derive(Debug)]
pub struct SchemaContent {
pub schema: String,
pub tablenames: Vec<TableName>,
pub views: Vec<TableName>,
}
#[cfg(test)]
#[cfg(feature = "with-postgres")]
mod test {
use crate::{
table::*,
*,
};
use log::*;
#[test]
fn film_table_info() {
let db_url = "postgres://postgres:p0stgr3s@localhost:5432/sakila";
let mut pool = Pool::new();
let mut em = pool.em(db_url).expect("must be ok");
let table = em
.get_table(&TableName::from("public.film"))
.expect("must have a table");
println!("table: {:#?}", table);
}
#[test]
fn test_foreign_tables() {
let db_url = "postgres://postgres:p0stgr3s@localhost:5432/sakila";
let mut pool = Pool::new();
let mut em = pool.em(db_url).expect("must be ok");
let table = em
.get_table(&TableName::from("public.film_actor"))
.expect("must be ok")
.expect("must have a table");
println!("table: {:#?}", table);
let foreign_tables = table.get_foreign_tables();
println!("foreign_tables: {:#?}", foreign_tables);
assert_eq!(foreign_tables, vec![
&TableName::from("public.actor"),
&TableName::from("public.film"),
]);
}
#[test]
fn referred_columns() {
let db_url = "postgres://postgres:p0stgr3s@localhost:5432/sakila";
let mut pool = Pool::new();
let em = pool.em(db_url);
let mut db = pool.db(db_url).unwrap();
assert!(em.is_ok());
let film_tablename = TableName::from("public.film");
let film = db
.get_table(&film_tablename)
.expect("must be ok")
.expect("must have a table");
let film_actor_tablename = TableName::from("public.film_actor");
let film_actor = db
.get_table(&film_actor_tablename)
.expect("must be ok")
.expect("must have a table");
let rc = film_actor.get_referred_columns_to_table(&film.name);
info!("rc: {:#?}", rc);
assert_eq!(
rc,
Some(&vec![ColumnName {
name: "film_id".to_string(),
table: None,
alias: None,
}])
);
}
#[test]
fn referred_columns_hero_id() {
let db_url = "postgres://postgres:p0stgr3s@localhost:5432/dota";
let mut pool = Pool::new();
let mut em = pool.em(db_url).expect("must be ok");
let hero_tablename = TableName::from("public.hero");
let hero = em
.get_table(&hero_tablename)
.expect("must be ok")
.expect("must have a table");
let hero_ability_tablename = TableName::from("public.hero_ability");
let hero_ability = em
.get_table(&hero_ability_tablename)
.expect("must be ok")
.expect("must have a table");
info!("hero {:#?}", hero);
info!("hero ability {:#?}", hero_ability);
let rc = hero_ability.get_referred_columns_to_table(&hero.name);
info!("rc: {:#?}", rc);
assert_eq!(
rc,
Some(&vec![ColumnName {
name: "id".to_string(),
table: None,
alias: None,
}])
);
let foreign_key = hero_ability.get_foreign_key_to_table(&hero.name);
info!("foreign_key: {:#?}", foreign_key);
assert_eq!(
foreign_key,
Some(&ForeignKey {
name: Some("hero_id_fkey".to_string()),
columns: vec![ColumnName {
name: "hero_id".to_string(),
table: None,
alias: None,
}],
foreign_table: TableName {
name: "hero".to_string(),
schema: Some("public".to_string()),
alias: None,
},
referred_columns: vec![ColumnName {
name: "id".to_string(),
table: None,
alias: None,
}],
})
);
}
}