This is an automated email from the ASF dual-hosted git repository.

lidavidm pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/arrow-adbc.git


The following commit(s) were added to refs/heads/main by this push:
     new 8cc3971a6 fix(rust/ffi): avoid undefined behavior in exported drivers 
(#4871)
8cc3971a6 is described below

commit 8cc3971a682a31ae63a406b092e94aef2f805ccc
Author: David Li <[email protected]>
AuthorDate: Sun Oct 11 16:36:16 2026 -0700

    fix(rust/ffi): avoid undefined behavior in exported drivers (#4871)
    
    - Use & instead of &mut to avoid violating borrowing rules
    - Use Mutex to ensure even misbehaved clients don't violate safety
    - Use Mutex to also provide interior mutability
    - Some minor refactoring
    
    Depends on #4861.
    
    Assisted-by: GPT-6 Astra
---
 .github/workflows/rust.yml                         |   2 +-
 rust/driver_manager/tests/cancellation.rs          |  38 +--
 rust/driver_manager/tests/common/dummy.rs          | 307 +++++++++++++++++++++
 rust/driver_manager/tests/common/mod.rs            |  36 +++
 rust/driver_manager/tests/exporter_cancellation.rs | 107 +++++++
 rust/ffi/src/driver_exporter.rs                    | 272 +++++++++++-------
 6 files changed, 620 insertions(+), 142 deletions(-)

diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml
index 66fd8c4f4..5e17b06ff 100644
--- a/.github/workflows/rust.yml
+++ b/.github/workflows/rust.yml
@@ -297,4 +297,4 @@ jobs:
       - name: adbc_driver_manager (miri)
         working-directory: rust
         run: |
-          cargo +nightly miri test -p adbc_driver_manager --test cancellation
+          cargo +nightly miri test -p adbc_driver_manager --test cancellation 
--test exporter_cancellation
diff --git a/rust/driver_manager/tests/cancellation.rs 
b/rust/driver_manager/tests/cancellation.rs
index f9d260bf4..959bc38a3 100644
--- a/rust/driver_manager/tests/cancellation.rs
+++ b/rust/driver_manager/tests/cancellation.rs
@@ -35,6 +35,10 @@ use adbc_ffi::{
 };
 use arrow_array::ffi_stream::FFI_ArrowArrayStream;
 
+mod common;
+
+use common::AtomicFlag;
+
 const TIMEOUT: Duration = Duration::from_secs(2);
 const CALLBACK_TIMEOUT: Duration = Duration::from_secs(10);
 
@@ -42,40 +46,6 @@ const CALLBACK_TIMEOUT: Duration = Duration::from_secs(10);
 // Simulated driver implementation
 // ------------------------------------------------------------
 
-struct AtomicFlag {
-    mu: std::sync::Mutex<bool>,
-    cv: std::sync::Condvar,
-}
-
-impl AtomicFlag {
-    fn new(value: bool) -> Self {
-        Self {
-            mu: std::sync::Mutex::new(value),
-            cv: std::sync::Condvar::new(),
-        }
-    }
-
-    fn clear(&self) {
-        let mut guard = self.mu.lock().unwrap();
-        *guard = false;
-        self.cv.notify_all();
-    }
-
-    fn set(&self) {
-        let mut guard = self.mu.lock().unwrap();
-        *guard = true;
-        self.cv.notify_all();
-    }
-
-    fn wait(&self, timeout: Duration) -> bool {
-        let (guard, _) = self
-            .cv
-            .wait_timeout_while(self.mu.lock().unwrap(), timeout, |flag| 
!*flag)
-            .unwrap();
-        *guard
-    }
-}
-
 // Track what's been done with the driver so we can verify invariants later.
 struct DriverObjectState {
     // count of non-cancel operations
diff --git a/rust/driver_manager/tests/common/dummy.rs 
b/rust/driver_manager/tests/common/dummy.rs
new file mode 100644
index 000000000..acac42995
--- /dev/null
+++ b/rust/driver_manager/tests/common/dummy.rs
@@ -0,0 +1,307 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+use adbc_core::error::{Error, Result, Status};
+use adbc_core::options::{
+    InfoCode, ObjectDepth, OptionConnection, OptionDatabase, OptionStatement, 
OptionValue,
+};
+use adbc_core::{Connection, Database, Driver, Optionable, Statement};
+use arrow_array::{RecordBatch, RecordBatchReader};
+
+use super::AtomicFlag;
+
+// hackery for test
+pub static IN_COMMIT: std::sync::LazyLock<AtomicFlag> =
+    std::sync::LazyLock::new(|| AtomicFlag::new(false));
+
+#[derive(Default)]
+pub struct DummyDriver {}
+
+impl Driver for DummyDriver {
+    type DatabaseType = DummyDatabase;
+
+    fn new_database(&mut self) -> Result<Self::DatabaseType> {
+        Ok(Self::DatabaseType::default())
+    }
+
+    fn new_database_with_opts(
+        &mut self,
+        opts: impl IntoIterator<Item = (<Self::DatabaseType as 
Optionable>::Option, OptionValue)>,
+    ) -> Result<Self::DatabaseType> {
+        let mut database = Self::DatabaseType::default();
+        for (key, value) in opts {
+            database.set_option(key, value)?;
+        }
+        Ok(database)
+    }
+}
+
+#[derive(Default)]
+pub struct DummyDatabase {}
+
+impl Optionable for DummyDatabase {
+    type Option = OptionDatabase;
+
+    fn set_option(&mut self, _key: Self::Option, _value: OptionValue) -> 
Result<()> {
+        Ok(())
+    }
+
+    fn get_option_bytes(&self, _key: Self::Option) -> Result<Vec<u8>> {
+        todo!()
+    }
+
+    fn get_option_double(&self, _key: Self::Option) -> Result<f64> {
+        todo!()
+    }
+
+    fn get_option_int(&self, _key: Self::Option) -> Result<i64> {
+        todo!()
+    }
+
+    fn get_option_string(&self, _key: Self::Option) -> Result<String> {
+        todo!()
+    }
+}
+
+impl Database for DummyDatabase {
+    type ConnectionType = DummyConnection;
+
+    fn new_connection(&self) -> Result<Self::ConnectionType> {
+        Ok(Self::ConnectionType {
+            flag: std::sync::Arc::new(AtomicFlag::new(false)),
+        })
+    }
+
+    fn new_connection_with_opts(
+        &self,
+        opts: impl IntoIterator<Item = (<Self::ConnectionType as 
Optionable>::Option, OptionValue)>,
+    ) -> Result<Self::ConnectionType> {
+        let mut connection = Self::ConnectionType {
+            flag: std::sync::Arc::new(AtomicFlag::new(false)),
+        };
+        for (key, value) in opts {
+            connection.set_option(key, value)?;
+        }
+        Ok(connection)
+    }
+}
+
+pub struct DummyConnection {
+    flag: std::sync::Arc<AtomicFlag>,
+}
+
+impl Optionable for DummyConnection {
+    type Option = OptionConnection;
+
+    fn set_option(&mut self, _key: Self::Option, _value: OptionValue) -> 
Result<()> {
+        Ok(())
+    }
+
+    fn get_option_bytes(&self, _key: Self::Option) -> Result<Vec<u8>> {
+        todo!()
+    }
+
+    fn get_option_double(&self, _key: Self::Option) -> Result<f64> {
+        todo!()
+    }
+
+    fn get_option_int(&self, _key: Self::Option) -> Result<i64> {
+        todo!()
+    }
+
+    fn get_option_string(&self, _key: Self::Option) -> Result<String> {
+        todo!()
+    }
+}
+
+impl Connection for DummyConnection {
+    type StatementType = DummyStatement;
+
+    fn new_statement(&mut self) -> Result<Self::StatementType> {
+        Ok(Self::StatementType::default())
+    }
+
+    fn cancel(&mut self) -> Result<()> {
+        todo!()
+    }
+
+    fn get_cancel_handle(&self) -> Box<dyn adbc_core::CancelHandle> {
+        struct CancelHandle {
+            flag: std::sync::Arc<AtomicFlag>,
+        }
+
+        impl adbc_core::CancelHandle for CancelHandle {
+            fn try_cancel(&self) -> Result<()> {
+                self.flag.set();
+                Ok(())
+            }
+        }
+
+        Box::new(CancelHandle {
+            flag: self.flag.clone(),
+        })
+    }
+
+    fn commit(&mut self) -> Result<()> {
+        IN_COMMIT.set();
+
+        if self.flag.wait(std::time::Duration::from_secs(5)) {
+            // properly cancelled
+            Ok(())
+        } else {
+            Err(Error::with_message_and_status(
+                "Commit was not cancelled",
+                Status::Internal,
+            ))
+        }
+    }
+
+    fn get_info(
+        &self,
+        _codes: Option<std::collections::HashSet<InfoCode>>,
+    ) -> Result<Box<dyn RecordBatchReader + Send + 'static>> {
+        todo!()
+    }
+
+    fn get_objects(
+        &self,
+        _depth: ObjectDepth,
+        _catalog: Option<&str>,
+        _db_schema: Option<&str>,
+        _table_name: Option<&str>,
+        _table_type: Option<Vec<&str>>,
+        _column_name: Option<&str>,
+    ) -> Result<Box<dyn RecordBatchReader + Send + 'static>> {
+        todo!()
+    }
+
+    fn get_statistics(
+        &self,
+        _catalog: Option<&str>,
+        _db_schema: Option<&str>,
+        _table_name: Option<&str>,
+        _approximate: bool,
+    ) -> Result<Box<dyn RecordBatchReader + Send + 'static>> {
+        todo!()
+    }
+
+    fn get_statistic_names(&self) -> Result<Box<dyn RecordBatchReader + Send + 
'static>> {
+        todo!()
+    }
+
+    fn get_table_schema(
+        &self,
+        _catalog: Option<&str>,
+        _db_schema: Option<&str>,
+        _table_name: &str,
+    ) -> Result<arrow_schema::Schema> {
+        todo!()
+    }
+
+    fn get_table_types(&self) -> Result<Box<dyn RecordBatchReader + Send + 
'static>> {
+        todo!()
+    }
+
+    fn read_partition(
+        &self,
+        _partition: impl AsRef<[u8]>,
+    ) -> Result<Box<dyn RecordBatchReader + Send + 'static>> {
+        todo!()
+    }
+
+    fn rollback(&mut self) -> Result<()> {
+        Ok(())
+    }
+}
+
+#[derive(Default)]
+pub struct DummyStatement {
+    // make sure this struct isn't zero-sized
+    _state: i32,
+}
+
+impl Optionable for DummyStatement {
+    type Option = OptionStatement;
+
+    fn set_option(&mut self, _key: Self::Option, _value: OptionValue) -> 
Result<()> {
+        Ok(())
+    }
+
+    fn get_option_bytes(&self, _key: Self::Option) -> Result<Vec<u8>> {
+        todo!()
+    }
+
+    fn get_option_double(&self, _key: Self::Option) -> Result<f64> {
+        todo!()
+    }
+
+    fn get_option_int(&self, _key: Self::Option) -> Result<i64> {
+        todo!()
+    }
+
+    fn get_option_string(&self, _key: Self::Option) -> Result<String> {
+        todo!()
+    }
+}
+
+impl Statement for DummyStatement {
+    fn bind(&mut self, _batch: RecordBatch) -> Result<()> {
+        Ok(())
+    }
+
+    fn bind_stream(&mut self, _reader: Box<dyn RecordBatchReader + Send>) -> 
Result<()> {
+        Ok(())
+    }
+
+    fn cancel(&mut self) -> Result<()> {
+        Ok(())
+    }
+
+    fn execute(&mut self) -> Result<Box<dyn RecordBatchReader + Send + 
'static>> {
+        todo!()
+    }
+
+    fn execute_partitions(&mut self) -> Result<adbc_core::PartitionedResult> {
+        todo!()
+    }
+
+    fn execute_schema(&mut self) -> Result<arrow_schema::Schema> {
+        todo!()
+    }
+
+    fn execute_update(&mut self) -> Result<Option<i64>> {
+        Ok(Some(0))
+    }
+
+    fn get_parameter_schema(&self) -> Result<arrow_schema::Schema> {
+        todo!()
+    }
+
+    fn prepare(&mut self) -> Result<()> {
+        Ok(())
+    }
+
+    fn set_sql_query(&mut self, _query: impl AsRef<str>) -> Result<()> {
+        Ok(())
+    }
+
+    fn set_substrait_plan(&mut self, _plan: impl AsRef<[u8]>) -> Result<()> {
+        Ok(())
+    }
+}
+
+adbc_ffi::export_driver!(AdbcDummyInit, DummyDriver);
diff --git a/rust/driver_manager/tests/common/mod.rs 
b/rust/driver_manager/tests/common/mod.rs
index f0b60d208..e546f6798 100644
--- a/rust/driver_manager/tests/common/mod.rs
+++ b/rust/driver_manager/tests/common/mod.rs
@@ -35,6 +35,42 @@ use arrow_array::{
 use arrow_schema::{ArrowError, DataType, Field, Schema, SchemaRef};
 use arrow_select::concat::concat_batches;
 
+pub mod dummy;
+
+pub struct AtomicFlag {
+    mu: std::sync::Mutex<bool>,
+    cv: std::sync::Condvar,
+}
+
+impl AtomicFlag {
+    pub fn new(value: bool) -> Self {
+        Self {
+            mu: std::sync::Mutex::new(value),
+            cv: std::sync::Condvar::new(),
+        }
+    }
+
+    pub fn clear(&self) {
+        let mut guard = self.mu.lock().unwrap();
+        *guard = false;
+        self.cv.notify_all();
+    }
+
+    pub fn set(&self) {
+        let mut guard = self.mu.lock().unwrap();
+        *guard = true;
+        self.cv.notify_all();
+    }
+
+    pub fn wait(&self, timeout: std::time::Duration) -> bool {
+        let (guard, _) = self
+            .cv
+            .wait_timeout_while(self.mu.lock().unwrap(), timeout, |flag| 
!*flag)
+            .unwrap();
+        *guard
+    }
+}
+
 pub struct SingleBatchReader {
     batch: Option<RecordBatch>,
     schema: SchemaRef,
diff --git a/rust/driver_manager/tests/exporter_cancellation.rs 
b/rust/driver_manager/tests/exporter_cancellation.rs
new file mode 100644
index 000000000..186dd47e3
--- /dev/null
+++ b/rust/driver_manager/tests/exporter_cancellation.rs
@@ -0,0 +1,107 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+// Test that the driver exporter and cancellation do not have undefined
+// behavior by introducing overlapping mutable borrows. Run under Miri.
+
+use std::sync::{Arc, Mutex};
+
+use adbc_core::error::{Result, Status};
+use adbc_core::options::AdbcVersion;
+use adbc_core::{CancelHandle, Connection, Database, Driver, Statement};
+use adbc_driver_manager::ManagedDriver;
+use adbc_ffi::FFI_AdbcDriverInitFunc;
+use arrow_array::{RecordBatch, RecordBatchReader};
+use arrow_schema::{ArrowError, Schema, SchemaRef};
+
+mod common;
+
+struct CancelOnDropReader {
+    cancel: Box<dyn CancelHandle>,
+    status: Arc<Mutex<Option<Status>>>,
+}
+
+impl Iterator for CancelOnDropReader {
+    type Item = std::result::Result<RecordBatch, ArrowError>;
+
+    fn next(&mut self) -> Option<Self::Item> {
+        None
+    }
+}
+
+impl RecordBatchReader for CancelOnDropReader {
+    fn schema(&self) -> SchemaRef {
+        Arc::new(Schema::empty())
+    }
+}
+
+impl Drop for CancelOnDropReader {
+    fn drop(&mut self) {
+        let status = match self.cancel.try_cancel() {
+            Ok(()) => Status::Ok,
+            Err(error) => error.status,
+        };
+        if let Ok(mut recorded) = self.status.lock() {
+            *recorded = Some(status);
+        }
+    }
+}
+
+#[test]
+fn statement_cancellation() -> Result<()> {
+    let mut driver = ManagedDriver::load_static(
+        &(common::dummy::AdbcDummyInit as FFI_AdbcDriverInitFunc),
+        AdbcVersion::V110,
+    )?;
+    let database = driver.new_database()?;
+    let mut connection = database.new_connection()?;
+    let mut statement = connection.new_statement()?;
+    let status = Arc::new(Mutex::new(None));
+    let reader = CancelOnDropReader {
+        cancel: statement.get_cancel_handle(),
+        status: status.clone(),
+    };
+
+    // The dummy driver drops the reader while bind_stream's mutable statement
+    // borrow is live. Its destructor re-enters statement_private_data through
+    // cancellation, creating a potential overlapping mutable borrow of the
+    // exporter state. This was fixed, but this ensures this case continues to
+    // work and should be run via miri.
+    statement.bind_stream(Box::new(reader))?;
+    assert_eq!(*status.lock().unwrap(), Some(Status::Unknown));
+    Ok(())
+}
+
+#[test]
+fn connection_cancellation() -> Result<()> {
+    let mut driver = ManagedDriver::load_static(
+        &(common::dummy::AdbcDummyInit as FFI_AdbcDriverInitFunc),
+        AdbcVersion::V110,
+    )?;
+    let database = driver.new_database()?;
+    let mut connection = database.new_connection()?;
+
+    let handle = connection.get_cancel_handle();
+    let guard = std::thread::spawn(move || {
+        
assert!(common::dummy::IN_COMMIT.wait(std::time::Duration::from_secs(5)));
+        handle.try_cancel().unwrap();
+    });
+
+    connection.commit()?;
+    guard.join().unwrap();
+    Ok(())
+}
diff --git a/rust/ffi/src/driver_exporter.rs b/rust/ffi/src/driver_exporter.rs
index 2c775f718..cd356768f 100644
--- a/rust/ffi/src/driver_exporter.rs
+++ b/rust/ffi/src/driver_exporter.rs
@@ -19,6 +19,7 @@ use std::collections::{HashMap, HashSet};
 use std::ffi::{CStr, CString};
 use std::hash::Hash;
 use std::os::raw::{c_char, c_int, c_void};
+use std::sync::Mutex;
 
 use arrow_array::StructArray;
 use arrow_array::ffi::{FFI_ArrowArray, FFI_ArrowSchema, from_ffi};
@@ -62,33 +63,46 @@ impl<DriverType: Driver> ExportedDatabase<DriverType> {
 }
 
 struct InitializedConnection<DriverType: Driver> {
-    connection: ConnectionType<DriverType>,
+    connection: Mutex<ConnectionType<DriverType>>,
     cancel_handle: Box<dyn adbc_core::CancelHandle>,
 }
 
 enum ExportedConnection<DriverType: Driver> {
     /// Pre-init options
-    Options(HashMap<OptionConnection, OptionValue>),
+    Options(Mutex<HashMap<OptionConnection, OptionValue>>),
     /// Initialized connection
     Connection(InitializedConnection<DriverType>),
 }
 
+type ConnectionTuple<'a, DriverType> = (
+    Option<std::sync::MutexGuard<'a, HashMap<OptionConnection, OptionValue>>>,
+    Option<std::sync::MutexGuard<'a, ConnectionType<DriverType>>>,
+);
+
 impl<DriverType: Driver> ExportedConnection<DriverType> {
-    fn tuple(
-        &mut self,
-    ) -> (
-        Option<&mut HashMap<OptionConnection, OptionValue>>,
-        Option<&mut ConnectionType<DriverType>>,
-    ) {
+    fn tuple(&self) -> Result<ConnectionTuple<'_, DriverType>> {
         match self {
-            Self::Options(options) => (Some(options), None),
-            Self::Connection(connection) => (None, Some(&mut 
connection.connection)),
+            Self::Options(options) => options
+                .lock()
+                .map_err(|_| {
+                    Error::with_message_and_status("Connection mutex 
poisoned", Status::Internal)
+                })
+                .map(|guard| (Some(guard), None)),
+            Self::Connection(connection) => connection
+                .connection
+                .lock()
+                .map_err(|_| {
+                    Error::with_message_and_status("Connection mutex 
poisoned", Status::Internal)
+                })
+                .map(|guard| (None, Some(guard))),
         }
     }
 
-    fn try_connection(&mut self) -> Result<&mut ConnectionType<DriverType>> {
+    fn try_connection(&self) -> Result<std::sync::MutexGuard<'_, 
ConnectionType<DriverType>>> {
         match self {
-            Self::Connection(connection) => Ok(&mut connection.connection),
+            Self::Connection(connection) => 
connection.connection.lock().map_err(|_| {
+                Error::with_message_and_status("Connection mutex poisoned", 
Status::Internal)
+            }),
             _ => Err(Error::with_message_and_status(
                 "Connection not initialized",
                 Status::InvalidState,
@@ -96,9 +110,9 @@ impl<DriverType: Driver> ExportedConnection<DriverType> {
         }
     }
 
-    fn try_cancel(&mut self) -> Result<&mut dyn adbc_core::CancelHandle> {
+    fn try_cancel(&self) -> Result<&dyn adbc_core::CancelHandle> {
         match self {
-            Self::Connection(connection) => 
Ok(connection.cancel_handle.as_mut()),
+            Self::Connection(connection) => 
Ok(connection.cancel_handle.as_ref()),
             _ => Err(Error::with_message_and_status(
                 "Connection not initialized",
                 Status::InvalidState,
@@ -107,10 +121,18 @@ impl<DriverType: Driver> ExportedConnection<DriverType> {
     }
 }
 
-struct ExportedStatement<DriverType: Driver>(
-    StatementType<DriverType>,
-    Box<dyn adbc_core::CancelHandle>,
-);
+struct ExportedStatement<DriverType: Driver> {
+    stmt: Mutex<StatementType<DriverType>>,
+    cancel: Box<dyn adbc_core::CancelHandle>,
+}
+
+impl<DriverType: Driver> ExportedStatement<DriverType> {
+    fn try_statement(&self) -> Result<std::sync::MutexGuard<'_, 
StatementType<DriverType>>> {
+        self.stmt.lock().map_err(|_| {
+            Error::with_message_and_status("Statement mutex poisoned", 
Status::Internal)
+        })
+    }
+}
 
 pub trait FFIDriver {
     fn ffi_driver() -> FFI_AdbcDriver;
@@ -298,16 +320,15 @@ macro_rules! check_not_null {
 #[macro_export]
 macro_rules! pointer_as_mut {
     ($ptr:ident, $err_out:expr) => {
-        match unsafe { $ptr.as_mut() } {
-            Some(p) => p,
-            None => {
-                let error = adbc_core::error::Error::with_message_and_status(
-                    format!("Passed null pointer for argument {:?}", 
stringify!($ptr)),
-                    adbc_core::error::Status::InvalidArguments,
-                );
-                unsafe { $crate::export_error($err_out, error) };
-                return adbc_core::error::Status::InvalidArguments.into();
-            }
+        if let Some(p) = unsafe { $ptr.as_mut() } {
+            p
+        } else {
+            let error = adbc_core::error::Error::with_message_and_status(
+                format!("Passed null pointer for argument {:?}", 
stringify!($ptr)),
+                adbc_core::error::Status::InvalidArguments,
+            );
+            unsafe { $crate::export_error($err_out, error) };
+            return adbc_core::error::Status::InvalidArguments.into();
         }
     };
 }
@@ -827,10 +848,10 @@ unsafe fn maybe_str<'a>(str: *const c_char) -> 
Result<Option<&'a str>> {
 // Connection
 
 unsafe fn connection_private_data<'a, DriverType: Driver>(
-    connection: &mut FFI_AdbcConnection,
-) -> Result<&'a mut ExportedConnection<DriverType>> {
-    let exported = connection.private_data as *mut 
ExportedConnection<DriverType>;
-    exported.as_mut().ok_or(Error::with_message_and_status(
+    connection: *mut FFI_AdbcConnection,
+) -> Result<&'a ExportedConnection<DriverType>> {
+    let exported = (*connection).private_data as *mut 
ExportedConnection<DriverType>;
+    exported.as_ref().ok_or(Error::with_message_and_status(
         "Uninitialized connection",
         Status::InvalidState,
     ))
@@ -843,7 +864,7 @@ unsafe fn connection_set_option_impl<DriverType: Driver, 
Value: Into<OptionValue
     value: Value,
     error: *mut FFI_AdbcError,
 ) -> AdbcStatusCode {
-    let connection = pointer_as_mut!(connection, error);
+    check_not_null!(connection, error);
     assert!(!key.is_null());
 
     let exported = 
check_err!(connection_private_data::<DriverType>(connection), error);
@@ -851,13 +872,22 @@ unsafe fn connection_set_option_impl<DriverType: Driver, 
Value: Into<OptionValue
 
     match exported {
         ExportedConnection::Options(options) => {
+            let mut options = check_err!(
+                options.lock().map_err(|_| {
+                    Error::with_message_and_status("Connection mutex 
poisoned", Status::Internal)
+                }),
+                error
+            );
             options.insert(key.into(), value.into());
         }
         ExportedConnection::Connection(connection) => {
-            check_err!(
-                connection.connection.set_option(key.into(), value.into()),
+            let mut connection = check_err!(
+                connection.connection.lock().map_err(|_| {
+                    Error::with_message_and_status("Connection mutex 
poisoned", Status::Internal)
+                }),
                 error
             );
+            check_err!(connection.set_option(key.into(), value.into()), error);
         }
     }
 
@@ -871,7 +901,9 @@ extern "C" fn connection_new<DriverType: Driver>(
     catch_panic(error, || {
         let connection = pointer_as_mut!(connection, error);
 
-        let exported = 
Box::new(ExportedConnection::<DriverType>::Options(HashMap::new()));
+        let exported = 
Box::new(ExportedConnection::<DriverType>::Options(Mutex::new(
+            HashMap::new(),
+        )));
         connection.private_data = Box::into_raw(exported) as *mut c_void;
 
         ADBC_STATUS_OK
@@ -887,8 +919,15 @@ extern "C" fn connection_init<DriverType: Driver>(
         let connection = pointer_as_mut!(connection, error);
         let database = pointer_as_mut!(database, error);
 
+        // It's OK to have a &mut here since the caller can't yet get a cancel 
handle
         let exported_connection = check_err!(
-            unsafe { connection_private_data::<DriverType>(connection) },
+            unsafe {
+                let exported = connection.private_data as *mut 
ExportedConnection<DriverType>;
+                exported.as_mut().ok_or(Error::with_message_and_status(
+                    "Uninitialized connection",
+                    Status::InvalidState,
+                ))
+            },
             error
         );
         let exported_database = check_err!(
@@ -899,6 +938,16 @@ extern "C" fn connection_init<DriverType: Driver>(
         if let ExportedConnection::Options(options) = exported_connection {
             let connection = match exported_database {
                 ExportedDatabase::Database(database) => {
+                    let options = check_err!(
+                        options.get_mut().map_err(|_| {
+                            Error::with_message_and_status(
+                                "Connection mutex poisoned",
+                                Status::Internal,
+                            )
+                        }),
+                        error
+                    );
+
                     database.new_connection_with_opts(options.clone())
                 }
                 _ => Err(Error::with_message_and_status(
@@ -908,8 +957,9 @@ extern "C" fn connection_init<DriverType: Driver>(
             };
             let connection = check_err!(connection, error);
             let cancel_handle = connection.get_cancel_handle();
+
             *exported_connection = 
ExportedConnection::Connection(InitializedConnection {
-                connection,
+                connection: Mutex::new(connection),
                 cancel_handle,
             });
         } else {
@@ -1022,7 +1072,7 @@ extern "C" fn connection_get_option<DriverType: Driver>(
     error: *mut FFI_AdbcError,
 ) -> AdbcStatusCode {
     catch_panic(error, || {
-        let connection = pointer_as_mut!(connection, error);
+        check_not_null!(connection, error);
         check_not_null!(key, error);
         check_not_null!(value, error);
         check_not_null!(length, error);
@@ -1031,9 +1081,10 @@ extern "C" fn connection_get_option<DriverType: Driver>(
             unsafe { connection_private_data::<DriverType>(connection) },
             error
         );
-        let (options, connection) = exported.tuple();
+        let (mut options, mut connection) = check_err!(exported.tuple(), 
error);
 
-        let optvalue = unsafe { get_option(connection, options, key) };
+        let optvalue =
+            unsafe { get_option(connection.as_deref_mut(), 
options.as_deref_mut(), key) };
         let optvalue = check_err!(optvalue, error);
         check_err!(unsafe { copy_string(&optvalue, value, length) }, error);
 
@@ -1048,7 +1099,7 @@ extern "C" fn connection_get_option_int<DriverType: 
Driver>(
     error: *mut FFI_AdbcError,
 ) -> AdbcStatusCode {
     catch_panic(error, || {
-        let connection = pointer_as_mut!(connection, error);
+        check_not_null!(connection, error);
         check_not_null!(key, error);
         check_not_null!(value, error);
 
@@ -1056,9 +1107,12 @@ extern "C" fn connection_get_option_int<DriverType: 
Driver>(
             unsafe { connection_private_data::<DriverType>(connection) },
             error
         );
-        let (options, connection) = exported.tuple();
+        let (mut options, mut connection) = check_err!(exported.tuple(), 
error);
 
-        let optvalue = check_err!(unsafe { get_option_int(connection, options, 
key) }, error);
+        let optvalue = check_err!(
+            unsafe { get_option_int(connection.as_deref_mut(), 
options.as_deref_mut(), key) },
+            error
+        );
         unsafe { std::ptr::write_unaligned(value, optvalue) };
 
         ADBC_STATUS_OK
@@ -1072,7 +1126,7 @@ extern "C" fn connection_get_option_double<DriverType: 
Driver>(
     error: *mut FFI_AdbcError,
 ) -> AdbcStatusCode {
     catch_panic(error, || {
-        let connection = pointer_as_mut!(connection, error);
+        check_not_null!(connection, error);
         check_not_null!(key, error);
         check_not_null!(value, error);
 
@@ -1080,10 +1134,10 @@ extern "C" fn connection_get_option_double<DriverType: 
Driver>(
             unsafe { connection_private_data::<DriverType>(connection) },
             error
         );
-        let (options, connection) = exported.tuple();
+        let (mut options, mut connection) = check_err!(exported.tuple(), 
error);
 
         let optvalue = check_err!(
-            unsafe { get_option_double(connection, options, key) },
+            unsafe { get_option_double(connection.as_deref_mut(), 
options.as_deref_mut(), key) },
             error
         );
         unsafe { std::ptr::write_unaligned(value, optvalue) };
@@ -1100,7 +1154,7 @@ extern "C" fn connection_get_option_bytes<DriverType: 
Driver>(
     error: *mut FFI_AdbcError,
 ) -> AdbcStatusCode {
     catch_panic(error, || {
-        let connection = pointer_as_mut!(connection, error);
+        check_not_null!(connection, error);
         check_not_null!(key, error);
         check_not_null!(value, error);
         check_not_null!(length, error);
@@ -1109,9 +1163,10 @@ extern "C" fn connection_get_option_bytes<DriverType: 
Driver>(
             unsafe { connection_private_data::<DriverType>(connection) },
             error
         );
-        let (options, connection) = exported.tuple();
+        let (mut options, mut connection) = check_err!(exported.tuple(), 
error);
 
-        let optvalue = unsafe { get_option_bytes(connection, options, key) };
+        let optvalue =
+            unsafe { get_option_bytes(connection.as_deref_mut(), 
options.as_deref_mut(), key) };
         let optvalue = check_err!(optvalue, error);
         unsafe { copy_bytes(&optvalue, value, length) };
 
@@ -1125,7 +1180,7 @@ extern "C" fn connection_get_table_types<DriverType: 
Driver + 'static>(
     error: *mut FFI_AdbcError,
 ) -> AdbcStatusCode {
     catch_panic(error, || {
-        let connection = pointer_as_mut!(connection, error);
+        check_not_null!(connection, error);
         check_not_null!(out, error);
 
         let exported = check_err!(
@@ -1152,7 +1207,7 @@ extern "C" fn connection_get_table_schema<DriverType: 
Driver>(
     error: *mut FFI_AdbcError,
 ) -> AdbcStatusCode {
     catch_panic(error, || {
-        let connection = pointer_as_mut!(connection, error);
+        check_not_null!(connection, error);
         check_not_null!(table_name, error);
         check_not_null!(schema, error);
 
@@ -1183,7 +1238,7 @@ extern "C" fn connection_get_info<DriverType: Driver + 
'static>(
     error: *mut FFI_AdbcError,
 ) -> AdbcStatusCode {
     catch_panic(error, || {
-        let connection = pointer_as_mut!(connection, error);
+        check_not_null!(connection, error);
         check_not_null!(out, error);
 
         let exported = check_err!(
@@ -1215,13 +1270,13 @@ extern "C" fn connection_commit<DriverType: Driver>(
     error: *mut FFI_AdbcError,
 ) -> AdbcStatusCode {
     catch_panic(error, || {
-        let connection = pointer_as_mut!(connection, error);
+        check_not_null!(connection, error);
 
         let exported = check_err!(
             unsafe { connection_private_data::<DriverType>(connection) },
             error
         );
-        let connection = check_err!(exported.try_connection(), error);
+        let mut connection = check_err!(exported.try_connection(), error);
         check_err!(connection.commit(), error);
 
         ADBC_STATUS_OK
@@ -1233,13 +1288,13 @@ extern "C" fn connection_rollback<DriverType: Driver>(
     error: *mut FFI_AdbcError,
 ) -> AdbcStatusCode {
     catch_panic(error, || {
-        let connection = pointer_as_mut!(connection, error);
+        check_not_null!(connection, error);
 
         let exported = check_err!(
             unsafe { connection_private_data::<DriverType>(connection) },
             error
         );
-        let connection = check_err!(exported.try_connection(), error);
+        let mut connection = check_err!(exported.try_connection(), error);
         check_err!(connection.rollback(), error);
 
         ADBC_STATUS_OK
@@ -1251,7 +1306,7 @@ extern "C" fn connection_cancel<DriverType: Driver>(
     error: *mut FFI_AdbcError,
 ) -> AdbcStatusCode {
     catch_panic(error, || {
-        let connection = pointer_as_mut!(connection, error);
+        check_not_null!(connection, error);
 
         let exported = check_err!(
             unsafe { connection_private_data::<DriverType>(connection) },
@@ -1270,7 +1325,7 @@ extern "C" fn connection_get_statistic_names<DriverType: 
Driver + 'static>(
     error: *mut FFI_AdbcError,
 ) -> AdbcStatusCode {
     catch_panic(error, || {
-        let connection = pointer_as_mut!(connection, error);
+        check_not_null!(connection, error);
         check_not_null!(out, error);
 
         let exported = check_err!(
@@ -1296,7 +1351,7 @@ extern "C" fn connection_read_partition<DriverType: 
Driver + 'static>(
     error: *mut FFI_AdbcError,
 ) -> AdbcStatusCode {
     catch_panic(error, || {
-        let connection = pointer_as_mut!(connection, error);
+        check_not_null!(connection, error);
         check_not_null!(serialized_partition, error);
         check_not_null!(out, error);
 
@@ -1327,7 +1382,7 @@ extern "C" fn connection_get_statistics<DriverType: 
Driver + 'static>(
     error: *mut FFI_AdbcError,
 ) -> AdbcStatusCode {
     catch_panic(error, || {
-        let connection = pointer_as_mut!(connection, error);
+        check_not_null!(connection, error);
         check_not_null!(out, error);
 
         let catalog = check_err!(unsafe { maybe_str(catalog) }, error);
@@ -1363,7 +1418,7 @@ extern "C" fn connection_get_objects<DriverType: Driver + 
'static>(
     error: *mut FFI_AdbcError,
 ) -> AdbcStatusCode {
     catch_panic(error, || {
-        let connection = pointer_as_mut!(connection, error);
+        check_not_null!(connection, error);
         check_not_null!(out, error);
 
         let depth = check_err!(ObjectDepth::try_from(depth), error);
@@ -1413,10 +1468,10 @@ extern "C" fn connection_get_objects<DriverType: Driver 
+ 'static>(
 // SAFETY: Will panic if `statement` is null.
 unsafe fn statement_private_data<'a, DriverType: Driver>(
     statement: *mut FFI_AdbcStatement,
-) -> Result<&'a mut ExportedStatement<DriverType>> {
+) -> Result<&'a ExportedStatement<DriverType>> {
     assert!(!statement.is_null());
     let exported = (*statement).private_data as *mut 
ExportedStatement<DriverType>;
-    exported.as_mut().ok_or(Error::with_message_and_status(
+    exported.as_ref().ok_or(Error::with_message_and_status(
         "Uninitialized statement",
         Status::InvalidState,
     ))
@@ -1433,8 +1488,9 @@ unsafe fn statement_set_option_impl<DriverType: Driver, 
Value: Into<OptionValue>
     assert!(!key.is_null());
 
     let exported = check_err!(statement_private_data::<DriverType>(statement), 
error);
+    let mut stmt = check_err!(exported.try_statement(), error);
     let key = check_err!(CStr::from_ptr(key).to_str(), error);
-    check_err!(exported.0.set_option(key.into(), value.into()), error);
+    check_err!(stmt.set_option(key.into(), value.into()), error);
     ADBC_STATUS_OK
 }
 
@@ -1444,22 +1500,21 @@ extern "C" fn statement_new<DriverType: Driver>(
     error: *mut FFI_AdbcError,
 ) -> AdbcStatusCode {
     catch_panic(error, || {
-        let connection = pointer_as_mut!(connection, error);
+        check_not_null!(connection, error);
         let statement = pointer_as_mut!(statement, error);
 
         let exported_connection = check_err!(
             unsafe { connection_private_data::<DriverType>(connection) },
             error
         );
-        let inner_connection = 
check_err!(exported_connection.try_connection(), error);
-
+        let mut inner_connection = 
check_err!(exported_connection.try_connection(), error);
         let inner_statement = check_err!(inner_connection.new_statement(), 
error);
         let cancel_handle = inner_statement.get_cancel_handle();
 
-        let exported = Box::new(ExportedStatement::<DriverType>(
-            inner_statement,
-            cancel_handle,
-        ));
+        let exported = Box::new(ExportedStatement::<DriverType> {
+            stmt: Mutex::new(inner_statement),
+            cancel: cancel_handle,
+        });
         statement.private_data = Box::into_raw(exported) as *mut c_void;
 
         ADBC_STATUS_OK
@@ -1568,7 +1623,8 @@ extern "C" fn statement_get_option<DriverType: Driver>(
             unsafe { statement_private_data::<DriverType>(statement) },
             error
         );
-        let optvalue = unsafe { get_option(Some(&mut exported.0), None, key) };
+        let mut stmt = check_err!(exported.try_statement(), error);
+        let optvalue = unsafe { get_option(Some(&mut *stmt), None, key) };
         let optvalue = check_err!(optvalue, error);
         check_err!(unsafe { copy_string(&optvalue, value, length) }, error);
 
@@ -1591,8 +1647,9 @@ extern "C" fn statement_get_option_int<DriverType: 
Driver>(
             unsafe { statement_private_data::<DriverType>(statement) },
             error
         );
+        let mut stmt = check_err!(exported.try_statement(), error);
         let optvalue = check_err!(
-            unsafe { get_option_int(Some(&mut exported.0), None, key) },
+            unsafe { get_option_int(Some(&mut *stmt), None, key) },
             error
         );
         unsafe { std::ptr::write_unaligned(value, optvalue) };
@@ -1616,8 +1673,9 @@ extern "C" fn statement_get_option_double<DriverType: 
Driver>(
             unsafe { statement_private_data::<DriverType>(statement) },
             error
         );
+        let mut stmt = check_err!(exported.try_statement(), error);
         let optvalue = check_err!(
-            unsafe { get_option_double(Some(&mut exported.0), None, key) },
+            unsafe { get_option_double(Some(&mut *stmt), None, key) },
             error
         );
         unsafe { std::ptr::write_unaligned(value, optvalue) };
@@ -1643,7 +1701,8 @@ extern "C" fn statement_get_option_bytes<DriverType: 
Driver>(
             unsafe { statement_private_data::<DriverType>(statement) },
             error
         );
-        let optvalue = unsafe { get_option_bytes(Some(&mut exported.0), None, 
key) };
+        let mut stmt = check_err!(exported.try_statement(), error);
+        let optvalue = unsafe { get_option_bytes(Some(&mut *stmt), None, key) 
};
         let optvalue = check_err!(optvalue, error);
         unsafe { copy_bytes(&optvalue, value, length) };
         ADBC_STATUS_OK
@@ -1665,7 +1724,7 @@ extern "C" fn statement_bind<DriverType: Driver>(
             unsafe { statement_private_data::<DriverType>(statement) },
             error
         );
-        let statement = &mut exported.0;
+        let mut statement = check_err!(exported.try_statement(), error);
 
         let schema = unsafe { schema.as_ref().unwrap() };
         let data = unsafe { FFI_ArrowArray::from_raw(values) };
@@ -1701,7 +1760,8 @@ extern "C" fn statement_bind_stream<DriverType: Driver>(
             unsafe { statement_private_data::<DriverType>(statement) },
             error
         );
-        let statement = &mut exported.0;
+        let mut stmt = check_err!(exported.try_statement(), error);
+        let statement = &mut *stmt;
 
         let reader = check_err!(unsafe { 
ArrowArrayStreamReader::from_raw(stream) }, error);
         let reader = Box::new(reader);
@@ -1722,8 +1782,7 @@ extern "C" fn statement_cancel<DriverType: Driver>(
             unsafe { statement_private_data::<DriverType>(statement) },
             error
         );
-        let cancel_handle = &mut exported.1;
-        check_err!(cancel_handle.try_cancel(), error);
+        check_err!(exported.cancel.try_cancel(), error);
 
         ADBC_STATUS_OK
     })
@@ -1742,7 +1801,7 @@ extern "C" fn statement_execute_query<DriverType: Driver 
+ 'static>(
             unsafe { statement_private_data::<DriverType>(statement) },
             error
         );
-        let statement = &mut exported.0;
+        let mut statement = check_err!(exported.try_statement(), error);
 
         if !out.is_null() {
             let reader = check_err!(statement.execute(), error);
@@ -1776,7 +1835,7 @@ extern "C" fn statement_execute_schema<DriverType: 
Driver>(
             unsafe { statement_private_data::<DriverType>(statement) },
             error
         );
-        let statement = &mut exported.0;
+        let mut statement = check_err!(exported.try_statement(), error);
 
         let schema_value = check_err!(statement.execute_schema(), error);
         let schema_value: FFI_ArrowSchema = 
check_err!(schema_value.try_into(), error);
@@ -1802,7 +1861,7 @@ extern "C" fn statement_execute_partitions<DriverType: 
Driver>(
             unsafe { statement_private_data::<DriverType>(statement) },
             error
         );
-        let statement = &mut exported.0;
+        let mut statement = check_err!(exported.try_statement(), error);
 
         let result = check_err!(statement.execute_partitions(), error);
 
@@ -1831,7 +1890,7 @@ extern "C" fn statement_prepare<DriverType: Driver>(
             unsafe { statement_private_data::<DriverType>(statement) },
             error
         );
-        let statement = &mut exported.0;
+        let mut statement = check_err!(exported.try_statement(), error);
         check_err!(statement.prepare(), error);
         ADBC_STATUS_OK
     })
@@ -1850,7 +1909,7 @@ extern "C" fn statement_set_sql_query<DriverType: Driver>(
             unsafe { statement_private_data::<DriverType>(statement) },
             error
         );
-        let statement = &mut exported.0;
+        let mut statement = check_err!(exported.try_statement(), error);
 
         let query = check_err!(unsafe { CStr::from_ptr(query).to_str() }, 
error);
         check_err!(statement.set_sql_query(query), error);
@@ -1873,7 +1932,7 @@ extern "C" fn statement_set_substrait_plan<DriverType: 
Driver>(
             unsafe { statement_private_data::<DriverType>(statement) },
             error
         );
-        let statement = &mut exported.0;
+        let mut statement = check_err!(exported.try_statement(), error);
 
         let plan = unsafe { std::slice::from_raw_parts(plan, length) };
         check_err!(statement.set_substrait_plan(plan), error);
@@ -1895,7 +1954,7 @@ extern "C" fn statement_get_parameter_schema<DriverType: 
Driver>(
             unsafe { statement_private_data::<DriverType>(statement) },
             error
         );
-        let statement = &exported.0;
+        let statement = check_err!(exported.try_statement(), error);
 
         let schema_value = check_err!(statement.get_parameter_schema(), error);
         let schema_value: FFI_ArrowSchema = 
check_err!(schema_value.try_into(), error);
@@ -1935,30 +1994,29 @@ unsafe extern "C" fn error_get_detail(
         return default;
     }
 
-    match error.as_ref() {
-        None => default,
-        Some(error) => {
-            let detail_count = error_get_detail_count(error);
-            if index >= detail_count {
-                return default;
-            }
-            let index = index as usize; // Cannot overflow since index >= 0 
and index < detail_count
+    if let Some(error) = error.as_ref() {
+        let detail_count = error_get_detail_count(error);
+        if index >= detail_count {
+            return default;
+        }
+        let index = index as usize; // Cannot overflow since index >= 0 and 
index < detail_count
 
-            if error.private_data.is_null() {
-                return default;
-            }
-            let private_data = error.private_data as *const ErrorPrivateData;
+        if error.private_data.is_null() {
+            return default;
+        }
+        let private_data = error.private_data as *const ErrorPrivateData;
 
-            let key = (&(*private_data).keys)[index].as_ptr();
-            let value = (&(*private_data).values)[index].as_ptr();
-            let value_length = (&(*private_data).values)[index].len();
+        let key = (&(*private_data).keys)[index].as_ptr();
+        let value = (&(*private_data).values)[index].as_ptr();
+        let value_length = (&(*private_data).values)[index].len();
 
-            FFI_AdbcErrorDetail {
-                key,
-                value,
-                value_length,
-            }
+        FFI_AdbcErrorDetail {
+            key,
+            value,
+            value_length,
         }
+    } else {
+        default
     }
 }
 

Reply via email to