diesel/sqlite/connection/
sqlite_value.rs

1#![allow(unsafe_code)] // ffi calls
2extern crate libsqlite3_sys as ffi;
3
4use std::cell::Ref;
5use std::ptr::NonNull;
6use std::{slice, str};
7
8use crate::sqlite::SqliteType;
9
10use super::owned_row::OwnedSqliteRow;
11use super::row::PrivateSqliteRow;
12
13/// Raw sqlite value as received from the database
14///
15/// Use the `read_*` functions to access the actual
16/// value or use existing `FromSql` implementations
17/// to convert this into rust values
18#[allow(missing_debug_implementations, missing_copy_implementations)]
19pub struct SqliteValue<'row, 'stmt, 'query> {
20    // This field exists to ensure that nobody
21    // can modify the underlying row while we are
22    // holding a reference to some row value here
23    _row: Option<Ref<'row, PrivateSqliteRow<'stmt, 'query>>>,
24    // we extract the raw value pointer as part of the constructor
25    // to safe the match statements for each method
26    // According to benchmarks this leads to a ~20-30% speedup
27    //
28    // This is sound as long as nobody calls `stmt.step()`
29    // while holding this value. We ensure this by including
30    // a reference to the row above.
31    value: NonNull<ffi::sqlite3_value>,
32}
33
34#[derive(Debug)]
35#[repr(transparent)]
36pub(super) struct OwnedSqliteValue {
37    pub(super) value: NonNull<ffi::sqlite3_value>,
38}
39
40impl Drop for OwnedSqliteValue {
41    fn drop(&mut self) {
42        unsafe { ffi::sqlite3_value_free(self.value.as_ptr()) }
43    }
44}
45
46// Unsafe Send impl safe since sqlite3_value is built with sqlite3_value_dup
47// see https://www.sqlite.org/c3ref/value.html
48unsafe impl Send for OwnedSqliteValue {}
49
50impl<'row, 'stmt, 'query> SqliteValue<'row, 'stmt, 'query> {
51    pub(super) fn new(
52        row: Ref<'row, PrivateSqliteRow<'stmt, 'query>>,
53        col_idx: usize,
54    ) -> Option<SqliteValue<'row, 'stmt, 'query>> {
55        let value = match &*row {
56            PrivateSqliteRow::Direct(stmt) => stmt.column_value(
57                col_idx
58                    .try_into()
59                    .expect("Diesel expects to run at least on a 32 bit platform"),
60            )?,
61            PrivateSqliteRow::Duplicated { values, .. } => {
62                values.get(col_idx).and_then(|v| v.as_ref())?.value
63            }
64        };
65
66        let ret = Self {
67            _row: Some(row),
68            value,
69        };
70        if ret.value_type().is_none() {
71            None
72        } else {
73            Some(ret)
74        }
75    }
76
77    pub(super) fn from_owned_row(
78        row: &'row OwnedSqliteRow,
79        col_idx: usize,
80    ) -> Option<SqliteValue<'row, 'stmt, 'query>> {
81        let value = row.values.get(col_idx).and_then(|v| v.as_ref())?.value;
82        let ret = Self { _row: None, value };
83        if ret.value_type().is_none() {
84            None
85        } else {
86            Some(ret)
87        }
88    }
89
90    pub(crate) fn parse_string<'value, R>(&'value mut self, f: impl FnOnce(&'value str) -> R) -> R {
91        let s = unsafe {
92            let ptr = ffi::sqlite3_value_text(self.value.as_ptr());
93            let len = ffi::sqlite3_value_bytes(self.value.as_ptr());
94            let bytes = slice::from_raw_parts(
95                ptr,
96                len.try_into()
97                    .expect("Diesel expects to run at least on a 32 bit platform"),
98            );
99            // The string is guaranteed to be utf8 according to
100            // https://www.sqlite.org/c3ref/value_blob.html
101            str::from_utf8_unchecked(bytes)
102        };
103        f(s)
104    }
105
106    /// Read the underlying value as string
107    ///
108    /// If the underlying value is not a string sqlite will convert it
109    /// into a string and return that value instead.
110    ///
111    /// Use the [`value_type()`](Self::value_type()) function to determine the actual
112    /// type of the value.
113    ///
114    /// See <https://www.sqlite.org/c3ref/value_blob.html> for details
115    pub fn read_text(&mut self) -> &str {
116        self.parse_string(|s| s)
117    }
118
119    /// Read the underlying value as blob
120    ///
121    /// If the underlying value is not a blob sqlite will convert it
122    /// into a blob and return that value instead.
123    ///
124    /// Use the [`value_type()`](Self::value_type()) function to determine the actual
125    /// type of the value.
126    ///
127    /// See <https://www.sqlite.org/c3ref/value_blob.html> for details
128    pub fn read_blob(&mut self) -> &[u8] {
129        unsafe {
130            let ptr = ffi::sqlite3_value_blob(self.value.as_ptr());
131            let len = ffi::sqlite3_value_bytes(self.value.as_ptr());
132            if len == 0 {
133                // rusts std-lib has an debug_assert that prevents creating
134                // slices without elements from a pointer
135                &[]
136            } else {
137                slice::from_raw_parts(
138                    ptr as *const u8,
139                    len.try_into()
140                        .expect("Diesel expects to run at least on a 32 bit platform"),
141                )
142            }
143        }
144    }
145
146    /// Read the underlying value as 32 bit integer
147    ///
148    /// If the underlying value is not an integer sqlite will convert it
149    /// into an integer and return that value instead.
150    ///
151    /// Use the [`value_type()`](Self::value_type()) function to determine the actual
152    /// type of the value.
153    ///
154    /// See <https://www.sqlite.org/c3ref/value_blob.html> for details
155    pub fn read_integer(&mut self) -> i32 {
156        unsafe { ffi::sqlite3_value_int(self.value.as_ptr()) }
157    }
158
159    /// Read the underlying value as 64 bit integer
160    ///
161    /// If the underlying value is not a string sqlite will convert it
162    /// into a string and return that value instead.
163    ///
164    /// Use the [`value_type()`](Self::value_type()) function to determine the actual
165    /// type of the value.
166    ///
167    /// See <https://www.sqlite.org/c3ref/value_blob.html> for details
168    pub fn read_long(&mut self) -> i64 {
169        unsafe { ffi::sqlite3_value_int64(self.value.as_ptr()) }
170    }
171
172    /// Read the underlying value as 64 bit float
173    ///
174    /// If the underlying value is not a string sqlite will convert it
175    /// into a string and return that value instead.
176    ///
177    /// Use the [`value_type()`](Self::value_type()) function to determine the actual
178    /// type of the value.
179    ///
180    /// See <https://www.sqlite.org/c3ref/value_blob.html> for details
181    pub fn read_double(&mut self) -> f64 {
182        unsafe { ffi::sqlite3_value_double(self.value.as_ptr()) }
183    }
184
185    /// Get the type of the value as returned by sqlite
186    pub fn value_type(&self) -> Option<SqliteType> {
187        let tpe = unsafe { ffi::sqlite3_value_type(self.value.as_ptr()) };
188        match tpe {
189            ffi::SQLITE_TEXT => Some(SqliteType::Text),
190            ffi::SQLITE_INTEGER => Some(SqliteType::Long),
191            ffi::SQLITE_FLOAT => Some(SqliteType::Double),
192            ffi::SQLITE_BLOB => Some(SqliteType::Binary),
193            ffi::SQLITE_NULL => None,
194            _ => unreachable!(
195                "Sqlite's documentation state that this case ({}) is not reachable. \
196                 If you ever see this error message please open an issue at \
197                 https://github.com/diesel-rs/diesel.",
198                tpe
199            ),
200        }
201    }
202}
203
204impl OwnedSqliteValue {
205    pub(super) fn copy_from_ptr(ptr: NonNull<ffi::sqlite3_value>) -> Option<OwnedSqliteValue> {
206        let tpe = unsafe { ffi::sqlite3_value_type(ptr.as_ptr()) };
207        if ffi::SQLITE_NULL == tpe {
208            return None;
209        }
210        let value = unsafe { ffi::sqlite3_value_dup(ptr.as_ptr()) };
211        Some(Self {
212            value: NonNull::new(value)?,
213        })
214    }
215
216    pub(super) fn duplicate(&self) -> OwnedSqliteValue {
217        // self.value is a `NonNull` ptr so this cannot be null
218        let value = unsafe { ffi::sqlite3_value_dup(self.value.as_ptr()) };
219        let value = NonNull::new(value).expect(
220            "Sqlite documentation states this returns only null if value is null \
221                 or OOM. If you ever see this panic message please open an issue at \
222                 https://github.com/diesel-rs/diesel.",
223        );
224        OwnedSqliteValue { value }
225    }
226}
227
228#[cfg(test)]
229mod tests {
230    use crate::connection::{LoadConnection, SimpleConnection};
231    use crate::row::Field;
232    use crate::row::Row;
233    use crate::sql_types::{Blob, Double, Int4, Text};
234    use crate::*;
235
236    #[expect(clippy::approx_constant)] // we really want to use 3.14
237    #[test]
238    fn can_convert_all_values() {
239        let mut conn = SqliteConnection::establish(":memory:").unwrap();
240
241        conn.batch_execute("CREATE TABLE tests(int INTEGER, text TEXT, blob BLOB, float FLOAT)")
242            .unwrap();
243
244        diesel::sql_query("INSERT INTO tests(int, text, blob, float) VALUES(?, ?, ?, ?)")
245            .bind::<Int4, _>(42)
246            .bind::<Text, _>("foo")
247            .bind::<Blob, _>(b"foo")
248            .bind::<Double, _>(3.14)
249            .execute(&mut conn)
250            .unwrap();
251
252        let mut res = conn
253            .load(diesel::sql_query(
254                "SELECT int, text, blob, float FROM tests",
255            ))
256            .unwrap();
257        let row = res.next().unwrap().unwrap();
258        let int_field = row.get(0).unwrap();
259        let text_field = row.get(1).unwrap();
260        let blob_field = row.get(2).unwrap();
261        let float_field = row.get(3).unwrap();
262
263        let mut int_value = int_field.value().unwrap();
264        assert_eq!(int_value.read_integer(), 42);
265        let mut int_value = int_field.value().unwrap();
266        assert_eq!(int_value.read_long(), 42);
267        let mut int_value = int_field.value().unwrap();
268        assert_eq!(int_value.read_double(), 42.0);
269        let mut int_value = int_field.value().unwrap();
270        assert_eq!(int_value.read_text(), "42");
271        let mut int_value = int_field.value().unwrap();
272        assert_eq!(int_value.read_blob(), b"42");
273
274        let mut text_value = text_field.value().unwrap();
275        assert_eq!(text_value.read_integer(), 0);
276        let mut text_value = text_field.value().unwrap();
277        assert_eq!(text_value.read_long(), 0);
278        let mut text_value = text_field.value().unwrap();
279        assert_eq!(text_value.read_double(), 0.0);
280        let mut text_value = text_field.value().unwrap();
281        assert_eq!(text_value.read_text(), "foo");
282        let mut text_value = text_field.value().unwrap();
283        assert_eq!(text_value.read_blob(), b"foo");
284
285        let mut blob_value = blob_field.value().unwrap();
286        assert_eq!(blob_value.read_integer(), 0);
287        let mut blob_value = blob_field.value().unwrap();
288        assert_eq!(blob_value.read_long(), 0);
289        let mut blob_value = blob_field.value().unwrap();
290        assert_eq!(blob_value.read_double(), 0.0);
291        let mut blob_value = blob_field.value().unwrap();
292        assert_eq!(blob_value.read_text(), "foo");
293        let mut blob_value = blob_field.value().unwrap();
294        assert_eq!(blob_value.read_blob(), b"foo");
295
296        let mut float_value = float_field.value().unwrap();
297        assert_eq!(float_value.read_integer(), 3);
298        let mut float_value = float_field.value().unwrap();
299        assert_eq!(float_value.read_long(), 3);
300        let mut float_value = float_field.value().unwrap();
301        assert_eq!(float_value.read_double(), 3.14);
302        let mut float_value = float_field.value().unwrap();
303        assert_eq!(float_value.read_text(), "3.14");
304        let mut float_value = float_field.value().unwrap();
305        assert_eq!(float_value.read_blob(), b"3.14");
306    }
307}