sqlite3_ext/vtab/mod.rs
1//! Create virtual tables.
2//!
3//! To create a virtual table, define the virtual table module and then register it on each
4//! connection it will be used from. The [sqlite3_ext_vtab](sqlite3_ext_macro::sqlite3_ext_vtab) macro is used to define the virtual table module. It can be registered using [Connection::create_module].
5//!
6//! There are 3 base types of virtual tables:
7//!
8//! - [StandardModule] is a virtual table which is created using the CREATE VIRTUAL TABLE
9//! command.
10//! - [EponymousModule] is a virtual table which is available ambiently in the database
11//! connection without being explicitly created.
12//! - [EponymousOnlyModule] is similar to EponymousModule, but CREATE VIRTUAL TABLE is
13//! explicitly forbidden for these modules.
14//!
15//! In addition to the base type of virtual table, there are several traits which can be
16//! implemented to add behavior.
17//!
18//! - [VTab] is required to be implemented by all virtual tables.
19//! - [CreateVTab] indicates that the table supports CREATE VIRTUAL TABLE.
20//! - [UpdateVTab] indicates that the table supports INSERT/UPDATE/DELETE.
21//! - [TransactionVTab] indicates that the table supports ROLLBACK.
22//! - [FindFunctionVTab] indicates that the table overrides certain SQL functions when they
23//! operate on the table.
24//! - [RenameVTab] indicates that the table supports ALTER TABLE RENAME TO.
25
26use super::{
27 ffi, function::ToContextResult, sqlite3_match_version, types::*, value::*, Connection,
28};
29pub use function::*;
30pub use index_info::*;
31pub use module::*;
32use std::{ffi::c_void, ops::Deref, slice};
33
34mod function;
35mod index_info;
36mod module;
37pub(crate) mod stubs;
38
39pub type DisconnectResult<T> = std::result::Result<(), (T, Error)>;
40
41/// A virtual table.
42///
43/// This trait defines functionality required by all virtual tables. A read-only,
44/// eponymous-only virtual table (e.g. a table-valued function) can implement only this trait.
45pub trait VTab<'vtab>: Sized {
46 /// Additional data associated with the virtual table module.
47 ///
48 /// When registering the module with [Connection::create_module], additional data can
49 /// be passed as a parameter. This data will be passed to [connect](VTab::connect) and
50 /// [create](CreateVTab::create). It can be used for any purpose.
51 type Aux: 'vtab;
52
53 /// Cursor implementation for this virtual table.
54 type Cursor: VTabCursor + 'vtab;
55
56 /// Corresponds to xConnect.
57 ///
58 /// This method is called called when connecting to an existing virtual table, either
59 /// because it was previously created with CREATE VIRTUAL TABLE (see
60 /// [CreateVTab::create]), or because it is an eponymous virtual table.
61 ///
62 /// This method must return a valid CREATE TABLE statement as a [String], along with a
63 /// configured table instance. Additionally, all virtual tables are recommended to set
64 /// a risk level using [VTabConnection::set_risk_level].
65 ///
66 /// The virtual table implementation will return an error if any of the arguments
67 /// contain invalid UTF-8.
68 fn connect(
69 db: &'vtab VTabConnection,
70 aux: &'vtab Self::Aux,
71 args: &[&str],
72 ) -> Result<(String, Self)>;
73
74 /// Corrresponds to xBestIndex.
75 ///
76 /// This method is called when SQLite is planning to query a virtual table. See
77 /// [IndexInfo] for details.
78 ///
79 /// If this method returns Err([SQLITE_CONSTRAINT]), that does not indicate an error. Rather,
80 /// it indicates that the particular combination of input parameters specified is insufficient
81 /// for the virtual table to do its job. This is logically the same as setting the
82 /// [estimated_cost](IndexInfo::set_estimated_cost) to infinity. If every call to best_index
83 /// for a particular query plan returns this error, that means there is no way for the virtual
84 /// table to be safely used, and the SQLite call will fail with a "no query solution" error.
85 fn best_index(&'vtab self, index_info: &mut IndexInfo) -> Result<()>;
86
87 /// Create an uninitialized query.
88 fn open(&'vtab self) -> Result<Self::Cursor>;
89
90 /// Corresponds to xDisconnect. This method is called when the database connection is
91 /// being closed. The implementation should not remove the underlying data, but it
92 /// should release any resources associated with the virtual table implementation. This method is the inverse of [Self::connect].
93 ///
94 /// The default implementation of this method simply returns Ok.
95 fn disconnect(self) -> DisconnectResult<Self> {
96 Ok(())
97 }
98}
99
100/// A non-eponymous virtual table that supports CREATE VIRTUAL TABLE.
101pub trait CreateVTab<'vtab>: VTab<'vtab> {
102 /// List of shadow table names.
103 ///
104 /// This can be set by a virtual table implementation to automatically implement the
105 /// xShadowName method. For example, "data" appears in this slice, then SQLite will
106 /// understand that "vtab_data" is a shadow table for a table named "vtab" created with
107 /// this module.
108 ///
109 /// Shadow tables are read-only if the database has SQLITE_DBCONFIG_DEFENSIVE set, and
110 /// SQLite is version 3.26.0 or greater. For more information, see [the SQLite
111 /// documentation](https://www.sqlite.org/vtab.html#the_xshadowname_method).
112 const SHADOW_NAMES: &'static [&'static str] = &[];
113
114 /// Corresponds to xCreate.
115 ///
116 /// This method is invoked when a CREATE VIRTUAL TABLE statement is invoked on the
117 /// module. Future connections to the created table will use [VTab::connect] instead.
118 ///
119 /// This method has the same requirements as [VTab::connect]; see that method
120 /// for more details.
121 fn create(
122 db: &'vtab VTabConnection,
123 aux: &'vtab Self::Aux,
124 args: &[&str],
125 ) -> Result<(String, Self)>;
126
127 /// Corresponds to xDestroy, when DROP TABLE is run on the virtual table. The virtual
128 /// table implementation should destroy any underlying state that was created by
129 /// [Self::create].
130 fn destroy(self) -> DisconnectResult<Self>;
131}
132
133/// A virtual table that supports INSERT/UPDATE/DELETE.
134pub trait UpdateVTab<'vtab>: VTab<'vtab> {
135 /// Modify a single row in the virtual table. The info parameter may be used to
136 /// determine the type of change being performed by this update.
137 ///
138 /// If the change is an INSERT for a table with rowids and the provided rowid was NULL,
139 /// then the virtual table must generate and return a rowid for the inserted row. In
140 /// all other cases, the returned Ok value of this method is ignored.
141 ///
142 /// It isn't possible to provide a mutable reference to the virtual table
143 /// implementation because there may be active cursors affecting the table or even the
144 /// row that is being updated. Use Rust's interior mutability types to properly
145 /// implement this method.
146 fn update(&'vtab self, info: &mut ChangeInfo) -> Result<i64>;
147}
148
149/// A virtual table that supports ROLLBACK.
150///
151/// See [VTabTransaction] for details.
152pub trait TransactionVTab<'vtab>: UpdateVTab<'vtab> {
153 type Transaction: VTabTransaction + 'vtab;
154
155 /// Begin a transaction.
156 fn begin(&'vtab self) -> Result<Self::Transaction>;
157}
158
159/// A virtual table that overloads some functions.
160///
161/// A virtual table implementation may choose to overload certain functions when the first
162/// argument to the function refers to a column in the virtual table. To do this, add a
163/// [VTabFunctionList] to the virtual table struct and return a reference to it from the
164/// [functions][FindFunctionVTab::functions] method. When a function uses a column from this
165/// virtual table as its first argument, the returned list will be checked to see if the
166/// virtual table would like to overload the function.
167///
168/// Overloading additionally allows the virtual table to indicate that the virtual table is
169/// able to exploit the function to speed up a query result. For this to work, the function
170/// must take exactly two arguments and appear as a boolean in the WHERE clause of a query. The
171/// [ConstraintOp] supplied with the function will then be provided as an [IndexInfoConstraint]
172/// to [VTab::best_index]. This feature additionally requires SQLite 3.25.0.
173///
174/// For more details, see [the SQLite documentation](https://www.sqlite.org/vtab.html#the_xfindfunction_method).
175///
176/// # Example
177///
178/// Here is a brief summary of how to use this trait:
179///
180/// ```no_run
181/// # use sqlite3_ext_macro::*;
182/// use sqlite3_ext::{function::*, vtab::*, *};
183///
184/// #[sqlite3_ext_vtab(StandardModule)]
185/// struct MyVTab<'vtab> {
186/// /// Used to store the overloaded functions
187/// functions: VTabFunctionList<'vtab, Self>
188/// }
189/// # sqlite3_ext_doctest_impl!(MyVTab<'vtab>);
190///
191/// impl MyVTab<'_> {
192/// /// Register the overloaded functions. Should be called from connect/create.
193/// fn init_functions(&mut self) {
194/// self.functions.add_method(1, "my_func", None, |vtab, ctx, args| {
195/// println!("my_func was called");
196/// ctx.set_result(&*args[0])
197/// });
198/// }
199/// }
200///
201/// /// Return the owned functions list.
202/// impl<'vtab> FindFunctionVTab<'vtab> for MyVTab<'vtab> {
203/// fn functions(&self) -> &VTabFunctionList<'vtab, Self> {
204/// &self.functions
205/// }
206/// }
207/// ```
208pub trait FindFunctionVTab<'vtab>: VTab<'vtab> {
209 /// Retrieve a reference to the [VTabFunctionList] associated with this virtual table.
210 fn functions(&'vtab self) -> &'vtab VTabFunctionList<'vtab, Self>;
211}
212
213/// A virtual table that supports ALTER TABLE RENAME.
214pub trait RenameVTab<'vtab>: VTab<'vtab> {
215 /// Corresponds to xRename, when ALTER TABLE RENAME is run on the virtual table. If
216 /// this method returns Ok, then SQLite will disconnect this virtual table
217 /// implementation and connect to a new implementation with the updated name.
218 fn rename(&'vtab self, name: &str) -> Result<()>;
219}
220
221/// Implementation of the cursor type for a virtual table.
222pub trait VTabCursor {
223 /// Begin a search of the virtual table. This method is always invoked after creating
224 /// the cursor, before any other methods of this trait. After calling this method, the
225 /// cursor should point to the first row of results (or [eof](VTabCursor::eof) should
226 /// return true to indicate there are no results).
227 ///
228 /// The index_num parameter is an arbitrary value which was passed to
229 /// [IndexInfo::set_index_num]. The index_str parameter is an arbitrary value which was
230 /// passed to [IndexInfo::set_index_str].
231 fn filter(
232 &mut self,
233 index_num: i32,
234 index_str: Option<&str>,
235 args: &mut [&mut ValueRef],
236 ) -> Result<()>;
237
238 /// Move the cursor one row forward.
239 fn next(&mut self) -> Result<()>;
240
241 /// Check if the cursor currently points beyond the end of the valid results.
242 fn eof(&mut self) -> bool;
243
244 /// Fetch the column numbered idx for the current row. The indexes correspond to the order the
245 /// columns were declared by [VTab::connect]. The output value must be assigned to the context
246 /// using [ColumnContext::set_result]. If no result is set, SQL NULL is returned. If this
247 /// method returns an Err value, the SQL statement will fail, even if a result had been set
248 /// before the failure.
249 fn column(&mut self, idx: usize, context: &ColumnContext) -> Result<()>;
250
251 /// Fetch the rowid for the current row.
252 fn rowid(&mut self) -> Result<i64>;
253}
254
255/// Implementation of the transaction type for a virtual table.
256///
257/// Virtual tables which modify resources outside of the database in which they are defined may
258/// require additional work in order to safely implement fallible transactions. If the virtual
259/// table only modifies data inside of the database in which it is defined, then SQLite's
260/// built-in transaction support is sufficient and implementing [TransactionVTab] is not
261/// necessary. The most important methods of this trait are
262/// [rollback](VTabTransaction::rollback) and [rollback_to](VTabTransaction::rollback_to). If
263/// it is not possible to correctly implement these methods for the virtual table, then there
264/// is no need to implement [TransactionVTab] at all.
265///
266/// Virtual table transactions do not nest, so there will never be more than one instance of
267/// this trait per virtual table. Instances are always dropped in a call to either
268/// [commit](VTabTransaction::commit) or [rollback](VTabTransaction::rollback), with one
269/// exception: eponymous tables implementing this trait automatically begin a transaction after
270/// [VTab::connect], but this transaction will be later on dropped without any methods being
271/// called on it. This is harmless, because if an UPDATE occurs for such a table, a new
272/// transaction will be created, dropping the previous one first.
273///
274/// Note that the [savepoint](VTabTransaction::savepoint), [release](VTabTransaction::release),
275/// and [rollback_to](VTabTransaction::rollback_to) methods require SQLite 3.7.7. On previous
276/// versions of SQLite, these methods will not be called, which may result in unsound behavior.
277/// In the following example, the virtual table will incorrectly commit changes which should
278/// have been rolled back.
279///
280/// ```sql
281/// BEGIN;
282/// SAVEPOINT a;
283/// UPDATE my_virtual_table SET foo = 'bar';
284/// ROLLBACK TO a;
285/// COMMIT;
286/// ```
287pub trait VTabTransaction {
288 /// Start a two-phase commit.
289 ///
290 /// This method is only invoked prior to a commit or rollback. In order to implement
291 /// two-phase commit, the sync method on all virtual tables is invoked prior to
292 /// invoking the commit method on any virtual table. If any of the sync methods fail,
293 /// the entire transaction is rolled back.
294 fn sync(&mut self) -> Result<()>;
295
296 /// Finish a commit.
297 ///
298 /// A call to this method always follows a prior call sync.
299 fn commit(self) -> Result<()>;
300
301 /// Roll back a commit.
302 fn rollback(self) -> Result<()>;
303
304 /// Save current state as a save point.
305 ///
306 /// The current state of the virtual table should be saved as savepoint n. There is
307 /// no guarantee that n starts at zero or increases by 1 in between calls.
308 ///
309 /// This method will only be called on SQLite 3.7.7 or later.
310 fn savepoint(&mut self, n: i32) -> Result<()>;
311
312 /// Invalidate previous save points.
313 ///
314 /// All save points numbered >= n should be invalidated. This does not mean the
315 /// changes are ready to be committed, just that there is no need to maintain a record
316 /// of those saved states any more.
317 ///
318 /// Note that there is no guarantee that n will be a value from a previous call to
319 /// [savepoint](VTabTransaction::savepoint).
320 ///
321 /// This method will only be called on SQLite 3.7.7 or later.
322 fn release(&mut self, n: i32) -> Result<()>;
323
324 /// Restore a save point.
325 ///
326 /// The virtual table should revert to the state it had when
327 /// [savepoint](VTabTransaction::savepoint) was called the lowest number >= n. There is
328 /// no guarantee that [savepoint](VTabTransaction::savepoint) was ever called with n
329 /// exactly.
330 ///
331 /// This method will only be called on SQLite 3.7.7 or later.
332 fn rollback_to(&mut self, n: i32) -> Result<()>;
333}
334
335/// A wrapper around [Connection] that supports configuring virtual table implementations.
336#[repr(transparent)]
337pub struct VTabConnection {
338 db: ffi::sqlite3,
339}
340
341impl VTabConnection {
342 unsafe fn from_ptr<'a>(db: *mut ffi::sqlite3) -> &'a Self {
343 &*(db as *mut Self)
344 }
345
346 /// Indicate that this virtual table properly verifies constraints for updates.
347 ///
348 /// If this is enabled, then the virtual table guarantees that if the
349 /// [UpdateVTab::update] method returns Err([SQLITE_CONSTRAINT]), it will do so before
350 /// any modifications to internal or persistent data structures have been made. If the
351 /// ON CONFLICT mode is ABORT, FAIL, IGNORE or ROLLBACK, SQLite is able to roll back a
352 /// statement or database transaction, and abandon or continue processing the current
353 /// SQL statement as appropriate. If the ON CONFLICT mode is REPLACE and the update
354 /// method returns SQLITE_CONSTRAINT, SQLite handles this as if the ON CONFLICT mode
355 /// had been ABORT.
356 ///
357 /// Requires SQLite 3.7.7. On earlier versions of SQLite, this is a harmless no-op.
358 pub fn enable_constraints(&self) {
359 sqlite3_match_version! {
360 3_007_007 => unsafe {
361 let guard = self.lock();
362 Error::from_sqlite_desc(ffi::sqlite3_vtab_config()(
363 guard.as_mut_ptr(),
364 ffi::SQLITE_VTAB_CONSTRAINT_SUPPORT,
365 1,
366 ), guard)
367 .unwrap()
368 },
369 _ => (),
370 }
371 }
372
373 /// Set the risk level of this virtual table.
374 ///
375 /// See the [RiskLevel](super::RiskLevel) enum for details about what the individual
376 /// options mean.
377 ///
378 /// Requires SQLite 3.31.0. On earlier versions of SQLite, this is a harmless no-op.
379 pub fn set_risk_level(&self, level: super::RiskLevel) {
380 let _ = level;
381 sqlite3_match_version! {
382 3_031_000 => unsafe {
383 let guard = self.lock();
384 Error::from_sqlite_desc(ffi::sqlite3_vtab_config()(
385 guard.as_mut_ptr(),
386 match level {
387 super::RiskLevel::Innocuous => ffi::SQLITE_VTAB_INNOCUOUS,
388 super::RiskLevel::DirectOnly => ffi::SQLITE_VTAB_DIRECTONLY,
389 },
390 ), guard)
391 .unwrap();
392 },
393 _ => (),
394 }
395 }
396}
397
398impl Deref for VTabConnection {
399 type Target = Connection;
400
401 fn deref(&self) -> &Connection {
402 unsafe { Connection::from_ptr(&self.db as *const _ as _) }
403 }
404}
405
406/// Information about an INSERT/UPDATE/DELETE on a virtual table.
407pub struct ChangeInfo {
408 #[cfg_attr(not(modern_sqlite), allow(unused))]
409 db: *mut ffi::sqlite3,
410 argc: usize,
411 argv: *mut *mut ValueRef,
412}
413
414impl ChangeInfo {
415 /// Returns the type of update being performed.
416 pub fn change_type(&self) -> ChangeType {
417 if self.args().len() == 0 {
418 ChangeType::Delete
419 } else if self.rowid().is_null() {
420 ChangeType::Insert
421 } else {
422 ChangeType::Update
423 }
424 }
425
426 /// Returns the rowid (or, for WITHOUT ROWID tables, the PRIMARY KEY column) of the row
427 /// being deleted or updated.
428 ///
429 /// Semantically, an UPDATE to a virtual table is identical to a DELETE followed by an
430 /// INSERT. In that sense, this method returns the rowid or PRIMARY KEY column of the
431 /// row being deleted. The rowid of the row being inserted is available as the first
432 /// element in [args](Self::args).
433 ///
434 /// For the mutable version, see [rowid_mut](Self::rowid_mut).
435 pub fn rowid(&self) -> &ValueRef {
436 debug_assert!(self.argc > 0);
437 unsafe { &**self.argv }
438 }
439
440 /// Mutable version of [rowid](Self::rowid).
441 pub fn rowid_mut(&mut self) -> &mut ValueRef {
442 debug_assert!(self.argc > 0);
443 unsafe { &mut **self.argv }
444 }
445
446 /// Returns the arguments for an INSERT or UPDATE. The meaning of the first element in
447 /// this slice depends on the type of change being performed:
448 ///
449 /// - For an INSERT on a WITHOUT ROWID table, the first element is always NULL. The
450 /// PRIMARY KEY is listed among the remaining elements.
451 /// - For an INSERT on a regular table, if the first element is NULL, it indicates that
452 /// a rowid must be generated and returned from [UpdateVTab::update]. Otherwise, the
453 /// first element is the rowid.
454 /// - For an UPDATE, the first element is the new value for the rowid or PRIMARY KEY
455 /// column.
456 ///
457 /// In all cases, the second and following elements correspond to the values for all
458 /// columns in the order declared in the virtual table's schema (returned by
459 /// [VTab::connect] / [CreateVTab::create]).
460 ///
461 /// For the mutable version, see [args_mut](Self::args_mut).
462 pub fn args(&self) -> &[&ValueRef] {
463 debug_assert!(self.argc > 0);
464 unsafe { slice::from_raw_parts(self.argv.offset(1) as _, self.argc - 1) }
465 }
466
467 /// Mutable version of [args](Self::args).
468 pub fn args_mut(&mut self) -> &mut [&mut ValueRef] {
469 debug_assert!(self.argc > 0);
470 unsafe { slice::from_raw_parts_mut(self.argv.offset(1) as _, self.argc - 1) }
471 }
472
473 /// Return the ON CONFLICT mode of the current SQL statement. In order for this method
474 /// to be useful, the virtual table needs to have previously enabled ON CONFLICT
475 /// support using [VTabConnection::enable_constraints].
476 ///
477 /// Requires SQLite 3.7.7. On earlier versions, this will always return
478 /// [ConflictMode::Abort].
479 pub fn conflict_mode(&self) -> ConflictMode {
480 sqlite3_match_version! {
481 3_007_007 => {
482 ConflictMode::from_sqlite(unsafe { ffi::sqlite3_vtab_on_conflict(self.db) })
483 }
484 _ => ConflictMode::Abort,
485 }
486 }
487}
488
489impl std::fmt::Debug for ChangeInfo {
490 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::result::Result<(), std::fmt::Error> {
491 f.debug_struct("ChangeInfo")
492 .field("change_type", &self.change_type())
493 .field("rowid", &self.rowid())
494 .field("args", &self.args())
495 .field("conflict_mode", &self.conflict_mode())
496 .finish()
497 }
498}
499
500/// Indicates the type of modification that is being applied to the virtual table.
501#[derive(Debug, Eq, PartialEq, Copy, Clone)]
502pub enum ChangeType {
503 /// Indicates an SQL INSERT.
504 Insert,
505 /// Indicates an SQL DELETE.
506 Delete,
507 /// Indicates an SQL UPDATE.
508 Update,
509}
510
511/// Indicates the ON CONFLICT mode for the SQL statement currently being executed.
512///
513/// An [UpdateVTab] which has used [VTabConnection::enable_constraints] can examine this value
514/// to determine how to handle a conflict during a change.
515///
516/// For details about what each mode means, see [the SQLite documentation](https://www.sqlite.org/lang_conflict.html).
517#[derive(Debug, Eq, PartialEq, Copy, Clone)]
518pub enum ConflictMode {
519 /// Corresponds to ON CONFLICT ROLLBACK.
520 Rollback,
521 /// Corresponds to ON CONFLICT IGNORE.
522 Ignore,
523 /// Corresponds to ON CONFLICT FAIL.
524 Fail,
525 /// Corresponds to ON CONFLICT ABORT.
526 Abort,
527 /// Corresponds to ON CONFLICT REPLACE.
528 Replace,
529}
530
531impl ConflictMode {
532 #[cfg(modern_sqlite)]
533 fn from_sqlite(val: i32) -> Self {
534 match val {
535 1 => ConflictMode::Rollback,
536 2 => ConflictMode::Ignore,
537 3 => ConflictMode::Fail,
538 4 => ConflictMode::Abort,
539 5 => ConflictMode::Replace,
540 _ => panic!("invalid conflict mode"),
541 }
542 }
543}
544
545/// Describes the run-time environment of the [VTabCursor::column] method.
546#[repr(transparent)]
547pub struct ColumnContext {
548 base: ffi::sqlite3_context,
549}
550
551impl ColumnContext {
552 pub(crate) fn as_ptr<'a>(&self) -> *mut ffi::sqlite3_context {
553 &self.base as *const ffi::sqlite3_context as _
554 }
555
556 pub(crate) unsafe fn from_ptr<'a>(base: *mut ffi::sqlite3_context) -> &'a mut Self {
557 &mut *(base as *mut Self)
558 }
559
560 /// Return a handle to the current database.
561 pub fn db(&self) -> &Connection {
562 unsafe { Connection::from_ptr(ffi::sqlite3_context_db_handle(self.as_ptr())) }
563 }
564
565 /// Return true if the column being fetched is part of an UPDATE operation during which
566 /// the column value will not change.
567 ///
568 /// See [ValueRef::nochange] for details and usage.
569 ///
570 /// This method is provided as an optimization. It is permissible for this method to
571 /// return false even if the value is unchanged. The virtual table implementation must
572 /// function correctly even if this method were to always return false.
573 ///
574 /// Requires SQLite 3.22.0. On earlier versions of SQLite, this method always returns
575 /// false.
576 pub fn nochange(&self) -> bool {
577 crate::sqlite3_match_version! {
578 3_022_000 => (unsafe { ffi::sqlite3_vtab_nochange(self.as_ptr()) } != 0),
579 _ => false,
580 }
581 }
582
583 /// Assign the given value to the column. This function always returns Ok.
584 pub fn set_result(&self, val: impl ToContextResult) -> Result<()> {
585 unsafe { val.assign_to(self.as_ptr()) };
586 Ok(())
587 }
588}