Skip to main content

rusqlite/types/
value_ref.rs

1use super::{Type, Value};
2use crate::types::{FromSqlError, FromSqlResult};
3
4/// A non-owning [dynamic type value](http://sqlite.org/datatype3.html). Typically, the
5/// memory backing this value is owned by SQLite.
6///
7/// See [`Value`](Value) for an owning dynamic type value.
8#[derive(Copy, Clone, Debug, PartialEq)]
9pub enum ValueRef<'a> {
10    /// The value is a `NULL` value.
11    Null,
12    /// The value is a signed integer.
13    Integer(i64),
14    /// The value is a floating point number.
15    Real(f64),
16    /// The value is a text string.
17    Text(&'a [u8]),
18    /// The value is a blob of data
19    Blob(&'a [u8]),
20}
21
22impl ValueRef<'_> {
23    /// Returns SQLite fundamental datatype.
24    #[inline]
25    #[must_use]
26    pub fn data_type(&self) -> Type {
27        match *self {
28            ValueRef::Null => Type::Null,
29            ValueRef::Integer(_) => Type::Integer,
30            ValueRef::Real(_) => Type::Real,
31            ValueRef::Text(_) => Type::Text,
32            ValueRef::Blob(_) => Type::Blob,
33        }
34    }
35}
36
37impl<'a> ValueRef<'a> {
38    /// If `self` is case `Integer`, returns the integral value. Otherwise,
39    /// returns [`Err(FromSqlError::InvalidType)`](FromSqlError::InvalidType).
40    #[inline]
41    pub fn as_i64(&self) -> FromSqlResult<i64> {
42        match *self {
43            ValueRef::Integer(i) => Ok(i),
44            _ => Err(FromSqlError::InvalidType),
45        }
46    }
47
48    /// If `self` is case `Null` returns None.
49    /// If `self` is case `Integer`, returns the integral value.
50    /// Otherwise, returns [`Err(FromSqlError::InvalidType)`](FromSqlError::InvalidType).
51    #[inline]
52    pub fn as_i64_or_null(&self) -> FromSqlResult<Option<i64>> {
53        match *self {
54            ValueRef::Null => Ok(None),
55            ValueRef::Integer(i) => Ok(Some(i)),
56            _ => Err(FromSqlError::InvalidType),
57        }
58    }
59
60    /// If `self` is case `Real`, returns the floating point value. Otherwise,
61    /// returns [`Err(FromSqlError::InvalidType)`](FromSqlError::InvalidType).
62    #[inline]
63    pub fn as_f64(&self) -> FromSqlResult<f64> {
64        match *self {
65            ValueRef::Real(f) => Ok(f),
66            _ => Err(FromSqlError::InvalidType),
67        }
68    }
69
70    /// If `self` is case `Null` returns None.
71    /// If `self` is case `Real`, returns the floating point value.
72    /// Otherwise, returns [`Err(FromSqlError::InvalidType)`](FromSqlError::InvalidType).
73    #[inline]
74    pub fn as_f64_or_null(&self) -> FromSqlResult<Option<f64>> {
75        match *self {
76            ValueRef::Null => Ok(None),
77            ValueRef::Real(f) => Ok(Some(f)),
78            _ => Err(FromSqlError::InvalidType),
79        }
80    }
81
82    /// If `self` is case `Text`, returns the string value. Otherwise, returns
83    /// [`Err(FromSqlError::InvalidType)`](FromSqlError::InvalidType).
84    #[inline]
85    pub fn as_str(&self) -> FromSqlResult<&'a str> {
86        match *self {
87            ValueRef::Text(t) => Ok(std::str::from_utf8(t)?),
88            _ => Err(FromSqlError::InvalidType),
89        }
90    }
91
92    /// If `self` is case `Null` returns None.
93    /// If `self` is case `Text`, returns the string value.
94    /// Otherwise, returns [`Err(FromSqlError::InvalidType)`](FromSqlError::InvalidType).
95    #[inline]
96    pub fn as_str_or_null(&self) -> FromSqlResult<Option<&'a str>> {
97        match *self {
98            ValueRef::Null => Ok(None),
99            ValueRef::Text(t) => Ok(Some(std::str::from_utf8(t)?)),
100            _ => Err(FromSqlError::InvalidType),
101        }
102    }
103
104    /// If `self` is case `Blob`, returns the byte slice. Otherwise, returns
105    /// [`Err(FromSqlError::InvalidType)`](FromSqlError::InvalidType).
106    #[inline]
107    pub fn as_blob(&self) -> FromSqlResult<&'a [u8]> {
108        match *self {
109            ValueRef::Blob(b) => Ok(b),
110            _ => Err(FromSqlError::InvalidType),
111        }
112    }
113
114    /// If `self` is case `Null` returns None.
115    /// If `self` is case `Blob`, returns the byte slice.
116    /// Otherwise, returns [`Err(FromSqlError::InvalidType)`](FromSqlError::InvalidType).
117    #[inline]
118    pub fn as_blob_or_null(&self) -> FromSqlResult<Option<&'a [u8]>> {
119        match *self {
120            ValueRef::Null => Ok(None),
121            ValueRef::Blob(b) => Ok(Some(b)),
122            _ => Err(FromSqlError::InvalidType),
123        }
124    }
125
126    /// Returns the byte slice that makes up this `ValueRef` if it's either
127    /// [`ValueRef::Blob`] or [`ValueRef::Text`].
128    #[inline]
129    pub fn as_bytes(&self) -> FromSqlResult<&'a [u8]> {
130        match self {
131            ValueRef::Text(s) | ValueRef::Blob(s) => Ok(s),
132            _ => Err(FromSqlError::InvalidType),
133        }
134    }
135
136    /// If `self` is case `Null` returns None.
137    /// If `self` is [`ValueRef::Blob`] or [`ValueRef::Text`] returns the byte
138    /// slice that makes up this value
139    #[inline]
140    pub fn as_bytes_or_null(&self) -> FromSqlResult<Option<&'a [u8]>> {
141        match *self {
142            ValueRef::Null => Ok(None),
143            ValueRef::Text(s) | ValueRef::Blob(s) => Ok(Some(s)),
144            _ => Err(FromSqlError::InvalidType),
145        }
146    }
147}
148
149impl TryFrom<ValueRef<'_>> for Value {
150    type Error = FromSqlError;
151
152    #[inline]
153    #[track_caller]
154    fn try_from(borrowed: ValueRef<'_>) -> Result<Self, Self::Error> {
155        Ok(match borrowed {
156            ValueRef::Null => Self::Null,
157            ValueRef::Integer(i) => Self::Integer(i),
158            ValueRef::Real(r) => Self::Real(r),
159            ValueRef::Text(s) => Self::Text(std::str::from_utf8(s)?.to_owned()),
160            ValueRef::Blob(b) => Self::Blob(b.to_vec()),
161        })
162    }
163}
164
165impl<'a> From<&'a str> for ValueRef<'a> {
166    #[inline]
167    fn from(s: &str) -> ValueRef<'_> {
168        ValueRef::Text(s.as_bytes())
169    }
170}
171
172impl<'a> From<&'a [u8]> for ValueRef<'a> {
173    #[inline]
174    fn from(s: &[u8]) -> ValueRef<'_> {
175        ValueRef::Blob(s)
176    }
177}
178
179impl<'a> From<&'a Value> for ValueRef<'a> {
180    #[inline]
181    fn from(value: &'a Value) -> Self {
182        match *value {
183            Value::Null => ValueRef::Null,
184            Value::Integer(i) => ValueRef::Integer(i),
185            Value::Real(r) => ValueRef::Real(r),
186            Value::Text(ref s) => ValueRef::Text(s.as_bytes()),
187            Value::Blob(ref b) => ValueRef::Blob(b),
188        }
189    }
190}
191
192impl<T> From<Option<T>> for ValueRef<'_>
193where
194    T: Into<Self>,
195{
196    #[inline]
197    fn from(s: Option<T>) -> Self {
198        match s {
199            Some(x) => x.into(),
200            None => ValueRef::Null,
201        }
202    }
203}
204
205#[cfg(any(
206    feature = "functions",
207    feature = "session",
208    feature = "vtab",
209    feature = "preupdate_hook"
210))]
211impl ValueRef<'_> {
212    pub(crate) unsafe fn from_value(value: *mut crate::ffi::sqlite3_value) -> Self {
213        use crate::ffi;
214        use crate::util::from_raw_parts;
215
216        unsafe {
217            match ffi::sqlite3_value_type(value) {
218                ffi::SQLITE_NULL => ValueRef::Null,
219                ffi::SQLITE_INTEGER => ValueRef::Integer(ffi::sqlite3_value_int64(value)),
220                ffi::SQLITE_FLOAT => ValueRef::Real(ffi::sqlite3_value_double(value)),
221                ffi::SQLITE_TEXT => {
222                    let text = ffi::sqlite3_value_text(value);
223                    let len = ffi::sqlite3_value_bytes(value);
224                    assert!(
225                        !text.is_null(),
226                        "unexpected SQLITE_TEXT value type with NULL data"
227                    );
228                    let s = from_raw_parts(text.cast::<u8>(), len);
229                    ValueRef::Text(s)
230                }
231                ffi::SQLITE_BLOB => {
232                    let (blob, len) = (
233                        ffi::sqlite3_value_blob(value),
234                        ffi::sqlite3_value_bytes(value),
235                    );
236
237                    assert!(
238                        len >= 0,
239                        "unexpected negative return from sqlite3_value_bytes"
240                    );
241                    if len > 0 {
242                        assert!(
243                            !blob.is_null(),
244                            "unexpected SQLITE_BLOB value type with NULL data"
245                        );
246                        ValueRef::Blob(from_raw_parts(blob.cast::<u8>(), len))
247                    } else {
248                        // The return value from sqlite3_value_blob() for a zero-length BLOB
249                        // is a NULL pointer.
250                        ValueRef::Blob(&[])
251                    }
252                }
253                _ => unreachable!("sqlite3_value_type returned invalid value"),
254            }
255        }
256    }
257
258    // TODO sqlite3_value_frombind // 3.28.0
259}
260
261#[cfg(test)]
262mod test {
263    #[cfg(all(target_family = "wasm", target_os = "unknown"))]
264    use wasm_bindgen_test::wasm_bindgen_test as test;
265
266    use super::ValueRef;
267    use crate::types::FromSqlResult;
268
269    #[test]
270    fn as_i64() -> FromSqlResult<()> {
271        assert!(ValueRef::Real(1.0).as_i64().is_err());
272        assert_eq!(ValueRef::Integer(1).as_i64(), Ok(1));
273        Ok(())
274    }
275    #[test]
276    fn as_i64_or_null() -> FromSqlResult<()> {
277        assert_eq!(ValueRef::Null.as_i64_or_null(), Ok(None));
278        assert!(ValueRef::Real(1.0).as_i64_or_null().is_err());
279        assert_eq!(ValueRef::Integer(1).as_i64_or_null(), Ok(Some(1)));
280        Ok(())
281    }
282    #[test]
283    fn as_f64() -> FromSqlResult<()> {
284        assert!(ValueRef::Integer(1).as_f64().is_err());
285        assert_eq!(ValueRef::Real(1.0).as_f64(), Ok(1.0));
286        Ok(())
287    }
288    #[test]
289    fn as_f64_or_null() -> FromSqlResult<()> {
290        assert_eq!(ValueRef::Null.as_f64_or_null(), Ok(None));
291        assert!(ValueRef::Integer(1).as_f64_or_null().is_err());
292        assert_eq!(ValueRef::Real(1.0).as_f64_or_null(), Ok(Some(1.0)));
293        Ok(())
294    }
295    #[test]
296    fn as_str() -> FromSqlResult<()> {
297        assert!(ValueRef::Null.as_str().is_err());
298        assert_eq!(ValueRef::Text(b"").as_str(), Ok(""));
299        Ok(())
300    }
301    #[test]
302    fn as_str_or_null() -> FromSqlResult<()> {
303        assert_eq!(ValueRef::Null.as_str_or_null(), Ok(None));
304        assert!(ValueRef::Integer(1).as_str_or_null().is_err());
305        assert_eq!(ValueRef::Text(b"").as_str_or_null(), Ok(Some("")));
306        Ok(())
307    }
308    #[test]
309    fn as_blob() -> FromSqlResult<()> {
310        assert!(ValueRef::Null.as_blob().is_err());
311        assert_eq!(ValueRef::Blob(b"").as_blob(), Ok(&b""[..]));
312        Ok(())
313    }
314    #[test]
315    fn as_blob_or_null() -> FromSqlResult<()> {
316        assert_eq!(ValueRef::Null.as_blob_or_null(), Ok(None));
317        assert!(ValueRef::Integer(1).as_blob_or_null().is_err());
318        assert_eq!(ValueRef::Blob(b"").as_blob_or_null(), Ok(Some(&b""[..])));
319        Ok(())
320    }
321    #[test]
322    fn as_bytes() -> FromSqlResult<()> {
323        assert!(ValueRef::Null.as_bytes().is_err());
324        assert_eq!(ValueRef::Blob(b"").as_bytes(), Ok(&b""[..]));
325        Ok(())
326    }
327    #[test]
328    fn as_bytes_or_null() -> FromSqlResult<()> {
329        assert_eq!(ValueRef::Null.as_bytes_or_null(), Ok(None));
330        assert!(ValueRef::Integer(1).as_bytes_or_null().is_err());
331        assert_eq!(ValueRef::Blob(b"").as_bytes_or_null(), Ok(Some(&b""[..])));
332        Ok(())
333    }
334    #[test]
335    fn from_value() {
336        use crate::types::Value;
337        assert_eq!(
338            ValueRef::from(&Value::Text("".to_owned())),
339            ValueRef::Text(b"")
340        );
341        assert_eq!(ValueRef::from(&Value::Blob(vec![])), ValueRef::Blob(b""));
342    }
343    #[test]
344    fn from_option() {
345        assert_eq!(ValueRef::from(None as Option<&str>), ValueRef::Null);
346        assert_eq!(ValueRef::from(Some("")), ValueRef::Text(b""));
347    }
348}