diff options
Diffstat (limited to 'src/vtab/mod.rs')
-rw-r--r-- | src/vtab/mod.rs | 163 |
1 files changed, 112 insertions, 51 deletions
diff --git a/src/vtab/mod.rs b/src/vtab/mod.rs index f364f1f..bdb6509 100644 --- a/src/vtab/mod.rs +++ b/src/vtab/mod.rs @@ -1,4 +1,4 @@ -//! `feature = "vtab"` Create virtual tables. +//! Create virtual tables. //! //! Follow these steps to create your own virtual table: //! 1. Write implementation of [`VTab`] and [`VTabCursor`] traits. @@ -57,7 +57,7 @@ use crate::{str_to_cstring, Connection, Error, InnerConnection, Result}; // ffi::sqlite3_vtab => VTab // ffi::sqlite3_vtab_cursor => VTabCursor -/// `feature = "vtab"` Virtual table module +/// Virtual table module /// /// (See [SQLite doc](https://sqlite.org/c3ref/module.html)) #[repr(transparent)] @@ -79,17 +79,19 @@ union ModuleZeroHack { // structs are allowed to be zeroed. const ZERO_MODULE: ffi::sqlite3_module = unsafe { ModuleZeroHack { - bytes: [0u8; std::mem::size_of::<ffi::sqlite3_module>()], + bytes: [0_u8; std::mem::size_of::<ffi::sqlite3_module>()], } .module }; -/// `feature = "vtab"` Create a read-only virtual table implementation. +/// Create a read-only virtual table implementation. /// /// Step 2 of [Creating New Virtual Table Implementations](https://sqlite.org/vtab.html#creating_new_virtual_table_implementations). +#[must_use] pub fn read_only_module<'vtab, T: CreateVTab<'vtab>>() -> &'static Module<'vtab, T> { // The xConnect and xCreate methods do the same thing, but they must be // different so that the virtual table is not an eponymous virtual table. + #[allow(clippy::needless_update)] &Module { base: ffi::sqlite3_module { // We don't use V3 @@ -122,13 +124,15 @@ pub fn read_only_module<'vtab, T: CreateVTab<'vtab>>() -> &'static Module<'vtab, } } -/// `feature = "vtab"` Create an eponymous only virtual table implementation. +/// Create an eponymous only virtual table implementation. /// /// Step 2 of [Creating New Virtual Table Implementations](https://sqlite.org/vtab.html#creating_new_virtual_table_implementations). +#[must_use] pub fn eponymous_only_module<'vtab, T: VTab<'vtab>>() -> &'static Module<'vtab, T> { // A virtual table is eponymous if its xCreate method is the exact same function // as the xConnect method For eponymous-only virtual tables, the xCreate // method is NULL + #[allow(clippy::needless_update)] &Module { base: ffi::sqlite3_module { // We don't use V3 @@ -187,18 +191,18 @@ impl VTabConnection { } } -/// `feature = "vtab"` Virtual table instance trait. +/// Virtual table instance trait. /// /// # Safety /// -/// The first item in a struct implementing VTab must be +/// The first item in a struct implementing `VTab` must be /// `rusqlite::sqlite3_vtab`, and the struct must be `#[repr(C)]`. /// /// ```rust,ignore /// #[repr(C)] /// struct MyTab { /// /// Base class. Must be first -/// base: ffi::sqlite3_vtab, +/// base: rusqlite::vtab::sqlite3_vtab, /// /* Virtual table implementations will typically add additional fields */ /// } /// ``` @@ -228,7 +232,7 @@ pub unsafe trait VTab<'vtab>: Sized { fn open(&'vtab self) -> Result<Self::Cursor>; } -/// `feature = "vtab"` Non-eponymous virtual table instance trait. +/// Non-eponymous virtual table instance trait. /// /// (See [SQLite doc](https://sqlite.org/c3ref/vtab.html)) pub trait CreateVTab<'vtab>: VTab<'vtab> { @@ -257,7 +261,7 @@ pub trait CreateVTab<'vtab>: VTab<'vtab> { } } -/// `feature = "vtab"` Index constraint operator. +/// Index constraint operator. /// See [Virtual Table Constraint Operator Codes](https://sqlite.org/c3ref/c_index_constraint_eq.html) for details. #[derive(Debug, PartialEq)] #[allow(non_snake_case, non_camel_case_types, missing_docs)] @@ -277,6 +281,8 @@ pub enum IndexConstraintOp { SQLITE_INDEX_CONSTRAINT_ISNOTNULL, // 3.21.0 SQLITE_INDEX_CONSTRAINT_ISNULL, // 3.21.0 SQLITE_INDEX_CONSTRAINT_IS, // 3.21.0 + SQLITE_INDEX_CONSTRAINT_LIMIT, // 3.38.0 + SQLITE_INDEX_CONSTRAINT_OFFSET, // 3.38.0 SQLITE_INDEX_CONSTRAINT_FUNCTION(u8), // 3.25.0 } @@ -297,20 +303,36 @@ impl From<u8> for IndexConstraintOp { 70 => IndexConstraintOp::SQLITE_INDEX_CONSTRAINT_ISNOTNULL, 71 => IndexConstraintOp::SQLITE_INDEX_CONSTRAINT_ISNULL, 72 => IndexConstraintOp::SQLITE_INDEX_CONSTRAINT_IS, + 73 => IndexConstraintOp::SQLITE_INDEX_CONSTRAINT_LIMIT, + 74 => IndexConstraintOp::SQLITE_INDEX_CONSTRAINT_OFFSET, v => IndexConstraintOp::SQLITE_INDEX_CONSTRAINT_FUNCTION(v), } } } -/// `feature = "vtab"` Pass information into and receive the reply from the +/// Pass information into and receive the reply from the /// [`VTab::best_index`] method. /// /// (See [SQLite doc](http://sqlite.org/c3ref/index_info.html)) pub struct IndexInfo(*mut ffi::sqlite3_index_info); impl IndexInfo { + /// Iterate on index constraint and its associated usage. + #[inline] + pub fn constraints_and_usages(&mut self) -> IndexConstraintAndUsageIter<'_> { + let constraints = + unsafe { slice::from_raw_parts((*self.0).aConstraint, (*self.0).nConstraint as usize) }; + let constraint_usages = unsafe { + slice::from_raw_parts_mut((*self.0).aConstraintUsage, (*self.0).nConstraint as usize) + }; + IndexConstraintAndUsageIter { + iter: constraints.iter().zip(constraint_usages.iter_mut()), + } + } + /// Record WHERE clause constraints. #[inline] + #[must_use] pub fn constraints(&self) -> IndexConstraintIter<'_> { let constraints = unsafe { slice::from_raw_parts((*self.0).aConstraint, (*self.0).nConstraint as usize) }; @@ -321,6 +343,7 @@ impl IndexInfo { /// Information about the ORDER BY clause. #[inline] + #[must_use] pub fn order_bys(&self) -> OrderByIter<'_> { let order_bys = unsafe { slice::from_raw_parts((*self.0).aOrderBy, (*self.0).nOrderBy as usize) }; @@ -331,6 +354,7 @@ impl IndexInfo { /// Number of terms in the ORDER BY clause #[inline] + #[must_use] pub fn num_of_order_by(&self) -> usize { unsafe { (*self.0).nOrderBy as usize } } @@ -370,6 +394,7 @@ 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 { @@ -383,6 +408,30 @@ impl IndexInfo { // TODO sqlite3_vtab_collation (http://sqlite.org/c3ref/vtab_collation.html) } +/// Iterate on index constraint and its associated usage. +pub struct IndexConstraintAndUsageIter<'a> { + iter: std::iter::Zip< + slice::Iter<'a, ffi::sqlite3_index_constraint>, + slice::IterMut<'a, ffi::sqlite3_index_constraint_usage>, + >, +} + +impl<'a> Iterator for IndexConstraintAndUsageIter<'a> { + type Item = (IndexConstraint<'a>, IndexConstraintUsage<'a>); + + #[inline] + fn next(&mut self) -> Option<(IndexConstraint<'a>, IndexConstraintUsage<'a>)> { + self.iter + .next() + .map(|raw| (IndexConstraint(raw.0), IndexConstraintUsage(raw.1))) + } + + #[inline] + fn size_hint(&self) -> (usize, Option<usize>) { + self.iter.size_hint() + } +} + /// `feature = "vtab"` pub struct IndexConstraintIter<'a> { iter: slice::Iter<'a, ffi::sqlite3_index_constraint>, @@ -393,7 +442,7 @@ impl<'a> Iterator for IndexConstraintIter<'a> { #[inline] fn next(&mut self) -> Option<IndexConstraint<'a>> { - self.iter.next().map(|raw| IndexConstraint(raw)) + self.iter.next().map(IndexConstraint) } #[inline] @@ -402,30 +451,33 @@ impl<'a> Iterator for IndexConstraintIter<'a> { } } -/// `feature = "vtab"` WHERE clause constraint. +/// WHERE clause constraint. pub struct IndexConstraint<'a>(&'a ffi::sqlite3_index_constraint); impl IndexConstraint<'_> { /// Column constrained. -1 for ROWID #[inline] + #[must_use] pub fn column(&self) -> c_int { self.0.iColumn } /// Constraint operator #[inline] + #[must_use] pub fn operator(&self) -> IndexConstraintOp { IndexConstraintOp::from(self.0.op) } /// True if this constraint is usable #[inline] + #[must_use] pub fn is_usable(&self) -> bool { self.0.usable != 0 } } -/// `feature = "vtab"` Information about what parameters to pass to +/// Information about what parameters to pass to /// [`VTabCursor::filter`]. pub struct IndexConstraintUsage<'a>(&'a mut ffi::sqlite3_index_constraint_usage); @@ -454,7 +506,7 @@ impl<'a> Iterator for OrderByIter<'a> { #[inline] fn next(&mut self) -> Option<OrderBy<'a>> { - self.iter.next().map(|raw| OrderBy(raw)) + self.iter.next().map(OrderBy) } #[inline] @@ -463,31 +515,35 @@ impl<'a> Iterator for OrderByIter<'a> { } } -/// `feature = "vtab"` A column of the ORDER BY clause. +/// A column of the ORDER BY clause. pub struct OrderBy<'a>(&'a ffi::sqlite3_index_info_sqlite3_index_orderby); impl OrderBy<'_> { /// Column number #[inline] + #[must_use] pub fn column(&self) -> c_int { self.0.iColumn } /// True for DESC. False for ASC. #[inline] + #[must_use] pub fn is_order_by_desc(&self) -> bool { self.0.desc != 0 } } -/// `feature = "vtab"` Virtual table cursor trait. +/// Virtual table cursor trait. +/// +/// # Safety /// /// Implementations must be like: /// ```rust,ignore /// #[repr(C)] /// struct MyTabCursor { /// /// Base class. Must be first -/// base: ffi::sqlite3_vtab_cursor, +/// base: rusqlite::vtab::sqlite3_vtab_cursor, /// /* Virtual table implementations will typically add additional fields */ /// } /// ``` @@ -514,7 +570,7 @@ pub unsafe trait VTabCursor: Sized { fn rowid(&self) -> Result<i64>; } -/// `feature = "vtab"` Context is used by [`VTabCursor::column`] to specify the +/// Context is used by [`VTabCursor::column`] to specify the /// cell value. pub struct Context(*mut ffi::sqlite3_context); @@ -530,7 +586,7 @@ impl Context { // TODO sqlite3_vtab_nochange (http://sqlite.org/c3ref/vtab_nochange.html) } -/// `feature = "vtab"` Wrapper to [`VTabCursor::filter`] arguments, the values +/// Wrapper to [`VTabCursor::filter`] arguments, the values /// requested by [`VTab::best_index`]. pub struct Values<'a> { args: &'a [*mut ffi::sqlite3_value], @@ -539,12 +595,14 @@ pub struct Values<'a> { impl Values<'_> { /// Returns the number of values. #[inline] + #[must_use] pub fn len(&self) -> usize { self.args.len() } /// Returns `true` if there is no value. #[inline] + #[must_use] pub fn is_empty(&self) -> bool { self.args.is_empty() } @@ -558,21 +616,17 @@ impl Values<'_> { FromSqlError::Other(err) => { Error::FromSqlConversionFailure(idx, value.data_type(), err) } - FromSqlError::OutOfRange(i) => Error::IntegralValueOutOfRange(idx, i), - #[cfg(feature = "i128_blob")] - FromSqlError::InvalidI128Size(_) => { - Error::InvalidColumnType(idx, idx.to_string(), value.data_type()) - } - #[cfg(feature = "uuid")] - FromSqlError::InvalidUuidSize(_) => { + FromSqlError::InvalidBlobSize { .. } => { Error::FromSqlConversionFailure(idx, value.data_type(), Box::new(err)) } + FromSqlError::OutOfRange(i) => Error::IntegralValueOutOfRange(idx, i), }) } // `sqlite3_value_type` returns `SQLITE_NULL` for pointer. // So it seems not possible to enhance `ValueRef::from_value`. #[cfg(feature = "array")] + #[cfg_attr(docsrs, doc(cfg(feature = "array")))] fn get_array(&self, idx: usize) -> Option<array::Array> { use crate::types::Value; let arg = self.args[idx]; @@ -591,6 +645,7 @@ impl Values<'_> { /// Turns `Values` into an iterator. #[inline] + #[must_use] pub fn iter(&self) -> ValueIter<'_> { ValueIter { iter: self.args.iter(), @@ -630,7 +685,7 @@ impl<'a> Iterator for ValueIter<'a> { } impl Connection { - /// `feature = "vtab"` Register a virtual table implementation. + /// Register a virtual table implementation. /// /// Step 3 of [Creating New Virtual Table /// Implementations](https://sqlite.org/vtab.html#creating_new_virtual_table_implementations). @@ -661,7 +716,7 @@ impl InnerConnection { self.db(), c_name.as_ptr(), &module.base, - boxed_aux as *mut c_void, + boxed_aux.cast::<c_void>(), Some(free_boxed_value::<T::Aux>), ) } @@ -680,17 +735,19 @@ impl InnerConnection { } } -/// `feature = "vtab"` Escape double-quote (`"`) character occurrences by +/// Escape double-quote (`"`) character occurrences by /// doubling them (`""`). +#[must_use] pub fn escape_double_quote(identifier: &str) -> Cow<'_, str> { if identifier.contains('"') { // escape quote by doubling them - Owned(identifier.replace("\"", "\"\"")) + Owned(identifier.replace('"', "\"\"")) } else { Borrowed(identifier) } } -/// `feature = "vtab"` Dequote string +/// Dequote string +#[must_use] pub fn dequote(s: &str) -> &str { if s.len() < 2 { return s; @@ -703,11 +760,12 @@ pub fn dequote(s: &str) -> &str { _ => s, } } -/// `feature = "vtab"` The boolean can be one of: +/// The boolean can be one of: /// ```text /// 1 yes true on /// 0 no false off /// ``` +#[must_use] pub fn parse_boolean(s: &str) -> Option<bool> { if s.eq_ignore_ascii_case("yes") || s.eq_ignore_ascii_case("on") @@ -728,7 +786,7 @@ pub fn parse_boolean(s: &str) -> Option<bool> { // FIXME copy/paste from function.rs unsafe extern "C" fn free_boxed_value<T>(p: *mut c_void) { - let _: Box<T> = Box::from_raw(p as *mut T); + drop(Box::from_raw(p.cast::<T>())); } unsafe extern "C" fn rust_create<'vtab, T>( @@ -745,7 +803,7 @@ where use std::ffi::CStr; let mut conn = VTabConnection(db); - let aux = aux as *mut T::Aux; + let aux = aux.cast::<T::Aux>(); let args = slice::from_raw_parts(argv, argc as usize); let vec = args .iter() @@ -757,7 +815,7 @@ where let rc = ffi::sqlite3_declare_vtab(db, c_sql.as_ptr()); if rc == ffi::SQLITE_OK { let boxed_vtab: *mut T = Box::into_raw(Box::new(vtab)); - *pp_vtab = boxed_vtab as *mut ffi::sqlite3_vtab; + *pp_vtab = boxed_vtab.cast::<ffi::sqlite3_vtab>(); ffi::SQLITE_OK } else { let err = error_from_sqlite_code(rc, None); @@ -797,7 +855,7 @@ where use std::ffi::CStr; let mut conn = VTabConnection(db); - let aux = aux as *mut T::Aux; + let aux = aux.cast::<T::Aux>(); let args = slice::from_raw_parts(argv, argc as usize); let vec = args .iter() @@ -809,7 +867,7 @@ where let rc = ffi::sqlite3_declare_vtab(db, c_sql.as_ptr()); if rc == ffi::SQLITE_OK { let boxed_vtab: *mut T = Box::into_raw(Box::new(vtab)); - *pp_vtab = boxed_vtab as *mut ffi::sqlite3_vtab; + *pp_vtab = boxed_vtab.cast::<ffi::sqlite3_vtab>(); ffi::SQLITE_OK } else { let err = error_from_sqlite_code(rc, None); @@ -842,7 +900,7 @@ unsafe extern "C" fn rust_best_index<'vtab, T>( where T: VTab<'vtab>, { - let vt = vtab as *mut T; + let vt = vtab.cast::<T>(); let mut idx_info = IndexInfo(info); match (*vt).best_index(&mut idx_info) { Ok(_) => ffi::SQLITE_OK, @@ -866,8 +924,8 @@ where if vtab.is_null() { return ffi::SQLITE_OK; } - let vtab = vtab as *mut T; - let _: Box<T> = Box::from_raw(vtab); + let vtab = vtab.cast::<T>(); + drop(Box::from_raw(vtab)); ffi::SQLITE_OK } @@ -878,10 +936,10 @@ where if vtab.is_null() { return ffi::SQLITE_OK; } - let vt = vtab as *mut T; + let vt = vtab.cast::<T>(); match (*vt).destroy() { Ok(_) => { - let _: Box<T> = Box::from_raw(vt); + drop(Box::from_raw(vt)); ffi::SQLITE_OK } Err(Error::SqliteFailure(err, s)) => { @@ -904,11 +962,11 @@ unsafe extern "C" fn rust_open<'vtab, T: 'vtab>( where T: VTab<'vtab>, { - let vt = vtab as *mut T; + let vt = vtab.cast::<T>(); match (*vt).open() { Ok(cursor) => { let boxed_cursor: *mut T::Cursor = Box::into_raw(Box::new(cursor)); - *pp_cursor = boxed_cursor as *mut ffi::sqlite3_vtab_cursor; + *pp_cursor = boxed_cursor.cast::<ffi::sqlite3_vtab_cursor>(); ffi::SQLITE_OK } Err(Error::SqliteFailure(err, s)) => { @@ -928,8 +986,8 @@ unsafe extern "C" fn rust_close<C>(cursor: *mut ffi::sqlite3_vtab_cursor) -> c_i where C: VTabCursor, { - let cr = cursor as *mut C; - let _: Box<C> = Box::from_raw(cr); + let cr = cursor.cast::<C>(); + drop(Box::from_raw(cr)); ffi::SQLITE_OK } @@ -969,7 +1027,7 @@ unsafe extern "C" fn rust_eof<C>(cursor: *mut ffi::sqlite3_vtab_cursor) -> c_int where C: VTabCursor, { - let cr = cursor as *mut C; + let cr = cursor.cast::<C>(); (*cr).eof() as c_int } @@ -981,7 +1039,7 @@ unsafe extern "C" fn rust_column<C>( where C: VTabCursor, { - let cr = cursor as *mut C; + let cr = cursor.cast::<C>(); let mut ctxt = Context(ctx); result_error(ctx, (*cr).column(&mut ctxt, i)) } @@ -993,7 +1051,7 @@ unsafe extern "C" fn rust_rowid<C>( where C: VTabCursor, { - let cr = cursor as *mut C; + let cr = cursor.cast::<C>(); match (*cr).rowid() { Ok(rowid) => { *p_rowid = rowid; @@ -1027,7 +1085,7 @@ unsafe fn cursor_error<T>(cursor: *mut ffi::sqlite3_vtab_cursor, result: Result< #[cold] unsafe fn set_err_msg(vtab: *mut ffi::sqlite3_vtab, err_msg: &str) { if !(*vtab).zErrMsg.is_null() { - ffi::sqlite3_free((*vtab).zErrMsg as *mut c_void); + ffi::sqlite3_free((*vtab).zErrMsg.cast::<c_void>()); } (*vtab).zErrMsg = alloc(err_msg); } @@ -1072,10 +1130,13 @@ fn alloc(s: &str) -> *mut c_char { } #[cfg(feature = "array")] +#[cfg_attr(docsrs, doc(cfg(feature = "array")))] pub mod array; #[cfg(feature = "csvtab")] +#[cfg_attr(docsrs, doc(cfg(feature = "csvtab")))] pub mod csvtab; #[cfg(feature = "series")] +#[cfg_attr(docsrs, doc(cfg(feature = "series")))] pub mod series; // SQLite >= 3.9.0 #[cfg(test)] |