Merge branch 'master' into primary-key-type
This commit is contained in:
commit
ab7e7f3401
90
.github/workflows/rust.yml
vendored
90
.github/workflows/rust.yml
vendored
@ -11,12 +11,11 @@ env:
|
|||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
|
|
||||||
compile:
|
compile-sqlite:
|
||||||
name: Compile
|
name: Compile SQLite
|
||||||
runs-on: ubuntu-20.04
|
runs-on: ubuntu-20.04
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
database: [sqlite, mysql, postgres]
|
|
||||||
runtime: [async-std, actix, tokio]
|
runtime: [async-std, actix, tokio]
|
||||||
tls: [native-tls, rustls]
|
tls: [native-tls, rustls]
|
||||||
steps:
|
steps:
|
||||||
@ -35,13 +34,77 @@ jobs:
|
|||||||
~/.cargo/git
|
~/.cargo/git
|
||||||
Cargo.lock
|
Cargo.lock
|
||||||
target
|
target
|
||||||
key: ${{ github.sha }}-${{ github.run_id }}-${{ runner.os }}-${{ matrix.database }}-${{ matrix.runtime }}-${{ matrix.tls }}
|
key: ${{ github.sha }}-${{ github.run_id }}-${{ runner.os }}-sqlite-${{ matrix.runtime }}-${{ matrix.tls }}
|
||||||
|
|
||||||
- uses: actions-rs/cargo@v1
|
- uses: actions-rs/cargo@v1
|
||||||
with:
|
with:
|
||||||
command: test
|
command: test
|
||||||
args: >
|
args: >
|
||||||
--features default,sqlx-${{ matrix.database }},runtime-${{ matrix.runtime }}-${{ matrix.tls }}
|
--features default,sqlx-sqlite,runtime-${{ matrix.runtime }}-${{ matrix.tls }}
|
||||||
|
--no-run
|
||||||
|
|
||||||
|
compile-mysql:
|
||||||
|
name: Compile MySQL
|
||||||
|
runs-on: ubuntu-20.04
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
runtime: [async-std, actix, tokio]
|
||||||
|
tls: [native-tls, rustls]
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v2
|
||||||
|
|
||||||
|
- uses: actions-rs/toolchain@v1
|
||||||
|
with:
|
||||||
|
profile: minimal
|
||||||
|
toolchain: stable
|
||||||
|
override: true
|
||||||
|
|
||||||
|
- uses: actions/cache@v2
|
||||||
|
with:
|
||||||
|
path: |
|
||||||
|
~/.cargo/registry
|
||||||
|
~/.cargo/git
|
||||||
|
Cargo.lock
|
||||||
|
target
|
||||||
|
key: ${{ github.sha }}-${{ github.run_id }}-${{ runner.os }}-mysql-${{ matrix.runtime }}-${{ matrix.tls }}
|
||||||
|
|
||||||
|
- uses: actions-rs/cargo@v1
|
||||||
|
with:
|
||||||
|
command: test
|
||||||
|
args: >
|
||||||
|
--features default,sqlx-mysql,runtime-${{ matrix.runtime }}-${{ matrix.tls }}
|
||||||
|
--no-run
|
||||||
|
|
||||||
|
compile-postgres:
|
||||||
|
name: Compile PostgreSQL
|
||||||
|
runs-on: ubuntu-20.04
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
runtime: [async-std, actix, tokio]
|
||||||
|
tls: [native-tls, rustls]
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v2
|
||||||
|
|
||||||
|
- uses: actions-rs/toolchain@v1
|
||||||
|
with:
|
||||||
|
profile: minimal
|
||||||
|
toolchain: stable
|
||||||
|
override: true
|
||||||
|
|
||||||
|
- uses: actions/cache@v2
|
||||||
|
with:
|
||||||
|
path: |
|
||||||
|
~/.cargo/registry
|
||||||
|
~/.cargo/git
|
||||||
|
Cargo.lock
|
||||||
|
target
|
||||||
|
key: ${{ github.sha }}-${{ github.run_id }}-${{ runner.os }}-postgres-${{ matrix.runtime }}-${{ matrix.tls }}
|
||||||
|
|
||||||
|
- uses: actions-rs/cargo@v1
|
||||||
|
with:
|
||||||
|
command: test
|
||||||
|
args: >
|
||||||
|
--features default,sqlx-postgres,runtime-${{ matrix.runtime }}-${{ matrix.tls }}
|
||||||
--no-run
|
--no-run
|
||||||
|
|
||||||
test:
|
test:
|
||||||
@ -89,6 +152,7 @@ jobs:
|
|||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
os: [ubuntu-latest]
|
os: [ubuntu-latest]
|
||||||
|
path: [async-std, tokio]
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v2
|
||||||
|
|
||||||
@ -102,18 +166,12 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
command: build
|
command: build
|
||||||
args: >
|
args: >
|
||||||
--manifest-path examples/async-std/Cargo.toml
|
--manifest-path examples/${{ matrix.path }}/Cargo.toml
|
||||||
|
|
||||||
- uses: actions-rs/cargo@v1
|
|
||||||
with:
|
|
||||||
command: build
|
|
||||||
args: >
|
|
||||||
--manifest-path examples/tokio/Cargo.toml
|
|
||||||
|
|
||||||
sqlite:
|
sqlite:
|
||||||
name: SQLite
|
name: SQLite
|
||||||
runs-on: ubuntu-20.04
|
runs-on: ubuntu-20.04
|
||||||
needs: compile
|
needs: compile-sqlite
|
||||||
env:
|
env:
|
||||||
DATABASE_URL: "sqlite::memory:"
|
DATABASE_URL: "sqlite::memory:"
|
||||||
strategy:
|
strategy:
|
||||||
@ -147,7 +205,7 @@ jobs:
|
|||||||
mysql:
|
mysql:
|
||||||
name: MySQL
|
name: MySQL
|
||||||
runs-on: ubuntu-20.04
|
runs-on: ubuntu-20.04
|
||||||
needs: compile
|
needs: compile-mysql
|
||||||
env:
|
env:
|
||||||
DATABASE_URL: "mysql://root:@localhost"
|
DATABASE_URL: "mysql://root:@localhost"
|
||||||
strategy:
|
strategy:
|
||||||
@ -199,7 +257,7 @@ jobs:
|
|||||||
mariadb:
|
mariadb:
|
||||||
name: MariaDB
|
name: MariaDB
|
||||||
runs-on: ubuntu-20.04
|
runs-on: ubuntu-20.04
|
||||||
needs: compile
|
needs: compile-mysql
|
||||||
env:
|
env:
|
||||||
DATABASE_URL: "mysql://root:@localhost"
|
DATABASE_URL: "mysql://root:@localhost"
|
||||||
strategy:
|
strategy:
|
||||||
@ -251,7 +309,7 @@ jobs:
|
|||||||
postgres:
|
postgres:
|
||||||
name: Postgres
|
name: Postgres
|
||||||
runs-on: ubuntu-20.04
|
runs-on: ubuntu-20.04
|
||||||
needs: compile
|
needs: compile-postgres
|
||||||
env:
|
env:
|
||||||
DATABASE_URL: "postgres://root:root@localhost"
|
DATABASE_URL: "postgres://root:root@localhost"
|
||||||
strategy:
|
strategy:
|
||||||
|
39
ARCHITECTURE.md
Normal file
39
ARCHITECTURE.md
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
# Architecture
|
||||||
|
|
||||||
|
To understand the architecture of SeaORM, let's discuss what is an ORM. ORM exists to provide abstractions over common operations you would do against a database and hide the implementation details like the actual SQL queries.
|
||||||
|
|
||||||
|
With a good ORM, you shouldn't bother to look under the API surface. Until you do. I hear you say *'abstraction leaks'*, and yes, it does.
|
||||||
|
|
||||||
|
The approach SeaORM takes is **'layered abstraction'**, where you'd dig one layer beneath if you want to. That's why we made SeaQuery into a standalone repository. It's useful on its own, and with a public API surface and a separate namespace, it's far more difficult to create confusing internal APIs than a monolithic approach.
|
||||||
|
|
||||||
|
The central idea in SeaORM is nearly everything is runtime configurable. At compile time, it does not know what the underlying database is.
|
||||||
|
|
||||||
|
What benefits does database-agnostic bring? For example, you can:
|
||||||
|
|
||||||
|
1. Make your app work on any database, depending on runtime configuration
|
||||||
|
1. Use the same models and transfer them across different databases
|
||||||
|
1. Share entities across different projects by creating a 'data structure crate', where the database is chosen by downstream 'behaviour crates'
|
||||||
|
|
||||||
|
The API of SeaORM is not a thin shell, but consist of layers, with each layer underneath being less abstract.
|
||||||
|
|
||||||
|
There are different stages when the API is being utilized.
|
||||||
|
|
||||||
|
So there are two dimensions to navigate the SeaORM code base, **'stage'** and **'abstractness'**.
|
||||||
|
|
||||||
|
First is the declaration stage. Entities and relations among them are being defined with the `EntityTrait`, `ColumnTrait` & `RelationTrait` etc.
|
||||||
|
|
||||||
|
Second is the query building stage.
|
||||||
|
|
||||||
|
The top most layer is `Entity`'s `find*`, `insert`, `update` & `delete` methods, where you can intuitively perform basic CRUD operations.
|
||||||
|
|
||||||
|
One layer down, is the `Select`, `Insert`, `Update` & `Delete` structs, where they each have their own API for more advanced operations.
|
||||||
|
|
||||||
|
One layer down, is the SeaQuery `SelectStatement`, `InsertStatement`, `UpdateStatement` & `DeleteStatement`, where they have a rich API for you to fiddle with the SQL syntax tree.
|
||||||
|
|
||||||
|
Third is the execution stage. A separate set of structs, `Selector`, `Inserter`, `Updater` & `Deleter`, are responsible for executing the statements against a database connection.
|
||||||
|
|
||||||
|
Finally is the resolution stage, when query results are converted into Rust structs for consumption.
|
||||||
|
|
||||||
|
Because only the execution and resolution stages are database specific, we have the possibility to use a different driver by replacing those.
|
||||||
|
|
||||||
|
I imagine some day, we will support a number of databases, with a matrix of different syntaxes, protocols and form-factors.
|
@ -5,6 +5,12 @@ All notable changes to this project will be documented in this file.
|
|||||||
The format is based on [Keep a Changelog](http://keepachangelog.com/)
|
The format is based on [Keep a Changelog](http://keepachangelog.com/)
|
||||||
and this project adheres to [Semantic Versioning](http://semver.org/).
|
and this project adheres to [Semantic Versioning](http://semver.org/).
|
||||||
|
|
||||||
|
## 0.1.3 - 2021-08-30
|
||||||
|
|
||||||
|
- [[#108]] Remove impl TryGetable for Option<T>
|
||||||
|
|
||||||
|
[#108]: https://github.com/SeaQL/sea-orm/issues/108
|
||||||
|
|
||||||
## 0.1.2 - 2021-08-23
|
## 0.1.2 - 2021-08-23
|
||||||
|
|
||||||
- [[#68]] Added `DateTimeWithTimeZone` as supported attribute type
|
- [[#68]] Added `DateTimeWithTimeZone` as supported attribute type
|
||||||
|
@ -7,7 +7,7 @@ members = [
|
|||||||
|
|
||||||
[package]
|
[package]
|
||||||
name = "sea-orm"
|
name = "sea-orm"
|
||||||
version = "0.1.2"
|
version = "0.1.3"
|
||||||
authors = ["Chris Tsang <tyt2y7@gmail.com>"]
|
authors = ["Chris Tsang <tyt2y7@gmail.com>"]
|
||||||
edition = "2018"
|
edition = "2018"
|
||||||
description = "🐚 An async & dynamic ORM for Rust"
|
description = "🐚 An async & dynamic ORM for Rust"
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
# Design Goals
|
# Design
|
||||||
|
|
||||||
We are heavily inspired by ActiveRecord, Eloquent and TypeORM.
|
We are heavily inspired by ActiveRecord, Eloquent and TypeORM.
|
||||||
|
|
||||||
@ -20,7 +20,7 @@ After some bitterness we realized it is not possible to capture everything compi
|
|||||||
want to encounter problems at run time either. The solution is to perform checking at 'test time' to
|
want to encounter problems at run time either. The solution is to perform checking at 'test time' to
|
||||||
uncover problems. These checks will be removed at production so there will be no run time penalty.
|
uncover problems. These checks will be removed at production so there will be no run time penalty.
|
||||||
|
|
||||||
## Readability
|
## API style
|
||||||
|
|
||||||
### Turbofish and inference
|
### Turbofish and inference
|
||||||
|
|
||||||
@ -66,3 +66,34 @@ we'd prefer having a builder and stating the params explicitly:
|
|||||||
```rust
|
```rust
|
||||||
has_many(cake::Entity).from(cake::Column::Id).to(fruit::Column::CakeId)
|
has_many(cake::Entity).from(cake::Column::Id).to(fruit::Column::CakeId)
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### Method overloading
|
||||||
|
|
||||||
|
Consider the following two methods, which accept the same parameter but in different forms:
|
||||||
|
|
||||||
|
```rust
|
||||||
|
fn method_with_model(m: Model) { ... }
|
||||||
|
fn method_with_active_model(a: ActiveModel) { ... }
|
||||||
|
```
|
||||||
|
|
||||||
|
We would define a trait
|
||||||
|
|
||||||
|
```rust
|
||||||
|
pub trait IntoActiveModel {
|
||||||
|
fn into_active_model(self) -> ActiveModel;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Such that `Model` and `ActiveModel` both impl this trait.
|
||||||
|
|
||||||
|
In this way, we can overload the two methods:
|
||||||
|
|
||||||
|
```rust
|
||||||
|
pub fn method<A>(a: A)
|
||||||
|
where
|
||||||
|
A: IntoActiveModel,
|
||||||
|
{
|
||||||
|
let a: ActiveModel = a.into_active_model();
|
||||||
|
...
|
||||||
|
}
|
||||||
|
```
|
10
README.md
10
README.md
@ -25,7 +25,7 @@ This is an early release of SeaORM, the API is not stable yet.
|
|||||||
```
|
```
|
||||||
|
|
||||||
[](https://www.sea-ql.org/SeaORM/docs/index)
|
[](https://www.sea-ql.org/SeaORM/docs/index)
|
||||||
[](https://github.com/SeaQL/sea-orm/tree/master/examples/sqlx)
|
[](https://github.com/SeaQL/sea-orm/tree/master/examples)
|
||||||
[](https://github.com/SeaQL/sea-orm/issues/37)
|
[](https://github.com/SeaQL/sea-orm/issues/37)
|
||||||
[](https://discord.com/invite/uCPdDXzbdv)
|
[](https://discord.com/invite/uCPdDXzbdv)
|
||||||
|
|
||||||
@ -108,7 +108,7 @@ let pear: fruit::ActiveModel = Fruit::update(pear).exec(db).await?;
|
|||||||
|
|
||||||
// update many: UPDATE "fruit" SET "cake_id" = NULL WHERE "fruit"."name" LIKE '%Apple%'
|
// update many: UPDATE "fruit" SET "cake_id" = NULL WHERE "fruit"."name" LIKE '%Apple%'
|
||||||
Fruit::update_many()
|
Fruit::update_many()
|
||||||
.col_expr(fruit::Column::CakeId, Expr::value(Value::Null))
|
.col_expr(fruit::Column::CakeId, Expr::value(Value::Int(None)))
|
||||||
.filter(fruit::Column::Name.contains("Apple"))
|
.filter(fruit::Column::Name.contains("Apple"))
|
||||||
.exec(db)
|
.exec(db)
|
||||||
.await?;
|
.await?;
|
||||||
@ -148,6 +148,12 @@ fruit::Entity::delete_many()
|
|||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## Learn More
|
||||||
|
|
||||||
|
1. [Design](https://github.com/SeaQL/sea-orm/tree/master/DESIGN.md)
|
||||||
|
1. [Architecture](https://github.com/SeaQL/sea-orm/tree/master/ARCHITECTURE.md)
|
||||||
|
|
||||||
## License
|
## License
|
||||||
|
|
||||||
Licensed under either of
|
Licensed under either of
|
||||||
|
@ -21,6 +21,20 @@ pub trait TryGetable: Sized {
|
|||||||
fn try_get(res: &QueryResult, pre: &str, col: &str) -> Result<Self, DbErr>;
|
fn try_get(res: &QueryResult, pre: &str, col: &str) -> Result<Self, DbErr>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub enum TryGetError {
|
||||||
|
DbErr(DbErr),
|
||||||
|
Null,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<TryGetError> for DbErr {
|
||||||
|
fn from(e: TryGetError) -> DbErr {
|
||||||
|
match e {
|
||||||
|
TryGetError::DbErr(e) => e,
|
||||||
|
TryGetError::Null => DbErr::Query("error occurred while decoding: Null".to_owned()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// QueryResult //
|
// QueryResult //
|
||||||
|
|
||||||
impl QueryResult {
|
impl QueryResult {
|
||||||
@ -28,7 +42,7 @@ impl QueryResult {
|
|||||||
where
|
where
|
||||||
T: TryGetable,
|
T: TryGetable,
|
||||||
{
|
{
|
||||||
T::try_get(self, pre, col)
|
Ok(T::try_get(self, pre, col)?)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -49,66 +63,48 @@ impl fmt::Debug for QueryResultRow {
|
|||||||
|
|
||||||
// TryGetable //
|
// TryGetable //
|
||||||
|
|
||||||
|
impl<T: TryGetable> TryGetable for Option<T> {
|
||||||
|
fn try_get(res: &QueryResult, pre: &str, col: &str) -> Result<Self, TryGetError> {
|
||||||
|
match T::try_get(res, pre, col) {
|
||||||
|
Ok(v) => Ok(Some(v)),
|
||||||
|
Err(TryGetError::Null) => Ok(None),
|
||||||
|
Err(e) => Err(e),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
macro_rules! try_getable_all {
|
macro_rules! try_getable_all {
|
||||||
( $type: ty ) => {
|
( $type: ty ) => {
|
||||||
impl TryGetable for $type {
|
impl TryGetable for $type {
|
||||||
fn try_get(res: &QueryResult, pre: &str, col: &str) -> Result<Self, DbErr> {
|
fn try_get(res: &QueryResult, pre: &str, col: &str) -> Result<Self, TryGetError> {
|
||||||
let column = format!("{}{}", pre, col);
|
|
||||||
match &res.row {
|
|
||||||
#[cfg(feature = "sqlx-mysql")]
|
|
||||||
QueryResultRow::SqlxMySql(row) => {
|
|
||||||
use sqlx::Row;
|
|
||||||
row.try_get(column.as_str())
|
|
||||||
.map_err(crate::sqlx_error_to_query_err)
|
|
||||||
}
|
|
||||||
#[cfg(feature = "sqlx-postgres")]
|
|
||||||
QueryResultRow::SqlxPostgres(row) => {
|
|
||||||
use sqlx::Row;
|
|
||||||
row.try_get(column.as_str())
|
|
||||||
.map_err(crate::sqlx_error_to_query_err)
|
|
||||||
}
|
|
||||||
#[cfg(feature = "sqlx-sqlite")]
|
|
||||||
QueryResultRow::SqlxSqlite(row) => {
|
|
||||||
use sqlx::Row;
|
|
||||||
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())?),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl TryGetable for Option<$type> {
|
|
||||||
fn try_get(res: &QueryResult, pre: &str, col: &str) -> Result<Self, DbErr> {
|
|
||||||
let column = format!("{}{}", pre, col);
|
let column = format!("{}{}", pre, col);
|
||||||
match &res.row {
|
match &res.row {
|
||||||
#[cfg(feature = "sqlx-mysql")]
|
#[cfg(feature = "sqlx-mysql")]
|
||||||
QueryResultRow::SqlxMySql(row) => {
|
QueryResultRow::SqlxMySql(row) => {
|
||||||
use sqlx::Row;
|
use sqlx::Row;
|
||||||
row.try_get::<Option<$type>, _>(column.as_str())
|
row.try_get::<Option<$type>, _>(column.as_str())
|
||||||
.map_err(crate::sqlx_error_to_query_err)
|
.map_err(|e| TryGetError::DbErr(crate::sqlx_error_to_query_err(e)))
|
||||||
|
.and_then(|opt| opt.ok_or(TryGetError::Null))
|
||||||
}
|
}
|
||||||
#[cfg(feature = "sqlx-postgres")]
|
#[cfg(feature = "sqlx-postgres")]
|
||||||
QueryResultRow::SqlxPostgres(row) => {
|
QueryResultRow::SqlxPostgres(row) => {
|
||||||
use sqlx::Row;
|
use sqlx::Row;
|
||||||
row.try_get::<Option<$type>, _>(column.as_str())
|
row.try_get::<Option<$type>, _>(column.as_str())
|
||||||
.map_err(crate::sqlx_error_to_query_err)
|
.map_err(|e| TryGetError::DbErr(crate::sqlx_error_to_query_err(e)))
|
||||||
|
.and_then(|opt| opt.ok_or(TryGetError::Null))
|
||||||
}
|
}
|
||||||
#[cfg(feature = "sqlx-sqlite")]
|
#[cfg(feature = "sqlx-sqlite")]
|
||||||
QueryResultRow::SqlxSqlite(row) => {
|
QueryResultRow::SqlxSqlite(row) => {
|
||||||
use sqlx::Row;
|
use sqlx::Row;
|
||||||
row.try_get::<Option<$type>, _>(column.as_str())
|
row.try_get::<Option<$type>, _>(column.as_str())
|
||||||
.map_err(crate::sqlx_error_to_query_err)
|
.map_err(|e| TryGetError::DbErr(crate::sqlx_error_to_query_err(e)))
|
||||||
|
.and_then(|opt| opt.ok_or(TryGetError::Null))
|
||||||
}
|
}
|
||||||
#[cfg(feature = "mock")]
|
#[cfg(feature = "mock")]
|
||||||
QueryResultRow::Mock(row) => match row.try_get(column.as_str()) {
|
QueryResultRow::Mock(row) => row.try_get(column.as_str()).map_err(|e| {
|
||||||
Ok(v) => Ok(Some(v)),
|
debug_print!("{:#?}", e.to_string());
|
||||||
Err(e) => {
|
TryGetError::Null
|
||||||
debug_print!("{:#?}", e.to_string());
|
}),
|
||||||
Ok(None)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -118,40 +114,15 @@ macro_rules! try_getable_all {
|
|||||||
macro_rules! try_getable_unsigned {
|
macro_rules! try_getable_unsigned {
|
||||||
( $type: ty ) => {
|
( $type: ty ) => {
|
||||||
impl TryGetable for $type {
|
impl TryGetable for $type {
|
||||||
fn try_get(res: &QueryResult, pre: &str, col: &str) -> Result<Self, DbErr> {
|
fn try_get(res: &QueryResult, pre: &str, col: &str) -> Result<Self, TryGetError> {
|
||||||
let column = format!("{}{}", pre, col);
|
|
||||||
match &res.row {
|
|
||||||
#[cfg(feature = "sqlx-mysql")]
|
|
||||||
QueryResultRow::SqlxMySql(row) => {
|
|
||||||
use sqlx::Row;
|
|
||||||
row.try_get(column.as_str())
|
|
||||||
.map_err(crate::sqlx_error_to_query_err)
|
|
||||||
}
|
|
||||||
#[cfg(feature = "sqlx-postgres")]
|
|
||||||
QueryResultRow::SqlxPostgres(_) => {
|
|
||||||
panic!("{} unsupported by sqlx-postgres", stringify!($type))
|
|
||||||
}
|
|
||||||
#[cfg(feature = "sqlx-sqlite")]
|
|
||||||
QueryResultRow::SqlxSqlite(row) => {
|
|
||||||
use sqlx::Row;
|
|
||||||
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())?),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl TryGetable for Option<$type> {
|
|
||||||
fn try_get(res: &QueryResult, pre: &str, col: &str) -> Result<Self, DbErr> {
|
|
||||||
let column = format!("{}{}", pre, col);
|
let column = format!("{}{}", pre, col);
|
||||||
match &res.row {
|
match &res.row {
|
||||||
#[cfg(feature = "sqlx-mysql")]
|
#[cfg(feature = "sqlx-mysql")]
|
||||||
QueryResultRow::SqlxMySql(row) => {
|
QueryResultRow::SqlxMySql(row) => {
|
||||||
use sqlx::Row;
|
use sqlx::Row;
|
||||||
row.try_get::<Option<$type>, _>(column.as_str())
|
row.try_get::<Option<$type>, _>(column.as_str())
|
||||||
.map_err(crate::sqlx_error_to_query_err)
|
.map_err(|e| TryGetError::DbErr(crate::sqlx_error_to_query_err(e)))
|
||||||
|
.and_then(|opt| opt.ok_or(TryGetError::Null))
|
||||||
}
|
}
|
||||||
#[cfg(feature = "sqlx-postgres")]
|
#[cfg(feature = "sqlx-postgres")]
|
||||||
QueryResultRow::SqlxPostgres(_) => {
|
QueryResultRow::SqlxPostgres(_) => {
|
||||||
@ -161,16 +132,14 @@ macro_rules! try_getable_unsigned {
|
|||||||
QueryResultRow::SqlxSqlite(row) => {
|
QueryResultRow::SqlxSqlite(row) => {
|
||||||
use sqlx::Row;
|
use sqlx::Row;
|
||||||
row.try_get::<Option<$type>, _>(column.as_str())
|
row.try_get::<Option<$type>, _>(column.as_str())
|
||||||
.map_err(crate::sqlx_error_to_query_err)
|
.map_err(|e| TryGetError::DbErr(crate::sqlx_error_to_query_err(e)))
|
||||||
|
.and_then(|opt| opt.ok_or(TryGetError::Null))
|
||||||
}
|
}
|
||||||
#[cfg(feature = "mock")]
|
#[cfg(feature = "mock")]
|
||||||
QueryResultRow::Mock(row) => match row.try_get(column.as_str()) {
|
QueryResultRow::Mock(row) => row.try_get(column.as_str()).map_err(|e| {
|
||||||
Ok(v) => Ok(Some(v)),
|
debug_print!("{:#?}", e.to_string());
|
||||||
Err(e) => {
|
TryGetError::Null
|
||||||
debug_print!("{:#?}", e.to_string());
|
}),
|
||||||
Ok(None)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -180,38 +149,15 @@ macro_rules! try_getable_unsigned {
|
|||||||
macro_rules! try_getable_mysql {
|
macro_rules! try_getable_mysql {
|
||||||
( $type: ty ) => {
|
( $type: ty ) => {
|
||||||
impl TryGetable for $type {
|
impl TryGetable for $type {
|
||||||
fn try_get(res: &QueryResult, pre: &str, col: &str) -> Result<Self, DbErr> {
|
fn try_get(res: &QueryResult, pre: &str, col: &str) -> Result<Self, TryGetError> {
|
||||||
let column = format!("{}{}", pre, col);
|
|
||||||
match &res.row {
|
|
||||||
#[cfg(feature = "sqlx-mysql")]
|
|
||||||
QueryResultRow::SqlxMySql(row) => {
|
|
||||||
use sqlx::Row;
|
|
||||||
row.try_get(column.as_str())
|
|
||||||
.map_err(crate::sqlx_error_to_query_err)
|
|
||||||
}
|
|
||||||
#[cfg(feature = "sqlx-postgres")]
|
|
||||||
QueryResultRow::SqlxPostgres(_) => {
|
|
||||||
panic!("{} unsupported by sqlx-postgres", stringify!($type))
|
|
||||||
}
|
|
||||||
#[cfg(feature = "sqlx-sqlite")]
|
|
||||||
QueryResultRow::SqlxSqlite(_) => {
|
|
||||||
panic!("{} unsupported by sqlx-sqlite", stringify!($type))
|
|
||||||
}
|
|
||||||
#[cfg(feature = "mock")]
|
|
||||||
QueryResultRow::Mock(row) => Ok(row.try_get(column.as_str())?),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl TryGetable for Option<$type> {
|
|
||||||
fn try_get(res: &QueryResult, pre: &str, col: &str) -> Result<Self, DbErr> {
|
|
||||||
let column = format!("{}{}", pre, col);
|
let column = format!("{}{}", pre, col);
|
||||||
match &res.row {
|
match &res.row {
|
||||||
#[cfg(feature = "sqlx-mysql")]
|
#[cfg(feature = "sqlx-mysql")]
|
||||||
QueryResultRow::SqlxMySql(row) => {
|
QueryResultRow::SqlxMySql(row) => {
|
||||||
use sqlx::Row;
|
use sqlx::Row;
|
||||||
row.try_get::<Option<$type>, _>(column.as_str())
|
row.try_get::<Option<$type>, _>(column.as_str())
|
||||||
.map_err(crate::sqlx_error_to_query_err)
|
.map_err(|e| TryGetError::DbErr(crate::sqlx_error_to_query_err(e)))
|
||||||
|
.and_then(|opt| opt.ok_or(TryGetError::Null))
|
||||||
}
|
}
|
||||||
#[cfg(feature = "sqlx-postgres")]
|
#[cfg(feature = "sqlx-postgres")]
|
||||||
QueryResultRow::SqlxPostgres(_) => {
|
QueryResultRow::SqlxPostgres(_) => {
|
||||||
@ -222,13 +168,10 @@ macro_rules! try_getable_mysql {
|
|||||||
panic!("{} unsupported by sqlx-sqlite", stringify!($type))
|
panic!("{} unsupported by sqlx-sqlite", stringify!($type))
|
||||||
}
|
}
|
||||||
#[cfg(feature = "mock")]
|
#[cfg(feature = "mock")]
|
||||||
QueryResultRow::Mock(row) => match row.try_get(column.as_str()) {
|
QueryResultRow::Mock(row) => row.try_get(column.as_str()).map_err(|e| {
|
||||||
Ok(v) => Ok(Some(v)),
|
debug_print!("{:#?}", e.to_string());
|
||||||
Err(e) => {
|
TryGetError::Null
|
||||||
debug_print!("{:#?}", e.to_string());
|
}),
|
||||||
Ok(None)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -238,31 +181,7 @@ macro_rules! try_getable_mysql {
|
|||||||
macro_rules! try_getable_postgres {
|
macro_rules! try_getable_postgres {
|
||||||
( $type: ty ) => {
|
( $type: ty ) => {
|
||||||
impl TryGetable for $type {
|
impl TryGetable for $type {
|
||||||
fn try_get(res: &QueryResult, pre: &str, col: &str) -> Result<Self, DbErr> {
|
fn try_get(res: &QueryResult, pre: &str, col: &str) -> Result<Self, TryGetError> {
|
||||||
let column = format!("{}{}", pre, col);
|
|
||||||
match &res.row {
|
|
||||||
#[cfg(feature = "sqlx-mysql")]
|
|
||||||
QueryResultRow::SqlxMySql(_) => {
|
|
||||||
panic!("{} unsupported by sqlx-mysql", stringify!($type))
|
|
||||||
}
|
|
||||||
#[cfg(feature = "sqlx-postgres")]
|
|
||||||
QueryResultRow::SqlxPostgres(row) => {
|
|
||||||
use sqlx::Row;
|
|
||||||
row.try_get(column.as_str())
|
|
||||||
.map_err(crate::sqlx_error_to_query_err)
|
|
||||||
}
|
|
||||||
#[cfg(feature = "sqlx-sqlite")]
|
|
||||||
QueryResultRow::SqlxSqlite(_) => {
|
|
||||||
panic!("{} unsupported by sqlx-sqlite", stringify!($type))
|
|
||||||
}
|
|
||||||
#[cfg(feature = "mock")]
|
|
||||||
QueryResultRow::Mock(row) => Ok(row.try_get(column.as_str())?),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl TryGetable for Option<$type> {
|
|
||||||
fn try_get(res: &QueryResult, pre: &str, col: &str) -> Result<Self, DbErr> {
|
|
||||||
let column = format!("{}{}", pre, col);
|
let column = format!("{}{}", pre, col);
|
||||||
match &res.row {
|
match &res.row {
|
||||||
#[cfg(feature = "sqlx-mysql")]
|
#[cfg(feature = "sqlx-mysql")]
|
||||||
@ -273,20 +192,18 @@ macro_rules! try_getable_postgres {
|
|||||||
QueryResultRow::SqlxPostgres(row) => {
|
QueryResultRow::SqlxPostgres(row) => {
|
||||||
use sqlx::Row;
|
use sqlx::Row;
|
||||||
row.try_get::<Option<$type>, _>(column.as_str())
|
row.try_get::<Option<$type>, _>(column.as_str())
|
||||||
.map_err(crate::sqlx_error_to_query_err)
|
.map_err(|e| TryGetError::DbErr(crate::sqlx_error_to_query_err(e)))
|
||||||
|
.and_then(|opt| opt.ok_or(TryGetError::Null))
|
||||||
}
|
}
|
||||||
#[cfg(feature = "sqlx-sqlite")]
|
#[cfg(feature = "sqlx-sqlite")]
|
||||||
QueryResultRow::SqlxSqlite(_) => {
|
QueryResultRow::SqlxSqlite(_) => {
|
||||||
panic!("{} unsupported by sqlx-sqlite", stringify!($type))
|
panic!("{} unsupported by sqlx-sqlite", stringify!($type))
|
||||||
}
|
}
|
||||||
#[cfg(feature = "mock")]
|
#[cfg(feature = "mock")]
|
||||||
QueryResultRow::Mock(row) => match row.try_get(column.as_str()) {
|
QueryResultRow::Mock(row) => row.try_get(column.as_str()).map_err(|e| {
|
||||||
Ok(v) => Ok(Some(v)),
|
debug_print!("{:#?}", e.to_string());
|
||||||
Err(e) => {
|
TryGetError::Null
|
||||||
debug_print!("{:#?}", e.to_string());
|
}),
|
||||||
Ok(None)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -320,83 +237,41 @@ use rust_decimal::Decimal;
|
|||||||
|
|
||||||
#[cfg(feature = "with-rust_decimal")]
|
#[cfg(feature = "with-rust_decimal")]
|
||||||
impl TryGetable for Decimal {
|
impl TryGetable for Decimal {
|
||||||
fn try_get(res: &QueryResult, pre: &str, col: &str) -> Result<Self, DbErr> {
|
fn try_get(res: &QueryResult, pre: &str, col: &str) -> Result<Self, TryGetError> {
|
||||||
let column = format!("{}{}", pre, col);
|
let column = format!("{}{}", pre, col);
|
||||||
match &res.row {
|
match &res.row {
|
||||||
#[cfg(feature = "sqlx-mysql")]
|
#[cfg(feature = "sqlx-mysql")]
|
||||||
QueryResultRow::SqlxMySql(row) => {
|
QueryResultRow::SqlxMySql(row) => {
|
||||||
use sqlx::Row;
|
use sqlx::Row;
|
||||||
row.try_get(column.as_str())
|
row.try_get::<Option<Decimal>, _>(column.as_str())
|
||||||
.map_err(crate::sqlx_error_to_query_err)
|
.map_err(|e| TryGetError::DbErr(crate::sqlx_error_to_query_err(e)))
|
||||||
|
.and_then(|opt| opt.ok_or(TryGetError::Null))
|
||||||
}
|
}
|
||||||
#[cfg(feature = "sqlx-postgres")]
|
#[cfg(feature = "sqlx-postgres")]
|
||||||
QueryResultRow::SqlxPostgres(row) => {
|
QueryResultRow::SqlxPostgres(row) => {
|
||||||
use sqlx::Row;
|
use sqlx::Row;
|
||||||
row.try_get(column.as_str())
|
row.try_get::<Option<Decimal>, _>(column.as_str())
|
||||||
.map_err(crate::sqlx_error_to_query_err)
|
.map_err(|e| TryGetError::DbErr(crate::sqlx_error_to_query_err(e)))
|
||||||
|
.and_then(|opt| opt.ok_or(TryGetError::Null))
|
||||||
}
|
}
|
||||||
#[cfg(feature = "sqlx-sqlite")]
|
#[cfg(feature = "sqlx-sqlite")]
|
||||||
QueryResultRow::SqlxSqlite(row) => {
|
QueryResultRow::SqlxSqlite(row) => {
|
||||||
use sqlx::Row;
|
use sqlx::Row;
|
||||||
let val: f64 = row
|
let val: Option<f64> = row
|
||||||
.try_get(column.as_str())
|
.try_get(column.as_str())
|
||||||
.map_err(crate::sqlx_error_to_query_err)?;
|
.map_err(|e| TryGetError::DbErr(crate::sqlx_error_to_query_err(e)))?;
|
||||||
use rust_decimal::prelude::FromPrimitive;
|
use rust_decimal::prelude::FromPrimitive;
|
||||||
Decimal::from_f64(val)
|
match val {
|
||||||
.ok_or_else(|| DbErr::Query("Failed to convert f64 into Decimal".to_owned()))
|
Some(v) => Decimal::from_f64(v)
|
||||||
}
|
.ok_or_else(|| TryGetError::DbErr(DbErr::Query("Failed to convert f64 into Decimal".to_owned()))),
|
||||||
#[cfg(feature = "mock")]
|
None => Err(TryGetError::Null)
|
||||||
QueryResultRow::Mock(row) => Ok(row.try_get(column.as_str())?),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(feature = "with-rust_decimal")]
|
|
||||||
impl TryGetable for Option<Decimal> {
|
|
||||||
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;
|
|
||||||
match row.try_get(column.as_str()) {
|
|
||||||
Ok(v) => Ok(Some(v)),
|
|
||||||
Err(e) => {
|
|
||||||
debug_print!("{:#?}", e.to_string());
|
|
||||||
Ok(None)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#[cfg(feature = "sqlx-postgres")]
|
|
||||||
QueryResultRow::SqlxPostgres(row) => {
|
|
||||||
use sqlx::Row;
|
|
||||||
match row.try_get(column.as_str()) {
|
|
||||||
Ok(v) => Ok(Some(v)),
|
|
||||||
Err(e) => {
|
|
||||||
debug_print!("{:#?}", e.to_string());
|
|
||||||
Ok(None)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#[cfg(feature = "sqlx-sqlite")]
|
|
||||||
QueryResultRow::SqlxSqlite(_) => {
|
|
||||||
let result: Result<Decimal, _> = TryGetable::try_get(res, pre, col);
|
|
||||||
match result {
|
|
||||||
Ok(v) => Ok(Some(v)),
|
|
||||||
Err(e) => {
|
|
||||||
debug_print!("{:#?}", e.to_string());
|
|
||||||
Ok(None)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#[cfg(feature = "mock")]
|
#[cfg(feature = "mock")]
|
||||||
QueryResultRow::Mock(row) => match row.try_get(column.as_str()) {
|
QueryResultRow::Mock(row) => row.try_get(column.as_str()).map_err(|e| {
|
||||||
Ok(v) => Ok(Some(v)),
|
debug_print!("{:#?}", e.to_string());
|
||||||
Err(e) => {
|
TryGetError::Null
|
||||||
debug_print!("{:#?}", e.to_string());
|
}),
|
||||||
Ok(None)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -25,7 +25,7 @@
|
|||||||
//! ```
|
//! ```
|
||||||
//!
|
//!
|
||||||
//! [](https://www.sea-ql.org/SeaORM/docs/index)
|
//! [](https://www.sea-ql.org/SeaORM/docs/index)
|
||||||
//! [](https://github.com/SeaQL/sea-orm/tree/master/examples/sqlx)
|
//! [](https://github.com/SeaQL/sea-orm/tree/master/examples)
|
||||||
//! [](https://github.com/SeaQL/sea-orm/issues/37)
|
//! [](https://github.com/SeaQL/sea-orm/issues/37)
|
||||||
//! [](https://discord.com/invite/uCPdDXzbdv)
|
//! [](https://discord.com/invite/uCPdDXzbdv)
|
||||||
//!
|
//!
|
||||||
@ -180,6 +180,12 @@
|
|||||||
//! # Ok(())
|
//! # Ok(())
|
||||||
//! # }
|
//! # }
|
||||||
//! ```
|
//! ```
|
||||||
|
//!
|
||||||
|
//! ## Learn More
|
||||||
|
//!
|
||||||
|
//! 1. [Design](https://github.com/SeaQL/sea-orm/tree/master/DESIGN.md)
|
||||||
|
//! 1. [Architecture](https://github.com/SeaQL/sea-orm/tree/master/ARCHITECTURE.md)
|
||||||
|
//!
|
||||||
//! ## License
|
//! ## License
|
||||||
//!
|
//!
|
||||||
//! Licensed under either of
|
//! Licensed under either of
|
||||||
|
Loading…
x
Reference in New Issue
Block a user