From 8d1e459c7d02d51d4cf1adb9d0ef7cfa06df9ebb Mon Sep 17 00:00:00 2001 From: ad hoc Date: Wed, 8 Nov 2023 14:50:54 +0100 Subject: [PATCH] fix rust bindings --- libsql-sys-tmp/src/ffi/mod.rs | 3 +- libsql-sys-tmp/src/lib.rs | 77 ++-- libsql-sys-tmp/src/wal.rs | 150 +++---- libsql-sys-tmp/src/wal_hook.rs | 478 ---------------------- libsql-sys/Cargo.toml | 4 + libsql-sys/src/lib.rs | 14 +- vendored/rusqlite/Cargo.toml | 2 +- vendored/rusqlite/src/inner_connection.rs | 17 +- vendored/rusqlite/src/lib.rs | 11 +- 9 files changed, 139 insertions(+), 617 deletions(-) delete mode 100644 libsql-sys-tmp/src/wal_hook.rs diff --git a/libsql-sys-tmp/src/ffi/mod.rs b/libsql-sys-tmp/src/ffi/mod.rs index 222bb2ad98..fdaf2ed500 100644 --- a/libsql-sys-tmp/src/ffi/mod.rs +++ b/libsql-sys-tmp/src/ffi/mod.rs @@ -3,8 +3,7 @@ pub mod types; pub use rusqlite::ffi::{ - libsql_wal_methods, libsql_wal_methods_find, libsql_wal_methods_register, - libsql_wal_methods_unregister, sqlite3, sqlite3_file, sqlite3_hard_heap_limit64, + libsql_wal_methods, sqlite3, sqlite3_file, sqlite3_hard_heap_limit64, sqlite3_io_methods, sqlite3_soft_heap_limit64, sqlite3_vfs, WalIndexHdr, SQLITE_CANTOPEN, SQLITE_CHECKPOINT_FULL, SQLITE_CHECKPOINT_TRUNCATE, SQLITE_IOERR_WRITE, SQLITE_OK, }; diff --git a/libsql-sys-tmp/src/lib.rs b/libsql-sys-tmp/src/lib.rs index 6225995840..ba00e8ed7d 100644 --- a/libsql-sys-tmp/src/lib.rs +++ b/libsql-sys-tmp/src/lib.rs @@ -1,30 +1,26 @@ #![allow(improper_ctypes)] pub mod ffi; -// pub mod wal_hook; pub mod wal; -use std::{ffi::{CString, c_int, c_void}, ops::Deref, time::Duration, ptr::slice_from_raw_parts_mut}; +use std::marker::PhantomData; +use std::ops::Deref; -use crate::wal::BusyHandler; pub use once_cell::sync::Lazy; -use rusqlite::ffi::{sqlite3, wal_impl, SQLITE_OK, WAL_SAVEPOINT_NDATA, PgHdr}; +use rusqlite::ffi::sqlite3; -use self::{ - ffi::{libsql_wal_methods, libsql_wal}, - wal::{Wal, CreateWal}, -}; +use crate::wal::make_create_wal; + +use self::wal::{Wal, CreateWal}; #[derive(Debug)] -pub struct Connection { +pub struct Connection { conn: rusqlite::Connection, - // Safety: _ctx MUST be dropped after the connection, because the connection has a pointer - // This pointer MUST NOT move out of the connection - _ctx: Box, + _pth: PhantomData, } -impl Deref for Connection { +impl Deref for Connection { type Target = rusqlite::Connection; fn deref(&self) -> &Self::Target { @@ -32,16 +28,16 @@ impl Deref for Connection { } } -impl Connection { - /// returns a dummy, in-memory connection. For testing purposes only - pub fn test() -> Self { - let conn = rusqlite::Connection::open_in_memory().unwrap(); - Self { - conn, - _ctx: Box::new(()), - } - } -} +// impl Connection { +// /// returns a dummy, in-memory connection. For testing purposes only +// pub fn test() -> Self { +// let conn = rusqlite::Connection::open_in_memory().unwrap(); +// Self { +// conn, +// _ctx: Box::new(()), +// } +// } +// } impl Connection { /// Opens a database with the regular wal methods in the directory pointed to by path @@ -52,47 +48,24 @@ impl Connection { auto_checkpoint: u32, ) -> Result { let path = path.as_ref().join("data"); - let mut _ctx = Box::new(hook_ctx); tracing::trace!( "Opening a connection with regular WAL at {}", path.display() ); let conn_str = format!("file:{}?_journal_mode=WAL", path.display()); - let filename = CString::new(conn_str).unwrap(); - let mut db: *mut rusqlite::ffi::sqlite3 = std::ptr::null_mut(); - + let conn = rusqlite::Connection::open_with_flags_and_wal(conn_str, flags, make_create_wal(create_wal))?; unsafe { - // We pass a pointer to the WAL methods data to the database connection. This means - // that the reference must outlive the connection. This is guaranteed by the marker in - // the returned connection. - let mut rc = rusqlite::ffi::libsql_open_v2( - filename.as_ptr(), - &mut db as *mut _, - flags.bits(), - std::ptr::null_mut(), - make_create_wal(Box::leak(Box::new(create_wal))), - ); - - if rc == 0 { - rc = rusqlite::ffi::sqlite3_wal_autocheckpoint(db, auto_checkpoint as _); - } - + let rc = rusqlite::ffi::sqlite3_wal_autocheckpoint(conn.handle(), auto_checkpoint as _); if rc != 0 { - rusqlite::ffi::sqlite3_close(db); return Err(rusqlite::Error::SqliteFailure( - rusqlite::ffi::Error::new(rc), - None, + rusqlite::ffi::Error::new(rc), + Some("failed to set auto_checkpoint".into()), )); } + } - assert!(!db.is_null()); - }; - - let conn = unsafe { rusqlite::Connection::from_handle_owned(db)? }; - conn.busy_timeout(Duration::from_millis(5000))?; - - Ok(Connection { conn, _ctx }) + Ok(Connection { conn, _pth: PhantomData }) } /// Returns the raw sqlite handle diff --git a/libsql-sys-tmp/src/wal.rs b/libsql-sys-tmp/src/wal.rs index 9cd6b30810..0088e468e7 100644 --- a/libsql-sys-tmp/src/wal.rs +++ b/libsql-sys-tmp/src/wal.rs @@ -17,7 +17,7 @@ pub trait CreateWal { fn destroy_log(&self, vfs: &mut Vfs, db_path: &CStr) -> Result<()>; fn log_exists(&self, vfs: &mut Vfs, db_path: &CStr) -> Result; - fn destroy(self) { } + fn destroy(self) where Self: Sized; } pub unsafe extern "C" fn open( @@ -88,7 +88,7 @@ pub unsafe extern "C" fn log_exists( let mut vfs = Vfs { vfs }; match this.log_exists(&mut vfs, db_path) { Ok(res) => { - *exists = res; + *exists = res as _; SQLITE_OK }, Err(code) => code.extended_code, @@ -97,28 +97,29 @@ pub unsafe extern "C" fn log_exists( pub unsafe extern "C" fn destroy_create_wal( create_wal: *mut create_wal_impl, -) -> c_int { +) { let this = Box::from_raw(create_wal as *mut T); this.destroy(); } -pub(crate) fn make_create_wal(create_wal: *mut T) -> libsql_create_wal { +pub(crate) fn make_create_wal(create_wal: T) -> libsql_create_wal { libsql_create_wal { - bUsesShm: create_wal.use_shared_memory(), - xOpen: Some(open), - xClose: Some(close), - xLogDestroy: Some(log_destroy), - xLogExists: Some(log_exists), - xDestroy: Some(destroy_create_wal), - pData: create_wal as *mut _, + bUsesShm: create_wal.use_shared_memory() as _, + xOpen: Some(open::), + xClose: Some(close::), + xLogDestroy: Some(log_destroy::), + xLogExists: Some(log_exists::), + xDestroy: Some(destroy_create_wal::), + pData: Box::into_raw(Box::new(create_wal)) as *mut _, } } -#[repr(c_int)] -enum CheckpointMode { - Passive, - Full, - Restart, +#[repr(i32)] +pub enum CheckpointMode { + Passive = SQLITE_CHECKPOINT_PASSIVE, + Full = SQLITE_CHECKPOINT_FULL, + Restart = SQLITE_CHECKPOINT_RESTART, + Truncate = SQLITE_CHECKPOINT_TRUNCATE, } pub trait Wal { @@ -126,7 +127,7 @@ pub trait Wal { fn limit(&mut self, size: i64); /// start a read transaction. Returns whether the in-memory page cache should be invalidated. fn begin_read_txn(&mut self) -> Result; - fn end_read_txn(&mut self) -> Result; + fn end_read_txn(&mut self); /// locate the frame containing page `page_no` fn find_frame(&mut self, page_no: u32) -> Result; @@ -140,7 +141,7 @@ pub trait Wal { fn undo(&mut self, cb: Option i32>, cb_ctx: *mut c_void) -> Result<()>; - fn savepoint(&mut self, rollback_data: &mut [u32]) -> Result<()>; + fn savepoint(&mut self, rollback_data: &mut [u32]); fn savepoin_undo(&mut self, rollback_data: &mut [u32]) -> Result<()>; fn insert_frames(&mut self, page_size: c_int, page_headers: *mut PgHdr, size_after: u32, is_commit: bool, sync_flags: c_int) -> Result<()>; @@ -158,7 +159,7 @@ pub trait Wal { fn exclusive_mode(&mut self, op: c_int) -> Result<()>; fn uses_heap_memory(&self) -> bool; - fn set_db(&mut self, db: *mut sqlite3) -> Result<()>; + fn set_db(&mut self, db: *mut sqlite3); /// Return the value to pass to a sqlite3_wal_hook callback, the /// number of frames in the WAL at the point of the last commit since @@ -172,7 +173,7 @@ pub trait BusyHandler { fn handle_busy(&mut self) -> bool; } -struct Vfs { +pub struct Vfs { vfs: *mut sqlite3_vfs, } @@ -188,22 +189,22 @@ fn construct_libsql_wal(wal: *mut W) -> libsql_wal { libsql_wal { methods: libsql_wal_methods { iVersion: 1, - xLimit: Some(limit), - xBeginReadTransaction: Some(begin_read_transaction), - xEndReadTransaction: Some(end_read_transaction), - xFindFrame: Some(find_frame), - xReadFrame: Some(read_frame), - xDbsize: Some(db_size), - xBeginWriteTransaction: Some(begin_write_transaction), - xEndWriteTransaction: Some(end_write_transaction), - xUndo: Some(undo), - xSavepoint: Some(savepoint), - xSavepointUndo: Some(savepoint_undo), - xFrames: Some(frames), - xCheckpoint: Some(checkpoint), - xCallback: Some(callback), - xExclusiveMode: Some(exclusive_mode), - xHeapMemory: Some(heap_memory), + xLimit: Some(limit::), + xBeginReadTransaction: Some(begin_read_transaction::), + xEndReadTransaction: Some(end_read_transaction::), + xFindFrame: Some(find_frame::), + xReadFrame: Some(read_frame::), + xDbsize: Some(db_size::), + xBeginWriteTransaction: Some(begin_write_transaction::), + xEndWriteTransaction: Some(end_write_transaction::), + xUndo: Some(undo::), + xSavepoint: Some(savepoint::), + xSavepointUndo: Some(savepoint_undo::), + xFrames: Some(frames::), + xCheckpoint: Some(checkpoint::), + xCallback: Some(callback::), + xExclusiveMode: Some(exclusive_mode::), + xHeapMemory: Some(heap_memory::), xSnapshotGet: None, xSnapshotOpen: None, xSnapshotRecover: None, @@ -212,7 +213,7 @@ fn construct_libsql_wal(wal: *mut W) -> libsql_wal { xFramesize: None, xFile: None, // TODO: not all wal are single file based xWriteLock: None, - xDb: Some(db), + xDb: Some(db::), }, pData: wal as *mut _, } @@ -236,26 +237,24 @@ pub unsafe extern "C" fn begin_read_transaction(wal: *mut wal_impl, chan } } -pub unsafe extern "C" fn end_read_transaction(wal: *mut Wal) { +pub unsafe extern "C" fn end_read_transaction(wal: *mut wal_impl) { let this = &mut (*(wal as *mut T)); - this.end_read_txn() + this.end_read_txn(); } -pub unsafe extern "C" fn find_frame(wal: *mut Wal, pgno: u32, frame: *mut u32) -> c_int { +pub unsafe extern "C" fn find_frame(wal: *mut wal_impl, pgno: u32, frame: *mut u32) -> c_int { let this = &mut (*(wal as *mut T)); match this.find_frame(pgno) { Ok(fno) => { *frame = fno; - SQLITE_OK; - } - Err(code) => { - code.extended_code; + SQLITE_OK } + Err(code) => code.extended_code, } } pub unsafe extern "C" fn read_frame( - wal: *mut Wal, + wal: *mut wal_impl, frame: u32, n_out: c_int, p_out: *mut u8, @@ -268,12 +267,12 @@ pub unsafe extern "C" fn read_frame( } } -pub unsafe extern "C" fn db_size(wal: *mut Wal) -> u32 { +pub unsafe extern "C" fn db_size(wal: *mut wal_impl) -> u32 { let this = &mut (*(wal as *mut T)); this.db_size() } -pub unsafe extern "C" fn begin_write_transaction(wal: *mut Wal) -> i32 { +pub unsafe extern "C" fn begin_write_transaction(wal: *mut wal_impl) -> i32 { let this = &mut (*(wal as *mut T)); match this.begin_write_txn() { Ok(_) => SQLITE_OK, @@ -281,7 +280,7 @@ pub unsafe extern "C" fn begin_write_transaction(wal: *mut Wal) -> i32 { } } -pub unsafe extern "C" fn end_write_transaction(wal: *mut Wal) -> i32 { +pub unsafe extern "C" fn end_write_transaction(wal: *mut wal_impl) -> i32 { let this = &mut (*(wal as *mut T)); match this.end_write_txn() { Ok(_) => SQLITE_OK, @@ -290,7 +289,7 @@ pub unsafe extern "C" fn end_write_transaction(wal: *mut Wal) -> i32 { } pub unsafe extern "C" fn undo( - wal: *mut Wal, + wal: *mut wal_impl, func: Option i32>, undo_ctx: *mut c_void, ) -> i32 { @@ -301,15 +300,15 @@ pub unsafe extern "C" fn undo( } } -pub unsafe extern "C" fn savepoint(wal: *mut Wal, wal_data: *mut u32) { +pub unsafe extern "C" fn savepoint(wal: *mut wal_impl, wal_data: *mut u32) { let this = &mut (*(wal as *mut T)); - let data = std::slice_from_raw_parts_mut(wal_data, WAL_SAVEPOINT_NDATA); + let data = std::slice::from_raw_parts_mut(wal_data, WAL_SAVEPOINT_NDATA as usize); this.savepoint(data); } -pub unsafe extern "C" fn savepoint_undo(wal: *mut Wal, wal_data: *mut u32) -> i32 { +pub unsafe extern "C" fn savepoint_undo(wal: *mut wal_impl, wal_data: *mut u32) -> i32 { let this = &mut (*(wal as *mut T)); - let data = std::slice_from_raw_parts_mut(wal_data, WAL_SAVEPOINT_NDATA); + let data = std::slice::from_raw_parts_mut(wal_data, WAL_SAVEPOINT_NDATA as usize); match this.savepoin_undo(data) { Ok(_) => SQLITE_OK, Err(code) => code.extended_code, @@ -317,7 +316,7 @@ pub unsafe extern "C" fn savepoint_undo(wal: *mut Wal, wal_data: *mut u3 } pub unsafe extern "C" fn frames( - wal: *mut Wal, + wal: *mut wal_impl, page_size: c_int, page_headers: *mut PgHdr, size_after: u32, @@ -325,7 +324,7 @@ pub unsafe extern "C" fn frames( sync_flags: c_int, ) -> c_int { let this = &mut (*(wal as *mut T)); - match this.insert_frames(page_size, page_headers, size_after, is_commit as bool, sync_flags) { + match this.insert_frames(page_size, page_headers, size_after, is_commit != 0, sync_flags) { Ok(_) => SQLITE_OK, Err(code) => code.extended_code, } @@ -333,7 +332,7 @@ pub unsafe extern "C" fn frames( #[tracing::instrument(skip(wal, db))] pub unsafe extern "C" fn checkpoint( - wal: *mut Wal, + wal: *mut wal_impl, db: *mut rusqlite::ffi::sqlite3, emode: c_int, busy_handler: Option c_int>, @@ -341,8 +340,8 @@ pub unsafe extern "C" fn checkpoint( sync_flags: c_int, n_buf: c_int, z_buf: *mut u8, - frames_in_wal: *mut c_int, - backfilled_frames: *mut c_int, + frames_in_wal_out: *mut c_int, + backfilled_frames_out: *mut c_int, ) -> i32 { let this = &mut (*(wal as *mut T)); struct SqliteBusyHandler { @@ -352,38 +351,51 @@ pub unsafe extern "C" fn checkpoint( impl BusyHandler for SqliteBusyHandler { fn handle_busy(&mut self) -> bool { - unsafe { (self.f)(self.data) as bool } + unsafe { (self.f)(self.data) != 0 } } } - let busy_handler = busy_handler.map(|f| SqliteBusyHandler { data: busy_arg, f }); - + let mut busy_handler = busy_handler.map(|f| SqliteBusyHandler { data: busy_arg, f }); let buf = std::slice::from_raw_parts_mut(z_buf, n_buf as usize); - match this.checkpoint(db, mode, busy_handler.as_mut(), sync_flags, buf) { + + let mode = match emode { + e if e == SQLITE_CHECKPOINT_TRUNCATE => CheckpointMode::Truncate, + e if e == SQLITE_CHECKPOINT_FULL => CheckpointMode::Full, + e if e == SQLITE_CHECKPOINT_PASSIVE => CheckpointMode::Passive, + e if e == SQLITE_CHECKPOINT_RESTART => CheckpointMode::Restart, + _ => panic!("invalid checkpoint mode"), + }; + + match this.checkpoint(db, mode, busy_handler.as_mut(), sync_flags as _, buf) { Ok((frames_in_wal, backfilled_frames)) => { + *frames_in_wal_out = frames_in_wal as _; + *backfilled_frames_out = backfilled_frames as _; SQLITE_OK }, Err(code) => code.extended_code, } } -pub unsafe extern "C" fn callback(wal: *mut Wal) -> i32 { +pub unsafe extern "C" fn callback(wal: *mut wal_impl) -> c_int { let this = &mut (*(wal as *mut T)); this.callback() } -pub unsafe extern "C" fn exclusive_mode(wal: *mut Wal, op: c_int) -> c_int { +pub unsafe extern "C" fn exclusive_mode(wal: *mut wal_impl, op: c_int) -> c_int { let this = &mut (*(wal as *mut T)); - this.exclusive_mode(op) + match this.exclusive_mode(op) { + Ok(_) => SQLITE_OK, + Err(code) => code.extended_code + } } -pub unsafe extern "C" fn heap_memory(wal: *mut Wal) -> i32 { +pub unsafe extern "C" fn heap_memory(wal: *mut wal_impl) -> c_int { let this = &mut (*(wal as *mut T)); - this.uses_heap_memory() as i32 + this.uses_heap_memory() as _ } -pub unsafe extern "C" fn db(wal: *mut Wal, db: *mut rusqlite::ffi::sqlite3) { +pub unsafe extern "C" fn db(wal: *mut wal_impl, db: *mut rusqlite::ffi::sqlite3) { let this = &mut (*(wal as *mut T)); - this.set_db(db) + this.set_db(db); } diff --git a/libsql-sys-tmp/src/wal_hook.rs b/libsql-sys-tmp/src/wal_hook.rs deleted file mode 100644 index 11bf57208a..0000000000 --- a/libsql-sys-tmp/src/wal_hook.rs +++ /dev/null @@ -1,478 +0,0 @@ -#![allow(clippy::not_unsafe_ptr_arg_deref)] -use std::{ - ffi::{c_char, c_int, c_void, CStr}, - marker::PhantomData, - panic::{catch_unwind, resume_unwind}, path::Path, -}; - -use crate::ffi::{libsql_wal_methods, sqlite3, sqlite3_file, sqlite3_vfs, types::*, PgHdr, Wal}; -use crate::get_orig_wal_methods; - -/// This macro handles the registering of a WalHook with the process's sqlite. It first instantiate a `WalMethodsHook` -/// to a stable location in memory, and then call `libsql_wal_methods_register` with the WAL methods. -/// -/// The methods are never unregistered, since they're expected to live for the entirety of the program. -#[macro_export] -macro_rules! init_static_wal_method { - ($name:ident, $ty:path) => { - pub static $name: $crate::Lazy<&'static $crate::WalMethodsHook<$ty>> = - once_cell::sync::Lazy::new(|| { - // we need a 'static address before we can register the methods. - static METHODS: $crate::Lazy<$crate::WalMethodsHook<$ty>> = - $crate::Lazy::new(|| $crate::WalMethodsHook::<$ty>::new()); - - let ret = unsafe { - $crate::ffi::libsql_wal_methods_register(METHODS.as_wal_methods_ptr() as *mut _) - }; - - assert!( - ret == 0, - "failed to register wal methods for {}", - stringify!($ty) - ); - - &METHODS - }); - }; -} - -/// The `WalHook` trait allows to intercept WAL method call. -/// -/// All the methods in this trait have the following format: - arguments to the WAL method - -/// function pointer to the wrapped WAL method -/// -/// The default implementations for this trait methods is to transparently call the wrapped methods -/// with the passed arguments -/// -/// # Safety -/// The implementer is responsible for calling the orig method with valid arguments. -pub unsafe trait WalHook { - type Context; - - fn name() -> &'static CStr; - /// Intercept `xFrame` call. `orig` is the function pointer to the underlying wal method. - /// The default implementation of this trait simply calls orig with the other passed arguments. - #[allow(clippy::too_many_arguments)] - fn on_frames( - wal: &mut Wal, - page_size: c_int, - page_headers: *mut PgHdr, - size_after: u32, - is_commit: c_int, - sync_flags: c_int, - orig: XWalFrameFn, - ) -> c_int { - unsafe { - (orig)( - wal, - page_size, - page_headers, - size_after, - is_commit, - sync_flags, - ) - } - } - - /// Intercept `xUndo` call. `orig` is the function pointer to the underlying wal method. - /// The default implementation of this trait simply calls orig with the other passed arguments. - fn on_undo( - wal: &mut Wal, - func: Option i32>, - undo_ctx: *mut c_void, - orig: XWalUndoFn, - ) -> i32 { - unsafe { orig(wal, func, undo_ctx) } - } - - fn wal_extract_ctx(wal: &mut Wal) -> &mut Self::Context { - let ctx_ptr = wal.pMethodsData as *mut Self::Context; - assert!(!ctx_ptr.is_null(), "missing wal context"); - unsafe { &mut *ctx_ptr } - } - - fn on_savepoint_undo(wal: &mut Wal, wal_data: *mut u32, orig: XWalSavePointUndoFn) -> i32 { - unsafe { orig(wal, wal_data) } - } - - #[allow(clippy::too_many_arguments)] - fn on_checkpoint( - wal: &mut Wal, - db: *mut sqlite3, - emode: i32, - busy_handler: Option i32>, - busy_arg: *mut c_void, - sync_flags: i32, - n_buf: i32, - z_buf: *mut u8, - frames_in_wal: *mut i32, - backfilled_frames: *mut i32, - orig: XWalCheckpointFn, - ) -> i32 { - unsafe { - orig( - wal, - db, - emode, - busy_handler, - busy_arg, - sync_flags, - n_buf, - z_buf, - frames_in_wal, - backfilled_frames, - ) - } - } -} - -init_static_wal_method!(TRANSPARENT_METHODS, TransparentMethods); - -/// Wal implemementation that just proxies calls to the wrapped WAL methods implementation -#[derive(Debug)] -pub enum TransparentMethods {} - -unsafe impl WalHook for TransparentMethods { - type Context = (); - - fn name() -> &'static CStr { - CStr::from_bytes_with_nul(b"transparent\0").unwrap() - } -} - -impl Default for WalMethodsHook { - fn default() -> Self { - Self::new() - } -} - -impl WalMethodsHook { - pub fn new() -> Self { - let default_methods = get_orig_wal_methods().expect("failed to get original WAL methods"); - - WalMethodsHook { - methods: libsql_wal_methods { - iVersion: 1, - xOpen: Some(xOpen::), - xClose: Some(xClose::), - xLimit: Some(xLimit::), - xBeginReadTransaction: Some(xBeginReadTransaction::), - xEndReadTransaction: Some(xEndReadTransaction::), - xFindFrame: Some(xFindFrame::), - xReadFrame: Some(xReadFrame::), - xDbsize: Some(xDbsize::), - xBeginWriteTransaction: Some(xBeginWriteTransaction::), - xEndWriteTransaction: Some(xEndWriteTransaction::), - xUndo: Some(xUndo::), - xSavepoint: Some(xSavepoint::), - xSavepointUndo: Some(xSavepointUndo::), - xFrames: Some(xFrames::), - xCheckpoint: Some(xCheckpoint::), - xCallback: Some(xCallback::), - xExclusiveMode: Some(xExclusiveMode::), - xHeapMemory: Some(xHeapMemory::), - xSnapshotGet: None, - xSnapshotOpen: None, - xSnapshotRecover: None, - xSnapshotCheck: None, - xSnapshotUnlock: None, - xFramesize: None, - xFile: Some(xFile::), - xWriteLock: None, - xDb: Some(xDb::), - xPathnameLen: Some(xPathnameLen::), - xGetWalPathname: Some(xGetPathname::), - xPreMainDbOpen: Some(xPreMainDbOpen::), - zName: T::name().as_ptr(), - bUsesShm: 0, - pNext: std::ptr::null_mut(), - }, - underlying_methods: default_methods, - _pth: PhantomData, - } - } - - pub fn as_wal_methods_ptr(&self) -> *const libsql_wal_methods { - self as *const _ as *mut _ - } -} - -macro_rules! catch_panic { - ($name:literal, { $($body:tt)* }) => { - { - let ret = catch_unwind(move || { - $($body)* - }); - - match ret { - Ok(x) => x, - Err(e) => { - let error = if let Some(s) = e.downcast_ref::() { - s.as_str() - } else if let Some(s) = e.downcast_ref::<&str>() { - s - } else { - "unknown" - }; - let bt = std::backtrace::Backtrace::force_capture(); - tracing::error!("panic in call to {}: {error}:\n{bt}", $name); - resume_unwind(e) - } - } - } - }; -} - -#[allow(non_snake_case)] -pub extern "C" fn xOpen( - vfs: *mut sqlite3_vfs, - db_file: *mut sqlite3_file, - wal_name: *const c_char, - no_shm_mode: i32, - max_size: i64, - methods: *mut libsql_wal_methods, - wal: *mut *mut Wal, -) -> i32 { - tracing::debug!("Opening WAL {}", unsafe { - std::ffi::CStr::from_ptr(wal_name).to_str().unwrap() - }); - let ref_methods = unsafe { &*(methods as *mut WalMethodsHook) }; - let origxOpen = unsafe { (*ref_methods.underlying_methods).xOpen.unwrap() }; - unsafe { (origxOpen)(vfs, db_file, wal_name, no_shm_mode, max_size, methods, wal) } -} - -fn get_orig_methods(wal: &mut Wal) -> &libsql_wal_methods { - let methods = get_methods::(wal); - assert!(!methods.underlying_methods.is_null()); - unsafe { &*methods.underlying_methods } -} - -fn get_methods(wal: &mut Wal) -> &mut WalMethodsHook { - assert!(!wal.pMethods.is_null()); - unsafe { &mut *(wal.pMethods as *mut _ as *mut WalMethodsHook) } -} - -#[allow(non_snake_case)] -pub extern "C" fn xClose( - wal: *mut Wal, - db: *mut rusqlite::ffi::sqlite3, - sync_flags: i32, - n_buf: c_int, - z_buf: *mut u8, -) -> c_int { - let orig_methods = unsafe { get_orig_methods::(&mut *wal) }; - unsafe { (orig_methods.xClose.unwrap())(wal, db, sync_flags, n_buf, z_buf) } -} - -#[allow(non_snake_case)] -pub extern "C" fn xLimit(wal: *mut Wal, limit: i64) { - let orig_methods = unsafe { get_orig_methods::(&mut *wal) }; - unsafe { (orig_methods.xLimit.unwrap())(wal, limit) } -} - -#[allow(non_snake_case)] -pub extern "C" fn xBeginReadTransaction(wal: *mut Wal, changed: *mut i32) -> i32 { - let orig_methods = unsafe { get_orig_methods::(&mut *wal) }; - unsafe { (orig_methods.xBeginReadTransaction.unwrap())(wal, changed) } -} - -#[allow(non_snake_case)] -pub extern "C" fn xEndReadTransaction(wal: *mut Wal) { - let orig_methods = unsafe { get_orig_methods::(&mut *wal) }; - unsafe { (orig_methods.xEndReadTransaction.unwrap())(wal) } -} - -#[allow(non_snake_case)] -pub extern "C" fn xFindFrame(wal: *mut Wal, pgno: u32, frame: *mut u32) -> c_int { - let orig_methods = unsafe { get_orig_methods::(&mut *wal) }; - unsafe { (orig_methods.xFindFrame.unwrap())(wal, pgno, frame) } -} - -#[allow(non_snake_case)] -pub extern "C" fn xReadFrame( - wal: *mut Wal, - frame: u32, - n_out: c_int, - p_out: *mut u8, -) -> i32 { - let orig_methods = unsafe { get_orig_methods::(&mut *wal) }; - unsafe { (orig_methods.xReadFrame.unwrap())(wal, frame, n_out, p_out) } -} - -#[allow(non_snake_case)] -pub extern "C" fn xDbsize(wal: *mut Wal) -> u32 { - let orig_methods = unsafe { get_orig_methods::(&mut *wal) }; - unsafe { (orig_methods.xDbsize.unwrap())(wal) } -} - -#[allow(non_snake_case)] -pub extern "C" fn xBeginWriteTransaction(wal: *mut Wal) -> i32 { - let orig_methods = unsafe { get_orig_methods::(&mut *wal) }; - unsafe { (orig_methods.xBeginWriteTransaction.unwrap())(wal) } -} - -#[allow(non_snake_case)] -pub extern "C" fn xEndWriteTransaction(wal: *mut Wal) -> i32 { - let orig_methods = unsafe { get_orig_methods::(&mut *wal) }; - unsafe { (orig_methods.xEndWriteTransaction.unwrap())(wal) } -} - -#[allow(non_snake_case)] -pub extern "C" fn xUndo( - wal: *mut Wal, - func: Option i32>, - undo_ctx: *mut c_void, -) -> i32 { - catch_panic!("xUndo", { - assert!(!wal.is_null()); - let wal = unsafe { &mut *wal }; - let orig_methods = get_orig_methods::(wal); - let orig_xundo = orig_methods.xUndo.unwrap(); - T::on_undo(wal, func, undo_ctx, orig_xundo) - }) -} - -#[allow(non_snake_case)] -pub extern "C" fn xSavepoint(wal: *mut Wal, wal_data: *mut u32) { - let orig_methods = unsafe { get_orig_methods::(&mut *wal) }; - unsafe { (orig_methods.xSavepoint.unwrap())(wal, wal_data) } -} - -#[allow(non_snake_case)] -pub extern "C" fn xSavepointUndo(wal: *mut Wal, wal_data: *mut u32) -> i32 { - catch_panic!("xSavepointUndo", { - let wal = unsafe { &mut *wal }; - let orig_methods = get_orig_methods::(wal); - let orig_xsavepointundo = orig_methods.xSavepointUndo.unwrap(); - T::on_savepoint_undo(wal, wal_data, orig_xsavepointundo) - }) -} - -#[allow(non_snake_case)] -pub extern "C" fn xFrames( - wal: *mut Wal, - page_size: c_int, - page_headers: *mut PgHdr, - size_after: u32, - is_commit: c_int, - sync_flags: c_int, -) -> c_int { - catch_panic!("xFrames", { - assert!(!wal.is_null()); - let wal = unsafe { &mut *wal }; - let orig_methods = get_orig_methods::(wal); - let orig_xframe = orig_methods.xFrames.unwrap(); - - T::on_frames( - wal, - page_size, - page_headers, - size_after, - is_commit, - sync_flags, - orig_xframe, - ) - }) -} - -#[tracing::instrument(skip(wal, db))] -#[allow(non_snake_case)] -pub extern "C" fn xCheckpoint( - wal: *mut Wal, - db: *mut rusqlite::ffi::sqlite3, - emode: c_int, - busy_handler: Option c_int>, - busy_arg: *mut c_void, - sync_flags: c_int, - n_buf: c_int, - z_buf: *mut u8, - frames_in_wal: *mut c_int, - backfilled_frames: *mut c_int, -) -> i32 { - catch_panic!("xCheckpoint", { - let wal = unsafe { &mut *wal }; - let orig_methods = get_orig_methods::(wal); - let orig_xcheckpoint = orig_methods.xCheckpoint.unwrap(); - T::on_checkpoint( - wal, - db, - emode, - busy_handler, - busy_arg, - sync_flags, - n_buf, - z_buf, - frames_in_wal, - backfilled_frames, - orig_xcheckpoint, - ) - }) -} - -#[allow(non_snake_case)] -pub extern "C" fn xCallback(wal: *mut Wal) -> i32 { - let orig_methods = unsafe { get_orig_methods::(&mut *wal) }; - unsafe { (orig_methods.xCallback.unwrap())(wal) } -} - -#[allow(non_snake_case)] -pub extern "C" fn xExclusiveMode(wal: *mut Wal, op: c_int) -> c_int { - let orig_methods = unsafe { get_orig_methods::(&mut *wal) }; - unsafe { (orig_methods.xExclusiveMode.unwrap())(wal, op) } -} - -#[allow(non_snake_case)] -pub extern "C" fn xHeapMemory(wal: *mut Wal) -> i32 { - let orig_methods = unsafe { get_orig_methods::(&mut *wal) }; - unsafe { (orig_methods.xHeapMemory.unwrap())(wal) } -} - -#[allow(non_snake_case)] -pub extern "C" fn xFile(wal: *mut Wal) -> *mut sqlite3_file { - let orig_methods = unsafe { get_orig_methods::(&mut *wal) }; - unsafe { (orig_methods.xFile.unwrap())(wal) } -} - -#[allow(non_snake_case)] -pub extern "C" fn xDb(wal: *mut Wal, db: *mut rusqlite::ffi::sqlite3) { - let orig_methods = unsafe { get_orig_methods::(&mut *wal) }; - unsafe { (orig_methods.xDb.unwrap())(wal, db) } -} - -#[allow(non_snake_case)] -pub extern "C" fn xPathnameLen(orig_len: i32) -> i32 { - orig_len + 4 -} - -#[allow(non_snake_case)] -pub extern "C" fn xGetPathname(buf: *mut c_char, orig: *const c_char, orig_len: c_int) { - unsafe { std::ptr::copy(orig, buf, orig_len as usize) } - unsafe { - std::ptr::copy( - "-wal".as_ptr(), - (buf as *mut u8).offset(orig_len as isize), - 4, - ) - } -} - -#[allow(non_snake_case)] -pub extern "C" fn xPreMainDbOpen( - methods: *mut libsql_wal_methods, - path: *const c_char, -) -> i32 { - let orig_methods = unsafe { &*(*(methods as *mut WalMethodsHook)).underlying_methods }; - unsafe { (orig_methods.xPreMainDbOpen.unwrap())(methods, path) } -} - -unsafe impl Send for WalMethodsHook {} -unsafe impl Sync for WalMethodsHook {} - -#[repr(C)] -#[allow(non_snake_case)] -pub struct WalMethodsHook { - pub methods: libsql_wal_methods, - // user data - underlying_methods: *mut libsql_wal_methods, - _pth: PhantomData, -} diff --git a/libsql-sys/Cargo.toml b/libsql-sys/Cargo.toml index ba3ee3f5aa..2a7ec09ba9 100644 --- a/libsql-sys/Cargo.toml +++ b/libsql-sys/Cargo.toml @@ -21,4 +21,8 @@ bindgen = "0.66.1" cc = "1.0" [features] +default = ["ffi", "api"] session = [] +ffi = [] +api = [] + diff --git a/libsql-sys/src/lib.rs b/libsql-sys/src/lib.rs index 7f2dc51ac8..c5cd962138 100644 --- a/libsql-sys/src/lib.rs +++ b/libsql-sys/src/lib.rs @@ -1,6 +1,7 @@ #[allow(clippy::all)] #[allow(non_snake_case)] #[allow(non_camel_case_types)] +#[cfg(feature = "ffi")] pub mod ffi { #![allow(non_snake_case, non_camel_case_types)] #![cfg_attr(test, allow(deref_nullptr))] // https://github.com/rust-lang/rust-bindgen/issues/2066 @@ -38,16 +39,25 @@ pub mod ffi { } } +#[cfg(feature = "api")] pub mod connection; pub mod error; +#[cfg(feature = "api")] pub mod statement; +#[cfg(feature = "api")] pub mod types; +#[cfg(feature = "api")] pub mod value; -pub mod wal_hook; +// pub mod wal_hook; +#[cfg(feature = "api")] pub use connection::Connection; +#[cfg(feature = "api")] pub use error::{Error, Result}; +#[cfg(feature = "api")] pub use statement::{prepare_stmt, Statement}; +#[cfg(feature = "api")] pub use types::*; +#[cfg(feature = "api")] pub use value::{Value, ValueType}; -pub use wal_hook::{WalHook, WalMethodsHook}; +// pub use wal_hook::{WalHook, WalMethodsHook}; diff --git a/vendored/rusqlite/Cargo.toml b/vendored/rusqlite/Cargo.toml index 955fa9af80..d64e47be84 100644 --- a/vendored/rusqlite/Cargo.toml +++ b/vendored/rusqlite/Cargo.toml @@ -107,7 +107,7 @@ fallible-iterator = "0.2" fallible-streaming-iterator = "0.1" uuid = { version = "1.0", optional = true } smallvec = "1.6.1" -libsql-sys = { path = "../../libsql-sys/" } +libsql-sys = { path = "../../libsql-sys/", default-features = false, features = ["ffi"] } [dev-dependencies] doc-comment = "0.3" diff --git a/vendored/rusqlite/src/inner_connection.rs b/vendored/rusqlite/src/inner_connection.rs index 7a4563ef0d..89a0c031a0 100644 --- a/vendored/rusqlite/src/inner_connection.rs +++ b/vendored/rusqlite/src/inner_connection.rs @@ -64,7 +64,7 @@ impl InnerConnection { c_path: &CStr, flags: OpenFlags, vfs: Option<&CStr>, - #[cfg(feature = "libsql-experimental")] wal: Option<&CStr>, + #[cfg(feature = "libsql-experimental")] create_wal: Option, ) -> Result { ensure_safe_sqlite_threading_mode()?; @@ -88,17 +88,20 @@ impl InnerConnection { None => ptr::null(), }; - #[cfg(feature = "libsql-experimental")] - let z_wal = wal - .map(|c_wal| c_wal.as_ptr()) - .unwrap_or_else(std::ptr::null); - unsafe { let mut db: *mut ffi::sqlite3 = ptr::null_mut(); #[cfg(not(feature = "libsql-experimental"))] let r = ffi::sqlite3_open_v2(c_path.as_ptr(), &mut db, flags.bits(), z_vfs); #[cfg(feature = "libsql-experimental")] - let r = ffi::libsql_open(c_path.as_ptr(), &mut db, flags.bits(), z_vfs, z_wal); + let r = match create_wal { + Some(create_wal) => { + ffi::libsql_open(c_path.as_ptr(), &mut db, flags.bits(), z_vfs, create_wal) + } + None => { + ffi::sqlite3_open_v2(c_path.as_ptr(), &mut db, flags.bits(), z_vfs) + } + }; + if r != ffi::SQLITE_OK { let e = if db.is_null() { error_from_sqlite_code(r, Some(c_path.to_string_lossy().to_string())) diff --git a/vendored/rusqlite/src/lib.rs b/vendored/rusqlite/src/lib.rs index afd5cb55d4..654420a92c 100644 --- a/vendored/rusqlite/src/lib.rs +++ b/vendored/rusqlite/src/lib.rs @@ -475,11 +475,11 @@ impl Connection { pub fn open_with_flags_and_wal>( path: P, flags: OpenFlags, - wal: &str, + create_wal: libsql_sys::ffi::libsql_create_wal, ) -> Result { + let c_path = path_to_cstring(path.as_ref())?; - let c_wal = str_to_cstring(wal)?; - InnerConnection::open_with_flags(&c_path, flags, None, Some(&c_wal)).map(|db| Connection { + InnerConnection::open_with_flags(&c_path, flags, None, Some(create_wal)).map(|db| Connection { db: RefCell::new(db), cache: StatementCache::with_capacity(STATEMENT_CACHE_DEFAULT_CAPACITY), }) @@ -499,12 +499,11 @@ impl Connection { path: P, flags: OpenFlags, vfs: &str, - wal: &str, + create_wal: ffi::libsql_create_wal, ) -> Result { let c_path = path_to_cstring(path.as_ref())?; let c_vfs = str_to_cstring(vfs)?; - let c_wal = str_to_cstring(wal)?; - InnerConnection::open_with_flags(&c_path, flags, Some(&c_vfs), Some(&c_wal)).map(|db| { + InnerConnection::open_with_flags(&c_path, flags, Some(&c_vfs), Some(create_wal)).map(|db| { Connection { db: RefCell::new(db), cache: StatementCache::with_capacity(STATEMENT_CACHE_DEFAULT_CAPACITY),