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"
repository = "https://github.com/SeaQL/sea-orm"
categories = [ "database" ]
keywords = [ "orm", "database", "sql", "mysql", "postgres", "sqlite" ]
keywords = [ "orm", "database", "sql", "mysql", "postgres", "sqlite", "async" ]
publish = false
[package.metadata.docs.rs]
features = ["default", "sqlx-mysql", "sqlx-postgres", "sqlx-sqlite", "runtime-async-std-native-tls"]
rustdoc-args = ["--cfg", "docsrs"]
[lib]
name = "sea_orm"
path = "src/lib.rs"
[dependencies]
async-stream = { version = "^0.3" }
chrono = { version = "^0", optional = true }
futures = { version = "^0.3" }
futures-util = { version = "^0.3" }
sea-query = { version = "^0.12" }
sea-orm-macros = { path = "sea-orm-macros", optional = true }
serde = { version = "^1.0", features = [ "derive" ] }
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 }
[dev-dependencies]
async-std = { version = "^1.9", features = [ "attributes" ] }
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]
debug-print = []
default = [ "macros", "with-json", "mock" ]
default = [ "macros", "with-json", "with-chrono", "mock" ]
macros = [ "sea-orm-macros" ]
mock = []
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-postgres = [ "sqlx-dep", "sea-query/sqlx-postgres", "sqlx/postgres" ]
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
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.
> 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
@ -27,12 +29,130 @@ Relying on SQLx, SeaORM is a new library with async support from day 1.
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
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 {}

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 {}

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 {}

View File

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

View File

@ -4,3 +4,4 @@ pub mod cake;
pub mod cake_filling;
pub mod filling;
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::filling::Entity as Filling;
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 {
Self::Id => ColumnType::Integer.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 {
fn def(&self) -> RelationDef {
match self {
Self::Fruit => Entity::has_one(super::fruit::Entity)
Self::Fruit => Entity::belongs_to(super::fruit::Entity)
.from(Column::FruitId)
.to(super::fruit::Column::Id)
.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 {}

View File

@ -53,14 +53,8 @@ impl ColumnTrait for Column {
impl RelationTrait for Relation {
fn def(&self) -> RelationDef {
match self {
Self::CakeFilling => Entity::has_many(super::cake_filling::Entity)
.from(Column::Id)
.to(super::cake_filling::Column::CakeId)
.into(),
Self::Fruit => Entity::has_many(super::fruit::Entity)
.from(Column::Id)
.to(super::fruit::Column::CakeId)
.into(),
Self::CakeFilling => Entity::has_many(super::cake_filling::Entity).into(),
Self::Fruit => Entity::has_many(super::fruit::Entity).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 {}

View File

@ -54,11 +54,11 @@ impl ColumnTrait for Column {
impl RelationTrait for Relation {
fn def(&self) -> RelationDef {
match self {
Self::Cake => Entity::has_one(super::cake::Entity)
Self::Cake => Entity::belongs_to(super::cake::Entity)
.from(Column::CakeId)
.to(super::cake::Column::Id)
.into(),
Self::Filling => Entity::has_one(super::filling::Entity)
Self::Filling => Entity::belongs_to(super::filling::Entity)
.from(Column::FillingId)
.to(super::filling::Column::Id)
.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 {}

View File

@ -52,10 +52,7 @@ impl ColumnTrait for Column {
impl RelationTrait for Relation {
fn def(&self) -> RelationDef {
match self {
Self::CakeFilling => Entity::has_many(super::cake_filling::Entity)
.from(Column::Id)
.to(super::cake_filling::Column::FillingId)
.into(),
Self::CakeFilling => Entity::has_many(super::cake_filling::Entity).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 {}

View File

@ -48,7 +48,7 @@ impl ColumnTrait for Column {
match self {
Self::Id => ColumnType::Integer.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 {
fn def(&self) -> RelationDef {
match self {
Self::Cake => Entity::has_one(super::cake::Entity)
Self::Cake => Entity::belongs_to(super::cake::Entity)
.from(Column::CakeId)
.to(super::cake::Column::Id)
.into(),
Self::Vendor => Entity::has_many(super::vendor::Entity)
.from(Column::Id)
.to(super::vendor::Column::FruitId)
.into(),
Self::Vendor => Entity::has_many(super::vendor::Entity).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 {}

View File

@ -47,7 +47,7 @@ impl ColumnTrait for Column {
match self {
Self::Id => ColumnType::Integer.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 {
fn def(&self) -> RelationDef {
match self {
Self::Fruit => Entity::has_one(super::fruit::Entity)
Self::Fruit => Entity::belongs_to(super::fruit::Entity)
.from(Column::FruitId)
.to(super::fruit::Column::Id)
.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 {}

View File

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

View File

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

View File

@ -1,7 +1,7 @@
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?;
println!("===== =====\n");
@ -15,7 +15,7 @@ pub async fn all_about_operation(db: &DbConn) -> Result<(), ExecErr> {
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 {
name: Set("pear".to_owned()),
..Default::default()
@ -25,10 +25,7 @@ pub async fn insert_and_update(db: &DbConn) -> Result<(), ExecErr> {
println!();
println!("Inserted: last_insert_id = {}\n", res.last_insert_id);
let pear: Option<fruit::Model> = Fruit::find_by_id(res.last_insert_id)
.one(db)
.await
.map_err(|_| ExecErr)?;
let pear: Option<fruit::Model> = Fruit::find_by_id(res.last_insert_id).one(db).await?;
println!();
println!("Pear: {:?}\n", pear);
@ -44,7 +41,7 @@ pub async fn insert_and_update(db: &DbConn) -> Result<(), ExecErr> {
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 {
name: Set("Banana".to_owned()),
..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 {
id: Unset(None),
name: Set("Pineapple".to_owned()),

View File

@ -1,7 +1,7 @@
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?;
println!("===== =====\n");
@ -41,7 +41,7 @@ pub async fn all_about_select(db: &DbConn) -> Result<(), QueryErr> {
Ok(())
}
async fn find_all(db: &DbConn) -> Result<(), QueryErr> {
async fn find_all(db: &DbConn) -> Result<(), DbErr> {
print!("find all cakes: ");
let cakes: Vec<cake::Model> = Cake::find().all(db).await?;
@ -63,7 +63,7 @@ async fn find_all(db: &DbConn) -> Result<(), QueryErr> {
Ok(())
}
async fn find_together(db: &DbConn) -> Result<(), QueryErr> {
async fn find_together(db: &DbConn) -> Result<(), DbErr> {
print!("find cakes and fruits: ");
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: ");
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(())
}
async fn count_fruits_by_cake(db: &DbConn) -> Result<(), QueryErr> {
async fn count_fruits_by_cake(db: &DbConn) -> Result<(), DbErr> {
#[derive(Debug, FromQueryResult)]
struct SelectResult {
name: String,
@ -138,7 +138,7 @@ async fn count_fruits_by_cake(db: &DbConn) -> Result<(), QueryErr> {
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: ");
let both: Vec<(cake::Model, Vec<filling::Model>)> =
@ -178,7 +178,7 @@ async fn find_many_to_many(db: &DbConn) -> Result<(), QueryErr> {
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?;
println!("===== =====\n");
@ -192,7 +192,7 @@ async fn all_about_select_json(db: &DbConn) -> Result<(), QueryErr> {
Ok(())
}
async fn find_all_json(db: &DbConn) -> Result<(), QueryErr> {
async fn find_all_json(db: &DbConn) -> Result<(), DbErr> {
print!("find all cakes: ");
let cakes = Cake::find().into_json().all(db).await?;
@ -208,7 +208,7 @@ async fn find_all_json(db: &DbConn) -> Result<(), QueryErr> {
Ok(())
}
async fn find_together_json(db: &DbConn) -> Result<(), QueryErr> {
async fn find_together_json(db: &DbConn) -> Result<(), DbErr> {
print!("find cakes and fruits: ");
let cakes_fruits = Cake::find()
@ -225,7 +225,7 @@ async fn find_together_json(db: &DbConn) -> Result<(), QueryErr> {
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: ");
let count = Cake::find()
@ -243,7 +243,7 @@ async fn count_fruits_by_cake_json(db: &DbConn) -> Result<(), QueryErr> {
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 futures::TryStreamExt;
use std::time::Duration;
@ -291,7 +291,7 @@ async fn find_all_stream(db: &DbConn) -> Result<(), QueryErr> {
Ok(())
}
async fn find_first_page(db: &DbConn) -> Result<(), QueryErr> {
async fn find_first_page(db: &DbConn) -> Result<(), DbErr> {
println!("fruits first page: ");
let page = fruit::Entity::find().paginate(db, 2).fetch_page(0).await?;
for fruit in page {
@ -301,7 +301,7 @@ async fn find_first_page(db: &DbConn) -> Result<(), QueryErr> {
Ok(())
}
async fn find_num_pages(db: &DbConn) -> Result<(), QueryErr> {
async fn find_num_pages(db: &DbConn) -> Result<(), DbErr> {
println!("fruits number of page: ");
let num_pages = fruit::Entity::find().paginate(db, 2).num_pages().await?;
println!("{:?}", num_pages);

View File

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

View File

@ -16,7 +16,6 @@ name = "sea_orm_codegen"
path = "src/lib.rs"
[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-query = { version = "^0.12" }
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() },
},
ColumnType::Text => quote! { ColumnType::Text.def() },
ColumnType::TinyInteger(s) => quote! { ColumnType::TinyInteger.def() },
ColumnType::SmallInteger(s) => quote! { ColumnType::SmallInteger.def() },
ColumnType::Integer(s) => quote! { ColumnType::Integer.def() },
ColumnType::BigInteger(s) => quote! { ColumnType::BigInteger.def() },
ColumnType::Float(s) => quote! { ColumnType::Float.def() },
ColumnType::Double(s) => quote! { ColumnType::Double.def() },
ColumnType::TinyInteger(_) => quote! { ColumnType::TinyInteger.def() },
ColumnType::SmallInteger(_) => quote! { ColumnType::SmallInteger.def() },
ColumnType::Integer(_) => quote! { ColumnType::Integer.def() },
ColumnType::BigInteger(_) => quote! { ColumnType::BigInteger.def() },
ColumnType::Float(_) => quote! { ColumnType::Float.def() },
ColumnType::Double(_) => quote! { ColumnType::Double.def() },
ColumnType::Decimal(s) => match s {
Some((s1, s2)) => quote! { ColumnType::Decimal(Some((#s1, #s2))).def() },
None => quote! { ColumnType::Decimal(None).def() },
},
ColumnType::DateTime(s) => quote! { ColumnType::DateTime.def() },
ColumnType::Timestamp(s) => quote! { ColumnType::Timestamp.def() },
ColumnType::Time(s) => quote! { ColumnType::Time.def() },
ColumnType::DateTime(_) => quote! { ColumnType::DateTime.def() },
ColumnType::Timestamp(_) => quote! { ColumnType::Timestamp.def() },
ColumnType::Time(_) => quote! { ColumnType::Time.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::Money(s) => match s {
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 sea_query::TableStatement;
use sea_schema::mysql::def::Schema;
use std::{collections::HashMap, mem::swap};
use std::collections::HashMap;
#[derive(Clone, Debug)]
pub struct EntityTransformer {

View File

@ -117,7 +117,6 @@ impl EntityWriter {
];
code_blocks.extend(Self::gen_impl_related(entity));
code_blocks.extend(vec![
Self::gen_impl_model(entity),
Self::gen_impl_active_model_behavior(),
]);
code_blocks
@ -256,18 +255,6 @@ impl EntityWriter {
.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 {
quote! {
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 {
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
}
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
}
}

View File

@ -30,7 +30,7 @@ pub fn expand_derive_from_query_result(ident: Ident, data: Data) -> syn::Result<
Ok(quote!(
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 {
#(#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 {
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 {
#(#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::{
MysqlQueryBuilder, PostgresQueryBuilder, QueryStatementBuilder, SchemaStatementBuilder,
SqliteQueryBuilder,
};
use std::{error::Error, fmt};
pub enum DatabaseConnection {
#[cfg(feature = "sqlx-mysql")]
@ -29,17 +28,6 @@ pub enum SchemaBuilderBackend {
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 {
fn default() -> Self {
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 {
#[cfg(feature = "sqlx-mysql")]
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 {
#[cfg(feature = "sqlx-mysql")]
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 {
#[cfg(feature = "sqlx-mysql")]
DatabaseConnection::SqlxMySqlPoolConnection(conn) => conn.query_all(stmt).await,
@ -139,7 +127,7 @@ impl DatabaseConnection {
}
#[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();
mocker.drain_transaction_log()
}

View File

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

View File

@ -10,23 +10,25 @@ pub use mock::*;
pub use statement::*;
pub use transaction::*;
use crate::DbErr;
#[derive(Debug, Default)]
pub struct 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")]
if crate::SqlxMySqlConnector::accepts(string) {
return Ok(crate::SqlxMySqlConnector::connect(string).await?);
return crate::SqlxMySqlConnector::connect(string).await;
}
#[cfg(feature = "sqlx-sqlite")]
if crate::SqlxSqliteConnector::accepts(string) {
return Ok(crate::SqlxSqliteConnector::connect(string).await?);
return crate::SqlxSqliteConnector::connect(string).await;
}
#[cfg(feature = "mock")]
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::{
debug_print, ConnectionErr, DatabaseConnection, ExecErr, ExecResult, MockDatabase, QueryErr,
QueryResult, Statement, Transaction,
debug_print, error::*, DatabaseConnection, ExecResult, MockDatabase, QueryResult, Statement,
Transaction,
};
use std::sync::{
atomic::{AtomicUsize, Ordering},
@ -15,9 +15,9 @@ pub struct MockDatabaseConnection {
}
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>;
}
@ -27,7 +27,7 @@ impl MockDatabaseConnector {
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(
MockDatabaseConnection::new(MockDatabase::new()),
))
@ -49,20 +49,20 @@ impl MockDatabaseConnection {
&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);
let counter = self.counter.fetch_add(1, Ordering::SeqCst);
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);
let counter = self.counter.fetch_add(1, Ordering::SeqCst);
let result = self.mocker.lock().unwrap().query(counter, statement)?;
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);
let counter = self.counter.fetch_add(1, Ordering::SeqCst);
self.mocker.lock().unwrap().query(counter, statement)

View File

@ -1,17 +1,17 @@
#[cfg(feature = "mock")]
mod mock;
#[cfg(feature = "sqlx-dep")]
mod sqlx_common;
#[cfg(feature = "sqlx-mysql")]
mod sqlx_mysql;
#[cfg(feature = "sqlx-sqlite")]
mod sqlx_sqlite;
#[cfg(feature = "sqlx-dep")]
mod sqlx_types;
#[cfg(feature = "mock")]
pub use mock::*;
#[cfg(feature = "sqlx-dep")]
pub use sqlx_common::*;
#[cfg(feature = "sqlx-mysql")]
pub use sqlx_mysql::*;
#[cfg(feature = "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!();
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;
@ -19,13 +21,13 @@ impl SqlxMySqlConnector {
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 {
Ok(DatabaseConnection::SqlxMySqlPoolConnection(
SqlxMySqlPoolConnection { pool },
))
} else {
Err(ConnectionErr)
Err(DbErr::Conn("Failed to connect.".to_owned()))
}
}
}
@ -37,43 +39,49 @@ impl SqlxMySqlConnector {
}
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);
let query = sqlx_query(&stmt);
if let Ok(conn) = &mut self.pool.acquire().await {
if let Ok(res) = query.execute(conn).await {
return Ok(res.into());
}
}
Err(ExecErr)
}
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)
match query.execute(conn).await {
Ok(res) => Ok(res.into()),
Err(err) => Err(sqlx_error_to_exec_err(err)),
}
} 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);
let query = sqlx_query(&stmt);
if let Ok(conn) = &mut self.pool.acquire().await {
if let Ok(rows) = query.fetch_all(conn).await {
return Ok(rows.into_iter().map(|r| r.into()).collect());
match query.fetch_one(conn).await {
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!();
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;
@ -19,13 +21,13 @@ impl SqlxSqliteConnector {
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 {
Ok(DatabaseConnection::SqlxSqlitePoolConnection(
SqlxSqlitePoolConnection { pool },
))
} else {
Err(ConnectionErr)
Err(DbErr::Conn("Failed to connect.".to_owned()))
}
}
}
@ -37,43 +39,49 @@ impl SqlxSqliteConnector {
}
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);
let query = sqlx_query(&stmt);
if let Ok(conn) = &mut self.pool.acquire().await {
if let Ok(res) = query.execute(conn).await {
return Ok(res.into());
}
}
Err(ExecErr)
}
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)
match query.execute(conn).await {
Ok(res) => Ok(res.into()),
Err(err) => Err(sqlx_error_to_exec_err(err)),
}
} 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);
let query = sqlx_query(&stmt);
if let Ok(conn) = &mut self.pool.acquire().await {
if let Ok(rows) = query.fetch_all(conn).await {
return Ok(rows.into_iter().map(|r| r.into()).collect());
match query.fetch_one(conn).await {
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::{
DatabaseConnection, DeleteResult, EntityTrait, ExecErr, Iterable, PrimaryKeyToColumn,
error::*, DatabaseConnection, DeleteResult, EntityTrait, Iterable, PrimaryKeyToColumn,
PrimaryKeyTrait, Value,
};
use std::fmt::Debug;
@ -66,8 +66,8 @@ pub trait ActiveModelTrait: Clone + Debug {
fn default() -> Self;
// 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 delete(self, db: &DatabaseConnection) -> impl Future<Output = Result<DeleteResult, ExecErr>>;
// fn save(self, db: &DatabaseConnection) -> impl Future<Output = Result<Self, DbErr>>;
// fn delete(self, db: &DatabaseConnection) -> impl Future<Output = Result<DeleteResult, DbErr>>;
}
/// Behaviors for users to override
@ -188,7 +188,7 @@ where
/// Insert the model if primary key is unset, update otherwise.
/// 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
A: ActiveModelBehavior + ActiveModelTrait<Entity = E>,
E::Model: IntoActiveModel<A>,
@ -212,7 +212,7 @@ where
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
A: ActiveModelTrait<Entity = E>,
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
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 res = find.await;
let model: Option<E::Model> = res.map_err(|_| ExecErr)?;
let found = find.await;
let model: Option<E::Model> = found?;
match 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 {
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
A: ActiveModelTrait<Entity = E>,
E: EntityTrait,
@ -246,7 +246,7 @@ where
pub async fn delete_active_model<A, E>(
mut am: A,
db: &DatabaseConnection,
) -> Result<DeleteResult, ExecErr>
) -> Result<DeleteResult, DbErr>
where
A: ActiveModelBehavior + ActiveModelTrait<Entity = E>,
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;
use std::fmt::Debug;
@ -19,11 +19,11 @@ pub trait ModelTrait: Clone + Debug {
}
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
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
Self: Sized,
{

View File

@ -1,7 +1,7 @@
pub use crate::{
ActiveModelBehavior, ActiveModelTrait, ColumnDef, ColumnTrait, ColumnType, DeriveActiveModel,
DeriveActiveModelBehavior, DeriveColumn, DeriveEntity, DeriveModel, DerivePrimaryKey,
EntityName, EntityTrait, EnumIter, Iden, IdenStatic, ModelTrait, PrimaryKeyToColumn,
PrimaryKeyTrait, QueryFilter, QueryResult, Related, RelationDef, RelationTrait, Select,
TypeErr, Value,
error::*, ActiveModelBehavior, ActiveModelTrait, ColumnDef, ColumnTrait, ColumnType,
DeriveActiveModel, DeriveActiveModelBehavior, DeriveColumn, DeriveEntity, DeriveModel,
DerivePrimaryKey, EntityName, EntityTrait, EnumIter, Iden, IdenStatic, ModelTrait,
PrimaryKeyToColumn, PrimaryKeyTrait, QueryFilter, QueryResult, Related, RelationDef,
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::{
ActiveModelTrait, DatabaseConnection, DeleteMany, DeleteOne, EntityTrait, ExecErr, Statement,
error::*, ActiveModelTrait, DatabaseConnection, DeleteMany, DeleteOne, EntityTrait, Statement,
};
use sea_query::DeleteStatement;
use std::future::Future;
@ -21,7 +21,7 @@ where
pub fn exec(
self,
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
exec_delete_only(self.query, db)
}
@ -34,7 +34,7 @@ where
pub fn exec(
self,
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
exec_delete_only(self.query, db)
}
@ -48,7 +48,7 @@ impl Deleter {
pub fn exec(
self,
db: &DatabaseConnection,
) -> impl Future<Output = Result<DeleteResult, ExecErr>> + '_ {
) -> impl Future<Output = Result<DeleteResult, DbErr>> + '_ {
let builder = db.get_query_builder_backend();
exec_delete(builder.build(&self.query), db)
}
@ -57,7 +57,7 @@ impl Deleter {
async fn exec_delete_only(
query: DeleteStatement,
db: &DatabaseConnection,
) -> Result<DeleteResult, ExecErr> {
) -> Result<DeleteResult, DbErr> {
Deleter::new(query).exec(db).await
}
@ -65,7 +65,7 @@ async fn exec_delete_only(
async fn exec_delete(
statement: Statement,
db: &DatabaseConnection,
) -> Result<DeleteResult, ExecErr> {
) -> Result<DeleteResult, DbErr> {
let result = db.execute(statement).await?;
Ok(DeleteResult {
rows_affected: result.rows_affected(),

View File

@ -1,5 +1,3 @@
use std::{error::Error, fmt};
#[derive(Debug)]
pub struct ExecResult {
pub(crate) result: ExecResultHolder,
@ -15,9 +13,6 @@ pub(crate) enum ExecResultHolder {
Mock(crate::MockExecResult),
}
#[derive(Debug)]
pub struct ExecErr;
// 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 std::future::Future;
@ -19,7 +19,7 @@ where
pub fn exec(
self,
db: &DatabaseConnection,
) -> impl Future<Output = Result<InsertResult, ExecErr>> + '_ {
) -> impl Future<Output = Result<InsertResult, DbErr>> + '_ {
// so that self is dropped before entering await
Inserter::new(self.into_query()).exec(db)
}
@ -33,7 +33,7 @@ impl Inserter {
pub fn exec(
self,
db: &DatabaseConnection,
) -> impl Future<Output = Result<InsertResult, ExecErr>> + '_ {
) -> impl Future<Output = Result<InsertResult, DbErr>> + '_ {
let builder = db.get_query_builder_backend();
exec_insert(builder.build(&self.query), db)
}
@ -43,7 +43,7 @@ impl Inserter {
async fn exec_insert(
statement: Statement,
db: &DatabaseConnection,
) -> Result<InsertResult, ExecErr> {
) -> Result<InsertResult, DbErr> {
let result = db.execute(statement).await?;
// TODO: Postgres instead use query_one + returning clause
Ok(InsertResult {

View File

@ -1,4 +1,4 @@
use crate::{DatabaseConnection, QueryErr, SelectorTrait};
use crate::{error::*, DatabaseConnection, SelectorTrait};
use async_stream::stream;
use futures::Stream;
use sea_query::{Alias, Expr, SelectStatement};
@ -23,7 +23,7 @@ where
S: SelectorTrait + 'db,
{
/// 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
.query
.clone()
@ -36,18 +36,18 @@ where
let mut buffer = Vec::with_capacity(rows.len());
for row in rows.into_iter() {
// 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)
}
/// 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
}
/// 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 stmt = builder.build(
SelectStatement::new()
@ -61,9 +61,7 @@ where
Some(res) => res,
None => return Ok(0),
};
let num_rows = result
.try_get::<i32>("", "num_rows")
.map_err(|_e| QueryErr)? as usize;
let num_rows = result.try_get::<i32>("", "num_rows")? as usize;
let num_pages = (num_rows / self.page_size) + (num_rows % self.page_size > 0) as usize;
Ok(num_pages)
}
@ -79,7 +77,7 @@ where
}
/// 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?;
self.next();
let opt = if !vec.is_empty() { Some(vec) } else { None };
@ -87,7 +85,7 @@ where
}
/// 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! {
loop {
if let Some(vec) = self.fetch_and_next().await? {
@ -105,7 +103,7 @@ where
mod tests {
use crate::entity::prelude::*;
use crate::tests_cfg::*;
use crate::{DatabaseConnection, MockDatabase, QueryErr, Transaction};
use crate::{DatabaseConnection, MockDatabase, Transaction};
use futures::TryStreamExt;
use sea_query::{Alias, Expr, SelectStatement, Value};
@ -150,7 +148,7 @@ mod tests {
}
#[async_std::test]
async fn fetch_page() -> Result<(), QueryErr> {
async fn fetch_page() -> Result<(), DbErr> {
let (db, pages) = setup();
let paginator = fruit::Entity::find().paginate(&db, 2);
@ -180,7 +178,7 @@ mod tests {
}
#[async_std::test]
async fn fetch() -> Result<(), QueryErr> {
async fn fetch() -> Result<(), DbErr> {
let (db, pages) = setup();
let mut paginator = fruit::Entity::find().paginate(&db, 2);
@ -214,7 +212,7 @@ mod tests {
}
#[async_std::test]
async fn num_pages() -> Result<(), QueryErr> {
async fn num_pages() -> Result<(), DbErr> {
let (db, num_rows) = setup_num_rows();
let num_rows = num_rows as usize;
@ -246,7 +244,7 @@ mod tests {
}
#[async_std::test]
async fn next_and_cur_page() -> Result<(), QueryErr> {
async fn next_and_cur_page() -> Result<(), DbErr> {
let (db, _) = setup();
let mut paginator = fruit::Entity::find().paginate(&db, 2);
@ -262,7 +260,7 @@ mod tests {
}
#[async_std::test]
async fn fetch_and_next() -> Result<(), QueryErr> {
async fn fetch_and_next() -> Result<(), DbErr> {
let (db, pages) = setup();
let mut paginator = fruit::Entity::find().paginate(&db, 2);
@ -297,7 +295,7 @@ mod tests {
}
#[async_std::test]
async fn into_stream() -> Result<(), QueryErr> {
async fn into_stream() -> Result<(), DbErr> {
let (db, pages) = setup();
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)]
pub struct QueryResult {
@ -14,14 +15,8 @@ pub(crate) enum QueryResultRow {
Mock(crate::MockRow),
}
#[derive(Debug)]
pub struct QueryErr;
#[derive(Debug)]
pub struct TypeErr;
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
Self: Sized;
}
@ -29,7 +24,7 @@ pub trait TryGetable {
// 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
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 //
macro_rules! try_getable_all {
( $type: ty ) => {
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);
match &res.row {
#[cfg(feature = "sqlx-mysql")]
QueryResultRow::SqlxMySql(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")]
QueryResultRow::SqlxSqlite(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")]
QueryResultRow::Mock(row) => Ok(row.try_get(column.as_str())?),
@ -101,7 +70,7 @@ macro_rules! try_getable_all {
}
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);
match &res.row {
#[cfg(feature = "sqlx-mysql")]
@ -134,13 +103,13 @@ macro_rules! try_getable_all {
macro_rules! try_getable_mysql {
( $type: ty ) => {
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);
match &res.row {
#[cfg(feature = "sqlx-mysql")]
QueryResultRow::SqlxMySql(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")]
QueryResultRow::SqlxSqlite(_) => {
@ -153,7 +122,7 @@ macro_rules! try_getable_mysql {
}
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);
match &res.row {
#[cfg(feature = "sqlx-mysql")]

View File

@ -1,7 +1,7 @@
use crate::{
query::combine, DatabaseConnection, EntityTrait, FromQueryResult, Iterable, JsonValue,
ModelTrait, Paginator, PrimaryKeyToColumn, QueryErr, QueryResult, Select, SelectTwo,
SelectTwoMany, TypeErr,
error::*, query::combine, DatabaseConnection, EntityTrait, FromQueryResult, Iterable,
JsonValue, ModelTrait, Paginator, PrimaryKeyToColumn, QueryResult, Select, SelectTwo,
SelectTwoMany,
};
use sea_query::SelectStatement;
use std::marker::PhantomData;
@ -18,7 +18,7 @@ where
pub trait SelectorTrait {
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>
@ -43,7 +43,7 @@ where
{
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, "")
}
}
@ -55,7 +55,7 @@ where
{
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((
M::from_query_result(&res, combine::SELECT_A)?,
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
}
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
}
@ -129,14 +129,14 @@ where
pub async fn one(
self,
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
}
pub async fn all(
self,
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
}
}
@ -168,14 +168,14 @@ where
pub async fn one(
self,
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
}
pub async fn all(
self,
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?;
Ok(consolidate_query_result::<E, F>(rows))
}
@ -185,7 +185,7 @@ impl<S> Selector<S>
where
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();
self.query.limit(1);
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 rows = db.query_all(builder.build(&self.query)).await?;
let mut models = Vec::new();

View File

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

View File

@ -18,7 +18,7 @@
//! API to make working with databases in Rust a first-class experience.
//!
//! ```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
@ -29,23 +29,22 @@
//!
//! 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
//!
//! 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::*};
//! # async fn function(db: &DbConn) -> Result<(), QueryErr> {
//! #
//! # use sea_orm::{DbConn, error::*, entity::*, query::*, tests_cfg::*};
//! # async fn function(db: &DbConn) -> Result<(), DbErr> {
//! // find all models
//! let cakes: Vec<cake::Model> = Cake::find().all(db).await?;
//!
@ -71,11 +70,10 @@
//! # Ok(())
//! # }
//! ```
//! ## Insert
//! ### Insert
//! ```
//! # use sea_orm::{DbConn, entity::*, query::*, tests_cfg::*};
//! # async fn function(db: &DbConn) -> Result<(), ExecErr> {
//! #
//! # use sea_orm::{DbConn, error::*, entity::*, query::*, tests_cfg::*};
//! # async fn function(db: &DbConn) -> Result<(), DbErr> {
//! let apple = fruit::ActiveModel {
//! name: Set("Apple".to_owned()),
//! ..Default::default() // no need to set primary key
@ -90,16 +88,13 @@
//! let res: InsertResult = Fruit::insert(pear).exec(db).await?;
//!
//! println!("InsertResult: {}", res.last_insert_id);
//! #
//! # Ok(())
//! # }
//! #
//! # async fn function2(db: &DbConn) -> Result<(), ExecErr> {
//! # async fn function2(db: &DbConn) -> Result<(), DbErr> {
//! # 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()
@ -107,25 +102,18 @@
//!
//! // insert many
//! Fruit::insert_many(vec![apple, pear]).exec(db).await?;
//! #
//! # 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};
//!
//! # 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?;
//! # 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();
//!
//! pear.name = Set("Sweet pear".to_owned());
//!
//! // update one
@ -141,21 +129,39 @@
//! # Ok(())
//! # }
//! ```
//! ## Delete
//! ### Save
//! ```
//! # use sea_orm::{DbConn, entity::*, query::*, tests_cfg::*};
//! #
//! # async fn function(db: &DbConn) -> Result<(), QueryErr> {
//! let orange: Option<fruit::Model> = Fruit::find_by_id(1).one(db).await?;
//! # use sea_orm::{DbConn, error::*, entity::*, query::*, tests_cfg::*};
//! # async fn function(db: &DbConn) -> Result<(), DbErr> {
//! 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?;
//!
//! # Ok(())
//! # }
//! #
//! # async fn function2(db: &DbConn) -> Result<(), ExecErr> {
//! # let orange: Option<fruit::Model> = Fruit::find_by_id(1).one(db).await.unwrap();
//! ```
//! ### Delete
//! ```
//! # 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();
//!
//! // delete one
//! 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'
//! fruit::Entity::delete_many()
@ -166,9 +172,30 @@
//! # 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 driver;
pub mod entity;
pub mod error;
mod executor;
pub mod query;
#[doc(hidden)]
@ -178,6 +205,7 @@ mod util;
pub use database::*;
pub use driver::*;
pub use entity::*;
pub use error::*;
pub use executor::*;
pub use query::*;
@ -188,3 +216,4 @@ pub use sea_orm_macros::{
pub use sea_query;
pub use sea_query::Iden;
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;
pub use serde_json::Value as 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 {
#[cfg(feature = "sqlx-mysql")]
QueryResultRow::SqlxMySql(row) => {

View File

@ -20,4 +20,4 @@ pub use select::*;
pub use traits::*;
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;
@ -31,7 +31,7 @@ async fn setup_schema(db: &DbConn) {
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 {
name: Set("Apple Pie".to_owned()),
..Default::default()
@ -57,10 +57,7 @@ async fn crud_cake(db: &DbConn) -> Result<(), ExecErr> {
println!();
println!("Updated: {:?}", apple);
let apple = cake::Entity::find_by_id(1)
.one(db)
.await
.map_err(|_| ExecErr)?;
let apple = cake::Entity::find_by_id(1).one(db).await?;
assert_eq!(
Some(cake::Model {
@ -77,10 +74,7 @@ async fn crud_cake(db: &DbConn) -> Result<(), ExecErr> {
println!();
println!("Deleted: {:?}", result);
let apple = cake::Entity::find_by_id(1)
.one(db)
.await
.map_err(|_| ExecErr)?;
let apple = cake::Entity::find_by_id(1).one(db).await?;
assert_eq!(None, apple);