/// The context received by an authorizer hook. /// /// See <https://sqlite.org/c3ref/set_authorizer.html> for more info. #[derive(Clone, Copy, Debug, Eq, PartialEq)] pubstruct AuthContext<'c> { /// The action to be authorized. pub action: AuthAction<'c>,
/// The database name, if applicable. pub database_name: Option<&'c str>,
/// The inner-most trigger or view responsible for the access attempt. /// `None` if the access attempt was made by top-level SQL code. pub accessor: Option<&'c str>,
}
/// Actions and arguments found within a statement during /// preparation. /// /// See <https://sqlite.org/c3ref/c_alter_table.html> for more info. #[derive(Clone, Copy, Debug, Eq, PartialEq)] #[non_exhaustive] #[allow(missing_docs)] pubenum AuthAction<'c> { /// This variant is not normally produced by SQLite. You may encounter it // if you're using a different version than what's supported by this library.
Unknown { /// The unknown authorization action code.
code: i32, /// The third arg to the authorizer callback.
arg1: Option<&'c str>, /// The fourth arg to the authorizer callback.
arg2: Option<&'c str>,
},
CreateIndex {
index_name: &'c str,
table_name: &'c str,
},
CreateTable {
table_name: &'c str,
},
CreateTempIndex {
index_name: &'c str,
table_name: &'c str,
},
CreateTempTable {
table_name: &'c str,
},
CreateTempTrigger {
trigger_name: &'c str,
table_name: &'c str,
},
CreateTempView {
view_name: &'c str,
},
CreateTrigger {
trigger_name: &'c str,
table_name: &'c str,
},
CreateView {
view_name: &'c str,
},
Delete {
table_name: &'c str,
},
DropIndex {
index_name: &'c str,
table_name: &'c str,
},
DropTable {
table_name: &'c str,
},
DropTempIndex {
index_name: &'c str,
table_name: &'c str,
},
DropTempTable {
table_name: &'c str,
},
DropTempTrigger {
trigger_name: &'c str,
table_name: &'c str,
},
DropTempView {
view_name: &'c str,
},
DropTrigger {
trigger_name: &'c str,
table_name: &'c str,
},
DropView {
view_name: &'c str,
},
Insert {
table_name: &'c str,
},
Pragma {
pragma_name: &'c str, /// The pragma value, if present (e.g., `PRAGMA name = value;`).
pragma_value: Option<&'c str>,
},
Read {
table_name: &'c str,
column_name: &'c str,
},
Select,
Transaction {
operation: TransactionOperation,
},
Update {
table_name: &'c str,
column_name: &'c str,
},
Attach {
filename: &'c str,
},
Detach {
database_name: &'c str,
},
AlterTable {
database_name: &'c str,
table_name: &'c str,
},
Reindex {
index_name: &'c str,
},
Analyze {
table_name: &'c str,
},
CreateVtable {
table_name: &'c str,
module_name: &'c str,
},
DropVtable {
table_name: &'c str,
module_name: &'c str,
},
Function {
function_name: &'c str,
},
Savepoint {
operation: TransactionOperation,
savepoint_name: &'c str,
},
Recursive,
}
impl Connection { /// Register a callback function to be invoked whenever /// a transaction is committed. /// /// The callback returns `true` to rollback. #[inline] pubfn commit_hook<F>(&self, hook: Option<F>) where
F: FnMut() -> bool + Send + 'static,
{ self.db.borrow_mut().commit_hook(hook);
}
/// Register a callback function to be invoked whenever /// a transaction is committed. #[inline] pubfn rollback_hook<F>(&self, hook: Option<F>) where
F: FnMut() + Send + 'static,
{ self.db.borrow_mut().rollback_hook(hook);
}
/// Register a callback function to be invoked whenever /// a row is updated, inserted or deleted in a rowid table. /// /// The callback parameters are: /// /// - the type of database update (`SQLITE_INSERT`, `SQLITE_UPDATE` or /// `SQLITE_DELETE`), /// - the name of the database ("main", "temp", ...), /// - the name of the table that is updated, /// - the ROWID of the row that is updated. #[inline] pubfn update_hook<F>(&self, hook: Option<F>) where
F: FnMut(Action, &str, &str, i64) + Send + 'static,
{ self.db.borrow_mut().update_hook(hook);
}
/// Register a callback that is invoked each time data is committed to a database in wal mode. /// /// A single database handle may have at most a single write-ahead log callback registered at one time. /// Calling `wal_hook` replaces any previously registered write-ahead log callback. /// Note that the `sqlite3_wal_autocheckpoint()` interface and the `wal_autocheckpoint` pragma /// both invoke `sqlite3_wal_hook()` and will overwrite any prior `sqlite3_wal_hook()` settings. pubfn wal_hook(&self, hook: Option<fn(&Wal, c_int) -> Result<()>>) { unsafeextern"C"fn wal_hook_callback(
client_data: *mut c_void,
db: *mut ffi::sqlite3,
db_name: *const c_char,
pages: c_int,
) -> c_int { let hook_fn: fn(&Wal, c_int) -> Result<()> = std::mem::transmute(client_data); let wal = Wal { db, db_name };
catch_unwind(|| match hook_fn(&wal, pages) {
Ok(_) => ffi::SQLITE_OK,
Err(e) => e
.sqlite_error()
.map_or(ffi::SQLITE_ERROR, |x| x.extended_code),
})
.unwrap_or_default()
} let c = self.db.borrow_mut(); match hook {
Some(f) => unsafe {
ffi::sqlite3_wal_hook(c.db(), Some(wal_hook_callback), f as *mut c_void)
},
None => unsafe { ffi::sqlite3_wal_hook(c.db(), None, ptr::null_mut()) },
};
}
/// Register a query progress callback. /// /// The parameter `num_ops` is the approximate number of virtual machine /// instructions that are evaluated between successive invocations of the /// `handler`. If `num_ops` is less than one then the progress handler /// is disabled. /// /// If the progress callback returns `true`, the operation is interrupted. pubfn progress_handler<F>(&self, num_ops: c_int, handler: Option<F>) where
F: FnMut() -> bool + Send + 'static,
{ self.db.borrow_mut().progress_handler(num_ops, handler);
}
/// Register an authorizer callback that's invoked /// as a statement is being prepared. #[inline] pubfn authorizer<'c, F>(&self, hook: Option<F>) where
F: for<'r> FnMut(AuthContext<'r>) -> Authorization + Send + 'static,
{ self.db.borrow_mut().authorizer(hook);
}
}
/// Checkpoint mode #[derive(Clone, Copy)] #[repr(i32)] #[non_exhaustive] pubenum CheckpointMode { /// Do as much as possible w/o blocking
PASSIVE = ffi::SQLITE_CHECKPOINT_PASSIVE, /// Wait for writers, then checkpoint
FULL = ffi::SQLITE_CHECKPOINT_FULL, /// Like FULL but wait for readers
RESTART = ffi::SQLITE_CHECKPOINT_RESTART, /// Like RESTART but also truncate WAL
TRUNCATE = ffi::SQLITE_CHECKPOINT_TRUNCATE,
}
let callback_fn = authorizer
.as_ref()
.map(|_| call_boxed_closure::<'c, F> as unsafe extern "C" fn(_, _, _, _, _, _) -> _); let boxed_authorizer = authorizer.map(Box::new);
matchunsafe {
ffi::sqlite3_set_authorizer( self.db(),
callback_fn,
boxed_authorizer
.as_ref()
.map_or_else(ptr::null_mut, |f| &**f as *const F as *mut _),
)
} {
ffi::SQLITE_OK => { self.authorizer = boxed_authorizer.map(|ba| ba as _);
}
err_code => { // The only error that `sqlite3_set_authorizer` returns is `SQLITE_MISUSE` // when compiled with `ENABLE_API_ARMOR` and the db pointer is invalid. // This library does not allow constructing a null db ptr, so if this branch // is hit, something very bad has happened. Panicking instead of returning // `Result` keeps this hook's API consistent with the others.
panic!("unexpectedly failed to set_authorizer: {}", unsafe { crate::error::error_from_handle(self.db(), err_code)
});
}
}
}
}
db.authorizer(None::<fn(AuthContext<'_>) -> Authorization>);
db.execute_batch("PRAGMA user_version=1")?; // Disallowed by first authorizer, but it's now removed.
Ok(())
}
#[test] fn wal_hook() -> Result<()> { let temp_dir = tempfile::tempdir().unwrap(); let path = temp_dir.path().join("wal-hook.db3");
let db = Connection::open(&path)?; let journal_mode: String =
db.pragma_update_and_check(None, "journal_mode", "wal", |row| row.get(0))?;
assert_eq!(journal_mode, "wal");
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.