Merge remote-tracking branch 'origin/master' into returning

This commit is contained in:
Billy Chan 2021-11-16 14:44:12 +08:00
commit fd50ffd5ea
No known key found for this signature in database
GPG Key ID: A2D690CAC7DF3CC7
29 changed files with 247 additions and 141 deletions

View File

@ -316,7 +316,7 @@ jobs:
fail-fast: false fail-fast: false
matrix: matrix:
os: [ubuntu-latest] os: [ubuntu-latest]
path: [86, 249, 262] path: [86, 249, 262, 319]
steps: steps:
- uses: actions/checkout@v2 - uses: actions/checkout@v2

View File

@ -63,7 +63,7 @@ default = [
] ]
macros = ["sea-orm-macros"] macros = ["sea-orm-macros"]
mock = [] mock = []
with-json = ["serde_json", "sea-query/with-json"] with-json = ["serde_json", "sea-query/with-json", "chrono/serde"]
with-chrono = ["chrono", "sea-query/with-chrono"] with-chrono = ["chrono", "sea-query/with-chrono"]
with-rust_decimal = ["rust_decimal", "sea-query/with-rust_decimal"] with-rust_decimal = ["rust_decimal", "sea-query/with-rust_decimal"]
with-uuid = ["uuid", "sea-query/with-uuid"] with-uuid = ["uuid", "sea-query/with-uuid"]

View File

@ -1,5 +1,5 @@
use super::*; use super::*;
use sea_orm::{entity::*, error::*, query::*, DbConn}; use sea_orm::{entity::*, error::*, DbConn};
pub async fn all_about_operation(db: &DbConn) -> Result<(), DbErr> { pub async fn all_about_operation(db: &DbConn) -> Result<(), DbErr> {
insert_and_update(db).await?; insert_and_update(db).await?;

18
issues/319/Cargo.toml Normal file
View File

@ -0,0 +1,18 @@
[workspace]
# A separate workspace
[package]
name = "sea-orm-issues-319"
version = "0.1.0"
edition = "2021"
publish = false
[dependencies]
async-std = { version = "^1", features = ["attributes"] }
serde = { version = "^1", features = ["derive"] }
sea-orm = { path = "../../", features = [
"sqlx-mysql",
"runtime-async-std-native-tls",
"with-json",
"macros",
], default-features = false }

14
issues/319/src/main.rs Normal file
View File

@ -0,0 +1,14 @@
mod material;
use sea_orm::*;
#[async_std::main]
pub async fn main() {
let db = Database::connect("mysql://sea:sea@localhost/bakery")
.await
.unwrap();
async_std::task::spawn(async move {
material::Entity::find().one(&db).await.unwrap();
})
.await;
}

View File

@ -0,0 +1,20 @@
use sea_orm::entity::prelude::*;
use serde::{Serialize, Deserialize};
#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Serialize, Deserialize)]
#[sea_orm(table_name = "materials")]
pub struct Model {
#[sea_orm(primary_key)]
pub id: i32,
pub created_at: DateTimeWithTimeZone,
pub updated_at: DateTimeWithTimeZone,
pub name: String,
#[sea_orm(column_type = "Text", nullable)]
pub description: Option<String>,
pub tag_ids: Vec<u8>,
}
#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]
pub enum Relation {}
impl ActiveModelBehavior for ActiveModel {}

View File

@ -4,7 +4,7 @@ use crate::{
use futures::Stream; use futures::Stream;
use std::{future::Future, pin::Pin}; use std::{future::Future, pin::Pin};
/// Creates constraints for any structure that wants to create a database connection /// Creates constraints for any structure that can create a database connection
/// and execute SQL statements /// and execute SQL statements
#[async_trait::async_trait] #[async_trait::async_trait]
pub trait ConnectionTrait<'a>: Sync { pub trait ConnectionTrait<'a>: Sync {

View File

@ -8,9 +8,10 @@ use std::fmt;
pub struct Statement { pub struct Statement {
/// The SQL query /// The SQL query
pub sql: String, pub sql: String,
/// The values for the SQL statement /// The values for the SQL statement's parameters
pub values: Option<Values>, pub values: Option<Values>,
/// The database backend to use /// The database backend this statement is constructed for.
/// The SQL dialect and values should be valid for the DbBackend.
pub db_backend: DbBackend, pub db_backend: DbBackend,
} }
@ -31,7 +32,7 @@ impl Statement {
} }
/// Create a SQL statement from a [crate::DatabaseBackend], a /// Create a SQL statement from a [crate::DatabaseBackend], a
/// raw SQL statement and defined values /// raw SQL statement and param values
pub fn from_sql_and_values<I>(db_backend: DbBackend, sql: &str, values: I) -> Self pub fn from_sql_and_values<I>(db_backend: DbBackend, sql: &str, values: I) -> Self
where where
I: IntoIterator<Item = Value>, I: IntoIterator<Item = Value>,

View File

@ -1,3 +1,5 @@
#![allow(missing_docs)]
use std::{pin::Pin, task::Poll}; use std::{pin::Pin, task::Poll};
#[cfg(feature = "mock")] #[cfg(feature = "mock")]

View File

@ -1,3 +1,5 @@
#![allow(missing_docs)]
use std::{ops::DerefMut, pin::Pin, task::Poll}; use std::{ops::DerefMut, pin::Pin, task::Poll};
use futures::Stream; use futures::Stream;
@ -11,9 +13,9 @@ use futures::lock::MutexGuard;
use crate::{DbErr, InnerConnection, QueryResult, Statement}; use crate::{DbErr, InnerConnection, QueryResult, Statement};
#[ouroboros::self_referencing]
/// `TransactionStream` cannot be used in a `transaction` closure as it does not impl `Send`. /// `TransactionStream` cannot be used in a `transaction` closure as it does not impl `Send`.
/// It seems to be a Rust limitation right now, and solution to work around this deemed to be extremely hard. /// It seems to be a Rust limitation right now, and solution to work around this deemed to be extremely hard.
#[ouroboros::self_referencing]
pub struct TransactionStream<'a> { pub struct TransactionStream<'a> {
stmt: Statement, stmt: Statement,
conn: MutexGuard<'a, InnerConnection>, conn: MutexGuard<'a, InnerConnection>,

View File

@ -23,7 +23,7 @@ pub struct MockDatabaseConnection {
mocker: Mutex<Box<dyn MockDatabaseTrait>>, mocker: Mutex<Box<dyn MockDatabaseTrait>>,
} }
/// A set of constraints for any type wanting to perform operations on the [MockDatabase] /// A Trait for any type wanting to perform operations on the [MockDatabase]
pub trait MockDatabaseTrait: Send + Debug { pub trait MockDatabaseTrait: Send + Debug {
/// Execute a statement in the [MockDatabase] /// Execute a statement in the [MockDatabase]
fn execute(&mut self, counter: usize, stmt: Statement) -> Result<ExecResult, DbErr>; fn execute(&mut self, counter: usize, stmt: Statement) -> Result<ExecResult, DbErr>;

View File

@ -9,3 +9,8 @@ pub fn sqlx_error_to_exec_err(err: sqlx::Error) -> DbErr {
pub fn sqlx_error_to_query_err(err: sqlx::Error) -> DbErr { pub fn sqlx_error_to_query_err(err: sqlx::Error) -> DbErr {
DbErr::Query(err.to_string()) DbErr::Query(err.to_string())
} }
/// Converts an [sqlx::error] connection error to a [DbErr]
pub fn sqlx_error_to_conn_err(err: sqlx::Error) -> DbErr {
DbErr::Conn(err.to_string())
}

View File

@ -41,12 +41,11 @@ impl SqlxMySqlConnector {
use sqlx::ConnectOptions; use sqlx::ConnectOptions;
opt.disable_statement_logging(); opt.disable_statement_logging();
} }
if let Ok(pool) = options.pool_options().connect_with(opt).await { match options.pool_options().connect_with(opt).await {
Ok(DatabaseConnection::SqlxMySqlPoolConnection( Ok(pool) => Ok(DatabaseConnection::SqlxMySqlPoolConnection(
SqlxMySqlPoolConnection { pool }, SqlxMySqlPoolConnection { pool },
)) )),
} else { Err(e) => Err(sqlx_error_to_conn_err(e)),
Err(DbErr::Conn("Failed to connect.".to_owned()))
} }
} }
} }

View File

@ -41,12 +41,11 @@ impl SqlxPostgresConnector {
use sqlx::ConnectOptions; use sqlx::ConnectOptions;
opt.disable_statement_logging(); opt.disable_statement_logging();
} }
if let Ok(pool) = options.pool_options().connect_with(opt).await { match options.pool_options().connect_with(opt).await {
Ok(DatabaseConnection::SqlxPostgresPoolConnection( Ok(pool) => Ok(DatabaseConnection::SqlxPostgresPoolConnection(
SqlxPostgresPoolConnection { pool }, SqlxPostgresPoolConnection { pool },
)) )),
} else { Err(e) => Err(sqlx_error_to_conn_err(e)),
Err(DbErr::Conn("Failed to connect.".to_owned()))
} }
} }
} }

View File

@ -45,12 +45,11 @@ impl SqlxSqliteConnector {
if options.get_max_connections().is_none() { if options.get_max_connections().is_none() {
options.max_connections(1); options.max_connections(1);
} }
if let Ok(pool) = options.pool_options().connect_with(opt).await { match options.pool_options().connect_with(opt).await {
Ok(DatabaseConnection::SqlxSqlitePoolConnection( Ok(pool) => Ok(DatabaseConnection::SqlxSqlitePoolConnection(
SqlxSqlitePoolConnection { pool }, SqlxSqlitePoolConnection { pool },
)) )),
} else { Err(e) => Err(sqlx_error_to_conn_err(e)),
Err(DbErr::Conn("Failed to connect.".to_owned()))
} }
} }
} }

View File

@ -76,12 +76,12 @@ where
ActiveValue::unchanged(value) ActiveValue::unchanged(value)
} }
/// Enforces a set of constraints on any type performing an Create, Update or Delete operation. /// A Trait for ActiveModel to perform Create, Update or Delete operation.
/// The type must also implement the [EntityTrait]. /// The type must also implement the [EntityTrait].
/// See module level docs [crate::entity] for a full example /// See module level docs [crate::entity] for a full example
#[async_trait] #[async_trait]
pub trait ActiveModelTrait: Clone + Debug { pub trait ActiveModelTrait: Clone + Debug {
/// Enforce the type to the constraints of the [EntityTrait] /// The Entity this ActiveModel belongs to
type Entity: EntityTrait; type Entity: EntityTrait;
/// Get a mutable [ActiveValue] from an ActiveModel /// Get a mutable [ActiveValue] from an ActiveModel
@ -204,9 +204,7 @@ pub trait ActiveModelTrait: Clone + Debug {
} }
} }
/// Enforce a set of constraints to a override the ActiveModel behavior /// A Trait for overriding the ActiveModel behavior
/// Behaviors for users to override.
/// The type must also implement the [ActiveModelTrait]
/// ///
/// ### Example /// ### Example
/// ```ignore /// ```ignore
@ -261,7 +259,7 @@ pub trait ActiveModelBehavior: ActiveModelTrait {
} }
} }
/// Enforce constraints for conversion to an ActiveModel /// A Trait for any type that can be converted into an ActiveModel
pub trait IntoActiveModel<A> pub trait IntoActiveModel<A>
where where
A: ActiveModelTrait, A: ActiveModelTrait,

View File

@ -13,7 +13,7 @@ pub trait IdenStatic: Iden + Copy + Debug + 'static {
fn as_str(&self) -> &str; fn as_str(&self) -> &str;
} }
/// Enforces the naming of an entity to a set of constraints /// A Trait for mapping an Entity to a database table
pub trait EntityName: IdenStatic + Default { pub trait EntityName: IdenStatic + Default {
/// Method to get the name for the schema, defaults to [Option::None] if not set /// Method to get the name for the schema, defaults to [Option::None] if not set
fn schema_name(&self) -> Option<&str> { fn schema_name(&self) -> Option<&str> {

View File

@ -6,7 +6,7 @@ use sea_query::{Alias, IntoIden, JoinType, SeaRc};
/// Same as [RelationDef] /// Same as [RelationDef]
pub type LinkDef = RelationDef; pub type LinkDef = RelationDef;
/// A set of constraints for links between Entities /// A Trait for links between Entities
pub trait Linked { pub trait Linked {
#[allow(missing_docs)] #[allow(missing_docs)]
type FromEntity: EntityTrait; type FromEntity: EntityTrait;

View File

@ -5,7 +5,7 @@ use crate::{
pub use sea_query::Value; pub use sea_query::Value;
use std::fmt::Debug; use std::fmt::Debug;
/// A set of constraints for a Model /// A Trait for a Model
pub trait ModelTrait: Clone + Send + Debug { pub trait ModelTrait: Clone + Send + Debug {
#[allow(missing_docs)] #[allow(missing_docs)]
type Entity: EntityTrait; type Entity: EntityTrait;
@ -35,7 +35,7 @@ pub trait ModelTrait: Clone + Send + Debug {
} }
} }
/// A set of constraints for implementing a [QueryResult] /// A Trait for implementing a [QueryResult]
pub trait FromQueryResult: Sized { pub trait FromQueryResult: Sized {
/// Instantiate a Model from a [QueryResult] /// Instantiate a Model from a [QueryResult]
fn from_query_result(res: &QueryResult, pre: &str) -> Result<Self, DbErr>; fn from_query_result(res: &QueryResult, pre: &str) -> Result<Self, DbErr>;

View File

@ -1,8 +1,8 @@
pub use crate::{ pub use crate::{
error::*, ActiveEnum, ActiveModelBehavior, ActiveModelTrait, ColumnDef, ColumnTrait, error::*, ActiveEnum, ActiveModelBehavior, ActiveModelTrait, ColumnDef, ColumnTrait,
ColumnType, DatabaseConnection, DbConn, EntityName, EntityTrait, EnumIter, ForeignKeyAction, ColumnType, DatabaseConnection, DbConn, EntityName, EntityTrait, EnumIter, ForeignKeyAction,
Iden, IdenStatic, Linked, ModelTrait, PrimaryKeyToColumn, PrimaryKeyTrait, QueryFilter, Iden, IdenStatic, Linked, ModelTrait, PaginatorTrait, PrimaryKeyToColumn, PrimaryKeyTrait,
QueryResult, Related, RelationDef, RelationTrait, Select, Value, QueryFilter, QueryResult, Related, RelationDef, RelationTrait, Select, Value,
}; };
#[cfg(feature = "macros")] #[cfg(feature = "macros")]

View File

@ -4,7 +4,7 @@ use sea_query::{FromValueTuple, IntoValueTuple};
use std::fmt::Debug; use std::fmt::Debug;
//LINT: composite primary key cannot auto increment //LINT: composite primary key cannot auto increment
/// A set of constraints to be used to define a Primary Key. /// A Trait for to be used to define a Primary Key.
/// ///
/// A primary key can be derived manually /// A primary key can be derived manually
/// ///

View File

@ -1,4 +1,7 @@
use crate::{error::*, ConnectionTrait, DbBackend, SelectorTrait}; use crate::{
error::*, ConnectionTrait, DbBackend, EntityTrait, FromQueryResult, Select, SelectModel,
SelectTwo, SelectTwoModel, Selector, SelectorTrait,
};
use async_stream::stream; use async_stream::stream;
use futures::Stream; use futures::Stream;
use sea_query::{Alias, Expr, SelectStatement}; use sea_query::{Alias, Expr, SelectStatement};
@ -155,9 +158,77 @@ where
} }
} }
#[async_trait::async_trait]
/// A Trait for any type that can paginate results
pub trait PaginatorTrait<'db, C>
where
C: ConnectionTrait<'db>,
{
/// Select operation
type Selector: SelectorTrait + Send + Sync + 'db;
/// Paginate the result of a select operation.
fn paginate(self, db: &'db C, page_size: usize) -> Paginator<'db, C, Self::Selector>;
/// Perform a count on the paginated results
async fn count(self, db: &'db C) -> Result<usize, DbErr>
where
Self: Send + Sized,
{
self.paginate(db, 1).num_items().await
}
}
impl<'db, C, S> PaginatorTrait<'db, C> for Selector<S>
where
C: ConnectionTrait<'db>,
S: SelectorTrait + Send + Sync + 'db,
{
type Selector = S;
fn paginate(self, db: &'db C, page_size: usize) -> Paginator<'db, C, S> {
Paginator {
query: self.query,
page: 0,
page_size,
db,
selector: PhantomData,
}
}
}
impl<'db, C, M, E> PaginatorTrait<'db, C> for Select<E>
where
C: ConnectionTrait<'db>,
E: EntityTrait<Model = M>,
M: FromQueryResult + Sized + Send + Sync + 'db,
{
type Selector = SelectModel<M>;
fn paginate(self, db: &'db C, page_size: usize) -> Paginator<'db, C, Self::Selector> {
self.into_model().paginate(db, page_size)
}
}
impl<'db, C, M, N, E, F> PaginatorTrait<'db, C> for SelectTwo<E, F>
where
C: ConnectionTrait<'db>,
E: EntityTrait<Model = M>,
F: EntityTrait<Model = N>,
M: FromQueryResult + Sized + Send + Sync + 'db,
N: FromQueryResult + Sized + Send + Sync + 'db,
{
type Selector = SelectTwoModel<M, N>;
fn paginate(self, db: &'db C, page_size: usize) -> Paginator<'db, C, Self::Selector> {
self.into_model().paginate(db, page_size)
}
}
#[cfg(test)] #[cfg(test)]
#[cfg(feature = "mock")] #[cfg(feature = "mock")]
mod tests { mod tests {
use super::*;
use crate::entity::prelude::*; use crate::entity::prelude::*;
use crate::{tests_cfg::*, ConnectionTrait}; use crate::{tests_cfg::*, ConnectionTrait};
use crate::{DatabaseConnection, DbBackend, MockDatabase, Transaction}; use crate::{DatabaseConnection, DbBackend, MockDatabase, Transaction};

View File

@ -1,7 +1,7 @@
use crate::{ use crate::{
error::*, ConnectionTrait, EntityTrait, FromQueryResult, IdenStatic, Iterable, ModelTrait, error::*, ConnectionTrait, EntityTrait, FromQueryResult, IdenStatic, Iterable, ModelTrait,
Paginator, PrimaryKeyToColumn, QueryResult, Select, SelectA, SelectB, SelectTwo, SelectTwoMany, PrimaryKeyToColumn, QueryResult, Select, SelectA, SelectB, SelectTwo, SelectTwoMany, Statement,
Statement, TryGetableMany, TryGetableMany,
}; };
use futures::{Stream, TryStreamExt}; use futures::{Stream, TryStreamExt};
use sea_query::SelectStatement; use sea_query::SelectStatement;
@ -11,13 +11,13 @@ use std::pin::Pin;
#[cfg(feature = "with-json")] #[cfg(feature = "with-json")]
use crate::JsonValue; use crate::JsonValue;
/// Defines a type to do `SELECT` operations though a [SelectStatement] on a Model /// Defines a type to do `SELECT` operations through a [SelectStatement] on a Model
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub struct Selector<S> pub struct Selector<S>
where where
S: SelectorTrait, S: SelectorTrait,
{ {
query: SelectStatement, pub(crate) query: SelectStatement,
selector: S, selector: S,
} }
@ -31,7 +31,7 @@ where
selector: S, selector: S,
} }
/// Used to enforce constraints on any type that wants to perform SELECT queries /// A Trait for any type that can perform SELECT queries
pub trait SelectorTrait { pub trait SelectorTrait {
#[allow(missing_docs)] #[allow(missing_docs)]
type Item: Sized; type Item: Sized;
@ -250,7 +250,7 @@ where
Selector::<SelectGetableValue<T, C>>::with_columns(self.query) Selector::<SelectGetableValue<T, C>>::with_columns(self.query)
} }
/// Get one Model from a SELECT operation /// Get one Model from the SELECT query
pub async fn one<'a, C>(self, db: &C) -> Result<Option<E::Model>, DbErr> pub async fn one<'a, C>(self, db: &C) -> Result<Option<E::Model>, DbErr>
where where
C: ConnectionTrait<'a>, C: ConnectionTrait<'a>,
@ -258,7 +258,7 @@ where
self.into_model().one(db).await self.into_model().one(db).await
} }
/// Get all the Models from a SELECT operation /// Get all Models from the SELECT query
pub async fn all<'a, C>(self, db: &C) -> Result<Vec<E::Model>, DbErr> pub async fn all<'a, C>(self, db: &C) -> Result<Vec<E::Model>, DbErr>
where where
C: ConnectionTrait<'a>, C: ConnectionTrait<'a>,
@ -276,26 +276,6 @@ where
{ {
self.into_model().stream(db).await self.into_model().stream(db).await
} }
/// Paginate the results of a SELECT operation on a Model
pub fn paginate<'a, C>(
self,
db: &'a C,
page_size: usize,
) -> Paginator<'a, C, SelectModel<E::Model>>
where
C: ConnectionTrait<'a>,
{
self.into_model().paginate(db, page_size)
}
/// Perform a `COUNT` operation on a items on a Model using pagination
pub async fn count<'a, C>(self, db: &'a C) -> Result<usize, DbErr>
where
C: ConnectionTrait<'a>,
{
self.paginate(db, 1).num_items().await
}
} }
impl<E, F> SelectTwo<E, F> impl<E, F> SelectTwo<E, F>
@ -324,7 +304,7 @@ where
} }
} }
/// Get one Model from a Select operation /// Get one Model from the Select query
pub async fn one<'a, C>(self, db: &C) -> Result<Option<(E::Model, Option<F::Model>)>, DbErr> pub async fn one<'a, C>(self, db: &C) -> Result<Option<(E::Model, Option<F::Model>)>, DbErr>
where where
C: ConnectionTrait<'a>, C: ConnectionTrait<'a>,
@ -332,7 +312,7 @@ where
self.into_model().one(db).await self.into_model().one(db).await
} }
/// Get all Models from a Select operation /// Get all Models from the Select query
pub async fn all<'a, C>(self, db: &C) -> Result<Vec<(E::Model, Option<F::Model>)>, DbErr> pub async fn all<'a, C>(self, db: &C) -> Result<Vec<(E::Model, Option<F::Model>)>, DbErr>
where where
C: ConnectionTrait<'a>, C: ConnectionTrait<'a>,
@ -350,26 +330,6 @@ where
{ {
self.into_model().stream(db).await self.into_model().stream(db).await
} }
/// Paginate the results of a select operation on two models
pub fn paginate<'a, C>(
self,
db: &'a C,
page_size: usize,
) -> Paginator<'a, C, SelectTwoModel<E::Model, F::Model>>
where
C: ConnectionTrait<'a>,
{
self.into_model().paginate(db, page_size)
}
/// Perform a count on the paginated results
pub async fn count<'a, C>(self, db: &'a C) -> Result<usize, DbErr>
where
C: ConnectionTrait<'a>,
{
self.paginate(db, 1).num_items().await
}
} }
impl<E, F> SelectTwoMany<E, F> impl<E, F> SelectTwoMany<E, F>
@ -417,7 +377,7 @@ where
self.into_model().stream(db).await self.into_model().stream(db).await
} }
/// Get all the Models from the select operation /// Get all Models from the select operation
pub async fn all<'a, C>(self, db: &C) -> Result<Vec<(E::Model, Vec<F::Model>)>, DbErr> pub async fn all<'a, C>(self, db: &C) -> Result<Vec<(E::Model, Vec<F::Model>)>, DbErr>
where where
C: ConnectionTrait<'a>, C: ConnectionTrait<'a>,
@ -456,35 +416,36 @@ where
} }
} }
/// Get a Model from a Select operation fn into_selector_raw<'a, C>(self, db: &C) -> SelectorRaw<S>
where
C: ConnectionTrait<'a>,
{
let builder = db.get_database_backend();
let stmt = builder.build(&self.query);
SelectorRaw {
stmt,
selector: self.selector,
}
}
/// Get an item from the Select query
pub async fn one<'a, C>(mut self, db: &C) -> Result<Option<S::Item>, DbErr> pub async fn one<'a, C>(mut self, db: &C) -> Result<Option<S::Item>, DbErr>
where where
C: ConnectionTrait<'a>, C: ConnectionTrait<'a>,
{ {
let builder = db.get_database_backend();
self.query.limit(1); self.query.limit(1);
let row = db.query_one(builder.build(&self.query)).await?; self.into_selector_raw(db).one(db).await
match row {
Some(row) => Ok(Some(S::from_raw_query_result(row)?)),
None => Ok(None),
}
} }
/// Get all results from a Select operation /// Get all items from the Select query
pub async fn all<'a, C>(self, db: &C) -> Result<Vec<S::Item>, DbErr> pub async fn all<'a, C>(self, db: &C) -> Result<Vec<S::Item>, DbErr>
where where
C: ConnectionTrait<'a>, C: ConnectionTrait<'a>,
{ {
let builder = db.get_database_backend(); self.into_selector_raw(db).all(db).await
let rows = db.query_all(builder.build(&self.query)).await?;
let mut models = Vec::new();
for row in rows.into_iter() {
models.push(S::from_raw_query_result(row)?);
}
Ok(models)
} }
/// Stream the results of the operation /// Stream the results of the Select operation
pub async fn stream<'a: 'b, 'b, C>( pub async fn stream<'a: 'b, 'b, C>(
self, self,
db: &'a C, db: &'a C,
@ -493,25 +454,7 @@ where
C: ConnectionTrait<'a>, C: ConnectionTrait<'a>,
S: 'b, S: 'b,
{ {
let builder = db.get_database_backend(); self.into_selector_raw(db).stream(db).await
let stream = db.stream(builder.build(&self.query)).await?;
Ok(Box::pin(stream.and_then(|row| {
futures::future::ready(S::from_raw_query_result(row))
})))
}
/// Paginate the result of a select operation on a Model
pub fn paginate<'a, C>(self, db: &'a C, page_size: usize) -> Paginator<'a, C, S>
where
C: ConnectionTrait<'a>,
{
Paginator {
query: self.query,
page: 0,
page_size,
db,
selector: PhantomData,
}
} }
} }
@ -519,8 +462,7 @@ impl<S> SelectorRaw<S>
where where
S: SelectorTrait, S: SelectorTrait,
{ {
/// Create `SelectorRaw` from Statment. Executing this `SelectorRaw` will /// Select a custom Model from a raw SQL [Statement].
/// return a type `M` which implement `FromQueryResult`.
pub fn from_statement<M>(stmt: Statement) -> SelectorRaw<SelectModel<M>> pub fn from_statement<M>(stmt: Statement) -> SelectorRaw<SelectModel<M>>
where where
M: FromQueryResult, M: FromQueryResult,
@ -683,6 +625,7 @@ where
} }
} }
/// Get an item from the Select query
/// ``` /// ```
/// # #[cfg(feature = "mock")] /// # #[cfg(feature = "mock")]
/// # use sea_orm::{error::*, tests_cfg::*, MockDatabase, Transaction, DbBackend}; /// # use sea_orm::{error::*, tests_cfg::*, MockDatabase, Transaction, DbBackend};
@ -725,6 +668,7 @@ where
} }
} }
/// Get all items from the Select query
/// ``` /// ```
/// # #[cfg(feature = "mock")] /// # #[cfg(feature = "mock")]
/// # use sea_orm::{error::*, tests_cfg::*, MockDatabase, Transaction, DbBackend}; /// # use sea_orm::{error::*, tests_cfg::*, MockDatabase, Transaction, DbBackend};
@ -767,6 +711,21 @@ where
} }
Ok(models) Ok(models)
} }
/// Stream the results of the Select operation
pub async fn stream<'a: 'b, 'b, C>(
self,
db: &'a C,
) -> Result<Pin<Box<dyn Stream<Item = Result<S::Item, DbErr>> + 'b>>, DbErr>
where
C: ConnectionTrait<'a>,
S: 'b,
{
let stream = db.stream(self.stmt).await?;
Ok(Box::pin(stream.and_then(|row| {
futures::future::ready(S::from_raw_query_result(row))
})))
}
} }
fn consolidate_query_result<L, R>( fn consolidate_query_result<L, R>(

View File

@ -22,4 +22,6 @@ pub use traits::*;
pub use update::*; pub use update::*;
pub use util::*; pub use util::*;
pub use crate::{ConnectionTrait, InsertResult, Statement, UpdateResult, Value, Values}; pub use crate::{
ConnectionTrait, InsertResult, PaginatorTrait, Statement, UpdateResult, Value, Values,
};

View File

@ -1,7 +1,7 @@
use crate::{DbBackend, Statement}; use crate::{DbBackend, Statement};
use sea_query::QueryStatementBuilder; use sea_query::QueryStatementBuilder;
/// Enforces a set of constraints to any type performing queries on a Model or ActiveModel /// A Trait for any type performing queries on a Model or ActiveModel
pub trait QueryTrait { pub trait QueryTrait {
/// Constrain the QueryStatement to [QueryStatementBuilder] trait /// Constrain the QueryStatement to [QueryStatementBuilder] trait
type QueryStatement: QueryStatementBuilder; type QueryStatement: QueryStatementBuilder;

View File

@ -9,27 +9,27 @@ use sea_query::{
impl Schema { impl Schema {
/// Creates Postgres enums from an Entity. See [TypeCreateStatement] for more details /// Creates Postgres enums from an Entity. See [TypeCreateStatement] for more details
pub fn create_enum_from_entity<E>(entity: E, db_backend: DbBackend) -> Vec<TypeCreateStatement> pub fn create_enum_from_entity<E>(&self, entity: E) -> Vec<TypeCreateStatement>
where where
E: EntityTrait, E: EntityTrait,
{ {
create_enum_from_entity(entity, db_backend) create_enum_from_entity(entity, self.backend)
} }
/// Creates a table from an Entity. See [TableCreateStatement] for more details /// Creates a table from an Entity. See [TableCreateStatement] for more details
pub fn create_table_from_entity<E>(entity: E, db_backend: DbBackend) -> TableCreateStatement pub fn create_table_from_entity<E>(&self, entity: E) -> TableCreateStatement
where where
E: EntityTrait, E: EntityTrait,
{ {
create_table_from_entity(entity, db_backend) create_table_from_entity(entity, self.backend)
} }
} }
pub(crate) fn create_enum_from_entity<E>(_: E, db_backend: DbBackend) -> Vec<TypeCreateStatement> pub(crate) fn create_enum_from_entity<E>(_: E, backend: DbBackend) -> Vec<TypeCreateStatement>
where where
E: EntityTrait, E: EntityTrait,
{ {
if matches!(db_backend, DbBackend::MySql | DbBackend::Sqlite) { if matches!(backend, DbBackend::MySql | DbBackend::Sqlite) {
return Vec::new(); return Vec::new();
} }
let mut vec = Vec::new(); let mut vec = Vec::new();
@ -52,7 +52,7 @@ where
vec vec
} }
pub(crate) fn create_table_from_entity<E>(entity: E, db_backend: DbBackend) -> TableCreateStatement pub(crate) fn create_table_from_entity<E>(entity: E, backend: DbBackend) -> TableCreateStatement
where where
E: EntityTrait, E: EntityTrait,
{ {
@ -61,7 +61,7 @@ where
for column in E::Column::iter() { for column in E::Column::iter() {
let orm_column_def = column.def(); let orm_column_def = column.def();
let types = match orm_column_def.col_type { let types = match orm_column_def.col_type {
ColumnType::Enum(s, variants) => match db_backend { ColumnType::Enum(s, variants) => match backend {
DbBackend::MySql => { DbBackend::MySql => {
ColumnType::Custom(format!("ENUM('{}')", variants.join("', '"))) ColumnType::Custom(format!("ENUM('{}')", variants.join("', '")))
} }
@ -175,8 +175,10 @@ mod tests {
#[test] #[test]
fn test_create_table_from_entity() { fn test_create_table_from_entity() {
let schema = Schema::new(DbBackend::MySql);
assert_eq!( assert_eq!(
Schema::create_table_from_entity(CakeFillingPrice, DbBackend::MySql) schema
.create_table_from_entity(CakeFillingPrice)
.to_string(MysqlQueryBuilder), .to_string(MysqlQueryBuilder),
Table::create() Table::create()
.table(CakeFillingPrice) .table(CakeFillingPrice)

View File

@ -1,5 +1,17 @@
use crate::DbBackend;
mod entity; mod entity;
/// This structure defines a schema for a table /// This is a helper struct to convert [`EntityTrait`](crate::EntityTrait)
/// into different [`sea_query`](crate::sea_query) statements.
#[derive(Debug)] #[derive(Debug)]
pub struct Schema; pub struct Schema {
backend: DbBackend,
}
impl Schema {
/// Create a helper for a specific database backend
pub fn new(backend: DbBackend) -> Self {
Self { backend }
}
}

View File

@ -108,7 +108,9 @@ where
} }
let expect_stmts: Vec<Statement> = creates.iter().map(|stmt| builder.build(stmt)).collect(); let expect_stmts: Vec<Statement> = creates.iter().map(|stmt| builder.build(stmt)).collect();
let create_from_entity_stmts: Vec<Statement> = Schema::create_enum_from_entity(entity, builder) let schema = Schema::new(builder);
let create_from_entity_stmts: Vec<Statement> = schema
.create_enum_from_entity(entity)
.iter() .iter()
.map(|stmt| builder.build(stmt)) .map(|stmt| builder.build(stmt))
.collect(); .collect();
@ -131,8 +133,9 @@ where
E: EntityTrait, E: EntityTrait,
{ {
let builder = db.get_database_backend(); let builder = db.get_database_backend();
let schema = Schema::new(builder);
assert_eq!( assert_eq!(
builder.build(&Schema::create_table_from_entity(entity, builder)), builder.build(&schema.create_table_from_entity(entity)),
builder.build(create) builder.build(create)
); );

View File

@ -1,6 +1,6 @@
pub use super::*; pub use super::*;
use rust_decimal_macros::dec; use rust_decimal_macros::dec;
use sea_orm::DbErr; use sea_orm::{query::*, DbErr};
use uuid::Uuid; use uuid::Uuid;
pub async fn test_update_cake(db: &DbConn) { pub async fn test_update_cake(db: &DbConn) {