From 98e3bc0c0c62848ba03c18c241784a88bbef374b Mon Sep 17 00:00:00 2001 From: Pekka Enberg Date: Thu, 27 Feb 2025 10:10:07 +0200 Subject: [PATCH 1/3] bindings/rust: Make library thread-safe --- bindings/rust/src/lib.rs | 47 ++++++++++++++++++++++++++++++++++------ 1 file changed, 40 insertions(+), 7 deletions(-) diff --git a/bindings/rust/src/lib.rs b/bindings/rust/src/lib.rs index 366700a9e..0e331caf6 100644 --- a/bindings/rust/src/lib.rs +++ b/bindings/rust/src/lib.rs @@ -6,12 +6,14 @@ pub use params::params_from_iter; use crate::params::*; use crate::value::*; use std::rc::Rc; -use std::sync::Arc; +use std::sync::{Arc, Mutex}; #[derive(Debug, thiserror::Error)] pub enum Error { #[error("SQL conversion failure: `{0}`")] ToSqlConversionFailure(BoxError), + #[error("Mutex lock error: {0}")] + MutexError(String), } impl From for Error { @@ -51,17 +53,33 @@ pub struct Database { inner: Arc, } +unsafe impl Send for Database {} +unsafe impl Sync for Database {} + impl Database { pub fn connect(&self) -> Result { let conn = self.inner.connect(); - Ok(Connection { inner: conn }) + Ok(Connection { + inner: Arc::new(Mutex::new(conn)), + }) } } pub struct Connection { - inner: Rc, + inner: Arc>>, } +impl Clone for Connection { + fn clone(&self) -> Self { + Self { + inner: Arc::clone(&self.inner), + } + } +} + +unsafe impl Send for Connection {} +unsafe impl Sync for Connection {} + impl Connection { pub async fn query(&self, sql: &str, params: impl IntoParams) -> Result { let mut stmt = self.prepare(sql).await?; @@ -74,17 +92,26 @@ impl Connection { } pub async fn prepare(&self, sql: &str) -> Result { - let stmt = self.inner.prepare(sql)?; + let conn = self + .inner + .lock() + .map_err(|e| Error::MutexError(e.to_string()))?; + + let stmt = conn.prepare(sql)?; + Ok(Statement { - _inner: Rc::new(stmt), + inner: Arc::new(Mutex::new(Rc::new(stmt))), }) } } pub struct Statement { - _inner: Rc, + inner: Arc>>, } +unsafe impl Send for Statement {} +unsafe impl Sync for Statement {} + impl Statement { pub async fn query(&mut self, params: impl IntoParams) -> Result { let _params = params.into_params()?; @@ -110,9 +137,12 @@ pub enum Params { pub struct Transaction {} pub struct Rows { - _inner: Rc, + inner: Arc>>, } +unsafe impl Send for Rows {} +unsafe impl Sync for Rows {} + impl Rows { pub async fn next(&mut self) -> Result> { todo!(); @@ -121,6 +151,9 @@ impl Rows { pub struct Row {} +unsafe impl Send for Row {} +unsafe impl Sync for Row {} + impl Row { pub fn get_value(&self, _index: usize) -> Result { todo!(); From 08c1dce549cf01cbaf41eff153e8bd35d7bdc145 Mon Sep 17 00:00:00 2001 From: Pekka Enberg Date: Thu, 27 Feb 2025 10:37:11 +0200 Subject: [PATCH 2/3] bindings/rust: Improve API support Add support for Statement::query() and others to wire up more of Limbo core to the Rust bindings. --- bindings/rust/src/lib.rs | 48 +++++++++++++++++++++++++++++++--------- 1 file changed, 37 insertions(+), 11 deletions(-) diff --git a/bindings/rust/src/lib.rs b/bindings/rust/src/lib.rs index 0e331caf6..9acc489c9 100644 --- a/bindings/rust/src/lib.rs +++ b/bindings/rust/src/lib.rs @@ -1,10 +1,11 @@ pub mod params; -mod value; +pub mod value; + +pub use value::Value; pub use params::params_from_iter; use crate::params::*; -use crate::value::*; use std::rc::Rc; use std::sync::{Arc, Mutex}; @@ -100,13 +101,13 @@ impl Connection { let stmt = conn.prepare(sql)?; Ok(Statement { - inner: Arc::new(Mutex::new(Rc::new(stmt))), + inner: Arc::new(Mutex::new(stmt)), }) } } pub struct Statement { - inner: Arc>>, + inner: Arc>, } unsafe impl Send for Statement {} @@ -114,8 +115,14 @@ unsafe impl Sync for Statement {} impl Statement { pub async fn query(&mut self, params: impl IntoParams) -> Result { - let _params = params.into_params()?; - todo!(); + let params = params.into_params()?; + match params { + crate::params::Params::None => {} + _ => todo!(), + } + Ok(Rows { + inner: Arc::clone(&self.inner), + }) } pub async fn execute(&mut self, params: impl IntoParams) -> Result { @@ -137,7 +144,7 @@ pub enum Params { pub struct Transaction {} pub struct Rows { - inner: Arc>>, + inner: Arc>, } unsafe impl Send for Rows {} @@ -145,17 +152,36 @@ unsafe impl Sync for Rows {} impl Rows { pub async fn next(&mut self) -> Result> { - todo!(); + let mut stmt = self + .inner + .lock() + .map_err(|e| Error::MutexError(e.to_string()))?; + + match stmt.step() { + Ok(limbo_core::StepResult::Row) => { + let row = stmt.row().unwrap(); + Ok(Some(Row { + values: row.get_values().to_vec(), + })) + } + _ => Ok(None), + } } } -pub struct Row {} +pub struct Row { + values: Vec, +} unsafe impl Send for Row {} unsafe impl Sync for Row {} impl Row { - pub fn get_value(&self, _index: usize) -> Result { - todo!(); + pub fn get_value(&self, index: usize) -> Result { + let value = &self.values[index]; + match value { + limbo_core::OwnedValue::Integer(i) => Ok(Value::Integer(*i)), + _ => todo!(), + } } } From 50f9cc449c9c6bc6bf186f79d943507d1342cb4d Mon Sep 17 00:00:00 2001 From: Pekka Enberg Date: Thu, 27 Feb 2025 10:43:58 +0200 Subject: [PATCH 3/3] bindings/rust: Fix complaints about non-Sync/Send use of Arc We probably should drop the `Rc` from `Connection` in the core, but let's paper over it for now. --- bindings/rust/src/lib.rs | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/bindings/rust/src/lib.rs b/bindings/rust/src/lib.rs index 9acc489c9..06f268f90 100644 --- a/bindings/rust/src/lib.rs +++ b/bindings/rust/src/lib.rs @@ -60,9 +60,11 @@ unsafe impl Sync for Database {} impl Database { pub fn connect(&self) -> Result { let conn = self.inner.connect(); - Ok(Connection { + #[allow(clippy::arc_with_non_send_sync)] + let connection = Connection { inner: Arc::new(Mutex::new(conn)), - }) + }; + Ok(connection) } } @@ -100,9 +102,11 @@ impl Connection { let stmt = conn.prepare(sql)?; - Ok(Statement { + #[allow(clippy::arc_with_non_send_sync)] + let statement = Statement { inner: Arc::new(Mutex::new(stmt)), - }) + }; + Ok(statement) } } @@ -120,9 +124,11 @@ impl Statement { crate::params::Params::None => {} _ => todo!(), } - Ok(Rows { + #[allow(clippy::arc_with_non_send_sync)] + let rows = Rows { inner: Arc::clone(&self.inner), - }) + }; + Ok(rows) } pub async fn execute(&mut self, params: impl IntoParams) -> Result {