diff --git a/libsqlite3-sys/build.rs b/libsqlite3-sys/build.rs index 37cbb79..e3d61d4 100644 --- a/libsqlite3-sys/build.rs +++ b/libsqlite3-sys/build.rs @@ -506,6 +506,7 @@ mod bindings { None } } + fn item_name(&self, original_item_name: &str) -> Option { original_item_name .strip_prefix("sqlite3_index_info_") diff --git a/src/cache.rs b/src/cache.rs index be15268..05ddb87 100644 --- a/src/cache.rs +++ b/src/cache.rs @@ -1,7 +1,7 @@ //! Prepared statements cache for faster execution. use crate::raw_statement::RawStatement; -use crate::{Connection, Result, Statement}; +use crate::{Connection, PrepFlags, Result, Statement}; use hashlink::LruCache; use std::cell::RefCell; use std::ops::{Deref, DerefMut}; @@ -144,7 +144,7 @@ impl StatementCache { let mut cache = self.0.borrow_mut(); let stmt = match cache.remove(trimmed) { Some(raw_stmt) => Ok(Statement::new(conn, raw_stmt)), - None => conn.prepare(trimmed), + None => conn.prepare_with_flags(trimmed, PrepFlags::SQLITE_PREPARE_PERSISTENT), }; stmt.map(|mut stmt| { stmt.stmt.set_statement_cache_key(trimmed); diff --git a/src/config.rs b/src/config.rs index 833d80f..194ee59 100644 --- a/src/config.rs +++ b/src/config.rs @@ -61,7 +61,8 @@ pub enum DbConfig { /// sqlite_master tables) are untainted by malicious content. #[cfg(feature = "modern_sqlite")] SQLITE_DBCONFIG_TRUSTED_SCHEMA = 1017, // 3.31.0 - /// Sets or clears a flag that enables collection of the sqlite3_stmt_scanstatus_v2() statistics + /// Sets or clears a flag that enables collection of the + /// sqlite3_stmt_scanstatus_v2() statistics #[cfg(feature = "modern_sqlite")] SQLITE_DBCONFIG_STMT_SCANSTATUS = 1018, // 3.42.0 /// Changes the default order in which tables and indexes are scanned diff --git a/src/inner_connection.rs b/src/inner_connection.rs index bee0e45..a7487a8 100644 --- a/src/inner_connection.rs +++ b/src/inner_connection.rs @@ -9,7 +9,7 @@ use std::sync::{Arc, Mutex}; use super::ffi; use super::str_for_sqlite; -use super::{Connection, InterruptHandle, OpenFlags, Result}; +use super::{Connection, InterruptHandle, OpenFlags, PrepFlags, Result}; use crate::error::{error_from_handle, error_from_sqlite_code, error_with_offset, Error}; use crate::raw_statement::RawStatement; use crate::statement::Statement; @@ -218,33 +218,24 @@ impl InnerConnection { unsafe { ffi::sqlite3_last_insert_rowid(self.db()) } } - pub fn prepare<'a>(&mut self, conn: &'a Connection, sql: &str) -> Result> { - let mut c_stmt = ptr::null_mut(); + pub fn prepare<'a>( + &mut self, + conn: &'a Connection, + sql: &str, + flags: PrepFlags, + ) -> Result> { + let mut c_stmt: *mut ffi::sqlite3_stmt = ptr::null_mut(); let (c_sql, len, _) = str_for_sqlite(sql.as_bytes())?; - let mut c_tail = ptr::null(); + let mut c_tail: *const c_char = ptr::null(); // TODO sqlite3_prepare_v3 (https://sqlite.org/c3ref/c_prepare_normalize.html) // 3.20.0, #728 #[cfg(not(feature = "unlock_notify"))] - let r = unsafe { - ffi::sqlite3_prepare_v2( - self.db(), - c_sql, - len, - &mut c_stmt as *mut *mut ffi::sqlite3_stmt, - &mut c_tail as *mut *const c_char, - ) - }; + let r = unsafe { self.prepare_(c_sql, len, flags, &mut c_stmt, &mut c_tail) }; #[cfg(feature = "unlock_notify")] let r = unsafe { use crate::unlock_notify; let mut rc; loop { - rc = ffi::sqlite3_prepare_v2( - self.db(), - c_sql, - len, - &mut c_stmt as *mut *mut ffi::sqlite3_stmt, - &mut c_tail as *mut *const c_char, - ); + rc = self.prepare_(c_sql, len, flags, &mut c_stmt, &mut c_tail); if !unlock_notify::is_locked(self.db, rc) { break; } @@ -261,8 +252,6 @@ impl InnerConnection { } // If the input text contains no SQL (if the input is an empty string or a // comment) then *ppStmt is set to NULL. - let c_stmt: *mut ffi::sqlite3_stmt = c_stmt; - let c_tail: *const c_char = c_tail; let tail = if c_tail.is_null() { 0 } else { @@ -278,6 +267,32 @@ impl InnerConnection { })) } + #[inline] + #[cfg(not(feature = "modern_sqlite"))] + unsafe fn prepare_( + &self, + z_sql: *const c_char, + n_byte: c_int, + _: PrepFlags, + pp_stmt: *mut *mut ffi::sqlite3_stmt, + pz_tail: *mut *const c_char, + ) -> c_int { + ffi::sqlite3_prepare_v2(self.db(), z_sql, n_byte, pp_stmt, pz_tail) + } + + #[inline] + #[cfg(feature = "modern_sqlite")] + unsafe fn prepare_( + &self, + z_sql: *const c_char, + n_byte: c_int, + flags: PrepFlags, + pp_stmt: *mut *mut ffi::sqlite3_stmt, + pz_tail: *mut *const c_char, + ) -> c_int { + ffi::sqlite3_prepare_v3(self.db(), z_sql, n_byte, flags.bits(), pp_stmt, pz_tail) + } + #[inline] pub fn changes(&self) -> u64 { #[cfg(not(feature = "modern_sqlite"))] diff --git a/src/lib.rs b/src/lib.rs index 9e8a2d7..11e1ad4 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -711,7 +711,18 @@ impl Connection { /// or if the underlying SQLite call fails. #[inline] pub fn prepare(&self, sql: &str) -> Result> { - self.db.borrow_mut().prepare(self, sql) + self.prepare_with_flags(sql, PrepFlags::default()) + } + + /// Prepare a SQL statement for execution. + /// + /// # Failure + /// + /// Will return `Err` if `sql` cannot be converted to a C-compatible string + /// or if the underlying SQLite call fails. + #[inline] + pub fn prepare_with_flags(&self, sql: &str, flags: PrepFlags) -> Result> { + self.db.borrow_mut().prepare(self, sql, flags) } /// Close the SQLite connection. @@ -897,7 +908,8 @@ impl Connection { /// /// This function is unsafe because improper use may impact the Connection. /// In particular, it should only be called on connections created - /// and owned by the caller, e.g. as a result of calling ffi::sqlite3_open(). + /// and owned by the caller, e.g. as a result of calling + /// ffi::sqlite3_open(). #[inline] pub unsafe fn from_handle_owned(db: *mut ffi::sqlite3) -> Result { let db = InnerConnection::new(db, true); @@ -1106,6 +1118,19 @@ impl Default for OpenFlags { } } +bitflags::bitflags! { + /// Prepare flags. See + /// [sqlite3_prepare_v3](https://sqlite.org/c3ref/c_prepare_normalize.html) for details. + #[derive(Clone, Copy, Debug, Default, Eq, Hash, PartialEq)] + #[repr(C)] + pub struct PrepFlags: ::std::os::raw::c_uint { + /// A hint to the query planner that the prepared statement will be retained for a long time and probably reused many times. + const SQLITE_PREPARE_PERSISTENT = 0x01; + /// Causes the SQL compiler to return an error (error code SQLITE_ERROR) if the statement uses any virtual tables. + const SQLITE_PREPARE_NO_VTAB = 0x04; + } +} + /// rusqlite's check for a safe SQLite threading mode requires SQLite 3.7.0 or /// later. If you are running against a SQLite older than that, rusqlite /// attempts to ensure safety by performing configuration and initialization of