1#![allow(unsafe_code)] // ffi calls
2#[cfg(not(all(target_family = "wasm", target_os = "unknown")))]
3extern crate libsqlite3_sys as ffi;
45#[cfg(all(target_family = "wasm", target_os = "unknown"))]
6use sqlite_wasm_rs as ffi;
78use core::cell::Ref;
9use core::ptr::NonNull;
10use core::{slice, str};
1112use crate::result::QueryResult;
13use crate::sqlite::SqliteType;
1415use super::owned_row::OwnedSqliteRow;
16use super::row::PrivateSqliteRow;
1718/// 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
28owner: 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.
36value: 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
44string_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
47initial_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
50converted: Option<OwnedSqliteValue>,
51}
5253enum 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`
56Row(Ref<'row, PrivateSqliteRow<'stmt, 'query>>),
57// A value outside a row: a function argument carries its connection, a
58 // duplicated value has none
59NonRow(Option<NonNull<ffi::sqlite3>>),
60}
6162/// 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}
6869impl Representation {
70fn target(self) -> &'static str {
71match self {
72Self::Text => "text",
73Self::Blob => "a blob",
74 }
75 }
76}
7778#[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 {
81pub(super) value: NonNull<ffi::sqlite3_value>,
82}
8384impl Dropfor OwnedSqliteValue {
85fn drop(&mut self) {
86unsafe { ffi::sqlite3_value_free(self.value.as_ptr()) }
87 }
88}
8990// Unsafe Send impl safe since sqlite3_value is built with sqlite3_value_dup
91// see https://www.sqlite.org/c3ref/value.html
92unsafe impl Sendfor OwnedSqliteValue {}
9394#[cold]
95fn allocation_failed(out_of_memory: bool, target: &str) -> ! {
96let 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}
103104#[cold]
105fn duplication_failed() -> crate::result::Error {
106crate::result::Error::DeserializationError(
107"SQLite failed to allocate a duplicated value".into(),
108 )
109}
110111impl<'row, 'stmt, 'query> SqliteValue<'row, 'stmt, 'query> {
112pub(super) fn new(
113 row: Ref<'row, PrivateSqliteRow<'stmt, 'query>>,
114 col_idx: usize,
115 ) -> Option<SqliteValue<'row, 'stmt, 'query>> {
116let 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`.
127let initial_type = unsafe { value_type_of(value) }?;
128Some(Self {
129 owner: ValueOwner::Row(row),
130value,
131 string_ref: None,
132initial_type,
133 converted: None,
134 })
135 }
136137pub(super) fn from_owned_row(
138 row: &'row OwnedSqliteRow,
139 col_idx: usize,
140 ) -> Option<SqliteValue<'row, 'stmt, 'query>> {
141let 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`.
143let initial_type = unsafe { value_type_of(value) }?;
144Some(Self {
145 owner: ValueOwner::NonRow(None),
146value,
147 string_ref: None,
148initial_type,
149 converted: None,
150 })
151 }
152153pub(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>> {
158let 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.
160let initial_type = unsafe { value_type_of(value) }?;
161Some(Self {
162 owner: ValueOwner::NonRow(Some(connection)),
163value,
164 string_ref: None,
165initial_type,
166 converted: None,
167 })
168 }
169170// Values from `sqlite3_value_dup` are disconnected from the connection, so they
171 // cannot be asked: https://www.sqlite.org/c3ref/value_blob.html
172fn connection(&self) -> Option<NonNull<ffi::sqlite3>> {
173match &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 }
181182/// Reports whether an allocation just failed, which must be asked before any other call.
183fn reports_out_of_memory(&self) -> bool {
184let Some(connection) = self.connection() else {
185return false;
186 };
187// SAFETY: The owner keeps the connection alive and this call only reads
188 // connection state.
189unsafe { ffi::sqlite3_errcode(connection.as_ptr()) == ffi::SQLITE_NOMEM }
190 }
191192/// 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.
194fn 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.
197let 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 );
201if !converts_in_place {
202return self.value;
203 }
204if 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.
207let 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.
209let Some(copy) = NonNull::new(copy) else {
210// Ask the connection before any other call to it clears the error code.
211allocation_failed(self.reports_out_of_memory(), wanted.target());
212 };
213self.converted = Some(OwnedSqliteValue { value: copy });
214 }
215self.converted
216 .as_ref()
217 .expect("We initialised it literally above")
218 .value
219 }
220221pub(crate) fn as_byte_string(&mut self) -> &[u8] {
222let 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.
225unsafe {
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
229if ffi::sqlite3_value_text(value.as_ptr()).is_null() {
230// Zero length text has a valid pointer, so this is a failed conversion.
231allocation_failed(self.reports_out_of_memory(), Representation::Text.target());
232 }
233let len = ffi::sqlite3_value_bytes(value.as_ptr());
234// The length above may have invalidated the pointer.
235let ptr = ffi::sqlite3_value_text(value.as_ptr());
236if ptr.is_null() {
237allocation_failed(self.reports_out_of_memory(), Representation::Text.target());
238 }
239 slice::from_raw_parts(
240ptr,
241len.try_into()
242 .expect("Diesel expects to run at least on a 32 bit platform"),
243 )
244 }
245 }
246247pub(crate) fn as_utf8_str(&mut self) -> Result<&str, core::str::Utf8Error> {
248 str::from_utf8(self.as_byte_string())
249 }
250251pub(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
255if 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.
258let lossy = alloc::string::String::from_utf8_lossy(self.as_byte_string()).into_owned();
259self.string_ref = Some(lossy.into_boxed_str());
260let s = self261 .string_ref
262 .as_deref()
263 .expect("We initialised it literally above");
264return f(s);
265 }
266let s = str::from_utf8(self.as_byte_string()).expect("The bytes are valid utf8 above");
267f(s)
268 }
269270/// 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.
286pub fn read_text(&mut self) -> &str {
287// TODO: Return Result in Diesel 3 so SQLite allocation failures reach callers.
288self.parse_string(|s| s)
289 }
290291/// 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.
310pub fn read_blob(&mut self) -> &[u8] {
311// TODO: Return Result in Diesel 3 so SQLite allocation failures reach callers.
312let 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.
315unsafe {
316// Preserve a zeroblob's length because failed expansion changes it to SQL NULL.
317let initial_blob_len = if #[allow(non_exhaustive_omitted_patterns)] match self.initial_type {
SqliteType::Binary => true,
_ => false,
}matches!(self.initial_type, SqliteType::Binary) {
318Some(ffi::sqlite3_value_bytes(value.as_ptr()))
319 } else {
320None321 };
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
325if ffi::sqlite3_value_blob(value.as_ptr()).is_null() {
326// Ask the connection before any other call to it clears the error code.
327let out_of_memory = self.reports_out_of_memory();
328let len = ffi::sqlite3_value_bytes(value.as_ptr());
329if !out_of_memory330 && ((#[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 {
333return &[];
334 }
335allocation_failed(out_of_memory, Representation::Blob.target());
336 }
337let len = ffi::sqlite3_value_bytes(value.as_ptr());
338// The length above may have invalidated the pointer.
339let ptr = ffi::sqlite3_value_blob(value.as_ptr());
340if ptr.is_null() {
341allocation_failed(self.reports_out_of_memory(), Representation::Blob.target());
342 }
343 slice::from_raw_parts(
344ptras *const u8,
345len.try_into()
346 .expect("Diesel expects to run at least on a 32 bit platform"),
347 )
348 }
349 }
350351/// 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
360pub fn read_integer(&mut self) -> i32 {
361unsafe { ffi::sqlite3_value_int(self.value.as_ptr()) }
362 }
363364/// 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
373pub fn read_long(&mut self) -> i64 {
374unsafe { ffi::sqlite3_value_int64(self.value.as_ptr()) }
375 }
376377/// 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
386pub fn read_double(&mut self) -> f64 {
387unsafe { ffi::sqlite3_value_double(self.value.as_ptr()) }
388 }
389390/// Get the type of the value as returned by sqlite
391pub fn value_type(&self) -> Option<SqliteType> {
392// SAFETY: `self.owner` keeps the value alive and this call only inspects it.
393unsafe { value_type_of(self.value) }
394 }
395}
396397/// 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.
404let tpe = unsafe { ffi::sqlite3_value_type(value.as_ptr()) };
405match 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}
419420impl 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.
425pub(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.
430let tpe = unsafe { ffi::sqlite3_value_type(ptr.as_ptr()) };
431if ffi::SQLITE_NULL == tpe {
432return 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.
436let value = unsafe { ffi::sqlite3_value_dup(ptr.as_ptr()) };
437// The value above is not null, so only a failed allocation returns null here.
438let value = NonNull::new(value).ok_or_else(duplication_failed)?;
439Ok(Some(Self { value }))
440 }
441442pub(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.
445let value = unsafe { ffi::sqlite3_value_dup(self.value.as_ptr()) };
446let value = NonNull::new(value).ok_or_else(duplication_failed)?;
447Ok(OwnedSqliteValue { value })
448 }
449}
450451#[cfg(test)]
452mod tests {
453use crate::connection::{LoadConnection, SimpleConnection};
454use crate::row::Field;
455use crate::row::Row;
456#[cfg(not(miri))]
457use crate::sql_types::{Blob, Double, Int4, Text};
458#[cfg(not(miri))]
459use crate::*;
460461#[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`).
467not(miri)
468 ))]
469mod allocation_failure {
470use super::super::SqliteValue;
471use crate::connection::{LoadConnection, SimpleConnection};
472use crate::deserialize::{self, FromSql};
473use crate::prelude::*;
474use crate::row::{Field, Row};
475use crate::sql_types::Binary;
476use crate::sqlite::connection::oom_test_support::{
477 panic_message, run_in_child, with_heap_limit,
478 };
479use crate::sqlite::{Sqlite, SqliteConnection};
480use crate::test_helpers::format_error;
481use alloc::string::{String, ToString};
482483const VALUE_LEN: usize = 1_048_576;
484485crate::table! {
486 oom_blob (id) {
487 id -> Integer,
488 value -> Binary,
489 }
490 }
491492crate::table! {
493 oom_text (id) {
494 id -> Integer,
495 value -> Text,
496 }
497 }
498499crate::table! {
500 oom_zeroblob (id) {
501 id -> Integer,
502 len -> Integer,
503 }
504 }
505506crate::define_sql_function! {
507fn read_blob_under_pressure(value: Binary) -> Integer;
508 }
509510/// Carries the panic message of a blob read that ran out of memory, as the
511 /// failing statement reports SQLite's own error instead.
512struct BlobUnderPressure(String);
513514impl FromSql<Binary, Sqlite> for BlobUnderPressure {
515fn from_sql(mut value: SqliteValue<'_, '_, '_>) -> deserialize::Result<Self> {
516let 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");
520Ok(Self(panic_message(&*payload).to_string()))
521 }
522 }
523524impl crate::deserialize::Queryable<Binary, Sqlite> for BlobUnderPressure {
525type Row = Self;
526527fn build(row: Self::Row) -> deserialize::Result<Self> {
528Ok(row)
529 }
530 }
531532/// Rejects every further SQLite allocation while `f` runs.
533fn without_spare_memory<R>(f: impl FnOnce() -> R) -> R {
534 with_heap_limit(0, f)
535 }
536537fn expect_panic(f: impl FnOnce() + core::panic::UnwindSafe, expected: &str) {
538let payload = std::panic::catch_unwind(f).expect_err("the read did not panic");
539let message = panic_message(&*payload);
540assert!(
541 message.contains(expected),
542"unexpected panic message: {message}"
543);
544 }
545546fn blob_connection(rows: i32) -> SqliteConnection {
547let mut conn = SqliteConnection::establish(":memory:").unwrap();
548// Diesel has no typed DDL.
549conn.batch_execute(
550"CREATE TABLE oom_blob (id INTEGER PRIMARY KEY, value BLOB NOT NULL)",
551 )
552 .unwrap();
553for id in 1..=rows {
554crate::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 }
564565fn utf16_text_connection(rows: i32) -> SqliteConnection {
566let mut conn = SqliteConnection::establish(":memory:").unwrap();
567// Diesel has no typed DDL and no typed representation of the database encoding.
568conn.batch_execute(
569"PRAGMA encoding = 'UTF-16le';
570 CREATE TABLE oom_text (id INTEGER PRIMARY KEY, value TEXT NOT NULL)",
571 )
572 .unwrap();
573for id in 1..=rows {
574crate::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 }
584585#[test]
586fn text_read_panics_when_conversion_fails() {
587 run_in_child(|| {
588let mut conn = utf16_text_connection(1);
589let mut rows = conn.load(oom_text::table.select(oom_text::value)).unwrap();
590let row = rows.next().unwrap().unwrap();
591let field = row.get(0).unwrap();
592let mut value = field.value().unwrap();
593594 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 }
604605#[test]
606fn duplicated_text_read_panics_when_conversion_fails() {
607 run_in_child(|| {
608let mut conn = utf16_text_connection(2);
609let mut rows = conn.load(oom_text::table.select(oom_text::value)).unwrap();
610let 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.
613let _second = rows.next().unwrap().unwrap();
614let field = first.get(0).unwrap();
615let mut value = field.value().unwrap();
616617 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 }
627628#[test]
629fn blob_read_as_text_panics_when_the_copy_fails() {
630 run_in_child(|| {
631let mut conn = blob_connection(1);
632let mut rows = conn.load(oom_blob::table.select(oom_blob::value)).unwrap();
633let row = rows.next().unwrap().unwrap();
634let field = row.get(0).unwrap();
635let mut value = field.value().unwrap();
636637 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 }
647648#[test]
649fn text_read_as_blob_panics_when_the_copy_fails() {
650 run_in_child(|| {
651let mut conn = utf16_text_connection(1);
652let mut rows = conn.load(oom_text::table.select(oom_text::value)).unwrap();
653let row = rows.next().unwrap().unwrap();
654let field = row.get(0).unwrap();
655let mut value = field.value().unwrap();
656657 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 }
667668#[test]
669fn blob_read_keeps_utf16_text_bytes_across_a_text_read() {
670let mut conn = utf16_text_connection(1);
671let mut rows = conn.load(oom_text::table.select(oom_text::value)).unwrap();
672let row = rows.next().unwrap().unwrap();
673let field = row.get(0).unwrap();
674let mut blob_value = field.value().unwrap();
675let mut text_value = field.value().unwrap();
676677let blob = blob_value.read_blob();
678assert_eq!(blob.len(), 2 * VALUE_LEN);
679let (pairs, rest) = blob.as_chunks::<2>();
680assert!(
681 pairs.iter().all(|pair| pair == b"x\0") && rest.is_empty(),
682"SQLite blob content changed"
683);
684685// Converting the shared value to UTF-8 frees its UTF-16 buffer.
686assert_eq!(text_value.read_text().len(), VALUE_LEN);
687let (pairs, rest) = blob.as_chunks::<2>();
688assert!(
689 pairs.iter().all(|pair| pair == b"x\0") && rest.is_empty(),
690"the text read invalidated the blob bytes"
691);
692 }
693694#[test]
695fn zeroblob_read_panics_when_expansion_fails() {
696 run_in_child(|| {
697let mut conn = SqliteConnection::establish(":memory:").unwrap();
698// Diesel has no typed DDL.
699conn.batch_execute(
700"CREATE TABLE oom_zeroblob (id INTEGER PRIMARY KEY, len INTEGER NOT NULL)",
701 )
702 .unwrap();
703crate::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();
710let observed = alloc::sync::Arc::new(core::sync::atomic::AtomicBool::new(false));
711let callback_observed = alloc::sync::Arc::clone(&observed);
712 read_blob_under_pressure_utils::register_impl(
713&mut conn,
714move |value: BlobUnderPressure| {
715assert!(
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);
7231
724},
725 )
726 .unwrap();
727728// Diesel has no typed representation of `zeroblob`, and a constant argument
729 // would be expanded by the virtual machine before the function sees it.
730let result = oom_zeroblob::table
731 .select(read_blob_under_pressure(crate::dsl::sql::<Binary>(
732"zeroblob(len)",
733 )))
734 .get_result::<i32>(&mut conn);
735assert!(result.is_err(), "the blob read did not fail the statement");
736assert!(
737 observed.load(core::sync::atomic::Ordering::Relaxed),
738"the function did not read its argument"
739);
740 });
741 }
742743#[test]
744fn row_duplication_reports_value_duplication_failure() {
745 run_in_child(|| {
746let mut conn = blob_connection(2);
747let 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.
749let _first = rows.next().unwrap().unwrap();
750751let error = match without_spare_memory(|| rows.next()) {
752Some(Err(e)) => e,
753Some(Ok(_)) => panic!("the row duplication did not fail"),
754None => panic!("the iterator ended instead of copying the row"),
755 };
756assert!(
757 format_error(&error).contains("SQLite failed to allocate a duplicated value"),
758"unexpected error: {error}"
759);
760 });
761 }
762 }
763764crate::table! {
765 empty_values (id) {
766 id -> Integer,
767 text -> Text,
768 blob -> Binary,
769 zero_blob -> Binary,
770 }
771 }
772773#[diesel_test_helper::test]
774fn can_read_empty_values_as_empty_blob() {
775use crate::prelude::*;
776let 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.
779conn.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();
784785// The same empty TEXT through the typed `FromSql<Binary, Sqlite>` path.
786let loaded = crate::select(crate::dsl::sql::<crate::sql_types::Binary>("''"))
787 .get_result::<Vec<u8>>(&mut conn)
788 .unwrap();
789assert!(loaded.is_empty());
790791let 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();
798let row = rows.next().unwrap().unwrap();
799let text_field = row.get(0).unwrap();
800let blob_field = row.get(1).unwrap();
801let zero_blob_field = row.get(2).unwrap();
802803let mut text_value = text_field.value().unwrap();
804assert_eq!(text_value.read_text(), "");
805let mut text_value = text_field.value().unwrap();
806assert_eq!(text_value.read_blob(), b"");
807808let mut blob_value = blob_field.value().unwrap();
809assert_eq!(blob_value.value_type(), Some(super::SqliteType::Text));
810assert_eq!(blob_value.read_blob(), b"");
811812// A zero length BLOB, for which SQLite also reports a null pointer.
813let mut zero_blob_value = zero_blob_field.value().unwrap();
814assert_eq!(
815 zero_blob_value.value_type(),
816Some(super::SqliteType::Binary)
817 );
818assert_eq!(zero_blob_value.read_blob(), b"");
819 }
820821// 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]
826fn blob_bytes_survive_a_text_read_of_the_same_value() {
827use crate::prelude::*;
828let 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.
831let mut rows = conn
832 .load(crate::select(crate::dsl::sql::<crate::sql_types::Binary>(
833"randomblob(1048576)",
834 )))
835 .unwrap();
836let row = rows.next().unwrap().unwrap();
837let field = row.get(0).unwrap();
838let mut blob_value = field.value().unwrap();
839let mut text_value = field.value().unwrap();
840let mut other_blob_value = field.value().unwrap();
841842let blob = blob_value.read_blob();
843let expected = Vec::from(blob);
844let address = blob.as_ptr();
845846// Converting the shared value to text would free the buffer `blob` points at.
847assert!(!text_value.read_text().is_empty());
848assert_eq!(blob, expected.as_slice(), "the text read moved the blob");
849assert_eq!(
850 other_blob_value.read_blob().as_ptr(),
851 address,
852"the text read converted the shared value"
853);
854 }
855856// 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]
862fn can_convert_all_values() {
863let mut conn = SqliteConnection::establish(":memory:").unwrap();
864865 conn.batch_execute("CREATE TABLE tests(int INTEGER, text TEXT, blob BLOB, float FLOAT)")
866 .unwrap();
867868 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();
875876let mut res = conn
877 .load(diesel::sql_query(
878"SELECT int, text, blob, float FROM tests",
879 ))
880 .unwrap();
881let row = res.next().unwrap().unwrap();
882let int_field = row.get(0).unwrap();
883let text_field = row.get(1).unwrap();
884let blob_field = row.get(2).unwrap();
885let float_field = row.get(3).unwrap();
886887let mut int_value = int_field.value().unwrap();
888assert_eq!(int_value.read_integer(), 42);
889let mut int_value = int_field.value().unwrap();
890assert_eq!(int_value.read_long(), 42);
891let mut int_value = int_field.value().unwrap();
892assert_eq!(int_value.read_double(), 42.0);
893let mut int_value = int_field.value().unwrap();
894assert_eq!(int_value.read_text(), "42");
895let mut int_value = int_field.value().unwrap();
896assert_eq!(int_value.read_blob(), b"42");
897898let mut text_value = text_field.value().unwrap();
899assert_eq!(text_value.read_integer(), 0);
900let mut text_value = text_field.value().unwrap();
901assert_eq!(text_value.read_long(), 0);
902let mut text_value = text_field.value().unwrap();
903assert_eq!(text_value.read_double(), 0.0);
904let mut text_value = text_field.value().unwrap();
905assert_eq!(text_value.read_text(), "foo");
906let mut text_value = text_field.value().unwrap();
907assert_eq!(text_value.read_blob(), b"foo");
908909let mut blob_value = blob_field.value().unwrap();
910assert_eq!(blob_value.read_integer(), 0);
911let mut blob_value = blob_field.value().unwrap();
912assert_eq!(blob_value.read_long(), 0);
913let mut blob_value = blob_field.value().unwrap();
914assert_eq!(blob_value.read_double(), 0.0);
915let mut blob_value = blob_field.value().unwrap();
916assert_eq!(blob_value.read_text(), "\u{fffd}\u{fffd}\u{fffd}"); // ���
917let mut blob_value = blob_field.value().unwrap();
918assert_eq!(blob_value.read_blob(), [0xFF, 0xFE, 0xFD]);
919920let mut float_value = float_field.value().unwrap();
921assert_eq!(float_value.read_integer(), 3);
922let mut float_value = float_field.value().unwrap();
923assert_eq!(float_value.read_long(), 3);
924let mut float_value = float_field.value().unwrap();
925assert_eq!(float_value.read_double(), 3.14);
926let mut float_value = float_field.value().unwrap();
927assert_eq!(float_value.read_text(), "3.14");
928let mut float_value = float_field.value().unwrap();
929assert_eq!(float_value.read_blob(), b"3.14");
930 }
931}