Merge branch 'master' into ss/bakery

This commit is contained in:
Sam Samai 2021-07-03 21:56:08 +10:00
commit 335bdc5c7c
52 changed files with 471 additions and 477 deletions

View File

@ -19,36 +19,44 @@ license = "MIT OR Apache-2.0"
documentation = "https://docs.rs/sea-orm" documentation = "https://docs.rs/sea-orm"
repository = "https://github.com/SeaQL/sea-orm" repository = "https://github.com/SeaQL/sea-orm"
categories = [ "database" ] categories = [ "database" ]
keywords = [ "orm", "database", "sql", "mysql", "postgres", "sqlite" ] keywords = [ "orm", "database", "sql", "mysql", "postgres", "sqlite", "async" ]
publish = false publish = false
[package.metadata.docs.rs]
features = ["default", "sqlx-mysql", "sqlx-postgres", "sqlx-sqlite", "runtime-async-std-native-tls"]
rustdoc-args = ["--cfg", "docsrs"]
[lib] [lib]
name = "sea_orm" name = "sea_orm"
path = "src/lib.rs" path = "src/lib.rs"
[dependencies] [dependencies]
async-stream = { version = "^0.3" } async-stream = { version = "^0.3" }
chrono = { version = "^0", optional = true }
futures = { version = "^0.3" } futures = { version = "^0.3" }
futures-util = { version = "^0.3" } futures-util = { version = "^0.3" }
sea-query = { version = "^0.12" } sea-query = { version = "^0.12" }
sea-orm-macros = { path = "sea-orm-macros", optional = true } sea-orm-macros = { path = "sea-orm-macros", optional = true }
serde = { version = "^1.0", features = [ "derive" ] } serde = { version = "^1.0", features = [ "derive" ] }
sqlx = { version = "^0.5", optional = true } sqlx = { version = "^0.5", optional = true }
strum = { version = "^0.20", features = [ "derive" ] } strum = { git = "https://github.com/SeaQL/strum.git", branch = "sea-orm", version = "^0.21", features = [ "derive", "sea-orm" ] }
serde_json = { version = "^1", optional = true } serde_json = { version = "^1", optional = true }
[dev-dependencies] [dev-dependencies]
async-std = { version = "^1.9", features = [ "attributes" ] } async-std = { version = "^1.9", features = [ "attributes" ] }
maplit = { version = "^1" } maplit = { version = "^1" }
sea-orm = { path = ".", features = ["sqlx-sqlite", "runtime-async-std-native-tls"] } sea-orm = { path = ".", features = ["sqlx-sqlite", "sqlx-json", "sqlx-chrono", "runtime-async-std-native-tls"] }
[features] [features]
debug-print = [] debug-print = []
default = [ "macros", "with-json", "mock" ] default = [ "macros", "with-json", "with-chrono", "mock" ]
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" ]
sqlx-dep = [ "sqlx", "sqlx/json" ] with-chrono = [ "chrono", "sea-query/with-chrono" ]
sqlx-dep = [ "sqlx" ]
sqlx-json = [ "sqlx/json", "with-json" ]
sqlx-chrono = [ "sqlx/chrono", "with-chrono" ]
sqlx-mysql = [ "sqlx-dep", "sea-query/sqlx-mysql", "sqlx/mysql" ] sqlx-mysql = [ "sqlx-dep", "sea-query/sqlx-mysql", "sqlx/mysql" ]
sqlx-postgres = [ "sqlx-dep", "sea-query/sqlx-postgres", "sqlx/postgres" ] sqlx-postgres = [ "sqlx-dep", "sea-query/sqlx-postgres", "sqlx/postgres" ]
sqlx-sqlite = [ "sqlx-dep", "sea-query/sqlx-sqlite", "sqlx/sqlite" ] sqlx-sqlite = [ "sqlx-dep", "sea-query/sqlx-sqlite", "sqlx/sqlite" ]

View File

@ -1,39 +0,0 @@
# Comparison with Diesel
SeaORM and Diesel shares the same goal: to offer you a complete solution in interfacing with databases.
Both SeaORM and Diesel works with MySQL, Postgres and SQLite, so you aren't constrained going with either.
However, there are things we chose to do differently.
## Architecture
First off, perhaps the number one requested feature, async Rust support. While using async may not offer you better performance today, programming in async is an architectural decision you have to make early on. By choosing SeaORM, we together look forward to Rust's async ecosystem maturing.
Under the hood, SeaORM together with SQLx offers you a pure Rust technology stack. While Diesel is tied to native drivers. Each side has their pros and cons, so it's up to your preference.
SeaORM has a modular design. If you don't like the idea of ORM, you'll definitely still want to use SeaQuery, the underlying query builder. It is light weight and can be easily integrated into any project. The SeaQuery API is also available to you when using SeaORM, so you receive the benefits of high level abstraction while still having the power of a flexible query builder when you need it.
SeaSchema is our schema discovery library, but it is not sealed inside SeaORM. So you can reuse our data structures for developing libraries inter-operating with SeaORM.
## Programming paradigm
In addition to the sync vs async foundation, the biggest distinction between Diesel and SeaORM is static vs dynamic. Diesel has an everything-compile-time design which has its pros and cons. SeaORM is dynamic, in which things are established runtime. It offers more flexibility. While you loses some compile-time guarantee, SeaORM helps you to prove correctness by unit testing instead.
Both libraries make heavy use of traits and generics, but SeaORM generate less types (each column in Diesel is a struct, while each column in SeaORM is a enum variant) and less depthness (there won't be `A<B<C<D>>>`). That probably means looser type/lifetime constraints and faster compilation.
You don't have to use macros when using SeaORM. We provide some derive macros for convenience, but they are entirely optional.
## Schema Builder
While in the Diesel ecosystem there are awesome libraries like barrel, SeaQL maintain the tooling for schema building. That means a familiar API and a more unified experience.
## Similarities
Both Diesel and SeaORM are schema first, meaning it all starts from your existing database schema, instead of starting from your OOP classes.
Both Diesel and SeaORM are relational, meaning you can do complex joins with defined relations.
## Final words
Diesel is a well established library in the Rust ecosystem. SeaORM is very new. Neither can replace the other. We hope that the Rust community will grow stronger together.

130
README.md
View File

@ -14,10 +14,12 @@
# SeaORM # SeaORM
Inspired by ActiveRecord, Eloquent and TypeORM, SeaORM aims to provide you an intuitive and ergonomic Inspired by ActiveRecord, Eloquent and TypeORM, SeaORM aims to provide you an intuitive and ergonomic
API to make working with databases in Rust a first-class experience. API to make working with databases in Rust a first-class experience.
> This is an early WIP of SeaORM, and is not yet published. See [example](examples/sqlx-mysql/src) for demo usage. ```rust
This is a preview of SeaORM, and is not yet released.
```
## Features ## Features
@ -27,12 +29,130 @@ Relying on SQLx, SeaORM is a new library with async support from day 1.
2. Dynamic 2. Dynamic
Built upon SeaQuery, a dynamic query builder, SeaORM allows you to build complex queries without 'fighting the ORM'. Built upon SeaQuery, SeaORM allows you to build complex queries without 'fighting the ORM'.
3. Testable 3. Testable
Use mock connections to write unit tests for your logic. Use mock connections to write unit tests for your logic.
4. API oriented 4. Service oriented
Quickly build search models that help you join, filter, sort and paginate data in APIs. Quickly build services that join, filter, sort and paginate data in APIs.
## A quick taste of SeaORM
### Select
```rust
// find all models
let cakes: Vec<cake::Model> = Cake::find().all(db).await?;
// find and filter
let chocolate: Vec<cake::Model> = Cake::find()
.filter(cake::Column::Name.contains("chocolate"))
.all(db)
.await?;
// find one model
let cheese: Option<cake::Model> = Cake::find_by_id(1).one(db).await?;
let cheese: cake::Model = cheese.unwrap();
// find related models (lazy)
let fruits: Vec<fruit::Model> = cheese.find_related(Fruit).all(db).await?;
// find related models (eager)
let cake_with_fruits: Vec<(cake::Model, Vec<fruit::Model>)> = Cake::find()
.find_with_related(Fruit)
.all(db)
.await?;
```
### Insert
```rust
let apple = fruit::ActiveModel {
name: Set("Apple".to_owned()),
..Default::default() // no need to set primary key
};
let pear = fruit::ActiveModel {
name: Set("Pear".to_owned()),
..Default::default()
};
// insert one
let res: InsertResult = Fruit::insert(pear).exec(db).await?;
println!("InsertResult: {}", res.last_insert_id);
// insert many
Fruit::insert_many(vec![apple, pear]).exec(db).await?;
```
### Update
```rust
use sea_orm::sea_query::{Expr, Value};
let pear: Option<fruit::Model> = Fruit::find_by_id(1).one(db).await?;
let mut pear: fruit::ActiveModel = pear.unwrap().into();
pear.name = Set("Sweet pear".to_owned());
// update one
let pear: fruit::ActiveModel = Fruit::update(pear).exec(db).await?;
// update many: UPDATE "fruit" SET "cake_id" = NULL WHERE "fruit"."name" LIKE '%Apple%'
Fruit::update_many()
.col_expr(fruit::Column::CakeId, Expr::value(Value::Null))
.filter(fruit::Column::Name.contains("Apple"))
.exec(db)
.await?;
```
### Save
```rust
let banana = fruit::ActiveModel {
id: Unset(None),
name: Set("Banana".to_owned()),
..Default::default()
};
// create, because primary key `id` is `Unset`
let mut banana = banana.save(db).await?;
banana.name = Set("Banana Mongo".to_owned());
// update, because primary key `id` is `Set`
let banana = banana.save(db).await?;
```
### Delete
```rust
let orange: Option<fruit::Model> = Fruit::find_by_id(1).one(db).await?;
let orange: fruit::ActiveModel = orange.unwrap().into();
// delete one
fruit::Entity::delete(orange).exec(db).await?;
// or simply
orange.delete(db).await?;
// delete many: DELETE FROM "fruit" WHERE "fruit"."name" LIKE 'Orange'
fruit::Entity::delete_many()
.filter(fruit::Column::Name.contains("Orange"))
.exec(db)
.await?;
```
## License
Licensed under either of
- Apache License, Version 2.0
([LICENSE-APACHE](LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0)
- MIT license
([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT)
at your option.
## Contribution
Unless you explicitly state otherwise, any contribution intentionally submitted
for inclusion in the work by you, as defined in the Apache-2.0 license, shall be
dual licensed as above, without any additional terms or conditions.

View File

@ -0,0 +1,3 @@
# Run `sh develop/cargo-readme.sh` on project root to generate `README.md` from `src/lib.rs`
# cargo install cargo-readme
cargo readme --no-badges --no-indent-headings --no-license --no-template --no-title > README.md

View File

@ -71,13 +71,4 @@ impl Related<super::fruit::Entity> for Entity {
} }
} }
impl Model {
pub fn find_cake_filling(&self) -> Select<super::cake_filling::Entity> {
Entity::find_related().belongs_to::<Entity>(self)
}
pub fn find_fruit(&self) -> Select<super::fruit::Entity> {
Entity::find_related().belongs_to::<Entity>(self)
}
}
impl ActiveModelBehavior for ActiveModel {} impl ActiveModelBehavior for ActiveModel {}

View File

@ -78,13 +78,4 @@ impl Related<super::filling::Entity> for Entity {
} }
} }
impl Model {
pub fn find_cake(&self) -> Select<super::cake::Entity> {
Entity::find_related().belongs_to::<Entity>(self)
}
pub fn find_filling(&self) -> Select<super::filling::Entity> {
Entity::find_related().belongs_to::<Entity>(self)
}
}
impl ActiveModelBehavior for ActiveModel {} impl ActiveModelBehavior for ActiveModel {}

View File

@ -63,10 +63,4 @@ impl Related<super::cake_filling::Entity> for Entity {
} }
} }
impl Model {
pub fn find_cake_filling(&self) -> Select<super::cake_filling::Entity> {
Entity::find_related().belongs_to::<Entity>(self)
}
}
impl ActiveModelBehavior for ActiveModel {} impl ActiveModelBehavior for ActiveModel {}

View File

@ -39,6 +39,7 @@ impl PrimaryKeyTrait for PrimaryKey {
#[derive(Copy, Clone, Debug, EnumIter)] #[derive(Copy, Clone, Debug, EnumIter)]
pub enum Relation { pub enum Relation {
Cake, Cake,
Vendor,
} }
impl ColumnTrait for Column { impl ColumnTrait for Column {
@ -59,6 +60,7 @@ impl RelationTrait for Relation {
.from(Column::CakeId) .from(Column::CakeId)
.to(super::cake::Column::Id) .to(super::cake::Column::Id)
.into(), .into(),
Self::Vendor => Entity::has_many(super::vendor::Entity).into(),
} }
} }
} }
@ -69,9 +71,9 @@ impl Related<super::cake::Entity> for Entity {
} }
} }
impl Model { impl Related<super::vendor::Entity> for Entity {
pub fn find_cake(&self) -> Select<super::cake::Entity> { fn to() -> RelationDef {
Entity::find_related().belongs_to::<Entity>(self) Relation::Vendor.def()
} }
} }

View File

@ -4,3 +4,4 @@ pub mod cake;
pub mod cake_filling; pub mod cake_filling;
pub mod filling; pub mod filling;
pub mod fruit; pub mod fruit;
pub mod vendor;

View File

@ -4,3 +4,4 @@ pub use super::cake::Entity as Cake;
pub use super::cake_filling::Entity as CakeFilling; pub use super::cake_filling::Entity as CakeFilling;
pub use super::filling::Entity as Filling; pub use super::filling::Entity as Filling;
pub use super::fruit::Entity as Fruit; pub use super::fruit::Entity as Fruit;
pub use super::vendor::Entity as Vendor;

View File

@ -47,7 +47,7 @@ impl ColumnTrait for Column {
match self { match self {
Self::Id => ColumnType::Integer.def(), Self::Id => ColumnType::Integer.def(),
Self::Name => ColumnType::String(Some(255u32)).def(), Self::Name => ColumnType::String(Some(255u32)).def(),
Self::FruitId => ColumnType::Integer.def(), Self::FruitId => ColumnType::Integer.def().null(),
} }
} }
} }
@ -55,7 +55,7 @@ impl ColumnTrait for Column {
impl RelationTrait for Relation { impl RelationTrait for Relation {
fn def(&self) -> RelationDef { fn def(&self) -> RelationDef {
match self { match self {
Self::Fruit => Entity::has_one(super::fruit::Entity) Self::Fruit => Entity::belongs_to(super::fruit::Entity)
.from(Column::FruitId) .from(Column::FruitId)
.to(super::fruit::Column::Id) .to(super::fruit::Column::Id)
.into(), .into(),
@ -69,10 +69,4 @@ impl Related<super::fruit::Entity> for Entity {
} }
} }
impl Model {
pub fn find_fruit(&self) -> Select<super::fruit::Entity> {
Entity::find_related().belongs_to::<Entity>(self)
}
}
impl ActiveModelBehavior for ActiveModel {} impl ActiveModelBehavior for ActiveModel {}

View File

@ -53,14 +53,8 @@ impl ColumnTrait for Column {
impl RelationTrait for Relation { impl RelationTrait for Relation {
fn def(&self) -> RelationDef { fn def(&self) -> RelationDef {
match self { match self {
Self::CakeFilling => Entity::has_many(super::cake_filling::Entity) Self::CakeFilling => Entity::has_many(super::cake_filling::Entity).into(),
.from(Column::Id) Self::Fruit => Entity::has_many(super::fruit::Entity).into(),
.to(super::cake_filling::Column::CakeId)
.into(),
Self::Fruit => Entity::has_many(super::fruit::Entity)
.from(Column::Id)
.to(super::fruit::Column::CakeId)
.into(),
} }
} }
} }
@ -77,13 +71,4 @@ impl Related<super::fruit::Entity> for Entity {
} }
} }
impl Model {
pub fn find_cake_filling(&self) -> Select<super::cake_filling::Entity> {
Entity::find_related().belongs_to::<Entity>(self)
}
pub fn find_fruit(&self) -> Select<super::fruit::Entity> {
Entity::find_related().belongs_to::<Entity>(self)
}
}
impl ActiveModelBehavior for ActiveModel {} impl ActiveModelBehavior for ActiveModel {}

View File

@ -54,11 +54,11 @@ impl ColumnTrait for Column {
impl RelationTrait for Relation { impl RelationTrait for Relation {
fn def(&self) -> RelationDef { fn def(&self) -> RelationDef {
match self { match self {
Self::Cake => Entity::has_one(super::cake::Entity) Self::Cake => Entity::belongs_to(super::cake::Entity)
.from(Column::CakeId) .from(Column::CakeId)
.to(super::cake::Column::Id) .to(super::cake::Column::Id)
.into(), .into(),
Self::Filling => Entity::has_one(super::filling::Entity) Self::Filling => Entity::belongs_to(super::filling::Entity)
.from(Column::FillingId) .from(Column::FillingId)
.to(super::filling::Column::Id) .to(super::filling::Column::Id)
.into(), .into(),
@ -78,13 +78,4 @@ impl Related<super::filling::Entity> for Entity {
} }
} }
impl Model {
pub fn find_cake(&self) -> Select<super::cake::Entity> {
Entity::find_related().belongs_to::<Entity>(self)
}
pub fn find_filling(&self) -> Select<super::filling::Entity> {
Entity::find_related().belongs_to::<Entity>(self)
}
}
impl ActiveModelBehavior for ActiveModel {} impl ActiveModelBehavior for ActiveModel {}

View File

@ -52,10 +52,7 @@ impl ColumnTrait for Column {
impl RelationTrait for Relation { impl RelationTrait for Relation {
fn def(&self) -> RelationDef { fn def(&self) -> RelationDef {
match self { match self {
Self::CakeFilling => Entity::has_many(super::cake_filling::Entity) Self::CakeFilling => Entity::has_many(super::cake_filling::Entity).into(),
.from(Column::Id)
.to(super::cake_filling::Column::FillingId)
.into(),
} }
} }
} }
@ -66,10 +63,4 @@ impl Related<super::cake_filling::Entity> for Entity {
} }
} }
impl Model {
pub fn find_cake_filling(&self) -> Select<super::cake_filling::Entity> {
Entity::find_related().belongs_to::<Entity>(self)
}
}
impl ActiveModelBehavior for ActiveModel {} impl ActiveModelBehavior for ActiveModel {}

View File

@ -48,7 +48,7 @@ impl ColumnTrait for Column {
match self { match self {
Self::Id => ColumnType::Integer.def(), Self::Id => ColumnType::Integer.def(),
Self::Name => ColumnType::String(Some(255u32)).def(), Self::Name => ColumnType::String(Some(255u32)).def(),
Self::CakeId => ColumnType::Integer.def(), Self::CakeId => ColumnType::Integer.def().null(),
} }
} }
} }
@ -56,14 +56,11 @@ impl ColumnTrait for Column {
impl RelationTrait for Relation { impl RelationTrait for Relation {
fn def(&self) -> RelationDef { fn def(&self) -> RelationDef {
match self { match self {
Self::Cake => Entity::has_one(super::cake::Entity) Self::Cake => Entity::belongs_to(super::cake::Entity)
.from(Column::CakeId) .from(Column::CakeId)
.to(super::cake::Column::Id) .to(super::cake::Column::Id)
.into(), .into(),
Self::Vendor => Entity::has_many(super::vendor::Entity) Self::Vendor => Entity::has_many(super::vendor::Entity).into(),
.from(Column::Id)
.to(super::vendor::Column::FruitId)
.into(),
} }
} }
} }
@ -80,13 +77,4 @@ impl Related<super::vendor::Entity> for Entity {
} }
} }
impl Model {
pub fn find_cake(&self) -> Select<super::cake::Entity> {
Entity::find_related().belongs_to::<Entity>(self)
}
pub fn find_vendor(&self) -> Select<super::vendor::Entity> {
Entity::find_related().belongs_to::<Entity>(self)
}
}
impl ActiveModelBehavior for ActiveModel {} impl ActiveModelBehavior for ActiveModel {}

View File

@ -47,7 +47,7 @@ impl ColumnTrait for Column {
match self { match self {
Self::Id => ColumnType::Integer.def(), Self::Id => ColumnType::Integer.def(),
Self::Name => ColumnType::String(Some(255u32)).def(), Self::Name => ColumnType::String(Some(255u32)).def(),
Self::FruitId => ColumnType::Integer.def(), Self::FruitId => ColumnType::Integer.def().null(),
} }
} }
} }
@ -55,7 +55,7 @@ impl ColumnTrait for Column {
impl RelationTrait for Relation { impl RelationTrait for Relation {
fn def(&self) -> RelationDef { fn def(&self) -> RelationDef {
match self { match self {
Self::Fruit => Entity::has_one(super::fruit::Entity) Self::Fruit => Entity::belongs_to(super::fruit::Entity)
.from(Column::FruitId) .from(Column::FruitId)
.to(super::fruit::Column::Id) .to(super::fruit::Column::Id)
.into(), .into(),
@ -69,10 +69,4 @@ impl Related<super::fruit::Entity> for Entity {
} }
} }
impl Model {
pub fn find_fruit(&self) -> Select<super::fruit::Entity> {
Entity::find_related().belongs_to::<Entity>(self)
}
}
impl ActiveModelBehavior for ActiveModel {} impl ActiveModelBehavior for ActiveModel {}

View File

@ -6,8 +6,7 @@ publish = false
[dependencies] [dependencies]
async-std = { version = "^1.9", features = [ "attributes" ] } async-std = { version = "^1.9", features = [ "attributes" ] }
sea-orm = { path = "../../", features = [ "sqlx-mysql", "runtime-async-std-native-tls", "debug-print", "with-json", "macros" ], default-features = false } sea-orm = { path = "../../", features = [ "sqlx-mysql", "runtime-async-std-native-tls", "debug-print", "sqlx-json", "macros" ], default-features = false }
strum = { version = "^0.20", features = [ "derive" ] }
serde_json = { version = "^1" } serde_json = { version = "^1" }
futures = { version = "^0.3" } futures = { version = "^0.3" }
async-stream = { version = "^0.3" } async-stream = { version = "^0.3" }

View File

@ -14,7 +14,7 @@ cargo run
All about selects: All about selects:
```sh ```sh
Database { connection: SqlxMySqlPoolConnection } SqlxMySqlPoolConnection
===== ===== ===== =====

View File

@ -1,7 +1,7 @@
use super::*; use super::*;
use sea_orm::{entity::*, query::*, DbConn}; use sea_orm::{entity::*, error::*, query::*, DbConn};
pub async fn all_about_operation(db: &DbConn) -> Result<(), ExecErr> { pub async fn all_about_operation(db: &DbConn) -> Result<(), DbErr> {
insert_and_update(db).await?; insert_and_update(db).await?;
println!("===== =====\n"); println!("===== =====\n");
@ -15,7 +15,7 @@ pub async fn all_about_operation(db: &DbConn) -> Result<(), ExecErr> {
Ok(()) Ok(())
} }
pub async fn insert_and_update(db: &DbConn) -> Result<(), ExecErr> { pub async fn insert_and_update(db: &DbConn) -> Result<(), DbErr> {
let pear = fruit::ActiveModel { let pear = fruit::ActiveModel {
name: Set("pear".to_owned()), name: Set("pear".to_owned()),
..Default::default() ..Default::default()
@ -25,10 +25,7 @@ pub async fn insert_and_update(db: &DbConn) -> Result<(), ExecErr> {
println!(); println!();
println!("Inserted: last_insert_id = {}\n", res.last_insert_id); println!("Inserted: last_insert_id = {}\n", res.last_insert_id);
let pear: Option<fruit::Model> = Fruit::find_by_id(res.last_insert_id) let pear: Option<fruit::Model> = Fruit::find_by_id(res.last_insert_id).one(db).await?;
.one(db)
.await
.map_err(|_| ExecErr)?;
println!(); println!();
println!("Pear: {:?}\n", pear); println!("Pear: {:?}\n", pear);
@ -44,7 +41,7 @@ pub async fn insert_and_update(db: &DbConn) -> Result<(), ExecErr> {
Ok(()) Ok(())
} }
pub async fn save_active_model(db: &DbConn) -> Result<(), ExecErr> { pub async fn save_active_model(db: &DbConn) -> Result<(), DbErr> {
let banana = fruit::ActiveModel { let banana = fruit::ActiveModel {
name: Set("Banana".to_owned()), name: Set("Banana".to_owned()),
..Default::default() ..Default::default()
@ -82,7 +79,7 @@ mod form {
} }
} }
async fn save_custom_active_model(db: &DbConn) -> Result<(), ExecErr> { async fn save_custom_active_model(db: &DbConn) -> Result<(), DbErr> {
let pineapple = form::ActiveModel { let pineapple = form::ActiveModel {
id: Unset(None), id: Unset(None),
name: Set("Pineapple".to_owned()), name: Set("Pineapple".to_owned()),

View File

@ -1,7 +1,7 @@
use super::*; use super::*;
use sea_orm::{entity::*, query::*, DbConn, FromQueryResult}; use sea_orm::{entity::*, error::*, query::*, DbConn, FromQueryResult};
pub async fn all_about_select(db: &DbConn) -> Result<(), QueryErr> { pub async fn all_about_select(db: &DbConn) -> Result<(), DbErr> {
find_all(db).await?; find_all(db).await?;
println!("===== =====\n"); println!("===== =====\n");
@ -41,7 +41,7 @@ pub async fn all_about_select(db: &DbConn) -> Result<(), QueryErr> {
Ok(()) Ok(())
} }
async fn find_all(db: &DbConn) -> Result<(), QueryErr> { async fn find_all(db: &DbConn) -> Result<(), DbErr> {
print!("find all cakes: "); print!("find all cakes: ");
let cakes: Vec<cake::Model> = Cake::find().all(db).await?; let cakes: Vec<cake::Model> = Cake::find().all(db).await?;
@ -63,7 +63,7 @@ async fn find_all(db: &DbConn) -> Result<(), QueryErr> {
Ok(()) Ok(())
} }
async fn find_together(db: &DbConn) -> Result<(), QueryErr> { async fn find_together(db: &DbConn) -> Result<(), DbErr> {
print!("find cakes and fruits: "); print!("find cakes and fruits: ");
let both = Cake::find().find_also_related(Fruit).all(db).await?; let both = Cake::find().find_also_related(Fruit).all(db).await?;
@ -82,7 +82,7 @@ impl Cake {
} }
} }
async fn find_one(db: &DbConn) -> Result<(), QueryErr> { async fn find_one(db: &DbConn) -> Result<(), DbErr> {
print!("find one by primary key: "); print!("find one by primary key: ");
let cheese: Option<cake::Model> = Cake::find_by_id(1).one(db).await?; let cheese: Option<cake::Model> = Cake::find_by_id(1).one(db).await?;
@ -112,7 +112,7 @@ async fn find_one(db: &DbConn) -> Result<(), QueryErr> {
Ok(()) Ok(())
} }
async fn count_fruits_by_cake(db: &DbConn) -> Result<(), QueryErr> { async fn count_fruits_by_cake(db: &DbConn) -> Result<(), DbErr> {
#[derive(Debug, FromQueryResult)] #[derive(Debug, FromQueryResult)]
struct SelectResult { struct SelectResult {
name: String, name: String,
@ -138,7 +138,7 @@ async fn count_fruits_by_cake(db: &DbConn) -> Result<(), QueryErr> {
Ok(()) Ok(())
} }
async fn find_many_to_many(db: &DbConn) -> Result<(), QueryErr> { async fn find_many_to_many(db: &DbConn) -> Result<(), DbErr> {
print!("find cakes and fillings: "); print!("find cakes and fillings: ");
let both: Vec<(cake::Model, Vec<filling::Model>)> = let both: Vec<(cake::Model, Vec<filling::Model>)> =
@ -178,7 +178,7 @@ async fn find_many_to_many(db: &DbConn) -> Result<(), QueryErr> {
Ok(()) Ok(())
} }
async fn all_about_select_json(db: &DbConn) -> Result<(), QueryErr> { async fn all_about_select_json(db: &DbConn) -> Result<(), DbErr> {
find_all_json(&db).await?; find_all_json(&db).await?;
println!("===== =====\n"); println!("===== =====\n");
@ -192,7 +192,7 @@ async fn all_about_select_json(db: &DbConn) -> Result<(), QueryErr> {
Ok(()) Ok(())
} }
async fn find_all_json(db: &DbConn) -> Result<(), QueryErr> { async fn find_all_json(db: &DbConn) -> Result<(), DbErr> {
print!("find all cakes: "); print!("find all cakes: ");
let cakes = Cake::find().into_json().all(db).await?; let cakes = Cake::find().into_json().all(db).await?;
@ -208,7 +208,7 @@ async fn find_all_json(db: &DbConn) -> Result<(), QueryErr> {
Ok(()) Ok(())
} }
async fn find_together_json(db: &DbConn) -> Result<(), QueryErr> { async fn find_together_json(db: &DbConn) -> Result<(), DbErr> {
print!("find cakes and fruits: "); print!("find cakes and fruits: ");
let cakes_fruits = Cake::find() let cakes_fruits = Cake::find()
@ -225,7 +225,7 @@ async fn find_together_json(db: &DbConn) -> Result<(), QueryErr> {
Ok(()) Ok(())
} }
async fn count_fruits_by_cake_json(db: &DbConn) -> Result<(), QueryErr> { async fn count_fruits_by_cake_json(db: &DbConn) -> Result<(), DbErr> {
print!("count fruits by cake: "); print!("count fruits by cake: ");
let count = Cake::find() let count = Cake::find()
@ -243,7 +243,7 @@ async fn count_fruits_by_cake_json(db: &DbConn) -> Result<(), QueryErr> {
Ok(()) Ok(())
} }
async fn find_all_stream(db: &DbConn) -> Result<(), QueryErr> { async fn find_all_stream(db: &DbConn) -> Result<(), DbErr> {
use async_std::task::sleep; use async_std::task::sleep;
use futures::TryStreamExt; use futures::TryStreamExt;
use std::time::Duration; use std::time::Duration;
@ -291,7 +291,7 @@ async fn find_all_stream(db: &DbConn) -> Result<(), QueryErr> {
Ok(()) Ok(())
} }
async fn find_first_page(db: &DbConn) -> Result<(), QueryErr> { async fn find_first_page(db: &DbConn) -> Result<(), DbErr> {
println!("fruits first page: "); println!("fruits first page: ");
let page = fruit::Entity::find().paginate(db, 2).fetch_page(0).await?; let page = fruit::Entity::find().paginate(db, 2).fetch_page(0).await?;
for fruit in page { for fruit in page {
@ -301,7 +301,7 @@ async fn find_first_page(db: &DbConn) -> Result<(), QueryErr> {
Ok(()) Ok(())
} }
async fn find_num_pages(db: &DbConn) -> Result<(), QueryErr> { async fn find_num_pages(db: &DbConn) -> Result<(), DbErr> {
println!("fruits number of page: "); println!("fruits number of page: ");
let num_pages = fruit::Entity::find().paginate(db, 2).num_pages().await?; let num_pages = fruit::Entity::find().paginate(db, 2).num_pages().await?;
println!("{:?}", num_pages); println!("{:?}", num_pages);

View File

@ -40,4 +40,5 @@ pub fn build_cli() -> App<'static, 'static> {
.version(env!("CARGO_PKG_VERSION")) .version(env!("CARGO_PKG_VERSION"))
.setting(AppSettings::VersionlessSubcommands) .setting(AppSettings::VersionlessSubcommands)
.subcommand(entity_subcommand) .subcommand(entity_subcommand)
.setting(AppSettings::SubcommandRequiredElseHelp)
} }

View File

@ -16,7 +16,6 @@ name = "sea_orm_codegen"
path = "src/lib.rs" path = "src/lib.rs"
[dependencies] [dependencies]
sea-orm = { path = "../", features = [ "sqlx-mysql", "runtime-async-std-native-tls", "debug-print", "with-json", "macros" ], default-features = false }
sea-schema = { version = "^0.2", default-features = false, features = [ "sqlx-mysql", "runtime-async-std-native-tls", "discovery", "writer" ] } sea-schema = { version = "^0.2", default-features = false, features = [ "sqlx-mysql", "runtime-async-std-native-tls", "discovery", "writer" ] }
sea-query = { version = "^0.12" } sea-query = { version = "^0.12" }
sqlx = { version = "^0.5", features = [ "mysql", "runtime-async-std-native-tls" ] } sqlx = { version = "^0.5", features = [ "mysql", "runtime-async-std-native-tls" ] }

View File

@ -61,21 +61,21 @@ impl Column {
None => quote! { ColumnType::String(None).def() }, None => quote! { ColumnType::String(None).def() },
}, },
ColumnType::Text => quote! { ColumnType::Text.def() }, ColumnType::Text => quote! { ColumnType::Text.def() },
ColumnType::TinyInteger(s) => quote! { ColumnType::TinyInteger.def() }, ColumnType::TinyInteger(_) => quote! { ColumnType::TinyInteger.def() },
ColumnType::SmallInteger(s) => quote! { ColumnType::SmallInteger.def() }, ColumnType::SmallInteger(_) => quote! { ColumnType::SmallInteger.def() },
ColumnType::Integer(s) => quote! { ColumnType::Integer.def() }, ColumnType::Integer(_) => quote! { ColumnType::Integer.def() },
ColumnType::BigInteger(s) => quote! { ColumnType::BigInteger.def() }, ColumnType::BigInteger(_) => quote! { ColumnType::BigInteger.def() },
ColumnType::Float(s) => quote! { ColumnType::Float.def() }, ColumnType::Float(_) => quote! { ColumnType::Float.def() },
ColumnType::Double(s) => quote! { ColumnType::Double.def() }, ColumnType::Double(_) => quote! { ColumnType::Double.def() },
ColumnType::Decimal(s) => match s { ColumnType::Decimal(s) => match s {
Some((s1, s2)) => quote! { ColumnType::Decimal(Some((#s1, #s2))).def() }, Some((s1, s2)) => quote! { ColumnType::Decimal(Some((#s1, #s2))).def() },
None => quote! { ColumnType::Decimal(None).def() }, None => quote! { ColumnType::Decimal(None).def() },
}, },
ColumnType::DateTime(s) => quote! { ColumnType::DateTime.def() }, ColumnType::DateTime(_) => quote! { ColumnType::DateTime.def() },
ColumnType::Timestamp(s) => quote! { ColumnType::Timestamp.def() }, ColumnType::Timestamp(_) => quote! { ColumnType::Timestamp.def() },
ColumnType::Time(s) => quote! { ColumnType::Time.def() }, ColumnType::Time(_) => quote! { ColumnType::Time.def() },
ColumnType::Date => quote! { ColumnType::Date.def() }, ColumnType::Date => quote! { ColumnType::Date.def() },
ColumnType::Binary(s) => quote! { ColumnType::Binary.def() }, ColumnType::Binary(_) => quote! { ColumnType::Binary.def() },
ColumnType::Boolean => quote! { ColumnType::Boolean.def() }, ColumnType::Boolean => quote! { ColumnType::Boolean.def() },
ColumnType::Money(s) => match s { ColumnType::Money(s) => match s {
Some((s1, s2)) => quote! { ColumnType::Money(Some((#s1, #s2))).def() }, Some((s1, s2)) => quote! { ColumnType::Money(Some((#s1, #s2))).def() },

View File

@ -1,7 +1,7 @@
use crate::{Column, Entity, EntityWriter, Error, PrimaryKey, Relation, RelationType}; use crate::{Column, Entity, EntityWriter, Error, PrimaryKey, Relation, RelationType};
use sea_query::TableStatement; use sea_query::TableStatement;
use sea_schema::mysql::def::Schema; use sea_schema::mysql::def::Schema;
use std::{collections::HashMap, mem::swap}; use std::collections::HashMap;
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub struct EntityTransformer { pub struct EntityTransformer {

View File

@ -117,7 +117,6 @@ impl EntityWriter {
]; ];
code_blocks.extend(Self::gen_impl_related(entity)); code_blocks.extend(Self::gen_impl_related(entity));
code_blocks.extend(vec![ code_blocks.extend(vec![
Self::gen_impl_model(entity),
Self::gen_impl_active_model_behavior(), Self::gen_impl_active_model_behavior(),
]); ]);
code_blocks code_blocks
@ -256,18 +255,6 @@ impl EntityWriter {
.collect() .collect()
} }
pub fn gen_impl_model(entity: &Entity) -> TokenStream {
let relation_ref_tables_snake_case = entity.get_relation_ref_tables_snake_case();
let relation_rel_find_helpers = entity.get_relation_rel_find_helpers();
quote! {
impl Model {
#(pub fn #relation_rel_find_helpers(&self) -> Select<super::#relation_ref_tables_snake_case::Entity> {
Entity::find_related().belongs_to::<Entity>(self)
})*
}
}
}
pub fn gen_impl_active_model_behavior() -> TokenStream { pub fn gen_impl_active_model_behavior() -> TokenStream {
quote! { quote! {
impl ActiveModelBehavior for ActiveModel {} impl ActiveModelBehavior for ActiveModel {}

View File

@ -37,11 +37,11 @@ pub fn expand_derive_active_model(ident: Ident, data: Data) -> syn::Result<Token
} }
impl ActiveModel { impl ActiveModel {
pub async fn save(self, db: &sea_orm::DatabaseConnection) -> Result<Self, sea_orm::ExecErr> { pub async fn save(self, db: &sea_orm::DatabaseConnection) -> Result<Self, sea_orm::DbErr> {
sea_orm::save_active_model::<Self, Entity>(self, db).await sea_orm::save_active_model::<Self, Entity>(self, db).await
} }
pub async fn delete(self, db: &sea_orm::DatabaseConnection) -> Result<sea_orm::DeleteResult, sea_orm::ExecErr> { pub async fn delete(self, db: &sea_orm::DatabaseConnection) -> Result<sea_orm::DeleteResult, sea_orm::DbErr> {
sea_orm::delete_active_model::<Self, Entity>(self, db).await sea_orm::delete_active_model::<Self, Entity>(self, db).await
} }
} }

View File

@ -30,7 +30,7 @@ pub fn expand_derive_from_query_result(ident: Ident, data: Data) -> syn::Result<
Ok(quote!( Ok(quote!(
impl sea_orm::FromQueryResult for #ident { impl sea_orm::FromQueryResult for #ident {
fn from_query_result(row: &sea_orm::QueryResult, pre: &str) -> Result<Self, sea_orm::TypeErr> { fn from_query_result(row: &sea_orm::QueryResult, pre: &str) -> Result<Self, sea_orm::DbErr> {
Ok(Self { Ok(Self {
#(#field: row.try_get(pre, #name)?),* #(#field: row.try_get(pre, #name)?),*
}) })

View File

@ -47,7 +47,7 @@ pub fn expand_derive_model(ident: Ident, data: Data) -> syn::Result<TokenStream>
} }
impl sea_orm::FromQueryResult for #ident { impl sea_orm::FromQueryResult for #ident {
fn from_query_result(row: &sea_orm::QueryResult, pre: &str) -> Result<Self, sea_orm::TypeErr> { fn from_query_result(row: &sea_orm::QueryResult, pre: &str) -> Result<Self, sea_orm::DbErr> {
Ok(Self { Ok(Self {
#(#field: row.try_get(pre, <<Self as ModelTrait>::Entity as EntityTrait>::Column::#name.as_str().into())?),* #(#field: row.try_get(pre, <<Self as ModelTrait>::Entity as EntityTrait>::Column::#name.as_str().into())?),*
}) })

View File

@ -1,9 +1,8 @@
use crate::{ExecErr, ExecResult, QueryErr, QueryResult, Statement, Transaction}; use crate::{error::*, ExecResult, QueryResult, Statement};
use sea_query::{ use sea_query::{
MysqlQueryBuilder, PostgresQueryBuilder, QueryStatementBuilder, SchemaStatementBuilder, MysqlQueryBuilder, PostgresQueryBuilder, QueryStatementBuilder, SchemaStatementBuilder,
SqliteQueryBuilder, SqliteQueryBuilder,
}; };
use std::{error::Error, fmt};
pub enum DatabaseConnection { pub enum DatabaseConnection {
#[cfg(feature = "sqlx-mysql")] #[cfg(feature = "sqlx-mysql")]
@ -29,17 +28,6 @@ pub enum SchemaBuilderBackend {
Sqlite, Sqlite,
} }
#[derive(Debug)]
pub struct ConnectionErr;
impl Error for ConnectionErr {}
impl fmt::Display for ConnectionErr {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{:?}", self)
}
}
impl Default for DatabaseConnection { impl Default for DatabaseConnection {
fn default() -> Self { fn default() -> Self {
Self::Disconnected Self::Disconnected
@ -89,7 +77,7 @@ impl DatabaseConnection {
} }
} }
pub async fn execute(&self, stmt: Statement) -> Result<ExecResult, ExecErr> { pub async fn execute(&self, stmt: Statement) -> Result<ExecResult, DbErr> {
match self { match self {
#[cfg(feature = "sqlx-mysql")] #[cfg(feature = "sqlx-mysql")]
DatabaseConnection::SqlxMySqlPoolConnection(conn) => conn.execute(stmt).await, DatabaseConnection::SqlxMySqlPoolConnection(conn) => conn.execute(stmt).await,
@ -101,7 +89,7 @@ impl DatabaseConnection {
} }
} }
pub async fn query_one(&self, stmt: Statement) -> Result<Option<QueryResult>, QueryErr> { pub async fn query_one(&self, stmt: Statement) -> Result<Option<QueryResult>, DbErr> {
match self { match self {
#[cfg(feature = "sqlx-mysql")] #[cfg(feature = "sqlx-mysql")]
DatabaseConnection::SqlxMySqlPoolConnection(conn) => conn.query_one(stmt).await, DatabaseConnection::SqlxMySqlPoolConnection(conn) => conn.query_one(stmt).await,
@ -113,7 +101,7 @@ impl DatabaseConnection {
} }
} }
pub async fn query_all(&self, stmt: Statement) -> Result<Vec<QueryResult>, QueryErr> { pub async fn query_all(&self, stmt: Statement) -> Result<Vec<QueryResult>, DbErr> {
match self { match self {
#[cfg(feature = "sqlx-mysql")] #[cfg(feature = "sqlx-mysql")]
DatabaseConnection::SqlxMySqlPoolConnection(conn) => conn.query_all(stmt).await, DatabaseConnection::SqlxMySqlPoolConnection(conn) => conn.query_all(stmt).await,
@ -139,7 +127,7 @@ impl DatabaseConnection {
} }
#[cfg(feature = "mock")] #[cfg(feature = "mock")]
pub fn into_transaction_log(self) -> Vec<Transaction> { pub fn into_transaction_log(self) -> Vec<crate::Transaction> {
let mut mocker = self.as_mock_connection().get_mocker_mutex().lock().unwrap(); let mut mocker = self.as_mock_connection().get_mocker_mutex().lock().unwrap();
mocker.drain_transaction_log() mocker.drain_transaction_log()
} }

View File

@ -1,7 +1,7 @@
use crate::{ use crate::{
DatabaseConnection, EntityTrait, ExecErr, ExecResult, ExecResultHolder, Iden, Iterable, error::*, DatabaseConnection, EntityTrait, ExecResult, ExecResultHolder, Iden, Iterable,
MockDatabaseConnection, MockDatabaseTrait, ModelTrait, QueryErr, QueryResult, QueryResultRow, MockDatabaseConnection, MockDatabaseTrait, ModelTrait, QueryResult, QueryResultRow, Statement,
Statement, Transaction, TypeErr, Transaction,
}; };
use sea_query::{Value, ValueType}; use sea_query::{Value, ValueType};
use std::collections::BTreeMap; use std::collections::BTreeMap;
@ -68,14 +68,14 @@ impl MockDatabase {
} }
impl MockDatabaseTrait for MockDatabase { impl MockDatabaseTrait for MockDatabase {
fn execute(&mut self, counter: usize, statement: Statement) -> Result<ExecResult, ExecErr> { fn execute(&mut self, counter: usize, statement: Statement) -> Result<ExecResult, DbErr> {
self.transaction_log.push(Transaction::one(statement)); self.transaction_log.push(Transaction::one(statement));
if counter < self.exec_results.len() { if counter < self.exec_results.len() {
Ok(ExecResult { Ok(ExecResult {
result: ExecResultHolder::Mock(std::mem::take(&mut self.exec_results[counter])), result: ExecResultHolder::Mock(std::mem::take(&mut self.exec_results[counter])),
}) })
} else { } else {
Err(ExecErr) Err(DbErr::Exec("`exec_results` buffer is empty.".to_owned()))
} }
} }
@ -83,7 +83,7 @@ impl MockDatabaseTrait for MockDatabase {
&mut self, &mut self,
counter: usize, counter: usize,
statement: Statement, statement: Statement,
) -> Result<Vec<QueryResult>, QueryErr> { ) -> Result<Vec<QueryResult>, DbErr> {
self.transaction_log.push(Transaction::one(statement)); self.transaction_log.push(Transaction::one(statement));
if counter < self.query_results.len() { if counter < self.query_results.len() {
Ok(std::mem::take(&mut self.query_results[counter]) Ok(std::mem::take(&mut self.query_results[counter])
@ -93,7 +93,7 @@ impl MockDatabaseTrait for MockDatabase {
}) })
.collect()) .collect())
} else { } else {
Err(QueryErr) Err(DbErr::Query("`query_results` buffer is empty.".to_owned()))
} }
} }
@ -103,7 +103,7 @@ impl MockDatabaseTrait for MockDatabase {
} }
impl MockRow { impl MockRow {
pub fn try_get<T>(&self, col: &str) -> Result<T, TypeErr> pub fn try_get<T>(&self, col: &str) -> Result<T, DbErr>
where where
T: ValueType, T: ValueType,
{ {

View File

@ -10,23 +10,25 @@ pub use mock::*;
pub use statement::*; pub use statement::*;
pub use transaction::*; pub use transaction::*;
use crate::DbErr;
#[derive(Debug, Default)] #[derive(Debug, Default)]
pub struct Database; pub struct Database;
impl Database { impl Database {
pub async fn connect(string: &str) -> Result<DatabaseConnection, ConnectionErr> { pub async fn connect(string: &str) -> Result<DatabaseConnection, DbErr> {
#[cfg(feature = "sqlx-mysql")] #[cfg(feature = "sqlx-mysql")]
if crate::SqlxMySqlConnector::accepts(string) { if crate::SqlxMySqlConnector::accepts(string) {
return Ok(crate::SqlxMySqlConnector::connect(string).await?); return crate::SqlxMySqlConnector::connect(string).await;
} }
#[cfg(feature = "sqlx-sqlite")] #[cfg(feature = "sqlx-sqlite")]
if crate::SqlxSqliteConnector::accepts(string) { if crate::SqlxSqliteConnector::accepts(string) {
return Ok(crate::SqlxSqliteConnector::connect(string).await?); return crate::SqlxSqliteConnector::connect(string).await;
} }
#[cfg(feature = "mock")] #[cfg(feature = "mock")]
if crate::MockDatabaseConnector::accepts(string) { if crate::MockDatabaseConnector::accepts(string) {
return Ok(crate::MockDatabaseConnector::connect(string).await?); return crate::MockDatabaseConnector::connect(string).await;
} }
Err(ConnectionErr) Err(DbErr::Conn(format!("The connection string '{}' has no supporting driver.", string)))
} }
} }

View File

@ -1,6 +1,6 @@
use crate::{ use crate::{
debug_print, ConnectionErr, DatabaseConnection, ExecErr, ExecResult, MockDatabase, QueryErr, debug_print, error::*, DatabaseConnection, ExecResult, MockDatabase, QueryResult, Statement,
QueryResult, Statement, Transaction, Transaction,
}; };
use std::sync::{ use std::sync::{
atomic::{AtomicUsize, Ordering}, atomic::{AtomicUsize, Ordering},
@ -15,9 +15,9 @@ pub struct MockDatabaseConnection {
} }
pub trait MockDatabaseTrait: Send { pub trait MockDatabaseTrait: Send {
fn execute(&mut self, counter: usize, stmt: Statement) -> Result<ExecResult, ExecErr>; fn execute(&mut self, counter: usize, stmt: Statement) -> Result<ExecResult, DbErr>;
fn query(&mut self, counter: usize, stmt: Statement) -> Result<Vec<QueryResult>, QueryErr>; fn query(&mut self, counter: usize, stmt: Statement) -> Result<Vec<QueryResult>, DbErr>;
fn drain_transaction_log(&mut self) -> Vec<Transaction>; fn drain_transaction_log(&mut self) -> Vec<Transaction>;
} }
@ -27,7 +27,7 @@ impl MockDatabaseConnector {
string.starts_with("mock://") string.starts_with("mock://")
} }
pub async fn connect(_string: &str) -> Result<DatabaseConnection, ConnectionErr> { pub async fn connect(_string: &str) -> Result<DatabaseConnection, DbErr> {
Ok(DatabaseConnection::MockDatabaseConnection( Ok(DatabaseConnection::MockDatabaseConnection(
MockDatabaseConnection::new(MockDatabase::new()), MockDatabaseConnection::new(MockDatabase::new()),
)) ))
@ -49,20 +49,20 @@ impl MockDatabaseConnection {
&self.mocker &self.mocker
} }
pub async fn execute(&self, statement: Statement) -> Result<ExecResult, ExecErr> { pub async fn execute(&self, statement: Statement) -> Result<ExecResult, DbErr> {
debug_print!("{}", statement); debug_print!("{}", statement);
let counter = self.counter.fetch_add(1, Ordering::SeqCst); let counter = self.counter.fetch_add(1, Ordering::SeqCst);
self.mocker.lock().unwrap().execute(counter, statement) self.mocker.lock().unwrap().execute(counter, statement)
} }
pub async fn query_one(&self, statement: Statement) -> Result<Option<QueryResult>, QueryErr> { pub async fn query_one(&self, statement: Statement) -> Result<Option<QueryResult>, DbErr> {
debug_print!("{}", statement); debug_print!("{}", statement);
let counter = self.counter.fetch_add(1, Ordering::SeqCst); let counter = self.counter.fetch_add(1, Ordering::SeqCst);
let result = self.mocker.lock().unwrap().query(counter, statement)?; let result = self.mocker.lock().unwrap().query(counter, statement)?;
Ok(result.into_iter().next()) Ok(result.into_iter().next())
} }
pub async fn query_all(&self, statement: Statement) -> Result<Vec<QueryResult>, QueryErr> { pub async fn query_all(&self, statement: Statement) -> Result<Vec<QueryResult>, DbErr> {
debug_print!("{}", statement); debug_print!("{}", statement);
let counter = self.counter.fetch_add(1, Ordering::SeqCst); let counter = self.counter.fetch_add(1, Ordering::SeqCst);
self.mocker.lock().unwrap().query(counter, statement) self.mocker.lock().unwrap().query(counter, statement)

View File

@ -1,17 +1,17 @@
#[cfg(feature = "mock")] #[cfg(feature = "mock")]
mod mock; mod mock;
#[cfg(feature = "sqlx-dep")]
mod sqlx_common;
#[cfg(feature = "sqlx-mysql")] #[cfg(feature = "sqlx-mysql")]
mod sqlx_mysql; mod sqlx_mysql;
#[cfg(feature = "sqlx-sqlite")] #[cfg(feature = "sqlx-sqlite")]
mod sqlx_sqlite; mod sqlx_sqlite;
#[cfg(feature = "sqlx-dep")]
mod sqlx_types;
#[cfg(feature = "mock")] #[cfg(feature = "mock")]
pub use mock::*; pub use mock::*;
#[cfg(feature = "sqlx-dep")]
pub use sqlx_common::*;
#[cfg(feature = "sqlx-mysql")] #[cfg(feature = "sqlx-mysql")]
pub use sqlx_mysql::*; pub use sqlx_mysql::*;
#[cfg(feature = "sqlx-sqlite")] #[cfg(feature = "sqlx-sqlite")]
pub use sqlx_sqlite::*; pub use sqlx_sqlite::*;
#[cfg(feature = "sqlx-dep")]
pub use sqlx_types::*;

View File

@ -0,0 +1,9 @@
use crate::DbErr;
pub fn sqlx_error_to_exec_err(err: sqlx::Error) -> DbErr {
DbErr::Exec(err.to_string())
}
pub fn sqlx_error_to_query_err(err: sqlx::Error) -> DbErr {
DbErr::Query(err.to_string())
}

View File

@ -6,7 +6,9 @@ use sqlx::{
sea_query::sea_query_driver_mysql!(); sea_query::sea_query_driver_mysql!();
use sea_query_driver_mysql::bind_query; use sea_query_driver_mysql::bind_query;
use crate::{debug_print, executor::*, ConnectionErr, DatabaseConnection, Statement}; use crate::{debug_print, error::*, executor::*, DatabaseConnection, Statement};
use super::sqlx_common::*;
pub struct SqlxMySqlConnector; pub struct SqlxMySqlConnector;
@ -19,13 +21,13 @@ impl SqlxMySqlConnector {
string.starts_with("mysql://") string.starts_with("mysql://")
} }
pub async fn connect(string: &str) -> Result<DatabaseConnection, ConnectionErr> { pub async fn connect(string: &str) -> Result<DatabaseConnection, DbErr> {
if let Ok(pool) = MySqlPool::connect(string).await { if let Ok(pool) = MySqlPool::connect(string).await {
Ok(DatabaseConnection::SqlxMySqlPoolConnection( Ok(DatabaseConnection::SqlxMySqlPoolConnection(
SqlxMySqlPoolConnection { pool }, SqlxMySqlPoolConnection { pool },
)) ))
} else { } else {
Err(ConnectionErr) Err(DbErr::Conn("Failed to connect.".to_owned()))
} }
} }
} }
@ -37,43 +39,49 @@ impl SqlxMySqlConnector {
} }
impl SqlxMySqlPoolConnection { impl SqlxMySqlPoolConnection {
pub async fn execute(&self, stmt: Statement) -> Result<ExecResult, ExecErr> { pub async fn execute(&self, stmt: Statement) -> Result<ExecResult, DbErr> {
debug_print!("{}", stmt); debug_print!("{}", stmt);
let query = sqlx_query(&stmt); let query = sqlx_query(&stmt);
if let Ok(conn) = &mut self.pool.acquire().await { if let Ok(conn) = &mut self.pool.acquire().await {
if let Ok(res) = query.execute(conn).await { match query.execute(conn).await {
return Ok(res.into()); Ok(res) => Ok(res.into()),
} Err(err) => Err(sqlx_error_to_exec_err(err)),
}
Err(ExecErr)
}
pub async fn query_one(&self, stmt: Statement) -> Result<Option<QueryResult>, QueryErr> {
debug_print!("{}", stmt);
let query = sqlx_query(&stmt);
if let Ok(conn) = &mut self.pool.acquire().await {
if let Ok(row) = query.fetch_one(conn).await {
Ok(Some(row.into()))
} else {
Ok(None)
} }
} else { } else {
Err(QueryErr) Err(DbErr::Exec("Failed to acquire connection from pool.".to_owned()))
} }
} }
pub async fn query_all(&self, stmt: Statement) -> Result<Vec<QueryResult>, QueryErr> { pub async fn query_one(&self, stmt: Statement) -> Result<Option<QueryResult>, DbErr> {
debug_print!("{}", stmt); debug_print!("{}", stmt);
let query = sqlx_query(&stmt); let query = sqlx_query(&stmt);
if let Ok(conn) = &mut self.pool.acquire().await { if let Ok(conn) = &mut self.pool.acquire().await {
if let Ok(rows) = query.fetch_all(conn).await { match query.fetch_one(conn).await {
return Ok(rows.into_iter().map(|r| r.into()).collect()); Ok(row) => Ok(Some(row.into())),
Err(err) => match err {
sqlx::Error::RowNotFound => Ok(None),
_ => Err(DbErr::Query(err.to_string())),
},
} }
} else {
Err(DbErr::Query("Failed to acquire connection from pool.".to_owned()))
}
}
pub async fn query_all(&self, stmt: Statement) -> Result<Vec<QueryResult>, DbErr> {
debug_print!("{}", stmt);
let query = sqlx_query(&stmt);
if let Ok(conn) = &mut self.pool.acquire().await {
match query.fetch_all(conn).await {
Ok(rows) => Ok(rows.into_iter().map(|r| r.into()).collect()),
Err(err) => Err(sqlx_error_to_query_err(err)),
}
} else {
Err(DbErr::Query("Failed to acquire connection from pool.".to_owned()))
} }
Err(QueryErr)
} }
} }

View File

@ -6,7 +6,9 @@ use sqlx::{
sea_query::sea_query_driver_sqlite!(); sea_query::sea_query_driver_sqlite!();
use sea_query_driver_sqlite::bind_query; use sea_query_driver_sqlite::bind_query;
use crate::{debug_print, executor::*, ConnectionErr, DatabaseConnection, Statement}; use crate::{debug_print, error::*, executor::*, DatabaseConnection, Statement};
use super::sqlx_common::*;
pub struct SqlxSqliteConnector; pub struct SqlxSqliteConnector;
@ -19,13 +21,13 @@ impl SqlxSqliteConnector {
string.starts_with("sqlite:") string.starts_with("sqlite:")
} }
pub async fn connect(string: &str) -> Result<DatabaseConnection, ConnectionErr> { pub async fn connect(string: &str) -> Result<DatabaseConnection, DbErr> {
if let Ok(pool) = SqlitePool::connect(string).await { if let Ok(pool) = SqlitePool::connect(string).await {
Ok(DatabaseConnection::SqlxSqlitePoolConnection( Ok(DatabaseConnection::SqlxSqlitePoolConnection(
SqlxSqlitePoolConnection { pool }, SqlxSqlitePoolConnection { pool },
)) ))
} else { } else {
Err(ConnectionErr) Err(DbErr::Conn("Failed to connect.".to_owned()))
} }
} }
} }
@ -37,43 +39,49 @@ impl SqlxSqliteConnector {
} }
impl SqlxSqlitePoolConnection { impl SqlxSqlitePoolConnection {
pub async fn execute(&self, stmt: Statement) -> Result<ExecResult, ExecErr> { pub async fn execute(&self, stmt: Statement) -> Result<ExecResult, DbErr> {
debug_print!("{}", stmt); debug_print!("{}", stmt);
let query = sqlx_query(&stmt); let query = sqlx_query(&stmt);
if let Ok(conn) = &mut self.pool.acquire().await { if let Ok(conn) = &mut self.pool.acquire().await {
if let Ok(res) = query.execute(conn).await { match query.execute(conn).await {
return Ok(res.into()); Ok(res) => Ok(res.into()),
} Err(err) => Err(sqlx_error_to_exec_err(err)),
}
Err(ExecErr)
}
pub async fn query_one(&self, stmt: Statement) -> Result<Option<QueryResult>, QueryErr> {
debug_print!("{}", stmt);
let query = sqlx_query(&stmt);
if let Ok(conn) = &mut self.pool.acquire().await {
if let Ok(row) = query.fetch_one(conn).await {
Ok(Some(row.into()))
} else {
Ok(None)
} }
} else { } else {
Err(QueryErr) Err(DbErr::Exec("Failed to acquire connection from pool.".to_owned()))
} }
} }
pub async fn query_all(&self, stmt: Statement) -> Result<Vec<QueryResult>, QueryErr> { pub async fn query_one(&self, stmt: Statement) -> Result<Option<QueryResult>, DbErr> {
debug_print!("{}", stmt); debug_print!("{}", stmt);
let query = sqlx_query(&stmt); let query = sqlx_query(&stmt);
if let Ok(conn) = &mut self.pool.acquire().await { if let Ok(conn) = &mut self.pool.acquire().await {
if let Ok(rows) = query.fetch_all(conn).await { match query.fetch_one(conn).await {
return Ok(rows.into_iter().map(|r| r.into()).collect()); Ok(row) => Ok(Some(row.into())),
Err(err) => match err {
sqlx::Error::RowNotFound => Ok(None),
_ => Err(DbErr::Query(err.to_string())),
},
} }
} else {
Err(DbErr::Query("Failed to acquire connection from pool.".to_owned()))
}
}
pub async fn query_all(&self, stmt: Statement) -> Result<Vec<QueryResult>, DbErr> {
debug_print!("{}", stmt);
let query = sqlx_query(&stmt);
if let Ok(conn) = &mut self.pool.acquire().await {
match query.fetch_all(conn).await {
Ok(rows) => Ok(rows.into_iter().map(|r| r.into()).collect()),
Err(err) => Err(sqlx_error_to_query_err(err)),
}
} else {
Err(DbErr::Query("Failed to acquire connection from pool.".to_owned()))
} }
Err(QueryErr)
} }
} }

View File

@ -1,13 +0,0 @@
use crate::{ExecErr, TypeErr};
impl From<sqlx::Error> for TypeErr {
fn from(_: sqlx::Error) -> TypeErr {
TypeErr
}
}
impl From<sqlx::Error> for ExecErr {
fn from(_: sqlx::Error) -> ExecErr {
ExecErr
}
}

View File

@ -1,5 +1,5 @@
use crate::{ use crate::{
DatabaseConnection, DeleteResult, EntityTrait, ExecErr, Iterable, PrimaryKeyToColumn, error::*, DatabaseConnection, DeleteResult, EntityTrait, Iterable, PrimaryKeyToColumn,
PrimaryKeyTrait, Value, PrimaryKeyTrait, Value,
}; };
use std::fmt::Debug; use std::fmt::Debug;
@ -66,8 +66,8 @@ pub trait ActiveModelTrait: Clone + Debug {
fn default() -> Self; fn default() -> Self;
// below is not yet possible. right now we define these methods in DeriveActiveModel // below is not yet possible. right now we define these methods in DeriveActiveModel
// fn save(self, db: &DatabaseConnection) -> impl Future<Output = Result<Self, ExecErr>>; // fn save(self, db: &DatabaseConnection) -> impl Future<Output = Result<Self, DbErr>>;
// fn delete(self, db: &DatabaseConnection) -> impl Future<Output = Result<DeleteResult, ExecErr>>; // fn delete(self, db: &DatabaseConnection) -> impl Future<Output = Result<DeleteResult, DbErr>>;
} }
/// Behaviors for users to override /// Behaviors for users to override
@ -188,7 +188,7 @@ where
/// Insert the model if primary key is unset, update otherwise. /// Insert the model if primary key is unset, update otherwise.
/// Only works if the entity has auto increment primary key. /// Only works if the entity has auto increment primary key.
pub async fn save_active_model<A, E>(mut am: A, db: &DatabaseConnection) -> Result<A, ExecErr> pub async fn save_active_model<A, E>(mut am: A, db: &DatabaseConnection) -> Result<A, DbErr>
where where
A: ActiveModelBehavior + ActiveModelTrait<Entity = E>, A: ActiveModelBehavior + ActiveModelTrait<Entity = E>,
E::Model: IntoActiveModel<A>, E::Model: IntoActiveModel<A>,
@ -212,7 +212,7 @@ where
Ok(am) Ok(am)
} }
async fn insert_and_select_active_model<A, E>(am: A, db: &DatabaseConnection) -> Result<A, ExecErr> async fn insert_and_select_active_model<A, E>(am: A, db: &DatabaseConnection) -> Result<A, DbErr>
where where
A: ActiveModelTrait<Entity = E>, A: ActiveModelTrait<Entity = E>,
E::Model: IntoActiveModel<A>, E::Model: IntoActiveModel<A>,
@ -223,18 +223,18 @@ where
// TODO: if the entity does not have auto increment primary key, then last_insert_id is a wrong value // TODO: if the entity does not have auto increment primary key, then last_insert_id is a wrong value
if <E::PrimaryKey as PrimaryKeyTrait>::auto_increment() && res.last_insert_id != 0 { if <E::PrimaryKey as PrimaryKeyTrait>::auto_increment() && res.last_insert_id != 0 {
let find = E::find_by_id(res.last_insert_id).one(db); let find = E::find_by_id(res.last_insert_id).one(db);
let res = find.await; let found = find.await;
let model: Option<E::Model> = res.map_err(|_| ExecErr)?; let model: Option<E::Model> = found?;
match model { match model {
Some(model) => Ok(model.into_active_model()), Some(model) => Ok(model.into_active_model()),
None => Err(ExecErr), None => Err(DbErr::Exec(format!("Failed to find inserted item: {} {}", E::default().to_string(), res.last_insert_id))),
} }
} else { } else {
Ok(A::default()) Ok(A::default())
} }
} }
async fn update_active_model<A, E>(am: A, db: &DatabaseConnection) -> Result<A, ExecErr> async fn update_active_model<A, E>(am: A, db: &DatabaseConnection) -> Result<A, DbErr>
where where
A: ActiveModelTrait<Entity = E>, A: ActiveModelTrait<Entity = E>,
E: EntityTrait, E: EntityTrait,
@ -246,7 +246,7 @@ where
pub async fn delete_active_model<A, E>( pub async fn delete_active_model<A, E>(
mut am: A, mut am: A,
db: &DatabaseConnection, db: &DatabaseConnection,
) -> Result<DeleteResult, ExecErr> ) -> Result<DeleteResult, DbErr>
where where
A: ActiveModelBehavior + ActiveModelTrait<Entity = E>, A: ActiveModelBehavior + ActiveModelTrait<Entity = E>,
E: EntityTrait, E: EntityTrait,

View File

@ -1,4 +1,4 @@
use crate::{EntityTrait, QueryFilter, QueryResult, Related, Select, TypeErr}; use crate::{EntityTrait, DbErr, QueryFilter, QueryResult, Related, Select};
pub use sea_query::Value; pub use sea_query::Value;
use std::fmt::Debug; use std::fmt::Debug;
@ -19,11 +19,11 @@ pub trait ModelTrait: Clone + Debug {
} }
pub trait FromQueryResult { pub trait FromQueryResult {
fn from_query_result(res: &QueryResult, pre: &str) -> Result<Self, TypeErr> fn from_query_result(res: &QueryResult, pre: &str) -> Result<Self, DbErr>
where where
Self: Sized; Self: Sized;
fn from_query_result_optional(res: &QueryResult, pre: &str) -> Result<Option<Self>, TypeErr> fn from_query_result_optional(res: &QueryResult, pre: &str) -> Result<Option<Self>, DbErr>
where where
Self: Sized, Self: Sized,
{ {

View File

@ -1,7 +1,7 @@
pub use crate::{ pub use crate::{
ActiveModelBehavior, ActiveModelTrait, ColumnDef, ColumnTrait, ColumnType, DeriveActiveModel, error::*, ActiveModelBehavior, ActiveModelTrait, ColumnDef, ColumnTrait, ColumnType,
DeriveActiveModelBehavior, DeriveColumn, DeriveEntity, DeriveModel, DerivePrimaryKey, DeriveActiveModel, DeriveActiveModelBehavior, DeriveColumn, DeriveEntity, DeriveModel,
EntityName, EntityTrait, EnumIter, Iden, IdenStatic, ModelTrait, PrimaryKeyToColumn, DerivePrimaryKey, EntityName, EntityTrait, EnumIter, Iden, IdenStatic, ModelTrait,
PrimaryKeyTrait, QueryFilter, QueryResult, Related, RelationDef, RelationTrait, Select, PrimaryKeyToColumn, PrimaryKeyTrait, QueryFilter, QueryResult, Related, RelationDef,
TypeErr, Value, RelationTrait, Select, Value,
}; };

16
src/error.rs Normal file
View File

@ -0,0 +1,16 @@
#[derive(Debug)]
pub enum DbErr {
Conn(String),
Exec(String),
Query(String),
}
impl std::fmt::Display for DbErr {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
match self {
Self::Conn(s) => write!(f, "Connection Error: {}", s),
Self::Exec(s) => write!(f, "Execution Error: {}", s),
Self::Query(s) => write!(f, "Query Error: {}", s),
}
}
}

View File

@ -1,5 +1,5 @@
use crate::{ use crate::{
ActiveModelTrait, DatabaseConnection, DeleteMany, DeleteOne, EntityTrait, ExecErr, Statement, error::*, ActiveModelTrait, DatabaseConnection, DeleteMany, DeleteOne, EntityTrait, Statement,
}; };
use sea_query::DeleteStatement; use sea_query::DeleteStatement;
use std::future::Future; use std::future::Future;
@ -21,7 +21,7 @@ where
pub fn exec( pub fn exec(
self, self,
db: &'a DatabaseConnection, db: &'a DatabaseConnection,
) -> impl Future<Output = Result<DeleteResult, ExecErr>> + 'a { ) -> impl Future<Output = Result<DeleteResult, DbErr>> + 'a {
// so that self is dropped before entering await // so that self is dropped before entering await
exec_delete_only(self.query, db) exec_delete_only(self.query, db)
} }
@ -34,7 +34,7 @@ where
pub fn exec( pub fn exec(
self, self,
db: &'a DatabaseConnection, db: &'a DatabaseConnection,
) -> impl Future<Output = Result<DeleteResult, ExecErr>> + 'a { ) -> impl Future<Output = Result<DeleteResult, DbErr>> + 'a {
// so that self is dropped before entering await // so that self is dropped before entering await
exec_delete_only(self.query, db) exec_delete_only(self.query, db)
} }
@ -48,7 +48,7 @@ impl Deleter {
pub fn exec( pub fn exec(
self, self,
db: &DatabaseConnection, db: &DatabaseConnection,
) -> impl Future<Output = Result<DeleteResult, ExecErr>> + '_ { ) -> impl Future<Output = Result<DeleteResult, DbErr>> + '_ {
let builder = db.get_query_builder_backend(); let builder = db.get_query_builder_backend();
exec_delete(builder.build(&self.query), db) exec_delete(builder.build(&self.query), db)
} }
@ -57,7 +57,7 @@ impl Deleter {
async fn exec_delete_only( async fn exec_delete_only(
query: DeleteStatement, query: DeleteStatement,
db: &DatabaseConnection, db: &DatabaseConnection,
) -> Result<DeleteResult, ExecErr> { ) -> Result<DeleteResult, DbErr> {
Deleter::new(query).exec(db).await Deleter::new(query).exec(db).await
} }
@ -65,7 +65,7 @@ async fn exec_delete_only(
async fn exec_delete( async fn exec_delete(
statement: Statement, statement: Statement,
db: &DatabaseConnection, db: &DatabaseConnection,
) -> Result<DeleteResult, ExecErr> { ) -> Result<DeleteResult, DbErr> {
let result = db.execute(statement).await?; let result = db.execute(statement).await?;
Ok(DeleteResult { Ok(DeleteResult {
rows_affected: result.rows_affected(), rows_affected: result.rows_affected(),

View File

@ -1,5 +1,3 @@
use std::{error::Error, fmt};
#[derive(Debug)] #[derive(Debug)]
pub struct ExecResult { pub struct ExecResult {
pub(crate) result: ExecResultHolder, pub(crate) result: ExecResultHolder,
@ -15,9 +13,6 @@ pub(crate) enum ExecResultHolder {
Mock(crate::MockExecResult), Mock(crate::MockExecResult),
} }
#[derive(Debug)]
pub struct ExecErr;
// ExecResult // // ExecResult //
impl ExecResult { impl ExecResult {
@ -50,13 +45,3 @@ impl ExecResult {
} }
} }
} }
// ExecErr //
impl Error for ExecErr {}
impl fmt::Display for ExecErr {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{:?}", self)
}
}

View File

@ -1,4 +1,4 @@
use crate::{ActiveModelTrait, DatabaseConnection, ExecErr, Insert, QueryTrait, Statement}; use crate::{error::*, ActiveModelTrait, DatabaseConnection, Insert, QueryTrait, Statement};
use sea_query::InsertStatement; use sea_query::InsertStatement;
use std::future::Future; use std::future::Future;
@ -19,7 +19,7 @@ where
pub fn exec( pub fn exec(
self, self,
db: &DatabaseConnection, db: &DatabaseConnection,
) -> impl Future<Output = Result<InsertResult, ExecErr>> + '_ { ) -> impl Future<Output = Result<InsertResult, DbErr>> + '_ {
// so that self is dropped before entering await // so that self is dropped before entering await
Inserter::new(self.into_query()).exec(db) Inserter::new(self.into_query()).exec(db)
} }
@ -33,7 +33,7 @@ impl Inserter {
pub fn exec( pub fn exec(
self, self,
db: &DatabaseConnection, db: &DatabaseConnection,
) -> impl Future<Output = Result<InsertResult, ExecErr>> + '_ { ) -> impl Future<Output = Result<InsertResult, DbErr>> + '_ {
let builder = db.get_query_builder_backend(); let builder = db.get_query_builder_backend();
exec_insert(builder.build(&self.query), db) exec_insert(builder.build(&self.query), db)
} }
@ -43,7 +43,7 @@ impl Inserter {
async fn exec_insert( async fn exec_insert(
statement: Statement, statement: Statement,
db: &DatabaseConnection, db: &DatabaseConnection,
) -> Result<InsertResult, ExecErr> { ) -> Result<InsertResult, DbErr> {
let result = db.execute(statement).await?; let result = db.execute(statement).await?;
// TODO: Postgres instead use query_one + returning clause // TODO: Postgres instead use query_one + returning clause
Ok(InsertResult { Ok(InsertResult {

View File

@ -1,4 +1,4 @@
use crate::{DatabaseConnection, QueryErr, SelectorTrait}; use crate::{error::*, DatabaseConnection, 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};
@ -23,7 +23,7 @@ where
S: SelectorTrait + 'db, S: SelectorTrait + 'db,
{ {
/// Fetch a specific page /// Fetch a specific page
pub async fn fetch_page(&self, page: usize) -> Result<Vec<S::Item>, QueryErr> { pub async fn fetch_page(&self, page: usize) -> Result<Vec<S::Item>, DbErr> {
let query = self let query = self
.query .query
.clone() .clone()
@ -36,18 +36,18 @@ where
let mut buffer = Vec::with_capacity(rows.len()); let mut buffer = Vec::with_capacity(rows.len());
for row in rows.into_iter() { for row in rows.into_iter() {
// TODO: Error handling // TODO: Error handling
buffer.push(S::from_raw_query_result(row).map_err(|_e| QueryErr)?); buffer.push(S::from_raw_query_result(row)?);
} }
Ok(buffer) Ok(buffer)
} }
/// Fetch the current page /// Fetch the current page
pub async fn fetch(&self) -> Result<Vec<S::Item>, QueryErr> { pub async fn fetch(&self) -> Result<Vec<S::Item>, DbErr> {
self.fetch_page(self.page).await self.fetch_page(self.page).await
} }
/// Get the total number of pages /// Get the total number of pages
pub async fn num_pages(&self) -> Result<usize, QueryErr> { pub async fn num_pages(&self) -> Result<usize, DbErr> {
let builder = self.db.get_query_builder_backend(); let builder = self.db.get_query_builder_backend();
let stmt = builder.build( let stmt = builder.build(
SelectStatement::new() SelectStatement::new()
@ -61,9 +61,7 @@ where
Some(res) => res, Some(res) => res,
None => return Ok(0), None => return Ok(0),
}; };
let num_rows = result let num_rows = result.try_get::<i32>("", "num_rows")? as usize;
.try_get::<i32>("", "num_rows")
.map_err(|_e| QueryErr)? as usize;
let num_pages = (num_rows / self.page_size) + (num_rows % self.page_size > 0) as usize; let num_pages = (num_rows / self.page_size) + (num_rows % self.page_size > 0) as usize;
Ok(num_pages) Ok(num_pages)
} }
@ -79,7 +77,7 @@ where
} }
/// Fetch one page and increment the page counter /// Fetch one page and increment the page counter
pub async fn fetch_and_next(&mut self) -> Result<Option<Vec<S::Item>>, QueryErr> { pub async fn fetch_and_next(&mut self) -> Result<Option<Vec<S::Item>>, DbErr> {
let vec = self.fetch().await?; let vec = self.fetch().await?;
self.next(); self.next();
let opt = if !vec.is_empty() { Some(vec) } else { None }; let opt = if !vec.is_empty() { Some(vec) } else { None };
@ -87,7 +85,7 @@ where
} }
/// Convert self into an async stream /// Convert self into an async stream
pub fn into_stream(mut self) -> PinBoxStream<'db, Result<Vec<S::Item>, QueryErr>> { pub fn into_stream(mut self) -> PinBoxStream<'db, Result<Vec<S::Item>, DbErr>> {
Box::pin(stream! { Box::pin(stream! {
loop { loop {
if let Some(vec) = self.fetch_and_next().await? { if let Some(vec) = self.fetch_and_next().await? {
@ -105,7 +103,7 @@ where
mod tests { mod tests {
use crate::entity::prelude::*; use crate::entity::prelude::*;
use crate::tests_cfg::*; use crate::tests_cfg::*;
use crate::{DatabaseConnection, MockDatabase, QueryErr, Transaction}; use crate::{DatabaseConnection, MockDatabase, Transaction};
use futures::TryStreamExt; use futures::TryStreamExt;
use sea_query::{Alias, Expr, SelectStatement, Value}; use sea_query::{Alias, Expr, SelectStatement, Value};
@ -150,7 +148,7 @@ mod tests {
} }
#[async_std::test] #[async_std::test]
async fn fetch_page() -> Result<(), QueryErr> { async fn fetch_page() -> Result<(), DbErr> {
let (db, pages) = setup(); let (db, pages) = setup();
let paginator = fruit::Entity::find().paginate(&db, 2); let paginator = fruit::Entity::find().paginate(&db, 2);
@ -180,7 +178,7 @@ mod tests {
} }
#[async_std::test] #[async_std::test]
async fn fetch() -> Result<(), QueryErr> { async fn fetch() -> Result<(), DbErr> {
let (db, pages) = setup(); let (db, pages) = setup();
let mut paginator = fruit::Entity::find().paginate(&db, 2); let mut paginator = fruit::Entity::find().paginate(&db, 2);
@ -214,7 +212,7 @@ mod tests {
} }
#[async_std::test] #[async_std::test]
async fn num_pages() -> Result<(), QueryErr> { async fn num_pages() -> Result<(), DbErr> {
let (db, num_rows) = setup_num_rows(); let (db, num_rows) = setup_num_rows();
let num_rows = num_rows as usize; let num_rows = num_rows as usize;
@ -246,7 +244,7 @@ mod tests {
} }
#[async_std::test] #[async_std::test]
async fn next_and_cur_page() -> Result<(), QueryErr> { async fn next_and_cur_page() -> Result<(), DbErr> {
let (db, _) = setup(); let (db, _) = setup();
let mut paginator = fruit::Entity::find().paginate(&db, 2); let mut paginator = fruit::Entity::find().paginate(&db, 2);
@ -262,7 +260,7 @@ mod tests {
} }
#[async_std::test] #[async_std::test]
async fn fetch_and_next() -> Result<(), QueryErr> { async fn fetch_and_next() -> Result<(), DbErr> {
let (db, pages) = setup(); let (db, pages) = setup();
let mut paginator = fruit::Entity::find().paginate(&db, 2); let mut paginator = fruit::Entity::find().paginate(&db, 2);
@ -297,7 +295,7 @@ mod tests {
} }
#[async_std::test] #[async_std::test]
async fn into_stream() -> Result<(), QueryErr> { async fn into_stream() -> Result<(), DbErr> {
let (db, pages) = setup(); let (db, pages) = setup();
let mut fruit_stream = fruit::Entity::find().paginate(&db, 2).into_stream(); let mut fruit_stream = fruit::Entity::find().paginate(&db, 2).into_stream();

View File

@ -1,4 +1,5 @@
use std::{error::Error, fmt}; use crate::DbErr;
use std::fmt;
#[derive(Debug)] #[derive(Debug)]
pub struct QueryResult { pub struct QueryResult {
@ -14,14 +15,8 @@ pub(crate) enum QueryResultRow {
Mock(crate::MockRow), Mock(crate::MockRow),
} }
#[derive(Debug)]
pub struct QueryErr;
#[derive(Debug)]
pub struct TypeErr;
pub trait TryGetable { pub trait TryGetable {
fn try_get(res: &QueryResult, pre: &str, col: &str) -> Result<Self, TypeErr> fn try_get(res: &QueryResult, pre: &str, col: &str) -> Result<Self, DbErr>
where where
Self: Sized; Self: Sized;
} }
@ -29,7 +24,7 @@ pub trait TryGetable {
// QueryResult // // QueryResult //
impl QueryResult { impl QueryResult {
pub fn try_get<T>(&self, pre: &str, col: &str) -> Result<T, TypeErr> pub fn try_get<T>(&self, pre: &str, col: &str) -> Result<T, DbErr>
where where
T: TryGetable, T: TryGetable,
{ {
@ -50,49 +45,23 @@ impl fmt::Debug for QueryResultRow {
} }
} }
// QueryErr //
impl Error for QueryErr {}
impl fmt::Display for QueryErr {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{:?}", self)
}
}
impl From<TypeErr> for QueryErr {
fn from(_: TypeErr) -> QueryErr {
QueryErr
}
}
// TypeErr //
impl Error for TypeErr {}
impl fmt::Display for TypeErr {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{:?}", self)
}
}
// TryGetable // // TryGetable //
macro_rules! try_getable_all { macro_rules! try_getable_all {
( $type: ty ) => { ( $type: ty ) => {
impl TryGetable for $type { impl TryGetable for $type {
fn try_get(res: &QueryResult, pre: &str, col: &str) -> Result<Self, TypeErr> { fn try_get(res: &QueryResult, pre: &str, col: &str) -> Result<Self, DbErr> {
let column = format!("{}{}", pre, col); let column = format!("{}{}", pre, col);
match &res.row { match &res.row {
#[cfg(feature = "sqlx-mysql")] #[cfg(feature = "sqlx-mysql")]
QueryResultRow::SqlxMySql(row) => { QueryResultRow::SqlxMySql(row) => {
use sqlx::Row; use sqlx::Row;
Ok(row.try_get(column.as_str())?) row.try_get(column.as_str()).map_err(crate::sqlx_error_to_query_err)
} }
#[cfg(feature = "sqlx-sqlite")] #[cfg(feature = "sqlx-sqlite")]
QueryResultRow::SqlxSqlite(row) => { QueryResultRow::SqlxSqlite(row) => {
use sqlx::Row; use sqlx::Row;
Ok(row.try_get(column.as_str())?) row.try_get(column.as_str()).map_err(crate::sqlx_error_to_query_err)
} }
#[cfg(feature = "mock")] #[cfg(feature = "mock")]
QueryResultRow::Mock(row) => Ok(row.try_get(column.as_str())?), QueryResultRow::Mock(row) => Ok(row.try_get(column.as_str())?),
@ -101,7 +70,7 @@ macro_rules! try_getable_all {
} }
impl TryGetable for Option<$type> { impl TryGetable for Option<$type> {
fn try_get(res: &QueryResult, pre: &str, col: &str) -> Result<Self, TypeErr> { fn try_get(res: &QueryResult, pre: &str, col: &str) -> Result<Self, DbErr> {
let column = format!("{}{}", pre, col); let column = format!("{}{}", pre, col);
match &res.row { match &res.row {
#[cfg(feature = "sqlx-mysql")] #[cfg(feature = "sqlx-mysql")]
@ -134,13 +103,13 @@ macro_rules! try_getable_all {
macro_rules! try_getable_mysql { macro_rules! try_getable_mysql {
( $type: ty ) => { ( $type: ty ) => {
impl TryGetable for $type { impl TryGetable for $type {
fn try_get(res: &QueryResult, pre: &str, col: &str) -> Result<Self, TypeErr> { fn try_get(res: &QueryResult, pre: &str, col: &str) -> Result<Self, DbErr> {
let column = format!("{}{}", pre, col); let column = format!("{}{}", pre, col);
match &res.row { match &res.row {
#[cfg(feature = "sqlx-mysql")] #[cfg(feature = "sqlx-mysql")]
QueryResultRow::SqlxMySql(row) => { QueryResultRow::SqlxMySql(row) => {
use sqlx::Row; use sqlx::Row;
Ok(row.try_get(column.as_str())?) row.try_get(column.as_str()).map_err(crate::sqlx_error_to_query_err)
} }
#[cfg(feature = "sqlx-sqlite")] #[cfg(feature = "sqlx-sqlite")]
QueryResultRow::SqlxSqlite(_) => { QueryResultRow::SqlxSqlite(_) => {
@ -153,7 +122,7 @@ macro_rules! try_getable_mysql {
} }
impl TryGetable for Option<$type> { impl TryGetable for Option<$type> {
fn try_get(res: &QueryResult, pre: &str, col: &str) -> Result<Self, TypeErr> { fn try_get(res: &QueryResult, pre: &str, col: &str) -> Result<Self, DbErr> {
let column = format!("{}{}", pre, col); let column = format!("{}{}", pre, col);
match &res.row { match &res.row {
#[cfg(feature = "sqlx-mysql")] #[cfg(feature = "sqlx-mysql")]

View File

@ -1,7 +1,7 @@
use crate::{ use crate::{
query::combine, DatabaseConnection, EntityTrait, FromQueryResult, Iterable, JsonValue, error::*, query::combine, DatabaseConnection, EntityTrait, FromQueryResult, Iterable,
ModelTrait, Paginator, PrimaryKeyToColumn, QueryErr, QueryResult, Select, SelectTwo, JsonValue, ModelTrait, Paginator, PrimaryKeyToColumn, QueryResult, Select, SelectTwo,
SelectTwoMany, TypeErr, SelectTwoMany,
}; };
use sea_query::SelectStatement; use sea_query::SelectStatement;
use std::marker::PhantomData; use std::marker::PhantomData;
@ -18,7 +18,7 @@ where
pub trait SelectorTrait { pub trait SelectorTrait {
type Item: Sized; type Item: Sized;
fn from_raw_query_result(res: QueryResult) -> Result<Self::Item, TypeErr>; fn from_raw_query_result(res: QueryResult) -> Result<Self::Item, DbErr>;
} }
pub struct SelectModel<M> pub struct SelectModel<M>
@ -43,7 +43,7 @@ where
{ {
type Item = M; type Item = M;
fn from_raw_query_result(res: QueryResult) -> Result<Self::Item, TypeErr> { fn from_raw_query_result(res: QueryResult) -> Result<Self::Item, DbErr> {
M::from_query_result(&res, "") M::from_query_result(&res, "")
} }
} }
@ -55,7 +55,7 @@ where
{ {
type Item = (M, Option<N>); type Item = (M, Option<N>);
fn from_raw_query_result(res: QueryResult) -> Result<Self::Item, TypeErr> { fn from_raw_query_result(res: QueryResult) -> Result<Self::Item, DbErr> {
Ok(( Ok((
M::from_query_result(&res, combine::SELECT_A)?, M::from_query_result(&res, combine::SELECT_A)?,
N::from_query_result_optional(&res, combine::SELECT_B)?, N::from_query_result_optional(&res, combine::SELECT_B)?,
@ -85,11 +85,11 @@ where
} }
} }
pub async fn one(self, db: &DatabaseConnection) -> Result<Option<E::Model>, QueryErr> { pub async fn one(self, db: &DatabaseConnection) -> Result<Option<E::Model>, DbErr> {
self.into_model::<E::Model>().one(db).await self.into_model::<E::Model>().one(db).await
} }
pub async fn all(self, db: &DatabaseConnection) -> Result<Vec<E::Model>, QueryErr> { pub async fn all(self, db: &DatabaseConnection) -> Result<Vec<E::Model>, DbErr> {
self.into_model::<E::Model>().all(db).await self.into_model::<E::Model>().all(db).await
} }
@ -129,14 +129,14 @@ where
pub async fn one( pub async fn one(
self, self,
db: &DatabaseConnection, db: &DatabaseConnection,
) -> Result<Option<(E::Model, Option<F::Model>)>, QueryErr> { ) -> Result<Option<(E::Model, Option<F::Model>)>, DbErr> {
self.into_model::<E::Model, F::Model>().one(db).await self.into_model::<E::Model, F::Model>().one(db).await
} }
pub async fn all( pub async fn all(
self, self,
db: &DatabaseConnection, db: &DatabaseConnection,
) -> Result<Vec<(E::Model, Option<F::Model>)>, QueryErr> { ) -> Result<Vec<(E::Model, Option<F::Model>)>, DbErr> {
self.into_model::<E::Model, F::Model>().all(db).await self.into_model::<E::Model, F::Model>().all(db).await
} }
} }
@ -168,14 +168,14 @@ where
pub async fn one( pub async fn one(
self, self,
db: &DatabaseConnection, db: &DatabaseConnection,
) -> Result<Option<(E::Model, Option<F::Model>)>, QueryErr> { ) -> Result<Option<(E::Model, Option<F::Model>)>, DbErr> {
self.into_model::<E::Model, F::Model>().one(db).await self.into_model::<E::Model, F::Model>().one(db).await
} }
pub async fn all( pub async fn all(
self, self,
db: &DatabaseConnection, db: &DatabaseConnection,
) -> Result<Vec<(E::Model, Vec<F::Model>)>, QueryErr> { ) -> Result<Vec<(E::Model, Vec<F::Model>)>, DbErr> {
let rows = self.into_model::<E::Model, F::Model>().all(db).await?; let rows = self.into_model::<E::Model, F::Model>().all(db).await?;
Ok(consolidate_query_result::<E, F>(rows)) Ok(consolidate_query_result::<E, F>(rows))
} }
@ -185,7 +185,7 @@ impl<S> Selector<S>
where where
S: SelectorTrait, S: SelectorTrait,
{ {
pub async fn one(mut self, db: &DatabaseConnection) -> Result<Option<S::Item>, QueryErr> { pub async fn one(mut self, db: &DatabaseConnection) -> Result<Option<S::Item>, DbErr> {
let builder = db.get_query_builder_backend(); let builder = db.get_query_builder_backend();
self.query.limit(1); self.query.limit(1);
let row = db.query_one(builder.build(&self.query)).await?; let row = db.query_one(builder.build(&self.query)).await?;
@ -195,7 +195,7 @@ where
} }
} }
pub async fn all(self, db: &DatabaseConnection) -> Result<Vec<S::Item>, QueryErr> { pub async fn all(self, db: &DatabaseConnection) -> Result<Vec<S::Item>, DbErr> {
let builder = db.get_query_builder_backend(); let builder = db.get_query_builder_backend();
let rows = db.query_all(builder.build(&self.query)).await?; let rows = db.query_all(builder.build(&self.query)).await?;
let mut models = Vec::new(); let mut models = Vec::new();

View File

@ -1,5 +1,5 @@
use crate::{ use crate::{
ActiveModelTrait, DatabaseConnection, EntityTrait, ExecErr, Statement, UpdateMany, UpdateOne, error::*, ActiveModelTrait, DatabaseConnection, EntityTrait, Statement, UpdateMany, UpdateOne,
}; };
use sea_query::UpdateStatement; use sea_query::UpdateStatement;
use std::future::Future; use std::future::Future;
@ -18,7 +18,10 @@ impl<'a, A: 'a> UpdateOne<A>
where where
A: ActiveModelTrait, A: ActiveModelTrait,
{ {
pub fn exec(self, db: &'a DatabaseConnection) -> impl Future<Output = Result<A, ExecErr>> + 'a { pub fn exec(
self,
db: &'a DatabaseConnection,
) -> impl Future<Output = Result<A, DbErr>> + 'a {
// so that self is dropped before entering await // so that self is dropped before entering await
exec_update_and_return_original(self.query, self.model, db) exec_update_and_return_original(self.query, self.model, db)
} }
@ -31,7 +34,7 @@ where
pub fn exec( pub fn exec(
self, self,
db: &'a DatabaseConnection, db: &'a DatabaseConnection,
) -> impl Future<Output = Result<UpdateResult, ExecErr>> + 'a { ) -> impl Future<Output = Result<UpdateResult, DbErr>> + 'a {
// so that self is dropped before entering await // so that self is dropped before entering await
exec_update_only(self.query, db) exec_update_only(self.query, db)
} }
@ -45,7 +48,7 @@ impl Updater {
pub fn exec( pub fn exec(
self, self,
db: &DatabaseConnection, db: &DatabaseConnection,
) -> impl Future<Output = Result<UpdateResult, ExecErr>> + '_ { ) -> impl Future<Output = Result<UpdateResult, DbErr>> + '_ {
let builder = db.get_query_builder_backend(); let builder = db.get_query_builder_backend();
exec_update(builder.build(&self.query), db) exec_update(builder.build(&self.query), db)
} }
@ -54,7 +57,7 @@ impl Updater {
async fn exec_update_only( async fn exec_update_only(
query: UpdateStatement, query: UpdateStatement,
db: &DatabaseConnection, db: &DatabaseConnection,
) -> Result<UpdateResult, ExecErr> { ) -> Result<UpdateResult, DbErr> {
Updater::new(query).exec(db).await Updater::new(query).exec(db).await
} }
@ -62,7 +65,7 @@ async fn exec_update_and_return_original<A>(
query: UpdateStatement, query: UpdateStatement,
model: A, model: A,
db: &DatabaseConnection, db: &DatabaseConnection,
) -> Result<A, ExecErr> ) -> Result<A, DbErr>
where where
A: ActiveModelTrait, A: ActiveModelTrait,
{ {
@ -74,7 +77,7 @@ where
async fn exec_update( async fn exec_update(
statement: Statement, statement: Statement,
db: &DatabaseConnection, db: &DatabaseConnection,
) -> Result<UpdateResult, ExecErr> { ) -> Result<UpdateResult, DbErr> {
let result = db.execute(statement).await?; let result = db.execute(statement).await?;
Ok(UpdateResult { Ok(UpdateResult {
rows_affected: result.rows_affected(), rows_affected: result.rows_affected(),

View File

@ -18,7 +18,7 @@
//! API to make working with databases in Rust a first-class experience. //! API to make working with databases in Rust a first-class experience.
//! //!
//! ```ignore //! ```ignore
//! This is an early WIP of SeaORM, and is not yet published. See [example](examples/sqlx-mysql/src) for demo usage. //! This is a preview of SeaORM, and is not yet released.
//! ``` //! ```
//! //!
//! ## Features //! ## Features
@ -29,23 +29,22 @@
//! //!
//! 2. Dynamic //! 2. Dynamic
//! //!
//! Built upon SeaQuery, a dynamic query builder, SeaORM allows you to build complex queries without 'fighting the ORM'. //! Built upon SeaQuery, SeaORM allows you to build complex queries without 'fighting the ORM'.
//! //!
//! 3. Testable //! 3. Testable
//! //!
//! Use mock connections to write unit tests for your logic. //! Use mock connections to write unit tests for your logic.
//! //!
//! 4. API oriented //! 4. Service oriented
//! //!
//! Quickly build search models that help you join, filter, sort and paginate data in APIs. //! Quickly build services that join, filter, sort and paginate data in APIs.
//! //!
//! # A quick taste of SeaORM //! ## A quick taste of SeaORM
//! //!
//! ## Select //! ### Select
//! ``` //! ```
//! # use sea_orm::{DbConn, entity::*, query::*, tests_cfg::*}; //! # use sea_orm::{DbConn, error::*, entity::*, query::*, tests_cfg::*};
//! # async fn function(db: &DbConn) -> Result<(), QueryErr> { //! # async fn function(db: &DbConn) -> Result<(), DbErr> {
//! #
//! // find all models //! // find all models
//! let cakes: Vec<cake::Model> = Cake::find().all(db).await?; //! let cakes: Vec<cake::Model> = Cake::find().all(db).await?;
//! //!
@ -71,11 +70,10 @@
//! # Ok(()) //! # Ok(())
//! # } //! # }
//! ``` //! ```
//! ## Insert //! ### Insert
//! ``` //! ```
//! # use sea_orm::{DbConn, entity::*, query::*, tests_cfg::*}; //! # use sea_orm::{DbConn, error::*, entity::*, query::*, tests_cfg::*};
//! # async fn function(db: &DbConn) -> Result<(), ExecErr> { //! # async fn function(db: &DbConn) -> Result<(), DbErr> {
//! #
//! let apple = fruit::ActiveModel { //! let apple = fruit::ActiveModel {
//! name: Set("Apple".to_owned()), //! name: Set("Apple".to_owned()),
//! ..Default::default() // no need to set primary key //! ..Default::default() // no need to set primary key
@ -90,16 +88,13 @@
//! let res: InsertResult = Fruit::insert(pear).exec(db).await?; //! let res: InsertResult = Fruit::insert(pear).exec(db).await?;
//! //!
//! println!("InsertResult: {}", res.last_insert_id); //! println!("InsertResult: {}", res.last_insert_id);
//! #
//! # Ok(()) //! # Ok(())
//! # } //! # }
//! # //! # async fn function2(db: &DbConn) -> Result<(), DbErr> {
//! # async fn function2(db: &DbConn) -> Result<(), ExecErr> {
//! # let apple = fruit::ActiveModel { //! # let apple = fruit::ActiveModel {
//! # name: Set("Apple".to_owned()), //! # name: Set("Apple".to_owned()),
//! # ..Default::default() // no need to set primary key //! # ..Default::default() // no need to set primary key
//! # }; //! # };
//! #
//! # let pear = fruit::ActiveModel { //! # let pear = fruit::ActiveModel {
//! # name: Set("Pear".to_owned()), //! # name: Set("Pear".to_owned()),
//! # ..Default::default() //! # ..Default::default()
@ -107,25 +102,18 @@
//! //!
//! // insert many //! // insert many
//! Fruit::insert_many(vec![apple, pear]).exec(db).await?; //! Fruit::insert_many(vec![apple, pear]).exec(db).await?;
//! #
//! # Ok(()) //! # Ok(())
//! # } //! # }
//! ``` //! ```
//! ## Update //! ### Update
//! ``` //! ```
//! # use sea_orm::{DbConn, entity::*, query::*, tests_cfg::*}; //! # use sea_orm::{DbConn, error::*, entity::*, query::*, tests_cfg::*};
//! #
//! use sea_orm::sea_query::{Expr, Value}; //! use sea_orm::sea_query::{Expr, Value};
//! //!
//! # async fn function(db: &DbConn) -> Result<(), QueryErr> { //! # async fn function(db: &DbConn) -> Result<(), DbErr> {
//! let pear: Option<fruit::Model> = Fruit::find_by_id(1).one(db).await?; //! let pear: Option<fruit::Model> = Fruit::find_by_id(1).one(db).await?;
//! # Ok(())
//! # }
//! #
//! # async fn function2(db: &DbConn) -> Result<(), ExecErr> {
//! # let pear: Option<fruit::Model> = Fruit::find_by_id(1).one(db).await.unwrap();
//!
//! let mut pear: fruit::ActiveModel = pear.unwrap().into(); //! let mut pear: fruit::ActiveModel = pear.unwrap().into();
//!
//! pear.name = Set("Sweet pear".to_owned()); //! pear.name = Set("Sweet pear".to_owned());
//! //!
//! // update one //! // update one
@ -141,21 +129,39 @@
//! # Ok(()) //! # Ok(())
//! # } //! # }
//! ``` //! ```
//! ## Delete //! ### Save
//! ``` //! ```
//! # use sea_orm::{DbConn, entity::*, query::*, tests_cfg::*}; //! # use sea_orm::{DbConn, error::*, entity::*, query::*, tests_cfg::*};
//! # //! # async fn function(db: &DbConn) -> Result<(), DbErr> {
//! # async fn function(db: &DbConn) -> Result<(), QueryErr> { //! let banana = fruit::ActiveModel {
//! let orange: Option<fruit::Model> = Fruit::find_by_id(1).one(db).await?; //! id: Unset(None),
//! name: Set("Banana".to_owned()),
//! ..Default::default()
//! };
//!
//! // create, because primary key `id` is `Unset`
//! let mut banana = banana.save(db).await?;
//!
//! banana.name = Set("Banana Mongo".to_owned());
//!
//! // update, because primary key `id` is `Set`
//! let banana = banana.save(db).await?;
//!
//! # Ok(()) //! # Ok(())
//! # } //! # }
//! # //! ```
//! # async fn function2(db: &DbConn) -> Result<(), ExecErr> { //! ### Delete
//! # let orange: Option<fruit::Model> = Fruit::find_by_id(1).one(db).await.unwrap(); //! ```
//! # use sea_orm::{DbConn, error::*, entity::*, query::*, tests_cfg::*};
//! # async fn function(db: &DbConn) -> Result<(), DbErr> {
//! let orange: Option<fruit::Model> = Fruit::find_by_id(1).one(db).await?;
//! let orange: fruit::ActiveModel = orange.unwrap().into(); //! let orange: fruit::ActiveModel = orange.unwrap().into();
//! //!
//! // delete one //! // delete one
//! fruit::Entity::delete(orange).exec(db).await?; //! fruit::Entity::delete(orange).exec(db).await?;
//! // or simply
//! # let orange: fruit::ActiveModel = Fruit::find_by_id(1).one(db).await.unwrap().unwrap().into();
//! orange.delete(db).await?;
//! //!
//! // delete many: DELETE FROM "fruit" WHERE "fruit"."name" LIKE 'Orange' //! // delete many: DELETE FROM "fruit" WHERE "fruit"."name" LIKE 'Orange'
//! fruit::Entity::delete_many() //! fruit::Entity::delete_many()
@ -166,9 +172,30 @@
//! # Ok(()) //! # Ok(())
//! # } //! # }
//! ``` //! ```
//! ## License
//!
//! Licensed under either of
//!
//! - Apache License, Version 2.0
//! ([LICENSE-APACHE](LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0)
//! - MIT license
//! ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT)
//!
//! at your option.
//!
//! ## Contribution
//!
//! Unless you explicitly state otherwise, any contribution intentionally submitted
//! for inclusion in the work by you, as defined in the Apache-2.0 license, shall be
//! dual licensed as above, without any additional terms or conditions.
#![doc(
html_logo_url = "https://raw.githubusercontent.com/SeaQL/sea-query/master/docs/SeaQL icon dark.png"
)]
mod database; mod database;
mod driver; mod driver;
pub mod entity; pub mod entity;
pub mod error;
mod executor; mod executor;
pub mod query; pub mod query;
#[doc(hidden)] #[doc(hidden)]
@ -178,6 +205,7 @@ mod util;
pub use database::*; pub use database::*;
pub use driver::*; pub use driver::*;
pub use entity::*; pub use entity::*;
pub use error::*;
pub use executor::*; pub use executor::*;
pub use query::*; pub use query::*;
@ -188,3 +216,4 @@ pub use sea_orm_macros::{
pub use sea_query; pub use sea_query;
pub use sea_query::Iden; pub use sea_query::Iden;
pub use strum::EnumIter; pub use strum::EnumIter;
pub use strum;

View File

@ -1,9 +1,9 @@
use crate::{FromQueryResult, QueryResult, QueryResultRow, TypeErr}; use crate::{FromQueryResult, DbErr, QueryResult, QueryResultRow};
use serde_json::Map; use serde_json::Map;
pub use serde_json::Value as JsonValue; pub use serde_json::Value as JsonValue;
impl FromQueryResult for JsonValue { impl FromQueryResult for JsonValue {
fn from_query_result(res: &QueryResult, pre: &str) -> Result<Self, TypeErr> { fn from_query_result(res: &QueryResult, pre: &str) -> Result<Self, DbErr> {
match &res.row { match &res.row {
#[cfg(feature = "sqlx-mysql")] #[cfg(feature = "sqlx-mysql")]
QueryResultRow::SqlxMySql(row) => { QueryResultRow::SqlxMySql(row) => {

View File

@ -20,4 +20,4 @@ pub use select::*;
pub use traits::*; pub use traits::*;
pub use update::*; pub use update::*;
pub use crate::executor::{ExecErr, InsertResult, QueryErr, UpdateResult}; pub use crate::executor::{InsertResult, UpdateResult};

View File

@ -1,4 +1,4 @@
use sea_orm::{entity::*, query::*, sea_query, tests_cfg::*, DbConn}; use sea_orm::{entity::*, error::*, sea_query, tests_cfg::*, DbConn};
mod setup; mod setup;
@ -31,7 +31,7 @@ async fn setup_schema(db: &DbConn) {
println!("Create table cake: {:?}", result); println!("Create table cake: {:?}", result);
} }
async fn crud_cake(db: &DbConn) -> Result<(), ExecErr> { async fn crud_cake(db: &DbConn) -> Result<(), DbErr> {
let apple = cake::ActiveModel { let apple = cake::ActiveModel {
name: Set("Apple Pie".to_owned()), name: Set("Apple Pie".to_owned()),
..Default::default() ..Default::default()
@ -57,10 +57,7 @@ async fn crud_cake(db: &DbConn) -> Result<(), ExecErr> {
println!(); println!();
println!("Updated: {:?}", apple); println!("Updated: {:?}", apple);
let apple = cake::Entity::find_by_id(1) let apple = cake::Entity::find_by_id(1).one(db).await?;
.one(db)
.await
.map_err(|_| ExecErr)?;
assert_eq!( assert_eq!(
Some(cake::Model { Some(cake::Model {
@ -77,10 +74,7 @@ async fn crud_cake(db: &DbConn) -> Result<(), ExecErr> {
println!(); println!();
println!("Deleted: {:?}", result); println!("Deleted: {:?}", result);
let apple = cake::Entity::find_by_id(1) let apple = cake::Entity::find_by_id(1).one(db).await?;
.one(db)
.await
.map_err(|_| ExecErr)?;
assert_eq!(None, apple); assert_eq!(None, apple);