Merge remote-tracking branch 'origin/master' into returning
This commit is contained in:
commit
fd50ffd5ea
2
.github/workflows/rust.yml
vendored
2
.github/workflows/rust.yml
vendored
@ -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
|
||||||
|
|
||||||
|
@ -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"]
|
||||||
|
@ -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
18
issues/319/Cargo.toml
Normal 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
14
issues/319/src/main.rs
Normal 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;
|
||||||
|
}
|
20
issues/319/src/material.rs
Normal file
20
issues/319/src/material.rs
Normal 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 {}
|
@ -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 {
|
||||||
|
@ -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>,
|
||||||
|
@ -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")]
|
||||||
|
@ -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>,
|
||||||
|
@ -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>;
|
||||||
|
@ -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())
|
||||||
|
}
|
||||||
|
@ -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()))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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()))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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()))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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,
|
||||||
|
@ -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> {
|
||||||
|
@ -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;
|
||||||
|
@ -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>;
|
||||||
|
@ -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")]
|
||||||
|
@ -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
|
||||||
///
|
///
|
||||||
|
@ -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};
|
||||||
|
@ -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>(
|
||||||
|
@ -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,
|
||||||
|
};
|
||||||
|
@ -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;
|
||||||
|
@ -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)
|
||||||
|
@ -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 }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -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)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -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) {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user