mirror of
				https://github.com/isar/rusqlite.git
				synced 2025-10-31 22:08:55 +08:00 
			
		
		
		
	Add support to updatable virtual tables
This commit is contained in:
		| @@ -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] | ||||
|   | ||||
		Reference in New Issue
	
	Block a user