diff --git a/crates/dao/src/dao.rs b/crates/dao/src/dao.rs index 33e7f65..43f7028 100644 --- a/crates/dao/src/dao.rs +++ b/crates/dao/src/dao.rs @@ -46,6 +46,26 @@ impl Dao { } } + pub fn get_opt<'a, T>(&'a self, s: &str) -> Result, DaoError> + where + T: FromValue, + { + let value: Option<&'a Value> = self.0.get(s); + match value { + Some(v) => { + match v { + Value::Nil => Ok(None), + _ => { + Ok(Some( + FromValue::from_value(v).map_err(DaoError::ConvertError)?, + )) + } + } + } + None => Ok(None), + } + } + pub fn get_value(&self, s: &str) -> Option<&Value> { self.0.get(s) } pub fn remove(&mut self, s: &str) -> Option { self.0.remove(s) } diff --git a/examples/insert_to_sqlite.rs b/examples/insert_to_sqlite.rs index e34262c..c5a64dc 100644 --- a/examples/insert_to_sqlite.rs +++ b/examples/insert_to_sqlite.rs @@ -38,7 +38,7 @@ fn main() { let db_url = "sqlite:///tmp/sqlite.db"; let mut pool = Pool::new(); - let em = pool.em(db_url).unwrap(); + let mut em = pool.em(db_url).unwrap(); let ret = em.db().execute_sql_with_return(create_sql, &[]); println!("ret: {:?}", ret); assert!(ret.is_ok()); diff --git a/examples/insert_usage.rs b/examples/insert_usage.rs index cb92e34..f0358b6 100644 --- a/examples/insert_usage.rs +++ b/examples/insert_usage.rs @@ -34,7 +34,7 @@ fn main() { let db_url = "postgres://postgres:p0stgr3s@localhost/sakila"; let mut pool = Pool::new(); - let em = pool.em(db_url).unwrap(); + let mut em = pool.em(db_url).unwrap(); let tom_cruise = for_insert::Actor { first_name: "TOM".into(), last_name: "CRUISE".to_string(), diff --git a/examples/insert_usage_mysql.rs b/examples/insert_usage_mysql.rs index 3939b5b..cd6d459 100644 --- a/examples/insert_usage_mysql.rs +++ b/examples/insert_usage_mysql.rs @@ -40,7 +40,7 @@ fn main() { let db_url = "mysql://root:r00tpwdh3r3@localhost/sakila"; let mut pool = Pool::new(); pool.ensure(db_url); - let mut em = pool.em_mut(db_url).expect("Can not connect"); + let mut em = pool.em(db_url).expect("Can not connect"); let tom_cruise = for_insert::Actor { first_name: "TOM".into(), last_name: "CRUISE".to_string(), diff --git a/examples/select_usage.rs b/examples/select_usage.rs index 43c0e05..94d9b3a 100644 --- a/examples/select_usage.rs +++ b/examples/select_usage.rs @@ -15,7 +15,7 @@ struct Actor { fn main() { let db_url = "postgres://postgres:p0stgr3s@localhost/sakila"; let mut pool = Pool::new(); - let em = pool.em(db_url).unwrap(); + let mut em = pool.em(db_url).unwrap(); let sql = "SELECT * FROM actor LIMIT 10"; let actors: Result, DbError> = em.execute_sql_with_return(sql, &[]); println!("Actor: {:#?}", actors); diff --git a/examples/select_usage_mysql.rs b/examples/select_usage_mysql.rs index 4af8c02..c34c150 100644 --- a/examples/select_usage_mysql.rs +++ b/examples/select_usage_mysql.rs @@ -15,9 +15,8 @@ struct Actor { fn main() { let db_url = "mysql://root:r00tpwdh3r3@localhost/sakila"; let mut pool = Pool::new(); - pool.ensure(db_url); let mut em = pool - .em_mut(db_url) + .em(db_url) .expect("Should be able to get a connection here.."); let sql = "SELECT * FROM actor LIMIT 10"; let actors: Result, DbError> = em.execute_sql_with_return(sql, &[]); diff --git a/examples/select_usage_sqlite.rs b/examples/select_usage_sqlite.rs new file mode 100644 index 0000000..a177091 --- /dev/null +++ b/examples/select_usage_sqlite.rs @@ -0,0 +1,27 @@ +use rustorm::{ + DbError, + FromDao, + Pool, + ToColumnNames, + ToTableName, +}; + +#[derive(Debug, FromDao, ToColumnNames, ToTableName)] +struct Actor { + actor_id: i32, + first_name: String, +} + +fn main() { + let db_url = "sqlite://sakila.db"; + let mut pool = Pool::new(); + let mut em = pool.em(db_url).unwrap(); + let sql = "SELECT * FROM actor LIMIT 10"; + let actors: Result, DbError> = em.execute_sql_with_return(sql, &[]); + println!("Actor: {:#?}", actors); + let actors = actors.unwrap(); + assert_eq!(actors.len(), 10); + for actor in actors { + println!("actor: {:?}", actor); + } +} diff --git a/examples/simple_select.rs b/examples/simple_select.rs index 0b4e998..5d08970 100644 --- a/examples/simple_select.rs +++ b/examples/simple_select.rs @@ -7,7 +7,7 @@ use rustorm::{ fn main() { let db_url = "postgres://postgres:p0stgr3s@localhost/sakila"; let mut pool = Pool::new(); - let dm = pool.dm(db_url).unwrap(); + let mut dm = pool.dm(db_url).unwrap(); let sql = "SELECT * FROM actor LIMIT 10"; let actors: Result = dm.execute_sql_with_return(sql, &[]); println!("Actor: {:#?}", actors); diff --git a/examples/update_usage_mysql.rs b/examples/update_usage_mysql.rs index 33ce989..66e906e 100644 --- a/examples/update_usage_mysql.rs +++ b/examples/update_usage_mysql.rs @@ -17,7 +17,7 @@ fn main() { let mut pool = Pool::new(); pool.ensure(db_url); let mut em = pool - .em_mut(db_url) + .em(db_url) .expect("Should be able to get a connection here.."); let sql = "UPDATE actor SET last_name = ? WHERE first_name = ?".to_string(); let rows: Result = em diff --git a/src/dao_manager.rs b/src/dao_manager.rs index 0320242..65d533b 100644 --- a/src/dao_manager.rs +++ b/src/dao_manager.rs @@ -1,5 +1,5 @@ use crate::{ - platform::DBPlatform, + DBPlatform, Dao, DataError, DbError, @@ -10,13 +10,17 @@ use crate::{ pub struct DaoManager(pub DBPlatform); impl DaoManager { - pub fn execute_sql_with_return(&self, sql: &str, params: &[&Value]) -> Result { + pub fn execute_sql_with_return( + &mut self, + sql: &str, + params: &[&Value], + ) -> Result { let rows = self.0.execute_sql_with_return(sql, params)?; Ok(rows) } pub fn execute_sql_with_records_return( - &self, + &mut self, sql: &str, params: &[&Value], ) -> Result, DbError> { @@ -26,7 +30,7 @@ impl DaoManager { } pub fn execute_sql_with_one_return( - &self, + &mut self, sql: &str, params: &[&Value], ) -> Result { @@ -44,7 +48,7 @@ impl DaoManager { } pub fn execute_sql_with_maybe_one_return( - &self, + &mut self, sql: &str, params: &[&Value], ) -> Result, DbError> { diff --git a/src/database.rs b/src/database.rs index 6833711..c27920b 100644 --- a/src/database.rs +++ b/src/database.rs @@ -4,36 +4,37 @@ use crate::{ Role, User, }, - EntityManager, + DbError, Rows, Table, TableName, Value, }; +use rustorm_codegen::FromDao; use serde::Serialize; -use crate::DbError; -use rustorm_codegen::FromDao; /// The current database name and its comment #[derive(Serialize, FromDao)] pub struct DatabaseName { - name: String, - description: Option, + pub(crate) name: String, + pub(crate) description: Option, } + + pub trait Database { - fn execute_sql_with_return(&self, sql: &str, param: &[&Value]) -> Result; + fn execute_sql_with_return(&mut self, sql: &str, param: &[&Value]) -> Result; - fn get_table(&self, em: &EntityManager, table_name: &TableName) -> Result; + fn get_table(&mut self, table_name: &TableName) -> Result; - fn get_all_tables(&self, em: &EntityManager) -> Result, DbError>; + fn get_all_tables(&mut self) -> Result, DbError>; - fn get_grouped_tables(&self, em: &EntityManager) -> Result, DbError>; + fn get_grouped_tables(&mut self) -> Result, DbError>; - fn get_users(&self, em: &EntityManager) -> Result, DbError>; + fn get_users(&mut self) -> Result, DbError>; - fn get_roles(&self, em: &EntityManager, username: &str) -> Result, DbError>; + fn get_roles(&mut self, username: &str) -> Result, DbError>; - fn get_database_name(&self, em: &EntityManager) -> Result, DbError>; + fn get_database_name(&mut self) -> Result, DbError>; } diff --git a/src/database_mut.rs b/src/database_mut.rs deleted file mode 100644 index d2db7cd..0000000 --- a/src/database_mut.rs +++ /dev/null @@ -1,17 +0,0 @@ -use crate::{ - Rows, - Table, - TableName, - Value, -}; - - -use crate::DbError; - - - -pub trait DatabaseMut { - fn execute_sql_with_return(&mut self, sql: &str, param: &[&Value]) -> Result; - - fn get_table(&mut self, table_name: &TableName) -> Result; -} diff --git a/src/entity.rs b/src/entity.rs index d5f2e7c..93d0f58 100644 --- a/src/entity.rs +++ b/src/entity.rs @@ -1,10 +1,9 @@ use crate::{ - platform::DBPlatform, table::SchemaContent, users::{ - Role, User, }, + DBPlatform, DataError, Database, DatabaseName, @@ -13,7 +12,7 @@ use crate::{ ToValue, Value, }; -use log::*; + use rustorm_dao::{ FromDao, TableName, @@ -22,33 +21,38 @@ use rustorm_dao::{ ToTableName, }; + pub struct EntityManager(pub DBPlatform); impl EntityManager { - pub fn set_session_user(&self, username: &str) -> Result<(), DbError> { + pub fn set_session_user(&mut self, username: &str) -> Result<(), DbError> { let sql = format!("SET SESSION ROLE '{}'", username); self.0.execute_sql_with_return(&sql, &[])?; Ok(()) } - pub fn get_role(&self, username: &str) -> Result, DbError> { - let result = self.0.get_roles(&self, username); - match result { - Ok(mut result) => { - match result.len() { - 0 => Ok(None), - 1 => Ok(Some(result.remove(0))), - _ => Err(DbError::DataError(DataError::MoreThan1RecordReturned)), - } - } - Err(e) => Err(e), - } - } - - pub fn db(&self) -> &dyn Database { &*self.0 } + // pub fn get_role(&self, username: &str) -> Result, DbError> { + // let result = self.0.get_roles(&self, username); + // match result { + // Ok(mut result) => { + // match result.len() { + // 0 => Ok(None), + // 1 => Ok(Some(result.remove(0))), + // _ => { + // Err(DbError::DataError( + // DataError::MoreThan1RecordReturned, + // )) + // } + // } + // } + // Err(e) => Err(e), + // } + // } + + pub fn db(&mut self) -> &mut dyn Database { &mut *self.0 } /// get all the records of this table - pub fn get_all(&self) -> Result, DbError> + pub fn get_all(&mut self) -> Result, DbError> where T: ToTableName + ToColumnNames + FromDao, { @@ -74,18 +78,19 @@ impl EntityManager { } /// get the table from database based on this column name - pub fn get_table(&self, table_name: &TableName) -> Result { - self.0.get_table(self, table_name) + pub fn get_table(&mut self, table_name: &TableName) -> Result { + self.0.get_table(table_name) } /// get all the user table and views from the database - pub fn get_all_tables(&self) -> Result, DbError> { + pub fn get_all_tables(&mut self) -> Result, DbError> { info!("EXPENSIVE DB OPERATION: get_all_tables"); - self.0.get_all_tables(self) + //self.0.get_all_tables(self) + todo!(); } /// Get the total count of records - pub fn get_total_records(&self, table_name: &TableName) -> Result { + pub fn get_total_records(&mut self, table_name: &TableName) -> Result { #[derive(crate::FromDao)] struct Count { count: i64, @@ -98,18 +103,20 @@ impl EntityManager { count.map(|c| c.count as usize) } - pub fn get_users(&self) -> Result, DbError> { self.0.get_users(self) } + pub fn get_users(&mut self) -> Result, DbError> { self.0.get_users() } - pub fn get_database_name(&self) -> Result, DbError> { - self.0.get_database_name(self) + pub fn get_database_name(&mut self) -> Result, DbError> { + //self.0.get_database_name(self) + todo!(); } /// get all table and views grouped per schema - pub fn get_grouped_tables(&self) -> Result, DbError> { - self.0.get_grouped_tables(self) + pub fn get_grouped_tables(&mut self) -> Result, DbError> { + //self.0.get_grouped_tables(self) + todo!(); } - pub fn insert(&self, _entities: &[&T]) -> Result, DbError> + pub fn insert(&mut self, _entities: &[&T]) -> Result, DbError> where T: ToTableName + ToColumnNames + ToDao, R: FromDao + ToColumnNames, @@ -119,12 +126,14 @@ impl EntityManager { DBPlatform::Sqlite(_) => self.insert_simple(_entities), #[cfg(feature = "with-postgres")] DBPlatform::Postgres(_) => self.insert_bulk_with_returning_support(_entities), + #[cfg(feature = "with-mysql")] + DBPlatform::Mysql(_) => self.insert_simple(_entities), } } /// called when the platform used is postgresql pub fn insert_bulk_with_returning_support( - &self, + &mut self, entities: &[&T], ) -> Result, DbError> where @@ -159,7 +168,7 @@ impl EntityManager { /// called multiple times when using database platform that doesn;t support multiple value /// insert such as sqlite - pub fn single_insert(&self, entity: &T) -> Result<(), DbError> + pub fn single_insert(&mut self, entity: &T) -> Result<(), DbError> where T: ToTableName + ToColumnNames + ToDao, { @@ -180,7 +189,7 @@ impl EntityManager { } /// this is soly for use with sqlite since sqlite doesn't support bulk insert - pub fn insert_simple(&self, entities: &[&T]) -> Result, DbError> + pub fn insert_simple(&mut self, entities: &[&T]) -> Result, DbError> where T: ToTableName + ToColumnNames + ToDao, R: FromDao + ToColumnNames, @@ -254,7 +263,18 @@ impl EntityManager { columns .iter() .enumerate() - .map(|(x, _)| format!("${}", y * columns_len + x + 1)) + .map(|(x, _)| { + #[allow(unreachable_patterns)] + match self.0 { + // #[cfg(feature = "with-sqlite")] + // DBPlatform::Sqlite(_) => format!("${}", _y * columns_len + _x + 1), + // #[cfg(feature = "with-postgres")] + // DBPlatform::Postgres(_) => format!("${}", _y * columns_len + _x + 1), + #[cfg(feature = "with-mysql")] + DBPlatform::Mysql(_) => "?".to_string(), + _ => format!("${}", y * columns_len + x + 1), + } + }) .collect::>() .join(", ") ) @@ -266,7 +286,7 @@ impl EntityManager { #[allow(clippy::redundant_closure)] pub fn execute_sql_with_return<'a, R>( - &self, + &mut self, sql: &str, params: &[&'a dyn ToValue], ) -> Result, DbError> @@ -280,7 +300,7 @@ impl EntityManager { } pub fn execute_sql_with_one_return<'a, R>( - &self, + &mut self, sql: &str, params: &[&'a dyn ToValue], ) -> Result @@ -301,7 +321,7 @@ impl EntityManager { } pub fn execute_sql_with_maybe_one_return<'a, R>( - &self, + &mut self, sql: &str, params: &[&'a dyn ToValue], ) -> Result, DbError> @@ -321,321 +341,3 @@ impl EntityManager { } } } - - -#[cfg(test)] -#[cfg(feature = "with-postgres")] -mod test_pg { - use crate::*; - use chrono::{ - offset::Utc, - DateTime, - NaiveDate, - }; - use log::*; - use uuid::Uuid; - - #[test] - fn use_em() { - #[derive(Debug, FromDao, ToColumnNames, crate::ToTableName)] - struct Actor { - actor_id: i32, - first_name: String, - } - let db_url = "postgres://postgres:p0stgr3s@localhost/sakila"; - let mut pool = Pool::new(); - let em = pool.em(db_url).unwrap(); - let actors: Result, DbError> = em.get_all(); - info!("Actor: {:#?}", actors); - let actors = actors.unwrap(); - for actor in actors { - info!("actor: {:?}", actor); - } - } - - #[test] - fn various_data_types() { - let db_url = "postgres://postgres:p0stgr3s@localhost/sakila"; - let mut pool = Pool::new(); - let em = pool.em(db_url).unwrap(); - #[derive(Debug, PartialEq, FromDao, ToDao, ToColumnNames, ToTableName)] - struct Sample { - vnil: Option, - vbool: bool, - vsmallint: i16, - vint: i32, - vbigint: i64, - vfloat: f32, - vdouble: f64, - vblob: Vec, - vchar: char, - vtext: String, - vuuid: Uuid, - vdate: NaiveDate, - vtimestamp: DateTime, - } - - let sample: Result, DbError> = em.execute_sql_with_return( - r#" - SELECT NULL::TEXT as vnil, - true::BOOL as vbool, - 1000::INT2 as vsmallint, - 32000::INT as vint, - 123000::INT4 as vbigint, - 1.0::FLOAT4 as vfloat, - 2.0::FLOAT8 as vdouble, - E'\\000'::BYTEA as vblob, - 'c'::CHAR as vchar, - 'Hello'::TEXT as vtext, - 'd25af116-fb30-4731-9cf9-2251c235e8fa'::UUID as vuuid, - now()::DATE as vdate, - now()::TIMESTAMP WITH TIME ZONE as vtimestamp - - "#, - &[], - ); - info!("{:#?}", sample); - assert!(sample.is_ok()); - - let sample = sample.unwrap(); - let sample = &sample[0]; - let now = Utc::now(); - let today = now.date(); - let naive_today = today.naive_utc(); - - assert_eq!(None, sample.vnil); - assert_eq!(true, sample.vbool); - assert_eq!(1000, sample.vsmallint); - assert_eq!(32000, sample.vint); - assert_eq!(123000, sample.vbigint); - assert_eq!(1.0, sample.vfloat); - assert_eq!(2.0, sample.vdouble); - assert_eq!(vec![0], sample.vblob); - assert_eq!('c', sample.vchar); - assert_eq!("Hello".to_string(), sample.vtext); - assert_eq!(naive_today, sample.vdate); - assert_eq!(today, sample.vtimestamp.date()); - } - - #[test] - fn various_data_types_nulls() { - let db_url = "postgres://postgres:p0stgr3s@localhost/sakila"; - let mut pool = Pool::new(); - let em = pool.em(db_url).unwrap(); - #[derive(Debug, PartialEq, FromDao, ToDao, ToColumnNames, ToTableName)] - struct Sample { - vnil: Option, - vbool: Option, - vsmallint: Option, - vint: Option, - vbigint: Option, - vfloat: Option, - vdouble: Option, - vblob: Option>, - vchar: Option, - vtext: Option, - vuuid: Option, - vdate: Option, - vtimestamp: Option>, - } - - let sample: Result, DbError> = em.execute_sql_with_return( - r#" - SELECT NULL::TEXT as vnil, - NULL::BOOL as vbool, - NULL::INT2 as vsmallint, - NULL::INT as vint, - NULL::INT4 as vbigint, - NULL::FLOAT4 as vfloat, - NULL::FLOAT8 as vdouble, - NULL::BYTEA as vblob, - NULL::CHAR as vchar, - NULL::TEXT as vtext, - NULL::UUID as vuuid, - NULL::DATE as vdate, - NULL::TIMESTAMP WITH TIME ZONE as vtimestamp - - "#, - &[], - ); - info!("{:#?}", sample); - assert!(sample.is_ok()); - - let sample = sample.unwrap(); - let sample = &sample[0]; - - assert_eq!(None, sample.vnil); - assert_eq!(None, sample.vbool); - assert_eq!(None, sample.vsmallint); - assert_eq!(None, sample.vint); - assert_eq!(None, sample.vbigint); - assert_eq!(None, sample.vfloat); - assert_eq!(None, sample.vdouble); - assert_eq!(None, sample.vblob); - assert_eq!(None, sample.vtext); - assert_eq!(None, sample.vdate); - assert_eq!(None, sample.vtimestamp); - } - - #[test] - fn edgecase_use_char_as_string() { - let db_url = "postgres://postgres:p0stgr3s@localhost/sakila"; - let mut pool = Pool::new(); - let em = pool.em(db_url).unwrap(); - #[derive(Debug, PartialEq, FromDao, ToDao, ToColumnNames, ToTableName)] - struct Sample { - vchar: String, - } - - let sample: Result, DbError> = em.execute_sql_with_return( - r#" - SELECT - 'c'::CHAR as VCHAR - "#, - &[], - ); - info!("{:#?}", sample); - assert!(sample.is_ok()); - - let sample = sample.unwrap(); - let sample = &sample[0]; - assert_eq!("c".to_string(), sample.vchar); - } - - #[test] - fn char1() { - let db_url = "postgres://postgres:p0stgr3s@localhost/sakila"; - let mut pool = Pool::new(); - let em = pool.em(db_url).unwrap(); - #[derive(Debug, PartialEq, FromDao, ToDao, ToColumnNames, ToTableName)] - struct Sample { - vchar: char, - } - - let sample: Result, DbError> = em.execute_sql_with_return( - r#" - SELECT - 'c'::CHAR as VCHAR - "#, - &[], - ); - info!("{:#?}", sample); - assert!(sample.is_ok()); - - let sample = sample.unwrap(); - let sample = &sample[0]; - assert_eq!('c', sample.vchar); - } - - #[test] - fn insert_some_data() { - #[derive(Debug, PartialEq, FromDao, ToDao, ToColumnNames, ToTableName)] - struct Actor { - first_name: String, - last_name: String, - } - let db_url = "postgres://postgres:p0stgr3s@localhost/sakila"; - let mut pool = Pool::new(); - let em = pool.em(db_url).unwrap(); - let tom_cruise = Actor { - first_name: "TOM".into(), - last_name: "CRUISE".to_string(), - }; - let tom_hanks = Actor { - first_name: "TOM".into(), - last_name: "HANKS".to_string(), - }; - - let actors: Result, DbError> = em.insert(&[&tom_cruise, &tom_hanks]); - info!("Actor: {:#?}", actors); - assert!(actors.is_ok()); - let actors = actors.unwrap(); - assert_eq!(tom_cruise, actors[0]); - assert_eq!(tom_hanks, actors[1]); - } - - #[test] - fn insert_some_data_with_different_retrieve() { - mod for_insert { - use super::*; - #[derive(Debug, PartialEq, ToDao, ToColumnNames, ToTableName)] - pub struct Actor { - pub first_name: String, - pub last_name: String, - } - } - - mod for_retrieve { - use super::*; - #[derive(Debug, FromDao, ToColumnNames, ToTableName)] - pub struct Actor { - pub actor_id: i32, - pub first_name: String, - pub last_name: String, - pub last_update: DateTime, - } - } - - let db_url = "postgres://postgres:p0stgr3s@localhost/sakila"; - let mut pool = Pool::new(); - let em = pool.em(db_url).unwrap(); - let tom_cruise = for_insert::Actor { - first_name: "TOM".into(), - last_name: "CRUISE".to_string(), - }; - let tom_hanks = for_insert::Actor { - first_name: "TOM".into(), - last_name: "HANKS".to_string(), - }; - - let actors: Result, DbError> = - em.insert(&[&tom_cruise, &tom_hanks]); - info!("Actor: {:#?}", actors); - assert!(actors.is_ok()); - let actors = actors.unwrap(); - let today = Utc::now().date(); - assert_eq!(tom_cruise.first_name, actors[0].first_name); - assert_eq!(tom_cruise.last_name, actors[0].last_name); - assert_eq!(today, actors[0].last_update.date()); - assert_eq!(tom_hanks.first_name, actors[1].first_name); - assert_eq!(tom_hanks.last_name, actors[1].last_name); - assert_eq!(today, actors[1].last_update.date()); - } - - #[test] - fn execute_sql_non_existing_table() { - #[derive(Debug, crate::FromDao)] - struct Event { - id: i32, - name: String, - created: DateTime, - } - let db_url = "postgres://postgres:p0stgr3s@localhost/sakila"; - let mut pool = Pool::new(); - let em = pool.em(db_url).unwrap(); - let id = 1; - let name = "dbus-notifications".to_string(); - let created = Utc::now(); - let events: Result, DbError> = em.execute_sql_with_return( - "SELECT $1::INT as id, $2::TEXT as name, $3::TIMESTAMP WITH TIME ZONE as created", - &[&id, &name, &created], - ); - info!("events: {:#?}", events); - assert!(events.is_ok()); - for event in events.unwrap().iter() { - assert_eq!(event.id, id); - assert_eq!(event.name, name); - assert_eq!(event.created.date(), created.date()); - } - } - - #[test] - fn get_table() { - let db_url = "postgres://postgres:p0stgr3s@localhost/sakila"; - let mut pool = Pool::new(); - let em = pool.em(db_url).unwrap(); - let actor = TableName::from("actor"); - let table = em.db().get_table(&em, &actor); - assert!(table.is_ok()); - } -} diff --git a/src/entity_mut.rs b/src/entity_mut.rs deleted file mode 100644 index bf397b3..0000000 --- a/src/entity_mut.rs +++ /dev/null @@ -1,339 +0,0 @@ -use crate::{ - platform_mut::DBPlatformMut, - DataError, - DatabaseMut, - DbError, - Table, - ToValue, - Value, -}; - -use rustorm_dao::{ - FromDao, - TableName, - ToColumnNames, - ToDao, - ToTableName, -}; - - -pub struct EntityManagerMut(pub DBPlatformMut); - -impl EntityManagerMut { - pub fn set_session_user(&mut self, username: &str) -> Result<(), DbError> { - let sql = format!("SET SESSION ROLE '{}'", username); - self.0.execute_sql_with_return(&sql, &[])?; - Ok(()) - } - - // pub fn get_role(&self, username: &str) -> Result, DbError> { - // let result = self.0.get_roles(&self, username); - // match result { - // Ok(mut result) => { - // match result.len() { - // 0 => Ok(None), - // 1 => Ok(Some(result.remove(0))), - // _ => { - // Err(DbError::DataError( - // DataError::MoreThan1RecordReturned, - // )) - // } - // } - // } - // Err(e) => Err(e), - // } - // } - - pub fn db(&mut self) -> &mut dyn DatabaseMut { &mut *self.0 } - - /// get all the records of this table - pub fn get_all(&mut self) -> Result, DbError> - where - T: ToTableName + ToColumnNames + FromDao, - { - let table = T::to_table_name(); - let columns = T::to_column_names(); - let enumerated_columns = columns - .iter() - .map(|c| c.name.to_owned()) - .collect::>() - .join(", "); - let sql = format!( - "SELECT {} FROM {}", - enumerated_columns, - table.complete_name() - ); - let rows = self.0.execute_sql_with_return(&sql, &[])?; - let mut entities = vec![]; - for dao in rows.iter() { - let entity = T::from_dao(&dao); - entities.push(entity) - } - Ok(entities) - } - - /// get the table from database based on this column name - pub fn get_table(&mut self, table_name: &TableName) -> Result { - self.0.get_table(table_name) - } - - // /// get all the user table and views from the database - // pub fn get_all_tables(&self) -> Result, DbError> { - // info!("EXPENSIVE DB OPERATION: get_all_tables"); - // self.0.get_all_tables(self) - // } - - /// Get the total count of records - pub fn get_total_records(&mut self, table_name: &TableName) -> Result { - #[derive(crate::FromDao)] - struct Count { - count: i64, - } - let sql = format!( - "SELECT COUNT(*) AS count FROM {}", - table_name.complete_name() - ); - let count: Result = self.execute_sql_with_one_return(&sql, &[]); - count.map(|c| c.count as usize) - } - - // pub fn get_users(&self) -> Result, DbError> { - // self.0.get_users(self) - // } - - // pub fn get_database_name(&self) -> Result, DbError> { - // self.0.get_database_name(self) - // } - - // /// get all table and views grouped per schema - // pub fn get_grouped_tables(&self) -> Result, DbError> { - // self.0.get_grouped_tables(self) - // } - - pub fn insert(&mut self, _entities: &[&T]) -> Result, DbError> - where - T: ToTableName + ToColumnNames + ToDao, - R: FromDao + ToColumnNames, - { - match self.0 { - // #[cfg(feature = "with-sqlite")] - // DBPlatform::Sqlite(_) => self.insert_simple(_entities), - // #[cfg(feature = "with-postgres")] - // DBPlatform::Postgres(_) => { - // self.insert_bulk_with_returning_support(_entities) - // } - #[cfg(feature = "with-mysql")] - DBPlatformMut::Mysql(_) => self.insert_simple(_entities), - } - } - - /// called when the platform used is postgresql - pub fn insert_bulk_with_returning_support( - &mut self, - entities: &[&T], - ) -> Result, DbError> - where - T: ToTableName + ToColumnNames + ToDao, - R: FromDao + ToColumnNames, - { - let columns = T::to_column_names(); - let mut sql = self.build_insert_clause(entities); - let return_columns = R::to_column_names(); - sql += &self.build_returning_clause(return_columns); - - let mut values: Vec = Vec::with_capacity(entities.len() * columns.len()); - for entity in entities { - let dao = entity.to_dao(); - for col in columns.iter() { - let value = dao.get_value(&col.name); - match value { - Some(value) => values.push(value.clone()), - None => values.push(Value::Nil), - } - } - } - let bvalues: Vec<&Value> = values.iter().collect(); - let rows = self.0.execute_sql_with_return(&sql, &bvalues)?; - let mut retrieved_entities = vec![]; - for dao in rows.iter() { - let retrieved = R::from_dao(&dao); - retrieved_entities.push(retrieved); - } - Ok(retrieved_entities) - } - - /// called multiple times when using database platform that doesn;t support multiple value - /// insert such as sqlite - pub fn single_insert(&mut self, entity: &T) -> Result<(), DbError> - where - T: ToTableName + ToColumnNames + ToDao, - { - let columns = T::to_column_names(); - let sql = self.build_insert_clause(&[entity]); - let dao = entity.to_dao(); - let mut values: Vec = Vec::with_capacity(columns.len()); - for col in columns.iter() { - let value = dao.get_value(&col.name); - match value { - Some(value) => values.push(value.clone()), - None => values.push(Value::Nil), - } - } - let bvalues: Vec<&Value> = values.iter().collect(); - self.0.execute_sql_with_return(&sql, &bvalues)?; - Ok(()) - } - - /// this is soly for use with sqlite since sqlite doesn't support bulk insert - pub fn insert_simple(&mut self, entities: &[&T]) -> Result, DbError> - where - T: ToTableName + ToColumnNames + ToDao, - R: FromDao + ToColumnNames, - { - let return_columns = R::to_column_names(); - let return_column_names = return_columns - .iter() - .map(|rc| rc.name.to_owned()) - .collect::>() - .join(", "); - - let table = T::to_table_name(); - //TODO: move this specific query to sqlite - let last_insert_sql = format!( - "\ - SELECT {} \ - FROM {} \ - WHERE ROWID = (\ - SELECT LAST_INSERT_ROWID() FROM {})", - return_column_names, - table.complete_name(), - table.complete_name() - ); - let mut retrieved_entities = vec![]; - println!("sql: {}", last_insert_sql); - for entity in entities { - self.single_insert(*entity)?; - let retrieved = self.execute_sql_with_return(&last_insert_sql, &[])?; - retrieved_entities.extend(retrieved); - } - Ok(retrieved_entities) - } - - /// build the returning clause - fn build_returning_clause(&self, return_columns: Vec) -> String { - format!( - "\nRETURNING \n{}", - return_columns - .iter() - .map(|rc| rc.name.to_owned()) - .collect::>() - .join(", ") - ) - } - - /// build an insert clause - fn build_insert_clause(&self, entities: &[&T]) -> String - where - T: ToTableName + ToColumnNames + ToDao, - { - let table = T::to_table_name(); - let columns = T::to_column_names(); - let columns_len = columns.len(); - let mut sql = String::new(); - sql += &format!("INSERT INTO {} ", table.complete_name()); - sql += &format!( - "({})\n", - columns - .iter() - .map(|c| c.name.to_owned()) - .collect::>() - .join(", ") - ); - sql += "VALUES "; - sql += &entities - .iter() - .enumerate() - .map(|(y, _)| { - format!( - "\n\t({})", - columns - .iter() - .enumerate() - .map(|(x, _)| { - #[allow(unreachable_patterns)] - match self.0 { - // #[cfg(feature = "with-sqlite")] - // DBPlatform::Sqlite(_) => format!("${}", _y * columns_len + _x + 1), - // #[cfg(feature = "with-postgres")] - // DBPlatform::Postgres(_) => format!("${}", _y * columns_len + _x + 1), - #[cfg(feature = "with-mysql")] - DBPlatformMut::Mysql(_) => "?".to_string(), - _ => format!("${}", y * columns_len + x + 1), - } - }) - .collect::>() - .join(", ") - ) - }) - .collect::>() - .join(", "); - sql - } - - #[allow(clippy::redundant_closure)] - pub fn execute_sql_with_return<'a, R>( - &mut self, - sql: &str, - params: &[&'a dyn ToValue], - ) -> Result, DbError> - where - R: FromDao, - { - let values: Vec = params.iter().map(|p| p.to_value()).collect(); - let bvalues: Vec<&Value> = values.iter().collect(); - let rows = self.0.execute_sql_with_return(sql, &bvalues)?; - Ok(rows.iter().map(|dao| R::from_dao(&dao)).collect::>()) - } - - pub fn execute_sql_with_one_return<'a, R>( - &mut self, - sql: &str, - params: &[&'a dyn ToValue], - ) -> Result - where - R: FromDao, - { - let result: Result, DbError> = self.execute_sql_with_return(sql, ¶ms); - match result { - Ok(mut result) => { - match result.len() { - 0 => Err(DbError::DataError(DataError::ZeroRecordReturned)), - 1 => Ok(result.remove(0)), - _ => Err(DbError::DataError(DataError::MoreThan1RecordReturned)), - } - } - Err(e) => Err(e), - } - } - - pub fn execute_sql_with_maybe_one_return<'a, R>( - &mut self, - sql: &str, - params: &[&'a dyn ToValue], - ) -> Result, DbError> - where - R: FromDao, - { - let result: Result, DbError> = self.execute_sql_with_return(sql, ¶ms); - match result { - Ok(mut result) => { - match result.len() { - 0 => Ok(None), - 1 => Ok(Some(result.remove(0))), - _ => Err(DbError::DataError(DataError::MoreThan1RecordReturned)), - } - } - Err(e) => Err(e), - } - } -} diff --git a/src/lib.rs b/src/lib.rs index 6728a95..bf5a777 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -34,7 +34,7 @@ //! let db_url = "sqlite://sakila.db"; //! #[cfg(feature = "with-postgres")] //! let db_url = "postgres://postgres:p0stgr3s@localhost/sakila"; -//! let em = pool.em(db_url).unwrap(); +//! let mut em = pool.em(db_url).unwrap(); //! let sql = "SELECT * FROM actor LIMIT 10"; //! let actors: Result, DbError> = //! em.execute_sql_with_return(sql, &[]); @@ -96,7 +96,7 @@ //! let db_url = "sqlite://sakila.db"; //! #[cfg(feature = "with-postgres")] //! let db_url = "postgres://postgres:p0stgr3s@localhost/sakila"; -//! let em = pool.em(db_url).unwrap(); +//! let mut em = pool.em(db_url).unwrap(); //! let tom_cruise = for_insert::Actor { //! first_name: "TOM".into(), //! last_name: "CRUISE".to_string(), @@ -149,12 +149,9 @@ pub mod column; pub mod common; mod dao_manager; mod database; -mod database_mut; mod entity; -mod entity_mut; pub mod error; mod platform; -mod platform_mut; pub mod pool; pub mod table; pub mod types; @@ -168,14 +165,12 @@ pub use database::{ Database, DatabaseName, }; - -pub use database_mut::DatabaseMut; pub use entity::EntityManager; -pub use entity_mut::EntityManagerMut; pub use error::{ DataError, DbError, }; +pub use platform::DBPlatform; pub use pool::Pool; pub use table::Table; @@ -220,3 +215,6 @@ pub mod codegen { ToTableName, }; } + +#[macro_use] +extern crate log; diff --git a/src/my/mod.rs b/src/my/mod.rs index f14761e..0909273 100644 --- a/src/my/mod.rs +++ b/src/my/mod.rs @@ -1,11 +1,17 @@ use crate::{ column, common, + table::SchemaContent, types::SqlType, + users::{ + Role, + User, + }, Column, ColumnName, DataError, - DatabaseMut, + Database, + DatabaseName, DbError, FromDao, Table, @@ -45,7 +51,7 @@ pub fn test_connection(db_url: &str) -> Result<(), MysqlError> { pub struct MysqlDB(pub r2d2::PooledConnection); -impl DatabaseMut for MysqlDB { +impl Database for MysqlDB { fn execute_sql_with_return(&mut self, sql: &str, param: &[&Value]) -> Result { fn collect(mut rows: mysql::QueryResult) -> Result { let column_types: Vec<_> = rows.columns_ref().iter().map(|c| c.column_type()).collect(); @@ -237,6 +243,16 @@ impl DatabaseMut for MysqlDB { table_key: vec![], }) } + + fn get_all_tables(&mut self) -> Result, DbError> { todo!() } + + fn get_grouped_tables(&mut self) -> Result, DbError> { todo!() } + + fn get_users(&mut self) -> Result, DbError> { todo!() } + + fn get_roles(&mut self, _username: &str) -> Result, DbError> { todo!() } + + fn get_database_name(&mut self) -> Result, DbError> { todo!() } } #[derive(Debug)] diff --git a/src/pg/column_info.rs b/src/pg/column_info.rs index 0eb3b13..7b850de 100644 --- a/src/pg/column_info.rs +++ b/src/pg/column_info.rs @@ -11,16 +11,17 @@ use crate::{ util, Column, ColumnName, + Database, DbError, - EntityManager, TableName, + ToValue, }; use log::*; use rustorm_dao; use uuid::Uuid; /// get all the columns of the table -pub fn get_columns(em: &EntityManager, table_name: &TableName) -> Result, DbError> { +pub fn get_columns(db: &mut dyn Database, table_name: &TableName) -> Result, DbError> { /// column name and comment #[derive(Debug, crate::codegen::FromDao)] struct ColumnSimple { @@ -69,18 +70,30 @@ pub fn get_columns(em: &EntityManager, table_name: &TableName) -> Result schema.to_string(), None => "public".to_string(), }; - let columns_simple: Result, DbError> = em.execute_sql_with_return(&sql, &[ - &table_name.name, - &schema, - &table_name.complete_name(), - ]); + let columns_simple: Result, DbError> = db + .execute_sql_with_return(&sql, &[ + &table_name.name.to_value(), + &schema.to_value(), + &table_name.complete_name().to_value(), + ]) + .map(|rows| { + rows.iter() + .map(|row| { + ColumnSimple { + number: row.get("number").expect("a number"), + name: row.get("name").expect("a name"), + comment: row.get_opt("comment").expect("a comment"), + } + }) + .collect() + }); match columns_simple { Ok(columns_simple) => { let mut columns = vec![]; for column_simple in columns_simple { - let specification = get_column_specification(em, table_name, &column_simple.name); - let column_stat = get_column_stat(em, table_name, &column_simple.name)?; + let specification = get_column_specification(db, table_name, &column_simple.name); + let column_stat = get_column_stat(db, table_name, &column_simple.name)?; match specification { Ok(specification) => { let column = @@ -101,7 +114,7 @@ pub fn get_columns(em: &EntityManager, table_name: &TableName) -> Result Result { @@ -410,13 +423,36 @@ fn get_column_specification( None => "public".to_string(), }; //info!("sql: {} column_name: {}, table_name: {}", sql, column_name, table_name.name); - let column_constraint: Result = - em.execute_sql_with_one_return(&sql, &[&column_name, &table_name.name, &schema]); - column_constraint.map(|c| c.to_column_specification(table_name, column_name)) + let mut column_constraints: Vec = db + .execute_sql_with_return(&sql, &[ + &column_name.to_value(), + &table_name.name.to_value(), + &schema.to_value(), + ]) + .map(|rows| { + rows.iter() + .map(|row| { + ColumnConstraintSimple { + not_null: row.get("not_null").expect("a not_null"), + data_type: row.get("data_type").expect("a data_type"), + default: row.get_opt("default").expect("a default"), + is_enum: row.get("is_enum").expect("a is_enum"), + is_array_enum: row.get("is_array_enum").expect("a is_array_enum"), + enum_choices: row.get("enum_choices").expect("enum_choices"), + array_enum_choices: row + .get("array_enum_choices") + .expect("array of enum choices"), + } + }) + .collect() + })?; + assert_eq!(column_constraints.len(), 1); + let column_constraint = column_constraints.remove(0); + Ok(column_constraint.to_column_specification(table_name, column_name)) } fn get_column_stat( - em: &EntityManager, + db: &mut dyn Database, table_name: &TableName, column_name: &str, ) -> Result, DbError> { @@ -433,9 +469,27 @@ fn get_column_stat( Some(ref schema) => schema.to_string(), None => "public".to_string(), }; - let column_stat: Result, DbError> = - em.execute_sql_with_maybe_one_return(&sql, &[&column_name, &table_name.name, &schema]); - column_stat + let mut column_stat: Vec = db + .execute_sql_with_return(&sql, &[ + &column_name.to_value(), + &table_name.name.to_value(), + &schema.to_value(), + ]) + .map(|rows| { + rows.iter() + .map(|row| { + ColumnStat { + avg_width: row.get("avg_width").expect("avg_width"), + n_distinct: row.get("n_distinct").expect("n_distinct"), + } + }) + .collect() + })?; + if column_stat.len() > 0 { + Ok(Some(column_stat.remove(0))) + } else { + Ok(None) + } } #[cfg(test)] @@ -474,9 +528,7 @@ mod test { }; let db_url = "postgres://postgres:p0stgr3s@localhost:5432/sakila"; let mut pool = Pool::new(); - let em = pool.em(db_url); - assert!(em.is_ok()); - let em = em.unwrap(); + let mut em = pool.em(db_url).unwrap(); let result: Result, DbError> = em.insert(&[&film1]); info!("result: {:#?}", result); assert!(result.is_ok()); @@ -486,12 +538,12 @@ mod test { fn column_specification_for_film_rating() { let db_url = "postgres://postgres:p0stgr3s@localhost:5432/sakila"; let mut pool = Pool::new(); - let em = pool.em(db_url); - assert!(em.is_ok()); - let em = em.unwrap(); + let db = pool.db(db_url); + assert!(db.is_ok()); + let mut db = db.unwrap(); let table = TableName::from("film"); let column = ColumnName::from("rating"); - let specification = get_column_specification(&em, &table, &column.name); + let specification = get_column_specification(&mut *db, &table, &column.name); info!("specification: {:#?}", specification); assert!(specification.is_ok()); let specification = specification.unwrap(); @@ -514,12 +566,12 @@ mod test { fn column_specification_for_actor_id() { let db_url = "postgres://postgres:p0stgr3s@localhost:5432/sakila"; let mut pool = Pool::new(); - let em = pool.em(db_url); - assert!(em.is_ok()); - let em = em.unwrap(); + let db = pool.db(db_url); + assert!(db.is_ok()); + let mut db = db.unwrap(); let actor_table = TableName::from("actor"); let actor_id_column = ColumnName::from("actor_id"); - let specification = get_column_specification(&em, &actor_table, &actor_id_column.name); + let specification = get_column_specification(&mut *db, &actor_table, &actor_id_column.name); info!("specification: {:#?}", specification); assert!(specification.is_ok()); let specification = specification.unwrap(); @@ -533,12 +585,12 @@ mod test { fn column_specification_for_actor_last_updated() { let db_url = "postgres://postgres:p0stgr3s@localhost:5432/sakila"; let mut pool = Pool::new(); - let em = pool.em(db_url); - assert!(em.is_ok()); - let em = em.unwrap(); + let db = pool.db(db_url); + assert!(db.is_ok()); + let mut db = db.unwrap(); let actor_table = TableName::from("actor"); let column = ColumnName::from("last_update"); - let specification = get_column_specification(&em, &actor_table, &column.name); + let specification = get_column_specification(&mut *db, &actor_table, &column.name); info!("specification: {:#?}", specification); assert!(specification.is_ok()); let specification = specification.unwrap(); @@ -556,11 +608,11 @@ mod test { fn column_for_actor() { let db_url = "postgres://postgres:p0stgr3s@localhost:5432/sakila"; let mut pool = Pool::new(); - let em = pool.em(db_url); - assert!(em.is_ok()); - let em = em.unwrap(); + let db = pool.db(db_url); + assert!(db.is_ok()); + let mut db = db.unwrap(); let actor_table = TableName::from("actor"); - let columns = get_columns(&em, &actor_table); + let columns = get_columns(&mut *db, &actor_table); info!("columns: {:#?}", columns); assert!(columns.is_ok()); let columns = columns.unwrap(); @@ -581,11 +633,11 @@ mod test { fn column_for_film() { let db_url = "postgres://postgres:p0stgr3s@localhost:5432/sakila"; let mut pool = Pool::new(); - let em = pool.em(db_url); - assert!(em.is_ok()); - let em = em.unwrap(); + let db = pool.db(db_url); + assert!(db.is_ok()); + let mut db = db.unwrap(); let table = TableName::from("film"); - let columns = get_columns(&em, &table); + let columns = get_columns(&mut *db, &table); info!("columns: {:#?}", columns); assert!(columns.is_ok()); let columns = columns.unwrap(); diff --git a/src/pg/mod.rs b/src/pg/mod.rs index 1474649..64374aa 100644 --- a/src/pg/mod.rs +++ b/src/pg/mod.rs @@ -3,16 +3,13 @@ use self::{ numeric::PgNumeric, }; use crate::{ - database::DatabaseName, error::PlatformError, table::SchemaContent, users::{ Role, User, }, - Database, DbError, - EntityManager, Table, TableName, Value, @@ -85,7 +82,7 @@ pub fn test_connection(db_url: &str) -> Result<(), PostgresError> { pub struct PostgresDB(pub r2d2::PooledConnection); impl Database for PostgresDB { - fn execute_sql_with_return(&self, sql: &str, param: &[&Value]) -> Result { + fn execute_sql_with_return(&mut self, sql: &str, param: &[&Value]) -> Result { let stmt = self.0.prepare(&sql); match stmt { Ok(stmt) => { @@ -146,20 +143,20 @@ impl Database for PostgresDB { } } - fn get_table(&self, em: &EntityManager, table_name: &TableName) -> Result { - table_info::get_table(em, table_name) + fn get_table(&mut self, table_name: &TableName) -> Result { + table_info::get_table(&mut *self, table_name) } - fn get_all_tables(&self, em: &EntityManager) -> Result, DbError> { - table_info::get_all_tables(em) + fn get_all_tables(&mut self) -> Result, DbError> { + table_info::get_all_tables(&mut *self) } - fn get_grouped_tables(&self, em: &EntityManager) -> Result, DbError> { - table_info::get_organized_tables(em) + fn get_grouped_tables(&mut self) -> Result, DbError> { + table_info::get_organized_tables(&mut *self) } /// get the list of database users - fn get_users(&self, em: &EntityManager) -> Result, DbError> { + fn get_users(&mut self) -> Result, DbError> { let sql = "SELECT oid::int AS sysid, rolname AS username, rolsuper AS is_superuser, @@ -176,11 +173,34 @@ impl Database for PostgresDB { ELSE rolvaliduntil END AS valid_until FROM pg_authid"; - em.execute_sql_with_return(&sql, &[]) + let rows: Result = self.execute_sql_with_return(&sql, &[]); + + rows.map(|rows| { + rows.iter() + .map(|row| { + User { + sysid: row.get("sysid").expect("sysid"), + username: row.get("username").expect("username"), + password: row.get("password").expect("password"), + is_superuser: row.get("is_superuser").expect("is_superuser"), + is_inherit: row.get("is_inherit").expect("is_inherit"), + can_create_db: row.get("can_create_db").expect("can_create_db"), + can_create_role: row.get("can_create_role").expect("can_create_role"), + can_login: row.get("can_login").expect("can_login"), + can_do_replication: row + .get("can_do_replication") + .expect("can_do_replication"), + can_bypass_rls: row.get("can_bypass_rls").expect("can_bypass_rls"), + valid_until: row.get("valid_until").expect("valid_until"), + conn_limit: row.get("conn_limit").expect("conn_limit"), + } + }) + .collect() + }) } /// get the list of roles for this user - fn get_roles(&self, em: &EntityManager, username: &str) -> Result, DbError> { + fn get_roles(&mut self, username: &str) -> Result, DbError> { let sql = "SELECT (SELECT rolname FROM pg_roles WHERE oid = m.roleid) AS role_name FROM pg_auth_members m @@ -188,18 +208,46 @@ impl Database for PostgresDB { ON m.member = pg_roles.oid WHERE pg_roles.rolname = $1 "; - em.execute_sql_with_return(&sql, &[&username.to_owned()]) + self.execute_sql_with_return(&sql, &[&username.to_value()]) + .map(|rows| { + rows.iter() + .map(|row| { + Role { + role_name: row.get("role_name").expect("role_name"), + } + }) + .collect() + }) } - fn get_database_name(&self, em: &EntityManager) -> Result, DbError> { + fn get_database_name(&mut self) -> Result, DbError> { let sql = "SELECT current_database() AS name, description FROM pg_database LEFT JOIN pg_shdescription ON objoid = pg_database.oid WHERE datname = current_database()"; - em.execute_sql_with_one_return(&sql, &[]).map(Some) + let mut database_names: Vec> = + self.execute_sql_with_return(&sql, &[]).map(|rows| { + rows.iter() + .map(|row| { + row.get_opt("name").expect("must not error").map(|name| { + DatabaseName { + name, + description: None, + } + }) + }) + .collect() + })?; + + if database_names.len() > 0 { + Ok(database_names.remove(0)) + } else { + Ok(None) + } } } + fn to_pg_values<'a>(values: &[&'a Value]) -> Vec> { values.iter().map(|v| PgValue(v)).collect() } @@ -458,7 +506,7 @@ mod test { fn test_character_array_data_type() { let db_url = "postgres://postgres:p0stgr3s@localhost:5432/sakila"; let mut pool = Pool::new(); - let dm = pool.dm(db_url).unwrap(); + let mut dm = pool.dm(db_url).unwrap(); let sql = format!("SELECT language_id, name FROM language",); let languages: Result = dm.execute_sql_with_return(&sql, &[]); println!("languages: {:#?}", languages); @@ -469,7 +517,7 @@ mod test { fn test_ts_vector() { let db_url = "postgres://postgres:p0stgr3s@localhost:5432/sakila"; let mut pool = Pool::new(); - let dm = pool.dm(db_url).unwrap(); + let mut dm = pool.dm(db_url).unwrap(); let sql = format!("SELECT film_id, title, fulltext::text FROM film LIMIT 40",); let films: Result = dm.execute_sql_with_return(&sql, &[]); println!("film: {:#?}", films); @@ -522,10 +570,10 @@ mod test { fn test_unknown_type() { let mut pool = Pool::new(); let db_url = "postgres://postgres:p0stgr3s@localhost/sakila"; - let db = pool.db(db_url).unwrap(); + let mut db = pool.db(db_url).unwrap(); let values: Vec = vec!["hi".into(), true.into(), 42.into(), 1.0.into()]; let bvalues: Vec<&Value> = values.iter().collect(); - let rows: Result = (&db).execute_sql_with_return( + let rows: Result = db.execute_sql_with_return( "select 'Hello', $1::TEXT, $2::BOOL, $3::INT, $4::FLOAT", &bvalues, ); @@ -537,10 +585,10 @@ mod test { fn test_unknown_type_i32_f32() { let mut pool = Pool::new(); let db_url = "postgres://postgres:p0stgr3s@localhost/sakila"; - let db = pool.db(db_url).unwrap(); + let mut db = pool.db(db_url).unwrap(); let values: Vec = vec![42.into(), 1.0.into()]; let bvalues: Vec<&Value> = values.iter().collect(); - let rows: Result = (&db).execute_sql_with_return("select $1, $2", &bvalues); + let rows: Result = db.execute_sql_with_return("select $1, $2", &bvalues); info!("rows: {:#?}", rows); assert!(!rows.is_ok()); } @@ -549,10 +597,10 @@ mod test { fn using_values() { let mut pool = Pool::new(); let db_url = "postgres://postgres:p0stgr3s@localhost/sakila"; - let db = pool.db(db_url).unwrap(); + let mut db = pool.db(db_url).unwrap(); let values: Vec = vec!["hi".into(), true.into(), 42.into(), 1.0.into()]; let bvalues: Vec<&Value> = values.iter().collect(); - let rows: Result = (&db).execute_sql_with_return( + let rows: Result = db.execute_sql_with_return( "select 'Hello'::TEXT, $1::TEXT, $2::BOOL, $3::INT, $4::FLOAT", &bvalues, ); @@ -580,8 +628,8 @@ mod test { fn with_nulls() { let mut pool = Pool::new(); let db_url = "postgres://postgres:p0stgr3s@localhost/sakila"; - let db = pool.db(db_url).unwrap(); - let rows:Result = (&db).execute_sql_with_return("select 'rust'::TEXT AS name, NULL::TEXT AS schedule, NULL::TEXT AS specialty from actor", &[]); + let mut db = pool.db(db_url).unwrap(); + let rows:Result = db.execute_sql_with_return("select 'rust'::TEXT AS name, NULL::TEXT AS schedule, NULL::TEXT AS specialty from actor", &[]); info!("columns: {:#?}", rows); assert!(rows.is_ok()); if let Ok(rows) = rows { @@ -606,7 +654,7 @@ mod test { fn test_get_users() { let mut pool = Pool::new(); let db_url = "postgres://postgres:p0stgr3s@localhost/sakila"; - let em = pool.em(db_url).unwrap(); + let mut em = pool.em(db_url).unwrap(); let users = em.get_users(); info!("users: {:#?}", users); assert!(users.is_ok()); diff --git a/src/pg/table_info.rs b/src/pg/table_info.rs index 7ed691e..1d80349 100644 --- a/src/pg/table_info.rs +++ b/src/pg/table_info.rs @@ -12,15 +12,17 @@ use crate::{ }, Column, ColumnName, + Database, DbError, - EntityManager, FromDao, TableName, + Value, }; use log::*; +use rustorm_dao::value::ToValue; /// get all database tables and views except from special schema -pub fn get_all_tables(em: &EntityManager) -> Result, DbError> { +pub fn get_all_tables(db: &mut dyn Database) -> Result, DbError> { #[derive(Debug, FromDao)] struct TableNameSimple { name: String, @@ -49,15 +51,24 @@ pub fn get_all_tables(em: &EntityManager) -> Result, DbError> { ) ORDER BY nspname, relname "#; - let tablenames_simple: Result, DbError> = - em.execute_sql_with_return(sql, &[]); + + let rows = db.execute_sql_with_return(sql, &[]); + let tablenames_simple: Result, DbError> = rows.map(|rows| { + rows.iter() + .map(|row| { + let name: String = row.get("name").expect("must have table name"); + let schema: String = row.get("schema").expect("must have schema"); + TableNameSimple { name, schema } + }) + .collect() + }); match tablenames_simple { Ok(simples) => { let mut tables = Vec::with_capacity(simples.len()); for simple in simples { let tablename = simple.to_tablename(); info!(" {}", tablename.complete_name()); - let table: Result = get_table(em, &tablename); + let table: Result = get_table(db, &tablename); match table { Ok(table) => { tables.push(table); @@ -88,7 +99,7 @@ impl TableKind { /// get all database tables or views from this schema fn get_schema_tables( - em: &EntityManager, + db: &mut dyn Database, schema: &str, kind: &TableKind, ) -> Result, DbError> { @@ -117,8 +128,20 @@ fn get_schema_tables( AND pg_namespace.nspname = $1 ORDER BY relname "#; - let tablenames_simple: Result, DbError> = - em.execute_sql_with_return(sql, &[&schema, &kind.to_sql_char()]); + let tablenames_simple: Result, DbError> = db + .execute_sql_with_return(sql, &[ + &Value::Text(schema.to_string()), + &Value::Char(kind.to_sql_char()), + ]) + .map(|rows| { + rows.iter() + .map(|row| { + let name: String = row.get("name").expect("must have table name"); + let schema: String = row.get("schema").expect("must have schema"); + TableNameSimple { name, schema } + }) + .collect() + }); match tablenames_simple { Ok(simples) => { let mut table_names = Vec::with_capacity(simples.len()); @@ -134,11 +157,7 @@ fn get_schema_tables( /// get all user created schema /// special tables such as: information_schema, pg_catalog, pg_toast, pg_temp_1, pg_toast_temp_1, /// etc. are excluded -fn get_schemas(em: &EntityManager) -> Result, DbError> { - #[derive(Debug, FromDao)] - struct SchemaSimple { - schema: String, - } +fn get_schemas(db: &mut dyn Database) -> Result, DbError> { let sql = r#"SELECT pg_namespace.nspname AS schema FROM pg_namespace @@ -148,19 +167,22 @@ fn get_schemas(em: &EntityManager) -> Result, DbError> { AND pg_namespace.nspname NOT LIKE 'pg_toast_temp_%' ORDER BY nspname "#; - let schema_simples: Result, DbError> = em.execute_sql_with_return(sql, &[]); - schema_simples.map(|simple| simple.iter().map(|s| s.schema.to_string()).collect()) + db.execute_sql_with_return(sql, &[]).map(|rows| { + rows.iter() + .map(|row| row.get("schema").expect("must have schema")) + .collect() + }) } /// get the table and views of this database organized per schema -pub fn get_organized_tables(em: &EntityManager) -> Result, DbError> { - let schemas = get_schemas(em); +pub fn get_organized_tables(db: &mut dyn Database) -> Result, DbError> { + let schemas = get_schemas(db); match schemas { Ok(schemas) => { let mut contents = Vec::with_capacity(schemas.len()); for schema in schemas { - let tables = get_schema_tables(em, &schema, &TableKind::Table)?; - let views = get_schema_tables(em, &schema, &TableKind::View)?; + let tables = get_schema_tables(db, &schema, &TableKind::Table)?; + let views = get_schema_tables(db, &schema, &TableKind::View)?; info!("views: {:#?}", views); contents.push(SchemaContent { schema: schema.to_string(), @@ -175,7 +197,7 @@ pub fn get_organized_tables(em: &EntityManager) -> Result, Db } /// get the table definition, its columns and table_keys -pub fn get_table(em: &EntityManager, table_name: &TableName) -> Result { +pub fn get_table(db: &mut dyn Database, table_name: &TableName) -> Result { #[derive(Debug, FromDao)] struct TableSimple { name: String, @@ -217,10 +239,27 @@ pub fn get_table(em: &EntityManager, table_name: &TableName) -> Result "public".to_string(), }; - let table_simple: TableSimple = - em.execute_sql_with_one_return(&sql, &[&table_name.name, &schema])?; - let columns: Vec = column_info::get_columns(em, table_name)?; - let keys: Vec = get_table_key(em, table_name)?; + let mut table_simples: Vec = db + .execute_sql_with_return(&sql, &[ + &Value::Text(table_name.name.to_string()), + &Value::Text(schema), + ]) + .map(|rows| { + rows.iter() + .map(|row| { + TableSimple { + name: row.get("name").expect("must have a table name"), + schema: row.get("schema").expect("must have a schema"), + comment: row.get_opt("comment").expect("must not error"), + is_view: row.get("is_view").expect("must have is_view"), + } + }) + .collect() + })?; + assert_eq!(table_simples.len(), 1); + let table_simple = table_simples.remove(0); + let columns: Vec = column_info::get_columns(db, table_name)?; + let keys: Vec = get_table_key(db, table_name)?; let table: Table = table_simple.to_table(columns, keys); Ok(table) } @@ -242,7 +281,7 @@ impl ColumnNameSimple { /// get the column names involved in a Primary key or unique key fn get_columnname_from_key( - em: &EntityManager, + db: &mut dyn Database, key_name: &str, table_name: &TableName, ) -> Result, DbError> { @@ -265,8 +304,21 @@ fn get_columnname_from_key( None => "public".to_string(), }; - let column_name_simple: Result, DbError> = - em.execute_sql_with_return(&sql, &[&key_name, &table_name.name, &schema]); + let column_name_simple: Result, DbError> = db + .execute_sql_with_return(&sql, &[ + &key_name.to_value(), + &table_name.name.to_value(), + &schema.to_value(), + ]) + .map(|rows| { + rows.iter() + .map(|row| { + ColumnNameSimple { + column: row.get("column").expect("a column"), + } + }) + .collect() + }); match column_name_simple { Ok(column_name_simple) => { let mut column_names = vec![]; @@ -281,7 +333,7 @@ fn get_columnname_from_key( } /// get the Primary keys, Unique keys of this table -fn get_table_key(em: &EntityManager, table_name: &TableName) -> Result, DbError> { +fn get_table_key(db: &mut dyn Database, table_name: &TableName) -> Result, DbError> { #[derive(Debug, FromDao)] struct TableKeySimple { key_name: String, @@ -291,26 +343,26 @@ fn get_table_key(em: &EntityManager, table_name: &TableName) -> Result TableKey { + fn to_table_key(&self, db: &mut dyn Database, table_name: &TableName) -> TableKey { if self.is_primary_key { let primary = Key { name: Some(self.key_name.to_owned()), - columns: get_columnname_from_key(em, &self.key_name, table_name).unwrap(), + columns: get_columnname_from_key(db, &self.key_name, table_name).unwrap(), }; TableKey::PrimaryKey(primary) } else if self.is_unique_key { let unique = Key { name: Some(self.key_name.to_owned()), - columns: get_columnname_from_key(em, &self.key_name, table_name).unwrap(), + columns: get_columnname_from_key(db, &self.key_name, table_name).unwrap(), }; TableKey::UniqueKey(unique) } else if self.is_foreign_key { - let foreign_key = get_foreign_key(em, &self.key_name, table_name).unwrap(); + let foreign_key = get_foreign_key(db, &self.key_name, table_name).unwrap(); TableKey::ForeignKey(foreign_key) } else { let key = table::Key { name: Some(self.key_name.to_owned()), - columns: get_columnname_from_key(em, &self.key_name, table_name).unwrap(), + columns: get_columnname_from_key(db, &self.key_name, table_name).unwrap(), }; TableKey::Key(key) } @@ -338,13 +390,25 @@ fn get_table_key(em: &EntityManager, table_name: &TableName) -> Result "public".to_string(), }; - let table_key_simple: Result, DbError> = - em.execute_sql_with_return(&sql, &[&table_name.name, &schema]); + let table_key_simple: Result, DbError> = db + .execute_sql_with_return(&sql, &[&table_name.name.to_value(), &schema.to_value()]) + .map(|rows| { + rows.iter() + .map(|row| { + TableKeySimple { + key_name: row.get("key_name").expect("a key_name"), + is_primary_key: row.get("is_primary_key").expect("is_primary_key"), + is_unique_key: row.get("is_unique_key").expect("is_unique_key"), + is_foreign_key: row.get("is_foreign_key").expect("is_foreign_key"), + } + }) + .collect() + }); match table_key_simple { Ok(table_key_simple) => { let mut table_keys = vec![]; for simple in table_key_simple { - let table_key = simple.to_table_key(em, table_name); + let table_key = simple.to_table_key(db, table_name); table_keys.push(table_key); } Ok(table_keys) @@ -355,7 +419,7 @@ fn get_table_key(em: &EntityManager, table_name: &TableName) -> Result Result { @@ -392,13 +456,25 @@ fn get_foreign_key( WHERE pg_constraint.conname = $1 "#; - let foreign_key_simple: Result = - em.execute_sql_with_one_return(&sql, &[&foreign_key]); - + let foreign_key_simple: Result, DbError> = db + .execute_sql_with_return(&sql, &[&foreign_key.to_value()]) + .map(|rows| { + rows.iter() + .map(|row| { + ForeignKeySimple { + key_name: row.get("key_name").expect("key_name"), + foreign_table: row.get("foreign_table").expect("foreign_table"), + foreign_schema: row.get_opt("foreign_schema").expect("foreign_schema"), + } + }) + .collect() + }); match foreign_key_simple { - Ok(simple) => { - let columns: Vec = get_columnname_from_key(em, foreign_key, table_name)?; - let referred_columns: Vec = get_referred_foreign_columns(em, foreign_key)?; + Ok(mut simple) => { + assert_eq!(simple.len(), 1); + let simple = simple.remove(0); + let columns: Vec = get_columnname_from_key(db, foreign_key, table_name)?; + let referred_columns: Vec = get_referred_foreign_columns(db, foreign_key)?; let foreign = simple.to_foreign_key(columns, referred_columns); Ok(foreign) } @@ -407,7 +483,7 @@ fn get_foreign_key( } fn get_referred_foreign_columns( - em: &EntityManager, + db: &mut dyn Database, foreign_key: &str, ) -> Result, DbError> { let sql = r#"SELECT DISTINCT conname AS key_name, @@ -421,8 +497,17 @@ fn get_referred_foreign_columns( WHERE pg_constraint.conname = $1 "#; - let foreign_columns: Result, DbError> = - em.execute_sql_with_return(&sql, &[&foreign_key]); + let foreign_columns: Result, DbError> = db + .execute_sql_with_return(&sql, &[&foreign_key.to_value()]) + .map(|rows| { + rows.iter() + .map(|row| { + ColumnNameSimple { + column: row.get("column").expect("a column"), + } + }) + .collect() + }); match foreign_columns { Ok(foreign_columns) => { let mut column_names = vec![]; @@ -449,10 +534,10 @@ mod test { fn all_schemas() { let db_url = "postgres://postgres:p0stgr3s@localhost:5432/sakila"; let mut pool = Pool::new(); - let em = pool.em(db_url); - assert!(em.is_ok()); - let em = em.unwrap(); - let schemas = get_schemas(&em); + let db = pool.db(db_url); + assert!(db.is_ok()); + let mut db = db.unwrap(); + let schemas = get_schemas(&mut *db); info!("schemas: {:#?}", schemas); assert!(schemas.is_ok()); let schemas = schemas.unwrap(); @@ -463,10 +548,10 @@ mod test { fn all_tables() { let db_url = "postgres://postgres:p0stgr3s@localhost:5432/sakila"; let mut pool = Pool::new(); - let em = pool.em(db_url); - assert!(em.is_ok()); - let em = em.unwrap(); - let tables = get_all_tables(&em); + let db = pool.db(db_url); + assert!(db.is_ok()); + let mut db = db.unwrap(); + let tables = get_all_tables(&mut *db); info!("tables: {:#?}", tables); assert!(tables.is_ok()); assert_eq!(30, tables.unwrap().len()); @@ -476,11 +561,11 @@ mod test { fn table_actor() { let db_url = "postgres://postgres:p0stgr3s@localhost:5432/sakila"; let mut pool = Pool::new(); - let em = pool.em(db_url); - assert!(em.is_ok()); - let em = em.unwrap(); + let db = pool.db(db_url); + assert!(db.is_ok()); + let mut db = db.unwrap(); let table = TableName::from("actor"); - let table = get_table(&em, &table); + let table = get_table(&mut *db, &table); info!("table: {:#?}", table); assert!(table.is_ok()); assert_eq!(table.unwrap().table_key, vec![TableKey::PrimaryKey(Key { @@ -497,11 +582,11 @@ mod test { fn foreign_key_with_different_referred_column() { let db_url = "postgres://postgres:p0stgr3s@localhost:5432/sakila"; let mut pool = Pool::new(); - let em = pool.em(db_url); - assert!(em.is_ok()); - let em = em.unwrap(); + let db = pool.db(db_url); + assert!(db.is_ok()); + let mut db = db.unwrap(); let table = TableName::from("store"); - let table = get_table(&em, &table); + let table = get_table(&mut *db, &table); info!("table: {:#?}", table); assert!(table.is_ok()); assert_eq!(table.unwrap().table_key, vec![ @@ -556,11 +641,11 @@ mod test { fn table_film_actor() { let db_url = "postgres://postgres:p0stgr3s@localhost:5432/sakila"; let mut pool = Pool::new(); - let em = pool.em(db_url); - assert!(em.is_ok()); - let em = em.unwrap(); + let db = pool.db(db_url); + assert!(db.is_ok()); + let mut db = db.unwrap(); let table = TableName::from("film_actor"); - let table = get_table(&em, &table); + let table = get_table(&mut *db, &table); info!("table: {:#?}", table); assert!(table.is_ok()); assert_eq!(table.unwrap().table_key, vec![ @@ -622,11 +707,11 @@ mod test { fn composite_foreign_key() { let db_url = "postgres://postgres:p0stgr3s@localhost:5432/sakila"; let mut pool = Pool::new(); - let em = pool.em(db_url); - assert!(em.is_ok()); - let em = em.unwrap(); + let db = pool.db(db_url); + assert!(db.is_ok()); + let mut db = db.unwrap(); let table = TableName::from("film_actor_awards"); - let table = get_table(&em, &table); + let table = get_table(&mut *db, &table); info!("table: {:#?}", table); assert!(table.is_ok()); assert_eq!(table.unwrap().table_key, vec![ @@ -689,10 +774,10 @@ mod test { fn organized_content() { let db_url = "postgres://postgres:p0stgr3s@localhost:5432/sakila"; let mut pool = Pool::new(); - let em = pool.em(db_url); - assert!(em.is_ok()); - let em = em.unwrap(); - let organized = get_organized_tables(&em); + let db = pool.db(db_url); + assert!(db.is_ok()); + let mut db = db.unwrap(); + let organized = get_organized_tables(&mut *db); //info!("organized: {:#?}", organized); assert!(organized.is_ok()); let organized = organized.unwrap(); diff --git a/src/platform.rs b/src/platform.rs index 71eba7f..a1d0ce7 100644 --- a/src/platform.rs +++ b/src/platform.rs @@ -3,13 +3,13 @@ use crate::{ Database, }; use cfg_if::cfg_if; -use log::*; use std::{ convert::TryFrom, ops::Deref, }; use url::Url; + cfg_if! {if #[cfg(feature = "with-postgres")]{ use crate::pg::PostgresDB; }} @@ -18,12 +18,18 @@ cfg_if! {if #[cfg(feature = "with-sqlite")]{ use crate::sqlite::SqliteDB; }} +cfg_if! {if #[cfg(feature = "with-mysql")]{ + use crate::my::MysqlDB; +}} + pub enum DBPlatform { #[cfg(feature = "with-postgres")] Postgres(Box), #[cfg(feature = "with-sqlite")] Sqlite(Box), + #[cfg(feature = "with-mysql")] + Mysql(Box), } impl Deref for DBPlatform { @@ -34,11 +40,25 @@ impl Deref for DBPlatform { #[cfg(feature = "with-postgres")] DBPlatform::Postgres(ref pg) => pg.deref(), #[cfg(feature = "with-sqlite")] - DBPlatform::Sqlite(ref sqlite) => sqlite.deref(), + DBPlatform::Sqlite(ref sq) => sq.deref(), + #[cfg(feature = "with-mysql")] + DBPlatform::Mysql(ref my) => my.deref(), } } } +impl std::ops::DerefMut for DBPlatform { + fn deref_mut(&mut self) -> &mut Self::Target { + match *self { + #[cfg(feature = "with-postgres")] + DBPlatform::Postgres(ref mut pg) => pg.deref_mut(), + #[cfg(feature = "with-sqlite")] + DBPlatform::Sqlite(ref mut sq) => sq.deref_mut(), + #[cfg(feature = "with-mysql")] + DBPlatform::Mysql(ref mut my) => my.deref_mut(), + } + } +} pub(crate) enum Platform { #[cfg(feature = "with-postgres")] diff --git a/src/platform_mut.rs b/src/platform_mut.rs deleted file mode 100644 index 00e5a7b..0000000 --- a/src/platform_mut.rs +++ /dev/null @@ -1,36 +0,0 @@ -use crate::DatabaseMut; -use cfg_if::cfg_if; - -use std::ops::Deref; - - - -cfg_if! {if #[cfg(feature = "with-mysql")]{ - use crate::my::MysqlDB; -}} - - -pub enum DBPlatformMut { - #[cfg(feature = "with-mysql")] - Mysql(Box), -} - -impl Deref for DBPlatformMut { - type Target = dyn DatabaseMut; - - fn deref(&self) -> &Self::Target { - match *self { - #[cfg(feature = "with-mysql")] - DBPlatformMut::Mysql(ref my) => my.deref(), - } - } -} - -impl std::ops::DerefMut for DBPlatformMut { - fn deref_mut(&mut self) -> &mut Self::Target { - match *self { - #[cfg(feature = "with-mysql")] - DBPlatformMut::Mysql(ref mut my) => my.deref_mut(), - } - } -} diff --git a/src/pool.rs b/src/pool.rs index 99d1998..594b85b 100644 --- a/src/pool.rs +++ b/src/pool.rs @@ -21,15 +21,11 @@ use crate::{ ConnectError, ParseError, }, - platform::{ - DBPlatform, - Platform, - }, - platform_mut::DBPlatformMut, + platform::Platform, + DBPlatform, DaoManager, DbError, EntityManager, - EntityManagerMut, }; use std::{ collections::BTreeMap, @@ -177,35 +173,14 @@ impl Pool { } } - /// get a database instance with a connection, ready to send sql statements - pub fn db(&mut self, db_url: &str) -> Result { - let pooled_conn = self.connect(db_url)?; - match pooled_conn { - #[cfg(feature = "with-postgres")] - PooledConn::PooledPg(pooled_pg) => { - Ok(DBPlatform::Postgres(Box::new(PostgresDB(*pooled_pg)))) - } - #[cfg(feature = "with-sqlite")] - PooledConn::PooledSq(pooled_sq) => { - Ok(DBPlatform::Sqlite(Box::new(SqliteDB(*pooled_sq)))) - } - #[cfg(feature = "with-mysql")] - _ => panic!("mysql unsupported in `db()`"), - } - } - - pub fn em(&mut self, db_url: &str) -> Result { - let db = self.db(db_url)?; - Ok(EntityManager(db)) - } - pub fn dm(&mut self, db_url: &str) -> Result { let db = self.db(db_url)?; Ok(DaoManager(db)) } /// get the pool for this specific db_url, create one if it doesn't have yet. - fn get_pool_mut(&self, db_url: &str) -> Result<&ConnPool, DbError> { + fn get_pool_mut(&mut self, db_url: &str) -> Result<&ConnPool, DbError> { + self.ensure(db_url)?; let platform: Result = TryFrom::try_from(db_url); match platform { Ok(platform) => { @@ -249,7 +224,7 @@ impl Pool { } /// get a usable database connection from - pub fn connect_mut(&self, db_url: &str) -> Result { + pub fn connect_mut(&mut self, db_url: &str) -> Result { let pool = self.get_pool_mut(db_url)?; match *pool { #[cfg(feature = "with-postgres")] @@ -280,22 +255,26 @@ impl Pool { } /// get a database instance with a connection, ready to send sql statements - pub fn db_mut(&self, db_url: &str) -> Result { + pub fn db(&mut self, db_url: &str) -> Result { let pooled_conn = self.connect_mut(db_url)?; - #[allow(unreachable_patterns)] match pooled_conn { - #[cfg(feature = "with-mysql")] - PooledConn::PooledMy(pooled_sq) => { - Ok(DBPlatformMut::Mysql(Box::new(MysqlDB(*pooled_sq)))) + #[cfg(feature = "with-postgres")] + PooledConn::PooledPg(pooled_pg) => { + Ok(DBPlatform::Postgres(Box::new(PostgresDB(*pooled_pg)))) } - _ => panic!("postgres and sqlite unsupported in `db_mut()`"), + #[cfg(feature = "with-sqlite")] + PooledConn::PooledSq(pooled_sq) => { + Ok(DBPlatform::Sqlite(Box::new(SqliteDB(*pooled_sq)))) + } + #[cfg(feature = "with-mysql")] + PooledConn::PooledMy(pooled_my) => Ok(DBPlatform::Mysql(Box::new(MysqlDB(*pooled_my)))), } } - pub fn em_mut(&self, db_url: &str) -> Result { - let db = self.db_mut(db_url)?; - Ok(EntityManagerMut(db)) + pub fn em(&mut self, db_url: &str) -> Result { + let db = self.db(db_url)?; + Ok(EntityManager(db)) } } diff --git a/src/sqlite/mod.rs b/src/sqlite/mod.rs index be74205..8a1bf0d 100644 --- a/src/sqlite/mod.rs +++ b/src/sqlite/mod.rs @@ -24,11 +24,11 @@ use crate::{ Database, DatabaseName, DbError, - EntityManager, FromDao, Rows, Table, TableName, + ToValue, Value, }; use log::*; @@ -97,9 +97,8 @@ fn to_sq_values(params: &[&Value]) -> Vec { sql_values } - impl Database for SqliteDB { - fn execute_sql_with_return(&self, sql: &str, params: &[&Value]) -> Result { + fn execute_sql_with_return(&mut self, sql: &str, params: &[&Value]) -> Result { info!("executing sql: {}", sql); info!("params: {:?}", params); let stmt = self.0.prepare(&sql); @@ -146,7 +145,7 @@ impl Database for SqliteDB { } #[allow(unused_variables)] - fn get_table(&self, em: &EntityManager, table_name: &TableName) -> Result { + fn get_table(&mut self, table_name: &TableName) -> Result { #[derive(Debug)] struct ColumnSimple { name: String, @@ -359,7 +358,7 @@ impl Database for SqliteDB { columns: primary_columns, }; info!("primary key: {:#?}", primary_key); - let foreign_keys = get_foreign_keys(em, table_name)?; + let foreign_keys = get_foreign_keys(&mut *self, table_name)?; let table_key_foreign: Vec = foreign_keys.into_iter().map(TableKey::ForeignKey).collect(); let mut table_keys = vec![TableKey::PrimaryKey(primary_key)]; @@ -374,25 +373,33 @@ impl Database for SqliteDB { Ok(table) } - fn get_all_tables(&self, em: &EntityManager) -> Result, DbError> { + fn get_all_tables(&mut self) -> Result, DbError> { #[derive(Debug, FromDao)] struct TableNameSimple { tbl_name: String, } let sql = "SELECT tbl_name FROM sqlite_master WHERE type IN ('table', 'view')"; - let result: Vec = em.execute_sql_with_return(sql, &[])?; + let result: Vec = self + .execute_sql_with_return(sql, &[])? + .iter() + .map(|row| { + TableNameSimple { + tbl_name: row.get("tbl_name").expect("tbl_name"), + } + }) + .collect(); let mut tables = vec![]; for r in result { let table_name = TableName::from(&r.tbl_name); - let table = em.get_table(&table_name)?; + let table = self.get_table(&table_name)?; tables.push(table); } Ok(tables) } - fn get_grouped_tables(&self, em: &EntityManager) -> Result, DbError> { - let table_names = get_table_names(em, &"table".to_string())?; - let view_names = get_table_names(em, &"view".to_string())?; + fn get_grouped_tables(&mut self) -> Result, DbError> { + let table_names = get_table_names(&mut *self, &"table".to_string())?; + let view_names = get_table_names(&mut *self, &"view".to_string())?; let schema_content = SchemaContent { schema: "".to_string(), tablenames: table_names, @@ -402,32 +409,40 @@ impl Database for SqliteDB { } /// there are no users in sqlite - fn get_users(&self, _em: &EntityManager) -> Result, DbError> { + fn get_users(&mut self) -> Result, DbError> { Err(DbError::UnsupportedOperation( "sqlite doesn't have operatio to extract users".to_string(), )) } /// there are not roles in sqlite - fn get_roles(&self, _em: &EntityManager, _username: &str) -> Result, DbError> { + fn get_roles(&mut self, _username: &str) -> Result, DbError> { Err(DbError::UnsupportedOperation( "sqlite doesn't have operatio to extract roles".to_string(), )) } /// TODO: return the filename if possible - fn get_database_name(&self, _em: &EntityManager) -> Result, DbError> { - Ok(None) - } + fn get_database_name(&mut self) -> Result, DbError> { Ok(None) } } -fn get_table_names(em: &EntityManager, kind: &str) -> Result, DbError> { + + +fn get_table_names(db: &mut dyn Database, kind: &str) -> Result, DbError> { #[derive(Debug, FromDao)] struct TableNameSimple { tbl_name: String, } let sql = "SELECT tbl_name FROM sqlite_master WHERE type = ?"; - let result: Vec = em.execute_sql_with_return(sql, &[&kind.to_string()])?; + let result: Vec = db + .execute_sql_with_return(sql, &[&kind.to_value()])? + .iter() + .map(|row| { + TableNameSimple { + tbl_name: row.get("tbl_name").expect("tbl_name"), + } + }) + .collect(); let mut table_names = vec![]; for r in result { let table_name = TableName::from(&r.tbl_name); @@ -437,7 +452,7 @@ fn get_table_names(em: &EntityManager, kind: &str) -> Result, DbE } /// get the foreign keys of table -fn get_foreign_keys(em: &EntityManager, table: &TableName) -> Result, DbError> { +fn get_foreign_keys(db: &mut dyn Database, table: &TableName) -> Result, DbError> { let sql = format!("PRAGMA foreign_key_list({});", table.complete_name()); #[derive(Debug, FromDao)] struct ForeignSimple { @@ -446,7 +461,18 @@ fn get_foreign_keys(em: &EntityManager, table: &TableName) -> Result = em.execute_sql_with_return(&sql, &[])?; + let result: Vec = db + .execute_sql_with_return(&sql, &[])? + .iter() + .map(|row| { + ForeignSimple { + id: row.get("id").expect("id"), + table: row.get("table").expect("table"), + from: row.get("from").expect("from"), + to: row.get("to").expect("to"), + } + }) + .collect(); let mut foreign_tables: Vec<(i64, TableName)> = result .iter() .map(|f| (f.id, TableName::from(&f.table))) @@ -508,10 +534,10 @@ mod test { fn test_get_all_tables() { let db_url = "sqlite://sakila.db"; let mut pool = Pool::new(); - let em = pool.em(db_url); - assert!(em.is_ok()); - let em = em.unwrap(); - let all_tables = em.get_all_tables(); + let db = pool.db(db_url); + assert!(db.is_ok()); + let mut db = db.unwrap(); + let all_tables = db.get_all_tables(); assert!(all_tables.is_ok()); let all_tables = all_tables.unwrap(); assert_eq!(all_tables.len(), 22); @@ -521,10 +547,10 @@ mod test { fn test_get_group_table() { let db_url = "sqlite://sakila.db"; let mut pool = Pool::new(); - let em = pool.em(db_url); - assert!(em.is_ok()); - let em = em.unwrap(); - let schema_content = em.get_grouped_tables(); + let db = pool.db(db_url); + assert!(db.is_ok()); + let mut db = db.unwrap(); + let schema_content = db.get_grouped_tables(); assert!(schema_content.is_ok()); let schema_content = schema_content.unwrap(); let schema_content = &schema_content[0]; @@ -546,12 +572,12 @@ mod test { fn test_get_table() { let db_url = "sqlite://sakila.db"; let mut pool = Pool::new(); - let em = pool.em(db_url); - assert!(em.is_ok()); - let em = em.unwrap(); + let db = pool.db(db_url); + assert!(db.is_ok()); + let mut db = db.unwrap(); let film = "film"; let film_table = TableName::from(film); - let table = em.get_table(&film_table); + let table = db.get_table(&film_table); assert!(table.is_ok()); let table = table.unwrap(); info!("table: {:#?}", table); @@ -752,12 +778,12 @@ mod test { fn test_get_table2() { let db_url = "sqlite://sakila.db"; let mut pool = Pool::new(); - let em = pool.em(db_url); - assert!(em.is_ok()); - let em = em.unwrap(); + let db = pool.db(db_url); + assert!(db.is_ok()); + let mut db = db.unwrap(); let table = "actor"; let table_name = TableName::from(table); - let table = em.get_table(&table_name); + let table = db.get_table(&table_name); assert!(table.is_ok()); let table = table.unwrap(); info!("table: {:#?}", table); @@ -862,12 +888,12 @@ mod test { fn test_get_table3() { let db_url = "sqlite://sakila.db"; let mut pool = Pool::new(); - let em = pool.em(db_url); - assert!(em.is_ok()); - let em = em.unwrap(); + let db = pool.db(db_url); + assert!(db.is_ok()); + let mut db = db.unwrap(); let table = "film_actor"; let table_name = TableName::from(table); - let table = em.get_table(&table_name); + let table = db.get_table(&table_name); assert!(table.is_ok()); let table = table.unwrap(); info!("table: {:#?}", table); @@ -998,12 +1024,12 @@ mod test { fn test_get_foreign() { let db_url = "sqlite://sakila.db"; let mut pool = Pool::new(); - let em = pool.em(db_url); - assert!(em.is_ok()); - let em = em.unwrap(); + let db = pool.db(db_url); + assert!(db.is_ok()); + let mut db = db.unwrap(); let film = "film_actor"; let film_table = TableName::from(film); - let foreign_keys = get_foreign_keys(&em, &film_table); + let foreign_keys = get_foreign_keys(&mut *db, &film_table); assert!(foreign_keys.is_ok()); assert_eq!(foreign_keys.unwrap(), vec![ ForeignKey { @@ -1025,12 +1051,12 @@ mod test { fn test_get_foreign2() { let db_url = "sqlite://sakila.db"; let mut pool = Pool::new(); - let em = pool.em(db_url); - assert!(em.is_ok()); - let em = em.unwrap(); + let db = pool.db(db_url); + assert!(db.is_ok()); + let mut db = db.unwrap(); let film = "film"; let film_table = TableName::from(film); - let foreign_keys = get_foreign_keys(&em, &film_table); + let foreign_keys = get_foreign_keys(&mut *db, &film_table); assert!(foreign_keys.is_ok()); assert_eq!(foreign_keys.unwrap(), vec![ ForeignKey { diff --git a/src/table.rs b/src/table.rs index ddbb8a6..7bb5a71 100644 --- a/src/table.rs +++ b/src/table.rs @@ -218,12 +218,12 @@ mod test { 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 em = em.unwrap(); let film_tablename = TableName::from("public.film"); - let film = em.get_table(&film_tablename); + let film = db.get_table(&film_tablename); let film_actor_tablename = TableName::from("public.film_actor"); - let film_actor = em.get_table(&film_actor_tablename); + let film_actor = db.get_table(&film_actor_tablename); assert!(film.is_ok()); info!("film: {:#?}", film); info!("FILM ACTOR {:#?}", film_actor); @@ -247,7 +247,7 @@ mod test { let mut pool = Pool::new(); let em = pool.em(db_url); assert!(em.is_ok()); - let em = em.unwrap(); + let mut em = em.unwrap(); let hero_tablename = TableName::from("public.hero"); let hero = em.get_table(&hero_tablename); let hero_ability_tablename = TableName::from("public.hero_ability"); diff --git a/src/users/mod.rs b/src/users/mod.rs index b038e7f..6ce412d 100644 --- a/src/users/mod.rs +++ b/src/users/mod.rs @@ -14,18 +14,18 @@ mod previlege; /// This is the user object mapped from pg_authid #[derive(Debug, Serialize, Deserialize, FromDao)] pub struct User { - sysid: i32, - username: String, - password: String, - is_superuser: bool, - is_inherit: bool, - can_create_db: bool, - can_create_role: bool, - can_login: bool, - can_do_replication: bool, - can_bypass_rls: bool, - valid_until: Option>, - conn_limit: Option, + pub(crate) sysid: i32, + pub(crate) username: String, + pub(crate) password: String, + pub(crate) is_superuser: bool, + pub(crate) is_inherit: bool, + pub(crate) can_create_db: bool, + pub(crate) can_create_role: bool, + pub(crate) can_login: bool, + pub(crate) can_do_replication: bool, + pub(crate) can_bypass_rls: bool, + pub(crate) valid_until: Option>, + pub(crate) conn_limit: Option, } #[derive(Debug, Serialize, Deserialize, FromDao)]