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::result::QueryResult;
13use crate::sqlite::SqliteType;
14
15use super::owned_row::OwnedSqliteRow;
16use super::row::PrivateSqliteRow;
17
18/// Raw sqlite value as received from the database
19///
20/// Use the `read_*` functions to access the actual
21/// value or use existing `FromSql` implementations
22/// to convert this into rust values
23#[allow(missing_debug_implementations, missing_copy_implementations)]
24pub struct SqliteValue<'row, 'stmt, 'query> {
25    // This field exists to ensure that nobody can modify the underlying row
26    // while we are holding a reference to some row value here, and to reach
27    // the connection a value is still attached to
28    owner: ValueOwner<'row, 'stmt, 'query>,
29    // we extract the raw value pointer as part of the constructor
30    // to safe the match statements for each method
31    // According to benchmarks this leads to a ~20-30% speedup
32    //
33    // This is sound as long as nobody calls `stmt.step()`
34    // while holding this value. We ensure this by including
35    // a reference to the row above.
36    value: NonNull<ffi::sqlite3_value>,
37    // An optional storage for a string that is
38    // created from an non-utf8 blob value via `read_str`
39    // This field mostly exists for as we cannot
40    // return an error in that case as the API is
41    // stable and doesn't return a `Result`. We instead
42    // use `String::from_utf8_lossy` there and need
43    // to store the potential owned result here
44    string_ref: Option<alloc::boxed::Box<str>>,
45    // The type the value declared before any read converted it, as
46    // https://www.sqlite.org/c3ref/value_blob.html requires asking first
47    initial_type: SqliteType,
48    // A private copy of the value, used by reads that would otherwise convert the
49    // shared value in place and free the buffer another handle points at
50    converted: Option<OwnedSqliteValue>,
51}
52
53enum ValueOwner<'row, 'stmt, 'query> {
54    // Only a direct row is still attached to its connection, a duplicated one
55    // holds values from `sqlite3_value_dup`
56    Row(Ref<'row, PrivateSqliteRow<'stmt, 'query>>),
57    // A value outside a row: a function argument carries its connection, a
58    // duplicated value has none
59    NonRow(Option<NonNull<ffi::sqlite3>>),
60}
61
62/// A form a value read hands out, which SQLite stores by converting the value.
63#[derive(#[automatically_derived]
impl ::core::marker::Copy for Representation { }Copy, #[automatically_derived]
#[doc(hidden)]
unsafe impl ::core::clone::TrivialClone for Representation { }
#[automatically_derived]
impl ::core::clone::Clone for Representation {
    #[inline]
    fn clone(&self) -> Self { *self }
}Clone)]
64enum Representation {
65    Text,
66    Blob,
67}
68
69impl Representation {
70    fn target(self) -> &'static str {
71        match self {
72            Self::Text => "text",
73            Self::Blob => "a blob",
74        }
75    }
76}
77
78#[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)]
79#[repr(transparent)]
80pub(super) struct OwnedSqliteValue {
81    pub(super) value: NonNull<ffi::sqlite3_value>,
82}
83
84impl Drop for OwnedSqliteValue {
85    fn drop(&mut self) {
86        unsafe { ffi::sqlite3_value_free(self.value.as_ptr()) }
87    }
88}
89
90// Unsafe Send impl safe since sqlite3_value is built with sqlite3_value_dup
91// see https://www.sqlite.org/c3ref/value.html
92unsafe impl Send for OwnedSqliteValue {}
93
94#[cold]
95fn allocation_failed(out_of_memory: bool, target: &str) -> ! {
96    let reason = if out_of_memory {
97        "ran out of memory"
98    } else {
99        "failed to allocate memory"
100    };
101    {
    ::core::panicking::panic_fmt(format_args!("SQLite {0} while reading a value as {1}",
            reason, target));
}panic!("SQLite {reason} while reading a value as {target}")
102}
103
104#[cold]
105fn duplication_failed() -> crate::result::Error {
106    crate::result::Error::DeserializationError(
107        "SQLite failed to allocate a duplicated value".into(),
108    )
109}
110
111impl<'row, 'stmt, 'query> SqliteValue<'row, 'stmt, 'query> {
112    pub(super) fn new(
113        row: Ref<'row, PrivateSqliteRow<'stmt, 'query>>,
114        col_idx: usize,
115    ) -> Option<SqliteValue<'row, 'stmt, 'query>> {
116        let value = match &*row {
117            PrivateSqliteRow::Direct(stmt) => stmt.column_value(
118                col_idx
119                    .try_into()
120                    .expect("Diesel expects to run at least on a 32 bit platform"),
121            )?,
122            PrivateSqliteRow::Duplicated { values, .. } => {
123                values.get(col_idx).and_then(|v| v.as_ref())?.value
124            }
125        };
126        // SAFETY: the row owns the value and keeps it alive for `'row`.
127        let initial_type = unsafe { value_type_of(value) }?;
128        Some(Self {
129            owner: ValueOwner::Row(row),
130            value,
131            string_ref: None,
132            initial_type,
133            converted: None,
134        })
135    }
136
137    pub(super) fn from_owned_row(
138        row: &'row OwnedSqliteRow,
139        col_idx: usize,
140    ) -> Option<SqliteValue<'row, 'stmt, 'query>> {
141        let value = row.values.get(col_idx).and_then(|v| v.as_ref())?.value;
142        // SAFETY: `row` owns the value and keeps it alive for `'row`.
143        let initial_type = unsafe { value_type_of(value) }?;
144        Some(Self {
145            owner: ValueOwner::NonRow(None),
146            value,
147            string_ref: None,
148            initial_type,
149            converted: None,
150        })
151    }
152
153    pub(super) fn from_function_row(
154        row: &'row [Option<OwnedSqliteValue>],
155        col_idx: usize,
156        connection: NonNull<ffi::sqlite3>,
157    ) -> Option<SqliteValue<'row, 'stmt, 'query>> {
158        let value = row.get(col_idx).and_then(|v| v.as_ref())?.value;
159        // SAFETY: the callback's argument row owns the value for the call.
160        let initial_type = unsafe { value_type_of(value) }?;
161        Some(Self {
162            owner: ValueOwner::NonRow(Some(connection)),
163            value,
164            string_ref: None,
165            initial_type,
166            converted: None,
167        })
168    }
169
170    // Values from `sqlite3_value_dup` are disconnected from the connection, so they
171    // cannot be asked: https://www.sqlite.org/c3ref/value_blob.html
172    fn connection(&self) -> Option<NonNull<ffi::sqlite3>> {
173        match &self.owner {
174            ValueOwner::Row(row) => match &**row {
175                PrivateSqliteRow::Direct(stmt) => NonNull::new(stmt.raw_connection()),
176                PrivateSqliteRow::Duplicated { .. } => None,
177            },
178            ValueOwner::NonRow(connection) => *connection,
179        }
180    }
181
182    /// Reports whether an allocation just failed, which must be asked before any other call.
183    fn reports_out_of_memory(&self) -> bool {
184        let Some(connection) = self.connection() else {
185            return false;
186        };
187        // SAFETY: The owner keeps the connection alive and this call only reads
188        // connection state.
189        unsafe { ffi::sqlite3_errcode(connection.as_ptr()) == ffi::SQLITE_NOMEM }
190    }
191
192    /// Returns the value to read `wanted` from, copying it first when SQLite would
193    /// convert the shared value in place and free a buffer another handle points at.
194    fn value_to_read(&mut self, wanted: Representation) -> NonNull<ffi::sqlite3_value> {
195        // A value gains its other textual form by conversion, while a numeric one
196        // gains it in a fresh buffer that replaces nothing.
197        let converts_in_place = #[allow(non_exhaustive_omitted_patterns)] match (self.initial_type, wanted) {
    (SqliteType::Text, Representation::Blob) |
        (SqliteType::Binary, Representation::Text) => true,
    _ => false,
}matches!(
198            (self.initial_type, wanted),
199            (SqliteType::Text, Representation::Blob) | (SqliteType::Binary, Representation::Text)
200        );
201        if !converts_in_place {
202            return self.value;
203        }
204        if self.converted.is_none() {
205            // SAFETY: `self.owner` keeps `self.value` alive across this call, which
206            // only reads it while copying it into an independently owned value.
207            let copy = unsafe { ffi::sqlite3_value_dup(self.value.as_ptr()) };
208            // The value above is not SQL NULL, so only a failed allocation is null.
209            let Some(copy) = NonNull::new(copy) else {
210                // Ask the connection before any other call to it clears the error code.
211                allocation_failed(self.reports_out_of_memory(), wanted.target());
212            };
213            self.converted = Some(OwnedSqliteValue { value: copy });
214        }
215        self.converted
216            .as_ref()
217            .expect("We initialised it literally above")
218            .value
219    }
220
221    pub(crate) fn as_byte_string(&mut self) -> &[u8] {
222        let value = self.value_to_read(Representation::Text);
223        // SAFETY: `self.owner` keeps the value alive, a copy is owned by `self`, and
224        // the returned slice borrows `self` for as long as either can be converted.
225        unsafe {
226            // Force the UTF-8 conversion now so the length below cannot
227            // trigger one (moving the buffer, or failing as zero length).
228            // https://www.sqlite.org/c3ref/column_blob.html
229            if ffi::sqlite3_value_text(value.as_ptr()).is_null() {
230                // Zero length text has a valid pointer, so this is a failed conversion.
231                allocation_failed(self.reports_out_of_memory(), Representation::Text.target());
232            }
233            let len = ffi::sqlite3_value_bytes(value.as_ptr());
234            // The length above may have invalidated the pointer.
235            let ptr = ffi::sqlite3_value_text(value.as_ptr());
236            if ptr.is_null() {
237                allocation_failed(self.reports_out_of_memory(), Representation::Text.target());
238            }
239            slice::from_raw_parts(
240                ptr,
241                len.try_into()
242                    .expect("Diesel expects to run at least on a 32 bit platform"),
243            )
244        }
245    }
246
247    pub(crate) fn as_utf8_str(&mut self) -> Result<&str, core::str::Utf8Error> {
248        str::from_utf8(self.as_byte_string())
249    }
250
251    pub(crate) fn parse_string<'value, R>(&'value mut self, f: impl FnOnce(&'value str) -> R) -> R {
252        // For blobs this might return non-utf values
253        //
254        // The sqlite documentation there seems to be at least inaccurate
255        if str::from_utf8(self.as_byte_string()).is_err() {
256            // Read again to drop the byte borrow before storing the lossy copy, as
257            // repeated reads of one value do not convert it again.
258            let lossy = alloc::string::String::from_utf8_lossy(self.as_byte_string()).into_owned();
259            self.string_ref = Some(lossy.into_boxed_str());
260            let s = self
261                .string_ref
262                .as_deref()
263                .expect("We initialised it literally above");
264            return f(s);
265        }
266        let s = str::from_utf8(self.as_byte_string()).expect("The bytes are valid utf8 above");
267        f(s)
268    }
269
270    /// Read the underlying value as string
271    ///
272    /// If the underlying value is not a string sqlite will convert it
273    /// into a string and return that value instead.
274    ///
275    /// Use the [`value_type()`](Self::value_type()) function to determine the actual
276    /// type of the value.
277    ///
278    /// See <https://www.sqlite.org/c3ref/value_blob.html> for details
279    ///
280    /// Reading a blob value as text copies it, so slices returned for the same
281    /// field elsewhere stay valid.
282    ///
283    /// # Panics
284    ///
285    /// Panics if SQLite cannot allocate the requested text representation.
286    pub fn read_text(&mut self) -> &str {
287        // TODO: Return Result in Diesel 3 so SQLite allocation failures reach callers.
288        self.parse_string(|s| s)
289    }
290
291    /// Read the underlying value as blob
292    ///
293    /// If the underlying value is not a blob sqlite will convert it
294    /// into a blob and return that value instead.
295    ///
296    /// Use the [`value_type()`](Self::value_type()) function to determine the actual
297    /// type of the value.
298    ///
299    /// See <https://www.sqlite.org/c3ref/value_blob.html> for details
300    ///
301    /// Zero-length text and blob values are read as an empty slice even when
302    /// SQLite reports a null pointer for them.
303    ///
304    /// Reading a text value as a blob copies it, so slices returned for the same
305    /// field elsewhere stay valid.
306    ///
307    /// # Panics
308    ///
309    /// Panics if SQLite cannot allocate the requested blob representation.
310    pub fn read_blob(&mut self) -> &[u8] {
311        // TODO: Return Result in Diesel 3 so SQLite allocation failures reach callers.
312        let value = self.value_to_read(Representation::Blob);
313        // SAFETY: as in `as_byte_string`, the owner keeps the value alive, `self` owns
314        // a copy, and the returned slice borrows `self` for as long as either lives.
315        unsafe {
316            // Preserve a zeroblob's length because failed expansion changes it to SQL NULL.
317            let initial_blob_len = if #[allow(non_exhaustive_omitted_patterns)] match self.initial_type {
    SqliteType::Binary => true,
    _ => false,
}matches!(self.initial_type, SqliteType::Binary) {
318                Some(ffi::sqlite3_value_bytes(value.as_ptr()))
319            } else {
320                None
321            };
322            // Pin the blob form before the length: bytes() must not measure
323            // (or fail at) a text conversion of the value instead.
324            // https://www.sqlite.org/c3ref/column_blob.html
325            if ffi::sqlite3_value_blob(value.as_ptr()).is_null() {
326                // Ask the connection before any other call to it clears the error code.
327                let out_of_memory = self.reports_out_of_memory();
328                let len = ffi::sqlite3_value_bytes(value.as_ptr());
329                if !out_of_memory
330                    && ((#[allow(non_exhaustive_omitted_patterns)] match self.initial_type {
    SqliteType::Text => true,
    _ => false,
}matches!(self.initial_type, SqliteType::Text) && len == 0)
331                        || initial_blob_len == Some(0))
332                {
333                    return &[];
334                }
335                allocation_failed(out_of_memory, Representation::Blob.target());
336            }
337            let len = ffi::sqlite3_value_bytes(value.as_ptr());
338            // The length above may have invalidated the pointer.
339            let ptr = ffi::sqlite3_value_blob(value.as_ptr());
340            if ptr.is_null() {
341                allocation_failed(self.reports_out_of_memory(), Representation::Blob.target());
342            }
343            slice::from_raw_parts(
344                ptr as *const u8,
345                len.try_into()
346                    .expect("Diesel expects to run at least on a 32 bit platform"),
347            )
348        }
349    }
350
351    /// Read the underlying value as 32 bit integer
352    ///
353    /// If the underlying value is not an integer sqlite will convert it
354    /// into an integer and return that value instead.
355    ///
356    /// Use the [`value_type()`](Self::value_type()) function to determine the actual
357    /// type of the value.
358    ///
359    /// See <https://www.sqlite.org/c3ref/value_blob.html> for details
360    pub fn read_integer(&mut self) -> i32 {
361        unsafe { ffi::sqlite3_value_int(self.value.as_ptr()) }
362    }
363
364    /// Read the underlying value as 64 bit integer
365    ///
366    /// If the underlying value is not a string sqlite will convert it
367    /// into a string and return that value instead.
368    ///
369    /// Use the [`value_type()`](Self::value_type()) function to determine the actual
370    /// type of the value.
371    ///
372    /// See <https://www.sqlite.org/c3ref/value_blob.html> for details
373    pub fn read_long(&mut self) -> i64 {
374        unsafe { ffi::sqlite3_value_int64(self.value.as_ptr()) }
375    }
376
377    /// Read the underlying value as 64 bit float
378    ///
379    /// If the underlying value is not a string sqlite will convert it
380    /// into a string and return that value instead.
381    ///
382    /// Use the [`value_type()`](Self::value_type()) function to determine the actual
383    /// type of the value.
384    ///
385    /// See <https://www.sqlite.org/c3ref/value_blob.html> for details
386    pub fn read_double(&mut self) -> f64 {
387        unsafe { ffi::sqlite3_value_double(self.value.as_ptr()) }
388    }
389
390    /// Get the type of the value as returned by sqlite
391    pub fn value_type(&self) -> Option<SqliteType> {
392        // SAFETY: `self.owner` keeps the value alive and this call only inspects it.
393        unsafe { value_type_of(self.value) }
394    }
395}
396
397/// Reads a value's type, which SQLite reports as SQL `NULL` for a failed conversion.
398///
399/// # Safety
400///
401/// `value` must point to a live `sqlite3_value`.
402unsafe fn value_type_of(value: NonNull<ffi::sqlite3_value>) -> Option<SqliteType> {
403    // SAFETY: the caller guarantees a live value, which this call only inspects.
404    let tpe = unsafe { ffi::sqlite3_value_type(value.as_ptr()) };
405    match tpe {
406        ffi::SQLITE_TEXT => Some(SqliteType::Text),
407        ffi::SQLITE_INTEGER => Some(SqliteType::Long),
408        ffi::SQLITE_FLOAT => Some(SqliteType::Double),
409        ffi::SQLITE_BLOB => Some(SqliteType::Binary),
410        ffi::SQLITE_NULL => None,
411        _ => {
    ::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!(
412            "Sqlite's documentation state that this case ({}) is not reachable. \
413             If you ever see this error message please open an issue at \
414             https://github.com/diesel-rs/diesel.",
415            tpe
416        ),
417    }
418}
419
420impl OwnedSqliteValue {
421    /// Copies a value out of a statement or a function argument.
422    ///
423    /// `Ok(None)` is SQL `NULL`. A failed allocation is an error instead, as reporting
424    /// it as `NULL` would hand out a wrong value.
425    pub(super) fn copy_from_ptr(
426        ptr: NonNull<ffi::sqlite3_value>,
427    ) -> QueryResult<Option<OwnedSqliteValue>> {
428        // SAFETY: `ptr` points to a live `sqlite3_value` owned by the statement or
429        // callback that outlives this call, and reading its type only inspects it.
430        let tpe = unsafe { ffi::sqlite3_value_type(ptr.as_ptr()) };
431        if ffi::SQLITE_NULL == tpe {
432            return Ok(None);
433        }
434        // SAFETY: the same live value as above, which `sqlite3_value_dup` only reads
435        // while it copies it into an independently owned value.
436        let value = unsafe { ffi::sqlite3_value_dup(ptr.as_ptr()) };
437        // The value above is not null, so only a failed allocation returns null here.
438        let value = NonNull::new(value).ok_or_else(duplication_failed)?;
439        Ok(Some(Self { value }))
440    }
441
442    pub(super) fn duplicate(&self) -> QueryResult<OwnedSqliteValue> {
443        // SAFETY: `self` owns `self.value` and keeps it alive across this call, and
444        // `sqlite3_value_dup` only reads it while copying it.
445        let value = unsafe { ffi::sqlite3_value_dup(self.value.as_ptr()) };
446        let value = NonNull::new(value).ok_or_else(duplication_failed)?;
447        Ok(OwnedSqliteValue { value })
448    }
449}
450
451#[cfg(test)]
452mod tests {
453    use crate::connection::{LoadConnection, SimpleConnection};
454    use crate::row::Field;
455    use crate::row::Row;
456    #[cfg(not(miri))]
457    use crate::sql_types::{Blob, Double, Int4, Text};
458    #[cfg(not(miri))]
459    use crate::*;
460
461    #[cfg(all(
462        feature = "std",
463        not(all(target_family = "wasm", target_os = "unknown")),
464        // These tests read memory allocated by the native library or rely on
465        // native code invoking a panic hook, neither of which is supported when
466        // running under miri with a native libsqlite3 (`-Zmiri-native-lib`).
467        not(miri)
468    ))]
469    mod allocation_failure {
470        use super::super::SqliteValue;
471        use crate::connection::{LoadConnection, SimpleConnection};
472        use crate::deserialize::{self, FromSql};
473        use crate::prelude::*;
474        use crate::row::{Field, Row};
475        use crate::sql_types::Binary;
476        use crate::sqlite::connection::oom_test_support::{
477            panic_message, run_in_child, with_heap_limit,
478        };
479        use crate::sqlite::{Sqlite, SqliteConnection};
480        use crate::test_helpers::format_error;
481        use alloc::string::{String, ToString};
482
483        const VALUE_LEN: usize = 1_048_576;
484
485        crate::table! {
486            oom_blob (id) {
487                id -> Integer,
488                value -> Binary,
489            }
490        }
491
492        crate::table! {
493            oom_text (id) {
494                id -> Integer,
495                value -> Text,
496            }
497        }
498
499        crate::table! {
500            oom_zeroblob (id) {
501                id -> Integer,
502                len -> Integer,
503            }
504        }
505
506        crate::define_sql_function! {
507            fn read_blob_under_pressure(value: Binary) -> Integer;
508        }
509
510        /// Carries the panic message of a blob read that ran out of memory, as the
511        /// failing statement reports SQLite's own error instead.
512        struct BlobUnderPressure(String);
513
514        impl FromSql<Binary, Sqlite> for BlobUnderPressure {
515            fn from_sql(mut value: SqliteValue<'_, '_, '_>) -> deserialize::Result<Self> {
516                let payload = std::panic::catch_unwind(core::panic::AssertUnwindSafe(|| {
517                    without_spare_memory(|| core::hint::black_box(value.read_blob().len()));
518                }))
519                .expect_err("the blob read did not panic");
520                Ok(Self(panic_message(&*payload).to_string()))
521            }
522        }
523
524        impl crate::deserialize::Queryable<Binary, Sqlite> for BlobUnderPressure {
525            type Row = Self;
526
527            fn build(row: Self::Row) -> deserialize::Result<Self> {
528                Ok(row)
529            }
530        }
531
532        /// Rejects every further SQLite allocation while `f` runs.
533        fn without_spare_memory<R>(f: impl FnOnce() -> R) -> R {
534            with_heap_limit(0, f)
535        }
536
537        fn expect_panic(f: impl FnOnce() + core::panic::UnwindSafe, expected: &str) {
538            let payload = std::panic::catch_unwind(f).expect_err("the read did not panic");
539            let message = panic_message(&*payload);
540            assert!(
541                message.contains(expected),
542                "unexpected panic message: {message}"
543            );
544        }
545
546        fn blob_connection(rows: i32) -> SqliteConnection {
547            let mut conn = SqliteConnection::establish(":memory:").unwrap();
548            // Diesel has no typed DDL.
549            conn.batch_execute(
550                "CREATE TABLE oom_blob (id INTEGER PRIMARY KEY, value BLOB NOT NULL)",
551            )
552            .unwrap();
553            for id in 1..=rows {
554                crate::insert_into(oom_blob::table)
555                    .values((
556                        oom_blob::id.eq(id),
557                        oom_blob::value.eq(alloc::vec![b'x'; VALUE_LEN]),
558                    ))
559                    .execute(&mut conn)
560                    .unwrap();
561            }
562            conn
563        }
564
565        fn utf16_text_connection(rows: i32) -> SqliteConnection {
566            let mut conn = SqliteConnection::establish(":memory:").unwrap();
567            // Diesel has no typed DDL and no typed representation of the database encoding.
568            conn.batch_execute(
569                "PRAGMA encoding = 'UTF-16le';
570                 CREATE TABLE oom_text (id INTEGER PRIMARY KEY, value TEXT NOT NULL)",
571            )
572            .unwrap();
573            for id in 1..=rows {
574                crate::insert_into(oom_text::table)
575                    .values((
576                        oom_text::id.eq(id),
577                        oom_text::value.eq("x".repeat(VALUE_LEN)),
578                    ))
579                    .execute(&mut conn)
580                    .unwrap();
581            }
582            conn
583        }
584
585        #[test]
586        fn text_read_panics_when_conversion_fails() {
587            run_in_child(|| {
588                let mut conn = utf16_text_connection(1);
589                let mut rows = conn.load(oom_text::table.select(oom_text::value)).unwrap();
590                let row = rows.next().unwrap().unwrap();
591                let field = row.get(0).unwrap();
592                let mut value = field.value().unwrap();
593
594                expect_panic(
595                    core::panic::AssertUnwindSafe(|| {
596                        without_spare_memory(|| {
597                            core::hint::black_box(value.read_text().len());
598                        });
599                    }),
600                    "SQLite ran out of memory while reading a value as text",
601                );
602            });
603        }
604
605        #[test]
606        fn duplicated_text_read_panics_when_conversion_fails() {
607            run_in_child(|| {
608                let mut conn = utf16_text_connection(2);
609                let mut rows = conn.load(oom_text::table.select(oom_text::value)).unwrap();
610                let first = rows.next().unwrap().unwrap();
611                // Advancing while the first row lives copies its values out of the statement,
612                // and `sqlite3_value_dup` disconnects them from the connection.
613                let _second = rows.next().unwrap().unwrap();
614                let field = first.get(0).unwrap();
615                let mut value = field.value().unwrap();
616
617                expect_panic(
618                    core::panic::AssertUnwindSafe(|| {
619                        without_spare_memory(|| {
620                            core::hint::black_box(value.read_text().len());
621                        });
622                    }),
623                    "SQLite failed to allocate memory while reading a value as text",
624                );
625            });
626        }
627
628        #[test]
629        fn blob_read_as_text_panics_when_the_copy_fails() {
630            run_in_child(|| {
631                let mut conn = blob_connection(1);
632                let mut rows = conn.load(oom_blob::table.select(oom_blob::value)).unwrap();
633                let row = rows.next().unwrap().unwrap();
634                let field = row.get(0).unwrap();
635                let mut value = field.value().unwrap();
636
637                expect_panic(
638                    core::panic::AssertUnwindSafe(|| {
639                        without_spare_memory(|| {
640                            core::hint::black_box(value.read_text().len());
641                        });
642                    }),
643                    "SQLite failed to allocate memory while reading a value as text",
644                );
645            });
646        }
647
648        #[test]
649        fn text_read_as_blob_panics_when_the_copy_fails() {
650            run_in_child(|| {
651                let mut conn = utf16_text_connection(1);
652                let mut rows = conn.load(oom_text::table.select(oom_text::value)).unwrap();
653                let row = rows.next().unwrap().unwrap();
654                let field = row.get(0).unwrap();
655                let mut value = field.value().unwrap();
656
657                expect_panic(
658                    core::panic::AssertUnwindSafe(|| {
659                        without_spare_memory(|| {
660                            core::hint::black_box(value.read_blob().len());
661                        });
662                    }),
663                    "SQLite failed to allocate memory while reading a value as a blob",
664                );
665            });
666        }
667
668        #[test]
669        fn blob_read_keeps_utf16_text_bytes_across_a_text_read() {
670            let mut conn = utf16_text_connection(1);
671            let mut rows = conn.load(oom_text::table.select(oom_text::value)).unwrap();
672            let row = rows.next().unwrap().unwrap();
673            let field = row.get(0).unwrap();
674            let mut blob_value = field.value().unwrap();
675            let mut text_value = field.value().unwrap();
676
677            let blob = blob_value.read_blob();
678            assert_eq!(blob.len(), 2 * VALUE_LEN);
679            let (pairs, rest) = blob.as_chunks::<2>();
680            assert!(
681                pairs.iter().all(|pair| pair == b"x\0") && rest.is_empty(),
682                "SQLite blob content changed"
683            );
684
685            // Converting the shared value to UTF-8 frees its UTF-16 buffer.
686            assert_eq!(text_value.read_text().len(), VALUE_LEN);
687            let (pairs, rest) = blob.as_chunks::<2>();
688            assert!(
689                pairs.iter().all(|pair| pair == b"x\0") && rest.is_empty(),
690                "the text read invalidated the blob bytes"
691            );
692        }
693
694        #[test]
695        fn zeroblob_read_panics_when_expansion_fails() {
696            run_in_child(|| {
697                let mut conn = SqliteConnection::establish(":memory:").unwrap();
698                // Diesel has no typed DDL.
699                conn.batch_execute(
700                    "CREATE TABLE oom_zeroblob (id INTEGER PRIMARY KEY, len INTEGER NOT NULL)",
701                )
702                .unwrap();
703                crate::insert_into(oom_zeroblob::table)
704                    .values((
705                        oom_zeroblob::id.eq(1),
706                        oom_zeroblob::len.eq(i32::try_from(VALUE_LEN).unwrap()),
707                    ))
708                    .execute(&mut conn)
709                    .unwrap();
710                let observed = alloc::sync::Arc::new(core::sync::atomic::AtomicBool::new(false));
711                let callback_observed = alloc::sync::Arc::clone(&observed);
712                read_blob_under_pressure_utils::register_impl(
713                    &mut conn,
714                    move |value: BlobUnderPressure| {
715                        assert!(
716                            value.0.contains(
717                                "SQLite ran out of memory while reading a value as a blob"
718                            ),
719                            "unexpected panic message: {}",
720                            value.0
721                        );
722                        callback_observed.store(true, core::sync::atomic::Ordering::Relaxed);
723                        1
724                    },
725                )
726                .unwrap();
727
728                // Diesel has no typed representation of `zeroblob`, and a constant argument
729                // would be expanded by the virtual machine before the function sees it.
730                let result = oom_zeroblob::table
731                    .select(read_blob_under_pressure(crate::dsl::sql::<Binary>(
732                        "zeroblob(len)",
733                    )))
734                    .get_result::<i32>(&mut conn);
735                assert!(result.is_err(), "the blob read did not fail the statement");
736                assert!(
737                    observed.load(core::sync::atomic::Ordering::Relaxed),
738                    "the function did not read its argument"
739                );
740            });
741        }
742
743        #[test]
744        fn row_duplication_reports_value_duplication_failure() {
745            run_in_child(|| {
746                let mut conn = blob_connection(2);
747                let mut rows = conn.load(oom_blob::table.select(oom_blob::value)).unwrap();
748                // Holding the first row makes the next step copy it out of the statement.
749                let _first = rows.next().unwrap().unwrap();
750
751                let error = match without_spare_memory(|| rows.next()) {
752                    Some(Err(e)) => e,
753                    Some(Ok(_)) => panic!("the row duplication did not fail"),
754                    None => panic!("the iterator ended instead of copying the row"),
755                };
756                assert!(
757                    format_error(&error).contains("SQLite failed to allocate a duplicated value"),
758                    "unexpected error: {error}"
759                );
760            });
761        }
762    }
763
764    crate::table! {
765        empty_values (id) {
766            id -> Integer,
767            text -> Text,
768            blob -> Binary,
769            zero_blob -> Binary,
770        }
771    }
772
773    #[diesel_test_helper::test]
774    fn can_read_empty_values_as_empty_blob() {
775        use crate::prelude::*;
776        let mut conn = SqliteConnection::establish(":memory:").unwrap();
777        // Diesel has no typed DDL, and BLOB affinity keeps the empty text literal in
778        // `blob` as TEXT, which the typed DSL cannot store there.
779        conn.batch_execute(
780            "CREATE TABLE empty_values (id INTEGER PRIMARY KEY, text TEXT, blob BLOB, zero_blob BLOB);
781             INSERT INTO empty_values (id, text, blob, zero_blob) VALUES (1, '', '', X'');",
782        )
783        .unwrap();
784
785        // The same empty TEXT through the typed `FromSql<Binary, Sqlite>` path.
786        let loaded = crate::select(crate::dsl::sql::<crate::sql_types::Binary>("''"))
787            .get_result::<Vec<u8>>(&mut conn)
788            .unwrap();
789        assert!(loaded.is_empty());
790
791        let mut rows = conn
792            .load(empty_values::table.select((
793                empty_values::text,
794                empty_values::blob,
795                empty_values::zero_blob,
796            )))
797            .unwrap();
798        let row = rows.next().unwrap().unwrap();
799        let text_field = row.get(0).unwrap();
800        let blob_field = row.get(1).unwrap();
801        let zero_blob_field = row.get(2).unwrap();
802
803        let mut text_value = text_field.value().unwrap();
804        assert_eq!(text_value.read_text(), "");
805        let mut text_value = text_field.value().unwrap();
806        assert_eq!(text_value.read_blob(), b"");
807
808        let mut blob_value = blob_field.value().unwrap();
809        assert_eq!(blob_value.value_type(), Some(super::SqliteType::Text));
810        assert_eq!(blob_value.read_blob(), b"");
811
812        // A zero length BLOB, for which SQLite also reports a null pointer.
813        let mut zero_blob_value = zero_blob_field.value().unwrap();
814        assert_eq!(
815            zero_blob_value.value_type(),
816            Some(super::SqliteType::Binary)
817        );
818        assert_eq!(zero_blob_value.read_blob(), b"");
819    }
820
821    // Reads text values, which requires accessing memory allocated by the
822    // native library. That is not supported when running under miri with a
823    // native libsqlite3 (`-Zmiri-native-lib`).
824    #[cfg(not(miri))] // ffi call
825    #[diesel_test_helper::test]
826    fn blob_bytes_survive_a_text_read_of_the_same_value() {
827        use crate::prelude::*;
828        let mut conn = SqliteConnection::establish(":memory:").unwrap();
829        // Diesel has no typed `randomblob`, and a stored blob points into the page
830        // image, which a conversion never frees.
831        let mut rows = conn
832            .load(crate::select(crate::dsl::sql::<crate::sql_types::Binary>(
833                "randomblob(1048576)",
834            )))
835            .unwrap();
836        let row = rows.next().unwrap().unwrap();
837        let field = row.get(0).unwrap();
838        let mut blob_value = field.value().unwrap();
839        let mut text_value = field.value().unwrap();
840        let mut other_blob_value = field.value().unwrap();
841
842        let blob = blob_value.read_blob();
843        let expected = Vec::from(blob);
844        let address = blob.as_ptr();
845
846        // Converting the shared value to text would free the buffer `blob` points at.
847        assert!(!text_value.read_text().is_empty());
848        assert_eq!(blob, expected.as_slice(), "the text read moved the blob");
849        assert_eq!(
850            other_blob_value.read_blob().as_ptr(),
851            address,
852            "the text read converted the shared value"
853        );
854    }
855
856    // Reads text values, which requires accessing memory allocated by the
857    // native library. That is not supported when running under miri with a
858    // native libsqlite3 (`-Zmiri-native-lib`).
859    #[cfg(not(miri))] // ffi call
860    #[expect(clippy::approx_constant)] // we really want to use 3.14
861    #[diesel_test_helper::test]
862    fn can_convert_all_values() {
863        let mut conn = SqliteConnection::establish(":memory:").unwrap();
864
865        conn.batch_execute("CREATE TABLE tests(int INTEGER, text TEXT, blob BLOB, float FLOAT)")
866            .unwrap();
867
868        diesel::sql_query("INSERT INTO tests(int, text, blob, float) VALUES(?, ?, ?, ?)")
869            .bind::<Int4, _>(42)
870            .bind::<Text, _>("foo")
871            .bind::<Blob, _>([0xFF_u8, 0xFE, 0xFD])
872            .bind::<Double, _>(3.14)
873            .execute(&mut conn)
874            .unwrap();
875
876        let mut res = conn
877            .load(diesel::sql_query(
878                "SELECT int, text, blob, float FROM tests",
879            ))
880            .unwrap();
881        let row = res.next().unwrap().unwrap();
882        let int_field = row.get(0).unwrap();
883        let text_field = row.get(1).unwrap();
884        let blob_field = row.get(2).unwrap();
885        let float_field = row.get(3).unwrap();
886
887        let mut int_value = int_field.value().unwrap();
888        assert_eq!(int_value.read_integer(), 42);
889        let mut int_value = int_field.value().unwrap();
890        assert_eq!(int_value.read_long(), 42);
891        let mut int_value = int_field.value().unwrap();
892        assert_eq!(int_value.read_double(), 42.0);
893        let mut int_value = int_field.value().unwrap();
894        assert_eq!(int_value.read_text(), "42");
895        let mut int_value = int_field.value().unwrap();
896        assert_eq!(int_value.read_blob(), b"42");
897
898        let mut text_value = text_field.value().unwrap();
899        assert_eq!(text_value.read_integer(), 0);
900        let mut text_value = text_field.value().unwrap();
901        assert_eq!(text_value.read_long(), 0);
902        let mut text_value = text_field.value().unwrap();
903        assert_eq!(text_value.read_double(), 0.0);
904        let mut text_value = text_field.value().unwrap();
905        assert_eq!(text_value.read_text(), "foo");
906        let mut text_value = text_field.value().unwrap();
907        assert_eq!(text_value.read_blob(), b"foo");
908
909        let mut blob_value = blob_field.value().unwrap();
910        assert_eq!(blob_value.read_integer(), 0);
911        let mut blob_value = blob_field.value().unwrap();
912        assert_eq!(blob_value.read_long(), 0);
913        let mut blob_value = blob_field.value().unwrap();
914        assert_eq!(blob_value.read_double(), 0.0);
915        let mut blob_value = blob_field.value().unwrap();
916        assert_eq!(blob_value.read_text(), "\u{fffd}\u{fffd}\u{fffd}"); // ���
917        let mut blob_value = blob_field.value().unwrap();
918        assert_eq!(blob_value.read_blob(), [0xFF, 0xFE, 0xFD]);
919
920        let mut float_value = float_field.value().unwrap();
921        assert_eq!(float_value.read_integer(), 3);
922        let mut float_value = float_field.value().unwrap();
923        assert_eq!(float_value.read_long(), 3);
924        let mut float_value = float_field.value().unwrap();
925        assert_eq!(float_value.read_double(), 3.14);
926        let mut float_value = float_field.value().unwrap();
927        assert_eq!(float_value.read_text(), "3.14");
928        let mut float_value = float_field.value().unwrap();
929        assert_eq!(float_value.read_blob(), b"3.14");
930    }
931}