mirror of
https://github.com/isar/rusqlite.git
synced 2025-12-16 00:12:28 +08:00
Merge remote-tracking branch 'upstream/master' into sub_type
# Conflicts: # src/functions.rs
This commit is contained in:
@@ -235,7 +235,7 @@ impl Connection {
|
||||
table.as_ptr(),
|
||||
column.as_ptr(),
|
||||
row_id,
|
||||
if read_only { 0 } else { 1 },
|
||||
!read_only as std::os::raw::c_int,
|
||||
&mut blob,
|
||||
)
|
||||
};
|
||||
@@ -473,14 +473,14 @@ mod test {
|
||||
assert_eq!(&bytes, b"Clob5");
|
||||
|
||||
// should not be able to seek negative or past end
|
||||
assert!(blob.seek(SeekFrom::Current(-20)).is_err());
|
||||
assert!(blob.seek(SeekFrom::End(0)).is_ok());
|
||||
assert!(blob.seek(SeekFrom::Current(1)).is_err());
|
||||
blob.seek(SeekFrom::Current(-20)).unwrap_err();
|
||||
blob.seek(SeekFrom::End(0)).unwrap();
|
||||
blob.seek(SeekFrom::Current(1)).unwrap_err();
|
||||
|
||||
// write_all should detect when we return Ok(0) because there is no space left,
|
||||
// and return a write error
|
||||
blob.reopen(rowid)?;
|
||||
assert!(blob.write_all(b"0123456789x").is_err());
|
||||
blob.write_all(b"0123456789x").unwrap_err();
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -519,7 +519,7 @@ mod test {
|
||||
// trying to write too much and then flush should fail
|
||||
assert_eq!(8, writer.write(b"01234567").unwrap());
|
||||
assert_eq!(8, writer.write(b"01234567").unwrap());
|
||||
assert!(writer.flush().is_err());
|
||||
writer.flush().unwrap_err();
|
||||
}
|
||||
|
||||
{
|
||||
@@ -536,7 +536,7 @@ mod test {
|
||||
|
||||
// trying to write_all too much should fail
|
||||
writer.write_all(b"aaaaaaaaaabbbbb").unwrap();
|
||||
assert!(writer.flush().is_err());
|
||||
writer.flush().unwrap_err();
|
||||
}
|
||||
|
||||
{
|
||||
|
||||
@@ -214,7 +214,7 @@ mod test {
|
||||
let mut s = [0u8; 10];
|
||||
blob.read_at_exact(&mut s, 0).unwrap();
|
||||
assert_eq!(&s, &one2ten, "write should go through");
|
||||
assert!(blob.read_at_exact(&mut s, 1).is_err());
|
||||
blob.read_at_exact(&mut s, 1).unwrap_err();
|
||||
|
||||
blob.read_at_exact(&mut s, 0).unwrap();
|
||||
assert_eq!(&s, &one2ten, "should be unchanged");
|
||||
@@ -225,13 +225,13 @@ mod test {
|
||||
|
||||
blob.read_at_exact(&mut fives, 5).unwrap();
|
||||
assert_eq!(&fives, &[6u8, 7, 8, 9, 10]);
|
||||
assert!(blob.read_at_exact(&mut fives, 7).is_err());
|
||||
assert!(blob.read_at_exact(&mut fives, 12).is_err());
|
||||
assert!(blob.read_at_exact(&mut fives, 10).is_err());
|
||||
assert!(blob.read_at_exact(&mut fives, i32::MAX as usize).is_err());
|
||||
assert!(blob
|
||||
.read_at_exact(&mut fives, i32::MAX as usize + 1)
|
||||
.is_err());
|
||||
blob.read_at_exact(&mut fives, 7).unwrap_err();
|
||||
blob.read_at_exact(&mut fives, 12).unwrap_err();
|
||||
blob.read_at_exact(&mut fives, 10).unwrap_err();
|
||||
blob.read_at_exact(&mut fives, i32::MAX as usize)
|
||||
.unwrap_err();
|
||||
blob.read_at_exact(&mut fives, i32::MAX as usize + 1)
|
||||
.unwrap_err();
|
||||
|
||||
// zero length writes are fine if in bounds
|
||||
blob.read_at_exact(&mut [], 10).unwrap();
|
||||
@@ -242,13 +242,11 @@ mod test {
|
||||
blob.read_at_exact(&mut s, 0).unwrap();
|
||||
assert_eq!(&s, &[1u8, 2, 3, 4, 5, 16, 17, 18, 19, 20]);
|
||||
|
||||
assert!(blob.write_at(&[100, 99, 98, 97, 96], 6).is_err());
|
||||
assert!(blob
|
||||
.write_at(&[100, 99, 98, 97, 96], i32::MAX as usize)
|
||||
.is_err());
|
||||
assert!(blob
|
||||
.write_at(&[100, 99, 98, 97, 96], i32::MAX as usize + 1)
|
||||
.is_err());
|
||||
blob.write_at(&[100, 99, 98, 97, 96], 6).unwrap_err();
|
||||
blob.write_at(&[100, 99, 98, 97, 96], i32::MAX as usize)
|
||||
.unwrap_err();
|
||||
blob.write_at(&[100, 99, 98, 97, 96], i32::MAX as usize + 1)
|
||||
.unwrap_err();
|
||||
|
||||
blob.read_at_exact(&mut s, 0).unwrap();
|
||||
assert_eq!(&s, &[1u8, 2, 3, 4, 5, 16, 17, 18, 19, 20]);
|
||||
@@ -265,7 +263,7 @@ mod test {
|
||||
blob.raw_read_at_exact(&mut empty, 0).unwrap().as_ptr(),
|
||||
empty.as_ptr().cast(),
|
||||
));
|
||||
assert!(blob.raw_read_at_exact(&mut s2, 5).is_err());
|
||||
blob.raw_read_at_exact(&mut s2, 5).unwrap_err();
|
||||
|
||||
let end_pos = blob.seek(std::io::SeekFrom::Current(0)).unwrap();
|
||||
assert_eq!(end_pos, 1);
|
||||
|
||||
@@ -58,11 +58,7 @@ impl Connection {
|
||||
pub fn busy_handler(&self, callback: Option<fn(i32) -> bool>) -> Result<()> {
|
||||
unsafe extern "C" fn busy_handler_callback(p_arg: *mut c_void, count: c_int) -> c_int {
|
||||
let handler_fn: fn(i32) -> bool = mem::transmute(p_arg);
|
||||
if let Ok(true) = catch_unwind(|| handler_fn(count)) {
|
||||
1
|
||||
} else {
|
||||
0
|
||||
}
|
||||
c_int::from(catch_unwind(|| handler_fn(count)).unwrap_or_default())
|
||||
}
|
||||
let c = self.db.borrow_mut();
|
||||
let r = match callback {
|
||||
|
||||
@@ -33,7 +33,7 @@ impl Statement<'_> {
|
||||
/// calling this method.
|
||||
pub fn column_names(&self) -> Vec<&str> {
|
||||
let n = self.column_count();
|
||||
let mut cols = Vec::with_capacity(n as usize);
|
||||
let mut cols = Vec::with_capacity(n);
|
||||
for i in 0..n {
|
||||
let s = self.column_name_unwrap(i);
|
||||
cols.push(s);
|
||||
@@ -95,6 +95,7 @@ impl Statement<'_> {
|
||||
pub fn column_name(&self, col: usize) -> Result<&str> {
|
||||
self.stmt
|
||||
.column_name(col)
|
||||
// clippy::or_fun_call (nightly) vs clippy::unnecessary-lazy-evaluations (stable)
|
||||
.ok_or(Error::InvalidColumnIndex(col))
|
||||
.map(|slice| {
|
||||
str::from_utf8(slice.to_bytes()).expect("Invalid UTF-8 sequence in column name")
|
||||
@@ -137,7 +138,7 @@ impl Statement<'_> {
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "column_decltype")))]
|
||||
pub fn columns(&self) -> Vec<Column> {
|
||||
let n = self.column_count();
|
||||
let mut cols = Vec::with_capacity(n as usize);
|
||||
let mut cols = Vec::with_capacity(n);
|
||||
for i in 0..n {
|
||||
let name = self.column_name_unwrap(i);
|
||||
let slice = self.stmt.column_decltype(i);
|
||||
|
||||
@@ -16,12 +16,12 @@ pub enum DbConfig {
|
||||
//SQLITE_DBCONFIG_MAINDBNAME = 1000, /* const char* */
|
||||
//SQLITE_DBCONFIG_LOOKASIDE = 1001, /* void* int int */
|
||||
/// Enable or disable the enforcement of foreign key constraints.
|
||||
SQLITE_DBCONFIG_ENABLE_FKEY = 1002,
|
||||
SQLITE_DBCONFIG_ENABLE_FKEY = ffi::SQLITE_DBCONFIG_ENABLE_FKEY,
|
||||
/// Enable or disable triggers.
|
||||
SQLITE_DBCONFIG_ENABLE_TRIGGER = 1003,
|
||||
SQLITE_DBCONFIG_ENABLE_TRIGGER = ffi::SQLITE_DBCONFIG_ENABLE_TRIGGER,
|
||||
/// Enable or disable the fts3_tokenizer() function which is part of the
|
||||
/// FTS3 full-text search engine extension.
|
||||
SQLITE_DBCONFIG_ENABLE_FTS3_TOKENIZER = 1004, // 3.12.0
|
||||
SQLITE_DBCONFIG_ENABLE_FTS3_TOKENIZER = ffi::SQLITE_DBCONFIG_ENABLE_FTS3_TOKENIZER, // 3.12.0
|
||||
//SQLITE_DBCONFIG_ENABLE_LOAD_EXTENSION = 1005,
|
||||
/// In WAL mode, enable or disable the checkpoint operation before closing
|
||||
/// the connection.
|
||||
@@ -115,7 +115,7 @@ impl Connection {
|
||||
check(ffi::sqlite3_db_config(
|
||||
c.db(),
|
||||
config as c_int,
|
||||
if new_val { 1 } else { 0 },
|
||||
new_val as c_int,
|
||||
&mut val,
|
||||
))?;
|
||||
Ok(val != 0)
|
||||
|
||||
@@ -43,7 +43,7 @@ pub(super) unsafe fn set_result(ctx: *mut sqlite3_context, result: &ToSqlOutput<
|
||||
ValueRef::Real(r) => ffi::sqlite3_result_double(ctx, r),
|
||||
ValueRef::Text(s) => {
|
||||
let length = s.len();
|
||||
if length > c_int::max_value() as usize {
|
||||
if length > c_int::MAX as usize {
|
||||
ffi::sqlite3_result_error_toobig(ctx);
|
||||
} else {
|
||||
let (c_str, len, destructor) = match str_for_sqlite(s) {
|
||||
@@ -57,7 +57,7 @@ pub(super) unsafe fn set_result(ctx: *mut sqlite3_context, result: &ToSqlOutput<
|
||||
}
|
||||
ValueRef::Blob(b) => {
|
||||
let length = b.len();
|
||||
if length > c_int::max_value() as usize {
|
||||
if length > c_int::MAX as usize {
|
||||
ffi::sqlite3_result_error_toobig(ctx);
|
||||
} else if length == 0 {
|
||||
ffi::sqlite3_result_zeroblob(ctx, 0);
|
||||
|
||||
36
src/error.rs
36
src/error.rs
@@ -34,7 +34,7 @@ pub enum Error {
|
||||
|
||||
/// Error converting a string to a C-compatible string because it contained
|
||||
/// an embedded nul.
|
||||
NulError(::std::ffi::NulError),
|
||||
NulError(std::ffi::NulError),
|
||||
|
||||
/// Error when using SQL named parameters and passing a parameter name not
|
||||
/// present in the SQL.
|
||||
@@ -212,14 +212,14 @@ impl From<str::Utf8Error> for Error {
|
||||
}
|
||||
}
|
||||
|
||||
impl From<::std::ffi::NulError> for Error {
|
||||
impl From<std::ffi::NulError> for Error {
|
||||
#[cold]
|
||||
fn from(err: ::std::ffi::NulError) -> Error {
|
||||
fn from(err: std::ffi::NulError) -> Error {
|
||||
Error::NulError(err)
|
||||
}
|
||||
}
|
||||
|
||||
const UNKNOWN_COLUMN: usize = std::usize::MAX;
|
||||
const UNKNOWN_COLUMN: usize = usize::MAX;
|
||||
|
||||
/// The conversion isn't precise, but it's convenient to have it
|
||||
/// to allow use of `get_raw(…).as_…()?` in callbacks that take `Error`.
|
||||
@@ -245,7 +245,7 @@ impl fmt::Display for Error {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
match *self {
|
||||
Error::SqliteFailure(ref err, None) => err.fmt(f),
|
||||
Error::SqliteFailure(_, Some(ref s)) => write!(f, "{}", s),
|
||||
Error::SqliteFailure(_, Some(ref s)) => write!(f, "{s}"),
|
||||
Error::SqliteSingleThreadedMode => write!(
|
||||
f,
|
||||
"SQLite was compiled or configured for single-threaded use only"
|
||||
@@ -263,21 +263,21 @@ impl fmt::Display for Error {
|
||||
}
|
||||
Error::IntegralValueOutOfRange(col, val) => {
|
||||
if col != UNKNOWN_COLUMN {
|
||||
write!(f, "Integer {} out of range at index {}", val, col)
|
||||
write!(f, "Integer {val} out of range at index {col}")
|
||||
} else {
|
||||
write!(f, "Integer {} out of range", val)
|
||||
write!(f, "Integer {val} out of range")
|
||||
}
|
||||
}
|
||||
Error::Utf8Error(ref err) => err.fmt(f),
|
||||
Error::NulError(ref err) => err.fmt(f),
|
||||
Error::InvalidParameterName(ref name) => write!(f, "Invalid parameter name: {}", name),
|
||||
Error::InvalidParameterName(ref name) => write!(f, "Invalid parameter name: {name}"),
|
||||
Error::InvalidPath(ref p) => write!(f, "Invalid path: {}", p.to_string_lossy()),
|
||||
Error::ExecuteReturnedResults => {
|
||||
write!(f, "Execute returned results - did you mean to call query?")
|
||||
}
|
||||
Error::QueryReturnedNoRows => write!(f, "Query returned no rows"),
|
||||
Error::InvalidColumnIndex(i) => write!(f, "Invalid column index: {}", i),
|
||||
Error::InvalidColumnName(ref name) => write!(f, "Invalid column name: {}", name),
|
||||
Error::InvalidColumnIndex(i) => write!(f, "Invalid column index: {i}"),
|
||||
Error::InvalidColumnName(ref name) => write!(f, "Invalid column name: {name}"),
|
||||
Error::InvalidColumnType(i, ref name, ref t) => write!(
|
||||
f,
|
||||
"Invalid column type {} at index: {}, name: {}",
|
||||
@@ -288,22 +288,22 @@ impl fmt::Display for Error {
|
||||
"Wrong number of parameters passed to query. Got {}, needed {}",
|
||||
i1, n1
|
||||
),
|
||||
Error::StatementChangedRows(i) => write!(f, "Query changed {} rows", i),
|
||||
Error::StatementChangedRows(i) => write!(f, "Query changed {i} rows"),
|
||||
|
||||
#[cfg(feature = "functions")]
|
||||
Error::InvalidFunctionParameterType(i, ref t) => {
|
||||
write!(f, "Invalid function parameter type {} at index {}", t, i)
|
||||
write!(f, "Invalid function parameter type {t} at index {i}")
|
||||
}
|
||||
#[cfg(feature = "vtab")]
|
||||
Error::InvalidFilterParameterType(i, ref t) => {
|
||||
write!(f, "Invalid filter parameter type {} at index {}", t, i)
|
||||
write!(f, "Invalid filter parameter type {t} at index {i}")
|
||||
}
|
||||
#[cfg(feature = "functions")]
|
||||
Error::UserFunctionError(ref err) => err.fmt(f),
|
||||
Error::ToSqlConversionFailure(ref err) => err.fmt(f),
|
||||
Error::InvalidQuery => write!(f, "Query is not read-only"),
|
||||
#[cfg(feature = "vtab")]
|
||||
Error::ModuleError(ref desc) => write!(f, "{}", desc),
|
||||
Error::ModuleError(ref desc) => write!(f, "{desc}"),
|
||||
#[cfg(feature = "functions")]
|
||||
Error::UnwindingPanic => write!(f, "unwinding panic"),
|
||||
#[cfg(feature = "functions")]
|
||||
@@ -317,7 +317,7 @@ impl fmt::Display for Error {
|
||||
offset,
|
||||
ref sql,
|
||||
..
|
||||
} => write!(f, "{} in {} at offset {}", msg, sql, offset),
|
||||
} => write!(f, "{msg} in {sql} at offset {offset}"),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -408,13 +408,13 @@ pub unsafe fn error_from_handle(db: *mut ffi::sqlite3, code: c_int) -> Error {
|
||||
}
|
||||
|
||||
#[cold]
|
||||
#[cfg(not(all(feature = "modern_sqlite", not(feature = "bundled-sqlcipher"))))] // SQLite >= 3.38.0
|
||||
#[cfg(not(feature = "modern_sqlite"))] // SQLite >= 3.38.0
|
||||
pub unsafe fn error_with_offset(db: *mut ffi::sqlite3, code: c_int, _sql: &str) -> Error {
|
||||
error_from_handle(db, code)
|
||||
}
|
||||
|
||||
#[cold]
|
||||
#[cfg(all(feature = "modern_sqlite", not(feature = "bundled-sqlcipher")))] // SQLite >= 3.38.0
|
||||
#[cfg(feature = "modern_sqlite")] // SQLite >= 3.38.0
|
||||
pub unsafe fn error_with_offset(db: *mut ffi::sqlite3, code: c_int, sql: &str) -> Error {
|
||||
if db.is_null() {
|
||||
error_from_sqlite_code(code, None)
|
||||
@@ -438,7 +438,7 @@ pub unsafe fn error_with_offset(db: *mut ffi::sqlite3, code: c_int, sql: &str) -
|
||||
|
||||
pub fn check(code: c_int) -> Result<()> {
|
||||
if code != crate::ffi::SQLITE_OK {
|
||||
Err(crate::error::error_from_sqlite_code(code, None))
|
||||
Err(error_from_sqlite_code(code, None))
|
||||
} else {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -75,14 +75,9 @@ unsafe fn report_error(ctx: *mut sqlite3_context, err: &Error) {
|
||||
// an explicit feature check for that, and this doesn't really warrant one.
|
||||
// We'll use the extended code if we're on the bundled version (since it's
|
||||
// at least 3.17.0) and the normal constraint error code if not.
|
||||
#[cfg(feature = "modern_sqlite")]
|
||||
fn constraint_error_code() -> i32 {
|
||||
ffi::SQLITE_CONSTRAINT_FUNCTION
|
||||
}
|
||||
#[cfg(not(feature = "modern_sqlite"))]
|
||||
fn constraint_error_code() -> i32 {
|
||||
ffi::SQLITE_CONSTRAINT
|
||||
}
|
||||
|
||||
if let Error::SqliteFailure(ref err, ref s) = *err {
|
||||
ffi::sqlite3_result_error_code(ctx, err.extended_code);
|
||||
@@ -168,8 +163,6 @@ impl Context<'_> {
|
||||
///
|
||||
/// Will panic if `idx` is greater than or equal to
|
||||
/// [`self.len()`](Context::len).
|
||||
#[cfg(feature = "modern_sqlite")] // 3.9.0
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "modern_sqlite")))]
|
||||
pub fn get_subtype(&self, idx: usize) -> std::os::raw::c_uint {
|
||||
let arg = self.args[idx];
|
||||
unsafe { ffi::sqlite3_value_subtype(arg) }
|
||||
@@ -639,7 +632,7 @@ unsafe extern "C" fn call_boxed_step<A, D, T>(
|
||||
D: Aggregate<A, T>,
|
||||
T: ToSql,
|
||||
{
|
||||
let pac = if let Some(pac) = aggregate_context(ctx, ::std::mem::size_of::<*mut A>()) {
|
||||
let pac = if let Some(pac) = aggregate_context(ctx, std::mem::size_of::<*mut A>()) {
|
||||
pac
|
||||
} else {
|
||||
ffi::sqlite3_result_error_nomem(ctx);
|
||||
@@ -686,7 +679,7 @@ unsafe extern "C" fn call_boxed_inverse<A, W, T>(
|
||||
W: WindowAggregate<A, T>,
|
||||
T: ToSql,
|
||||
{
|
||||
let pac = if let Some(pac) = aggregate_context(ctx, ::std::mem::size_of::<*mut A>()) {
|
||||
let pac = if let Some(pac) = aggregate_context(ctx, std::mem::size_of::<*mut A>()) {
|
||||
pac
|
||||
} else {
|
||||
ffi::sqlite3_result_error_nomem(ctx);
|
||||
@@ -821,7 +814,6 @@ where
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use regex::Regex;
|
||||
use std::f64::EPSILON;
|
||||
use std::os::raw::c_double;
|
||||
|
||||
#[cfg(feature = "window")]
|
||||
@@ -846,7 +838,7 @@ mod test {
|
||||
)?;
|
||||
let result: Result<f64> = db.query_row("SELECT half(6)", [], |r| r.get(0));
|
||||
|
||||
assert!((3f64 - result?).abs() < EPSILON);
|
||||
assert!((3f64 - result?).abs() < f64::EPSILON);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -860,11 +852,11 @@ mod test {
|
||||
half,
|
||||
)?;
|
||||
let result: Result<f64> = db.query_row("SELECT half(6)", [], |r| r.get(0));
|
||||
assert!((3f64 - result?).abs() < EPSILON);
|
||||
assert!((3f64 - result?).abs() < f64::EPSILON);
|
||||
|
||||
db.remove_function("half", 1)?;
|
||||
let result: Result<f64> = db.query_row("SELECT half(6)", [], |r| r.get(0));
|
||||
assert!(result.is_err());
|
||||
result.unwrap_err();
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
||||
26
src/hooks.rs
26
src/hooks.rs
@@ -10,7 +10,7 @@ use crate::ffi;
|
||||
use crate::{Connection, InnerConnection};
|
||||
|
||||
/// Action Codes
|
||||
#[derive(Clone, Copy, Debug, PartialEq)]
|
||||
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
|
||||
#[repr(i32)]
|
||||
#[non_exhaustive]
|
||||
#[allow(clippy::upper_case_acronyms)]
|
||||
@@ -37,10 +37,10 @@ impl From<i32> for Action {
|
||||
}
|
||||
}
|
||||
|
||||
/// The context recieved by an authorizer hook.
|
||||
/// The context received by an authorizer hook.
|
||||
///
|
||||
/// See <https://sqlite.org/c3ref/set_authorizer.html> for more info.
|
||||
#[derive(Clone, Copy, Debug, PartialEq)]
|
||||
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
|
||||
pub struct AuthContext<'c> {
|
||||
/// The action to be authorized.
|
||||
pub action: AuthAction<'c>,
|
||||
@@ -57,7 +57,7 @@ pub struct AuthContext<'c> {
|
||||
/// preparation.
|
||||
///
|
||||
/// See <https://sqlite.org/c3ref/c_alter_table.html> for more info.
|
||||
#[derive(Clone, Copy, Debug, PartialEq)]
|
||||
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
|
||||
#[non_exhaustive]
|
||||
#[allow(missing_docs)]
|
||||
pub enum AuthAction<'c> {
|
||||
@@ -181,7 +181,6 @@ pub enum AuthAction<'c> {
|
||||
operation: TransactionOperation,
|
||||
savepoint_name: &'c str,
|
||||
},
|
||||
#[cfg(feature = "modern_sqlite")]
|
||||
Recursive,
|
||||
}
|
||||
|
||||
@@ -285,7 +284,6 @@ impl<'c> AuthAction<'c> {
|
||||
operation: TransactionOperation::from_str(operation_str),
|
||||
savepoint_name,
|
||||
},
|
||||
#[cfg(feature = "modern_sqlite")] // 3.8.3
|
||||
(ffi::SQLITE_RECURSIVE, ..) => Self::Recursive,
|
||||
(code, arg1, arg2) => Self::Unknown { code, arg1, arg2 },
|
||||
}
|
||||
@@ -296,7 +294,7 @@ pub(crate) type BoxedAuthorizer =
|
||||
Box<dyn for<'c> FnMut(AuthContext<'c>) -> Authorization + Send + 'static>;
|
||||
|
||||
/// A transaction operation.
|
||||
#[derive(Clone, Copy, Debug, PartialEq)]
|
||||
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
|
||||
#[non_exhaustive]
|
||||
#[allow(missing_docs)]
|
||||
pub enum TransactionOperation {
|
||||
@@ -318,7 +316,7 @@ impl TransactionOperation {
|
||||
}
|
||||
|
||||
/// [`authorizer`](Connection::authorizer) return code
|
||||
#[derive(Clone, Copy, Debug, PartialEq)]
|
||||
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
|
||||
#[non_exhaustive]
|
||||
pub enum Authorization {
|
||||
/// Authorize the action.
|
||||
@@ -428,11 +426,7 @@ impl InnerConnection {
|
||||
let boxed_hook: *mut F = p_arg.cast::<F>();
|
||||
(*boxed_hook)()
|
||||
});
|
||||
if let Ok(true) = r {
|
||||
1
|
||||
} else {
|
||||
0
|
||||
}
|
||||
c_int::from(r.unwrap_or_default())
|
||||
}
|
||||
|
||||
// unlike `sqlite3_create_function_v2`, we cannot specify a `xDestroy` with
|
||||
@@ -570,11 +564,7 @@ impl InnerConnection {
|
||||
let boxed_handler: *mut F = p_arg.cast::<F>();
|
||||
(*boxed_handler)()
|
||||
});
|
||||
if let Ok(true) = r {
|
||||
1
|
||||
} else {
|
||||
0
|
||||
}
|
||||
c_int::from(r.unwrap_or_default())
|
||||
}
|
||||
|
||||
if let Some(handler) = handler {
|
||||
|
||||
@@ -25,11 +25,11 @@ pub struct InnerConnection {
|
||||
// interrupt would only acquire the lock after the query's completion.
|
||||
interrupt_lock: Arc<Mutex<*mut ffi::sqlite3>>,
|
||||
#[cfg(feature = "hooks")]
|
||||
pub free_commit_hook: Option<unsafe fn(*mut ::std::os::raw::c_void)>,
|
||||
pub free_commit_hook: Option<unsafe fn(*mut std::os::raw::c_void)>,
|
||||
#[cfg(feature = "hooks")]
|
||||
pub free_rollback_hook: Option<unsafe fn(*mut ::std::os::raw::c_void)>,
|
||||
pub free_rollback_hook: Option<unsafe fn(*mut std::os::raw::c_void)>,
|
||||
#[cfg(feature = "hooks")]
|
||||
pub free_update_hook: Option<unsafe fn(*mut ::std::os::raw::c_void)>,
|
||||
pub free_update_hook: Option<unsafe fn(*mut std::os::raw::c_void)>,
|
||||
#[cfg(feature = "hooks")]
|
||||
pub progress_handler: Option<Box<dyn FnMut() -> bool + Send>>,
|
||||
#[cfg(feature = "hooks")]
|
||||
@@ -105,7 +105,7 @@ impl InnerConnection {
|
||||
{
|
||||
e = Error::SqliteFailure(
|
||||
ffi::Error::new(r),
|
||||
Some(format!("{}: {}", msg, c_path.to_string_lossy())),
|
||||
Some(format!("{msg}: {}", c_path.to_string_lossy())),
|
||||
);
|
||||
}
|
||||
ffi::sqlite3_close(db);
|
||||
@@ -208,7 +208,7 @@ impl InnerConnection {
|
||||
Ok(())
|
||||
} else {
|
||||
let message = super::errmsg_to_string(errmsg);
|
||||
ffi::sqlite3_free(errmsg.cast::<::std::os::raw::c_void>());
|
||||
ffi::sqlite3_free(errmsg.cast::<std::os::raw::c_void>());
|
||||
Err(error_from_sqlite_code(r, Some(message)))
|
||||
}
|
||||
}
|
||||
@@ -295,7 +295,6 @@ impl InnerConnection {
|
||||
unsafe { ffi::sqlite3_get_autocommit(self.db()) != 0 }
|
||||
}
|
||||
|
||||
#[cfg(feature = "modern_sqlite")] // 3.8.6
|
||||
pub fn is_busy(&self) -> bool {
|
||||
let db = self.db();
|
||||
unsafe {
|
||||
@@ -310,7 +309,6 @@ impl InnerConnection {
|
||||
false
|
||||
}
|
||||
|
||||
#[cfg(feature = "modern_sqlite")] // 3.10.0
|
||||
pub fn cache_flush(&mut self) -> Result<()> {
|
||||
crate::error::check(unsafe { ffi::sqlite3_db_cacheflush(self.db()) })
|
||||
}
|
||||
@@ -319,7 +317,6 @@ impl InnerConnection {
|
||||
#[inline]
|
||||
fn remove_hooks(&mut self) {}
|
||||
|
||||
#[cfg(feature = "modern_sqlite")] // 3.7.11
|
||||
pub fn db_readonly(&self, db_name: super::DatabaseName<'_>) -> Result<bool> {
|
||||
let name = db_name.as_cstring()?;
|
||||
let r = unsafe { ffi::sqlite3_db_readonly(self.db, name.as_ptr()) };
|
||||
@@ -328,7 +325,7 @@ impl InnerConnection {
|
||||
1 => Ok(true),
|
||||
-1 => Err(Error::SqliteFailure(
|
||||
ffi::Error::new(ffi::SQLITE_MISUSE),
|
||||
Some(format!("{:?} is not the name of a database", db_name)),
|
||||
Some(format!("{db_name:?} is not the name of a database")),
|
||||
)),
|
||||
_ => Err(error_from_sqlite_code(
|
||||
r,
|
||||
@@ -354,7 +351,7 @@ impl InnerConnection {
|
||||
2 => Ok(super::transaction::TransactionState::Write),
|
||||
-1 => Err(Error::SqliteFailure(
|
||||
ffi::Error::new(ffi::SQLITE_MISUSE),
|
||||
Some(format!("{:?} is not the name of a valid schema", db_name)),
|
||||
Some(format!("{db_name:?} is not the name of a valid schema")),
|
||||
)),
|
||||
_ => Err(error_from_sqlite_code(
|
||||
r,
|
||||
@@ -378,7 +375,7 @@ impl Drop for InnerConnection {
|
||||
|
||||
if let Err(e) = self.close() {
|
||||
if panicking() {
|
||||
eprintln!("Error while closing SQLite connection: {:?}", e);
|
||||
eprintln!("Error while closing SQLite connection: {e:?}");
|
||||
} else {
|
||||
panic!("Error while closing SQLite connection: {:?}", e);
|
||||
}
|
||||
@@ -400,7 +397,7 @@ fn ensure_safe_sqlite_threading_mode() -> Result<()> {
|
||||
|
||||
#[cfg(not(any(target_arch = "wasm32")))]
|
||||
fn ensure_safe_sqlite_threading_mode() -> Result<()> {
|
||||
// Ensure SQLite was compiled in thredsafe mode.
|
||||
// Ensure SQLite was compiled in threadsafe mode.
|
||||
if unsafe { ffi::sqlite3_threadsafe() == 0 } {
|
||||
return Err(Error::SqliteSingleThreadedMode);
|
||||
}
|
||||
|
||||
67
src/lib.rs
67
src/lib.rs
@@ -57,7 +57,6 @@
|
||||
pub use libsqlite3_sys as ffi;
|
||||
|
||||
use std::cell::RefCell;
|
||||
use std::convert;
|
||||
use std::default::Default;
|
||||
use std::ffi::{CStr, CString};
|
||||
use std::fmt;
|
||||
@@ -272,7 +271,7 @@ fn str_for_sqlite(s: &[u8]) -> Result<(*const c_char, c_int, ffi::sqlite3_destru
|
||||
// Helper to cast to c_int safely, returning the correct error type if the cast
|
||||
// failed.
|
||||
fn len_as_c_int(len: usize) -> Result<c_int> {
|
||||
if len >= (c_int::max_value() as usize) {
|
||||
if len >= (c_int::MAX as usize) {
|
||||
Err(Error::SqliteFailure(
|
||||
ffi::Error::new(ffi::SQLITE_TOOBIG),
|
||||
None,
|
||||
@@ -315,15 +314,9 @@ pub const TEMP_DB: DatabaseName<'static> = DatabaseName::Temp;
|
||||
|
||||
// Currently DatabaseName is only used by the backup and blob mods, so hide
|
||||
// this (private) impl to avoid dead code warnings.
|
||||
#[cfg(any(
|
||||
feature = "backup",
|
||||
feature = "blob",
|
||||
feature = "session",
|
||||
feature = "modern_sqlite"
|
||||
))]
|
||||
impl DatabaseName<'_> {
|
||||
#[inline]
|
||||
fn as_cstring(&self) -> Result<util::SmallCString> {
|
||||
fn as_cstring(&self) -> Result<SmallCString> {
|
||||
use self::DatabaseName::{Attached, Main, Temp};
|
||||
match *self {
|
||||
Main => str_to_cstring("main"),
|
||||
@@ -724,7 +717,7 @@ impl Connection {
|
||||
where
|
||||
P: Params,
|
||||
F: FnOnce(&Row<'_>) -> Result<T, E>,
|
||||
E: convert::From<Error>,
|
||||
E: From<Error>,
|
||||
{
|
||||
let mut stmt = self.prepare(sql)?;
|
||||
stmt.check_no_tail()?;
|
||||
@@ -962,22 +955,16 @@ impl Connection {
|
||||
|
||||
/// Determine if all associated prepared statements have been reset.
|
||||
#[inline]
|
||||
#[cfg(feature = "modern_sqlite")] // 3.8.6
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "modern_sqlite")))]
|
||||
pub fn is_busy(&self) -> bool {
|
||||
self.db.borrow().is_busy()
|
||||
}
|
||||
|
||||
/// Flush caches to disk mid-transaction
|
||||
#[cfg(feature = "modern_sqlite")] // 3.10.0
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "modern_sqlite")))]
|
||||
pub fn cache_flush(&self) -> Result<()> {
|
||||
self.db.borrow_mut().cache_flush()
|
||||
}
|
||||
|
||||
/// Determine if a database is read-only
|
||||
#[cfg(feature = "modern_sqlite")] // 3.7.11
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "modern_sqlite")))]
|
||||
pub fn is_readonly(&self, db_name: DatabaseName<'_>) -> Result<bool> {
|
||||
self.db.borrow().db_readonly(db_name)
|
||||
}
|
||||
@@ -1071,9 +1058,9 @@ bitflags::bitflags! {
|
||||
/// The database is created if it does not already exist
|
||||
const SQLITE_OPEN_CREATE = ffi::SQLITE_OPEN_CREATE;
|
||||
/// The filename can be interpreted as a URI if this flag is set.
|
||||
const SQLITE_OPEN_URI = 0x0000_0040;
|
||||
const SQLITE_OPEN_URI = ffi::SQLITE_OPEN_URI;
|
||||
/// The database will be opened as an in-memory database.
|
||||
const SQLITE_OPEN_MEMORY = 0x0000_0080;
|
||||
const SQLITE_OPEN_MEMORY = ffi::SQLITE_OPEN_MEMORY;
|
||||
/// The new database connection will not use a per-connection mutex (the
|
||||
/// connection will use the "multi-thread" threading mode, in SQLite
|
||||
/// parlance).
|
||||
@@ -1177,7 +1164,6 @@ impl InterruptHandle {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "modern_sqlite")] // 3.7.10
|
||||
unsafe fn db_filename(db: *mut ffi::sqlite3) -> Option<PathBuf> {
|
||||
let db_name = DatabaseName::Main.as_cstring().unwrap();
|
||||
let db_filename = ffi::sqlite3_db_filename(db, db_name.as_ptr());
|
||||
@@ -1187,10 +1173,6 @@ unsafe fn db_filename(db: *mut ffi::sqlite3) -> Option<PathBuf> {
|
||||
CStr::from_ptr(db_filename).to_str().ok().map(PathBuf::from)
|
||||
}
|
||||
}
|
||||
#[cfg(not(feature = "modern_sqlite"))]
|
||||
unsafe fn db_filename(_: *mut ffi::sqlite3) -> Option<PathBuf> {
|
||||
None
|
||||
}
|
||||
|
||||
#[cfg(doctest)]
|
||||
doc_comment::doctest!("../README.md");
|
||||
@@ -1278,7 +1260,7 @@ mod test {
|
||||
}
|
||||
|
||||
let path_string = path.to_str().unwrap();
|
||||
let db = Connection::open(&path_string)?;
|
||||
let db = Connection::open(path_string)?;
|
||||
let the_answer: Result<i64> = db.query_row("SELECT x FROM foo", [], |r| r.get(0));
|
||||
|
||||
assert_eq!(42i64, the_answer?);
|
||||
@@ -1287,17 +1269,16 @@ mod test {
|
||||
|
||||
#[test]
|
||||
fn test_open() {
|
||||
assert!(Connection::open_in_memory().is_ok());
|
||||
Connection::open_in_memory().unwrap();
|
||||
|
||||
let db = checked_memory_handle();
|
||||
assert!(db.close().is_ok());
|
||||
db.close().unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_open_failure() {
|
||||
let filename = "no_such_file.db";
|
||||
let result = Connection::open_with_flags(filename, OpenFlags::SQLITE_OPEN_READ_ONLY);
|
||||
assert!(result.is_err());
|
||||
let err = result.unwrap_err();
|
||||
if let Error::SqliteFailure(e, Some(msg)) = err {
|
||||
assert_eq!(ErrorCode::CannotOpen, e.code);
|
||||
@@ -1392,7 +1373,7 @@ mod test {
|
||||
OpenFlags::SQLITE_OPEN_READ_ONLY | OpenFlags::SQLITE_OPEN_READ_WRITE,
|
||||
OpenFlags::SQLITE_OPEN_READ_ONLY | OpenFlags::SQLITE_OPEN_CREATE,
|
||||
] {
|
||||
assert!(Connection::open_in_memory_with_flags(*bad_flags).is_err());
|
||||
Connection::open_in_memory_with_flags(*bad_flags).unwrap_err();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1410,7 +1391,7 @@ mod test {
|
||||
|
||||
db.execute_batch("UPDATE foo SET x = 3 WHERE x < 3")?;
|
||||
|
||||
assert!(db.execute_batch("INVALID SQL").is_err());
|
||||
db.execute_batch("INVALID SQL").unwrap_err();
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -1434,8 +1415,9 @@ mod test {
|
||||
fn test_execute_select() {
|
||||
let db = checked_memory_handle();
|
||||
let err = db.execute("SELECT 1 WHERE 1 < ?", [1i32]).unwrap_err();
|
||||
assert!(
|
||||
err == Error::ExecuteReturnedResults,
|
||||
assert_eq!(
|
||||
err,
|
||||
Error::ExecuteReturnedResults,
|
||||
"Unexpected error: {}",
|
||||
err
|
||||
);
|
||||
@@ -1572,7 +1554,7 @@ mod test {
|
||||
|
||||
let bad_query_result = db.query_row("NOT A PROPER QUERY; test123", [], |_| Ok(()));
|
||||
|
||||
assert!(bad_query_result.is_err());
|
||||
bad_query_result.unwrap_err();
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -1596,7 +1578,7 @@ mod test {
|
||||
|
||||
let bad_query_result: Result<i64> = db.query_row("NOT A PROPER QUERY", [], |r| r.get(0));
|
||||
let bad_query_result = bad_query_result.optional();
|
||||
assert!(bad_query_result.is_err());
|
||||
bad_query_result.unwrap_err();
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -1634,7 +1616,7 @@ mod test {
|
||||
db.execute_batch("CREATE TABLE foo(x INTEGER);")?;
|
||||
|
||||
let err = db.prepare("SELECT * FROM does_not_exist").unwrap_err();
|
||||
assert!(format!("{}", err).contains("does_not_exist"));
|
||||
assert!(format!("{err}").contains("does_not_exist"));
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -1665,7 +1647,6 @@ mod test {
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(feature = "modern_sqlite")]
|
||||
fn test_is_busy() -> Result<()> {
|
||||
let db = Connection::open_in_memory()?;
|
||||
assert!(!db.is_busy());
|
||||
@@ -1688,7 +1669,7 @@ mod test {
|
||||
let query = "SELECT 12345";
|
||||
let stmt = db.prepare(query)?;
|
||||
|
||||
assert!(format!("{:?}", stmt).contains(query));
|
||||
assert!(format!("{stmt:?}").contains(query));
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -1696,18 +1677,14 @@ mod test {
|
||||
fn test_notnull_constraint_error() -> Result<()> {
|
||||
// extended error codes for constraints were added in SQLite 3.7.16; if we're
|
||||
// running on our bundled version, we know the extended error code exists.
|
||||
#[cfg(feature = "modern_sqlite")]
|
||||
fn check_extended_code(extended_code: c_int) {
|
||||
assert_eq!(extended_code, ffi::SQLITE_CONSTRAINT_NOTNULL);
|
||||
}
|
||||
#[cfg(not(feature = "modern_sqlite"))]
|
||||
fn check_extended_code(_extended_code: c_int) {}
|
||||
|
||||
let db = Connection::open_in_memory()?;
|
||||
db.execute_batch("CREATE TABLE foo(x NOT NULL)")?;
|
||||
|
||||
let result = db.execute("INSERT INTO foo (x) VALUES (NULL)", []);
|
||||
assert!(result.is_err());
|
||||
|
||||
match result.unwrap_err() {
|
||||
Error::SqliteFailure(err, _) => {
|
||||
@@ -1726,7 +1703,7 @@ mod test {
|
||||
let minor = (n % 1_000_000) / 1_000;
|
||||
let patch = n % 1_000;
|
||||
|
||||
assert!(version().contains(&format!("{}.{}.{}", major, minor, patch)));
|
||||
assert!(version().contains(&format!("{major}.{minor}.{patch}")));
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -1739,7 +1716,7 @@ mod test {
|
||||
db.create_scalar_function(
|
||||
"interrupt",
|
||||
0,
|
||||
crate::functions::FunctionFlags::default(),
|
||||
functions::FunctionFlags::default(),
|
||||
move |_| {
|
||||
interrupt_handle.interrupt();
|
||||
Ok((0, None))
|
||||
@@ -1833,7 +1810,7 @@ mod test {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
|
||||
match *self {
|
||||
CustomError::SomeError => write!(f, "my custom error"),
|
||||
CustomError::Sqlite(ref se) => write!(f, "my custom error: {}", se),
|
||||
CustomError::Sqlite(ref se) => write!(f, "my custom error: {se}"),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2111,17 +2088,15 @@ mod test {
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(feature = "modern_sqlite")]
|
||||
fn test_cache_flush() -> Result<()> {
|
||||
let db = Connection::open_in_memory()?;
|
||||
db.cache_flush()
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(feature = "modern_sqlite")]
|
||||
pub fn db_readonly() -> Result<()> {
|
||||
let db = Connection::open_in_memory()?;
|
||||
assert!(!db.is_readonly(super::MAIN_DB)?);
|
||||
assert!(!db.is_readonly(MAIN_DB)?);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -39,10 +39,10 @@ pub enum Limit {
|
||||
/// The maximum index number of any parameter in an SQL statement.
|
||||
SQLITE_LIMIT_VARIABLE_NUMBER = ffi::SQLITE_LIMIT_VARIABLE_NUMBER,
|
||||
/// The maximum depth of recursion for triggers.
|
||||
SQLITE_LIMIT_TRIGGER_DEPTH = 10,
|
||||
SQLITE_LIMIT_TRIGGER_DEPTH = ffi::SQLITE_LIMIT_TRIGGER_DEPTH,
|
||||
/// The maximum number of auxiliary worker threads that a single prepared
|
||||
/// statement may start.
|
||||
SQLITE_LIMIT_WORKER_THREADS = 11,
|
||||
SQLITE_LIMIT_WORKER_THREADS = ffi::SQLITE_LIMIT_WORKER_THREADS,
|
||||
}
|
||||
|
||||
impl Connection {
|
||||
@@ -71,55 +71,49 @@ mod test {
|
||||
|
||||
#[test]
|
||||
fn test_limit_values() {
|
||||
assert_eq!(
|
||||
Limit::SQLITE_LIMIT_LENGTH as i32,
|
||||
ffi::SQLITE_LIMIT_LENGTH as i32,
|
||||
);
|
||||
assert_eq!(Limit::SQLITE_LIMIT_LENGTH as i32, ffi::SQLITE_LIMIT_LENGTH,);
|
||||
assert_eq!(
|
||||
Limit::SQLITE_LIMIT_SQL_LENGTH as i32,
|
||||
ffi::SQLITE_LIMIT_SQL_LENGTH as i32,
|
||||
);
|
||||
assert_eq!(
|
||||
Limit::SQLITE_LIMIT_COLUMN as i32,
|
||||
ffi::SQLITE_LIMIT_COLUMN as i32,
|
||||
ffi::SQLITE_LIMIT_SQL_LENGTH,
|
||||
);
|
||||
assert_eq!(Limit::SQLITE_LIMIT_COLUMN as i32, ffi::SQLITE_LIMIT_COLUMN,);
|
||||
assert_eq!(
|
||||
Limit::SQLITE_LIMIT_EXPR_DEPTH as i32,
|
||||
ffi::SQLITE_LIMIT_EXPR_DEPTH as i32,
|
||||
ffi::SQLITE_LIMIT_EXPR_DEPTH,
|
||||
);
|
||||
assert_eq!(
|
||||
Limit::SQLITE_LIMIT_COMPOUND_SELECT as i32,
|
||||
ffi::SQLITE_LIMIT_COMPOUND_SELECT as i32,
|
||||
ffi::SQLITE_LIMIT_COMPOUND_SELECT,
|
||||
);
|
||||
assert_eq!(
|
||||
Limit::SQLITE_LIMIT_VDBE_OP as i32,
|
||||
ffi::SQLITE_LIMIT_VDBE_OP as i32,
|
||||
ffi::SQLITE_LIMIT_VDBE_OP,
|
||||
);
|
||||
assert_eq!(
|
||||
Limit::SQLITE_LIMIT_FUNCTION_ARG as i32,
|
||||
ffi::SQLITE_LIMIT_FUNCTION_ARG as i32,
|
||||
ffi::SQLITE_LIMIT_FUNCTION_ARG,
|
||||
);
|
||||
assert_eq!(
|
||||
Limit::SQLITE_LIMIT_ATTACHED as i32,
|
||||
ffi::SQLITE_LIMIT_ATTACHED as i32,
|
||||
ffi::SQLITE_LIMIT_ATTACHED,
|
||||
);
|
||||
assert_eq!(
|
||||
Limit::SQLITE_LIMIT_LIKE_PATTERN_LENGTH as i32,
|
||||
ffi::SQLITE_LIMIT_LIKE_PATTERN_LENGTH as i32,
|
||||
ffi::SQLITE_LIMIT_LIKE_PATTERN_LENGTH,
|
||||
);
|
||||
assert_eq!(
|
||||
Limit::SQLITE_LIMIT_VARIABLE_NUMBER as i32,
|
||||
ffi::SQLITE_LIMIT_VARIABLE_NUMBER as i32,
|
||||
ffi::SQLITE_LIMIT_VARIABLE_NUMBER,
|
||||
);
|
||||
#[cfg(feature = "bundled")]
|
||||
assert_eq!(
|
||||
Limit::SQLITE_LIMIT_TRIGGER_DEPTH as i32,
|
||||
ffi::SQLITE_LIMIT_TRIGGER_DEPTH as i32,
|
||||
ffi::SQLITE_LIMIT_TRIGGER_DEPTH,
|
||||
);
|
||||
#[cfg(feature = "bundled")]
|
||||
assert_eq!(
|
||||
Limit::SQLITE_LIMIT_WORKER_THREADS as i32,
|
||||
ffi::SQLITE_LIMIT_WORKER_THREADS as i32,
|
||||
ffi::SQLITE_LIMIT_WORKER_THREADS,
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -41,7 +41,7 @@ use sealed::Sealed;
|
||||
/// - Using the [`rusqlite::params!`](crate::params!) macro, e.g.
|
||||
/// `thing.query(rusqlite::params![1, "foo", bar])`. This is mostly useful for
|
||||
/// heterogeneous lists where the number of parameters greater than 16, or
|
||||
/// homogenous lists of paramters where the number of parameters exceeds 32.
|
||||
/// homogenous lists of parameters where the number of parameters exceeds 32.
|
||||
///
|
||||
/// - For small homogeneous lists of parameters, they can either be passed as:
|
||||
///
|
||||
|
||||
@@ -37,7 +37,7 @@ impl Sql {
|
||||
} else {
|
||||
Err(Error::SqliteFailure(
|
||||
ffi::Error::new(ffi::SQLITE_MISUSE),
|
||||
Some(format!("Invalid keyword \"{}\"", keyword)),
|
||||
Some(format!("Invalid keyword \"{keyword}\"")),
|
||||
))
|
||||
}
|
||||
}
|
||||
@@ -67,14 +67,14 @@ impl Sql {
|
||||
ToSqlOutput::ZeroBlob(_) => {
|
||||
return Err(Error::SqliteFailure(
|
||||
ffi::Error::new(ffi::SQLITE_MISUSE),
|
||||
Some(format!("Unsupported value \"{:?}\"", value)),
|
||||
Some(format!("Unsupported value \"{value:?}\"")),
|
||||
));
|
||||
}
|
||||
#[cfg(feature = "array")]
|
||||
ToSqlOutput::Array(_) => {
|
||||
return Err(Error::SqliteFailure(
|
||||
ffi::Error::new(ffi::SQLITE_MISUSE),
|
||||
Some(format!("Unsupported value \"{:?}\"", value)),
|
||||
Some(format!("Unsupported value \"{value:?}\"")),
|
||||
));
|
||||
}
|
||||
};
|
||||
@@ -92,7 +92,7 @@ impl Sql {
|
||||
_ => {
|
||||
return Err(Error::SqliteFailure(
|
||||
ffi::Error::new(ffi::SQLITE_MISUSE),
|
||||
Some(format!("Unsupported value \"{:?}\"", value)),
|
||||
Some(format!("Unsupported value \"{value:?}\"")),
|
||||
));
|
||||
}
|
||||
};
|
||||
@@ -369,7 +369,7 @@ mod test {
|
||||
fn pragma() -> Result<()> {
|
||||
let db = Connection::open_in_memory()?;
|
||||
let mut columns = Vec::new();
|
||||
db.pragma(None, "table_info", &"sqlite_master", |row| {
|
||||
db.pragma(None, "table_info", "sqlite_master", |row| {
|
||||
let column: String = row.get(1)?;
|
||||
columns.push(column);
|
||||
Ok(())
|
||||
@@ -412,8 +412,8 @@ mod test {
|
||||
journal_mode,
|
||||
);
|
||||
// Sanity checks to ensure the move to a generic `ToSql` wasn't breaking
|
||||
let mode = db
|
||||
.pragma_update_and_check(None, "journal_mode", &"OFF", |row| row.get::<_, String>(0))?;
|
||||
let mode =
|
||||
db.pragma_update_and_check(None, "journal_mode", "OFF", |row| row.get::<_, String>(0))?;
|
||||
assert!(mode == "off" || mode == "memory", "mode: {:?}", mode);
|
||||
|
||||
let param: &dyn crate::ToSql = &"OFF";
|
||||
@@ -448,7 +448,7 @@ mod test {
|
||||
#[test]
|
||||
fn locking_mode() -> Result<()> {
|
||||
let db = Connection::open_in_memory()?;
|
||||
let r = db.pragma_update(None, "locking_mode", &"exclusive");
|
||||
let r = db.pragma_update(None, "locking_mode", "exclusive");
|
||||
if cfg!(feature = "extra_check") {
|
||||
r.unwrap_err();
|
||||
} else {
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
use super::ffi;
|
||||
use super::StatementStatus;
|
||||
use crate::util::ParamIndexCache;
|
||||
#[cfg(feature = "modern_sqlite")]
|
||||
use crate::util::SqliteMallocString;
|
||||
use std::ffi::CStr;
|
||||
use std::os::raw::c_int;
|
||||
@@ -110,7 +109,7 @@ impl RawStatement {
|
||||
#[cfg(feature = "unlock_notify")]
|
||||
pub fn step(&self) -> c_int {
|
||||
use crate::unlock_notify;
|
||||
let mut db = core::ptr::null_mut::<ffi::sqlite3>();
|
||||
let mut db = ptr::null_mut::<ffi::sqlite3>();
|
||||
loop {
|
||||
unsafe {
|
||||
let mut rc = ffi::sqlite3_step(self.ptr);
|
||||
@@ -196,14 +195,13 @@ impl RawStatement {
|
||||
}
|
||||
|
||||
// does not work for PRAGMA
|
||||
#[cfg(feature = "extra_check")]
|
||||
#[inline]
|
||||
#[cfg(all(feature = "extra_check", feature = "modern_sqlite"))] // 3.7.4
|
||||
pub fn readonly(&self) -> bool {
|
||||
unsafe { ffi::sqlite3_stmt_readonly(self.ptr) != 0 }
|
||||
}
|
||||
|
||||
#[inline]
|
||||
#[cfg(feature = "modern_sqlite")] // 3.14.0
|
||||
pub(crate) fn expanded_sql(&self) -> Option<SqliteMallocString> {
|
||||
unsafe { SqliteMallocString::from_raw(ffi::sqlite3_expanded_sql(self.ptr)) }
|
||||
}
|
||||
|
||||
@@ -171,7 +171,7 @@ pub struct AndThenRows<'stmt, F> {
|
||||
|
||||
impl<T, E, F> Iterator for AndThenRows<'_, F>
|
||||
where
|
||||
E: convert::From<Error>,
|
||||
E: From<Error>,
|
||||
F: FnMut(&Row<'_>) -> Result<T, E>,
|
||||
{
|
||||
type Item = Result<T, E>;
|
||||
@@ -391,7 +391,7 @@ impl RowIndex for usize {
|
||||
impl RowIndex for &'_ str {
|
||||
#[inline]
|
||||
fn idx(&self, stmt: &Statement<'_>) -> Result<usize> {
|
||||
stmt.column_index(*self)
|
||||
stmt.column_index(self)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -448,7 +448,7 @@ mod tests {
|
||||
let val = conn.query_row("SELECT a FROM test", [], |row| <(u32,)>::try_from(row))?;
|
||||
assert_eq!(val, (42,));
|
||||
let fail = conn.query_row("SELECT a FROM test", [], |row| <(u32, u32)>::try_from(row));
|
||||
assert!(fail.is_err());
|
||||
fail.unwrap_err();
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -466,7 +466,7 @@ mod tests {
|
||||
let fail = conn.query_row("SELECT a, b FROM test", [], |row| {
|
||||
<(u32, u32, u32)>::try_from(row)
|
||||
});
|
||||
assert!(fail.is_err());
|
||||
fail.unwrap_err();
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
||||
@@ -19,12 +19,14 @@ use crate::{errmsg_to_string, str_to_cstring, Connection, DatabaseName, Result};
|
||||
|
||||
// https://sqlite.org/session.html
|
||||
|
||||
type Filter = Option<Box<dyn Fn(&str) -> bool>>;
|
||||
|
||||
/// An instance of this object is a session that can be
|
||||
/// used to record changes to a database.
|
||||
pub struct Session<'conn> {
|
||||
phantom: PhantomData<&'conn Connection>,
|
||||
s: *mut ffi::sqlite3_session,
|
||||
filter: Option<Box<dyn Fn(&str) -> bool>>,
|
||||
filter: Filter,
|
||||
}
|
||||
|
||||
impl Session<'_> {
|
||||
@@ -168,7 +170,7 @@ impl Session<'_> {
|
||||
if r != ffi::SQLITE_OK {
|
||||
let errmsg: *mut c_char = errmsg;
|
||||
let message = errmsg_to_string(&*errmsg);
|
||||
ffi::sqlite3_free(errmsg as *mut ::std::os::raw::c_void);
|
||||
ffi::sqlite3_free(errmsg as *mut c_void);
|
||||
return Err(error_from_sqlite_code(r, Some(message)));
|
||||
}
|
||||
}
|
||||
@@ -656,7 +658,7 @@ impl Connection {
|
||||
/// See [here](https://sqlite.org/session.html#SQLITE_CHANGESET_CONFLICT) for details.
|
||||
#[allow(missing_docs)]
|
||||
#[repr(i32)]
|
||||
#[derive(Debug, PartialEq)]
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
#[non_exhaustive]
|
||||
#[allow(clippy::upper_case_acronyms)]
|
||||
pub enum ConflictType {
|
||||
@@ -684,7 +686,7 @@ impl From<i32> for ConflictType {
|
||||
/// See [here](https://sqlite.org/session.html#SQLITE_CHANGESET_ABORT) for details.
|
||||
#[allow(missing_docs)]
|
||||
#[repr(i32)]
|
||||
#[derive(Debug, PartialEq)]
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
#[non_exhaustive]
|
||||
#[allow(clippy::upper_case_acronyms)]
|
||||
pub enum ConflictAction {
|
||||
|
||||
@@ -3,7 +3,7 @@ use std::os::raw::{c_int, c_void};
|
||||
#[cfg(feature = "array")]
|
||||
use std::rc::Rc;
|
||||
use std::slice::from_raw_parts;
|
||||
use std::{convert, fmt, mem, ptr, str};
|
||||
use std::{fmt, mem, ptr, str};
|
||||
|
||||
use super::ffi;
|
||||
use super::{len_as_c_int, str_for_sqlite};
|
||||
@@ -202,7 +202,7 @@ impl Statement<'_> {
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// Or, equivalently (but without the [`params!`] macro).
|
||||
/// Or, equivalently (but without the [`crate::params!`] macro).
|
||||
///
|
||||
/// ```rust,no_run
|
||||
/// # use rusqlite::{Connection, Result};
|
||||
@@ -417,7 +417,7 @@ impl Statement<'_> {
|
||||
pub fn query_and_then<T, E, P, F>(&mut self, params: P, f: F) -> Result<AndThenRows<'_, F>>
|
||||
where
|
||||
P: Params,
|
||||
E: convert::From<Error>,
|
||||
E: From<Error>,
|
||||
F: FnMut(&Row<'_>) -> Result<T, E>,
|
||||
{
|
||||
self.query(params).map(|rows| rows.and_then(f))
|
||||
@@ -447,7 +447,7 @@ impl Statement<'_> {
|
||||
f: F,
|
||||
) -> Result<AndThenRows<'_, F>>
|
||||
where
|
||||
E: convert::From<Error>,
|
||||
E: From<Error>,
|
||||
F: FnMut(&Row<'_>) -> Result<T, E>,
|
||||
{
|
||||
self.query_and_then(params, f)
|
||||
@@ -796,7 +796,7 @@ impl Statement<'_> {
|
||||
self.conn.decode_result(stmt.finalize())
|
||||
}
|
||||
|
||||
#[cfg(all(feature = "modern_sqlite", feature = "extra_check"))]
|
||||
#[cfg(feature = "extra_check")]
|
||||
#[inline]
|
||||
fn check_update(&self) -> Result<()> {
|
||||
// sqlite3_column_count works for DML but not for DDL (ie ALTER)
|
||||
@@ -806,16 +806,6 @@ impl Statement<'_> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(all(not(feature = "modern_sqlite"), feature = "extra_check"))]
|
||||
#[inline]
|
||||
fn check_update(&self) -> Result<()> {
|
||||
// sqlite3_column_count works for DML but not for DDL (ie ALTER)
|
||||
if self.column_count() > 0 {
|
||||
return Err(Error::ExecuteReturnedResults);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "extra_check"))]
|
||||
#[inline]
|
||||
#[allow(clippy::unnecessary_wraps)]
|
||||
@@ -825,8 +815,6 @@ impl Statement<'_> {
|
||||
|
||||
/// Returns a string containing the SQL text of prepared statement with
|
||||
/// bound parameters expanded.
|
||||
#[cfg(feature = "modern_sqlite")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "modern_sqlite")))]
|
||||
pub fn expanded_sql(&self) -> Option<String> {
|
||||
self.stmt
|
||||
.expanded_sql()
|
||||
@@ -1407,7 +1395,6 @@ mod test {
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(feature = "modern_sqlite")]
|
||||
fn test_expanded_sql() -> Result<()> {
|
||||
let db = Connection::open_in_memory()?;
|
||||
let stmt = db.prepare("SELECT ?")?;
|
||||
@@ -1422,7 +1409,7 @@ mod test {
|
||||
// dynamic slice:
|
||||
db.query_row(
|
||||
"SELECT ?1, ?2, ?3",
|
||||
&[&1u8 as &dyn ToSql, &"one", &Some("one")],
|
||||
[&1u8 as &dyn ToSql, &"one", &Some("one")],
|
||||
|row| row.get::<_, u8>(0),
|
||||
)?;
|
||||
// existing collection:
|
||||
@@ -1474,10 +1461,10 @@ mod test {
|
||||
let conn = Connection::open_in_memory()?;
|
||||
let mut stmt = conn.prepare("")?;
|
||||
assert_eq!(0, stmt.column_count());
|
||||
assert!(stmt.parameter_index("test").is_ok());
|
||||
assert!(stmt.step().is_err());
|
||||
stmt.parameter_index("test").unwrap();
|
||||
stmt.step().unwrap_err();
|
||||
stmt.reset();
|
||||
assert!(stmt.execute([]).is_err());
|
||||
stmt.execute([]).unwrap_err();
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -1507,12 +1494,12 @@ mod test {
|
||||
#[test]
|
||||
fn test_utf16_conversion() -> Result<()> {
|
||||
let db = Connection::open_in_memory()?;
|
||||
db.pragma_update(None, "encoding", &"UTF-16le")?;
|
||||
db.pragma_update(None, "encoding", "UTF-16le")?;
|
||||
let encoding: String = db.pragma_query_value(None, "encoding", |row| row.get(0))?;
|
||||
assert_eq!("UTF-16le", encoding);
|
||||
db.execute_batch("CREATE TABLE foo(x TEXT)")?;
|
||||
let expected = "テスト";
|
||||
db.execute("INSERT INTO foo(x) VALUES (?)", &[&expected])?;
|
||||
db.execute("INSERT INTO foo(x) VALUES (?)", [&expected])?;
|
||||
let actual: String = db.query_row("SELECT x FROM foo", [], |row| row.get(0))?;
|
||||
assert_eq!(expected, actual);
|
||||
Ok(())
|
||||
@@ -1537,12 +1524,11 @@ mod test {
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(all(feature = "modern_sqlite", not(feature = "bundled-sqlcipher")))] // SQLite >= 3.38.0
|
||||
#[cfg(feature = "modern_sqlite")] // SQLite >= 3.38.0
|
||||
fn test_error_offset() -> Result<()> {
|
||||
use crate::ffi::ErrorCode;
|
||||
let db = Connection::open_in_memory()?;
|
||||
let r = db.execute_batch("SELECT CURRENT_TIMESTANP;");
|
||||
assert!(r.is_err());
|
||||
match r.unwrap_err() {
|
||||
Error::SqlInputError { error, offset, .. } => {
|
||||
assert_eq!(error.code, ErrorCode::Unknown);
|
||||
|
||||
@@ -255,7 +255,7 @@ impl Savepoint<'_> {
|
||||
name: T,
|
||||
) -> Result<Savepoint<'_>> {
|
||||
let name = name.into();
|
||||
conn.execute_batch(&format!("SAVEPOINT {}", name))
|
||||
conn.execute_batch(&format!("SAVEPOINT {name}"))
|
||||
.map(|_| Savepoint {
|
||||
conn,
|
||||
name,
|
||||
@@ -267,7 +267,7 @@ impl Savepoint<'_> {
|
||||
|
||||
#[inline]
|
||||
fn with_depth(conn: &Connection, depth: u32) -> Result<Savepoint<'_>> {
|
||||
let name = format!("_rusqlite_sp_{}", depth);
|
||||
let name = format!("_rusqlite_sp_{depth}");
|
||||
Savepoint::with_depth_and_name(conn, depth, name)
|
||||
}
|
||||
|
||||
@@ -559,7 +559,7 @@ mod test {
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
fn assert_nested_tx_error(e: crate::Error) {
|
||||
fn assert_nested_tx_error(e: Error) {
|
||||
if let Error::SqliteFailure(e, Some(m)) = &e {
|
||||
assert_eq!(e.extended_code, crate::ffi::SQLITE_ERROR);
|
||||
// FIXME: Not ideal...
|
||||
|
||||
@@ -287,15 +287,15 @@ mod test {
|
||||
fn test_sqlite_functions() -> Result<()> {
|
||||
let db = checked_memory_handle()?;
|
||||
let result: Result<NaiveTime> = db.query_row("SELECT CURRENT_TIME", [], |r| r.get(0));
|
||||
assert!(result.is_ok());
|
||||
result.unwrap();
|
||||
let result: Result<NaiveDate> = db.query_row("SELECT CURRENT_DATE", [], |r| r.get(0));
|
||||
assert!(result.is_ok());
|
||||
result.unwrap();
|
||||
let result: Result<NaiveDateTime> =
|
||||
db.query_row("SELECT CURRENT_TIMESTAMP", [], |r| r.get(0));
|
||||
assert!(result.is_ok());
|
||||
result.unwrap();
|
||||
let result: Result<DateTime<Utc>> =
|
||||
db.query_row("SELECT CURRENT_TIMESTAMP", [], |r| r.get(0));
|
||||
assert!(result.is_ok());
|
||||
result.unwrap();
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -303,7 +303,7 @@ mod test {
|
||||
fn test_naive_date_time_param() -> Result<()> {
|
||||
let db = checked_memory_handle()?;
|
||||
let result: Result<bool> = db.query_row("SELECT 1 WHERE ? BETWEEN datetime('now', '-1 minute') AND datetime('now', '+1 minute')", [Utc::now().naive_utc()], |r| r.get(0));
|
||||
assert!(result.is_ok());
|
||||
result.unwrap();
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -311,13 +311,13 @@ mod test {
|
||||
fn test_date_time_param() -> Result<()> {
|
||||
let db = checked_memory_handle()?;
|
||||
let result: Result<bool> = db.query_row("SELECT 1 WHERE ? BETWEEN datetime('now', '-1 minute') AND datetime('now', '+1 minute')", [Utc::now()], |r| r.get(0));
|
||||
assert!(result.is_ok());
|
||||
result.unwrap();
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_lenient_parse_timezone() {
|
||||
assert!(DateTime::<Utc>::column_result(ValueRef::Text(b"1970-01-01T00:00:00Z")).is_ok());
|
||||
assert!(DateTime::<Utc>::column_result(ValueRef::Text(b"1970-01-01T00:00:00+00")).is_ok());
|
||||
DateTime::<Utc>::column_result(ValueRef::Text(b"1970-01-01T00:00:00Z")).unwrap();
|
||||
DateTime::<Utc>::column_result(ValueRef::Text(b"1970-01-01T00:00:00+00")).unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -52,7 +52,7 @@ impl fmt::Display for FromSqlError {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
match *self {
|
||||
FromSqlError::InvalidType => write!(f, "Invalid type"),
|
||||
FromSqlError::OutOfRange(i) => write!(f, "Value {} out of range", i),
|
||||
FromSqlError::OutOfRange(i) => write!(f, "Value {i} out of range"),
|
||||
FromSqlError::InvalidBlobSize {
|
||||
expected_size,
|
||||
blob_size,
|
||||
@@ -240,11 +240,11 @@ mod test {
|
||||
|
||||
fn check_ranges<T>(db: &Connection, out_of_range: &[i64], in_range: &[i64])
|
||||
where
|
||||
T: Into<i64> + FromSql + ::std::fmt::Debug,
|
||||
T: Into<i64> + FromSql + std::fmt::Debug,
|
||||
{
|
||||
for n in out_of_range {
|
||||
let err = db
|
||||
.query_row("SELECT ?", &[n], |r| r.get::<_, T>(0))
|
||||
.query_row("SELECT ?", [n], |r| r.get::<_, T>(0))
|
||||
.unwrap_err();
|
||||
match err {
|
||||
Error::IntegralValueOutOfRange(_, value) => assert_eq!(*n, value),
|
||||
@@ -254,7 +254,7 @@ mod test {
|
||||
for n in in_range {
|
||||
assert_eq!(
|
||||
*n,
|
||||
db.query_row("SELECT ?", &[n], |r| r.get::<_, T>(0))
|
||||
db.query_row("SELECT ?", [n], |r| r.get::<_, T>(0))
|
||||
.unwrap()
|
||||
.into()
|
||||
);
|
||||
|
||||
@@ -140,7 +140,6 @@ impl fmt::Display for Type {
|
||||
mod test {
|
||||
use super::Value;
|
||||
use crate::{params, Connection, Error, Result, Statement};
|
||||
use std::f64::EPSILON;
|
||||
use std::os::raw::{c_double, c_int};
|
||||
|
||||
fn checked_memory_handle() -> Result<Connection> {
|
||||
@@ -154,7 +153,7 @@ mod test {
|
||||
let db = checked_memory_handle()?;
|
||||
|
||||
let v1234 = vec![1u8, 2, 3, 4];
|
||||
db.execute("INSERT INTO foo(b) VALUES (?)", &[&v1234])?;
|
||||
db.execute("INSERT INTO foo(b) VALUES (?)", [&v1234])?;
|
||||
|
||||
let v: Vec<u8> = db.query_row("SELECT b FROM foo", [], |r| r.get(0))?;
|
||||
assert_eq!(v, v1234);
|
||||
@@ -166,7 +165,7 @@ mod test {
|
||||
let db = checked_memory_handle()?;
|
||||
|
||||
let empty = vec![];
|
||||
db.execute("INSERT INTO foo(b) VALUES (?)", &[&empty])?;
|
||||
db.execute("INSERT INTO foo(b) VALUES (?)", [&empty])?;
|
||||
|
||||
let v: Vec<u8> = db.query_row("SELECT b FROM foo", [], |r| r.get(0))?;
|
||||
assert_eq!(v, empty);
|
||||
@@ -178,7 +177,7 @@ mod test {
|
||||
let db = checked_memory_handle()?;
|
||||
|
||||
let s = "hello, world!";
|
||||
db.execute("INSERT INTO foo(t) VALUES (?)", &[&s])?;
|
||||
db.execute("INSERT INTO foo(t) VALUES (?)", [&s])?;
|
||||
|
||||
let from: String = db.query_row("SELECT t FROM foo", [], |r| r.get(0))?;
|
||||
assert_eq!(from, s);
|
||||
@@ -217,8 +216,8 @@ mod test {
|
||||
let s = Some("hello, world!");
|
||||
let b = Some(vec![1u8, 2, 3, 4]);
|
||||
|
||||
db.execute("INSERT INTO foo(t) VALUES (?)", &[&s])?;
|
||||
db.execute("INSERT INTO foo(b) VALUES (?)", &[&b])?;
|
||||
db.execute("INSERT INTO foo(t) VALUES (?)", [&s])?;
|
||||
db.execute("INSERT INTO foo(b) VALUES (?)", [&b])?;
|
||||
|
||||
let mut stmt = db.prepare("SELECT t, b FROM foo ORDER BY ROWID ASC")?;
|
||||
let mut rows = stmt.query([])?;
|
||||
@@ -264,7 +263,7 @@ mod test {
|
||||
assert_eq!(vec![1, 2], row.get::<_, Vec<u8>>(0)?);
|
||||
assert_eq!("text", row.get::<_, String>(1)?);
|
||||
assert_eq!(1, row.get::<_, c_int>(2)?);
|
||||
assert!((1.5 - row.get::<_, c_double>(3)?).abs() < EPSILON);
|
||||
assert!((1.5 - row.get::<_, c_double>(3)?).abs() < f64::EPSILON);
|
||||
assert_eq!(row.get::<_, Option<c_int>>(4)?, None);
|
||||
assert_eq!(row.get::<_, Option<c_double>>(4)?, None);
|
||||
assert_eq!(row.get::<_, Option<String>>(4)?, None);
|
||||
@@ -355,7 +354,7 @@ mod test {
|
||||
assert_eq!(Value::Text(String::from("text")), row.get::<_, Value>(1)?);
|
||||
assert_eq!(Value::Integer(1), row.get::<_, Value>(2)?);
|
||||
match row.get::<_, Value>(3)? {
|
||||
Value::Real(val) => assert!((1.5 - val).abs() < EPSILON),
|
||||
Value::Real(val) => assert!((1.5 - val).abs() < f64::EPSILON),
|
||||
x => panic!("Invalid Value {:?}", x),
|
||||
}
|
||||
assert_eq!(Value::Null, row.get::<_, Value>(4)?);
|
||||
|
||||
@@ -41,7 +41,7 @@ mod test {
|
||||
let data: serde_json::Value = serde_json::from_str(json).unwrap();
|
||||
db.execute(
|
||||
"INSERT INTO foo (t, b) VALUES (?, ?)",
|
||||
&[&data as &dyn ToSql, &json.as_bytes()],
|
||||
[&data as &dyn ToSql, &json.as_bytes()],
|
||||
)?;
|
||||
|
||||
let t: serde_json::Value = db.query_row("SELECT t FROM foo", [], |r| r.get(0))?;
|
||||
|
||||
@@ -154,7 +154,7 @@ mod test {
|
||||
let db = Connection::open_in_memory()?;
|
||||
let result: Result<OffsetDateTime> =
|
||||
db.query_row("SELECT CURRENT_TIMESTAMP", [], |r| r.get(0));
|
||||
assert!(result.is_ok());
|
||||
result.unwrap();
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -162,7 +162,7 @@ mod test {
|
||||
fn test_param() -> Result<()> {
|
||||
let db = Connection::open_in_memory()?;
|
||||
let result: Result<bool> = db.query_row("SELECT 1 WHERE ? BETWEEN datetime('now', '-1 minute') AND datetime('now', '+1 minute')", [OffsetDateTime::now_utc()], |r| r.get(0));
|
||||
assert!(result.is_ok());
|
||||
result.unwrap();
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -278,7 +278,7 @@ mod test {
|
||||
let _a: &[&dyn ToSql] = crate::params![a];
|
||||
let r = ToSql::to_sql(&a);
|
||||
|
||||
assert!(r.is_ok());
|
||||
r.unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -287,10 +287,10 @@ mod test {
|
||||
let s = "str";
|
||||
let cow: Cow<str> = Cow::Borrowed(s);
|
||||
let r = cow.to_sql();
|
||||
assert!(r.is_ok());
|
||||
r.unwrap();
|
||||
let cow: Cow<str> = Cow::Owned::<str>(String::from(s));
|
||||
let r = cow.to_sql();
|
||||
assert!(r.is_ok());
|
||||
r.unwrap();
|
||||
// Ensure this compiles.
|
||||
let _p: &[&dyn ToSql] = crate::params![cow];
|
||||
}
|
||||
@@ -301,7 +301,7 @@ mod test {
|
||||
let _s: &[&dyn ToSql] = crate::params![s];
|
||||
let r = ToSql::to_sql(&s);
|
||||
|
||||
assert!(r.is_ok());
|
||||
r.unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -310,7 +310,7 @@ mod test {
|
||||
let _s: &[&dyn ToSql] = crate::params![s];
|
||||
let r = s.to_sql();
|
||||
|
||||
assert!(r.is_ok());
|
||||
r.unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -319,7 +319,7 @@ mod test {
|
||||
let _s: &[&dyn ToSql] = crate::params![s];
|
||||
let r = ToSql::to_sql(&s);
|
||||
|
||||
assert!(r.is_ok());
|
||||
r.unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -331,39 +331,38 @@ mod test {
|
||||
let s: Rc<Box<str>> = Rc::new(source_str.clone());
|
||||
let _s: &[&dyn ToSql] = crate::params![s];
|
||||
let r = s.to_sql();
|
||||
assert!(r.is_ok());
|
||||
r.unwrap();
|
||||
|
||||
let s: Arc<Box<str>> = Arc::new(source_str.clone());
|
||||
let _s: &[&dyn ToSql] = crate::params![s];
|
||||
let r = s.to_sql();
|
||||
assert!(r.is_ok());
|
||||
r.unwrap();
|
||||
|
||||
let s: Arc<str> = Arc::from(&*source_str);
|
||||
let _s: &[&dyn ToSql] = crate::params![s];
|
||||
let r = s.to_sql();
|
||||
assert!(r.is_ok());
|
||||
r.unwrap();
|
||||
|
||||
let s: Arc<dyn ToSql> = Arc::new(source_str.clone());
|
||||
let _s: &[&dyn ToSql] = crate::params![s];
|
||||
let r = s.to_sql();
|
||||
assert!(r.is_ok());
|
||||
r.unwrap();
|
||||
|
||||
let s: Rc<str> = Rc::from(&*source_str);
|
||||
let _s: &[&dyn ToSql] = crate::params![s];
|
||||
let r = s.to_sql();
|
||||
assert!(r.is_ok());
|
||||
r.unwrap();
|
||||
|
||||
let s: Rc<dyn ToSql> = Rc::new(source_str);
|
||||
let _s: &[&dyn ToSql] = crate::params![s];
|
||||
let r = s.to_sql();
|
||||
assert!(r.is_ok());
|
||||
r.unwrap();
|
||||
}
|
||||
|
||||
#[cfg(feature = "i128_blob")]
|
||||
#[test]
|
||||
fn test_i128() -> crate::Result<()> {
|
||||
use crate::Connection;
|
||||
use std::i128;
|
||||
let db = Connection::open_in_memory()?;
|
||||
db.execute_batch("CREATE TABLE foo (i128 BLOB, desc TEXT)")?;
|
||||
db.execute(
|
||||
|
||||
@@ -5,7 +5,5 @@ pub(crate) use param_cache::ParamIndexCache;
|
||||
pub(crate) use small_cstr::SmallCString;
|
||||
|
||||
// Doesn't use any modern features or vtab stuff, but is only used by them.
|
||||
#[cfg(any(feature = "modern_sqlite", feature = "vtab"))]
|
||||
mod sqlite_string;
|
||||
#[cfg(any(feature = "modern_sqlite", feature = "vtab"))]
|
||||
pub(crate) use sqlite_string::SqliteMallocString;
|
||||
|
||||
@@ -5,7 +5,7 @@ use std::ffi::{CStr, CString, NulError};
|
||||
/// small enough. Also guarantees it's input is UTF-8 -- used for cases where we
|
||||
/// need to pass a NUL-terminated string to SQLite, and we have a `&str`.
|
||||
#[derive(Clone, PartialEq, Eq, PartialOrd, Ord)]
|
||||
pub(crate) struct SmallCString(smallvec::SmallVec<[u8; 16]>);
|
||||
pub(crate) struct SmallCString(SmallVec<[u8; 16]>);
|
||||
|
||||
impl SmallCString {
|
||||
#[inline]
|
||||
@@ -163,8 +163,8 @@ mod test {
|
||||
assert_eq!(SmallCString::new("").unwrap().0.as_slice(), b"\0");
|
||||
assert_eq!(SmallCString::new("").unwrap().as_bytes_without_nul(), b"");
|
||||
|
||||
assert!(SmallCString::new("\0").is_err());
|
||||
assert!(SmallCString::new("\0abc").is_err());
|
||||
assert!(SmallCString::new("abc\0").is_err());
|
||||
SmallCString::new("\0").unwrap_err();
|
||||
SmallCString::new("\0abc").unwrap_err();
|
||||
SmallCString::new("abc\0").unwrap_err();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,10 +1,7 @@
|
||||
// This is used when either vtab or modern-sqlite is on. Different methods are
|
||||
// used in each feature. Avoid having to track this for each function. We will
|
||||
// still warn for anything that's not used by either, though.
|
||||
#![cfg_attr(
|
||||
not(all(feature = "vtab", feature = "modern-sqlite")),
|
||||
allow(dead_code)
|
||||
)]
|
||||
#![cfg_attr(not(feature = "vtab"), allow(dead_code))]
|
||||
use crate::ffi;
|
||||
use std::marker::PhantomData;
|
||||
use std::os::raw::{c_char, c_int};
|
||||
@@ -134,7 +131,8 @@ impl SqliteMallocString {
|
||||
// (everything is aligned to 1)
|
||||
// - `size` is also never zero, although this function doesn't actually require
|
||||
// it now.
|
||||
let layout = Layout::from_size_align_unchecked(s.len().saturating_add(1), 1);
|
||||
let len = s.len().saturating_add(1).min(isize::MAX as usize);
|
||||
let layout = Layout::from_size_align_unchecked(len, 1);
|
||||
// Note: This call does not return.
|
||||
handle_alloc_error(layout);
|
||||
});
|
||||
@@ -214,7 +212,7 @@ mod test {
|
||||
let mut v = vec![];
|
||||
for i in 0..1000 {
|
||||
v.push(SqliteMallocString::from_str(&i.to_string()).into_raw());
|
||||
v.push(SqliteMallocString::from_str(&format!("abc {} 😀", i)).into_raw());
|
||||
v.push(SqliteMallocString::from_str(&format!("abc {i} 😀")).into_raw());
|
||||
}
|
||||
unsafe {
|
||||
for (i, s) in v.chunks_mut(2).enumerate() {
|
||||
@@ -226,7 +224,7 @@ mod test {
|
||||
);
|
||||
assert_eq!(
|
||||
std::ffi::CStr::from_ptr(s1).to_str().unwrap(),
|
||||
&format!("abc {} 😀", i)
|
||||
&format!("abc {i} 😀")
|
||||
);
|
||||
let _ = SqliteMallocString::from_raw(s0).unwrap();
|
||||
let _ = SqliteMallocString::from_raw(s1).unwrap();
|
||||
|
||||
@@ -208,7 +208,7 @@ mod test {
|
||||
{
|
||||
let mut stmt = db.prepare("SELECT value from rarray(?);")?;
|
||||
|
||||
let rows = stmt.query_map(&[&ptr], |row| row.get::<_, i64>(0))?;
|
||||
let rows = stmt.query_map([&ptr], |row| row.get::<_, i64>(0))?;
|
||||
assert_eq!(2, Rc::strong_count(&ptr));
|
||||
let mut count = 0;
|
||||
for (i, value) in rows.enumerate() {
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
//! // Note: This should be done once (usually when opening the DB).
|
||||
//! let db = Connection::open_in_memory()?;
|
||||
//! rusqlite::vtab::csvtab::load_module(&db)?;
|
||||
//! // Assum3e my_csv.csv
|
||||
//! // Assume my_csv.csv
|
||||
//! let schema = "
|
||||
//! CREATE VIRTUAL TABLE my_csv_data
|
||||
//! USING csv(filename = 'my_csv.csv')
|
||||
@@ -208,13 +208,13 @@ unsafe impl<'vtab> VTab<'vtab> for CsvTab {
|
||||
let mut record = csv::ByteRecord::new();
|
||||
if reader.read_byte_record(&mut record)? {
|
||||
for (i, _) in record.iter().enumerate() {
|
||||
cols.push(format!("c{}", i));
|
||||
cols.push(format!("c{i}"));
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if let Some(n_col) = n_col {
|
||||
for i in 0..n_col {
|
||||
cols.push(format!("c{}", i));
|
||||
cols.push(format!("c{i}"));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -187,8 +187,7 @@ pub fn eponymous_only_module<'vtab, T: VTab<'vtab>>() -> &'static Module<'vtab,
|
||||
/// Virtual table configuration options
|
||||
#[repr(i32)]
|
||||
#[non_exhaustive]
|
||||
#[cfg(feature = "modern_sqlite")] // 3.7.7
|
||||
#[derive(Debug, Clone, Copy, PartialEq)]
|
||||
#[derive(Debug, Clone, Copy, Eq, PartialEq)]
|
||||
pub enum VTabConfig {
|
||||
/// Equivalent to SQLITE_VTAB_CONSTRAINT_SUPPORT
|
||||
ConstraintSupport = 1,
|
||||
@@ -203,8 +202,6 @@ pub struct VTabConnection(*mut ffi::sqlite3);
|
||||
|
||||
impl VTabConnection {
|
||||
/// Configure various facets of the virtual table interface
|
||||
#[cfg(feature = "modern_sqlite")] // 3.7.7
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "modern_sqlite")))]
|
||||
pub fn config(&mut self, config: VTabConfig) -> Result<()> {
|
||||
crate::error::check(unsafe { ffi::sqlite3_vtab_config(self.0, config as c_int) })
|
||||
}
|
||||
@@ -322,7 +319,7 @@ pub trait UpdateVTab<'vtab>: CreateVTab<'vtab> {
|
||||
|
||||
/// Index constraint operator.
|
||||
/// See [Virtual Table Constraint Operator Codes](https://sqlite.org/c3ref/c_index_constraint_eq.html) for details.
|
||||
#[derive(Debug, PartialEq)]
|
||||
#[derive(Debug, Eq, PartialEq)]
|
||||
#[allow(non_snake_case, non_camel_case_types, missing_docs)]
|
||||
#[allow(clippy::upper_case_acronyms)]
|
||||
pub enum IndexConstraintOp {
|
||||
@@ -369,7 +366,6 @@ impl From<u8> for IndexConstraintOp {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "modern_sqlite")] // 3.9.0
|
||||
bitflags::bitflags! {
|
||||
/// Virtual table scan flags
|
||||
/// See [Function Flags](https://sqlite.org/c3ref/c_index_scan_unique.html) for details.
|
||||
@@ -461,7 +457,7 @@ impl IndexInfo {
|
||||
#[inline]
|
||||
pub fn set_order_by_consumed(&mut self, order_by_consumed: bool) {
|
||||
unsafe {
|
||||
(*self.0).orderByConsumed = if order_by_consumed { 1 } else { 0 };
|
||||
(*self.0).orderByConsumed = order_by_consumed as c_int;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -474,8 +470,6 @@ impl IndexInfo {
|
||||
}
|
||||
|
||||
/// Estimated number of rows returned.
|
||||
#[cfg(feature = "modern_sqlite")] // SQLite >= 3.8.2
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "modern_sqlite")))]
|
||||
#[inline]
|
||||
pub fn set_estimated_rows(&mut self, estimated_rows: i64) {
|
||||
unsafe {
|
||||
@@ -484,16 +478,12 @@ impl IndexInfo {
|
||||
}
|
||||
|
||||
/// Mask of SQLITE_INDEX_SCAN_* flags.
|
||||
#[cfg(feature = "modern_sqlite")] // SQLite >= 3.9.0
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "modern_sqlite")))]
|
||||
#[inline]
|
||||
pub fn set_idx_flags(&mut self, flags: IndexFlags) {
|
||||
unsafe { (*self.0).idxFlags = flags.bits() };
|
||||
}
|
||||
|
||||
/// Mask of columns used by statement
|
||||
#[cfg(feature = "modern_sqlite")] // SQLite >= 3.10.0
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "modern_sqlite")))]
|
||||
#[inline]
|
||||
pub fn col_used(&self) -> u64 {
|
||||
unsafe { (*self.0).colUsed }
|
||||
@@ -509,7 +499,7 @@ impl IndexInfo {
|
||||
if collation.is_null() {
|
||||
return Err(Error::SqliteFailure(
|
||||
ffi::Error::new(ffi::SQLITE_MISUSE),
|
||||
Some(format!("{} is out of range", constraint_idx)),
|
||||
Some(format!("{constraint_idx} is out of range")),
|
||||
));
|
||||
}
|
||||
Ok(unsafe { CStr::from_ptr(collation) }.to_str()?)
|
||||
@@ -623,7 +613,7 @@ impl IndexConstraintUsage<'_> {
|
||||
/// if `omit`, do not code a test for this constraint
|
||||
#[inline]
|
||||
pub fn set_omit(&mut self, omit: bool) {
|
||||
self.0.omit = if omit { 1 } else { 0 };
|
||||
self.0.omit = omit as std::os::raw::c_uchar;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -934,7 +924,7 @@ pub fn parameter(c_slice: &[u8]) -> Result<(&str, &str)> {
|
||||
return Ok((param, value));
|
||||
}
|
||||
}
|
||||
Err(Error::ModuleError(format!("illegal argument: '{}'", arg)))
|
||||
Err(Error::ModuleError(format!("illegal argument: '{arg}'")))
|
||||
}
|
||||
|
||||
// FIXME copy/paste from function.rs
|
||||
@@ -963,7 +953,7 @@ where
|
||||
.map(|&cs| CStr::from_ptr(cs).to_bytes()) // FIXME .to_str() -> Result<&str, Utf8Error>
|
||||
.collect::<Vec<_>>();
|
||||
match T::create(&mut conn, aux.as_ref(), &vec[..]) {
|
||||
Ok((sql, vtab)) => match ::std::ffi::CString::new(sql) {
|
||||
Ok((sql, vtab)) => match std::ffi::CString::new(sql) {
|
||||
Ok(c_sql) => {
|
||||
let rc = ffi::sqlite3_declare_vtab(db, c_sql.as_ptr());
|
||||
if rc == ffi::SQLITE_OK {
|
||||
@@ -1015,7 +1005,7 @@ where
|
||||
.map(|&cs| CStr::from_ptr(cs).to_bytes()) // FIXME .to_str() -> Result<&str, Utf8Error>
|
||||
.collect::<Vec<_>>();
|
||||
match T::connect(&mut conn, aux.as_ref(), &vec[..]) {
|
||||
Ok((sql, vtab)) => match ::std::ffi::CString::new(sql) {
|
||||
Ok((sql, vtab)) => match std::ffi::CString::new(sql) {
|
||||
Ok(c_sql) => {
|
||||
let rc = ffi::sqlite3_declare_vtab(db, c_sql.as_ptr());
|
||||
if rc == ffi::SQLITE_OK {
|
||||
@@ -1334,7 +1324,7 @@ pub mod csvtab;
|
||||
#[cfg(feature = "series")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "series")))]
|
||||
pub mod series; // SQLite >= 3.9.0
|
||||
#[cfg(test)]
|
||||
#[cfg(all(test, feature = "modern_sqlite"))]
|
||||
mod vtablog;
|
||||
|
||||
#[cfg(test)]
|
||||
|
||||
@@ -115,6 +115,7 @@ unsafe impl<'vtab> VTab<'vtab> for SeriesTab {
|
||||
}
|
||||
if idx_num.contains(QueryPlanFlags::BOTH) {
|
||||
// Both start= and stop= boundaries are available.
|
||||
//#[allow(clippy::bool_to_int_with_if)]
|
||||
info.set_estimated_cost(f64::from(
|
||||
2 - if idx_num.contains(QueryPlanFlags::STEP) {
|
||||
1
|
||||
|
||||
@@ -153,7 +153,7 @@ impl<'vtab> CreateVTab<'vtab> for VTabLog {
|
||||
|
||||
impl<'vtab> UpdateVTab<'vtab> for VTabLog {
|
||||
fn delete(&mut self, arg: ValueRef<'_>) -> Result<()> {
|
||||
println!("VTabLog::delete({}, {:?})", self.i_inst, arg);
|
||||
println!("VTabLog::delete({}, {arg:?})", self.i_inst);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -163,7 +163,7 @@ impl<'vtab> UpdateVTab<'vtab> for VTabLog {
|
||||
self.i_inst,
|
||||
args.iter().collect::<Vec<ValueRef<'_>>>()
|
||||
);
|
||||
Ok(self.n_row as i64)
|
||||
Ok(self.n_row)
|
||||
}
|
||||
|
||||
fn update(&mut self, args: &Values<'_>) -> Result<()> {
|
||||
@@ -246,7 +246,7 @@ unsafe impl VTabCursor for VTabLogCursor<'_> {
|
||||
self.row_id
|
||||
)
|
||||
} else {
|
||||
format!("{}{}", i, self.row_id)
|
||||
format!("{i}{}", self.row_id)
|
||||
};
|
||||
println!(
|
||||
"VTabLogCursor::column(tab={}, cursor={}, i={}): {}",
|
||||
|
||||
Reference in New Issue
Block a user