Merge branch 'master' into linked-api
This commit is contained in:
commit
3cf8866620
90
.github/workflows/rust.yml
vendored
90
.github/workflows/rust.yml
vendored
@ -11,12 +11,11 @@ env:
|
||||
|
||||
jobs:
|
||||
|
||||
compile:
|
||||
name: Compile
|
||||
compile-sqlite:
|
||||
name: Compile SQLite
|
||||
runs-on: ubuntu-20.04
|
||||
strategy:
|
||||
matrix:
|
||||
database: [sqlite, mysql, postgres]
|
||||
runtime: [async-std, actix, tokio]
|
||||
tls: [native-tls, rustls]
|
||||
steps:
|
||||
@ -35,13 +34,77 @@ jobs:
|
||||
~/.cargo/git
|
||||
Cargo.lock
|
||||
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
|
||||
with:
|
||||
command: test
|
||||
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
|
||||
|
||||
test:
|
||||
@ -89,6 +152,7 @@ jobs:
|
||||
strategy:
|
||||
matrix:
|
||||
os: [ubuntu-latest]
|
||||
path: [async-std, tokio]
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
|
||||
@ -102,18 +166,12 @@ jobs:
|
||||
with:
|
||||
command: build
|
||||
args: >
|
||||
--manifest-path examples/async-std/Cargo.toml
|
||||
|
||||
- uses: actions-rs/cargo@v1
|
||||
with:
|
||||
command: build
|
||||
args: >
|
||||
--manifest-path examples/tokio/Cargo.toml
|
||||
--manifest-path examples/${{ matrix.path }}/Cargo.toml
|
||||
|
||||
sqlite:
|
||||
name: SQLite
|
||||
runs-on: ubuntu-20.04
|
||||
needs: compile
|
||||
needs: compile-sqlite
|
||||
env:
|
||||
DATABASE_URL: "sqlite::memory:"
|
||||
strategy:
|
||||
@ -147,7 +205,7 @@ jobs:
|
||||
mysql:
|
||||
name: MySQL
|
||||
runs-on: ubuntu-20.04
|
||||
needs: compile
|
||||
needs: compile-mysql
|
||||
env:
|
||||
DATABASE_URL: "mysql://root:@localhost"
|
||||
strategy:
|
||||
@ -199,7 +257,7 @@ jobs:
|
||||
mariadb:
|
||||
name: MariaDB
|
||||
runs-on: ubuntu-20.04
|
||||
needs: compile
|
||||
needs: compile-mysql
|
||||
env:
|
||||
DATABASE_URL: "mysql://root:@localhost"
|
||||
strategy:
|
||||
@ -251,7 +309,7 @@ jobs:
|
||||
postgres:
|
||||
name: Postgres
|
||||
runs-on: ubuntu-20.04
|
||||
needs: compile
|
||||
needs: compile-postgres
|
||||
env:
|
||||
DATABASE_URL: "postgres://root:root@localhost"
|
||||
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/)
|
||||
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
|
||||
|
||||
- [[#68]] Added `DateTimeWithTimeZone` as supported attribute type
|
||||
|
@ -7,7 +7,7 @@ members = [
|
||||
|
||||
[package]
|
||||
name = "sea-orm"
|
||||
version = "0.1.2"
|
||||
version = "0.1.3"
|
||||
authors = ["Chris Tsang <tyt2y7@gmail.com>"]
|
||||
edition = "2018"
|
||||
description = "🐚 An async & dynamic ORM for Rust"
|
||||
|
@ -1,4 +1,4 @@
|
||||
# Design Goals
|
||||
# Design
|
||||
|
||||
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
|
||||
uncover problems. These checks will be removed at production so there will be no run time penalty.
|
||||
|
||||
## Readability
|
||||
## API style
|
||||
|
||||
### Turbofish and inference
|
||||
|
||||
@ -65,4 +65,35 @@ has_many(cake::Entity, cake::Column::Id, fruit::Column::CakeId)
|
||||
we'd prefer having a builder and stating the params explicitly:
|
||||
```rust
|
||||
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://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://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%'
|
||||
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"))
|
||||
.exec(db)
|
||||
.await?;
|
||||
@ -148,6 +148,12 @@ fruit::Entity::delete_many()
|
||||
.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
|
||||
|
||||
Licensed under either of
|
||||
|
@ -17,8 +17,22 @@ pub(crate) enum QueryResultRow {
|
||||
Mock(crate::MockRow),
|
||||
}
|
||||
|
||||
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()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub trait TryGetable {
|
||||
fn try_get(res: &QueryResult, pre: &str, col: &str) -> Result<Self, DbErr>
|
||||
fn try_get(res: &QueryResult, pre: &str, col: &str) -> Result<Self, TryGetError>
|
||||
where
|
||||
Self: Sized;
|
||||
}
|
||||
@ -30,7 +44,7 @@ impl QueryResult {
|
||||
where
|
||||
T: TryGetable,
|
||||
{
|
||||
T::try_get(self, pre, col)
|
||||
Ok(T::try_get(self, pre, col)?)
|
||||
}
|
||||
}
|
||||
|
||||
@ -51,66 +65,48 @@ impl fmt::Debug for QueryResultRow {
|
||||
|
||||
// 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 {
|
||||
( $type: ty ) => {
|
||||
impl TryGetable for $type {
|
||||
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;
|
||||
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> {
|
||||
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::<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")]
|
||||
QueryResultRow::SqlxPostgres(row) => {
|
||||
use sqlx::Row;
|
||||
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")]
|
||||
QueryResultRow::SqlxSqlite(row) => {
|
||||
use sqlx::Row;
|
||||
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")]
|
||||
QueryResultRow::Mock(row) => match row.try_get(column.as_str()) {
|
||||
Ok(v) => Ok(Some(v)),
|
||||
Err(e) => {
|
||||
debug_print!("{:#?}", e.to_string());
|
||||
Ok(None)
|
||||
}
|
||||
},
|
||||
QueryResultRow::Mock(row) => row.try_get(column.as_str()).map_err(|e| {
|
||||
debug_print!("{:#?}", e.to_string());
|
||||
TryGetError::Null
|
||||
}),
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -120,40 +116,15 @@ macro_rules! try_getable_all {
|
||||
macro_rules! try_getable_unsigned {
|
||||
( $type: ty ) => {
|
||||
impl TryGetable for $type {
|
||||
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;
|
||||
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> {
|
||||
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::<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")]
|
||||
QueryResultRow::SqlxPostgres(_) => {
|
||||
@ -163,16 +134,14 @@ macro_rules! try_getable_unsigned {
|
||||
QueryResultRow::SqlxSqlite(row) => {
|
||||
use sqlx::Row;
|
||||
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")]
|
||||
QueryResultRow::Mock(row) => match row.try_get(column.as_str()) {
|
||||
Ok(v) => Ok(Some(v)),
|
||||
Err(e) => {
|
||||
debug_print!("{:#?}", e.to_string());
|
||||
Ok(None)
|
||||
}
|
||||
},
|
||||
QueryResultRow::Mock(row) => row.try_get(column.as_str()).map_err(|e| {
|
||||
debug_print!("{:#?}", e.to_string());
|
||||
TryGetError::Null
|
||||
}),
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -182,38 +151,15 @@ macro_rules! try_getable_unsigned {
|
||||
macro_rules! try_getable_mysql {
|
||||
( $type: ty ) => {
|
||||
impl TryGetable for $type {
|
||||
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;
|
||||
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> {
|
||||
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::<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")]
|
||||
QueryResultRow::SqlxPostgres(_) => {
|
||||
@ -224,13 +170,10 @@ macro_rules! try_getable_mysql {
|
||||
panic!("{} unsupported by sqlx-sqlite", stringify!($type))
|
||||
}
|
||||
#[cfg(feature = "mock")]
|
||||
QueryResultRow::Mock(row) => match row.try_get(column.as_str()) {
|
||||
Ok(v) => Ok(Some(v)),
|
||||
Err(e) => {
|
||||
debug_print!("{:#?}", e.to_string());
|
||||
Ok(None)
|
||||
}
|
||||
},
|
||||
QueryResultRow::Mock(row) => row.try_get(column.as_str()).map_err(|e| {
|
||||
debug_print!("{:#?}", e.to_string());
|
||||
TryGetError::Null
|
||||
}),
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -240,31 +183,7 @@ macro_rules! try_getable_mysql {
|
||||
macro_rules! try_getable_postgres {
|
||||
( $type: ty ) => {
|
||||
impl TryGetable for $type {
|
||||
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(_) => {
|
||||
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> {
|
||||
fn try_get(res: &QueryResult, pre: &str, col: &str) -> Result<Self, TryGetError> {
|
||||
let column = format!("{}{}", pre, col);
|
||||
match &res.row {
|
||||
#[cfg(feature = "sqlx-mysql")]
|
||||
@ -275,20 +194,18 @@ macro_rules! try_getable_postgres {
|
||||
QueryResultRow::SqlxPostgres(row) => {
|
||||
use sqlx::Row;
|
||||
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")]
|
||||
QueryResultRow::SqlxSqlite(_) => {
|
||||
panic!("{} unsupported by sqlx-sqlite", stringify!($type))
|
||||
}
|
||||
#[cfg(feature = "mock")]
|
||||
QueryResultRow::Mock(row) => match row.try_get(column.as_str()) {
|
||||
Ok(v) => Ok(Some(v)),
|
||||
Err(e) => {
|
||||
debug_print!("{:#?}", e.to_string());
|
||||
Ok(None)
|
||||
}
|
||||
},
|
||||
QueryResultRow::Mock(row) => row.try_get(column.as_str()).map_err(|e| {
|
||||
debug_print!("{:#?}", e.to_string());
|
||||
TryGetError::Null
|
||||
}),
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -322,83 +239,41 @@ use rust_decimal::Decimal;
|
||||
|
||||
#[cfg(feature = "with-rust_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);
|
||||
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)
|
||||
row.try_get::<Option<Decimal>, _>(column.as_str())
|
||||
.map_err(|e| TryGetError::DbErr(crate::sqlx_error_to_query_err(e)))
|
||||
.and_then(|opt| opt.ok_or(TryGetError::Null))
|
||||
}
|
||||
#[cfg(feature = "sqlx-postgres")]
|
||||
QueryResultRow::SqlxPostgres(row) => {
|
||||
use sqlx::Row;
|
||||
row.try_get(column.as_str())
|
||||
.map_err(crate::sqlx_error_to_query_err)
|
||||
row.try_get::<Option<Decimal>, _>(column.as_str())
|
||||
.map_err(|e| TryGetError::DbErr(crate::sqlx_error_to_query_err(e)))
|
||||
.and_then(|opt| opt.ok_or(TryGetError::Null))
|
||||
}
|
||||
#[cfg(feature = "sqlx-sqlite")]
|
||||
QueryResultRow::SqlxSqlite(row) => {
|
||||
use sqlx::Row;
|
||||
let val: f64 = row
|
||||
let val: Option<f64> = row
|
||||
.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;
|
||||
Decimal::from_f64(val)
|
||||
.ok_or_else(|| DbErr::Query("Failed to convert f64 into Decimal".to_owned()))
|
||||
}
|
||||
#[cfg(feature = "mock")]
|
||||
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)
|
||||
}
|
||||
match val {
|
||||
Some(v) => Decimal::from_f64(v)
|
||||
.ok_or_else(|| TryGetError::DbErr(DbErr::Query("Failed to convert f64 into Decimal".to_owned()))),
|
||||
None => Err(TryGetError::Null)
|
||||
}
|
||||
}
|
||||
#[cfg(feature = "mock")]
|
||||
QueryResultRow::Mock(row) => match row.try_get(column.as_str()) {
|
||||
Ok(v) => Ok(Some(v)),
|
||||
Err(e) => {
|
||||
debug_print!("{:#?}", e.to_string());
|
||||
Ok(None)
|
||||
}
|
||||
},
|
||||
QueryResultRow::Mock(row) => row.try_get(column.as_str()).map_err(|e| {
|
||||
debug_print!("{:#?}", e.to_string());
|
||||
TryGetError::Null
|
||||
}),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -25,7 +25,7 @@
|
||||
//! ```
|
||||
//!
|
||||
//! [](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://discord.com/invite/uCPdDXzbdv)
|
||||
//!
|
||||
@ -180,6 +180,12 @@
|
||||
//! # 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
|
||||
//!
|
||||
//! Licensed under either of
|
||||
|
Loading…
x
Reference in New Issue
Block a user