Merge branch 'master' into serialize-query-result

This commit is contained in:
Chris Tsang 2021-05-27 02:33:24 +08:00 committed by GitHub
commit 73ed4b6f81
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 117 additions and 37 deletions

View File

@ -37,7 +37,7 @@ Use mock connections to write unit tests for your logic.
Quickly build search models that help you filter, sort and paginate data in APIs.
## Design goals
## Design Goals
1. Intuitive and ergonomic
@ -49,4 +49,10 @@ Balance between compile-time checking and compilation speed.
3. Avoid 'symbol soup'
Avoid macros with DSL, use derive macros where appropriate. Be friendly with IDE tools.
Avoid macros with DSL, use derive macros where appropriate. Be friendly with IDE tools.
## Test Time
After some bitterness we realized it is not possible to capture everything compile time. But we don't
want to encounter problems at run time either. The solution is to perform checking at 'test time' to
uncover problems. These checks will be removed at production so there will be no run time penalty.

51
src/connector/executor.rs Normal file
View File

@ -0,0 +1,51 @@
use sqlx::mysql::MySqlQueryResult;
use std::{error::Error, fmt};
#[derive(Debug)]
pub struct ExecResult {
pub(crate) result: ExecResultHolder,
}
#[derive(Debug)]
pub(crate) enum ExecResultHolder {
SqlxMySql(MySqlQueryResult),
}
#[derive(Debug)]
pub struct ExecErr;
// ExecResult //
impl ExecResult {
pub fn last_insert_id(&self) -> u64 {
match &self.result {
ExecResultHolder::SqlxMySql(result) => {
result.last_insert_id()
}
}
}
pub fn rows_affected(&self) -> u64 {
match &self.result {
ExecResultHolder::SqlxMySql(result) => {
result.rows_affected()
}
}
}
}
// ExecErr //
impl Error for ExecErr {}
impl fmt::Display for ExecErr {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{:?}", self)
}
}
impl From<sqlx::Error> for ExecErr {
fn from(_: sqlx::Error) -> ExecErr {
ExecErr
}
}

View File

@ -1,10 +1,10 @@
mod executor;
mod select;
#[cfg(feature = "serialize-query-result")]
mod select_json;
pub use executor::*;
pub use select::*;
#[cfg(feature = "serialize-query-result")]
pub use select_json::*;
@ -21,6 +21,8 @@ pub trait Connector {
#[async_trait]
pub trait Connection {
async fn execute(&self, stmt: Statement) -> Result<ExecResult, ExecErr>;
async fn query_one(&self, stmt: Statement) -> Result<QueryResult, QueryErr>;
async fn query_all(&self, stmt: Statement) -> Result<Vec<QueryResult>, QueryErr>;

View File

@ -3,25 +3,32 @@ use std::fmt;
pub struct Statement {
pub sql: String,
pub values: Values,
pub values: Option<Values>,
}
impl From<(String, Values)> for Statement {
fn from(stmt: (String, Values)) -> Statement {
Statement {
sql: stmt.0,
values: stmt.1,
values: Some(stmt.1),
}
}
}
impl fmt::Display for Statement {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let string = inject_parameters(
&self.sql,
self.values.0.clone(),
&MySqlQueryBuilder::default(),
);
write!(f, "{}", &string)
match &self.values {
Some(values) => {
let string = inject_parameters(
&self.sql,
values.0.clone(),
&MySqlQueryBuilder::default(),
);
write!(f, "{}", &string)
}
None => {
write!(f, "{}", &self.sql)
}
}
}
}

View File

@ -1,5 +1,5 @@
use async_trait::async_trait;
use sqlx::{mysql::MySqlRow, MySqlPool};
use sqlx::{mysql::{MySqlRow, MySqlArguments, MySqlQueryResult}, MySql, MySqlPool};
sea_query::sea_query_driver_mysql!();
use sea_query_driver_mysql::bind_query;
@ -31,10 +31,22 @@ impl Connector for SqlxMySqlConnector {
#[async_trait]
impl Connection for &SqlxMySqlPoolConnection {
async fn execute(&self, stmt: Statement) -> Result<ExecResult, ExecErr> {
debug_print!("{}", stmt);
let query = sqlx_query(&stmt);
if let Ok(conn) = &mut self.pool.acquire().await {
if let Ok(res) = query.execute(conn).await {
return Ok(res.into());
}
}
Err(ExecErr)
}
async fn query_one(&self, stmt: Statement) -> Result<QueryResult, QueryErr> {
debug_print!("{}", stmt);
let query = bind_query(sqlx::query(&stmt.sql), &stmt.values);
let query = sqlx_query(&stmt);
if let Ok(conn) = &mut self.pool.acquire().await {
if let Ok(row) = query.fetch_one(conn).await {
return Ok(row.into());
@ -46,7 +58,7 @@ impl Connection for &SqlxMySqlPoolConnection {
async fn query_all(&self, stmt: Statement) -> Result<Vec<QueryResult>, QueryErr> {
debug_print!("{}", stmt);
let query = bind_query(sqlx::query(&stmt.sql), &stmt.values);
let query = sqlx_query(&stmt);
if let Ok(conn) = &mut self.pool.acquire().await {
if let Ok(rows) = query.fetch_all(conn).await {
return Ok(rows.into_iter().map(|r| r.into()).collect());
@ -63,3 +75,19 @@ impl From<MySqlRow> for QueryResult {
}
}
}
impl From<MySqlQueryResult> for ExecResult {
fn from(result: MySqlQueryResult) -> ExecResult {
ExecResult {
result: ExecResultHolder::SqlxMySql(result),
}
}
}
fn sqlx_query(stmt: &Statement) -> sqlx::query::Query<'_, MySql, MySqlArguments> {
let mut query = sqlx::query(&stmt.sql);
if let Some(values) = &stmt.values {
query = bind_query(query, values);
}
query
}

View File

@ -70,7 +70,7 @@ pub trait EntityTrait: EntityName {
/// .to_string(),
/// [
/// r#"SELECT "cake_filling"."cake_id", "cake_filling"."filling_id" FROM "cake_filling""#,
/// r#"WHERE "cake_filling"."cake_id" = 2 AND ("cake_filling"."filling_id" = 3)"#,
/// r#"WHERE "cake_filling"."cake_id" = 2 AND "cake_filling"."filling_id" = 3"#,
/// ].join(" ")
/// );
/// ```

View File

@ -116,7 +116,7 @@ mod tests {
"SELECT `cake`.`id` AS `A_id`, `cake`.`name` AS `A_name`,",
"`fruit`.`id` AS `B_id`, `fruit`.`name` AS `B_name`, `fruit`.`cake_id` AS `B_cake_id`",
"FROM `cake` LEFT JOIN `fruit` ON `cake`.`id` = `fruit`.`cake_id`",
"WHERE `cake`.`id` = 1 AND (`fruit`.`id` = 2)",
"WHERE `cake`.`id` = 1 AND `fruit`.`id` = 2",
].join(" ")
);
}

View File

@ -1,12 +1,8 @@
use crate::{ColumnTrait, Identity, IntoSimpleExpr, RelationDef};
use sea_query::{Alias, Expr, SelectExpr, SelectStatement, SimpleExpr};
use sea_query::{Alias, Expr, IntoCondition, SelectExpr, SelectStatement, SimpleExpr};
pub use sea_query::{Condition, JoinType, Order};
use std::rc::Rc;
pub trait IntoCondition {
fn into_condition(self) -> Condition;
}
pub trait QueryHelper: Sized {
fn query(&mut self) -> &mut SelectStatement;
@ -71,10 +67,10 @@ pub trait QueryHelper: Sized {
/// .filter(cake::Column::Id.eq(5))
/// .build(MysqlQueryBuilder)
/// .to_string(),
/// "SELECT `cake`.`id`, `cake`.`name` FROM `cake` WHERE `cake`.`id` = 4 AND (`cake`.`id` = 5)"
/// "SELECT `cake`.`id`, `cake`.`name` FROM `cake` WHERE `cake`.`id` = 4 AND `cake`.`id` = 5"
/// );
/// ```
///
///
/// Add a condition tree.
/// ```
/// use sea_orm::{Condition, ColumnTrait, EntityTrait, QueryHelper, tests_cfg::cake, sea_query::MysqlQueryBuilder};
@ -92,7 +88,9 @@ pub trait QueryHelper: Sized {
/// );
/// ```
fn filter<F>(mut self, filter: F) -> Self
where F: IntoCondition {
where
F: IntoCondition,
{
self.query().cond_where(filter.into_condition());
self
}
@ -216,18 +214,6 @@ pub trait QueryHelper: Sized {
}
}
impl IntoCondition for SimpleExpr {
fn into_condition(self) -> Condition {
Condition::all().add(self)
}
}
impl IntoCondition for Condition {
fn into_condition(self) -> Condition {
self
}
}
fn join_condition(rel: RelationDef) -> SimpleExpr {
let from_tbl = rel.from_tbl.clone();
let to_tbl = rel.to_tbl.clone();