//! Create virtual tables. //! //! Follow these steps to create your own virtual table: //! 1. Write implementation of [`VTab`] and [`VTabCursor`] traits. //! 2. Create an instance of the [`Module`] structure specialized for [`VTab`] //! impl. from step 1. //! 3. Register your [`Module`] structure using [`Connection::create_module`]. //! 4. Run a `CREATE VIRTUAL TABLE` command that specifies the new module in the //! `USING` clause. //! //! (See [SQLite doc](http://sqlite.org/vtab.html)) use std::borrow::Cow::{self, Borrowed, Owned}; use std::marker::PhantomData; use std::marker::Sync; use std::os::raw::{c_char, c_int, c_void}; use std::ptr; use std::slice;
unsafeimpl<'vtab, T: VTab<'vtab>> Send for Module<'vtab, T> {} unsafeimpl<'vtab, T: VTab<'vtab>> Sync for Module<'vtab, T> {}
union ModuleZeroHack {
bytes: [u8; std::mem::size_of::<ffi::sqlite3_module>()],
module: ffi::sqlite3_module,
}
// Used as a trailing initializer for sqlite3_module -- this way we avoid having // the build fail if buildtime_bindgen is on. This is safe, as bindgen-generated // structs are allowed to be zeroed. const ZERO_MODULE: ffi::sqlite3_module = unsafe {
ModuleZeroHack {
bytes: [0_u8; std::mem::size_of::<ffi::sqlite3_module>()],
}
.module
};
/// 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] pubfn read_only_module<'vtab, T: CreateVTab<'vtab>>() -> &'static Module<'vtab, T> { match T::KIND {
VTabKind::EponymousOnly => eponymous_only_module(),
VTabKind::Eponymous => { // A virtual table is eponymous if its xCreate method is the exact same function // as the xConnect method
module!('vtab, T, T::Cursor, Some(rust_connect::<T>), Some(rust_disconnect::<T>), None)
}
_ => { // The xConnect and xCreate methods may do the same thing, but they must be // different so that the virtual table is not an eponymous virtual table.
module!('vtab, T, T::Cursor, Some(rust_create::<T>), Some(rust_destroy::<T>), None)
}
}
}
/// 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] pubfn eponymous_only_module<'vtab, T: VTab<'vtab>>() -> &'static Module<'vtab, T> { // For eponymous-only virtual tables, the xCreate method is NULL
module!('vtab, T, T::Cursor, None, None, None)
}
/// Get access to the underlying SQLite database connection handle. /// /// # Warning /// /// You should not need to use this function. If you do need to, please /// [open an issue on the rusqlite repository](https://github.com/rusqlite/rusqlite/issues) and describe /// your use case. /// /// # Safety /// /// This function is unsafe because it gives you raw access /// to the SQLite connection, and what you do with it could impact the /// safety of this `Connection`. pubunsafefn handle(&mutself) -> *mut ffi::sqlite3 { self.0
}
}
/// Eponymous-only virtual table instance trait. /// /// # Safety /// /// 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: rusqlite::vtab::sqlite3_vtab, /// /* Virtual table implementations will typically add additional fields */ /// } /// ``` /// /// (See [SQLite doc](https://sqlite.org/c3ref/vtab.html)) pubunsafetrait VTab<'vtab>: Sized { /// Client data passed to [`Connection::create_module`]. type Aux; /// Specific cursor implementation type Cursor: VTabCursor;
/// Establish a new connection to an existing virtual table. /// /// (See [SQLite doc](https://sqlite.org/vtab.html#the_xconnect_method)) fn connect(
db: &mut VTabConnection,
aux: Option<&Self::Aux>,
args: &[&[u8]],
) -> Result<(String, Self)>;
/// Read-only virtual table instance trait. /// /// (See [SQLite doc](https://sqlite.org/c3ref/vtab.html)) pubtrait CreateVTab<'vtab>: VTab<'vtab> { /// For [`EponymousOnly`](VTabKind::EponymousOnly), /// [`create`](CreateVTab::create) and [`destroy`](CreateVTab::destroy) are /// not called const KIND: VTabKind; /// Create a new instance of a virtual table in response to a CREATE VIRTUAL /// TABLE statement. The `db` parameter is a pointer to the SQLite /// database connection that is executing the CREATE VIRTUAL TABLE /// statement. /// /// Call [`connect`](VTab::connect) by default. /// (See [SQLite doc](https://sqlite.org/vtab.html#the_xcreate_method)) fn create(
db: &mut VTabConnection,
aux: Option<&Self::Aux>,
args: &[&[u8]],
) -> Result<(String, Self)> { Self::connect(db, aux, args)
}
/// Destroy the underlying table implementation. This method undoes the work /// of [`create`](CreateVTab::create). /// /// Do nothing by default. /// (See [SQLite doc](https://sqlite.org/vtab.html#the_xdestroy_method)) fn destroy(&self) -> Result<()> {
Ok(())
}
}
/// Writable virtual table instance trait. /// /// (See [SQLite doc](https://sqlite.org/vtab.html#xupdate)) pubtrait UpdateVTab<'vtab>: CreateVTab<'vtab> { /// Delete rowid or PK fn delete(&mutself, arg: ValueRef<'_>) -> Result<()>; /// Insert: `args[0] == NULL: old rowid or PK, args[1]: new rowid or PK, /// args[2]: ...` /// /// Return the new rowid. // TODO Make the distinction between argv[1] == NULL and argv[1] != NULL ? fn insert(&mutself, args: &Values<'_>) -> Result<i64>; /// Update: `args[0] != NULL: old rowid or PK, args[1]: new row id or PK, /// args[2]: ...` fn update(&mutself, args: &Values<'_>) -> Result<()>;
}
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)] pubstruct IndexFlags: ::std::os::raw::c_int { /// Default const NONE = 0; /// Scan visits at most 1 row. const SQLITE_INDEX_SCAN_UNIQUE = ffi::SQLITE_INDEX_SCAN_UNIQUE;
}
}
/// Pass information into and receive the reply from the /// [`VTab::best_index`] method. /// /// (See [SQLite doc](http://sqlite.org/c3ref/index_info.html)) #[derive(Debug)] pubstruct IndexInfo(*mut ffi::sqlite3_index_info);
impl IndexInfo { /// Iterate on index constraint and its associated usage. #[inline] pubfn constraints_and_usages(&mutself) -> 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] pubfn constraints(&self) -> IndexConstraintIter<'_> { let constraints = unsafe { slice::from_raw_parts((*self.0).aConstraint, (*self.0).nConstraint as usize) };
IndexConstraintIter {
iter: constraints.iter(),
}
}
/// Information about the ORDER BY clause. #[inline] #[must_use] pubfn order_bys(&self) -> OrderByIter<'_> { let order_bys = unsafe { slice::from_raw_parts((*self.0).aOrderBy, (*self.0).nOrderBy as usize) };
OrderByIter {
iter: order_bys.iter(),
}
}
/// Number of terms in the ORDER BY clause #[inline] #[must_use] pubfn num_of_order_by(&self) -> usize { unsafe { (*self.0).nOrderBy as usize }
}
/// Information about what parameters to pass to [`VTabCursor::filter`]. #[inline] pubfn constraint_usage(&mutself, constraint_idx: usize) -> IndexConstraintUsage<'_> { let constraint_usages = unsafe {
slice::from_raw_parts_mut((*self.0).aConstraintUsage, (*self.0).nConstraint as usize)
};
IndexConstraintUsage(&mut constraint_usages[constraint_idx])
}
/// Number used to identify the index #[inline] pubfn set_idx_num(&mutself, idx_num: c_int) { unsafe {
(*self.0).idxNum = idx_num;
}
}
/// String used to identify the index pubfn set_idx_str(&mutself, idx_str: &str) { unsafe {
(*self.0).idxStr = alloc(idx_str);
(*self.0).needToFreeIdxStr = 1;
}
}
/// True if output is already ordered #[inline] pubfn set_order_by_consumed(&mutself, order_by_consumed: bool) { unsafe {
(*self.0).orderByConsumed = order_by_consumed as c_int;
}
}
/// Estimated cost of using this index #[inline] pubfn set_estimated_cost(&mutself, estimated_ost: f64) { unsafe {
(*self.0).estimatedCost = estimated_ost;
}
}
/// Estimated number of rows returned. #[inline] pubfn set_estimated_rows(&mutself, estimated_rows: i64) { unsafe {
(*self.0).estimatedRows = estimated_rows;
}
}
/// Iterate on index constraint and its associated usage. pubstruct 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>);
/// True if this constraint is usable #[inline] #[must_use] pubfn is_usable(&self) -> bool { self.0.usable != 0
}
}
/// Information about what parameters to pass to /// [`VTabCursor::filter`]. pubstruct IndexConstraintUsage<'a>(&'a mut ffi::sqlite3_index_constraint_usage);
impl IndexConstraintUsage<'_> { /// if `argv_index` > 0, constraint is part of argv to /// [`VTabCursor::filter`] #[inline] pubfn set_argv_index(&mutself, argv_index: c_int) { self.0.argvIndex = argv_index;
}
/// if `omit`, do not code a test for this constraint #[inline] pubfn set_omit(&mutself, omit: bool) { self.0.omit = omit as std::os::raw::c_uchar;
}
}
/// True for DESC. False for ASC. #[inline] #[must_use] pubfn is_order_by_desc(&self) -> bool { self.0.desc != 0
}
}
/// Virtual table cursor trait. /// /// # Safety /// /// Implementations must be like: /// ```rust,ignore /// #[repr(C)] /// struct MyTabCursor { /// /// Base class. Must be first /// base: rusqlite::vtab::sqlite3_vtab_cursor, /// /* Virtual table implementations will typically add additional fields */ /// } /// ``` /// /// (See [SQLite doc](https://sqlite.org/c3ref/vtab_cursor.html)) pubunsafetrait VTabCursor: Sized { /// Begin a search of a virtual table. /// (See [SQLite doc](https://sqlite.org/vtab.html#the_xfilter_method)) fn filter(&mutself, idx_num: c_int, idx_str: Option<&str>, args: &Values<'_>) -> Result<()>; /// Advance cursor to the next row of a result set initiated by /// [`filter`](VTabCursor::filter). (See [SQLite doc](https://sqlite.org/vtab.html#the_xnext_method)) fn next(&mutself) -> Result<()>; /// Must return `false` if the cursor currently points to a valid row of /// data, or `true` otherwise. /// (See [SQLite doc](https://sqlite.org/vtab.html#the_xeof_method)) fn eof(&self) -> bool; /// Find the value for the `i`-th column of the current row. /// `i` is zero-based so the first column is numbered 0. /// May return its result back to SQLite using one of the specified `ctx`. /// (See [SQLite doc](https://sqlite.org/vtab.html#the_xcolumn_method)) fn column(&self, ctx: &mut Context, i: c_int) -> Result<()>; /// Return the rowid of row that the cursor is currently pointing at. /// (See [SQLite doc](https://sqlite.org/vtab.html#the_xrowid_method)) fn rowid(&self) -> Result<i64>;
}
/// Context is used by [`VTabCursor::column`] to specify the /// cell value. pubstruct Context(*mut ffi::sqlite3_context);
impl Context { /// Set current cell value #[inline] pubfn set_result<T: ToSql>(&mutself, value: &T) -> Result<()> { let t = value.to_sql()?; unsafe { set_result(self.0, &[], &t) };
Ok(())
}
unsafeextern"C"fn rust_update<'vtab, T: 'vtab>(
vtab: *mut ffi::sqlite3_vtab,
argc: c_int,
argv: *mut *mut ffi::sqlite3_value,
p_rowid: *mut ffi::sqlite3_int64,
) -> c_int where
T: UpdateVTab<'vtab>,
{
assert!(argc >= 1); let args = slice::from_raw_parts_mut(argv, argc as usize); let vt = vtab.cast::<T>(); let r = if args.len() == 1 {
(*vt).delete(ValueRef::from_value(args[0]))
} elseif ffi::sqlite3_value_type(args[0]) == ffi::SQLITE_NULL { // TODO Make the distinction between argv[1] == NULL and argv[1] != NULL ? let values = Values { args }; match (*vt).insert(&values) {
Ok(rowid) => {
*p_rowid = rowid;
Ok(())
}
Err(e) => Err(e),
}
} else { let values = Values { args };
(*vt).update(&values)
}; match r {
Ok(_) => ffi::SQLITE_OK,
Err(Error::SqliteFailure(err, s)) => { iflet Some(err_msg) = s {
set_err_msg(vtab, &err_msg);
}
err.extended_code
}
Err(err) => {
set_err_msg(vtab, &err.to_string());
ffi::SQLITE_ERROR
}
}
}
/// Virtual table cursors can set an error message by assigning a string to /// `zErrMsg`. #[cold] unsafefn cursor_error<T>(cursor: *mut ffi::sqlite3_vtab_cursor, result: Result<T>) -> c_int { match result {
Ok(_) => ffi::SQLITE_OK,
Err(Error::SqliteFailure(err, s)) => { iflet Some(err_msg) = s {
set_err_msg((*cursor).pVtab, &err_msg);
}
err.extended_code
}
Err(err) => {
set_err_msg((*cursor).pVtab, &err.to_string());
ffi::SQLITE_ERROR
}
}
}
/// Virtual tables methods can set an error message by assigning a string to /// `zErrMsg`. #[cold] unsafefn set_err_msg(vtab: *mut ffi::sqlite3_vtab, err_msg: &str) { if !(*vtab).zErrMsg.is_null() {
ffi::sqlite3_free((*vtab).zErrMsg.cast::<c_void>());
}
(*vtab).zErrMsg = alloc(err_msg);
}
/// To raise an error, the `column` method should use this method to set the /// error message and return the error code. #[cold] unsafefn result_error<T>(ctx: *mut ffi::sqlite3_context, result: Result<T>) -> c_int { match result {
Ok(_) => ffi::SQLITE_OK,
Err(Error::SqliteFailure(err, s)) => { match err.extended_code {
ffi::SQLITE_TOOBIG => {
ffi::sqlite3_result_error_toobig(ctx);
}
ffi::SQLITE_NOMEM => {
ffi::sqlite3_result_error_nomem(ctx);
}
code => {
ffi::sqlite3_result_error_code(ctx, code); iflet Some(Ok(cstr)) = s.map(|s| str_to_cstring(&s)) {
ffi::sqlite3_result_error(ctx, cstr.as_ptr(), -1);
}
}
};
err.extended_code
}
Err(err) => {
ffi::sqlite3_result_error_code(ctx, ffi::SQLITE_ERROR); iflet Ok(cstr) = str_to_cstring(&err.to_string()) {
ffi::sqlite3_result_error(ctx, cstr.as_ptr(), -1);
}
ffi::SQLITE_ERROR
}
}
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.