sqlite3_ext/value/passed_ref.rs
1use std::any::{Any, TypeId};
2
3pub(crate) const POINTER_TAG: *const i8 = b"sqlite3_ext:PassedRef\0".as_ptr() as _;
4
5/// Pass arbitrary values through SQLite.
6///
7/// Values of this type can be passed into SQL queries and returned by SQL functions, and later retrieved using
8/// [ValueRef::get_ref](super::ValueRef::get_ref). SQLite takes ownership of the stored value,
9/// and does not provide any mechanism for getting a PassedRef from a query result, so this
10/// feature is primarily useful for passing values into SQL, or between application-defined
11/// functions.
12///
13/// This mechanism relies on [std::any::Any] to ensure type safety, which requires that values
14/// are `'static`. If you want to transfer a reference through a PassedRef, use a shared
15/// pointer like [std::rc::Rc].
16///
17/// This feature requires SQLite 3.20.0. On earlier versions of SQLite, returning a PassedRef
18/// object from an application-defined function has no effect. If supporting older versions of
19/// SQLite is required, [UnsafePtr](super::UnsafePtr) can be used instead.
20///
21/// # Examples
22///
23/// This example shows `produce_ref` returning a PassedRef which is later consumed by
24/// `consume_ref`.
25///
26/// ```no_run
27/// use sqlite3_ext::{PassedRef, function::Context, Result, ValueRef};
28///
29/// fn produce_ref(ctx: &Context, args: &mut [&mut ValueRef]) -> PassedRef<String> {
30/// let val = "owned string".to_owned();
31/// PassedRef::new(val)
32/// }
33///
34/// fn consume_ref(ctx: &Context, args: &mut [&mut ValueRef]) -> Result<()> {
35/// let val = args[0].get_ref::<String>().unwrap();
36/// assert_eq!(val, "owned string");
37/// Ok(())
38/// }
39/// ```
40#[repr(C)]
41pub struct PassedRef<T: 'static> {
42 type_id: TypeId,
43 value: T,
44}
45
46impl<T: 'static> PassedRef<T> {
47 /// Create a new PassedRef containing the value.
48 pub fn new(value: T) -> PassedRef<T> {
49 PassedRef {
50 type_id: value.type_id(),
51 value,
52 }
53 }
54
55 pub(crate) fn get(&self) -> Option<&T> {
56 if TypeId::of::<T>() == self.type_id {
57 Some(&self.value)
58 } else {
59 None
60 }
61 }
62}
63
64impl<T: 'static> std::fmt::Debug for PassedRef<T> {
65 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::result::Result<(), std::fmt::Error> {
66 f.debug_struct("PassedRef")
67 .field("type_id", &self.type_id)
68 .finish_non_exhaustive()
69 }
70}
71
72#[cfg(all(modern_sqlite, test, feature = "static"))]
73mod test {
74 use crate::test_helpers::prelude::*;
75
76 #[test]
77 fn get_ref() {
78 let h = TestHelpers::new();
79 #[derive(PartialEq, Debug)]
80 struct MyStruct {
81 s: String,
82 }
83 let owned_struct = MyStruct {
84 s: "input string".to_owned(),
85 };
86 h.with_value(PassedRef::new(owned_struct), |val| {
87 assert_eq!(val.value_type(), ValueType::Null);
88 assert_eq!(
89 val.get_ref::<MyStruct>(),
90 Some(&MyStruct {
91 s: "input string".to_owned()
92 })
93 );
94 let mut dbg = format!("{:?}", val);
95 dbg.replace_range(38..(dbg.len() - 9), "XXX");
96 assert_eq!(dbg, "Null(PassedRef { type_id: TypeId { t: XXX }, .. })");
97 Ok(())
98 });
99 }
100
101 #[test]
102 fn invalid_get_ref() {
103 let h = TestHelpers::new();
104 h.with_value(PassedRef::new(0i32), |val| {
105 assert_eq!(val.value_type(), ValueType::Null);
106 assert_eq!(val.get_ref::<String>(), None);
107 Ok(())
108 });
109 }
110
111 #[test]
112 fn get_mut_ref() {
113 use std::cell::Cell;
114 use std::rc::Rc;
115
116 let h = TestHelpers::new();
117 let r = Rc::new(Cell::new(0i32));
118 h.with_value(PassedRef::new(r.clone()), |val| {
119 let r = val.get_ref::<Rc<Cell<i32>>>().unwrap();
120 r.set(2);
121 Ok(())
122 });
123 assert_eq!(r.get(), 2);
124 }
125}