diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index 2926e2ab..8b1ed882 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -316,7 +316,7 @@ jobs: fail-fast: false matrix: os: [ubuntu-latest] - path: [86, 249, 262] + path: [86, 249, 262, 319] steps: - uses: actions/checkout@v2 diff --git a/Cargo.toml b/Cargo.toml index 2345844c..46d972dd 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -63,7 +63,7 @@ default = [ ] macros = ["sea-orm-macros"] 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-rust_decimal = ["rust_decimal", "sea-query/with-rust_decimal"] with-uuid = ["uuid", "sea-query/with-uuid"] diff --git a/examples/basic/src/operation.rs b/examples/basic/src/operation.rs index 34e5441f..49d23919 100644 --- a/examples/basic/src/operation.rs +++ b/examples/basic/src/operation.rs @@ -1,5 +1,5 @@ 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> { insert_and_update(db).await?; diff --git a/issues/319/Cargo.toml b/issues/319/Cargo.toml new file mode 100644 index 00000000..3eb4adaa --- /dev/null +++ b/issues/319/Cargo.toml @@ -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 } diff --git a/issues/319/src/main.rs b/issues/319/src/main.rs new file mode 100644 index 00000000..1710c37c --- /dev/null +++ b/issues/319/src/main.rs @@ -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; +} diff --git a/issues/319/src/material.rs b/issues/319/src/material.rs new file mode 100644 index 00000000..9586189d --- /dev/null +++ b/issues/319/src/material.rs @@ -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, + pub tag_ids: Vec, +} + +#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)] +pub enum Relation {} + +impl ActiveModelBehavior for ActiveModel {} diff --git a/src/database/connection.rs b/src/database/connection.rs index c5730a4b..8c409999 100644 --- a/src/database/connection.rs +++ b/src/database/connection.rs @@ -4,7 +4,7 @@ use crate::{ use futures::Stream; 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 #[async_trait::async_trait] pub trait ConnectionTrait<'a>: Sync { diff --git a/src/database/statement.rs b/src/database/statement.rs index ce911295..a1ba8126 100644 --- a/src/database/statement.rs +++ b/src/database/statement.rs @@ -8,9 +8,10 @@ use std::fmt; pub struct Statement { /// The SQL query pub sql: String, - /// The values for the SQL statement + /// The values for the SQL statement's parameters pub values: Option, - /// 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, } @@ -31,7 +32,7 @@ impl Statement { } /// 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(db_backend: DbBackend, sql: &str, values: I) -> Self where I: IntoIterator, diff --git a/src/database/stream/query.rs b/src/database/stream/query.rs index 03a0fc6e..c2c88d0d 100644 --- a/src/database/stream/query.rs +++ b/src/database/stream/query.rs @@ -1,3 +1,5 @@ +#![allow(missing_docs)] + use std::{pin::Pin, task::Poll}; #[cfg(feature = "mock")] diff --git a/src/database/stream/transaction.rs b/src/database/stream/transaction.rs index 2dddc59c..77a59819 100644 --- a/src/database/stream/transaction.rs +++ b/src/database/stream/transaction.rs @@ -1,3 +1,5 @@ +#![allow(missing_docs)] + use std::{ops::DerefMut, pin::Pin, task::Poll}; use futures::Stream; @@ -11,9 +13,9 @@ use futures::lock::MutexGuard; use crate::{DbErr, InnerConnection, QueryResult, Statement}; -#[ouroboros::self_referencing] /// `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. +#[ouroboros::self_referencing] pub struct TransactionStream<'a> { stmt: Statement, conn: MutexGuard<'a, InnerConnection>, diff --git a/src/driver/mock.rs b/src/driver/mock.rs index 3f271759..c0ffa0a2 100644 --- a/src/driver/mock.rs +++ b/src/driver/mock.rs @@ -23,7 +23,7 @@ pub struct MockDatabaseConnection { mocker: Mutex>, } -/// 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 { /// Execute a statement in the [MockDatabase] fn execute(&mut self, counter: usize, stmt: Statement) -> Result; diff --git a/src/driver/sqlx_common.rs b/src/driver/sqlx_common.rs index 5e035c9c..18ca059d 100644 --- a/src/driver/sqlx_common.rs +++ b/src/driver/sqlx_common.rs @@ -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 { 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()) +} diff --git a/src/driver/sqlx_mysql.rs b/src/driver/sqlx_mysql.rs index b2b89c68..3f9936e4 100644 --- a/src/driver/sqlx_mysql.rs +++ b/src/driver/sqlx_mysql.rs @@ -41,12 +41,11 @@ impl SqlxMySqlConnector { use sqlx::ConnectOptions; opt.disable_statement_logging(); } - if let Ok(pool) = options.pool_options().connect_with(opt).await { - Ok(DatabaseConnection::SqlxMySqlPoolConnection( + match options.pool_options().connect_with(opt).await { + Ok(pool) => Ok(DatabaseConnection::SqlxMySqlPoolConnection( SqlxMySqlPoolConnection { pool }, - )) - } else { - Err(DbErr::Conn("Failed to connect.".to_owned())) + )), + Err(e) => Err(sqlx_error_to_conn_err(e)), } } } diff --git a/src/driver/sqlx_postgres.rs b/src/driver/sqlx_postgres.rs index 84645a58..3abebe31 100644 --- a/src/driver/sqlx_postgres.rs +++ b/src/driver/sqlx_postgres.rs @@ -41,12 +41,11 @@ impl SqlxPostgresConnector { use sqlx::ConnectOptions; opt.disable_statement_logging(); } - if let Ok(pool) = options.pool_options().connect_with(opt).await { - Ok(DatabaseConnection::SqlxPostgresPoolConnection( + match options.pool_options().connect_with(opt).await { + Ok(pool) => Ok(DatabaseConnection::SqlxPostgresPoolConnection( SqlxPostgresPoolConnection { pool }, - )) - } else { - Err(DbErr::Conn("Failed to connect.".to_owned())) + )), + Err(e) => Err(sqlx_error_to_conn_err(e)), } } } diff --git a/src/driver/sqlx_sqlite.rs b/src/driver/sqlx_sqlite.rs index 69eee575..255686d2 100644 --- a/src/driver/sqlx_sqlite.rs +++ b/src/driver/sqlx_sqlite.rs @@ -45,12 +45,11 @@ impl SqlxSqliteConnector { if options.get_max_connections().is_none() { options.max_connections(1); } - if let Ok(pool) = options.pool_options().connect_with(opt).await { - Ok(DatabaseConnection::SqlxSqlitePoolConnection( + match options.pool_options().connect_with(opt).await { + Ok(pool) => Ok(DatabaseConnection::SqlxSqlitePoolConnection( SqlxSqlitePoolConnection { pool }, - )) - } else { - Err(DbErr::Conn("Failed to connect.".to_owned())) + )), + Err(e) => Err(sqlx_error_to_conn_err(e)), } } } diff --git a/src/entity/active_model.rs b/src/entity/active_model.rs index 945b956a..efb234ea 100644 --- a/src/entity/active_model.rs +++ b/src/entity/active_model.rs @@ -76,12 +76,12 @@ where 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]. /// See module level docs [crate::entity] for a full example #[async_trait] pub trait ActiveModelTrait: Clone + Debug { - /// Enforce the type to the constraints of the [EntityTrait] + /// The Entity this ActiveModel belongs to type Entity: EntityTrait; /// 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 -/// Behaviors for users to override. -/// The type must also implement the [ActiveModelTrait] +/// A Trait for overriding the ActiveModel behavior /// /// ### Example /// ```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 where A: ActiveModelTrait, diff --git a/src/entity/base_entity.rs b/src/entity/base_entity.rs index 681cce68..8ce8060a 100644 --- a/src/entity/base_entity.rs +++ b/src/entity/base_entity.rs @@ -13,7 +13,7 @@ pub trait IdenStatic: Iden + Copy + Debug + 'static { 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 { /// Method to get the name for the schema, defaults to [Option::None] if not set fn schema_name(&self) -> Option<&str> { diff --git a/src/entity/link.rs b/src/entity/link.rs index 53d23b97..b929624d 100644 --- a/src/entity/link.rs +++ b/src/entity/link.rs @@ -6,7 +6,7 @@ use sea_query::{Alias, IntoIden, JoinType, SeaRc}; /// Same as [RelationDef] pub type LinkDef = RelationDef; -/// A set of constraints for links between Entities +/// A Trait for links between Entities pub trait Linked { #[allow(missing_docs)] type FromEntity: EntityTrait; diff --git a/src/entity/model.rs b/src/entity/model.rs index 3f162dbe..b11a700b 100644 --- a/src/entity/model.rs +++ b/src/entity/model.rs @@ -5,7 +5,7 @@ use crate::{ pub use sea_query::Value; use std::fmt::Debug; -/// A set of constraints for a Model +/// A Trait for a Model pub trait ModelTrait: Clone + Send + Debug { #[allow(missing_docs)] 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 { /// Instantiate a Model from a [QueryResult] fn from_query_result(res: &QueryResult, pre: &str) -> Result; diff --git a/src/entity/prelude.rs b/src/entity/prelude.rs index 211cf853..28f5fe70 100644 --- a/src/entity/prelude.rs +++ b/src/entity/prelude.rs @@ -1,8 +1,8 @@ pub use crate::{ error::*, ActiveEnum, ActiveModelBehavior, ActiveModelTrait, ColumnDef, ColumnTrait, ColumnType, DatabaseConnection, DbConn, EntityName, EntityTrait, EnumIter, ForeignKeyAction, - Iden, IdenStatic, Linked, ModelTrait, PrimaryKeyToColumn, PrimaryKeyTrait, QueryFilter, - QueryResult, Related, RelationDef, RelationTrait, Select, Value, + Iden, IdenStatic, Linked, ModelTrait, PaginatorTrait, PrimaryKeyToColumn, PrimaryKeyTrait, + QueryFilter, QueryResult, Related, RelationDef, RelationTrait, Select, Value, }; #[cfg(feature = "macros")] diff --git a/src/entity/primary_key.rs b/src/entity/primary_key.rs index d4075a2c..2c596dde 100644 --- a/src/entity/primary_key.rs +++ b/src/entity/primary_key.rs @@ -4,7 +4,7 @@ use sea_query::{FromValueTuple, IntoValueTuple}; use std::fmt::Debug; //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 /// diff --git a/src/executor/paginator.rs b/src/executor/paginator.rs index fd8e822c..f52b3bd8 100644 --- a/src/executor/paginator.rs +++ b/src/executor/paginator.rs @@ -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 futures::Stream; 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 + where + Self: Send + Sized, + { + self.paginate(db, 1).num_items().await + } +} + +impl<'db, C, S> PaginatorTrait<'db, C> for Selector +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 +where + C: ConnectionTrait<'db>, + E: EntityTrait, + M: FromQueryResult + Sized + Send + Sync + 'db, +{ + type Selector = SelectModel; + + 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 +where + C: ConnectionTrait<'db>, + E: EntityTrait, + F: EntityTrait, + M: FromQueryResult + Sized + Send + Sync + 'db, + N: FromQueryResult + Sized + Send + Sync + 'db, +{ + type Selector = SelectTwoModel; + + fn paginate(self, db: &'db C, page_size: usize) -> Paginator<'db, C, Self::Selector> { + self.into_model().paginate(db, page_size) + } +} + #[cfg(test)] #[cfg(feature = "mock")] mod tests { + use super::*; use crate::entity::prelude::*; use crate::{tests_cfg::*, ConnectionTrait}; use crate::{DatabaseConnection, DbBackend, MockDatabase, Transaction}; diff --git a/src/executor/select.rs b/src/executor/select.rs index 5acdae02..9fb906cf 100644 --- a/src/executor/select.rs +++ b/src/executor/select.rs @@ -1,7 +1,7 @@ use crate::{ error::*, ConnectionTrait, EntityTrait, FromQueryResult, IdenStatic, Iterable, ModelTrait, - Paginator, PrimaryKeyToColumn, QueryResult, Select, SelectA, SelectB, SelectTwo, SelectTwoMany, - Statement, TryGetableMany, + PrimaryKeyToColumn, QueryResult, Select, SelectA, SelectB, SelectTwo, SelectTwoMany, Statement, + TryGetableMany, }; use futures::{Stream, TryStreamExt}; use sea_query::SelectStatement; @@ -11,13 +11,13 @@ use std::pin::Pin; #[cfg(feature = "with-json")] 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)] pub struct Selector where S: SelectorTrait, { - query: SelectStatement, + pub(crate) query: SelectStatement, selector: S, } @@ -31,7 +31,7 @@ where 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 { #[allow(missing_docs)] type Item: Sized; @@ -250,7 +250,7 @@ where Selector::>::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, DbErr> where C: ConnectionTrait<'a>, @@ -258,7 +258,7 @@ where 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, DbErr> where C: ConnectionTrait<'a>, @@ -276,26 +276,6 @@ where { 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> - 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 - where - C: ConnectionTrait<'a>, - { - self.paginate(db, 1).num_items().await - } } impl SelectTwo @@ -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)>, DbErr> where C: ConnectionTrait<'a>, @@ -332,7 +312,7 @@ where 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)>, DbErr> where C: ConnectionTrait<'a>, @@ -350,26 +330,6 @@ where { 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> - 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 - where - C: ConnectionTrait<'a>, - { - self.paginate(db, 1).num_items().await - } } impl SelectTwoMany @@ -417,7 +377,7 @@ where 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)>, DbErr> where 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 + 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, DbErr> where C: ConnectionTrait<'a>, { - let builder = db.get_database_backend(); self.query.limit(1); - let row = db.query_one(builder.build(&self.query)).await?; - match row { - Some(row) => Ok(Some(S::from_raw_query_result(row)?)), - None => Ok(None), - } + self.into_selector_raw(db).one(db).await } - /// Get all results from a Select operation + /// Get all items from the Select query pub async fn all<'a, C>(self, db: &C) -> Result, DbErr> where C: ConnectionTrait<'a>, { - let builder = db.get_database_backend(); - 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) + self.into_selector_raw(db).all(db).await } - /// Stream the results of the operation + /// Stream the results of the Select operation pub async fn stream<'a: 'b, 'b, C>( self, db: &'a C, @@ -493,25 +454,7 @@ where C: ConnectionTrait<'a>, S: 'b, { - let builder = db.get_database_backend(); - 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, - } + self.into_selector_raw(db).stream(db).await } } @@ -519,8 +462,7 @@ impl SelectorRaw where S: SelectorTrait, { - /// Create `SelectorRaw` from Statment. Executing this `SelectorRaw` will - /// return a type `M` which implement `FromQueryResult`. + /// Select a custom Model from a raw SQL [Statement]. pub fn from_statement(stmt: Statement) -> SelectorRaw> where M: FromQueryResult, @@ -683,6 +625,7 @@ where } } + /// Get an item from the Select query /// ``` /// # #[cfg(feature = "mock")] /// # use sea_orm::{error::*, tests_cfg::*, MockDatabase, Transaction, DbBackend}; @@ -725,6 +668,7 @@ where } } + /// Get all items from the Select query /// ``` /// # #[cfg(feature = "mock")] /// # use sea_orm::{error::*, tests_cfg::*, MockDatabase, Transaction, DbBackend}; @@ -767,6 +711,21 @@ where } Ok(models) } + + /// Stream the results of the Select operation + pub async fn stream<'a: 'b, 'b, C>( + self, + db: &'a C, + ) -> Result> + '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( diff --git a/src/query/mod.rs b/src/query/mod.rs index fcf8b168..3168e81e 100644 --- a/src/query/mod.rs +++ b/src/query/mod.rs @@ -22,4 +22,6 @@ pub use traits::*; pub use update::*; pub use util::*; -pub use crate::{ConnectionTrait, InsertResult, Statement, UpdateResult, Value, Values}; +pub use crate::{ + ConnectionTrait, InsertResult, PaginatorTrait, Statement, UpdateResult, Value, Values, +}; diff --git a/src/query/traits.rs b/src/query/traits.rs index 0517cc64..cfc35350 100644 --- a/src/query/traits.rs +++ b/src/query/traits.rs @@ -1,7 +1,7 @@ use crate::{DbBackend, Statement}; 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 { /// Constrain the QueryStatement to [QueryStatementBuilder] trait type QueryStatement: QueryStatementBuilder; diff --git a/src/schema/entity.rs b/src/schema/entity.rs index c24a1385..8b0ad144 100644 --- a/src/schema/entity.rs +++ b/src/schema/entity.rs @@ -9,27 +9,27 @@ use sea_query::{ impl Schema { /// Creates Postgres enums from an Entity. See [TypeCreateStatement] for more details - pub fn create_enum_from_entity(entity: E, db_backend: DbBackend) -> Vec + pub fn create_enum_from_entity(&self, entity: E) -> Vec where 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 - pub fn create_table_from_entity(entity: E, db_backend: DbBackend) -> TableCreateStatement + pub fn create_table_from_entity(&self, entity: E) -> TableCreateStatement where E: EntityTrait, { - create_table_from_entity(entity, db_backend) + create_table_from_entity(entity, self.backend) } } -pub(crate) fn create_enum_from_entity(_: E, db_backend: DbBackend) -> Vec +pub(crate) fn create_enum_from_entity(_: E, backend: DbBackend) -> Vec where E: EntityTrait, { - if matches!(db_backend, DbBackend::MySql | DbBackend::Sqlite) { + if matches!(backend, DbBackend::MySql | DbBackend::Sqlite) { return Vec::new(); } let mut vec = Vec::new(); @@ -52,7 +52,7 @@ where vec } -pub(crate) fn create_table_from_entity(entity: E, db_backend: DbBackend) -> TableCreateStatement +pub(crate) fn create_table_from_entity(entity: E, backend: DbBackend) -> TableCreateStatement where E: EntityTrait, { @@ -61,7 +61,7 @@ where for column in E::Column::iter() { let orm_column_def = column.def(); let types = match orm_column_def.col_type { - ColumnType::Enum(s, variants) => match db_backend { + ColumnType::Enum(s, variants) => match backend { DbBackend::MySql => { ColumnType::Custom(format!("ENUM('{}')", variants.join("', '"))) } @@ -175,8 +175,10 @@ mod tests { #[test] fn test_create_table_from_entity() { + let schema = Schema::new(DbBackend::MySql); assert_eq!( - Schema::create_table_from_entity(CakeFillingPrice, DbBackend::MySql) + schema + .create_table_from_entity(CakeFillingPrice) .to_string(MysqlQueryBuilder), Table::create() .table(CakeFillingPrice) diff --git a/src/schema/mod.rs b/src/schema/mod.rs index 3a5ce734..e89d9cc5 100644 --- a/src/schema/mod.rs +++ b/src/schema/mod.rs @@ -1,5 +1,17 @@ +use crate::DbBackend; + 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)] -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 } + } +} diff --git a/tests/common/setup/mod.rs b/tests/common/setup/mod.rs index fae61984..62e06b65 100644 --- a/tests/common/setup/mod.rs +++ b/tests/common/setup/mod.rs @@ -108,7 +108,9 @@ where } let expect_stmts: Vec = creates.iter().map(|stmt| builder.build(stmt)).collect(); - let create_from_entity_stmts: Vec = Schema::create_enum_from_entity(entity, builder) + let schema = Schema::new(builder); + let create_from_entity_stmts: Vec = schema + .create_enum_from_entity(entity) .iter() .map(|stmt| builder.build(stmt)) .collect(); @@ -131,8 +133,9 @@ where E: EntityTrait, { let builder = db.get_database_backend(); + let schema = Schema::new(builder); assert_eq!( - builder.build(&Schema::create_table_from_entity(entity, builder)), + builder.build(&schema.create_table_from_entity(entity)), builder.build(create) ); diff --git a/tests/crud/updates.rs b/tests/crud/updates.rs index 262031ef..55af604e 100644 --- a/tests/crud/updates.rs +++ b/tests/crud/updates.rs @@ -1,6 +1,6 @@ pub use super::*; use rust_decimal_macros::dec; -use sea_orm::DbErr; +use sea_orm::{query::*, DbErr}; use uuid::Uuid; pub async fn test_update_cake(db: &DbConn) {