Implement our own sqlite3_exec

Should fix issue related to unlock notify: #767
Caveat: many CString allocated.
This commit is contained in:
gwenn 2020-06-26 19:35:14 +02:00
parent 57db338537
commit dd886578d2
6 changed files with 40 additions and 35 deletions

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")]
pub fn enable_load_extension(&mut self, onoff: c_int) -> Result<()> {
let r = unsafe { ffi::sqlite3_enable_load_extension(self.db, onoff) };
@ -262,8 +247,17 @@ impl InnerConnection {
// comment) then *ppStmt is set to NULL.
let c_stmt: *mut ffi::sqlite3_stmt = c_stmt;
let c_tail: *const c_char = c_tail;
// TODO ignore spaces, comments, ... at the end
let tail = !c_tail.is_null() && unsafe { c_tail != c_sql.offset(len as isize) };
let tail = if c_tail.is_null() {
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 {
RawStatement::new(c_stmt, tail)
}))

View File

@ -435,8 +435,6 @@ impl Connection {
/// Convenience method to run multiple SQL statements (that cannot take any
/// parameters).
///
/// Uses [sqlite3_exec](http://www.sqlite.org/c3ref/exec.html) under the hood.
///
/// ## Example
///
/// ```rust,no_run
@ -456,7 +454,19 @@ impl Connection {
/// Will return `Err` if `sql` cannot be converted to a C-compatible string
/// or if the underlying SQLite call fails.
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.

View File

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

View File

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

View File

@ -622,7 +622,7 @@ impl Statement<'_> {
}
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);
self.conn.decode_result(stmt.finalize())
}
@ -707,7 +707,7 @@ impl Statement<'_> {
/// connection has closed is illegal, but `RawStatement` does not enforce
/// this, as it loses our protective `'conn` lifetime bound.
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);
stmt
}

View File

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