Add support to updatable virtual tables

This commit is contained in:
gwenn 2022-03-17 21:06:44 +01:00 committed by Thom Chiovoloni
parent 624c3a2d05
commit 3787f432a4

View File

@ -84,6 +84,43 @@ const ZERO_MODULE: ffi::sqlite3_module = unsafe {
.module
};
/// Create an modifiable 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 update_module<'vtab, T: UpdateVTab<'vtab>>() -> &'static Module<'vtab, T> {
#[allow(clippy::needless_update)]
&Module {
base: ffi::sqlite3_module {
iVersion: 2,
xCreate: Some(rust_create::<T>),
xConnect: Some(rust_connect::<T>),
xBestIndex: Some(rust_best_index::<T>),
xDisconnect: Some(rust_disconnect::<T>),
xDestroy: Some(rust_destroy::<T>),
xOpen: Some(rust_open::<T>),
xClose: Some(rust_close::<T::Cursor>),
xFilter: Some(rust_filter::<T::Cursor>),
xNext: Some(rust_next::<T::Cursor>),
xEof: Some(rust_eof::<T::Cursor>),
xColumn: Some(rust_column::<T::Cursor>),
xRowid: Some(rust_rowid::<T::Cursor>),
xUpdate: Some(rust_update::<T>),
xBegin: None,
xSync: None,
xCommit: None,
xRollback: None,
xFindFunction: None,
xRename: None,
xSavepoint: None,
xRelease: None,
xRollbackTo: None,
..ZERO_MODULE
},
phantom: PhantomData::<&'vtab T>,
}
}
/// 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).
@ -280,6 +317,21 @@ pub trait CreateVTab<'vtab>: VTab<'vtab> {
}
}
/// Non-read-only virtual table instance trait.
///
/// (See [SQLite doc](https://sqlite.org/vtab.html#xupdate))
pub trait UpdateVTab<'vtab>: CreateVTab<'vtab> {
/// Delete rowid or PK
fn delete(&mut self, 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(&mut self, args: &Values<'_>) -> Result<i64>;
/// Update: args[0] != NULL: old rowid or PK, args[1]: new row id or PK,
/// args[2]: ...
fn update(&mut self, args: &Values<'_>) -> Result<()>;
}
/// Index constraint operator.
/// See [Virtual Table Constraint Operator Codes](https://sqlite.org/c3ref/c_index_constraint_eq.html) for details.
#[derive(Debug, PartialEq)]
@ -1153,6 +1205,49 @@ where
}
}
unsafe extern "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]))
} else if 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)) => {
if let 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]