Mock Database Connection

This commit is contained in:
Chris Tsang 2021-06-13 22:09:07 +08:00
parent da545c266c
commit 6b7ea75393
20 changed files with 401 additions and 246 deletions

View File

@ -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" ]

View File

@ -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
View 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
View 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()
}
}

View File

@ -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()
}
}

View File

@ -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
View 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)
}
}

View File

@ -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::*;

View File

@ -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 {

View File

@ -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)
}
}

View File

@ -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>> + '_ {

View File

@ -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
}
}

View File

@ -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>> + '_ {

View File

@ -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::*;

View File

@ -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),

View File

@ -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),
},
}
}
}

View File

@ -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> {

View File

@ -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>> + '_ {

View File

@ -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))
}
}
}
}

View File

@ -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
}
}