diff --git a/Cargo.toml b/Cargo.toml index b8da7967..dda0f88c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -23,8 +23,9 @@ name = "sea_orm" path = "src/lib.rs" [dependencies] -async-trait = "^0.1" +async-stream = { version = "^0.3" } futures = { version = "^0.3" } +futures-util = { version = "^0.3" } sea-query = { path = "../sea-query", version = "^0.12" } sea-orm-macros = { path = "sea-orm-macros", optional = true } # sea-schema = { path = "../sea-schema" } @@ -32,15 +33,14 @@ serde = { version = "^1.0", features = [ "derive" ] } sqlx = { version = "^0.5", optional = true } strum = { version = "^0.20", features = [ "derive" ] } serde_json = { version = "^1", optional = true } -async-stream = { version = "^0.3" } -futures-util = { version = "^0.3" } [features] debug-print = [] -default = [ "macros", "sqlx-mysql", "runtime-async-std-native-tls", "with-json" ] +default = [ "macros", "with-json", "mock", "sqlx-mysql", "runtime-async-std-native-tls" ] macros = [ "sea-orm-macros" ] -with-json = [ "serde_json" ] -sqlx-dep = [ "sqlx" ] +mock = [] +with-json = [ "serde_json", "sea-query/with-json" ] +sqlx-dep = [ "sqlx", "sqlx/json" ] sqlx-mysql = [ "sqlx-dep", "sea-query/sqlx-mysql", "sqlx/mysql" ] sqlx-postgres = [ "sqlx-dep", "sea-query/sqlx-postgres", "sqlx/postgres" ] runtime-actix-native-tls = [ "sqlx/runtime-actix-native-tls" ] diff --git a/examples/sqlx-mysql/Cargo.toml b/examples/sqlx-mysql/Cargo.toml index 44f398b1..27c52348 100644 --- a/examples/sqlx-mysql/Cargo.toml +++ b/examples/sqlx-mysql/Cargo.toml @@ -6,7 +6,7 @@ publish = false [dependencies] async-std = { version = "^1.9", features = [ "attributes" ] } -sea-orm = { path = "../../", features = [ "sqlx-mysql", "runtime-async-std-native-tls", "debug-print" ] } +sea-orm = { path = "../../", features = [ "sqlx-mysql", "runtime-async-std-native-tls", "debug-print", "with-json", "macros" ], default-features = false } # sea-query = { path = "../../../sea-query" } strum = { version = "^0.20", features = [ "derive" ] } serde_json = { version = "^1" } diff --git a/src/database/connection.rs b/src/database/connection.rs new file mode 100644 index 00000000..85ed546c --- /dev/null +++ b/src/database/connection.rs @@ -0,0 +1,137 @@ +use crate::{ExecErr, ExecResult, QueryErr, QueryResult, Statement}; +use sea_query::{ + DeleteStatement, InsertStatement, MysqlQueryBuilder, PostgresQueryBuilder, SelectStatement, + UpdateStatement, +}; +use std::{error::Error, fmt}; + +pub enum DatabaseConnection { + #[cfg(feature = "sqlx-mysql")] + SqlxMySqlPoolConnection(crate::SqlxMySqlPoolConnection), + #[cfg(feature = "mock")] + MockDatabaseConnection(crate::MockDatabaseConnection), + Disconnected, +} + +pub enum QueryBuilderBackend { + MySql, + Postgres, +} + +#[derive(Debug)] +pub struct ConnectionErr; + +impl Error for ConnectionErr {} + +impl fmt::Display for ConnectionErr { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "{:?}", self) + } +} + +impl Default for DatabaseConnection { + fn default() -> Self { + Self::Disconnected + } +} + +impl std::fmt::Debug for DatabaseConnection { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + write!( + f, + "{}", + match self { + #[cfg(feature = "sqlx-mysql")] + Self::SqlxMySqlPoolConnection(_) => "SqlxMySqlPoolConnection", + #[cfg(feature = "mock")] + Self::MockDatabaseConnection(_) => "MockDatabaseConnection", + Self::Disconnected => "Disconnected", + } + ) + } +} + +impl DatabaseConnection { + pub fn get_query_builder_backend(&self) -> QueryBuilderBackend { + match self { + #[cfg(feature = "sqlx-mysql")] + DatabaseConnection::SqlxMySqlPoolConnection(_) => QueryBuilderBackend::MySql, + #[cfg(feature = "mock")] + DatabaseConnection::MockDatabaseConnection(_) => QueryBuilderBackend::Postgres, + DatabaseConnection::Disconnected => panic!("Disconnected"), + } + } + + pub async fn execute( + &self, + stmt: Statement, + ) -> Result { + match self { + #[cfg(feature = "sqlx-mysql")] + DatabaseConnection::SqlxMySqlPoolConnection(conn) => conn.execute(stmt).await, + #[cfg(feature = "mock")] + DatabaseConnection::MockDatabaseConnection(conn) => conn.execute(stmt).await, + DatabaseConnection::Disconnected => panic!("Disconnected"), + } + } + + pub async fn query_one( + &self, + stmt: Statement, + ) -> Result, QueryErr> { + match self { + #[cfg(feature = "sqlx-mysql")] + DatabaseConnection::SqlxMySqlPoolConnection(conn) => conn.query_one(stmt).await, + #[cfg(feature = "mock")] + DatabaseConnection::MockDatabaseConnection(conn) => conn.query_one(stmt).await, + DatabaseConnection::Disconnected => panic!("Disconnected"), + } + } + + pub async fn query_all( + &self, + stmt: Statement, + ) -> Result, QueryErr> { + match self { + #[cfg(feature = "sqlx-mysql")] + DatabaseConnection::SqlxMySqlPoolConnection(conn) => conn.query_all(stmt).await, + #[cfg(feature = "mock")] + DatabaseConnection::MockDatabaseConnection(conn) => conn.query_all(stmt).await, + DatabaseConnection::Disconnected => panic!("Disconnected"), + } + } +} + +impl QueryBuilderBackend { + pub fn build_select_statement(&self, statement: &SelectStatement) -> Statement { + match self { + Self::MySql => statement.build(MysqlQueryBuilder), + Self::Postgres => statement.build(PostgresQueryBuilder), + } + .into() + } + + pub fn build_insert_statement(&self, statement: &InsertStatement) -> Statement { + match self { + Self::MySql => statement.build(MysqlQueryBuilder), + Self::Postgres => statement.build(PostgresQueryBuilder), + } + .into() + } + + pub fn build_update_statement(&self, statement: &UpdateStatement) -> Statement { + match self { + Self::MySql => statement.build(MysqlQueryBuilder), + Self::Postgres => statement.build(PostgresQueryBuilder), + } + .into() + } + + pub fn build_delete_statement(&self, statement: &DeleteStatement) -> Statement { + match self { + Self::MySql => statement.build(MysqlQueryBuilder), + Self::Postgres => statement.build(PostgresQueryBuilder), + } + .into() + } +} diff --git a/src/database/mock.rs b/src/database/mock.rs new file mode 100644 index 00000000..8336d093 --- /dev/null +++ b/src/database/mock.rs @@ -0,0 +1,66 @@ +use crate::{ExecErr, ExecResult, MockDatabaseTrait, QueryErr, QueryResult, Statement, TypeErr}; +use sea_query::{Value, ValueType}; +use std::collections::BTreeMap; + +#[derive(Debug, Default)] +pub struct MockDatabase { + transaction_log: Vec, + exec_results: Vec, + query_results: Vec>, +} + +#[derive(Clone, Debug)] +pub struct MockExecResult { + pub last_insert_id: u64, + pub rows_affected: u64, +} + +#[derive(Clone, Debug)] +pub struct MockRow { + values: BTreeMap, +} + +impl MockDatabase { + pub fn new() -> Self { + Default::default() + } +} + +impl MockDatabaseTrait for MockDatabase { + fn execute(&mut self, counter: usize, statement: Statement) -> Result { + self.transaction_log.push(statement); + if counter < self.exec_results.len() { + Err(ExecErr) + // Ok(self.exec_results[counter].clone()) + } else { + Err(ExecErr) + } + } + + fn query( + &mut self, + counter: usize, + statement: Statement, + ) -> Result, QueryErr> { + self.transaction_log.push(statement); + if counter < self.query_results.len() { + Err(QueryErr) + // Ok(self.query_results[counter].clone()) + } else { + Err(QueryErr) + } + } +} + +impl MockRow { + pub fn try_get(&self, col: &str) -> Result + where + T: ValueType, + { + Ok(self.values.get(col).unwrap().clone().unwrap()) + } + + pub fn into_column_value_tuples(self) -> impl Iterator { + self.values.into_iter() + } +} diff --git a/src/database/mod.rs b/src/database/mod.rs index 72d694b3..894278d8 100644 --- a/src/database/mod.rs +++ b/src/database/mod.rs @@ -1,63 +1,38 @@ +mod connection; +#[cfg(feature = "mock")] +mod mock; mod statement; +pub use connection::*; +#[cfg(feature = "mock")] +pub use mock::*; pub use statement::*; -use crate::{Connection, ConnectionErr, Connector, SqlxMySqlConnector, SqlxMySqlPoolConnection}; -use sea_query::{GenericBuilder, MySqlQueryBuilder}; - #[derive(Debug, Default)] pub struct Database { connection: DatabaseConnection, } -pub enum DatabaseConnection { - SqlxMySqlPoolConnection(SqlxMySqlPoolConnection), - Disconnected, -} - -// DatabaseConnection // - -impl Default for DatabaseConnection { - fn default() -> Self { - Self::Disconnected - } -} - -impl std::fmt::Debug for DatabaseConnection { - fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { - write!( - f, - "{}", - match self { - Self::SqlxMySqlPoolConnection(_) => "SqlxMySqlPoolConnection", - Self::Disconnected => "Disconnected", - } - ) - } -} - -// Database // - impl Database { pub async fn connect(&mut self, string: &str) -> Result<(), ConnectionErr> { - if SqlxMySqlConnector::accepts(string) { - self.connection = SqlxMySqlConnector::connect(string).await?; + #[cfg(feature = "sqlx-mysql")] + if crate::SqlxMySqlConnector::accepts(string) { + self.connection = crate::SqlxMySqlConnector::connect(string).await?; + return Ok(()); + } + #[cfg(feature = "mock")] + if crate::MockDatabaseConnector::accepts(string) { + self.connection = crate::MockDatabaseConnector::connect(string).await?; return Ok(()); } Err(ConnectionErr) } - pub fn get_connection(&self) -> impl Connection + '_ { - match &self.connection { - DatabaseConnection::SqlxMySqlPoolConnection(conn) => conn, - DatabaseConnection::Disconnected => panic!("Disconnected"), - } + pub fn get_connection(&self) -> &DatabaseConnection { + &self.connection } - pub fn get_query_builder_backend(&self) -> impl GenericBuilder { - match &self.connection { - DatabaseConnection::SqlxMySqlPoolConnection(_) => MySqlQueryBuilder::default(), - DatabaseConnection::Disconnected => panic!("Disconnected"), - } + pub fn get_query_builder_backend(&self) -> QueryBuilderBackend { + self.connection.get_query_builder_backend() } } diff --git a/src/database/statement.rs b/src/database/statement.rs index c2f3c2f4..61604b38 100644 --- a/src/database/statement.rs +++ b/src/database/statement.rs @@ -1,6 +1,7 @@ use sea_query::{inject_parameters, MySqlQueryBuilder, Values}; use std::fmt; +#[derive(Debug, Clone)] pub struct Statement { pub sql: String, pub values: Option, diff --git a/src/driver/mock.rs b/src/driver/mock.rs new file mode 100644 index 00000000..9189e306 --- /dev/null +++ b/src/driver/mock.rs @@ -0,0 +1,76 @@ +use crate::{ + debug_print, ConnectionErr, DatabaseConnection, ExecErr, ExecResult, MockDatabase, QueryErr, + QueryResult, Statement, +}; +use std::sync::{ + atomic::{AtomicUsize, Ordering}, + Mutex, +}; + +pub struct MockDatabaseConnector; + +pub struct MockDatabaseConnection { + counter: AtomicUsize, + mocker: Mutex>, +} + +pub trait MockDatabaseTrait: Send { + fn execute(&mut self, counter: usize, stmt: Statement) -> Result; + + fn query(&mut self, counter: usize, stmt: Statement) -> Result, QueryErr>; +} + +impl MockDatabaseConnector { + pub fn accepts(_string: &str) -> bool { + true + } + + pub async fn connect(_string: &str) -> Result { + Ok(DatabaseConnection::MockDatabaseConnection( + MockDatabaseConnection::new(MockDatabase::new()), + )) + } +} + +impl MockDatabaseConnection { + pub fn new(m: M) -> Self + where + M: MockDatabaseTrait, + { + Self { + counter: AtomicUsize::new(0), + mocker: Mutex::new(Box::new(m)), + } + } +} + +impl MockDatabaseConnection { + pub async fn execute(&self, statement: Statement) -> Result { + debug_print!("{}", statement); + self.counter.fetch_add(1, Ordering::SeqCst); + self.mocker + .lock() + .unwrap() + .execute(self.counter.load(Ordering::SeqCst), statement) + } + + pub async fn query_one(&self, statement: Statement) -> Result, QueryErr> { + debug_print!("{}", statement); + self.counter.fetch_add(1, Ordering::SeqCst); + let result = self + .mocker + .lock() + .unwrap() + .query(self.counter.load(Ordering::SeqCst), statement)?; + Ok(result.into_iter().next()) + } + + pub async fn query_all(&self, statement: Statement) -> Result, QueryErr> { + debug_print!("{}", statement); + self.counter.fetch_add(1, Ordering::SeqCst); + self.mocker + .lock() + .unwrap() + .query(self.counter.load(Ordering::SeqCst), statement) + } +} diff --git a/src/driver/mod.rs b/src/driver/mod.rs index f49ce557..963a1597 100644 --- a/src/driver/mod.rs +++ b/src/driver/mod.rs @@ -1,3 +1,9 @@ +#[cfg(feature = "mock")] +mod mock; +#[cfg(feature = "sqlx-mysql")] mod sqlx_mysql; +#[cfg(feature = "mock")] +pub use mock::*; +#[cfg(feature = "sqlx-mysql")] pub use sqlx_mysql::*; diff --git a/src/driver/sqlx_mysql.rs b/src/driver/sqlx_mysql.rs index 80d5a7b9..a53e6142 100644 --- a/src/driver/sqlx_mysql.rs +++ b/src/driver/sqlx_mysql.rs @@ -1,4 +1,3 @@ -use async_trait::async_trait; use sqlx::{ mysql::{MySqlArguments, MySqlQueryResult, MySqlRow}, MySql, MySqlPool, @@ -7,7 +6,7 @@ use sqlx::{ sea_query::sea_query_driver_mysql!(); use sea_query_driver_mysql::bind_query; -use crate::{debug_print, executor::*, DatabaseConnection, Statement}; +use crate::{debug_print, executor::*, DatabaseConnection, Statement, ConnectionErr}; pub struct SqlxMySqlConnector; @@ -15,13 +14,12 @@ pub struct SqlxMySqlPoolConnection { pool: MySqlPool, } -#[async_trait] -impl Connector for SqlxMySqlConnector { - fn accepts(string: &str) -> bool { +impl SqlxMySqlConnector { + pub fn accepts(string: &str) -> bool { string.starts_with("mysql://") } - async fn connect(string: &str) -> Result { + pub async fn connect(string: &str) -> Result { if let Ok(pool) = MySqlPool::connect(string).await { Ok(DatabaseConnection::SqlxMySqlPoolConnection( SqlxMySqlPoolConnection { pool }, @@ -38,9 +36,8 @@ impl SqlxMySqlConnector { } } -#[async_trait] -impl Connection for &SqlxMySqlPoolConnection { - async fn execute(&self, stmt: Statement) -> Result { +impl SqlxMySqlPoolConnection { + pub async fn execute(&self, stmt: Statement) -> Result { debug_print!("{}", stmt); let query = sqlx_query(&stmt); @@ -52,7 +49,7 @@ impl Connection for &SqlxMySqlPoolConnection { Err(ExecErr) } - async fn query_one(&self, stmt: Statement) -> Result, QueryErr> { + pub async fn query_one(&self, stmt: Statement) -> Result, QueryErr> { debug_print!("{}", stmt); let query = sqlx_query(&stmt); @@ -67,7 +64,7 @@ impl Connection for &SqlxMySqlPoolConnection { } } - async fn query_all(&self, stmt: Statement) -> Result, QueryErr> { + pub async fn query_all(&self, stmt: Statement) -> Result, QueryErr> { debug_print!("{}", stmt); let query = sqlx_query(&stmt); @@ -96,6 +93,18 @@ impl From for ExecResult { } } +impl From for TypeErr { + fn from(_: sqlx::Error) -> TypeErr { + TypeErr + } +} + +impl From for ExecErr { + fn from(_: sqlx::Error) -> ExecErr { + ExecErr + } +} + fn sqlx_query(stmt: &Statement) -> sqlx::query::Query<'_, MySql, MySqlArguments> { let mut query = sqlx::query(&stmt.sql); if let Some(values) = &stmt.values { diff --git a/src/executor/connector.rs b/src/executor/connector.rs deleted file mode 100644 index 5a6daf4d..00000000 --- a/src/executor/connector.rs +++ /dev/null @@ -1,33 +0,0 @@ -use super::{ExecErr, ExecResult, QueryErr, QueryResult}; -use crate::{DatabaseConnection, Statement}; -use async_trait::async_trait; -use std::{error::Error, fmt}; - -#[async_trait] -pub trait Connector { - fn accepts(string: &str) -> bool; - - async fn connect(string: &str) -> Result; -} - -#[async_trait] -pub trait Connection { - async fn execute(&self, stmt: Statement) -> Result; - - async fn query_one(&self, stmt: Statement) -> Result, QueryErr>; - - async fn query_all(&self, stmt: Statement) -> Result, QueryErr>; -} - -#[derive(Debug)] -pub struct ConnectionErr; - -// ConnectionErr // - -impl Error for ConnectionErr {} - -impl fmt::Display for ConnectionErr { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "{:?}", self) - } -} diff --git a/src/executor/delete.rs b/src/executor/delete.rs index 2a6e8903..388f4a8c 100644 --- a/src/executor/delete.rs +++ b/src/executor/delete.rs @@ -1,7 +1,8 @@ use crate::{ - ActiveModelTrait, Connection, Database, DeleteMany, DeleteOne, EntityTrait, ExecErr, Statement, + ActiveModelTrait, Database, DeleteMany, DeleteOne, EntityTrait, ExecErr, QueryBuilderBackend, + Statement, }; -use sea_query::{DeleteStatement, QueryBuilder}; +use sea_query::DeleteStatement; use std::future::Future; #[derive(Clone, Debug)] @@ -45,11 +46,8 @@ impl Deleter { Self { query } } - pub fn build(&self, builder: B) -> Statement - where - B: QueryBuilder, - { - self.query.build(builder).into() + pub fn build(&self, builder: QueryBuilderBackend) -> Statement { + builder.build_delete_statement(&self.query) } pub fn exec(self, db: &Database) -> impl Future> + '_ { diff --git a/src/executor/execute.rs b/src/executor/execute.rs index b5b51e09..b735cce2 100644 --- a/src/executor/execute.rs +++ b/src/executor/execute.rs @@ -1,4 +1,3 @@ -use sqlx::mysql::MySqlQueryResult; use std::{error::Error, fmt}; #[derive(Debug)] @@ -8,7 +7,10 @@ pub struct ExecResult { #[derive(Debug)] pub(crate) enum ExecResultHolder { - SqlxMySql(MySqlQueryResult), + #[cfg(feature = "sqlx-mysql")] + SqlxMySql(sqlx::mysql::MySqlQueryResult), + #[cfg(feature = "mock")] + Mock(crate::MockExecResult), } #[derive(Debug)] @@ -19,13 +21,19 @@ pub struct ExecErr; impl ExecResult { pub fn last_insert_id(&self) -> u64 { match &self.result { + #[cfg(feature = "sqlx-mysql")] ExecResultHolder::SqlxMySql(result) => result.last_insert_id(), + #[cfg(feature = "mock")] + ExecResultHolder::Mock(result) => result.last_insert_id, } } pub fn rows_affected(&self) -> u64 { match &self.result { + #[cfg(feature = "sqlx-mysql")] ExecResultHolder::SqlxMySql(result) => result.rows_affected(), + #[cfg(feature = "mock")] + ExecResultHolder::Mock(result) => result.rows_affected, } } } @@ -39,9 +47,3 @@ impl fmt::Display for ExecErr { write!(f, "{:?}", self) } } - -impl From for ExecErr { - fn from(_: sqlx::Error) -> ExecErr { - ExecErr - } -} diff --git a/src/executor/insert.rs b/src/executor/insert.rs index 0488be9c..79a65970 100644 --- a/src/executor/insert.rs +++ b/src/executor/insert.rs @@ -1,5 +1,7 @@ -use crate::{ActiveModelTrait, Connection, Database, ExecErr, Insert, QueryTrait, Statement}; -use sea_query::{InsertStatement, QueryBuilder}; +use crate::{ + ActiveModelTrait, Database, ExecErr, Insert, QueryBuilderBackend, QueryTrait, Statement, +}; +use sea_query::InsertStatement; use std::future::Future; #[derive(Clone, Debug)] @@ -27,11 +29,8 @@ impl Inserter { Self { query } } - pub fn build(&self, builder: B) -> Statement - where - B: QueryBuilder, - { - self.query.build(builder).into() + pub fn build(&self, builder: QueryBuilderBackend) -> Statement { + builder.build_insert_statement(&self.query) } pub fn exec(self, db: &Database) -> impl Future> + '_ { diff --git a/src/executor/mod.rs b/src/executor/mod.rs index 7f37bcf1..4be8227e 100644 --- a/src/executor/mod.rs +++ b/src/executor/mod.rs @@ -1,4 +1,3 @@ -mod connector; mod delete; mod execute; mod insert; @@ -7,7 +6,6 @@ mod query; mod select; mod update; -pub use connector::*; pub use delete::*; pub use execute::*; pub use insert::*; diff --git a/src/executor/paginator.rs b/src/executor/paginator.rs index abc2093d..3196248e 100644 --- a/src/executor/paginator.rs +++ b/src/executor/paginator.rs @@ -1,4 +1,4 @@ -use crate::{Connection, Database, QueryErr, SelectorTrait}; +use crate::{Database, QueryErr, SelectorTrait}; use async_stream::stream; use futures::Stream; use sea_query::{Alias, Expr, SelectStatement}; @@ -31,7 +31,7 @@ where .offset((self.page_size * page) as u64) .to_owned(); let builder = self.db.get_query_builder_backend(); - let stmt = query.build(builder).into(); + let stmt = builder.build_select_statement(&query); let rows = self.db.get_connection().query_all(stmt).await?; let mut buffer = Vec::with_capacity(rows.len()); for row in rows.into_iter() { @@ -49,14 +49,14 @@ where /// Get the total number of pages pub async fn num_pages(&self) -> Result { let builder = self.db.get_query_builder_backend(); - let stmt = SelectStatement::new() - .expr(Expr::cust("COUNT(*) AS num_rows")) - .from_subquery( - self.query.clone().reset_limit().reset_offset().to_owned(), - Alias::new("sub_query"), - ) - .build(builder) - .into(); + let stmt = builder.build_select_statement( + SelectStatement::new() + .expr(Expr::cust("COUNT(*) AS num_rows")) + .from_subquery( + self.query.clone().reset_limit().reset_offset().to_owned(), + Alias::new("sub_query"), + ), + ); let result = match self.db.get_connection().query_one(stmt).await? { Some(res) => res, None => return Ok(0), diff --git a/src/executor/query.rs b/src/executor/query.rs index d6c71ad6..3f73cce4 100644 --- a/src/executor/query.rs +++ b/src/executor/query.rs @@ -1,4 +1,3 @@ -use sqlx::mysql::MySqlRow; use std::{error::Error, fmt}; #[derive(Debug)] @@ -8,7 +7,10 @@ pub struct QueryResult { #[derive(Debug)] pub(crate) enum QueryResultRow { - SqlxMySql(MySqlRow), + #[cfg(feature = "sqlx-mysql")] + SqlxMySql(sqlx::mysql::MySqlRow), + #[cfg(feature = "mock")] + Mock(crate::MockRow), } #[derive(Debug)] @@ -60,12 +62,6 @@ impl fmt::Display for TypeErr { } } -impl From for TypeErr { - fn from(_: sqlx::Error) -> TypeErr { - TypeErr - } -} - // TryGetable // macro_rules! try_getable { @@ -74,10 +70,13 @@ macro_rules! try_getable { fn try_get(res: &QueryResult, pre: &str, col: &str) -> Result { let column = format!("{}{}", pre, col); match &res.row { + #[cfg(feature = "sqlx-mysql")] QueryResultRow::SqlxMySql(row) => { use sqlx::Row; Ok(row.try_get(column.as_str())?) } + #[cfg(feature = "mock")] + QueryResultRow::Mock(row) => Ok(row.try_get(column.as_str())?), } } } @@ -86,6 +85,7 @@ macro_rules! try_getable { fn try_get(res: &QueryResult, pre: &str, col: &str) -> Result { let column = format!("{}{}", pre, col); match &res.row { + #[cfg(feature = "sqlx-mysql")] QueryResultRow::SqlxMySql(row) => { use sqlx::Row; match row.try_get(column.as_str()) { @@ -93,6 +93,11 @@ macro_rules! try_getable { Err(_) => Ok(None), } } + #[cfg(feature = "mock")] + QueryResultRow::Mock(row) => match row.try_get(column.as_str()) { + Ok(v) => Ok(Some(v)), + Err(_) => Ok(None), + }, } } } diff --git a/src/executor/select.rs b/src/executor/select.rs index b137267d..d89a92b6 100644 --- a/src/executor/select.rs +++ b/src/executor/select.rs @@ -1,8 +1,8 @@ use crate::{ - query::combine, Connection, Database, EntityTrait, FromQueryResult, JsonValue, Paginator, - QueryErr, QueryResult, Select, SelectTwo, Statement, TypeErr, + query::combine, Database, EntityTrait, FromQueryResult, JsonValue, Paginator, + QueryBuilderBackend, QueryErr, QueryResult, Select, SelectTwo, Statement, TypeErr, }; -use sea_query::{QueryBuilder, SelectStatement}; +use sea_query::SelectStatement; use std::marker::PhantomData; #[derive(Clone, Debug)] @@ -134,11 +134,8 @@ impl Selector where S: SelectorTrait, { - pub fn build(&self, builder: B) -> Statement - where - B: QueryBuilder, - { - self.query.build(builder).into() + pub fn build(&self, builder: QueryBuilderBackend) -> Statement { + builder.build_select_statement(&self.query) } pub async fn one(mut self, db: &Database) -> Result, QueryErr> { diff --git a/src/executor/update.rs b/src/executor/update.rs index 2912c751..28773534 100644 --- a/src/executor/update.rs +++ b/src/executor/update.rs @@ -1,7 +1,8 @@ use crate::{ - ActiveModelTrait, Connection, Database, EntityTrait, ExecErr, Statement, UpdateMany, UpdateOne, + ActiveModelTrait, Database, EntityTrait, ExecErr, QueryBuilderBackend, Statement, UpdateMany, + UpdateOne, }; -use sea_query::{QueryBuilder, UpdateStatement}; +use sea_query::UpdateStatement; use std::future::Future; #[derive(Clone, Debug)] @@ -42,11 +43,8 @@ impl Updater { Self { query } } - pub fn build(&self, builder: B) -> Statement - where - B: QueryBuilder, - { - self.query.build(builder).into() + pub fn build(&self, builder: QueryBuilderBackend) -> Statement { + builder.build_update_statement(&self.query) } pub fn exec(self, db: &Database) -> impl Future> + '_ { diff --git a/src/query/json.rs b/src/query/json.rs index d8a4c963..4cc07157 100644 --- a/src/query/json.rs +++ b/src/query/json.rs @@ -1,11 +1,13 @@ use crate::{FromQueryResult, QueryResult, QueryResultRow, TypeErr}; +use serde_json::Map; pub use serde_json::Value as JsonValue; -use serde_json::{json, Map}; impl FromQueryResult for JsonValue { fn from_query_result(res: &QueryResult, pre: &str) -> Result { match &res.row { + #[cfg(feature = "sqlx-mysql")] QueryResultRow::SqlxMySql(row) => { + use serde_json::json; use sqlx::{Column, MySql, Row, Type}; let mut map = Map::new(); for column in row.columns() { @@ -41,6 +43,19 @@ impl FromQueryResult for JsonValue { } Ok(JsonValue::Object(map)) } + #[cfg(feature = "mock")] + QueryResultRow::Mock(row) => { + let mut map = Map::new(); + for (column, value) in row.clone().into_column_value_tuples() { + let col = if !column.starts_with(pre) { + continue; + } else { + column.replacen(pre, "", 1) + }; + map.insert(col, sea_query::sea_value_to_json_value(&value)); + } + Ok(JsonValue::Object(map)) + } } } } diff --git a/src/query/result.rs b/src/query/result.rs deleted file mode 100644 index 713fbb87..00000000 --- a/src/query/result.rs +++ /dev/null @@ -1,94 +0,0 @@ -use sqlx::mysql::MySqlRow; -use std::{error::Error, fmt}; - -#[derive(Debug)] -pub struct QueryResult { - pub(crate) row: QueryResultRow, -} - -#[derive(Debug)] -pub(crate) enum QueryResultRow { - SqlxMySql(MySqlRow), -} - -#[derive(Debug)] -pub struct TypeErr; - -pub trait TryGetable { - fn try_get(res: &QueryResult, pre: &str, col: &str) -> Result - where - Self: Sized; -} - -// TryGetable // - -macro_rules! try_getable { - ( $type: ty ) => { - impl TryGetable for $type { - fn try_get(res: &QueryResult, pre: &str, col: &str) -> Result { - let column = format!("{}{}", pre, col); - match &res.row { - QueryResultRow::SqlxMySql(row) => { - use sqlx::Row; - Ok(row.try_get(column.as_str())?) - } - } - } - } - - impl TryGetable for Option<$type> { - fn try_get(res: &QueryResult, pre: &str, col: &str) -> Result { - let column = format!("{}{}", pre, col); - match &res.row { - QueryResultRow::SqlxMySql(row) => { - use sqlx::Row; - match row.try_get(column.as_str()) { - Ok(v) => Ok(Some(v)), - Err(_) => Ok(None), - } - } - } - } - } - }; -} - -try_getable!(bool); -try_getable!(i8); -try_getable!(i16); -try_getable!(i32); -try_getable!(i64); -try_getable!(u8); -try_getable!(u16); -try_getable!(u32); -try_getable!(u64); -try_getable!(f32); -try_getable!(f64); -try_getable!(String); - -// QueryResult // - -impl QueryResult { - pub fn try_get(&self, pre: &str, col: &str) -> Result - where - T: TryGetable, - { - T::try_get(self, pre, col) - } -} - -// TypeErr // - -impl Error for TypeErr {} - -impl fmt::Display for TypeErr { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "{:?}", self) - } -} - -impl From for TypeErr { - fn from(_: sqlx::Error) -> TypeErr { - TypeErr - } -}