Skip to main content

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}