Merge pull request #773 from gwenn/execute_batch

Implement our own sqlite3_exec
This commit is contained in:
gwenn 2020-06-28 06:18:56 +02:00 committed by GitHub
commit 024e2e6bf0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 61 additions and 35 deletions

View File

@ -135,6 +135,10 @@ name = "vtab"
name = "cache" name = "cache"
harness = false harness = false
[[bench]]
name = "exec"
harness = false
[package.metadata.docs.rs] [package.metadata.docs.rs]
features = [ "backup", "blob", "chrono", "collation", "functions", "limits", "load_extension", "serde_json", "trace", "url", "vtab", "window", "modern_sqlite", "column_decltype" ] features = [ "backup", "blob", "chrono", "collation", "functions", "limits", "load_extension", "serde_json", "trace", "url", "vtab", "window", "modern_sqlite", "column_decltype" ]
all-features = false all-features = false

17
benches/exec.rs Normal file
View File

@ -0,0 +1,17 @@
use bencher::{benchmark_group, benchmark_main, Bencher};
use rusqlite::{Connection, NO_PARAMS};
fn bench_execute(b: &mut Bencher) {
let db = Connection::open_in_memory().unwrap();
let sql = "PRAGMA user_version=1";
b.iter(|| db.execute(sql, NO_PARAMS).unwrap());
}
fn bench_execute_batch(b: &mut Bencher) {
let db = Connection::open_in_memory().unwrap();
let sql = "PRAGMA user_version=1";
b.iter(|| db.execute_batch(sql).unwrap());
}
benchmark_group!(exec_benches, bench_execute, bench_execute_batch);
benchmark_main!(exec_benches);

View File

@ -171,21 +171,6 @@ impl InnerConnection {
} }
} }
pub fn execute_batch(&mut self, sql: &str) -> Result<()> {
// use CString instead of SmallCString because it's probably big.
let c_sql = std::ffi::CString::new(sql)?;
unsafe {
let r = ffi::sqlite3_exec(
self.db(),
c_sql.as_ptr(),
None,
ptr::null_mut(),
ptr::null_mut(),
);
self.decode_result(r)
}
}
#[cfg(feature = "load_extension")] #[cfg(feature = "load_extension")]
pub fn enable_load_extension(&mut self, onoff: c_int) -> Result<()> { pub fn enable_load_extension(&mut self, onoff: c_int) -> Result<()> {
let r = unsafe { ffi::sqlite3_enable_load_extension(self.db, onoff) }; let r = unsafe { ffi::sqlite3_enable_load_extension(self.db, onoff) };
@ -262,8 +247,17 @@ impl InnerConnection {
// comment) then *ppStmt is set to NULL. // comment) then *ppStmt is set to NULL.
let c_stmt: *mut ffi::sqlite3_stmt = c_stmt; let c_stmt: *mut ffi::sqlite3_stmt = c_stmt;
let c_tail: *const c_char = c_tail; let c_tail: *const c_char = c_tail;
// TODO ignore spaces, comments, ... at the end let tail = if c_tail.is_null() {
let tail = !c_tail.is_null() && unsafe { c_tail != c_sql.offset(len as isize) }; 0
} else {
// TODO nightly feature ptr_offset_from #41079
let n = (c_tail as isize) - (c_sql as isize);
if n <= 0 || n >= len as isize {
0
} else {
n as usize
}
};
Ok(Statement::new(conn, unsafe { Ok(Statement::new(conn, unsafe {
RawStatement::new(c_stmt, tail) RawStatement::new(c_stmt, tail)
})) }))

View File

@ -435,8 +435,6 @@ impl Connection {
/// Convenience method to run multiple SQL statements (that cannot take any /// Convenience method to run multiple SQL statements (that cannot take any
/// parameters). /// parameters).
/// ///
/// Uses [sqlite3_exec](http://www.sqlite.org/c3ref/exec.html) under the hood.
///
/// ## Example /// ## Example
/// ///
/// ```rust,no_run /// ```rust,no_run
@ -456,7 +454,19 @@ impl Connection {
/// Will return `Err` if `sql` cannot be converted to a C-compatible string /// Will return `Err` if `sql` cannot be converted to a C-compatible string
/// or if the underlying SQLite call fails. /// or if the underlying SQLite call fails.
pub fn execute_batch(&self, sql: &str) -> Result<()> { pub fn execute_batch(&self, sql: &str) -> Result<()> {
self.db.borrow_mut().execute_batch(sql) let mut sql = sql;
while !sql.is_empty() {
let stmt = self.prepare(sql)?;
if !stmt.stmt.is_null() && stmt.step()? {
return Err(Error::ExecuteReturnedResults);
}
let tail = stmt.stmt.tail();
if tail == 0 || tail >= sql.len() {
break;
}
sql = &sql[tail..];
}
Ok(())
} }
/// Convenience method to prepare and execute a single SQL statement. /// Convenience method to prepare and execute a single SQL statement.

View File

@ -256,8 +256,7 @@ impl Connection {
// The two syntaxes yield identical results. // The two syntaxes yield identical results.
sql.push_equal_sign(); sql.push_equal_sign();
sql.push_value(pragma_value)?; sql.push_value(pragma_value)?;
self.execute(&sql, NO_PARAMS)?; self.execute_batch(&sql)
Ok(())
} }
/// Set a new value to `pragma_name` and return the updated value. /// Set a new value to `pragma_name` and return the updated value.

View File

@ -12,7 +12,7 @@ use std::sync::Arc;
#[derive(Debug)] #[derive(Debug)]
pub struct RawStatement { pub struct RawStatement {
ptr: *mut ffi::sqlite3_stmt, ptr: *mut ffi::sqlite3_stmt,
tail: bool, tail: usize,
// Cached indices of named parameters, computed on the fly. // Cached indices of named parameters, computed on the fly.
cache: crate::util::ParamIndexCache, cache: crate::util::ParamIndexCache,
// Cached SQL (trimmed) that we use as the key when we're in the statement // Cached SQL (trimmed) that we use as the key when we're in the statement
@ -29,7 +29,7 @@ pub struct RawStatement {
} }
impl RawStatement { impl RawStatement {
pub unsafe fn new(stmt: *mut ffi::sqlite3_stmt, tail: bool) -> RawStatement { pub unsafe fn new(stmt: *mut ffi::sqlite3_stmt, tail: usize) -> RawStatement {
RawStatement { RawStatement {
ptr: stmt, ptr: stmt,
tail, tail,
@ -170,6 +170,10 @@ impl RawStatement {
#[cfg(feature = "extra_check")] #[cfg(feature = "extra_check")]
pub fn has_tail(&self) -> bool { pub fn has_tail(&self) -> bool {
self.tail != 0
}
pub fn tail(&self) -> usize {
self.tail self.tail
} }
} }

View File

@ -622,7 +622,7 @@ impl Statement<'_> {
} }
fn finalize_(&mut self) -> Result<()> { fn finalize_(&mut self) -> Result<()> {
let mut stmt = unsafe { RawStatement::new(ptr::null_mut(), false) }; let mut stmt = unsafe { RawStatement::new(ptr::null_mut(), 0) };
mem::swap(&mut stmt, &mut self.stmt); mem::swap(&mut stmt, &mut self.stmt);
self.conn.decode_result(stmt.finalize()) self.conn.decode_result(stmt.finalize())
} }
@ -707,7 +707,7 @@ impl Statement<'_> {
/// connection has closed is illegal, but `RawStatement` does not enforce /// connection has closed is illegal, but `RawStatement` does not enforce
/// this, as it loses our protective `'conn` lifetime bound. /// this, as it loses our protective `'conn` lifetime bound.
pub(crate) unsafe fn into_raw(mut self) -> RawStatement { pub(crate) unsafe fn into_raw(mut self) -> RawStatement {
let mut stmt = RawStatement::new(ptr::null_mut(), false); let mut stmt = RawStatement::new(ptr::null_mut(), 0);
mem::swap(&mut stmt, &mut self.stmt); mem::swap(&mut stmt, &mut self.stmt);
stmt stmt
} }

View File

@ -1,4 +1,4 @@
use crate::{Connection, Result, NO_PARAMS}; use crate::{Connection, Result};
use std::ops::Deref; use std::ops::Deref;
/// Options for transaction behavior. See [BEGIN /// Options for transaction behavior. See [BEGIN
@ -120,7 +120,7 @@ impl Transaction<'_> {
TransactionBehavior::Immediate => "BEGIN IMMEDIATE", TransactionBehavior::Immediate => "BEGIN IMMEDIATE",
TransactionBehavior::Exclusive => "BEGIN EXCLUSIVE", TransactionBehavior::Exclusive => "BEGIN EXCLUSIVE",
}; };
conn.execute(query, NO_PARAMS).map(move |_| Transaction { conn.execute_batch(query).map(move |_| Transaction {
conn, conn,
drop_behavior: DropBehavior::Rollback, drop_behavior: DropBehavior::Rollback,
}) })
@ -180,7 +180,7 @@ impl Transaction<'_> {
} }
fn commit_(&mut self) -> Result<()> { fn commit_(&mut self) -> Result<()> {
self.conn.execute("COMMIT", NO_PARAMS)?; self.conn.execute_batch("COMMIT")?;
Ok(()) Ok(())
} }
@ -190,7 +190,7 @@ impl Transaction<'_> {
} }
fn rollback_(&mut self) -> Result<()> { fn rollback_(&mut self) -> Result<()> {
self.conn.execute("ROLLBACK", NO_PARAMS)?; self.conn.execute_batch("ROLLBACK")?;
Ok(()) Ok(())
} }
@ -238,7 +238,7 @@ impl Savepoint<'_> {
name: T, name: T,
) -> Result<Savepoint<'_>> { ) -> Result<Savepoint<'_>> {
let name = name.into(); let name = name.into();
conn.execute(&format!("SAVEPOINT {}", name), NO_PARAMS) conn.execute_batch(&format!("SAVEPOINT {}", name))
.map(|_| Savepoint { .map(|_| Savepoint {
conn, conn,
name, name,
@ -291,8 +291,7 @@ impl Savepoint<'_> {
} }
fn commit_(&mut self) -> Result<()> { fn commit_(&mut self) -> Result<()> {
self.conn self.conn.execute_batch(&format!("RELEASE {}", self.name))?;
.execute(&format!("RELEASE {}", self.name), NO_PARAMS)?;
self.committed = true; self.committed = true;
Ok(()) Ok(())
} }
@ -305,8 +304,7 @@ impl Savepoint<'_> {
/// rolled back, and can be rolled back again or committed. /// rolled back, and can be rolled back again or committed.
pub fn rollback(&mut self) -> Result<()> { pub fn rollback(&mut self) -> Result<()> {
self.conn self.conn
.execute(&format!("ROLLBACK TO {}", self.name), NO_PARAMS)?; .execute_batch(&format!("ROLLBACK TO {}", self.name))
Ok(())
} }
/// Consumes the savepoint, committing or rolling back according to the /// Consumes the savepoint, committing or rolling back according to the