Skip to main content

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 as ffi;
7
8use core::cell::Ref;
9use core::ptr::NonNull;
10use core::{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    // An optional storage for a string that is
37    // created from an non-utf8 blob value via `read_str`
38    // This field mostly exists for as we cannot
39    // return an error in that case as the API is
40    // stable and doesn't return a `Result`. We instead
41    // use `String::from_utf8_lossy` there and need
42    // to store the potential owned result here
43    string_ref: Option<alloc::boxed::Box<str>>,
44}
45
46#[derive(#[automatically_derived]
impl ::core::fmt::Debug for OwnedSqliteValue {
    #[inline]
    fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
        ::core::fmt::Formatter::debug_struct_field1_finish(f,
            "OwnedSqliteValue", "value", &&self.value)
    }
}Debug)]
47#[repr(transparent)]
48pub(super) struct OwnedSqliteValue {
49    pub(super) value: NonNull<ffi::sqlite3_value>,
50}
51
52impl Drop for OwnedSqliteValue {
53    fn drop(&mut self) {
54        unsafe { ffi::sqlite3_value_free(self.value.as_ptr()) }
55    }
56}
57
58// Unsafe Send impl safe since sqlite3_value is built with sqlite3_value_dup
59// see https://www.sqlite.org/c3ref/value.html
60unsafe impl Send for OwnedSqliteValue {}
61
62impl<'row, 'stmt, 'query> SqliteValue<'row, 'stmt, 'query> {
63    pub(super) fn new(
64        row: Ref<'row, PrivateSqliteRow<'stmt, 'query>>,
65        col_idx: usize,
66    ) -> Option<SqliteValue<'row, 'stmt, 'query>> {
67        let value = match &*row {
68            PrivateSqliteRow::Direct(stmt) => stmt.column_value(
69                col_idx
70                    .try_into()
71                    .expect("Diesel expects to run at least on a 32 bit platform"),
72            )?,
73            PrivateSqliteRow::Duplicated { values, .. } => {
74                values.get(col_idx).and_then(|v| v.as_ref())?.value
75            }
76        };
77
78        let ret = Self {
79            _row: Some(row),
80            value,
81            string_ref: None,
82        };
83        if ret.value_type().is_none() {
84            None
85        } else {
86            Some(ret)
87        }
88    }
89
90    pub(super) fn from_owned_row(
91        row: &'row OwnedSqliteRow,
92        col_idx: usize,
93    ) -> Option<SqliteValue<'row, 'stmt, 'query>> {
94        let value = row.values.get(col_idx).and_then(|v| v.as_ref())?.value;
95        let ret = Self {
96            _row: None,
97            value,
98            string_ref: None,
99        };
100        if ret.value_type().is_none() {
101            None
102        } else {
103            Some(ret)
104        }
105    }
106
107    pub(super) fn from_function_row(
108        row: &'row [Option<OwnedSqliteValue>],
109        col_idx: usize,
110    ) -> Option<SqliteValue<'row, 'stmt, 'query>> {
111        let value = row.get(col_idx).and_then(|v| v.as_ref())?.value;
112        let ret = Self {
113            _row: None,
114            value,
115            string_ref: None,
116        };
117        if ret.value_type().is_none() {
118            None
119        } else {
120            Some(ret)
121        }
122    }
123
124    pub(crate) fn as_byte_string(&mut self) -> &'row [u8] {
125        unsafe {
126            // https://sqlite.org/c3ref/value_blob.html
127            // Please pay particular attention to the fact that the pointer returned
128            // from sqlite3_value_blob(), sqlite3_value_text(), or sqlite3_value_text16()
129            // can be invalidated by a subsequent call to sqlite3_value_bytes(), sqlite3_value_bytes16(),
130            // sqlite3_value_text(), or sqlite3_value_text16().
131            let len = ffi::sqlite3_value_bytes(self.value.as_ptr());
132            let ptr = ffi::sqlite3_value_text(self.value.as_ptr());
133            slice::from_raw_parts(
134                ptr,
135                len.try_into()
136                    .expect("Diesel expects to run at least on a 32 bit platform"),
137            )
138        }
139    }
140
141    pub(crate) fn as_utf8_str(&mut self) -> Result<&'row str, core::str::Utf8Error> {
142        str::from_utf8(self.as_byte_string())
143    }
144
145    pub(crate) fn parse_string<'value, R>(&'value mut self, f: impl FnOnce(&'value str) -> R) -> R {
146        let bytes = self.as_byte_string();
147        // For blobs this might return non-utf values
148        //
149        // The sqlite documentation there seems to be at least inaccurate
150        let s = match alloc::string::String::from_utf8_lossy(bytes) {
151            alloc::borrow::Cow::Borrowed(s) => s,
152            alloc::borrow::Cow::Owned(b) => {
153                self.string_ref = Some(b.into_boxed_str());
154                self.string_ref
155                    .as_deref()
156                    .expect("We initialised it literally above")
157            }
158        };
159        f(s)
160    }
161
162    /// Read the underlying value as string
163    ///
164    /// If the underlying value is not a string sqlite will convert it
165    /// into a string and return that value instead.
166    ///
167    /// Use the [`value_type()`](Self::value_type()) function to determine the actual
168    /// type of the value.
169    ///
170    /// See <https://www.sqlite.org/c3ref/value_blob.html> for details
171    pub fn read_text(&mut self) -> &str {
172        self.parse_string(|s| s)
173    }
174
175    /// Read the underlying value as blob
176    ///
177    /// If the underlying value is not a blob sqlite will convert it
178    /// into a blob and return that value instead.
179    ///
180    /// Use the [`value_type()`](Self::value_type()) function to determine the actual
181    /// type of the value.
182    ///
183    /// See <https://www.sqlite.org/c3ref/value_blob.html> for details
184    pub fn read_blob(&mut self) -> &'row [u8] {
185        unsafe {
186            // https://sqlite.org/c3ref/value_blob.html
187            // Please pay particular attention to the fact that the pointer returned
188            // from sqlite3_value_blob(), sqlite3_value_text(), or sqlite3_value_text16()
189            // can be invalidated by a subsequent call to sqlite3_value_bytes(), sqlite3_value_bytes16(),
190            // sqlite3_value_text(), or sqlite3_value_text16().
191            let len = ffi::sqlite3_value_bytes(self.value.as_ptr());
192            let ptr = ffi::sqlite3_value_blob(self.value.as_ptr());
193            if len == 0 {
194                // rusts std-lib has an debug_assert that prevents creating
195                // slices without elements from a pointer
196                &[]
197            } else {
198                slice::from_raw_parts(
199                    ptr as *const u8,
200                    len.try_into()
201                        .expect("Diesel expects to run at least on a 32 bit platform"),
202                )
203            }
204        }
205    }
206
207    /// Read the underlying value as 32 bit integer
208    ///
209    /// If the underlying value is not an integer sqlite will convert it
210    /// into an integer and return that value instead.
211    ///
212    /// Use the [`value_type()`](Self::value_type()) function to determine the actual
213    /// type of the value.
214    ///
215    /// See <https://www.sqlite.org/c3ref/value_blob.html> for details
216    pub fn read_integer(&mut self) -> i32 {
217        unsafe { ffi::sqlite3_value_int(self.value.as_ptr()) }
218    }
219
220    /// Read the underlying value as 64 bit integer
221    ///
222    /// If the underlying value is not a string sqlite will convert it
223    /// into a string and return that value instead.
224    ///
225    /// Use the [`value_type()`](Self::value_type()) function to determine the actual
226    /// type of the value.
227    ///
228    /// See <https://www.sqlite.org/c3ref/value_blob.html> for details
229    pub fn read_long(&mut self) -> i64 {
230        unsafe { ffi::sqlite3_value_int64(self.value.as_ptr()) }
231    }
232
233    /// Read the underlying value as 64 bit float
234    ///
235    /// If the underlying value is not a string sqlite will convert it
236    /// into a string and return that value instead.
237    ///
238    /// Use the [`value_type()`](Self::value_type()) function to determine the actual
239    /// type of the value.
240    ///
241    /// See <https://www.sqlite.org/c3ref/value_blob.html> for details
242    pub fn read_double(&mut self) -> f64 {
243        unsafe { ffi::sqlite3_value_double(self.value.as_ptr()) }
244    }
245
246    /// Get the type of the value as returned by sqlite
247    pub fn value_type(&self) -> Option<SqliteType> {
248        let tpe = unsafe { ffi::sqlite3_value_type(self.value.as_ptr()) };
249        match tpe {
250            ffi::SQLITE_TEXT => Some(SqliteType::Text),
251            ffi::SQLITE_INTEGER => Some(SqliteType::Long),
252            ffi::SQLITE_FLOAT => Some(SqliteType::Double),
253            ffi::SQLITE_BLOB => Some(SqliteType::Binary),
254            ffi::SQLITE_NULL => None,
255            _ => {
    ::core::panicking::panic_fmt(format_args!("internal error: entered unreachable code: {0}",
            format_args!("Sqlite\'s documentation state that this case ({0}) is not reachable. If you ever see this error message please open an issue at https://github.com/diesel-rs/diesel.",
                tpe)));
}unreachable!(
256                "Sqlite's documentation state that this case ({}) is not reachable. \
257                 If you ever see this error message please open an issue at \
258                 https://github.com/diesel-rs/diesel.",
259                tpe
260            ),
261        }
262    }
263}
264
265impl OwnedSqliteValue {
266    pub(super) fn copy_from_ptr(ptr: NonNull<ffi::sqlite3_value>) -> Option<OwnedSqliteValue> {
267        let tpe = unsafe { ffi::sqlite3_value_type(ptr.as_ptr()) };
268        if ffi::SQLITE_NULL == tpe {
269            return None;
270        }
271        let value = unsafe { ffi::sqlite3_value_dup(ptr.as_ptr()) };
272        Some(Self {
273            value: NonNull::new(value)?,
274        })
275    }
276
277    pub(super) fn duplicate(&self) -> OwnedSqliteValue {
278        // self.value is a `NonNull` ptr so this cannot be null
279        let value = unsafe { ffi::sqlite3_value_dup(self.value.as_ptr()) };
280        let value = NonNull::new(value).expect(
281            "Sqlite documentation states this returns only null if value is null \
282                 or OOM. If you ever see this panic message please open an issue at \
283                 https://github.com/diesel-rs/diesel.",
284        );
285        OwnedSqliteValue { value }
286    }
287}
288
289#[cfg(test)]
290mod tests {
291    use crate::connection::{LoadConnection, SimpleConnection};
292    use crate::row::Field;
293    use crate::row::Row;
294    use crate::sql_types::{Blob, Double, Int4, Text};
295    use crate::*;
296
297    #[expect(clippy::approx_constant)] // we really want to use 3.14
298    #[diesel_test_helper::test]
299    fn can_convert_all_values() {
300        let mut conn = SqliteConnection::establish(":memory:").unwrap();
301
302        conn.batch_execute("CREATE TABLE tests(int INTEGER, text TEXT, blob BLOB, float FLOAT)")
303            .unwrap();
304
305        diesel::sql_query("INSERT INTO tests(int, text, blob, float) VALUES(?, ?, ?, ?)")
306            .bind::<Int4, _>(42)
307            .bind::<Text, _>("foo")
308            .bind::<Blob, _>([0xFF_u8, 0xFE, 0xFD])
309            .bind::<Double, _>(3.14)
310            .execute(&mut conn)
311            .unwrap();
312
313        let mut res = conn
314            .load(diesel::sql_query(
315                "SELECT int, text, blob, float FROM tests",
316            ))
317            .unwrap();
318        let row = res.next().unwrap().unwrap();
319        let int_field = row.get(0).unwrap();
320        let text_field = row.get(1).unwrap();
321        let blob_field = row.get(2).unwrap();
322        let float_field = row.get(3).unwrap();
323
324        let mut int_value = int_field.value().unwrap();
325        assert_eq!(int_value.read_integer(), 42);
326        let mut int_value = int_field.value().unwrap();
327        assert_eq!(int_value.read_long(), 42);
328        let mut int_value = int_field.value().unwrap();
329        assert_eq!(int_value.read_double(), 42.0);
330        let mut int_value = int_field.value().unwrap();
331        assert_eq!(int_value.read_text(), "42");
332        let mut int_value = int_field.value().unwrap();
333        assert_eq!(int_value.read_blob(), b"42");
334
335        let mut text_value = text_field.value().unwrap();
336        assert_eq!(text_value.read_integer(), 0);
337        let mut text_value = text_field.value().unwrap();
338        assert_eq!(text_value.read_long(), 0);
339        let mut text_value = text_field.value().unwrap();
340        assert_eq!(text_value.read_double(), 0.0);
341        let mut text_value = text_field.value().unwrap();
342        assert_eq!(text_value.read_text(), "foo");
343        let mut text_value = text_field.value().unwrap();
344        assert_eq!(text_value.read_blob(), b"foo");
345
346        let mut blob_value = blob_field.value().unwrap();
347        assert_eq!(blob_value.read_integer(), 0);
348        let mut blob_value = blob_field.value().unwrap();
349        assert_eq!(blob_value.read_long(), 0);
350        let mut blob_value = blob_field.value().unwrap();
351        assert_eq!(blob_value.read_double(), 0.0);
352        let mut blob_value = blob_field.value().unwrap();
353        assert_eq!(blob_value.read_text(), "\u{fffd}\u{fffd}\u{fffd}"); // ���
354        let mut blob_value = blob_field.value().unwrap();
355        assert_eq!(blob_value.read_blob(), [0xFF, 0xFE, 0xFD]);
356
357        let mut float_value = float_field.value().unwrap();
358        assert_eq!(float_value.read_integer(), 3);
359        let mut float_value = float_field.value().unwrap();
360        assert_eq!(float_value.read_long(), 3);
361        let mut float_value = float_field.value().unwrap();
362        assert_eq!(float_value.read_double(), 3.14);
363        let mut float_value = float_field.value().unwrap();
364        assert_eq!(float_value.read_text(), "3.14");
365        let mut float_value = float_field.value().unwrap();
366        assert_eq!(float_value.read_blob(), b"3.14");
367    }
368}