From 97c6e88bfdb9aaba55607c0eed7ad87c25d4ebeb Mon Sep 17 00:00:00 2001 From: gwenn Date: Mon, 11 Nov 2024 18:02:54 +0100 Subject: [PATCH] Introduce StmtRef / ConnRef --- src/inner_connection.rs | 21 ++++++++++- src/lib.rs | 11 +----- src/raw_statement.rs | 19 ++++++++-- src/trace.rs | 81 +++++++++++++++++++++++++++++++++++------ 4 files changed, 106 insertions(+), 26 deletions(-) diff --git a/src/inner_connection.rs b/src/inner_connection.rs index bc7f72f..f1269c6 100644 --- a/src/inner_connection.rs +++ b/src/inner_connection.rs @@ -309,7 +309,7 @@ impl InnerConnection { #[inline] pub fn is_autocommit(&self) -> bool { - unsafe { ffi::sqlite3_get_autocommit(self.db()) != 0 } + unsafe { get_autocommit(self.db()) } } pub fn is_busy(&self) -> bool { @@ -393,6 +393,25 @@ impl InnerConnection { } } +#[inline] +pub(crate) unsafe fn get_autocommit(ptr: *mut ffi::sqlite3) -> bool { + ffi::sqlite3_get_autocommit(ptr) != 0 +} + +#[inline] +pub(crate) unsafe fn db_filename( + ptr: *mut ffi::sqlite3, + db_name: crate::DatabaseName<'_>, +) -> Option<&str> { + let db_name = db_name.as_cstr().unwrap(); + let db_filename = ffi::sqlite3_db_filename(ptr, db_name.as_ptr()); + if db_filename.is_null() { + None + } else { + CStr::from_ptr(db_filename).to_str().ok() + } +} + impl Drop for InnerConnection { #[expect(unused_must_use)] #[inline] diff --git a/src/lib.rs b/src/lib.rs index 15d8a34..e80da3a 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -644,16 +644,7 @@ impl Connection { /// likely to be more robust. #[inline] pub fn path(&self) -> Option<&str> { - unsafe { - let db = self.handle(); - let db_name = DatabaseName::Main.as_cstr().unwrap(); - let db_filename = ffi::sqlite3_db_filename(db, db_name.as_ptr()); - if db_filename.is_null() { - None - } else { - CStr::from_ptr(db_filename).to_str().ok() - } - } + unsafe { crate::inner_connection::db_filename(self.handle(), DatabaseName::Main) } } /// Attempts to free as much heap memory as possible from the database diff --git a/src/raw_statement.rs b/src/raw_statement.rs index f8078dc..303a50b 100644 --- a/src/raw_statement.rs +++ b/src/raw_statement.rs @@ -204,13 +204,12 @@ impl RawStatement { #[inline] pub(crate) fn expanded_sql(&self) -> Option { - unsafe { SqliteMallocString::from_raw(ffi::sqlite3_expanded_sql(self.ptr)) } + unsafe { expanded_sql(self.ptr) } } #[inline] pub fn get_status(&self, status: StatementStatus, reset: bool) -> i32 { - assert!(!self.ptr.is_null()); - unsafe { ffi::sqlite3_stmt_status(self.ptr, status as i32, reset as i32) } + unsafe { stmt_status(self.ptr, status, reset) } } #[inline] @@ -233,6 +232,20 @@ impl RawStatement { // TODO sqlite3_normalized_sql (https://sqlite.org/c3ref/expanded_sql.html) // 3.27.0 + SQLITE_ENABLE_NORMALIZE } +#[inline] +pub(crate) unsafe fn expanded_sql(ptr: *mut ffi::sqlite3_stmt) -> Option { + SqliteMallocString::from_raw(ffi::sqlite3_expanded_sql(ptr)) +} +#[inline] +pub(crate) unsafe fn stmt_status( + ptr: *mut ffi::sqlite3_stmt, + status: StatementStatus, + reset: bool, +) -> i32 { + assert!(!ptr.is_null()); + ffi::sqlite3_stmt_status(ptr, status as i32, reset as i32) +} + impl Drop for RawStatement { fn drop(&mut self) { self.finalize_(); diff --git a/src/trace.rs b/src/trace.rs index 2ec0ba9..97a22a6 100644 --- a/src/trace.rs +++ b/src/trace.rs @@ -1,6 +1,8 @@ //! Tracing and profiling functions. Error and warning log. +use std::borrow::Cow; use std::ffi::{CStr, CString}; +use std::marker::PhantomData; use std::mem; use std::os::raw::{c_char, c_int, c_uint, c_void}; use std::panic::catch_unwind; @@ -8,7 +10,7 @@ use std::ptr; use std::time::Duration; use super::ffi; -use crate::Connection; +use crate::{Connection, DatabaseName, StatementStatus}; /// Set up the process-wide SQLite error logging callback. /// @@ -86,13 +88,59 @@ bitflags::bitflags! { pub enum TraceEvent<'s> { /// when a prepared statement first begins running and possibly at other times during the execution /// of the prepared statement, such as at the start of each trigger subprogram - Stmt(/*Statement,*/ &'s str), + Stmt(StmtRef<'s>, &'s str), /// when the statement finishes - Profile(/*Statement,*/ Duration), + Profile(StmtRef<'s>, Duration), /// whenever a prepared statement generates a single row of result - Row(/*Statement*/), + Row(StmtRef<'s>), /// when a database connection closes - Close(/*Connection*/), + Close(ConnRef<'s>), +} + +/// Statement reference +pub struct StmtRef<'s> { + ptr: *mut ffi::sqlite3_stmt, + phantom: PhantomData<&'s ()>, +} + +impl StmtRef<'_> { + fn new(ptr: *mut ffi::sqlite3_stmt) -> Self { + StmtRef { + ptr, + phantom: PhantomData, + } + } + /// SQL text + pub fn sql(&self) -> Cow<'_, str> { + unsafe { CStr::from_ptr(ffi::sqlite3_sql(self.ptr)).to_string_lossy() } + } + /// Expanded SQL text + pub fn expanded_sql(&self) -> Option { + unsafe { + crate::raw_statement::expanded_sql(self.ptr).map(|s| s.to_string_lossy().to_string()) + } + } + /// Get the value for one of the status counters for this statement. + pub fn get_status(&self, status: StatementStatus) -> i32 { + unsafe { crate::raw_statement::stmt_status(self.ptr, status, false) } + } +} + +/// Connection reference +pub struct ConnRef<'s> { + ptr: *mut ffi::sqlite3, + phantom: PhantomData<&'s ()>, +} + +impl ConnRef<'_> { + /// Test for auto-commit mode. + pub fn is_autocommit(&self) -> bool { + unsafe { crate::inner_connection::get_autocommit(self.ptr) } + } + /// the path to the database file, if one exists and is known. + pub fn db_filename(&self) -> Option<&str> { + unsafe { crate::inner_connection::db_filename(self.ptr, DatabaseName::Main) } + } } impl Connection { @@ -154,23 +202,32 @@ impl Connection { unsafe extern "C" fn trace_callback( evt: c_uint, ctx: *mut c_void, - _p: *mut c_void, + p: *mut c_void, x: *mut c_void, ) -> c_int { let trace_fn: fn(TraceEvent<'_>) = mem::transmute(ctx); drop(catch_unwind(|| match evt { ffi::SQLITE_TRACE_STMT => { let str = CStr::from_ptr(x as *const c_char).to_string_lossy(); - trace_fn(TraceEvent::Stmt(&str)) + trace_fn(TraceEvent::Stmt( + StmtRef::new(p as *mut ffi::sqlite3_stmt), + &str, + )) } ffi::SQLITE_TRACE_PROFILE => { let ns = *(x as *const i64); - trace_fn(TraceEvent::Profile(Duration::from_nanos( - u64::try_from(ns).unwrap_or_default(), - ))) + trace_fn(TraceEvent::Profile( + StmtRef::new(p as *mut ffi::sqlite3_stmt), + Duration::from_nanos(u64::try_from(ns).unwrap_or_default()), + )) } - ffi::SQLITE_TRACE_ROW => trace_fn(TraceEvent::Row()), - ffi::SQLITE_TRACE_CLOSE => trace_fn(TraceEvent::Close()), + ffi::SQLITE_TRACE_ROW => { + trace_fn(TraceEvent::Row(StmtRef::new(p as *mut ffi::sqlite3_stmt))) + } + ffi::SQLITE_TRACE_CLOSE => trace_fn(TraceEvent::Close(ConnRef { + ptr: p as *mut ffi::sqlite3, + phantom: PhantomData, + })), _ => {} })); // The integer return value from the callback is currently ignored, though this may change in future releases.