* Implement `IntoActiveValue` for `time` types. I tried to implement a [custom active model](https://www.sea-ql.org/SeaORM/docs/advanced-query/custom-active-model/), and one of the columns was `Option<TimeDateTimeWithTimeZone>`. I got a compiler error: ``` error[E0277]: the trait bound `std::option::Option<sea_orm::prelude::TimeDateTimeWithTimeZone>: IntoActiveValue<_>` is not satisfied ``` Looking into the source code, it seemed a simple oversight that this trait was implemented for the `chrono` types but not the `time` types, and it was easy enough to fix since there's already a macro to implement it for new types. I also noticed that the `time` types are not accounted for in `src/query/json.rs` while the `chrono` types are, which I assume is also an oversight. However, I don't have a need for that at this point and the fix for that seemed less trivial, so I'm just bringing it to your attention. Thanks for SeaORM! * Implement `IntoActiveValue` for `Vec<u8>` types * Add tests to double check and prevent it from happening again * Add docs * Fixup * Serialize `time` types as `serde_json::Value` Co-authored-by: Jimmy Cuadra <jimmy@jimmycuadra.com>
222 lines
9.6 KiB
Rust
222 lines
9.6 KiB
Rust
use crate::{DbErr, FromQueryResult, QueryResult};
|
|
use serde_json::Map;
|
|
pub use serde_json::Value as JsonValue;
|
|
|
|
impl FromQueryResult for JsonValue {
|
|
#[allow(unused_variables, unused_mut)]
|
|
fn from_query_result(res: &QueryResult, pre: &str) -> Result<Self, DbErr> {
|
|
let mut map = Map::new();
|
|
#[allow(unused_macros)]
|
|
macro_rules! try_get_type {
|
|
( $type: ty, $col: ident ) => {
|
|
if let Ok(v) = res.try_get::<Option<$type>>(pre, &$col) {
|
|
map.insert($col.to_owned(), json!(v));
|
|
continue;
|
|
}
|
|
};
|
|
}
|
|
match &res.row {
|
|
#[cfg(feature = "sqlx-mysql")]
|
|
crate::QueryResultRow::SqlxMySql(row) => {
|
|
use serde_json::json;
|
|
use sqlx::{Column, MySql, Row, Type};
|
|
for column in row.columns() {
|
|
let col = if !column.name().starts_with(pre) {
|
|
continue;
|
|
} else {
|
|
column.name().replacen(pre, "", 1)
|
|
};
|
|
let col_type = column.type_info();
|
|
macro_rules! match_mysql_type {
|
|
( $type: ty ) => {
|
|
if <$type as Type<MySql>>::type_info().eq(col_type) {
|
|
try_get_type!($type, col)
|
|
}
|
|
};
|
|
}
|
|
match_mysql_type!(bool);
|
|
match_mysql_type!(i8);
|
|
match_mysql_type!(i16);
|
|
match_mysql_type!(i32);
|
|
match_mysql_type!(i64);
|
|
match_mysql_type!(u8);
|
|
match_mysql_type!(u16);
|
|
match_mysql_type!(u32);
|
|
match_mysql_type!(u64);
|
|
match_mysql_type!(f32);
|
|
match_mysql_type!(f64);
|
|
match_mysql_type!(String);
|
|
#[cfg(feature = "with-chrono")]
|
|
match_mysql_type!(chrono::NaiveDate);
|
|
#[cfg(feature = "with-chrono")]
|
|
match_mysql_type!(chrono::NaiveTime);
|
|
#[cfg(feature = "with-chrono")]
|
|
match_mysql_type!(chrono::NaiveDateTime);
|
|
#[cfg(feature = "with-chrono")]
|
|
match_mysql_type!(chrono::DateTime<chrono::Utc>);
|
|
#[cfg(feature = "with-time")]
|
|
match_mysql_type!(time::Date);
|
|
#[cfg(feature = "with-time")]
|
|
match_mysql_type!(time::Time);
|
|
#[cfg(feature = "with-time")]
|
|
match_mysql_type!(time::PrimitiveDateTime);
|
|
#[cfg(feature = "with-time")]
|
|
match_mysql_type!(time::OffsetDateTime);
|
|
#[cfg(feature = "with-rust_decimal")]
|
|
match_mysql_type!(rust_decimal::Decimal);
|
|
#[cfg(feature = "with-json")]
|
|
try_get_type!(serde_json::Value, col);
|
|
try_get_type!(String, col);
|
|
#[cfg(feature = "with-uuid")]
|
|
try_get_type!(uuid::Uuid, col);
|
|
try_get_type!(Vec<u8>, col);
|
|
}
|
|
Ok(JsonValue::Object(map))
|
|
}
|
|
#[cfg(feature = "sqlx-postgres")]
|
|
crate::QueryResultRow::SqlxPostgres(row) => {
|
|
use serde_json::json;
|
|
use sqlx::{postgres::types::Oid, Column, Postgres, Row, Type};
|
|
for column in row.columns() {
|
|
let col = if !column.name().starts_with(pre) {
|
|
continue;
|
|
} else {
|
|
column.name().replacen(pre, "", 1)
|
|
};
|
|
let col_type = column.type_info();
|
|
macro_rules! match_postgres_type {
|
|
( $type: ty ) => {
|
|
if <$type as Type<Postgres>>::type_info().eq(col_type) {
|
|
try_get_type!($type, col)
|
|
}
|
|
};
|
|
}
|
|
match_postgres_type!(bool);
|
|
match_postgres_type!(i8);
|
|
match_postgres_type!(i16);
|
|
match_postgres_type!(i32);
|
|
match_postgres_type!(i64);
|
|
// match_postgres_type!(u8); // unsupported by SQLx Postgres
|
|
// match_postgres_type!(u16); // unsupported by SQLx Postgres
|
|
// Since 0.6.0, SQLx has dropped direct mapping from PostgreSQL's OID to Rust's `u32`;
|
|
// Instead, `u32` was wrapped by a `sqlx::Oid`.
|
|
if <Oid as Type<Postgres>>::type_info().eq(col_type) {
|
|
try_get_type!(u32, col)
|
|
}
|
|
// match_postgres_type!(u64); // unsupported by SQLx Postgres
|
|
match_postgres_type!(f32);
|
|
match_postgres_type!(f64);
|
|
#[cfg(feature = "with-chrono")]
|
|
match_postgres_type!(chrono::NaiveDate);
|
|
#[cfg(feature = "with-chrono")]
|
|
match_postgres_type!(chrono::NaiveTime);
|
|
#[cfg(feature = "with-chrono")]
|
|
match_postgres_type!(chrono::NaiveDateTime);
|
|
#[cfg(feature = "with-chrono")]
|
|
match_postgres_type!(chrono::DateTime<chrono::FixedOffset>);
|
|
#[cfg(feature = "with-time")]
|
|
match_postgres_type!(time::Date);
|
|
#[cfg(feature = "with-time")]
|
|
match_postgres_type!(time::Time);
|
|
#[cfg(feature = "with-time")]
|
|
match_postgres_type!(time::PrimitiveDateTime);
|
|
#[cfg(feature = "with-time")]
|
|
match_postgres_type!(time::OffsetDateTime);
|
|
#[cfg(feature = "with-rust_decimal")]
|
|
match_postgres_type!(rust_decimal::Decimal);
|
|
#[cfg(feature = "with-json")]
|
|
try_get_type!(serde_json::Value, col);
|
|
try_get_type!(String, col);
|
|
#[cfg(feature = "with-uuid")]
|
|
try_get_type!(uuid::Uuid, col);
|
|
try_get_type!(Vec<u8>, col);
|
|
}
|
|
Ok(JsonValue::Object(map))
|
|
}
|
|
#[cfg(feature = "sqlx-sqlite")]
|
|
crate::QueryResultRow::SqlxSqlite(row) => {
|
|
use serde_json::json;
|
|
use sqlx::{Column, Row, Sqlite, Type};
|
|
for column in row.columns() {
|
|
let col = if !column.name().starts_with(pre) {
|
|
continue;
|
|
} else {
|
|
column.name().replacen(pre, "", 1)
|
|
};
|
|
let col_type = column.type_info();
|
|
macro_rules! match_sqlite_type {
|
|
( $type: ty ) => {
|
|
if <$type as Type<Sqlite>>::type_info().eq(col_type) {
|
|
try_get_type!($type, col)
|
|
}
|
|
};
|
|
}
|
|
match_sqlite_type!(bool);
|
|
match_sqlite_type!(i8);
|
|
match_sqlite_type!(i16);
|
|
match_sqlite_type!(i32);
|
|
match_sqlite_type!(i64);
|
|
match_sqlite_type!(u8);
|
|
match_sqlite_type!(u16);
|
|
match_sqlite_type!(u32);
|
|
// match_sqlite_type!(u64); // unsupported by SQLx Sqlite
|
|
match_sqlite_type!(f32);
|
|
match_sqlite_type!(f64);
|
|
#[cfg(feature = "with-chrono")]
|
|
match_sqlite_type!(chrono::NaiveDate);
|
|
#[cfg(feature = "with-chrono")]
|
|
match_sqlite_type!(chrono::NaiveTime);
|
|
#[cfg(feature = "with-chrono")]
|
|
match_sqlite_type!(chrono::NaiveDateTime);
|
|
try_get_type!(String, col);
|
|
#[cfg(feature = "with-uuid")]
|
|
try_get_type!(uuid::Uuid, col);
|
|
try_get_type!(Vec<u8>, col);
|
|
}
|
|
Ok(JsonValue::Object(map))
|
|
}
|
|
#[cfg(feature = "mock")]
|
|
crate::QueryResultRow::Mock(row) => {
|
|
for (column, value) in row.clone().into_column_value_tuples() {
|
|
let col = if !column.starts_with(pre) {
|
|
continue;
|
|
} else {
|
|
column.replacen(pre, "", 1)
|
|
};
|
|
map.insert(col, sea_query::sea_value_to_json_value(&value));
|
|
}
|
|
Ok(JsonValue::Object(map))
|
|
}
|
|
#[allow(unreachable_patterns)]
|
|
_ => unreachable!(),
|
|
}
|
|
}
|
|
}
|
|
|
|
#[cfg(test)]
|
|
#[cfg(feature = "mock")]
|
|
mod tests {
|
|
use crate::tests_cfg::cake;
|
|
use crate::{entity::*, DbBackend, DbErr, MockDatabase};
|
|
use sea_query::Value;
|
|
|
|
#[smol_potat::test]
|
|
async fn to_json_1() -> Result<(), DbErr> {
|
|
let db = MockDatabase::new(DbBackend::Postgres)
|
|
.append_query_results(vec![vec![maplit::btreemap! {
|
|
"id" => Into::<Value>::into(128), "name" => Into::<Value>::into("apple")
|
|
}]])
|
|
.into_connection();
|
|
|
|
assert_eq!(
|
|
cake::Entity::find().into_json().one(&db).await.unwrap(),
|
|
Some(serde_json::json!({
|
|
"id": 128,
|
|
"name": "apple"
|
|
}))
|
|
);
|
|
|
|
Ok(())
|
|
}
|
|
}
|