diesel/sqlite/connection/
sqlite_value.rs

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