commit 0c4bed11d173609ed50b0460e27c697abc75a9e8
parent 1e2c8796b7ece8d125fdceaae0c2fa1718d58e2d
Author: triesap <tyson@radroots.org>
Date: Sat, 13 Jun 2026 02:28:17 -0700
sql_core: align dependent feature maps
- Make SQL core workspace dependencies opt into features explicitly
- Remove non-portable embedded SQLite from the wasm SQL facade
- Keep replica wasm bindings on the std-backed sync surface
- Update replica and coverage metadata for the repaired feature contract
Diffstat:
14 files changed, 46 insertions(+), 671 deletions(-)
diff --git a/Cargo.lock b/Cargo.lock
@@ -678,24 +678,13 @@ dependencies = [
]
[[package]]
-name = "chacha20"
-version = "0.10.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6f8d983286843e49675a4b7a2d174efe136dc93a18d69130dd18198a6c167601"
-dependencies = [
- "cfg-if",
- "cpufeatures 0.3.0",
- "rand_core 0.10.0",
-]
-
-[[package]]
name = "chacha20poly1305"
version = "0.10.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "10cd79432192d1c0f4e1a0fef9527696cc039165d729fb41b3f4f4f354c2dc35"
dependencies = [
"aead",
- "chacha20 0.9.1",
+ "chacha20",
"cipher",
"poly1305",
"zeroize",
@@ -710,7 +699,6 @@ dependencies = [
"iana-time-zone",
"js-sys",
"num-traits",
- "serde",
"wasm-bindgen",
"windows-link",
]
@@ -1916,7 +1904,6 @@ dependencies = [
"cfg-if",
"libc",
"r-efi 6.0.0",
- "rand_core 0.10.0",
"wasip2",
"wasip3",
]
@@ -2884,7 +2871,7 @@ dependencies = [
"bip39",
"bitcoin_hashes",
"cbc",
- "chacha20 0.9.1",
+ "chacha20",
"chacha20poly1305",
"getrandom 0.2.17",
"hex",
@@ -4577,16 +4564,10 @@ dependencies = [
name = "radroots_sql_wasm_core"
version = "0.1.0-alpha.2"
dependencies = [
- "chrono",
- "js-sys",
"radroots_sql_core",
"radroots_sql_wasm_bridge",
- "rusqlite",
"serde",
"serde-wasm-bindgen",
- "serde_json",
- "thiserror 1.0.69",
- "uuid",
"wasm-bindgen",
]
@@ -4639,17 +4620,6 @@ dependencies = [
]
[[package]]
-name = "rand"
-version = "0.10.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "bc266eb313df6c5c09c1c7b1fbe2510961e5bcd3add930c1e31f7ed9da0feff8"
-dependencies = [
- "chacha20 0.10.0",
- "getrandom 0.4.2",
- "rand_core 0.10.0",
-]
-
-[[package]]
name = "rand_chacha"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -4688,12 +4658,6 @@ dependencies = [
]
[[package]]
-name = "rand_core"
-version = "0.10.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0c8d0fd677905edcbeedbf2edb6494d676f0e98d54d5cf9bda0b061cb8fb8aba"
-
-[[package]]
name = "range-set-blaze"
version = "0.1.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -7541,7 +7505,6 @@ dependencies = [
"getrandom 0.4.2",
"js-sys",
"md-5",
- "rand 0.10.0",
"sha1_smol",
"wasm-bindgen",
]
diff --git a/Cargo.toml b/Cargo.toml
@@ -94,7 +94,7 @@ radroots_simplex_smp_proto = { path = "crates/simplex_smp_proto", version = "0.1
radroots_simplex_smp_transport = { path = "crates/simplex_smp_transport", version = "0.1.0-alpha.2", default-features = false }
radroots_sql_wasm_bridge = { path = "crates/sql_wasm_bridge", version = "0.1.0-alpha.2" }
radroots_sql_wasm_core = { path = "crates/sql_wasm_core", version = "0.1.0-alpha.2", default-features = false }
-radroots_sql_core = { path = "crates/sql_core", version = "0.1.0-alpha.2" }
+radroots_sql_core = { path = "crates/sql_core", version = "0.1.0-alpha.2", default-features = false }
radroots_test_fixtures = { path = "crates/test_fixtures", version = "0.1.0-alpha.2" }
radroots_replica_db_schema = { path = "crates/replica_db_schema", version = "0.1.0-alpha.2", default-features = false }
radroots_replica_sync = { path = "crates/replica_sync", version = "0.1.0-alpha.2", default-features = false }
diff --git a/crates/replica_db/Cargo.toml b/crates/replica_db/Cargo.toml
@@ -16,10 +16,16 @@ readme = "README"
crate-type = ["rlib"]
[features]
-default = []
-web = ["radroots_sql_core/web"]
-native = ["radroots_sql_core/native"]
-embedded = ["radroots_sql_core/embedded"]
+default = ["std"]
+std = ["radroots_sql_core/std"]
+web = [
+ "std",
+ "radroots_sql_core/web",
+]
+native = [
+ "std",
+ "radroots_sql_core/native",
+]
coverage-minimal = []
[dependencies]
diff --git a/crates/replica_db/README b/crates/replica_db/README
@@ -9,8 +9,7 @@ facades and shaped query helpers for the `radroots` core libraries.
* migration, backup, restore, and export helpers for replica database state;
* typed CRUD helpers over the shared replica schema models;
* shaped query helpers for common trade, farm, and event freshness lookups;
- * feature-gated web, native, and embedded backend support through
- `radroots_sql_core`.
+ * feature-gated web and native backend support through `radroots_sql_core`.
## Copyright
diff --git a/crates/replica_db_wasm/Cargo.toml b/crates/replica_db_wasm/Cargo.toml
@@ -22,7 +22,7 @@ radroots_sql_wasm_core = { workspace = true, default-features = false, features
] }
radroots_replica_db = { workspace = true }
radroots_replica_db_schema = { workspace = true }
-radroots_replica_sync = { workspace = true }
+radroots_replica_sync = { workspace = true, features = ["std"] }
js-sys = { workspace = true }
serde = { workspace = true, features = ["derive"] }
serde_json = { workspace = true }
diff --git a/crates/replica_db_wasm/src/utils.rs b/crates/replica_db_wasm/src/utils.rs
@@ -1,20 +1,8 @@
use serde::Serialize;
-use serde::de::DeserializeOwned;
use wasm_bindgen::prelude::*;
use radroots_sql_core::SqlError;
-pub fn parse_optional_json<T>(json: &str) -> Result<Option<T>, serde_json::Error>
-where
- T: DeserializeOwned,
-{
- if json.trim().is_empty() {
- return Ok(None);
- }
- let value: Option<T> = serde_json::from_str(json)?;
- Ok(value)
-}
-
pub fn value_to_js<T>(value: T) -> Result<JsValue, JsValue>
where
T: Serialize,
diff --git a/crates/replica_sync/Cargo.toml b/crates/replica_sync/Cargo.toml
@@ -20,6 +20,7 @@ default = ["std"]
std = [
"radroots_events/std",
"radroots_events_codec/std",
+ "radroots_sql_core/std",
"dep:base64",
"dep:uuid",
]
diff --git a/crates/replica_sync_wasm/Cargo.toml b/crates/replica_sync_wasm/Cargo.toml
@@ -24,7 +24,7 @@ radroots_sql_core = { workspace = true, features = ["bridge"] }
radroots_sql_wasm_core = { workspace = true, default-features = false, features = [
"bridge",
] }
-radroots_replica_sync = { workspace = true }
+radroots_replica_sync = { workspace = true, features = ["std"] }
serde = { workspace = true, features = ["derive"] }
serde_json = { workspace = true }
serde-wasm-bindgen = { workspace = true }
diff --git a/crates/sql_core/Cargo.toml b/crates/sql_core/Cargo.toml
@@ -23,7 +23,10 @@ std = [
"serde/std",
"serde_json/std",
]
-web = ["std"]
+web = [
+ "std",
+ "uuid/js",
+]
bridge = [
"std",
"web",
diff --git a/crates/sql_wasm_core/Cargo.toml b/crates/sql_wasm_core/Cargo.toml
@@ -17,21 +17,17 @@ crate-type = ["cdylib", "rlib"]
[features]
default = ["bridge"]
-bridge = ["dep:radroots_sql_wasm_bridge"]
-embedded = ["dep:rusqlite", "radroots_sql_core/native"]
+bridge = [
+ "radroots_sql_core/bridge",
+ "dep:radroots_sql_wasm_bridge",
+ "dep:serde",
+ "dep:serde-wasm-bindgen",
+ "dep:wasm-bindgen",
+]
[dependencies]
radroots_sql_core = { workspace = true }
radroots_sql_wasm_bridge = { workspace = true, optional = true }
-rusqlite = { workspace = true, features = [
- "bundled",
- "serialize",
-], optional = true }
-chrono = { workspace = true, features = ["serde"] }
-serde = { workspace = true, features = ["derive"] }
-serde_json = { workspace = true }
-serde-wasm-bindgen = { workspace = true }
-thiserror = { workspace = true }
-wasm-bindgen = { workspace = true }
-js-sys = { workspace = true }
-uuid = { workspace = true, features = ["v4", "fast-rng", "js"] }
+serde = { workspace = true, optional = true }
+serde-wasm-bindgen = { workspace = true, optional = true }
+wasm-bindgen = { workspace = true, optional = true }
diff --git a/crates/sql_wasm_core/README b/crates/sql_wasm_core/README
@@ -7,9 +7,7 @@ runtime facade for the `radroots` core libraries.
* shared wasm entry points for SQL execution, query, export, and transaction
control;
- * compile-time selection between the host bridge layer and an embedded SQL
- engine;
- * an optional singleton embedded engine for self-contained wasm runtimes;
+ * host bridge integration for browser and worker SQL runtimes;
* small JSON parsing and JavaScript error helpers for wasm callers.
## Copyright
diff --git a/crates/sql_wasm_core/src/embedded.rs b/crates/sql_wasm_core/src/embedded.rs
@@ -1,493 +0,0 @@
-#![forbid(unsafe_code)]
-
-use std::sync::Mutex;
-
-use radroots_sql_core::sqlite_util;
-use radroots_sql_core::{ExecOutcome, SqlError, SqlExecutor};
-use rusqlite::{Connection, MAIN_DB, params_from_iter};
-use serde_json::Value;
-
-const SAVEPOINT_BEGIN: &str = "savepoint radroots_schema_tx";
-const SAVEPOINT_RELEASE: &str = "release savepoint radroots_schema_tx";
-const SAVEPOINT_ROLLBACK: &str = "rollback to savepoint radroots_schema_tx";
-
-#[cfg(test)]
-mod failpoints {
- use std::sync::atomic::{AtomicUsize, Ordering};
-
- #[derive(Clone, Copy)]
- pub enum Point {
- Open = 1 << 0,
- BeginExecute = 1 << 1,
- ReleaseExecute = 1 << 2,
- ExportSerialize = 1 << 3,
- EncodeRows = 1 << 4,
- RowToJson = 1 << 5,
- }
-
- static FLAGS: AtomicUsize = AtomicUsize::new(0);
-
- pub fn set(point: Point) {
- FLAGS.fetch_or(point as usize, Ordering::SeqCst);
- }
-
- pub fn take(point: Point) -> bool {
- let mask = point as usize;
- let prev = FLAGS.fetch_and(!mask, Ordering::SeqCst);
- (prev & mask) != 0
- }
-
- pub fn clear() {
- FLAGS.store(0, Ordering::SeqCst);
- }
-}
-
-#[cfg(test)]
-fn forced_error() -> rusqlite::Error {
- rusqlite::Error::InvalidParameterName("forced".to_string())
-}
-
-fn open_in_memory_with_failpoint() -> Result<Connection, rusqlite::Error> {
- #[cfg(test)]
- if failpoints::take(failpoints::Point::Open) {
- return Err(forced_error());
- }
- Connection::open_in_memory()
-}
-
-fn execute_begin_savepoint(conn: &Connection) -> Result<(), SqlError> {
- #[cfg(test)]
- let result = if failpoints::take(failpoints::Point::BeginExecute) {
- Err(forced_error())
- } else {
- conn.execute(SAVEPOINT_BEGIN, [])
- };
- #[cfg(not(test))]
- let result = conn.execute(SAVEPOINT_BEGIN, []);
- result.map(|_| ()).map_err(map_rusqlite)
-}
-
-fn execute_release_savepoint(conn: &Connection) -> Result<(), SqlError> {
- #[cfg(test)]
- let result = if failpoints::take(failpoints::Point::ReleaseExecute) {
- Err(forced_error())
- } else {
- conn.execute(SAVEPOINT_RELEASE, [])
- };
- #[cfg(not(test))]
- let result = conn.execute(SAVEPOINT_RELEASE, []);
- result.map(|_| ()).map_err(map_rusqlite)
-}
-
-fn serialize_main(conn: &Connection) -> Result<Vec<u8>, rusqlite::Error> {
- #[cfg(test)]
- if failpoints::take(failpoints::Point::ExportSerialize) {
- return Err(forced_error());
- }
- conn.serialize(MAIN_DB).map(|data| data.to_vec())
-}
-
-fn map_row(row: &rusqlite::Row<'_>) -> rusqlite::Result<Value> {
- #[cfg(test)]
- if failpoints::take(failpoints::Point::RowToJson) {
- return Err(forced_error());
- }
- sqlite_util::row_to_json(row)
-}
-
-fn encode_rows(rows: &[Value]) -> Result<String, SqlError> {
- #[cfg(test)]
- if failpoints::take(failpoints::Point::EncodeRows) {
- return serde_json::to_string(&FailSerialize).map_err(SqlError::from);
- }
- serde_json::to_string(rows).map_err(SqlError::from)
-}
-
-#[cfg(test)]
-struct FailSerialize;
-
-#[cfg(test)]
-impl serde::Serialize for FailSerialize {
- fn serialize<S>(&self, _serializer: S) -> Result<S::Ok, S::Error>
- where
- S: serde::Serializer,
- {
- Err(serde::ser::Error::custom("forced"))
- }
-}
-
-#[derive(Debug)]
-pub struct EmbeddedSqlEngine {
- conn: Mutex<Connection>,
-}
-
-impl EmbeddedSqlEngine {
- pub fn new() -> Result<Self, SqlError> {
- let conn = open_in_memory_with_failpoint().map_err(map_rusqlite)?;
- Ok(Self {
- conn: Mutex::new(conn),
- })
- }
-
- pub fn exec(&self, sql: &str, params_json: &str) -> Result<ExecOutcome, SqlError> {
- let binds = sqlite_util::parse_params(params_json)?;
- let conn = self.conn.lock().map_err(|_| SqlError::Internal)?;
- if binds.is_empty() {
- let total_changes_before = conn.total_changes();
- conn.execute_batch(sql).map_err(map_rusqlite)?;
- let total_changes_after = conn.total_changes();
- let last_insert_id = conn.last_insert_rowid();
- return Ok(ExecOutcome {
- changes: (total_changes_after - total_changes_before) as i64,
- last_insert_id,
- });
- }
- let changes = conn
- .execute(sql, params_from_iter(binds.into_iter()))
- .map_err(map_rusqlite)?;
- let last_insert_id = conn.last_insert_rowid();
- Ok(ExecOutcome {
- changes: changes as i64,
- last_insert_id,
- })
- }
-
- pub fn query_rows(&self, sql: &str, params_json: &str) -> Result<Vec<Value>, SqlError> {
- let binds = sqlite_util::parse_params(params_json)?;
- let rows = {
- let conn = self.conn.lock().map_err(|_| SqlError::Internal)?;
- let mut stmt = conn.prepare(sql).map_err(map_rusqlite)?;
- let params = params_from_iter(binds.into_iter());
- let mapped = stmt.query_map(params, map_row)?;
- mapped
- .collect::<Result<Vec<_>, _>>()
- .map_err(map_rusqlite)?
- };
- Ok(rows)
- }
-
- pub fn query_raw(&self, sql: &str, params_json: &str) -> Result<String, SqlError> {
- let rows = self.query_rows(sql, params_json)?;
- encode_rows(&rows)
- }
-
- pub fn begin_tx(&self) -> Result<(), SqlError> {
- let conn = self.conn.lock().map_err(|_| SqlError::Internal)?;
- execute_begin_savepoint(&conn)
- }
-
- pub fn commit_tx(&self) -> Result<(), SqlError> {
- let conn = self.conn.lock().map_err(|_| SqlError::Internal)?;
- execute_release_savepoint(&conn)
- }
-
- pub fn rollback_tx(&self) -> Result<(), SqlError> {
- let conn = self.conn.lock().map_err(|_| SqlError::Internal)?;
- conn.execute(SAVEPOINT_ROLLBACK, []).map_err(map_rusqlite)?;
- execute_release_savepoint(&conn)
- }
-
- pub fn export_bytes(&self) -> Result<Vec<u8>, SqlError> {
- let conn = self.conn.lock().map_err(|_| SqlError::Internal)?;
- let data = serialize_main(&conn).map_err(map_rusqlite)?;
- Ok(data)
- }
-}
-
-impl SqlExecutor for EmbeddedSqlEngine {
- fn exec(&self, sql: &str, params_json: &str) -> Result<ExecOutcome, SqlError> {
- EmbeddedSqlEngine::exec(self, sql, params_json)
- }
-
- fn query_raw(&self, sql: &str, params_json: &str) -> Result<String, SqlError> {
- EmbeddedSqlEngine::query_raw(self, sql, params_json)
- }
-
- fn begin(&self) -> Result<(), SqlError> {
- EmbeddedSqlEngine::begin_tx(self)
- }
-
- fn commit(&self) -> Result<(), SqlError> {
- EmbeddedSqlEngine::commit_tx(self)
- }
-
- fn rollback(&self) -> Result<(), SqlError> {
- EmbeddedSqlEngine::rollback_tx(self)
- }
-}
-
-fn map_rusqlite(err: rusqlite::Error) -> SqlError {
- SqlError::InvalidQuery(err.to_string())
-}
-
-pub fn coverage_branch_probe(input: bool) -> &'static str {
- if input { "sql" } else { "sql" }
-}
-
-#[cfg(all(test, feature = "embedded"))]
-mod tests {
- use super::{EmbeddedSqlEngine, coverage_branch_probe, failpoints};
- use radroots_sql_core::SqlExecutor;
-
- const CREATE_TABLE_SQL: &str = "CREATE TABLE test_items (id INTEGER PRIMARY KEY, name TEXT)";
-
- fn poison_engine(engine: &EmbeddedSqlEngine) {
- let _ = std::panic::catch_unwind(|| {
- let _guard = engine.conn.lock().unwrap();
- panic!("poison");
- });
- }
-
- #[test]
- fn open_in_memory_failpoint_surfaces_error() {
- failpoints::clear();
- failpoints::set(failpoints::Point::Open);
- let err = EmbeddedSqlEngine::new().unwrap_err();
- assert_eq!(err.code(), "ERR_INVALID_QUERY");
- }
-
- #[test]
- fn exec_query_roundtrip() {
- let engine = EmbeddedSqlEngine::new().unwrap();
- engine.exec(CREATE_TABLE_SQL, "[]").unwrap();
- let outcome = engine
- .exec("INSERT INTO test_items (name) VALUES (?)", "[\"rad\"]")
- .unwrap();
- assert_eq!(outcome.changes, 1);
- let rows = engine
- .query_rows("SELECT name FROM test_items WHERE id = ?", "[1]")
- .unwrap();
- let name = rows
- .first()
- .and_then(|row| row.get("name"))
- .and_then(|value| value.as_str())
- .expect("missing name");
- assert_eq!(name, "rad");
- }
-
- #[test]
- fn rollback_discards_changes() {
- let engine = EmbeddedSqlEngine::new().unwrap();
- engine.exec(CREATE_TABLE_SQL, "[]").unwrap();
- engine.begin_tx().unwrap();
- engine
- .exec("INSERT INTO test_items (name) VALUES (?)", "[\"rad\"]")
- .unwrap();
- engine.rollback_tx().unwrap();
- let rows = engine
- .query_rows("SELECT name FROM test_items", "[]")
- .unwrap();
- assert!(rows.is_empty());
- }
-
- #[test]
- fn exec_runs_multi_statement_batches_without_params() {
- let engine = EmbeddedSqlEngine::new().unwrap();
-
- let outcome = engine
- .exec(
- "CREATE TABLE demo (id INTEGER PRIMARY KEY, name TEXT NOT NULL);\
-\nCREATE UNIQUE INDEX demo_name_idx ON demo(name);",
- "[]",
- )
- .unwrap();
- assert_eq!(outcome.changes, 0);
-
- let insert = engine
- .exec("INSERT INTO demo (name) VALUES ('alpha')", "[]")
- .unwrap();
- assert_eq!(insert.changes, 1);
-
- let rows = engine
- .query_rows(
- "SELECT name FROM sqlite_master WHERE type = 'index' AND name = 'demo_name_idx'",
- "[]",
- )
- .unwrap();
- assert_eq!(rows.len(), 1);
- }
-
- #[test]
- fn exec_empty_bind_batch_surfaces_invalid_query() {
- let engine = EmbeddedSqlEngine::new().unwrap();
- let err = engine.exec("CREATE TABLE broken (", "[]").unwrap_err();
- assert_eq!(err.code(), "ERR_INVALID_QUERY");
- }
-
- #[test]
- fn export_bytes_non_empty() {
- let engine = EmbeddedSqlEngine::new().unwrap();
- engine.exec(CREATE_TABLE_SQL, "[]").unwrap();
- engine
- .exec("INSERT INTO test_items (name) VALUES (?)", "[\"rad\"]")
- .unwrap();
- let bytes = engine.export_bytes().unwrap();
- assert!(!bytes.is_empty());
- }
-
- #[test]
- fn query_raw_commit_and_trait_executor_paths() {
- let engine = EmbeddedSqlEngine::new().unwrap();
- engine.exec(CREATE_TABLE_SQL, "[]").unwrap();
- engine.begin_tx().unwrap();
- engine
- .exec("INSERT INTO test_items (name) VALUES (?)", "[\"rad\"]")
- .unwrap();
- engine.commit_tx().unwrap();
- let rows = engine
- .query_raw("SELECT name FROM test_items ORDER BY id ASC", "[]")
- .unwrap();
- assert!(rows.contains("rad"));
-
- let executor: &dyn SqlExecutor = &engine;
- executor.begin().unwrap();
- let _ = executor
- .exec("INSERT INTO test_items (name) VALUES (?)", "[\"trait\"]")
- .unwrap();
- executor.rollback().unwrap();
- let rows_after = executor
- .query_raw("SELECT name FROM test_items ORDER BY id ASC", "[]")
- .unwrap();
- assert!(rows_after.contains("rad"));
- assert!(!rows_after.contains("trait"));
- }
-
- #[test]
- fn invalid_sql_paths_surface_invalid_query() {
- let engine = EmbeddedSqlEngine::new().unwrap();
- let err_exec = engine
- .exec("INSERT INTO missing (name) VALUES (?)", "[\"rad\"]")
- .unwrap_err();
- assert_eq!(err_exec.code(), "ERR_INVALID_QUERY");
-
- let err_rows = engine.query_rows("SELECT name FROM missing", "[]");
- assert_eq!(err_rows.unwrap_err().code(), "ERR_INVALID_QUERY");
-
- let err_raw = engine.query_raw("SELECT name FROM missing", "[]");
- assert_eq!(err_raw.unwrap_err().code(), "ERR_INVALID_QUERY");
-
- let err_commit = engine.commit_tx().unwrap_err();
- assert_eq!(err_commit.code(), "ERR_INVALID_QUERY");
-
- let err_rollback = engine.rollback_tx().unwrap_err();
- assert_eq!(err_rollback.code(), "ERR_INVALID_QUERY");
-
- let executor: &dyn SqlExecutor = &engine;
- let err_trait_commit = executor.commit().unwrap_err();
- assert_eq!(err_trait_commit.code(), "ERR_INVALID_QUERY");
- }
-
- #[test]
- fn invalid_params_surface_errors() {
- let engine = EmbeddedSqlEngine::new().unwrap();
- let err_exec = engine.exec(CREATE_TABLE_SQL, "{}").unwrap_err();
- assert_eq!(err_exec.code(), "ERR_SERIALIZATION");
-
- let err_rows = engine.query_rows("SELECT 1", "{}").unwrap_err();
- assert_eq!(err_rows.code(), "ERR_SERIALIZATION");
-
- let err_raw = engine.query_raw("SELECT 1", "{}").unwrap_err();
- assert_eq!(err_raw.code(), "ERR_SERIALIZATION");
- }
-
- #[test]
- fn query_rows_surfaces_prepare_and_bind_errors() {
- let engine = EmbeddedSqlEngine::new().unwrap();
- let err_prepare = engine
- .query_rows("SELEC name FROM test_items", "[]")
- .unwrap_err();
- assert_eq!(err_prepare.code(), "ERR_INVALID_QUERY");
-
- engine.exec(CREATE_TABLE_SQL, "[]").unwrap();
- let err_bind = engine
- .query_rows("SELECT name FROM test_items WHERE id = ?", "[]")
- .unwrap_err();
- assert_eq!(err_bind.code(), "ERR_INVALID_QUERY");
- }
-
- #[test]
- fn query_rows_collect_error_is_reported() {
- let engine = EmbeddedSqlEngine::new().unwrap();
- engine.exec(CREATE_TABLE_SQL, "[]").unwrap();
- engine
- .exec("INSERT INTO test_items (name) VALUES (?)", "[\"rad\"]")
- .unwrap();
- failpoints::clear();
- failpoints::set(failpoints::Point::RowToJson);
- let err = engine
- .query_rows("SELECT name FROM test_items", "[]")
- .unwrap_err();
- assert_eq!(err.code(), "ERR_INVALID_QUERY");
- }
-
- #[test]
- fn query_raw_serialization_error_is_reported() {
- let engine = EmbeddedSqlEngine::new().unwrap();
- engine.exec(CREATE_TABLE_SQL, "[]").unwrap();
- engine
- .exec("INSERT INTO test_items (name) VALUES (?)", "[\"rad\"]")
- .unwrap();
- failpoints::clear();
- failpoints::set(failpoints::Point::EncodeRows);
- let err = engine
- .query_raw("SELECT name FROM test_items", "[]")
- .unwrap_err();
- assert_eq!(err.code(), "ERR_SERIALIZATION");
- }
-
- #[test]
- fn begin_tx_failpoint_surfaces_error() {
- let engine = EmbeddedSqlEngine::new().unwrap();
- failpoints::clear();
- failpoints::set(failpoints::Point::BeginExecute);
- let err = engine.begin_tx().unwrap_err();
- assert_eq!(err.code(), "ERR_INVALID_QUERY");
- }
-
- #[test]
- fn rollback_release_failpoint_surfaces_error() {
- let engine = EmbeddedSqlEngine::new().unwrap();
- engine.exec(CREATE_TABLE_SQL, "[]").unwrap();
- engine.begin_tx().unwrap();
- failpoints::clear();
- failpoints::set(failpoints::Point::ReleaseExecute);
- let err = engine.rollback_tx().unwrap_err();
- assert_eq!(err.code(), "ERR_INVALID_QUERY");
- }
-
- #[test]
- fn export_bytes_failpoint_surfaces_error() {
- let engine = EmbeddedSqlEngine::new().unwrap();
- engine.exec(CREATE_TABLE_SQL, "[]").unwrap();
- failpoints::clear();
- failpoints::set(failpoints::Point::ExportSerialize);
- let err = engine.export_bytes().unwrap_err();
- assert_eq!(err.code(), "ERR_INVALID_QUERY");
- }
-
- #[test]
- fn lock_errors_surface_internal() {
- let engine = EmbeddedSqlEngine::new().unwrap();
- poison_engine(&engine);
- let err_exec = engine.exec(CREATE_TABLE_SQL, "[]");
- assert_eq!(err_exec.unwrap_err().code(), "ERR_INTERNAL");
- let err_rows = engine.query_rows("SELECT 1", "[]");
- assert_eq!(err_rows.unwrap_err().code(), "ERR_INTERNAL");
- let err_raw = engine.query_raw("SELECT 1", "[]");
- assert_eq!(err_raw.unwrap_err().code(), "ERR_INTERNAL");
- let err_begin = engine.begin_tx();
- assert_eq!(err_begin.unwrap_err().code(), "ERR_INTERNAL");
- let err_commit = engine.commit_tx();
- assert_eq!(err_commit.unwrap_err().code(), "ERR_INTERNAL");
- let err_rollback = engine.rollback_tx();
- assert_eq!(err_rollback.unwrap_err().code(), "ERR_INTERNAL");
- let err_export = engine.export_bytes();
- assert_eq!(err_export.unwrap_err().code(), "ERR_INTERNAL");
- }
-
- #[test]
- fn coverage_branch_probe_hits_both_paths() {
- assert_eq!(coverage_branch_probe(true), "sql");
- assert_eq!(coverage_branch_probe(false), "sql");
- }
-}
diff --git a/crates/sql_wasm_core/src/lib.rs b/crates/sql_wasm_core/src/lib.rs
@@ -1,33 +1,23 @@
-#[cfg(target_arch = "wasm32")]
+#[cfg(all(feature = "bridge", target_arch = "wasm32"))]
use radroots_sql_core::error::SqlError;
-#[cfg(target_arch = "wasm32")]
+#[cfg(all(feature = "bridge", target_arch = "wasm32"))]
use radroots_sql_core::utils;
-#[cfg(target_arch = "wasm32")]
+#[cfg(all(feature = "bridge", target_arch = "wasm32"))]
use serde::de::DeserializeOwned;
-#[cfg(all(feature = "embedded", target_arch = "wasm32"))]
-use js_sys::Uint8Array;
-#[cfg(all(feature = "embedded", target_arch = "wasm32"))]
-use std::sync::OnceLock;
-
-#[cfg(target_arch = "wasm32")]
+#[cfg(all(feature = "bridge", target_arch = "wasm32"))]
use wasm_bindgen::JsValue;
-#[cfg(target_arch = "wasm32")]
+#[cfg(all(feature = "bridge", target_arch = "wasm32"))]
use wasm_bindgen::prelude::*;
-#[cfg(feature = "embedded")]
-mod embedded;
-#[cfg(feature = "embedded")]
-pub use embedded::EmbeddedSqlEngine;
-
-#[cfg(target_arch = "wasm32")]
+#[cfg(all(feature = "bridge", target_arch = "wasm32"))]
pub fn parse_json<T: DeserializeOwned>(s: &str) -> Result<T, SqlError> {
utils::parse_json(s)
}
-#[cfg(target_arch = "wasm32")]
+#[cfg(all(feature = "bridge", target_arch = "wasm32"))]
pub fn err_js(err: SqlError) -> JsValue {
let value = err.to_json();
match serde_wasm_bindgen::to_value(&value) {
@@ -36,112 +26,36 @@ pub fn err_js(err: SqlError) -> JsValue {
}
}
-#[cfg(all(feature = "embedded", target_arch = "wasm32"))]
-pub fn embedded_engine() -> Result<&'static EmbeddedSqlEngine, SqlError> {
- static ENGINE: OnceLock<EmbeddedSqlEngine> = OnceLock::new();
- if let Some(engine) = ENGINE.get() {
- return Ok(engine);
- }
- let engine = EmbeddedSqlEngine::new()?;
- let _ = ENGINE.set(engine);
- ENGINE.get().ok_or(SqlError::Internal)
-}
-
-#[cfg(all(feature = "embedded", target_arch = "wasm32"))]
-#[wasm_bindgen(js_name = exec_sql)]
-pub fn exec_sql(sql: &str, params_json: &str) -> JsValue {
- let outcome = match embedded_engine().and_then(|engine| engine.exec(sql, params_json)) {
- Ok(outcome) => outcome,
- Err(err) => return err_js(err),
- };
- let payload = serde_json::json!({
- "changes": outcome.changes,
- "last_insert_id": outcome.last_insert_id,
- "lastInsertRowid": outcome.last_insert_id,
- });
- match serde_wasm_bindgen::to_value(&payload) {
- Ok(value) => value,
- Err(err) => err_js(SqlError::SerializationError(err.to_string())),
- }
-}
-
-#[cfg(all(feature = "embedded", target_arch = "wasm32"))]
-#[wasm_bindgen(js_name = query_sql)]
-pub fn query_sql(sql: &str, params_json: &str) -> JsValue {
- let rows = match embedded_engine().and_then(|engine| engine.query_rows(sql, params_json)) {
- Ok(rows) => rows,
- Err(err) => return err_js(err),
- };
- match serde_wasm_bindgen::to_value(&rows) {
- Ok(value) => value,
- Err(err) => err_js(SqlError::SerializationError(err.to_string())),
- }
-}
-
-#[cfg(all(feature = "embedded", target_arch = "wasm32"))]
-pub fn export_bytes() -> JsValue {
- let bytes = match embedded_engine().and_then(|engine| engine.export_bytes()) {
- Ok(bytes) => bytes,
- Err(err) => return err_js(err),
- };
- let array = Uint8Array::from(bytes.as_slice());
- JsValue::from(array)
-}
-
-#[cfg(all(feature = "embedded", target_arch = "wasm32"))]
-#[wasm_bindgen(js_name = begin_tx)]
-pub fn begin_tx() {
- if let Ok(engine) = embedded_engine() {
- let _ = engine.begin_tx();
- }
-}
-
-#[cfg(all(feature = "embedded", target_arch = "wasm32"))]
-#[wasm_bindgen(js_name = commit_tx)]
-pub fn commit_tx() {
- if let Ok(engine) = embedded_engine() {
- let _ = engine.commit_tx();
- }
-}
-
-#[cfg(all(feature = "embedded", target_arch = "wasm32"))]
-#[wasm_bindgen(js_name = rollback_tx)]
-pub fn rollback_tx() {
- if let Ok(engine) = embedded_engine() {
- let _ = engine.rollback_tx();
- }
-}
-
-#[cfg(all(feature = "bridge", not(feature = "embedded"), target_arch = "wasm32"))]
+#[cfg(all(feature = "bridge", target_arch = "wasm32"))]
#[wasm_bindgen(js_name = exec_sql)]
pub fn exec_sql(sql: &str, params_json: &str) -> JsValue {
radroots_sql_wasm_bridge::exec(sql, params_json)
}
-#[cfg(all(feature = "bridge", not(feature = "embedded"), target_arch = "wasm32"))]
+#[cfg(all(feature = "bridge", target_arch = "wasm32"))]
#[wasm_bindgen(js_name = query_sql)]
pub fn query_sql(sql: &str, params_json: &str) -> JsValue {
radroots_sql_wasm_bridge::query(sql, params_json)
}
-#[cfg(all(feature = "bridge", not(feature = "embedded"), target_arch = "wasm32"))]
+#[cfg(all(feature = "bridge", target_arch = "wasm32"))]
pub fn export_bytes() -> JsValue {
radroots_sql_wasm_bridge::export_bytes()
}
-#[cfg(all(feature = "bridge", not(feature = "embedded"), target_arch = "wasm32"))]
+#[cfg(all(feature = "bridge", target_arch = "wasm32"))]
#[wasm_bindgen(js_name = begin_tx)]
pub fn begin_tx() {
radroots_sql_wasm_bridge::begin_tx()
}
-#[cfg(all(feature = "bridge", not(feature = "embedded"), target_arch = "wasm32"))]
+#[cfg(all(feature = "bridge", target_arch = "wasm32"))]
#[wasm_bindgen(js_name = commit_tx)]
pub fn commit_tx() {
radroots_sql_wasm_bridge::commit_tx()
}
-#[cfg(all(feature = "bridge", not(feature = "embedded"), target_arch = "wasm32"))]
+#[cfg(all(feature = "bridge", target_arch = "wasm32"))]
#[wasm_bindgen(js_name = rollback_tx)]
pub fn rollback_tx() {
radroots_sql_wasm_bridge::rollback_tx()
diff --git a/policy/coverage/profiles.toml b/policy/coverage/profiles.toml
@@ -20,5 +20,5 @@ test_threads = 1
[profiles.crates."radroots_sql_wasm_core"]
no_default_features = true
-features = ["embedded"]
+features = ["bridge"]
test_threads = 1