mirror of
https://github.com/isar/rusqlite.git
synced 2024-11-24 01:49:19 +08:00
Merge pull request #1339 from gwenn/prepare_with_flags
Use SQLITE_PREPARE_PERSISTENT for CachedStatement
This commit is contained in:
commit
371e60ab6f
@ -506,6 +506,7 @@ mod bindings {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
fn item_name(&self, original_item_name: &str) -> Option<String> {
|
||||
original_item_name
|
||||
.strip_prefix("sqlite3_index_info_")
|
||||
|
@ -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);
|
||||
|
@ -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
|
||||
|
@ -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<Statement<'a>> {
|
||||
let mut c_stmt = ptr::null_mut();
|
||||
pub fn prepare<'a>(
|
||||
&mut self,
|
||||
conn: &'a Connection,
|
||||
sql: &str,
|
||||
flags: PrepFlags,
|
||||
) -> Result<Statement<'a>> {
|
||||
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"))]
|
||||
|
29
src/lib.rs
29
src/lib.rs
@ -711,7 +711,18 @@ impl Connection {
|
||||
/// or if the underlying SQLite call fails.
|
||||
#[inline]
|
||||
pub fn prepare(&self, sql: &str) -> Result<Statement<'_>> {
|
||||
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<Statement<'_>> {
|
||||
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<Connection> {
|
||||
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
|
||||
|
Loading…
Reference in New Issue
Block a user