Merge remote-tracking branch 'origin/master' into sub_type

This commit is contained in:
gwenn
2024-01-16 21:15:50 +01:00
45 changed files with 30772 additions and 6310 deletions

View File

@@ -57,7 +57,7 @@ impl Connection {
}
/// Prepared statements LRU cache.
// #[derive(Debug)] // FIXME: https://github.com/kyren/hashlink/pull/4
#[derive(Debug)]
pub struct StatementCache(RefCell<LruCache<Arc<str>, RawStatement>>);
#[allow(clippy::non_send_fields_in_send_ty)]

View File

@@ -3,12 +3,16 @@ use std::str;
use crate::{Error, Result, Statement};
/// Information about a column of a SQLite query.
#[cfg(feature = "column_decltype")]
#[cfg_attr(docsrs, doc(cfg(feature = "column_decltype")))]
#[derive(Debug)]
pub struct Column<'stmt> {
name: &'stmt str,
decl_type: Option<&'stmt str>,
}
#[cfg(feature = "column_decltype")]
#[cfg_attr(docsrs, doc(cfg(feature = "column_decltype")))]
impl Column<'_> {
/// Returns the name of the column.
#[inline]
@@ -90,6 +94,8 @@ impl Statement<'_> {
/// Returns an `Error::InvalidColumnIndex` if `idx` is outside the valid
/// column range for this row.
///
/// # Panics
///
/// Panics when column name is not valid UTF-8.
#[inline]
pub fn column_name(&self, col: usize) -> Result<&str> {

View File

@@ -9,6 +9,7 @@ use crate::{Connection, Result};
/// Database Connection Configuration Options
/// See [Database Connection Configuration Options](https://sqlite.org/c3ref/c_dbconfig_enable_fkey.html) for details.
#[repr(i32)]
#[derive(Copy, Clone, Debug)]
#[allow(non_snake_case, non_camel_case_types)]
#[non_exhaustive]
#[allow(clippy::upper_case_acronyms)]

View File

@@ -141,6 +141,10 @@ pub enum Error {
/// byte offset of the start of invalid token
offset: c_int,
},
/// Loadable extension initialization error
#[cfg(feature = "loadable_extension")]
#[cfg_attr(docsrs, doc(cfg(feature = "loadable_extension")))]
InitError(ffi::InitError),
}
impl PartialEq for Error {
@@ -200,6 +204,8 @@ impl PartialEq for Error {
offset: o2,
},
) => e1 == e2 && m1 == m2 && s1 == s2 && o1 == o2,
#[cfg(feature = "loadable_extension")]
(Error::InitError(e1), Error::InitError(e2)) => e1 == e2,
(..) => false,
}
}
@@ -241,6 +247,14 @@ impl From<FromSqlError> for Error {
}
}
#[cfg(feature = "loadable_extension")]
impl From<ffi::InitError> for Error {
#[cold]
fn from(err: ffi::InitError) -> Error {
Error::InitError(err)
}
}
impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match *self {
@@ -311,6 +325,8 @@ impl fmt::Display for Error {
ref sql,
..
} => write!(f, "{msg} in {sql} at offset {offset}"),
#[cfg(feature = "loadable_extension")]
Error::InitError(ref err) => err.fmt(f),
}
}
}
@@ -360,6 +376,8 @@ impl error::Error for Error {
Error::BlobSizeError => None,
#[cfg(feature = "modern_sqlite")]
Error::SqlInputError { ref error, .. } => Some(error),
#[cfg(feature = "loadable_extension")]
Error::InitError(ref err) => Some(err),
}
}
}
@@ -367,6 +385,7 @@ impl error::Error for Error {
impl Error {
/// Returns the underlying SQLite error if this is [`Error::SqliteFailure`].
#[inline]
#[must_use]
pub fn sqlite_error(&self) -> Option<&ffi::Error> {
match self {
Self::SqliteFailure(error, _) => Some(error),
@@ -377,6 +396,7 @@ impl Error {
/// Returns the underlying SQLite error code if this is
/// [`Error::SqliteFailure`].
#[inline]
#[must_use]
pub fn sqlite_error_code(&self) -> Option<ffi::ErrorCode> {
self.sqlite_error().map(|error| error.code)
}
@@ -439,10 +459,7 @@ pub fn check(code: c_int) -> Result<()> {
/// Transform Rust error to SQLite error (message and code).
/// # Safety
/// This function is unsafe because it uses raw pointer
pub unsafe fn to_sqlite_error(
e: &Error,
err_msg: *mut *mut std::os::raw::c_char,
) -> std::os::raw::c_int {
pub unsafe fn to_sqlite_error(e: &Error, err_msg: *mut *mut std::os::raw::c_char) -> c_int {
use crate::util::alloc;
match e {
Error::SqliteFailure(err, s) => {

View File

@@ -270,11 +270,11 @@ where
/// call to [`step()`](Aggregate::step) to set up the context for an
/// invocation of the function. (Note: `init()` will not be called if
/// there are no rows.)
fn init(&self, _: &mut Context<'_>) -> Result<A>;
fn init(&self, ctx: &mut Context<'_>) -> Result<A>;
/// "step" function called once for each row in an aggregate group. May be
/// called 0 times if there are no rows.
fn step(&self, _: &mut Context<'_>, _: &mut A) -> Result<()>;
fn step(&self, ctx: &mut Context<'_>, acc: &mut A) -> Result<()>;
/// Computes and returns the final result. Will be called exactly once for
/// each invocation of the function. If [`step()`](Aggregate::step) was
@@ -285,7 +285,7 @@ where
/// given `None`.
///
/// The passed context will have no arguments.
fn finalize(&self, _: &mut Context<'_>, _: Option<A>) -> Result<(T, SubType)>;
fn finalize(&self, ctx: &mut Context<'_>, acc: Option<A>) -> Result<(T, SubType)>;
}
/// `WindowAggregate` is the callback interface for
@@ -299,16 +299,17 @@ where
{
/// Returns the current value of the aggregate. Unlike xFinal, the
/// implementation should not delete any context.
fn value(&self, _: Option<&A>) -> Result<(T, SubType)>;
fn value(&self, acc: Option<&mut A>) -> Result<(T, SubType)>;
/// Removes a row from the current window.
fn inverse(&self, _: &mut Context<'_>, _: &mut A) -> Result<()>;
fn inverse(&self, ctx: &mut Context<'_>, acc: &mut A) -> Result<()>;
}
bitflags::bitflags! {
/// Function Flags.
/// See [sqlite3_create_function](https://sqlite.org/c3ref/create_function.html)
/// and [Function Flags](https://sqlite.org/c3ref/c_deterministic.html) for details.
#[derive(Clone, Copy, Debug)]
#[repr(C)]
pub struct FunctionFlags: ::std::os::raw::c_int {
/// Specifies UTF-8 as the text encoding this SQL function prefers for its parameters.
@@ -327,6 +328,8 @@ bitflags::bitflags! {
const SQLITE_SUBTYPE = 0x0000_0010_0000; // 3.30.0
/// Means that the function is unlikely to cause problems even if misused.
const SQLITE_INNOCUOUS = 0x0000_0020_0000; // 3.31.0
/// Indicates to SQLite that a function might call `sqlite3_result_subtype()` to cause a sub-type to be associated with its result.
const SQLITE_RESULT_SUBTYPE = 0x0000_0100_0000; // 3.45.0
}
}
@@ -641,6 +644,7 @@ unsafe extern "C" fn call_boxed_step<A, D, T>(
args: slice::from_raw_parts(argv, argc as usize),
};
#[allow(clippy::unnecessary_cast)]
if (*pac as *mut A).is_null() {
*pac = Box::into_raw(Box::new((*boxed_aggr).init(&mut ctx)?));
}
@@ -711,7 +715,9 @@ where
// Within the xFinal callback, it is customary to set N=0 in calls to
// sqlite3_aggregate_context(C,N) so that no pointless memory allocations occur.
let a: Option<A> = match aggregate_context(ctx, 0) {
Some(pac) => {
Some(pac) =>
{
#[allow(clippy::unnecessary_cast)]
if (*pac as *mut A).is_null() {
None
} else {
@@ -760,17 +766,10 @@ where
{
// Within the xValue callback, it is customary to set N=0 in calls to
// sqlite3_aggregate_context(C,N) so that no pointless memory allocations occur.
let a: Option<&A> = match aggregate_context(ctx, 0) {
Some(pac) => {
if (*pac as *mut A).is_null() {
None
} else {
let a = &**pac;
Some(a)
}
}
None => None,
};
let pac = aggregate_context(ctx, 0).filter(|&pac| {
#[allow(clippy::unnecessary_cast)]
!(*pac as *mut A).is_null()
});
let r = catch_unwind(|| {
let boxed_aggr: *mut W = ffi::sqlite3_user_data(ctx).cast::<W>();
@@ -778,7 +777,7 @@ where
!boxed_aggr.is_null(),
"Internal error - null aggregate pointer"
);
(*boxed_aggr).value(a)
(*boxed_aggr).value(pac.map(|pac| &mut **pac))
});
let t = match r {
Err(_) => {
@@ -1042,7 +1041,7 @@ mod test {
Ok(())
}
fn value(&self, sum: Option<&i64>) -> Result<(Option<i64>, SubType)> {
fn value(&self, sum: Option<&mut i64>) -> Result<(Option<i64>, SubType)> {
Ok((sum.copied(), None))
}
}

View File

@@ -776,9 +776,10 @@ mod test {
.unwrap();
let authorizer = move |ctx: AuthContext<'_>| match ctx.action {
AuthAction::Read { column_name, .. } if column_name == "private" => {
Authorization::Ignore
}
AuthAction::Read {
column_name: "private",
..
} => Authorization::Ignore,
AuthAction::DropTable { .. } => Authorization::Deny,
AuthAction::Pragma { .. } => panic!("shouldn't be called"),
_ => Authorization::Allow,

View File

@@ -4,7 +4,7 @@ use std::os::raw::{c_char, c_int};
use std::path::Path;
use std::ptr;
use std::str;
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::atomic::AtomicBool;
use std::sync::{Arc, Mutex};
use super::ffi;
@@ -40,7 +40,7 @@ pub struct InnerConnection {
unsafe impl Send for InnerConnection {}
impl InnerConnection {
#[allow(clippy::mutex_atomic)]
#[allow(clippy::mutex_atomic, clippy::arc_with_non_send_sync)] // See unsafe impl Send / Sync for InterruptHandle
#[inline]
pub unsafe fn new(db: *mut ffi::sqlite3, owned: bool) -> InnerConnection {
InnerConnection {
@@ -390,7 +390,7 @@ impl Drop for InnerConnection {
}
}
#[cfg(not(any(target_arch = "wasm32")))]
#[cfg(not(any(target_arch = "wasm32", feature = "loadable_extension")))]
static SQLITE_INIT: std::sync::Once = std::sync::Once::new();
pub static BYPASS_SQLITE_INIT: AtomicBool = AtomicBool::new(false);
@@ -440,7 +440,9 @@ fn ensure_safe_sqlite_threading_mode() -> Result<()> {
Ok(())
}
} else {
#[cfg(not(feature = "loadable_extension"))]
SQLITE_INIT.call_once(|| {
use std::sync::atomic::Ordering;
if BYPASS_SQLITE_INIT.load(Ordering::Relaxed) {
return;
}

View File

@@ -74,6 +74,7 @@ use crate::raw_statement::RawStatement;
use crate::types::ValueRef;
pub use crate::cache::CachedStatement;
#[cfg(feature = "column_decltype")]
pub use crate::column::Column;
pub use crate::error::{to_sqlite_error, Error};
pub use crate::ffi::ErrorCode;
@@ -82,9 +83,14 @@ pub use crate::load_extension_guard::LoadExtensionGuard;
pub use crate::params::{params_from_iter, Params, ParamsFromIter};
pub use crate::row::{AndThenRows, Map, MappedRows, Row, RowIndex, Rows};
pub use crate::statement::{Statement, StatementStatus};
#[cfg(feature = "modern_sqlite")]
pub use crate::transaction::TransactionState;
pub use crate::transaction::{DropBehavior, Savepoint, Transaction, TransactionBehavior};
pub use crate::types::ToSql;
pub use crate::version::*;
#[cfg(feature = "rusqlite-macros")]
#[doc(hidden)]
pub use rusqlite_macros::__bind;
mod error;
@@ -215,6 +221,51 @@ macro_rules! named_params {
};
}
/// Captured identifiers in SQL
///
/// * only SQLite `$x` / `@x` / `:x` syntax works (Rust `&x` syntax does not
/// work).
/// * `$x.y` expression does not work.
///
/// # Example
///
/// ```rust, no_run
/// # use rusqlite::{prepare_and_bind, Connection, Result, Statement};
///
/// fn misc(db: &Connection) -> Result<Statement> {
/// let name = "Lisa";
/// let age = 8;
/// let smart = true;
/// Ok(prepare_and_bind!(db, "SELECT $name, @age, :smart;"))
/// }
/// ```
#[cfg(feature = "rusqlite-macros")]
#[cfg_attr(docsrs, doc(cfg(feature = "rusqlite-macros")))]
#[macro_export]
macro_rules! prepare_and_bind {
($conn:expr, $sql:literal) => {{
let mut stmt = $conn.prepare($sql)?;
$crate::__bind!(stmt $sql);
stmt
}};
}
/// Captured identifiers in SQL
///
/// * only SQLite `$x` / `@x` / `:x` syntax works (Rust `&x` syntax does not
/// work).
/// * `$x.y` expression does not work.
#[cfg(feature = "rusqlite-macros")]
#[cfg_attr(docsrs, doc(cfg(feature = "rusqlite-macros")))]
#[macro_export]
macro_rules! prepare_cached_and_bind {
($conn:expr, $sql:literal) => {{
let mut stmt = $conn.prepare_cached($sql)?;
$crate::__bind!(stmt $sql);
stmt
}};
}
/// A typedef of the result returned by many methods.
pub type Result<T, E = Error> = result::Result<T, E>;
@@ -568,7 +619,7 @@ impl Connection {
#[inline]
pub fn execute<P: Params>(&self, sql: &str, params: P) -> Result<usize> {
self.prepare(sql)
.and_then(|mut stmt| stmt.check_no_tail().and_then(|_| stmt.execute(params)))
.and_then(|mut stmt| stmt.check_no_tail().and_then(|()| stmt.execute(params)))
}
/// Returns the path to the database file, if one exists and is known.
@@ -651,7 +702,7 @@ impl Connection {
// https://sqlite.org/tclsqlite.html#onecolumn
#[cfg(test)]
pub(crate) fn one_column<T: crate::types::FromSql>(&self, sql: &str) -> Result<T> {
pub(crate) fn one_column<T: types::FromSql>(&self, sql: &str) -> Result<T> {
self.query_row(sql, [], |r| r.get(0))
}
@@ -901,6 +952,17 @@ impl Connection {
})
}
/// Like SQLITE_EXTENSION_INIT2 macro
#[cfg(feature = "loadable_extension")]
#[cfg_attr(docsrs, doc(cfg(feature = "loadable_extension")))]
pub unsafe fn extension_init2(
db: *mut ffi::sqlite3,
p_api: *mut ffi::sqlite3_api_routines,
) -> Result<Connection> {
ffi::rusqlite_extension_init2(p_api)?;
Connection::from_handle(db)
}
/// Create a `Connection` from a raw owned handle.
///
/// The returned connection will attempt to close the inner connection
@@ -912,7 +974,7 @@ 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().
/// `ffi::sqlite3_open`().
#[inline]
pub unsafe fn from_handle_owned(db: *mut ffi::sqlite3) -> Result<Connection> {
let db = InnerConnection::new(db, true);
@@ -1432,7 +1494,7 @@ mod test {
#[test]
#[cfg(feature = "extra_check")]
fn test_execute_select() {
fn test_execute_select_with_no_row() {
let db = checked_memory_handle();
let err = db.execute("SELECT 1 WHERE 1 < ?1", [1i32]).unwrap_err();
assert_eq!(
@@ -1442,6 +1504,13 @@ mod test {
);
}
#[test]
fn test_execute_select_with_row() {
let db = checked_memory_handle();
let err = db.execute("SELECT 1", []).unwrap_err();
assert_eq!(err, Error::ExecuteReturnedResults);
}
#[test]
#[cfg(feature = "extra_check")]
fn test_execute_multiple() {
@@ -1812,7 +1881,7 @@ mod test {
#[test]
fn test_from_handle_owned() -> Result<()> {
let mut handle: *mut ffi::sqlite3 = std::ptr::null_mut();
let r = unsafe { ffi::sqlite3_open(":memory:\0".as_ptr() as *const i8, &mut handle) };
let r = unsafe { ffi::sqlite3_open(":memory:\0".as_ptr() as *const c_char, &mut handle) };
assert_eq!(r, ffi::SQLITE_OK);
let db = unsafe { Connection::from_handle_owned(handle) }?;
db.execute_batch("PRAGMA VACUUM")?;
@@ -2114,9 +2183,25 @@ mod test {
}
#[test]
pub fn db_readonly() -> Result<()> {
fn db_readonly() -> Result<()> {
let db = Connection::open_in_memory()?;
assert!(!db.is_readonly(MAIN_DB)?);
Ok(())
}
#[test]
#[cfg(feature = "rusqlite-macros")]
fn prepare_and_bind() -> Result<()> {
let db = Connection::open_in_memory()?;
let name = "Lisa";
let age = 8;
let mut stmt = prepare_and_bind!(db, "SELECT $name, $age;");
let (v1, v2) = stmt
.raw_query()
.next()
.and_then(|o| o.ok_or(Error::QueryReturnedNoRows))
.and_then(|r| Ok((r.get::<_, String>(0)?, r.get::<_, i64>(1)?)))?;
assert_eq!((v1.as_str(), v2), (name, age));
Ok(())
}
}

View File

@@ -9,6 +9,7 @@ use std::os::raw::c_int;
/// See the official documentation for more information:
/// - <https://www.sqlite.org/c3ref/c_limit_attached.html>
/// - <https://www.sqlite.org/limits.html>
#[derive(Copy, Clone, Debug)]
#[repr(i32)]
#[non_exhaustive]
#[allow(clippy::upper_case_acronyms, non_camel_case_types)]

View File

@@ -384,7 +384,6 @@ mod test {
let mut rows = table_info.query(["sqlite_master"])?;
while let Some(row) = rows.next()? {
let row = row;
let column: String = row.get(1)?;
columns.push(column);
}

View File

@@ -14,9 +14,11 @@ pub struct Rows<'stmt> {
impl<'stmt> Rows<'stmt> {
#[inline]
fn reset(&mut self) {
fn reset(&mut self) -> Result<()> {
if let Some(stmt) = self.stmt.take() {
stmt.reset();
stmt.reset()
} else {
Ok(())
}
}
@@ -29,8 +31,8 @@ impl<'stmt> Rows<'stmt> {
/// This interface is not compatible with Rust's `Iterator` trait, because
/// the lifetime of the returned row is tied to the lifetime of `self`.
/// This is a fallible "streaming iterator". For a more natural interface,
/// consider using [`query_map`](crate::Statement::query_map) or
/// [`query_and_then`](crate::Statement::query_and_then) instead, which
/// consider using [`query_map`](Statement::query_map) or
/// [`query_and_then`](Statement::query_and_then) instead, which
/// return types that implement `Iterator`.
#[allow(clippy::should_implement_trait)] // cannot implement Iterator
#[inline]
@@ -105,6 +107,7 @@ impl<'stmt> Rows<'stmt> {
}
impl Drop for Rows<'_> {
#[allow(unused_must_use)]
#[inline]
fn drop(&mut self) {
self.reset();
@@ -217,12 +220,12 @@ impl<'stmt> FallibleStreamingIterator for Rows<'stmt> {
Ok(())
}
Ok(false) => {
self.reset();
let r = self.reset();
self.row = None;
Ok(())
r
}
Err(e) => {
self.reset();
let _ = self.reset(); // prevents infinite loop on error
self.row = None;
Err(e)
}
@@ -247,7 +250,7 @@ pub struct Row<'stmt> {
impl<'stmt> Row<'stmt> {
/// Get the value of a particular column of the result row.
///
/// ## Failure
/// # Panics
///
/// Panics if calling [`row.get(idx)`](Row::get) would return an error,
/// including:
@@ -330,7 +333,7 @@ impl<'stmt> Row<'stmt> {
/// it can be difficult to use, and most callers will be better served by
/// [`get`](Row::get) or [`get_unwrap`](Row::get_unwrap).
///
/// ## Failure
/// # Panics
///
/// Panics if calling [`row.get_ref(idx)`](Row::get_ref) would return an
/// error, including:
@@ -585,4 +588,30 @@ mod tests {
// We don't test one bigger because it's unimplemented
Ok(())
}
#[test]
#[cfg(feature = "bundled")]
fn pathological_case() -> Result<()> {
let conn = Connection::open_in_memory()?;
conn.execute_batch(
"CREATE TABLE foo(x);
CREATE TRIGGER oops BEFORE INSERT ON foo BEGIN SELECT RAISE(FAIL, 'Boom'); END;",
)?;
let mut stmt = conn.prepare("INSERT INTO foo VALUES (0) RETURNING rowid;")?;
{
let iterator_count = stmt.query_map([], |_| Ok(()))?.count();
assert_eq!(1, iterator_count); // should be 0
use fallible_streaming_iterator::FallibleStreamingIterator;
let fallible_iterator_count = stmt.query([])?.count().unwrap_or(0);
assert_eq!(0, fallible_iterator_count);
}
{
let iterator_last = stmt.query_map([], |_| Ok(()))?.last();
assert!(iterator_last.is_some()); // should be none
use fallible_iterator::FallibleIterator;
let fallible_iterator_last = stmt.query([])?.map(|_| Ok(())).last();
assert!(fallible_iterator_last.is_err());
}
Ok(())
}
}

View File

@@ -22,8 +22,9 @@ pub struct OwnedData {
}
impl OwnedData {
/// SAFETY: Caller must be certain that `ptr` is allocated by
/// `sqlite3_malloc`.
/// # Safety
///
/// Caller must be certain that `ptr` is allocated by `sqlite3_malloc`.
pub unsafe fn from_raw_nonnull(ptr: NonNull<u8>, sz: usize) -> Self {
Self { ptr, sz }
}
@@ -65,7 +66,7 @@ impl<'conn> Deref for Data<'conn> {
impl Connection {
/// Serialize a database.
pub fn serialize<'conn>(&'conn self, schema: DatabaseName<'_>) -> Result<Data<'conn>> {
pub fn serialize(&self, schema: DatabaseName) -> Result<Data> {
let schema = schema.as_cstring()?;
let mut sz = 0;
let mut ptr: *mut u8 = unsafe {
@@ -142,7 +143,9 @@ mod test {
let db = Connection::open_in_memory()?;
db.execute_batch("CREATE TABLE x AS SELECT 'data'")?;
let data = db.serialize(DatabaseName::Main)?;
let Data::Owned(data) = data else { panic!("expected OwnedData")};
let Data::Owned(data) = data else {
panic!("expected OwnedData")
};
assert!(data.sz > 0);
Ok(())
}
@@ -152,7 +155,9 @@ mod test {
let src = Connection::open_in_memory()?;
src.execute_batch("CREATE TABLE x AS SELECT 'data'")?;
let data = src.serialize(DatabaseName::Main)?;
let Data::Owned(data) = data else { panic!("expected OwnedData")};
let Data::Owned(data) = data else {
panic!("expected OwnedData")
};
let mut dst = Connection::open_in_memory()?;
dst.deserialize(DatabaseName::Main, data, false)?;

View File

@@ -405,7 +405,7 @@ impl Drop for ChangesetIter<'_> {
}
/// An item passed to a conflict-handler by
/// [`Connection::apply`](crate::Connection::apply), or an item generated by
/// [`Connection::apply`](Connection::apply), or an item generated by
/// [`ChangesetIter::next`](ChangesetIter::next).
// TODO enum ? Delete, Insert, Update, ...
pub struct ChangesetItem {

View File

@@ -435,6 +435,10 @@ impl Statement<'_> {
///
/// Will return `None` if the column index is out of bounds or if the
/// parameter is positional.
///
/// # Panics
///
/// Panics when parameter name is not valid UTF-8.
#[inline]
pub fn parameter_name(&self, index: usize) -> Option<&'_ str> {
self.stmt.bind_parameter_name(index as i32).map(|name| {
@@ -450,7 +454,7 @@ impl Statement<'_> {
{
let expected = self.stmt.bind_parameter_count();
let mut index = 0;
for p in params.into_iter() {
for p in params {
index += 1; // The leftmost SQL parameter has an index of 1.
if index > expected {
break;
@@ -646,9 +650,12 @@ impl Statement<'_> {
fn execute_with_bound_parameters(&mut self) -> Result<usize> {
self.check_update()?;
let r = self.stmt.step();
self.stmt.reset();
let rr = self.stmt.reset();
match r {
ffi::SQLITE_DONE => Ok(self.conn.changes() as usize),
ffi::SQLITE_DONE => match rr {
ffi::SQLITE_OK => Ok(self.conn.changes() as usize),
_ => Err(self.conn.decode_result(rr).unwrap_err()),
},
ffi::SQLITE_ROW => Err(Error::ExecuteReturnedResults),
_ => Err(self.conn.decode_result(r).unwrap_err()),
}
@@ -744,7 +751,7 @@ impl Statement<'_> {
/// Reset all bindings
pub fn clear_bindings(&mut self) {
self.stmt.clear_bindings()
self.stmt.clear_bindings();
}
}
@@ -843,8 +850,11 @@ impl Statement<'_> {
}
#[inline]
pub(super) fn reset(&self) -> c_int {
self.stmt.reset()
pub(super) fn reset(&self) -> Result<()> {
match self.stmt.reset() {
ffi::SQLITE_OK => Ok(()),
code => Err(self.conn.decode_result(code).unwrap_err()),
}
}
}
@@ -1270,7 +1280,7 @@ mod test {
assert_eq!(0, stmt.column_count());
stmt.parameter_index("test").unwrap();
stmt.step().unwrap_err();
stmt.reset();
stmt.reset().unwrap(); // SQLITE_OMIT_AUTORESET = false
stmt.execute([]).unwrap_err();
Ok(())
}

View File

@@ -8,8 +8,7 @@ use std::ptr;
use std::time::Duration;
use super::ffi;
use crate::error::error_from_sqlite_code;
use crate::{Connection, Result};
use crate::Connection;
/// Set up the process-wide SQLite error logging callback.
///
@@ -25,7 +24,8 @@ use crate::{Connection, Result};
/// * It must be threadsafe if SQLite is used in a multithreaded way.
///
/// cf [The Error And Warning Log](http://sqlite.org/errlog.html).
pub unsafe fn config_log(callback: Option<fn(c_int, &str)>) -> Result<()> {
#[cfg(not(feature = "loadable_extension"))]
pub unsafe fn config_log(callback: Option<fn(c_int, &str)>) -> crate::Result<()> {
extern "C" fn log_callback(p_arg: *mut c_void, err: c_int, msg: *const c_char) {
let c_slice = unsafe { CStr::from_ptr(msg).to_bytes() };
let callback: fn(c_int, &str) = unsafe { mem::transmute(p_arg) };
@@ -48,7 +48,7 @@ pub unsafe fn config_log(callback: Option<fn(c_int, &str)>) -> Result<()> {
if rc == ffi::SQLITE_OK {
Ok(())
} else {
Err(error_from_sqlite_code(rc, None))
Err(crate::error::error_from_sqlite_code(rc, None))
}
}

View File

@@ -122,7 +122,7 @@ impl Transaction<'_> {
TransactionBehavior::Immediate => "BEGIN IMMEDIATE",
TransactionBehavior::Exclusive => "BEGIN EXCLUSIVE",
};
conn.execute_batch(query).map(move |_| Transaction {
conn.execute_batch(query).map(move |()| Transaction {
conn,
drop_behavior: DropBehavior::Rollback,
})
@@ -251,7 +251,7 @@ impl Savepoint<'_> {
fn with_name_<T: Into<String>>(conn: &Connection, name: T) -> Result<Savepoint<'_>> {
let name = name.into();
conn.execute_batch(&format!("SAVEPOINT {name}"))
.map(|_| Savepoint {
.map(|()| Savepoint {
conn,
name,
drop_behavior: DropBehavior::Rollback,
@@ -346,8 +346,8 @@ impl Savepoint<'_> {
match self.drop_behavior() {
DropBehavior::Commit => self
.commit_()
.or_else(|_| self.rollback().and_then(|_| self.commit_())),
DropBehavior::Rollback => self.rollback().and_then(|_| self.commit_()),
.or_else(|_| self.rollback().and_then(|()| self.commit_())),
DropBehavior::Rollback => self.rollback().and_then(|()| self.commit_()),
DropBehavior::Ignore => Ok(()),
DropBehavior::Panic => panic!("Savepoint dropped unexpectedly."),
}
@@ -471,8 +471,7 @@ impl Connection {
///
/// The savepoint defaults to rolling back when it is dropped. If you want
/// the savepoint to commit, you must call [`commit`](Savepoint::commit) or
/// [`set_drop_behavior(DropBehavior::Commit)`](Savepoint::
/// set_drop_behavior).
/// [`set_drop_behavior(DropBehavior::Commit)`](Savepoint::set_drop_behavior).
///
/// ## Example
///

View File

@@ -111,7 +111,7 @@ pub struct Null;
/// SQLite data types.
/// See [Fundamental Datatypes](https://sqlite.org/c3ref/c_blob.html).
#[derive(Clone, Debug, PartialEq, Eq)]
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum Type {
/// NULL
Null,

View File

@@ -36,8 +36,7 @@ impl ValueRef<'_> {
impl<'a> ValueRef<'a> {
/// If `self` is case `Integer`, returns the integral value. Otherwise,
/// returns [`Err(Error::InvalidColumnType)`](crate::Error::
/// InvalidColumnType).
/// returns [`Err(Error::InvalidColumnType)`](crate::Error::InvalidColumnType).
#[inline]
pub fn as_i64(&self) -> FromSqlResult<i64> {
match *self {
@@ -48,8 +47,7 @@ impl<'a> ValueRef<'a> {
/// If `self` is case `Null` returns None.
/// If `self` is case `Integer`, returns the integral value.
/// Otherwise returns [`Err(Error::InvalidColumnType)`](crate::Error::
/// InvalidColumnType).
/// Otherwise returns [`Err(Error::InvalidColumnType)`](crate::Error::InvalidColumnType).
#[inline]
pub fn as_i64_or_null(&self) -> FromSqlResult<Option<i64>> {
match *self {
@@ -60,8 +58,7 @@ impl<'a> ValueRef<'a> {
}
/// If `self` is case `Real`, returns the floating point value. Otherwise,
/// returns [`Err(Error::InvalidColumnType)`](crate::Error::
/// InvalidColumnType).
/// returns [`Err(Error::InvalidColumnType)`](crate::Error::InvalidColumnType).
#[inline]
pub fn as_f64(&self) -> FromSqlResult<f64> {
match *self {
@@ -72,8 +69,7 @@ impl<'a> ValueRef<'a> {
/// If `self` is case `Null` returns None.
/// If `self` is case `Real`, returns the floating point value.
/// Otherwise returns [`Err(Error::InvalidColumnType)`](crate::Error::
/// InvalidColumnType).
/// Otherwise returns [`Err(Error::InvalidColumnType)`](crate::Error::InvalidColumnType).
#[inline]
pub fn as_f64_or_null(&self) -> FromSqlResult<Option<f64>> {
match *self {
@@ -97,8 +93,7 @@ impl<'a> ValueRef<'a> {
/// If `self` is case `Null` returns None.
/// If `self` is case `Text`, returns the string value.
/// Otherwise returns [`Err(Error::InvalidColumnType)`](crate::Error::
/// InvalidColumnType).
/// Otherwise returns [`Err(Error::InvalidColumnType)`](crate::Error::InvalidColumnType).
#[inline]
pub fn as_str_or_null(&self) -> FromSqlResult<Option<&'a str>> {
match *self {
@@ -122,8 +117,7 @@ impl<'a> ValueRef<'a> {
/// If `self` is case `Null` returns None.
/// If `self` is case `Blob`, returns the byte slice.
/// Otherwise returns [`Err(Error::InvalidColumnType)`](crate::Error::
/// InvalidColumnType).
/// Otherwise returns [`Err(Error::InvalidColumnType)`](crate::Error::InvalidColumnType).
#[inline]
pub fn as_blob_or_null(&self) -> FromSqlResult<Option<&'a [u8]>> {
match *self {
@@ -133,7 +127,7 @@ impl<'a> ValueRef<'a> {
}
}
/// Returns the byte slice that makes up this ValueRef if it's either
/// Returns the byte slice that makes up this `ValueRef` if it's either
/// [`ValueRef::Blob`] or [`ValueRef::Text`].
#[inline]
pub fn as_bytes(&self) -> FromSqlResult<&'a [u8]> {

View File

@@ -14,6 +14,10 @@ pub fn version_number() -> i32 {
/// Returns the SQLite version as a string; e.g., `"3.16.2"` for version 3.16.2.
///
/// See [`sqlite3_libversion()`](https://www.sqlite.org/c3ref/libversion.html).
///
/// # Panics
///
/// Panics when version is not valid UTF-8.
#[inline]
#[must_use]
pub fn version() -> &'static str {

View File

@@ -373,6 +373,7 @@ bitflags::bitflags! {
/// Virtual table scan flags
/// See [Function Flags](https://sqlite.org/c3ref/c_index_scan_unique.html) for details.
#[repr(C)]
#[derive(Copy, Clone, Debug)]
pub struct IndexFlags: ::std::os::raw::c_int {
/// Default
const NONE = 0;