diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index 7be8cd0f8..6e3e1ace1 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -1,15 +1,15 @@ # GitHub Actions with Conditional Job Running Based on Commit Message -# +# # -------------------------------------------------------------------------------- -# +# # Following jobs will always run -# +# # - `clippy` # - `test` # - `examples` -# +# # Following jobs will be run when no keywords were found in commit message) -# +# # - `compile-sqlite` # - `sqlite` # - `compile-mysql` @@ -17,37 +17,36 @@ # - `mariadb` # - `compile-postgres` # - `postgres` -# +# # Following jobs will be run if keywords `[issues]` were found in commit message -# +# # - Jobs that will always run # - `issues` -# +# # Following jobs will be run if keywords `[cli]` were found in commit message -# +# # - Jobs that will always run # - `cli` -# +# # Following jobs will be run if keywords `[sqlite]` were found in commit message -# +# # - Jobs that will always run # - `compile-sqlite` # - `sqlite` -# +# # Following jobs will be run if keywords `[mysql]` were found in commit message -# +# # - Jobs that will always run # - `compile-mysql` # - `mysql` # - `mariadb` -# +# # Following jobs will be run if keywords `[postgres]` were found in commit message -# +# # - Jobs that will always run # - `compile-postgres` # - `postgres` - name: tests on: @@ -73,7 +72,6 @@ env: CARGO_TERM_COLOR: always jobs: - init: name: Init runs-on: ubuntu-latest @@ -299,7 +297,7 @@ jobs: }} runs-on: ubuntu-latest env: - DATABASE_URL: "sqlite::memory:" + DATABASE_URL: 'sqlite::memory:' strategy: fail-fast: false matrix: @@ -331,7 +329,7 @@ jobs: }} runs-on: ubuntu-latest env: - DATABASE_URL: "mysql://root:@localhost" + DATABASE_URL: 'mysql://root:@localhost' strategy: fail-fast: false matrix: @@ -349,7 +347,7 @@ jobs: MYSQL_ALLOW_EMPTY_PASSWORD: yes MYSQL_ROOT_PASSWORD: ports: - - "3306:3306" + - '3306:3306' options: >- --health-cmd="mysqladmin ping" --health-interval=10s @@ -381,7 +379,7 @@ jobs: }} runs-on: ubuntu-latest env: - DATABASE_URL: "mysql://root:@localhost" + DATABASE_URL: 'mysql://root:@localhost' strategy: fail-fast: false matrix: @@ -399,7 +397,7 @@ jobs: MYSQL_ALLOW_EMPTY_PASSWORD: yes MYSQL_ROOT_PASSWORD: ports: - - "3306:3306" + - '3306:3306' options: >- --health-cmd="mysqladmin ping" --health-interval=10s @@ -430,7 +428,7 @@ jobs: }} runs-on: ubuntu-latest env: - DATABASE_URL: "postgres://root:root@localhost" + DATABASE_URL: 'postgres://root:root@localhost' strategy: fail-fast: false matrix: @@ -445,7 +443,7 @@ jobs: POSTGRES_USER: root POSTGRES_PASSWORD: root ports: - - "5432:5432" + - '5432:5432' options: >- --health-cmd pg_isready --health-interval 10s diff --git a/examples/proxy_wasmtime_example/Cargo.toml b/examples/proxy_wasmtime_example/Cargo.toml deleted file mode 100644 index 1ab928aae..000000000 --- a/examples/proxy_wasmtime_example/Cargo.toml +++ /dev/null @@ -1,34 +0,0 @@ -[package] -name = "sea-orm-proxy-wasmtime-example" -version = "0.1.0" -authors = ["Langyo "] -edition = "2021" -publish = false -build = "build.rs" - -[workspace] -members = [".", "module"] -resolver = "2" - -[dependencies] -anyhow = "1" -bytes = "1" -async-trait = "0.1" -serde = { version = "1", features = ["derive"] } -serde_json = "1" -reqwest = { version = "0.11", features = ["blocking"] } -lazy_static = "1" -flume = "0.11" -async-std = { version = "^1", features = ["attributes", "tokio1"] } - -wit-component = "0.16" -wasmtime = { version = "14", features = ["component-model"] } -wasmtime-wasi = "14" -surrealdb = { version = "1", features = ["kv-mem"] } -sea-orm = { path = "../..", features = [ - "proxy", -] } - -[dev-dependencies] -smol = { version = "1.2" } -smol-potat = { version = "1.1" } diff --git a/examples/proxy_wasmtime_example/README.md b/examples/proxy_wasmtime_example/README.md deleted file mode 100644 index 4735d6d2a..000000000 --- a/examples/proxy_wasmtime_example/README.md +++ /dev/null @@ -1,8 +0,0 @@ -# SeaORM Proxy Demo on WASI - -Run this demo by [wasmtime](https://wasmtime.dev/) with the following command: - -```bash -cargo build --target wasm32-wasi --package module --release -cargo run -``` diff --git a/examples/proxy_wasmtime_example/build.rs b/examples/proxy_wasmtime_example/build.rs deleted file mode 100644 index 28f3a5440..000000000 --- a/examples/proxy_wasmtime_example/build.rs +++ /dev/null @@ -1,18 +0,0 @@ -use std::{env, path::Path, process::Command}; - -fn main() { - // Build the wasm component binary - let pwd = Path::new(env!("CARGO_MANIFEST_DIR")).to_path_buf(); - Command::new("cargo") - .current_dir(pwd.clone()) - .arg("build") - .arg("--target") - .arg("wasm32-wasi") - .arg("--package") - .arg("module") - .arg("--release") - .status() - .unwrap(); - - println!("cargo:rerun-if-changed=module/**/*"); -} diff --git a/examples/proxy_wasmtime_example/module/Cargo.toml b/examples/proxy_wasmtime_example/module/Cargo.toml deleted file mode 100644 index b32eaf0ef..000000000 --- a/examples/proxy_wasmtime_example/module/Cargo.toml +++ /dev/null @@ -1,14 +0,0 @@ -[package] -name = "module" -version = "0.1.0" -authors = ["Langyo "] -edition = "2021" -publish = false - -[dependencies] -serde_json = "1" -serde = { version = "1", features = ["derive"] } -anyhow = "1" -tokio_wasi = { version = "1", features = ["full"] } - -sea-orm = { path = "../../../", features = ["proxy", "runtime-tokio"] } diff --git a/examples/proxy_wasmtime_example/module/src/entity/mod.rs b/examples/proxy_wasmtime_example/module/src/entity/mod.rs deleted file mode 100644 index e8b6291ac..000000000 --- a/examples/proxy_wasmtime_example/module/src/entity/mod.rs +++ /dev/null @@ -1 +0,0 @@ -pub mod post; diff --git a/examples/proxy_wasmtime_example/module/src/entity/post.rs b/examples/proxy_wasmtime_example/module/src/entity/post.rs deleted file mode 100644 index 868846046..000000000 --- a/examples/proxy_wasmtime_example/module/src/entity/post.rs +++ /dev/null @@ -1,17 +0,0 @@ -use sea_orm::entity::prelude::*; -use serde::{Deserialize, Serialize}; - -#[derive(Clone, Debug, PartialEq, Eq, DeriveEntityModel, Deserialize, Serialize)] -#[sea_orm(table_name = "posts")] -pub struct Model { - #[sea_orm(primary_key)] - pub id: i64, - - pub title: String, - pub text: String, -} - -#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)] -pub enum Relation {} - -impl ActiveModelBehavior for ActiveModel {} diff --git a/examples/proxy_wasmtime_example/module/src/main.rs b/examples/proxy_wasmtime_example/module/src/main.rs deleted file mode 100644 index 8ca4c8a90..000000000 --- a/examples/proxy_wasmtime_example/module/src/main.rs +++ /dev/null @@ -1,144 +0,0 @@ -//! Proxy connection example. - -#![deny(missing_docs)] - -mod entity; - -use serde::{Deserialize, Serialize}; -use std::{ - collections::BTreeMap, - sync::{Arc, Mutex}, -}; - -use sea_orm::{ - ActiveValue::Set, Database, DbBackend, DbErr, EntityTrait, ProxyDatabaseTrait, ProxyExecResult, - ProxyRow, Statement, -}; - -use entity::post::{ActiveModel, Entity}; - -#[derive(Clone, Debug, Serialize, Deserialize)] -pub(crate) enum RequestMsg { - Query(String), - Execute(String), - - Debug(String), -} - -#[derive(Clone, Debug, Serialize, Deserialize)] -pub(crate) enum ResponseMsg { - Query(Vec), - Execute(ProxyExecResult), - - None, -} - -#[derive(Debug)] -struct ProxyDb {} - -impl ProxyDatabaseTrait for ProxyDb { - fn query(&self, statement: Statement) -> Result, DbErr> { - let sql = statement.sql.clone(); - println!( - "{}", - serde_json::to_string(&RequestMsg::Query(sql)).unwrap() - ); - - let mut input = String::new(); - std::io::stdin().read_line(&mut input).unwrap(); - let ret: ResponseMsg = serde_json::from_str(&input).unwrap(); - let ret = match ret { - ResponseMsg::Query(v) => v, - _ => unreachable!("Not a query result"), - }; - - let mut rows: Vec = vec![]; - for row in ret { - let mut map: BTreeMap = BTreeMap::new(); - for (k, v) in row.as_object().unwrap().iter() { - map.insert(k.to_owned(), { - if v.is_string() { - sea_orm::Value::String(Some(Box::new(v.as_str().unwrap().to_string()))) - } else if v.is_number() { - sea_orm::Value::BigInt(Some(v.as_i64().unwrap())) - } else if v.is_boolean() { - sea_orm::Value::Bool(Some(v.as_bool().unwrap())) - } else { - unreachable!("Unknown json type") - } - }); - } - rows.push(ProxyRow { values: map }); - } - - Ok(rows) - } - - fn execute(&self, statement: Statement) -> Result { - let sql = { - if let Some(values) = statement.values { - // Replace all the '?' with the statement values - let mut new_sql = statement.sql.clone(); - let mark_count = new_sql.matches('?').count(); - for (i, v) in values.0.iter().enumerate() { - if i >= mark_count { - break; - } - new_sql = new_sql.replacen('?', &v.to_string(), 1); - } - - new_sql - } else { - statement.sql - } - }; - - // Send the query to stdout - let msg = RequestMsg::Execute(sql); - let msg = serde_json::to_string(&msg).unwrap(); - println!("{}", msg); - - // Get the result from stdin - let mut input = String::new(); - std::io::stdin().read_line(&mut input).unwrap(); - let ret: ResponseMsg = serde_json::from_str(&input).unwrap(); - let ret = match ret { - ResponseMsg::Execute(v) => v, - _ => unreachable!(), - }; - - Ok(ret) - } -} - -#[tokio::main(flavor = "current_thread")] -async fn main() { - let db = Database::connect_proxy(DbBackend::MySql, Arc::new(Mutex::new(Box::new(ProxyDb {})))) - .await - .unwrap(); - - let data = ActiveModel { - title: Set("Homo".to_owned()), - text: Set("いいよ、来いよ".to_owned()), - ..Default::default() - }; - Entity::insert(data).exec(&db).await.unwrap(); - let data = ActiveModel { - title: Set("Homo".to_owned()), - text: Set("そうだよ".to_owned()), - ..Default::default() - }; - Entity::insert(data).exec(&db).await.unwrap(); - let data = ActiveModel { - title: Set("Homo".to_owned()), - text: Set("悔い改めて".to_owned()), - ..Default::default() - }; - Entity::insert(data).exec(&db).await.unwrap(); - - let list = Entity::find().all(&db).await.unwrap().to_vec(); - println!( - "{}", - serde_json::to_string(&RequestMsg::Debug(format!("{:?}", list))).unwrap() - ); -} diff --git a/examples/proxy_wasmtime_example/res/wasi_snapshot_preview1.command.wasm b/examples/proxy_wasmtime_example/res/wasi_snapshot_preview1.command.wasm deleted file mode 100644 index f0997c619..000000000 Binary files a/examples/proxy_wasmtime_example/res/wasi_snapshot_preview1.command.wasm and /dev/null differ diff --git a/examples/proxy_wasmtime_example/src/main.rs b/examples/proxy_wasmtime_example/src/main.rs deleted file mode 100644 index e4c25333e..000000000 --- a/examples/proxy_wasmtime_example/src/main.rs +++ /dev/null @@ -1,122 +0,0 @@ -use anyhow::Result; -use bytes::Bytes; - -use sea_orm::{ConnectionTrait, Database, DatabaseBackend, ProxyExecResult, Statement}; -use wasmtime::{Config, Engine}; -use wit_component::ComponentEncoder; - -mod runtime; -mod stream; - -use { - runtime::Runtime, - stream::{RequestMsg, ResponseMsg}, -}; - -#[async_std::main] -async fn main() -> Result<()> { - // Transfer the wasm binary to wasm component binary - let adapter = include_bytes!("../res/wasi_snapshot_preview1.command.wasm"); - let pwd = std::path::Path::new(env!("CARGO_MANIFEST_DIR")).to_path_buf(); - let component = pwd.join("target/wasm32-wasi/release/module.wasm"); - let component = std::fs::read(component)?; - let component = &ComponentEncoder::default() - .module(&component)? - .validate(true) - .adapter("wasi_snapshot_preview1", adapter)? - .encode()?; - - let mut config = Config::new(); - config.wasm_component_model(true); - - let engine = &Engine::new(&config)?; - - let cwasm = engine.precompile_component(component)?; - let cwasm = Bytes::from(cwasm); - - // Create the database connection - println!("Creating database connection..."); - let db = Database::connect("sqlite::memory:").await?; - db.execute(Statement::from_string( - DatabaseBackend::Sqlite, - r#" - CREATE TABLE IF NOT EXISTS posts ( - id INTEGER PRIMARY KEY AUTOINCREMENT, - title TEXT NOT NULL, - text TEXT NOT NULL - ) - "#, - )) - .await?; - - // Run the prototype demo - println!("Running prototype demo..."); - let mut runner = Runtime::new(cwasm).init()?; - - let tx = runner.tx.clone(); - let rx = runner.rx.clone(); - - std::thread::spawn(move || { - runner.run().unwrap(); - }); - - while let Ok(msg) = rx.recv() { - match msg { - RequestMsg::Execute(sql) => { - let ret: ProxyExecResult = db - .execute(Statement::from_string(DatabaseBackend::Sqlite, sql)) - .await? - .into(); - println!("Execute result: {:?}", ret); - let ret = ResponseMsg::Execute(ret); - tx.send(ret)?; - } - RequestMsg::Query(sql) => { - let ret: Vec = db - .query_all(Statement::from_string(DatabaseBackend::Sqlite, sql)) - .await? - .iter() - .map(|r| sea_orm::from_query_result_to_proxy_row(&r)) - .map(|r| { - // This demo only converts it to json value currently. - // But it can be converted to any other format that includes the type information. - // You can use 'match' to deal the type of the value on sea_orm::Value. - r.into() - }) - .collect(); - println!("Query result: {:?}", ret); - - let ret = ResponseMsg::Query(ret); - tx.send(ret)?; - } - RequestMsg::Debug(msg) => { - println!("VM Debug: {}", msg); - } - } - } - - Ok(()) -} - -#[cfg(test)] -mod tests { - use anyhow::Result; - - #[smol_potat::test] - async fn try_run() -> Result<()> { - // Build the wasm component binary - use std::{env, path::Path, process::Command}; - let pwd = Path::new(env!("CARGO_MANIFEST_DIR")).to_path_buf(); - Command::new("cargo") - .current_dir(pwd.clone()) - .arg("build") - .arg("--target") - .arg("wasm32-wasi") - .arg("--package") - .arg("module") - .arg("--release") - .status()?; - - crate::main() - } -} diff --git a/examples/proxy_wasmtime_example/src/runtime.rs b/examples/proxy_wasmtime_example/src/runtime.rs deleted file mode 100644 index 789dbf057..000000000 --- a/examples/proxy_wasmtime_example/src/runtime.rs +++ /dev/null @@ -1,115 +0,0 @@ -use anyhow::{anyhow, Result}; -use bytes::Bytes; -use flume::{Receiver, Sender}; - -use wasmtime::{ - component::{Component, Linker}, - Config, Engine, Store, -}; -use wasmtime_wasi::preview2::{ - command::{self, sync::Command}, - Table, WasiCtx, WasiCtxBuilder, WasiView, -}; - -use crate::stream::{ - HostInputStreamBox, HostOutputStreamBox, {RequestMsg, ResponseMsg}, -}; -struct Ctx { - wasi: WasiCtx, - table: Table, -} - -impl WasiView for Ctx { - fn ctx(&self) -> &WasiCtx { - &self.wasi - } - fn ctx_mut(&mut self) -> &mut WasiCtx { - &mut self.wasi - } - fn table(&self) -> &Table { - &self.table - } - fn table_mut(&mut self) -> &mut Table { - &mut self.table - } -} - -#[derive(Clone)] -pub struct Runtime { - engine: Engine, - component: Component, -} - -pub struct Runner { - store: Store, - component: Component, - linker: Linker, - - pub tx: Sender, - pub rx: Receiver, -} - -impl Runtime { - pub fn new(bin: Bytes) -> Self { - let mut config = Config::new(); - config.wasm_component_model(true); - let engine = Engine::new(&config).unwrap(); - - let component = unsafe { Component::deserialize(&engine, &bin).unwrap() }; - - Self { engine, component } - } - - pub fn init(&mut self) -> Result { - let mut linker = Linker::new(&self.engine); - command::sync::add_to_linker(&mut linker).unwrap(); - - let mut wasi = WasiCtxBuilder::new(); - wasi.inherit_stderr(); - - let (tx_in, rx_in) = flume::unbounded(); - let (tx_out, rx_out) = flume::unbounded(); - - let input_stream = HostInputStreamBox { - tasks: Default::default(), - }; - let output_stream = HostOutputStreamBox { tx: tx_out }; - - let rx = rx_in.clone(); - let tasks = input_stream.tasks.clone(); - std::thread::spawn(move || { - while let Ok(msg) = rx.recv() { - tasks.lock().unwrap().push(msg); - } - }); - - wasi.stdin(input_stream); - wasi.stdout(output_stream); - - let wasi = wasi.build(); - let table = Table::new(); - let store = Store::new(&self.engine, Ctx { wasi, table }); - - Ok(Runner { - store, - component: self.component.clone(), - linker, - - tx: tx_in, - rx: rx_out, - }) - } -} - -impl Runner { - pub fn run(&mut self) -> Result<()> { - let (command, _) = Command::instantiate(&mut self.store, &self.component, &self.linker)?; - - command - .wasi_cli_run() - .call_run(&mut self.store)? - .map_err(|()| anyhow!("guest command returned error"))?; - - Ok(()) - } -} diff --git a/examples/proxy_wasmtime_example/src/stream.rs b/examples/proxy_wasmtime_example/src/stream.rs deleted file mode 100644 index 517ad0643..000000000 --- a/examples/proxy_wasmtime_example/src/stream.rs +++ /dev/null @@ -1,113 +0,0 @@ -use bytes::Bytes; -use flume::Sender; -use serde::{Deserialize, Serialize}; -use std::sync::{Arc, Mutex}; - -use sea_orm::ProxyExecResult; -use wasmtime_wasi::preview2::{ - HostInputStream, HostOutputStream, StdinStream, StdoutStream, StreamResult, Subscribe, -}; - -#[derive(Clone, Debug, Serialize, Deserialize)] -pub enum RequestMsg { - Query(String), - Execute(String), - - Debug(String), -} - -#[derive(Clone, Debug, Serialize, Deserialize)] -pub enum ResponseMsg { - Query(Vec), - Execute(ProxyExecResult), - - None, -} - -pub struct InputStream { - pub tasks: Arc>>, -} - -#[async_trait::async_trait] -impl Subscribe for InputStream { - async fn ready(&mut self) {} -} - -#[async_trait::async_trait] -impl HostInputStream for InputStream { - fn read(&mut self, _size: usize) -> StreamResult { - loop { - { - let mut tasks = self.tasks.lock().unwrap(); - if tasks.len() > 0 { - let ret = tasks.remove(0); - let ret = serde_json::to_string(&ret).unwrap() + "\n"; - let ret = Bytes::from(ret); - - return Ok(ret); - } - } - std::thread::sleep(std::time::Duration::from_millis(100)); - } - } -} - -pub struct OutputStream { - pub tx: Sender, -} - -#[async_trait::async_trait] -impl Subscribe for OutputStream { - async fn ready(&mut self) {} -} - -#[async_trait::async_trait] -impl HostOutputStream for OutputStream { - fn write(&mut self, bytes: Bytes) -> StreamResult<()> { - let msg = String::from_utf8(bytes.to_vec()).expect("Failed to parse message"); - let msg = serde_json::from_str::(&msg).expect("Failed to parse message"); - - self.tx.send(msg).expect("Failed to send message"); - Ok(()) - } - - fn flush(&mut self) -> StreamResult<()> { - Ok(()) - } - - fn check_write(&mut self) -> StreamResult { - Ok(8192) - } -} - -pub struct HostInputStreamBox { - pub tasks: Arc>>, -} - -impl StdinStream for HostInputStreamBox { - fn stream(&self) -> Box { - Box::new(InputStream { - tasks: self.tasks.clone(), - }) - } - - fn isatty(&self) -> bool { - false - } -} - -pub struct HostOutputStreamBox { - pub tx: Sender, -} - -impl StdoutStream for HostOutputStreamBox { - fn stream(&self) -> Box { - Box::new(OutputStream { - tx: self.tx.clone(), - }) - } - - fn isatty(&self) -> bool { - false - } -}