* 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>
191 lines
6.3 KiB
Rust
191 lines
6.3 KiB
Rust
//! Proxy connection example.
|
|
|
|
#![deny(missing_docs)]
|
|
|
|
mod entity;
|
|
|
|
use std::{
|
|
collections::BTreeMap,
|
|
sync::{Arc, Mutex},
|
|
};
|
|
|
|
use gluesql::{memory_storage::MemoryStorage, prelude::Glue};
|
|
use sea_orm::{
|
|
ActiveValue::Set, Database, DbBackend, DbErr, EntityTrait, ProxyDatabaseTrait, ProxyExecResult,
|
|
ProxyRow, Statement,
|
|
};
|
|
|
|
use entity::post::{ActiveModel, Entity};
|
|
|
|
struct ProxyDb {
|
|
mem: Mutex<Glue<MemoryStorage>>,
|
|
}
|
|
|
|
impl std::fmt::Debug for ProxyDb {
|
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
f.debug_struct("ProxyDb").finish()
|
|
}
|
|
}
|
|
|
|
impl ProxyDatabaseTrait for ProxyDb {
|
|
fn query(&self, statement: Statement) -> Result<Vec<ProxyRow>, DbErr> {
|
|
println!("SQL query: {:?}", statement);
|
|
let sql = statement.sql.clone();
|
|
|
|
let mut ret: Vec<ProxyRow> = vec![];
|
|
async_std::task::block_on(async {
|
|
for payload in self.mem.lock().unwrap().execute(sql).await.unwrap().iter() {
|
|
match payload {
|
|
gluesql::prelude::Payload::Select { labels, rows } => {
|
|
for row in rows.iter() {
|
|
let mut map = BTreeMap::new();
|
|
for (label, column) in labels.iter().zip(row.iter()) {
|
|
map.insert(
|
|
label.to_owned(),
|
|
match column {
|
|
gluesql::prelude::Value::I64(val) => {
|
|
sea_orm::Value::BigInt(Some(*val))
|
|
}
|
|
gluesql::prelude::Value::Str(val) => {
|
|
sea_orm::Value::String(Some(Box::new(val.to_owned())))
|
|
}
|
|
_ => unreachable!("Unsupported value: {:?}", column),
|
|
},
|
|
);
|
|
}
|
|
ret.push(map.into());
|
|
}
|
|
}
|
|
_ => unreachable!("Unsupported payload: {:?}", payload),
|
|
}
|
|
}
|
|
});
|
|
|
|
Ok(ret)
|
|
}
|
|
|
|
fn execute(&self, statement: Statement) -> Result<ProxyExecResult, DbErr> {
|
|
let sql = if let Some(values) = statement.values {
|
|
// Replace all the '?' with the statement values
|
|
use sqlparser::ast::{Expr, Value};
|
|
use sqlparser::dialect::GenericDialect;
|
|
use sqlparser::parser::Parser;
|
|
|
|
let dialect = GenericDialect {};
|
|
let mut ast = Parser::parse_sql(&dialect, statement.sql.as_str()).unwrap();
|
|
match &mut ast[0] {
|
|
sqlparser::ast::Statement::Insert {
|
|
columns, source, ..
|
|
} => {
|
|
for item in columns.iter_mut() {
|
|
item.quote_style = Some('"');
|
|
}
|
|
|
|
if let Some(obj) = source {
|
|
match &mut *obj.body {
|
|
sqlparser::ast::SetExpr::Values(obj) => {
|
|
for (mut item, val) in obj.rows[0].iter_mut().zip(values.0.iter()) {
|
|
match &mut item {
|
|
Expr::Value(item) => {
|
|
*item = match val {
|
|
sea_orm::Value::String(val) => {
|
|
Value::SingleQuotedString(match val {
|
|
Some(val) => val.to_string(),
|
|
None => "".to_string(),
|
|
})
|
|
}
|
|
sea_orm::Value::BigInt(val) => Value::Number(
|
|
val.unwrap_or(0).to_string(),
|
|
false,
|
|
),
|
|
_ => todo!(),
|
|
};
|
|
}
|
|
_ => todo!(),
|
|
}
|
|
}
|
|
}
|
|
_ => todo!(),
|
|
}
|
|
}
|
|
}
|
|
_ => todo!(),
|
|
}
|
|
|
|
let statement = &ast[0];
|
|
statement.to_string()
|
|
} else {
|
|
statement.sql
|
|
};
|
|
|
|
println!("SQL execute: {}", sql);
|
|
async_std::task::block_on(async {
|
|
self.mem.lock().unwrap().execute(sql).await.unwrap();
|
|
});
|
|
|
|
Ok(ProxyExecResult {
|
|
last_insert_id: 1,
|
|
rows_affected: 1,
|
|
})
|
|
}
|
|
}
|
|
|
|
#[async_std::main]
|
|
async fn main() {
|
|
let mem = MemoryStorage::default();
|
|
let mut glue = Glue::new(mem);
|
|
|
|
glue.execute(
|
|
r#"
|
|
CREATE TABLE IF NOT EXISTS posts (
|
|
id INTEGER PRIMARY KEY,
|
|
title TEXT NOT NULL,
|
|
text TEXT NOT NULL
|
|
)
|
|
"#,
|
|
)
|
|
.await
|
|
.unwrap();
|
|
|
|
let db = Database::connect_proxy(
|
|
DbBackend::Sqlite,
|
|
Arc::new(Mutex::new(Box::new(ProxyDb {
|
|
mem: Mutex::new(glue),
|
|
}))),
|
|
)
|
|
.await
|
|
.unwrap();
|
|
|
|
println!("Initialized");
|
|
|
|
let data = ActiveModel {
|
|
id: Set(11),
|
|
title: Set("Homo".to_owned()),
|
|
text: Set("いいよ、来いよ".to_owned()),
|
|
};
|
|
Entity::insert(data).exec(&db).await.unwrap();
|
|
let data = ActiveModel {
|
|
id: Set(45),
|
|
title: Set("Homo".to_owned()),
|
|
text: Set("そうだよ".to_owned()),
|
|
};
|
|
Entity::insert(data).exec(&db).await.unwrap();
|
|
let data = ActiveModel {
|
|
id: Set(14),
|
|
title: Set("Homo".to_owned()),
|
|
text: Set("悔い改めて".to_owned()),
|
|
};
|
|
Entity::insert(data).exec(&db).await.unwrap();
|
|
|
|
let list = Entity::find().all(&db).await.unwrap().to_vec();
|
|
println!("Result: {:?}", list);
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
#[smol_potat::test]
|
|
async fn try_run() {
|
|
crate::main()
|
|
}
|
|
}
|