1#![allow(unsafe_code)] #[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 std::cell::Ref;
9use std::ptr::NonNull;
10use std::{slice, str};
11
12use crate::sqlite::SqliteType;
13use crate::QueryResult;
14
15use super::owned_row::OwnedSqliteRow;
16use super::row::PrivateSqliteRow;
17
18#[allow(missing_debug_implementations, missing_copy_implementations)]
24pub struct SqliteValue<'row, 'stmt, 'query> {
25 owner: ValueOwner<'row, 'stmt, 'query>,
29 value: NonNull<ffi::sqlite3_value>,
37 string_ref: Option<Box<str>>,
45 initial_type: SqliteType,
48 converted: Option<OwnedSqliteValue>,
51}
52
53enum ValueOwner<'row, 'stmt, 'query> {
54 Row(Ref<'row, PrivateSqliteRow<'stmt, 'query>>),
57 NonRow(Option<NonNull<ffi::sqlite3>>),
60}
61
62#[derive(Copy, 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(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
90unsafe 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 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 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 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 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 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 fn reports_out_of_memory(&self) -> bool {
184 let Some(connection) = self.connection() else {
185 return false;
186 };
187 unsafe { ffi::sqlite3_errcode(connection.as_ptr()) == ffi::SQLITE_NOMEM }
190 }
191
192 fn value_to_read(&mut self, wanted: Representation) -> NonNull<ffi::sqlite3_value> {
195 let converts_in_place = 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 let copy = unsafe { ffi::sqlite3_value_dup(self.value.as_ptr()) };
208 let Some(copy) = NonNull::new(copy) else {
210 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 unsafe {
226 if ffi::sqlite3_value_text(value.as_ptr()).is_null() {
230 allocation_failed(self.reports_out_of_memory(), Representation::Text.target());
232 }
233 let len = ffi::sqlite3_value_bytes(value.as_ptr());
234 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 if str::from_utf8(self.as_byte_string()).is_err() {
256 let lossy = 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 pub fn read_text(&mut self) -> &str {
287 self.parse_string(|s| s)
289 }
290
291 pub fn read_blob(&mut self) -> &[u8] {
311 let value = self.value_to_read(Representation::Blob);
313 unsafe {
316 let initial_blob_len = if matches!(self.initial_type, SqliteType::Binary) {
318 Some(ffi::sqlite3_value_bytes(value.as_ptr()))
319 } else {
320 None
321 };
322 if ffi::sqlite3_value_blob(value.as_ptr()).is_null() {
326 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 && ((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 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 pub fn read_integer(&mut self) -> i32 {
361 unsafe { ffi::sqlite3_value_int(self.value.as_ptr()) }
362 }
363
364 pub fn read_long(&mut self) -> i64 {
374 unsafe { ffi::sqlite3_value_int64(self.value.as_ptr()) }
375 }
376
377 pub fn read_double(&mut self) -> f64 {
387 unsafe { ffi::sqlite3_value_double(self.value.as_ptr()) }
388 }
389
390 pub fn value_type(&self) -> Option<SqliteType> {
392 unsafe { value_type_of(self.value) }
394 }
395}
396
397unsafe fn value_type_of(value: NonNull<ffi::sqlite3_value>) -> Option<SqliteType> {
403 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 _ => 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 pub(super) fn copy_from_ptr(
426 ptr: NonNull<ffi::sqlite3_value>,
427 ) -> QueryResult<Option<OwnedSqliteValue>> {
428 let tpe = unsafe { ffi::sqlite3_value_type(ptr.as_ptr()) };
431 if ffi::SQLITE_NULL == tpe {
432 return Ok(None);
433 }
434 let value = unsafe { ffi::sqlite3_value_dup(ptr.as_ptr()) };
437 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 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 use crate::sql_types::{Blob, Double, Int4, Text};
457 use crate::*;
458
459 #[cfg(not(all(target_family = "wasm", target_os = "unknown")))]
460 mod allocation_failure {
461 use super::super::SqliteValue;
462 use crate::connection::{LoadConnection, SimpleConnection};
463 use crate::deserialize::{self, FromSql};
464 use crate::prelude::*;
465 use crate::row::{Field, Row};
466 use crate::sql_types::Binary;
467 use crate::sqlite::connection::oom_test_support::{
468 panic_message, run_in_child, with_heap_limit,
469 };
470 use crate::sqlite::{Sqlite, SqliteConnection};
471
472 const VALUE_LEN: usize = 1_048_576;
473
474 crate::table! {
475 oom_blob (id) {
476 id -> Integer,
477 value -> Binary,
478 }
479 }
480
481 crate::table! {
482 oom_text (id) {
483 id -> Integer,
484 value -> Text,
485 }
486 }
487
488 crate::table! {
489 oom_zeroblob (id) {
490 id -> Integer,
491 len -> Integer,
492 }
493 }
494
495 crate::define_sql_function! {
496 fn read_blob_under_pressure(value: Binary) -> Integer;
497 }
498
499 struct BlobUnderPressure(String);
502
503 impl FromSql<Binary, Sqlite> for BlobUnderPressure {
504 fn from_sql(mut value: SqliteValue<'_, '_, '_>) -> deserialize::Result<Self> {
505 let payload = std::panic::catch_unwind(core::panic::AssertUnwindSafe(|| {
506 without_spare_memory(|| core::hint::black_box(value.read_blob().len()));
507 }))
508 .expect_err("the blob read did not panic");
509 Ok(Self(panic_message(&*payload).to_string()))
510 }
511 }
512
513 impl crate::deserialize::Queryable<Binary, Sqlite> for BlobUnderPressure {
514 type Row = Self;
515
516 fn build(row: Self::Row) -> deserialize::Result<Self> {
517 Ok(row)
518 }
519 }
520
521 fn without_spare_memory<R>(f: impl FnOnce() -> R) -> R {
523 with_heap_limit(0, f)
524 }
525
526 fn expect_panic(f: impl FnOnce() + core::panic::UnwindSafe, expected: &str) {
527 let payload = std::panic::catch_unwind(f).expect_err("the read did not panic");
528 let message = panic_message(&*payload);
529 assert!(
530 message.contains(expected),
531 "unexpected panic message: {message}"
532 );
533 }
534
535 fn blob_connection(rows: i32) -> SqliteConnection {
536 let mut conn = SqliteConnection::establish(":memory:").unwrap();
537 conn.batch_execute(
539 "CREATE TABLE oom_blob (id INTEGER PRIMARY KEY, value BLOB NOT NULL)",
540 )
541 .unwrap();
542 for id in 1..=rows {
543 crate::insert_into(oom_blob::table)
544 .values((
545 oom_blob::id.eq(id),
546 oom_blob::value.eq(vec![b'x'; VALUE_LEN]),
547 ))
548 .execute(&mut conn)
549 .unwrap();
550 }
551 conn
552 }
553
554 fn utf16_text_connection(rows: i32) -> SqliteConnection {
555 let mut conn = SqliteConnection::establish(":memory:").unwrap();
556 conn.batch_execute(
558 "PRAGMA encoding = 'UTF-16le';
559 CREATE TABLE oom_text (id INTEGER PRIMARY KEY, value TEXT NOT NULL)",
560 )
561 .unwrap();
562 for id in 1..=rows {
563 crate::insert_into(oom_text::table)
564 .values((
565 oom_text::id.eq(id),
566 oom_text::value.eq("x".repeat(VALUE_LEN)),
567 ))
568 .execute(&mut conn)
569 .unwrap();
570 }
571 conn
572 }
573
574 #[test]
575 fn text_read_panics_when_conversion_fails() {
576 run_in_child(|| {
577 let mut conn = utf16_text_connection(1);
578 let mut rows = conn.load(oom_text::table.select(oom_text::value)).unwrap();
579 let row = rows.next().unwrap().unwrap();
580 let field = row.get(0).unwrap();
581 let mut value = field.value().unwrap();
582
583 expect_panic(
584 core::panic::AssertUnwindSafe(|| {
585 without_spare_memory(|| {
586 core::hint::black_box(value.read_text().len());
587 });
588 }),
589 "SQLite ran out of memory while reading a value as text",
590 );
591 });
592 }
593
594 #[test]
595 fn duplicated_text_read_panics_when_conversion_fails() {
596 run_in_child(|| {
597 let mut conn = utf16_text_connection(2);
598 let mut rows = conn.load(oom_text::table.select(oom_text::value)).unwrap();
599 let first = rows.next().unwrap().unwrap();
600 let _second = rows.next().unwrap().unwrap();
603 let field = first.get(0).unwrap();
604 let mut value = field.value().unwrap();
605
606 expect_panic(
607 core::panic::AssertUnwindSafe(|| {
608 without_spare_memory(|| {
609 core::hint::black_box(value.read_text().len());
610 });
611 }),
612 "SQLite failed to allocate memory while reading a value as text",
613 );
614 });
615 }
616
617 #[test]
618 fn blob_read_as_text_panics_when_the_copy_fails() {
619 run_in_child(|| {
620 let mut conn = blob_connection(1);
621 let mut rows = conn.load(oom_blob::table.select(oom_blob::value)).unwrap();
622 let row = rows.next().unwrap().unwrap();
623 let field = row.get(0).unwrap();
624 let mut value = field.value().unwrap();
625
626 expect_panic(
627 core::panic::AssertUnwindSafe(|| {
628 without_spare_memory(|| {
629 core::hint::black_box(value.read_text().len());
630 });
631 }),
632 "SQLite failed to allocate memory while reading a value as text",
633 );
634 });
635 }
636
637 #[test]
638 fn text_read_as_blob_panics_when_the_copy_fails() {
639 run_in_child(|| {
640 let mut conn = utf16_text_connection(1);
641 let mut rows = conn.load(oom_text::table.select(oom_text::value)).unwrap();
642 let row = rows.next().unwrap().unwrap();
643 let field = row.get(0).unwrap();
644 let mut value = field.value().unwrap();
645
646 expect_panic(
647 core::panic::AssertUnwindSafe(|| {
648 without_spare_memory(|| {
649 core::hint::black_box(value.read_blob().len());
650 });
651 }),
652 "SQLite failed to allocate memory while reading a value as a blob",
653 );
654 });
655 }
656
657 #[test]
658 fn blob_read_keeps_utf16_text_bytes_across_a_text_read() {
659 let mut conn = utf16_text_connection(1);
660 let mut rows = conn.load(oom_text::table.select(oom_text::value)).unwrap();
661 let row = rows.next().unwrap().unwrap();
662 let field = row.get(0).unwrap();
663 let mut blob_value = field.value().unwrap();
664 let mut text_value = field.value().unwrap();
665
666 let blob = blob_value.read_blob();
667 assert_eq!(blob.len(), 2 * VALUE_LEN);
668 let (pairs, rest) = blob.as_chunks::<2>();
669 assert!(
670 pairs.iter().all(|pair| pair == b"x\0") && rest.is_empty(),
671 "SQLite blob content changed"
672 );
673
674 assert_eq!(text_value.read_text().len(), VALUE_LEN);
676 let (pairs, rest) = blob.as_chunks::<2>();
677 assert!(
678 pairs.iter().all(|pair| pair == b"x\0") && rest.is_empty(),
679 "the text read invalidated the blob bytes"
680 );
681 }
682
683 #[test]
684 fn zeroblob_read_panics_when_expansion_fails() {
685 run_in_child(|| {
686 let mut conn = SqliteConnection::establish(":memory:").unwrap();
687 conn.batch_execute(
689 "CREATE TABLE oom_zeroblob (id INTEGER PRIMARY KEY, len INTEGER NOT NULL)",
690 )
691 .unwrap();
692 crate::insert_into(oom_zeroblob::table)
693 .values((
694 oom_zeroblob::id.eq(1),
695 oom_zeroblob::len.eq(i32::try_from(VALUE_LEN).unwrap()),
696 ))
697 .execute(&mut conn)
698 .unwrap();
699 let observed = std::sync::Arc::new(core::sync::atomic::AtomicBool::new(false));
700 let callback_observed = std::sync::Arc::clone(&observed);
701 read_blob_under_pressure_utils::register_impl(
702 &mut conn,
703 move |value: BlobUnderPressure| {
704 assert!(
705 value.0.contains(
706 "SQLite ran out of memory while reading a value as a blob"
707 ),
708 "unexpected panic message: {}",
709 value.0
710 );
711 callback_observed.store(true, core::sync::atomic::Ordering::Relaxed);
712 1
713 },
714 )
715 .unwrap();
716
717 let result = oom_zeroblob::table
720 .select(read_blob_under_pressure(crate::dsl::sql::<Binary>(
721 "zeroblob(len)",
722 )))
723 .get_result::<i32>(&mut conn);
724 assert!(result.is_err(), "the blob read did not fail the statement");
725 assert!(
726 observed.load(core::sync::atomic::Ordering::Relaxed),
727 "the function did not read its argument"
728 );
729 });
730 }
731
732 #[test]
733 fn row_duplication_reports_value_duplication_failure() {
734 run_in_child(|| {
735 let mut conn = blob_connection(2);
736 let mut rows = conn.load(oom_blob::table.select(oom_blob::value)).unwrap();
737 let _first = rows.next().unwrap().unwrap();
739
740 let error = match without_spare_memory(|| rows.next()) {
741 Some(Err(e)) => e,
742 Some(Ok(_)) => panic!("the row duplication did not fail"),
743 None => panic!("the iterator ended instead of copying the row"),
744 };
745 assert!(
746 error
747 .to_string()
748 .contains("SQLite failed to allocate a duplicated value"),
749 "unexpected error: {error}"
750 );
751 });
752 }
753 }
754
755 crate::table! {
756 empty_values (id) {
757 id -> Integer,
758 text -> Text,
759 blob -> Binary,
760 zero_blob -> Binary,
761 }
762 }
763
764 #[diesel_test_helper::test]
765 fn can_read_empty_values_as_empty_blob() {
766 use crate::prelude::*;
767 let mut conn = SqliteConnection::establish(":memory:").unwrap();
768 conn.batch_execute(
771 "CREATE TABLE empty_values (id INTEGER PRIMARY KEY, text TEXT, blob BLOB, zero_blob BLOB);
772 INSERT INTO empty_values (id, text, blob, zero_blob) VALUES (1, '', '', X'');",
773 )
774 .unwrap();
775
776 let loaded = crate::select(crate::dsl::sql::<crate::sql_types::Binary>("''"))
778 .get_result::<Vec<u8>>(&mut conn)
779 .unwrap();
780 assert!(loaded.is_empty());
781
782 let mut rows = conn
783 .load(empty_values::table.select((
784 empty_values::text,
785 empty_values::blob,
786 empty_values::zero_blob,
787 )))
788 .unwrap();
789 let row = rows.next().unwrap().unwrap();
790 let text_field = row.get(0).unwrap();
791 let blob_field = row.get(1).unwrap();
792 let zero_blob_field = row.get(2).unwrap();
793
794 let mut text_value = text_field.value().unwrap();
795 assert_eq!(text_value.read_text(), "");
796 let mut text_value = text_field.value().unwrap();
797 assert_eq!(text_value.read_blob(), b"");
798
799 let mut blob_value = blob_field.value().unwrap();
800 assert_eq!(blob_value.value_type(), Some(super::SqliteType::Text));
801 assert_eq!(blob_value.read_blob(), b"");
802
803 let mut zero_blob_value = zero_blob_field.value().unwrap();
805 assert_eq!(
806 zero_blob_value.value_type(),
807 Some(super::SqliteType::Binary)
808 );
809 assert_eq!(zero_blob_value.read_blob(), b"");
810 }
811
812 #[diesel_test_helper::test]
813 fn blob_bytes_survive_a_text_read_of_the_same_value() {
814 use crate::prelude::*;
815 let mut conn = SqliteConnection::establish(":memory:").unwrap();
816 let mut rows = conn
819 .load(crate::select(crate::dsl::sql::<crate::sql_types::Binary>(
820 "randomblob(1048576)",
821 )))
822 .unwrap();
823 let row = rows.next().unwrap().unwrap();
824 let field = row.get(0).unwrap();
825 let mut blob_value = field.value().unwrap();
826 let mut text_value = field.value().unwrap();
827 let mut other_blob_value = field.value().unwrap();
828
829 let blob = blob_value.read_blob();
830 let expected = Vec::from(blob);
831 let address = blob.as_ptr();
832
833 assert!(!text_value.read_text().is_empty());
835 assert_eq!(blob, expected.as_slice(), "the text read moved the blob");
836 assert_eq!(
837 other_blob_value.read_blob().as_ptr(),
838 address,
839 "the text read converted the shared value"
840 );
841 }
842
843 #[expect(clippy::approx_constant)] #[diesel_test_helper::test]
845 fn can_convert_all_values() {
846 let mut conn = SqliteConnection::establish(":memory:").unwrap();
847
848 conn.batch_execute("CREATE TABLE tests(int INTEGER, text TEXT, blob BLOB, float FLOAT)")
849 .unwrap();
850
851 diesel::sql_query("INSERT INTO tests(int, text, blob, float) VALUES(?, ?, ?, ?)")
852 .bind::<Int4, _>(42)
853 .bind::<Text, _>("foo")
854 .bind::<Blob, _>([0xFF_u8, 0xFE, 0xFD])
855 .bind::<Double, _>(3.14)
856 .execute(&mut conn)
857 .unwrap();
858
859 let mut res = conn
860 .load(diesel::sql_query(
861 "SELECT int, text, blob, float FROM tests",
862 ))
863 .unwrap();
864 let row = res.next().unwrap().unwrap();
865 let int_field = row.get(0).unwrap();
866 let text_field = row.get(1).unwrap();
867 let blob_field = row.get(2).unwrap();
868 let float_field = row.get(3).unwrap();
869
870 let mut int_value = int_field.value().unwrap();
871 assert_eq!(int_value.read_integer(), 42);
872 let mut int_value = int_field.value().unwrap();
873 assert_eq!(int_value.read_long(), 42);
874 let mut int_value = int_field.value().unwrap();
875 assert_eq!(int_value.read_double(), 42.0);
876 let mut int_value = int_field.value().unwrap();
877 assert_eq!(int_value.read_text(), "42");
878 let mut int_value = int_field.value().unwrap();
879 assert_eq!(int_value.read_blob(), b"42");
880
881 let mut text_value = text_field.value().unwrap();
882 assert_eq!(text_value.read_integer(), 0);
883 let mut text_value = text_field.value().unwrap();
884 assert_eq!(text_value.read_long(), 0);
885 let mut text_value = text_field.value().unwrap();
886 assert_eq!(text_value.read_double(), 0.0);
887 let mut text_value = text_field.value().unwrap();
888 assert_eq!(text_value.read_text(), "foo");
889 let mut text_value = text_field.value().unwrap();
890 assert_eq!(text_value.read_blob(), b"foo");
891
892 let mut blob_value = blob_field.value().unwrap();
893 assert_eq!(blob_value.read_integer(), 0);
894 let mut blob_value = blob_field.value().unwrap();
895 assert_eq!(blob_value.read_long(), 0);
896 let mut blob_value = blob_field.value().unwrap();
897 assert_eq!(blob_value.read_double(), 0.0);
898 let mut blob_value = blob_field.value().unwrap();
899 assert_eq!(blob_value.read_text(), "\u{fffd}\u{fffd}\u{fffd}"); let mut blob_value = blob_field.value().unwrap();
901 assert_eq!(blob_value.read_blob(), [0xFF, 0xFE, 0xFD]);
902
903 let mut float_value = float_field.value().unwrap();
904 assert_eq!(float_value.read_integer(), 3);
905 let mut float_value = float_field.value().unwrap();
906 assert_eq!(float_value.read_long(), 3);
907 let mut float_value = float_field.value().unwrap();
908 assert_eq!(float_value.read_double(), 3.14);
909 let mut float_value = float_field.value().unwrap();
910 assert_eq!(float_value.read_text(), "3.14");
911 let mut float_value = float_field.value().unwrap();
912 assert_eq!(float_value.read_blob(), b"3.14");
913 }
914}