Mock Database Connection
This commit is contained in:
parent
da545c266c
commit
6b7ea75393
12
Cargo.toml
12
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" ]
|
||||
|
@ -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" }
|
||||
|
137
src/database/connection.rs
Normal file
137
src/database/connection.rs
Normal file
@ -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<ExecResult, ExecErr> {
|
||||
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<Option<QueryResult>, 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<Vec<QueryResult>, 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()
|
||||
}
|
||||
}
|
66
src/database/mock.rs
Normal file
66
src/database/mock.rs
Normal file
@ -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<Statement>,
|
||||
exec_results: Vec<ExecResult>,
|
||||
query_results: Vec<Vec<QueryResult>>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct MockExecResult {
|
||||
pub last_insert_id: u64,
|
||||
pub rows_affected: u64,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct MockRow {
|
||||
values: BTreeMap<String, Value>,
|
||||
}
|
||||
|
||||
impl MockDatabase {
|
||||
pub fn new() -> Self {
|
||||
Default::default()
|
||||
}
|
||||
}
|
||||
|
||||
impl MockDatabaseTrait for MockDatabase {
|
||||
fn execute(&mut self, counter: usize, statement: Statement) -> Result<ExecResult, ExecErr> {
|
||||
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<Vec<QueryResult>, 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<T>(&self, col: &str) -> Result<T, TypeErr>
|
||||
where
|
||||
T: ValueType,
|
||||
{
|
||||
Ok(self.values.get(col).unwrap().clone().unwrap())
|
||||
}
|
||||
|
||||
pub fn into_column_value_tuples(self) -> impl Iterator<Item = (String, Value)> {
|
||||
self.values.into_iter()
|
||||
}
|
||||
}
|
@ -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()
|
||||
}
|
||||
}
|
||||
|
@ -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<Values>,
|
||||
|
76
src/driver/mock.rs
Normal file
76
src/driver/mock.rs
Normal file
@ -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<Box<dyn MockDatabaseTrait>>,
|
||||
}
|
||||
|
||||
pub trait MockDatabaseTrait: Send {
|
||||
fn execute(&mut self, counter: usize, stmt: Statement) -> Result<ExecResult, ExecErr>;
|
||||
|
||||
fn query(&mut self, counter: usize, stmt: Statement) -> Result<Vec<QueryResult>, QueryErr>;
|
||||
}
|
||||
|
||||
impl MockDatabaseConnector {
|
||||
pub fn accepts(_string: &str) -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
pub async fn connect(_string: &str) -> Result<DatabaseConnection, ConnectionErr> {
|
||||
Ok(DatabaseConnection::MockDatabaseConnection(
|
||||
MockDatabaseConnection::new(MockDatabase::new()),
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
impl MockDatabaseConnection {
|
||||
pub fn new<M: 'static>(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<ExecResult, ExecErr> {
|
||||
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<Option<QueryResult>, 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<Vec<QueryResult>, QueryErr> {
|
||||
debug_print!("{}", statement);
|
||||
self.counter.fetch_add(1, Ordering::SeqCst);
|
||||
self.mocker
|
||||
.lock()
|
||||
.unwrap()
|
||||
.query(self.counter.load(Ordering::SeqCst), statement)
|
||||
}
|
||||
}
|
@ -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::*;
|
||||
|
@ -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<DatabaseConnection, ConnectionErr> {
|
||||
pub async fn connect(string: &str) -> Result<DatabaseConnection, ConnectionErr> {
|
||||
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<ExecResult, ExecErr> {
|
||||
impl SqlxMySqlPoolConnection {
|
||||
pub async fn execute(&self, stmt: Statement) -> Result<ExecResult, ExecErr> {
|
||||
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<Option<QueryResult>, QueryErr> {
|
||||
pub async fn query_one(&self, stmt: Statement) -> Result<Option<QueryResult>, 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<Vec<QueryResult>, QueryErr> {
|
||||
pub async fn query_all(&self, stmt: Statement) -> Result<Vec<QueryResult>, QueryErr> {
|
||||
debug_print!("{}", stmt);
|
||||
|
||||
let query = sqlx_query(&stmt);
|
||||
@ -96,6 +93,18 @@ impl From<MySqlQueryResult> for ExecResult {
|
||||
}
|
||||
}
|
||||
|
||||
impl From<sqlx::Error> for TypeErr {
|
||||
fn from(_: sqlx::Error) -> TypeErr {
|
||||
TypeErr
|
||||
}
|
||||
}
|
||||
|
||||
impl From<sqlx::Error> 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 {
|
||||
|
@ -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<DatabaseConnection, ConnectionErr>;
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
pub trait Connection {
|
||||
async fn execute(&self, stmt: Statement) -> Result<ExecResult, ExecErr>;
|
||||
|
||||
async fn query_one(&self, stmt: Statement) -> Result<Option<QueryResult>, QueryErr>;
|
||||
|
||||
async fn query_all(&self, stmt: Statement) -> Result<Vec<QueryResult>, 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)
|
||||
}
|
||||
}
|
@ -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<B>(&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<Output = Result<DeleteResult, ExecErr>> + '_ {
|
||||
|
@ -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<sqlx::Error> for ExecErr {
|
||||
fn from(_: sqlx::Error) -> ExecErr {
|
||||
ExecErr
|
||||
}
|
||||
}
|
||||
|
@ -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<B>(&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<Output = Result<InsertResult, ExecErr>> + '_ {
|
||||
|
@ -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::*;
|
||||
|
@ -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<usize, QueryErr> {
|
||||
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),
|
||||
|
@ -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<sqlx::Error> 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<Self, TypeErr> {
|
||||
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<Self, TypeErr> {
|
||||
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),
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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<S> Selector<S>
|
||||
where
|
||||
S: SelectorTrait,
|
||||
{
|
||||
pub fn build<B>(&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<Option<S::Item>, QueryErr> {
|
||||
|
@ -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<B>(&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<Output = Result<UpdateResult, ExecErr>> + '_ {
|
||||
|
@ -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<Self, TypeErr> {
|
||||
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))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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<Self, TypeErr>
|
||||
where
|
||||
Self: Sized;
|
||||
}
|
||||
|
||||
// TryGetable //
|
||||
|
||||
macro_rules! try_getable {
|
||||
( $type: ty ) => {
|
||||
impl TryGetable for $type {
|
||||
fn try_get(res: &QueryResult, pre: &str, col: &str) -> Result<Self, TypeErr> {
|
||||
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<Self, TypeErr> {
|
||||
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<T>(&self, pre: &str, col: &str) -> Result<T, TypeErr>
|
||||
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<sqlx::Error> for TypeErr {
|
||||
fn from(_: sqlx::Error) -> TypeErr {
|
||||
TypeErr
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user