* feat: Add proxy connection type * feat: Add proxy database's proxy functions trait. * fix: Remove some unused impl to fix the unit test * test: Create the proxy by empty declaration. * test: Try to genereate query and exec commands. * perf: Add more query debug trait for debugging. * chore: Add the example for wasi + proxy. * chore: Try to read string from wasmtime vm. * chore: Sucks, but how to do without tokio::spawn? * chore: Complete the basic memory read logic. * chore: Abandon the WASI demo, native demo first... * refactor: Use single proxy connection generator to avoid stack overflow * refactor: Rename the inner structs' name * fix: Fix CI clippy and unit test * fix: Rename the example. * chore: Try to embed surrealdb for proxy test. * fix: Transfer the query result correctly. * refactor: Rename the example. * chore: Ready to add example for wasmtime proxy. * feat: Try to compile sea-orm into wasm binary. But it would failed on wasm32-wasi target because of the socket deps. It can be compiled on wasm32-unknown-unknown target. * fix: WASM targets can't use sqlx. * fix: Try to fix CI by remove toml. * fix: Try to fix CI by remove toml. * fix: Move vm to the example's root dir. * fix: Add a pre-build script. * chore: Add README. * fix: Try to fix CI. * feat: Add proxy logic in wasm module. * fix: Try to run the wasi module. But WASI cannot support multi threads.. so the module was run failed. * refactor: Bump wasmtime to 14. * fix: Now we can use async traits on wasmtime. The solution is add the current thread tag to tokio-wasi. * build: Use build.rs instead of dynamic command. * feat: Add the execute result's transfer logic. * fix: Convert sqlx query result for sea-query. * fix: Now we can transfer wasm's query to outside. * refactor: Convert to ProxyRow first. It's the solution to know the type information about the value. * fix: Multiple time library reference. * feat: Add a new proxy example which uses GlueSQL. * test: Add the test cases for three new examples. Just try to run once... * ci: Add wasm component's compiler for unit test. * ci: Add wasi target. * ci: It may needs wasi target twice... * feat: Add more keys for proxy execute result. To transfer the fully information of the execute result. * fix: Use custom id type instead of json value. * fix: Wrong reference type. * fix: Rewrite the transformer. * perf: Add ToString trait for proxy exec result. * revert: Again. Refs: 9bac6e91ca9df04ccd8368906e1613cfc5b96218 * revert: Back to the basic proxy exec result. Refs: e0330dde73a54d461d5f38c69eec5e13bcc928d4 * refactor: Update GlueSQL and SurrealDB examples. (#1980) * refactor: Bump gluesql to 0.15 Relate to https://github.com/gluesql/gluesql/issues/1438 * Use SQLParser to parse and replace placeholders. * Use SQLParser for surrealdb demo. * Transform the query by SQLParser. * Tweaks * Remove wasmtime example. (#2001) * ci: Add additional targets. * Remove proxy wasmtime example. * Format --------- Co-authored-by: 伊欧 <langyo.china@gmail.com> Co-authored-by: 伊欧 <m13776491897@163.com>
261 lines
12 KiB
Rust
261 lines
12 KiB
Rust
use crate::{error::*, 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 ) => {
|
|
match col_type.kind() {
|
|
#[cfg(feature = "postgres-array")]
|
|
sqlx::postgres::PgTypeKind::Array(_) => {
|
|
if <Vec<$type> as Type<Postgres>>::type_info().eq(col_type) {
|
|
try_get_type!(Vec<$type>, col);
|
|
}
|
|
}
|
|
_ => {
|
|
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);
|
|
#[cfg(all(feature = "with-json", feature = "postgres-array"))]
|
|
try_get_type!(Vec<serde_json::Value>, col);
|
|
try_get_type!(String, col);
|
|
#[cfg(feature = "postgres-array")]
|
|
try_get_type!(Vec<String>, col);
|
|
#[cfg(feature = "with-uuid")]
|
|
try_get_type!(uuid::Uuid, col);
|
|
#[cfg(all(feature = "with-uuid", feature = "postgres-array"))]
|
|
try_get_type!(Vec<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);
|
|
#[cfg(feature = "with-time")]
|
|
match_sqlite_type!(time::Date);
|
|
#[cfg(feature = "with-time")]
|
|
match_sqlite_type!(time::Time);
|
|
#[cfg(feature = "with-time")]
|
|
match_sqlite_type!(time::PrimitiveDateTime);
|
|
#[cfg(feature = "with-time")]
|
|
match_sqlite_type!(time::OffsetDateTime);
|
|
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))
|
|
}
|
|
#[cfg(feature = "proxy")]
|
|
crate::QueryResultRow::Proxy(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([[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(())
|
|
}
|
|
}
|