From 32296ab1abd13a0dce7fd21acbe96e4fc9b9f315 Mon Sep 17 00:00:00 2001 From: "Jeremy W. Rowe" Date: Thu, 19 Dec 2024 09:31:55 -0500 Subject: [PATCH] Removes sqlite3_config and sqlite3_initialize from local db new This change removes ffi::sqlite3_config() and ffi::sqlite3_initialize() from local/database.new and moves the configuration down to local/connection#connect. This is done because a SQLite connection can not be initialized multiple times, this change frees users of the library to intialize SQLite connections at a different level. initialize specifically presents a problem when libsql is being included in a shared library where there are other shared libraries that interact with the SQLite C API. This change *should* be relatively safe, since connections can change the threading mode on a per connection basis so long that sqlite was compiled with SQLITE_THREADSAFE=1 or SQLITE_THREADSAFE=2. Refs: * https://sqlite.org/threadsafe.html * https://sqlite.org/c3ref/c_config_covering_index_scan.html#sqliteconfigserialized. --- libsql/src/local/connection.rs | 16 +++++++++++++- libsql/src/local/database.rs | 38 +++++++++------------------------- 2 files changed, 25 insertions(+), 29 deletions(-) diff --git a/libsql/src/local/connection.rs b/libsql/src/local/connection.rs index 56ff743ee5..5bc2a5f1ad 100644 --- a/libsql/src/local/connection.rs +++ b/libsql/src/local/connection.rs @@ -38,14 +38,28 @@ impl Connection { pub(crate) fn connect(db: &Database) -> Result { let mut raw = std::ptr::null_mut(); let db_path = db.db_path.clone(); + let flags: c_int = ffi::SQLITE_OPEN_FULLMUTEX | db.flags.bits(); + let err = unsafe { + if ffi::sqlite3_threadsafe() == 0 { + // https://sqlite.org/threadsafe.html + return Err(Error::ConnectionFailed(String::from( + "SQLite thread safety check failed: SQLite was compiled without thread-safety support \ + (SQLITE_THREADSAFE=0). For libsql to operate correctly, SQLite must be compiled with either \ + serialized mode (SQLITE_THREADSAFE=1) or multi-threaded mode (SQLITE_THREADSAFE=2). \ + Thread-safety is required because libsql needs to ensure safe concurrent access to database \ + connections across multiple threads. \ + Please rebuild SQLite with the appropriate thread-safety options." + ))); + } + ffi::sqlite3_open_v2( std::ffi::CString::new(db_path.as_str()) .unwrap() .as_c_str() .as_ptr() as *const _, &mut raw, - db.flags.bits() as c_int, + flags, std::ptr::null(), ) }; diff --git a/libsql/src/local/database.rs b/libsql/src/local/database.rs index 726bb98a4e..ab1645804b 100644 --- a/libsql/src/local/database.rs +++ b/libsql/src/local/database.rs @@ -1,5 +1,3 @@ -use std::sync::Once; - cfg_replication!( use http::uri::InvalidUri; use crate::database::{EncryptionConfig, FrameNo}; @@ -24,7 +22,6 @@ cfg_sync! { use crate::{database::OpenFlags, local::connection::Connection}; use crate::{Error::ConnectionFailed, Result}; -use libsql_sys::ffi; // A libSQL database. pub struct Database { @@ -230,29 +227,6 @@ impl Database { } pub fn new(db_path: String, flags: OpenFlags) -> Database { - static LIBSQL_INIT: Once = Once::new(); - - LIBSQL_INIT.call_once(|| { - // Ensure that we are configured with the correct threading model - // if this config is not set correctly the entire api is unsafe. - unsafe { - assert_eq!( - ffi::sqlite3_config(ffi::SQLITE_CONFIG_SERIALIZED), - ffi::SQLITE_OK, - "libsql was configured with an incorrect threading configuration and - the api is not safe to use. Please check that no multi-thread options have - been set. If nothing was configured then please open an issue at: - https://github.com/libsql/libsql" - ); - - assert_eq!( - ffi::sqlite3_initialize(), - ffi::SQLITE_OK, - "libsql failed to initialize" - ); - } - }); - Database { db_path, flags, @@ -420,7 +394,11 @@ impl Database { } #[cfg(feature = "sync")] - async fn try_push(&self, sync_ctx: &mut SyncContext, conn: &Connection) -> Result { + async fn try_push( + &self, + sync_ctx: &mut SyncContext, + conn: &Connection, + ) -> Result { let page_size = { let rows = conn .query("PRAGMA page_size", crate::params::Params::None)? @@ -471,7 +449,11 @@ impl Database { } #[cfg(feature = "sync")] - async fn try_pull(&self, sync_ctx: &mut SyncContext, conn: &Connection) -> Result { + async fn try_pull( + &self, + sync_ctx: &mut SyncContext, + conn: &Connection, + ) -> Result { let generation = sync_ctx.generation(); let mut frame_no = sync_ctx.durable_frame_num() + 1; conn.wal_insert_begin()?;