Merge branch 'master' into ss/bakery
This commit is contained in:
commit
335bdc5c7c
18
Cargo.toml
18
Cargo.toml
@ -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" ]
|
||||
|
39
Diesel.md
39
Diesel.md
@ -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
130
README.md
@ -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.
|
||||
|
3
build-tools/generate-readme.sh
Normal file
3
build-tools/generate-readme.sh
Normal 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
|
@ -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 {}
|
||||
|
@ -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 {}
|
||||
|
@ -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 {}
|
||||
|
@ -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()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -4,3 +4,4 @@ pub mod cake;
|
||||
pub mod cake_filling;
|
||||
pub mod filling;
|
||||
pub mod fruit;
|
||||
pub mod vendor;
|
||||
|
@ -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;
|
||||
|
@ -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 {}
|
||||
|
@ -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 {}
|
||||
|
@ -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 {}
|
||||
|
@ -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 {}
|
||||
|
@ -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 {}
|
||||
|
@ -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 {}
|
||||
|
@ -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" }
|
||||
|
@ -14,7 +14,7 @@ cargo run
|
||||
All about selects:
|
||||
|
||||
```sh
|
||||
Database { connection: SqlxMySqlPoolConnection }
|
||||
SqlxMySqlPoolConnection
|
||||
|
||||
===== =====
|
||||
|
||||
|
@ -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()),
|
||||
|
@ -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);
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -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" ] }
|
||||
|
@ -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() },
|
||||
|
@ -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 {
|
||||
|
@ -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 {}
|
||||
|
@ -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
|
||||
}
|
||||
}
|
||||
|
@ -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)?),*
|
||||
})
|
||||
|
@ -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())?),*
|
||||
})
|
||||
|
@ -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()
|
||||
}
|
||||
|
@ -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,
|
||||
{
|
||||
|
@ -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)))
|
||||
}
|
||||
}
|
||||
|
@ -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)
|
||||
|
@ -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::*;
|
||||
|
9
src/driver/sqlx_common.rs
Normal file
9
src/driver/sqlx_common.rs
Normal 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())
|
||||
}
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
}
|
||||
}
|
@ -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,
|
||||
|
@ -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,
|
||||
{
|
||||
|
@ -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
16
src/error.rs
Normal 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),
|
||||
}
|
||||
}
|
||||
}
|
@ -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(),
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
@ -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 {
|
||||
|
@ -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();
|
||||
|
@ -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")]
|
||||
|
@ -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();
|
||||
|
@ -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(),
|
||||
|
101
src/lib.rs
101
src/lib.rs
@ -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;
|
||||
|
@ -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) => {
|
||||
|
@ -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};
|
||||
|
@ -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);
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user