1use fallible_iterator::FallibleIterator;
2use fallible_streaming_iterator::FallibleStreamingIterator;
3use std::convert;
4
5use super::{Error, Result, Statement};
6use crate::types::{FromSql, FromSqlError, ValueRef};
7
8#[must_use = "Rows is lazy and will do nothing unless consumed"]
10pub struct Rows<'stmt> {
11 pub(crate) stmt: Option<&'stmt Statement<'stmt>>,
12 row: Option<Row<'stmt>>,
13}
14
15impl<'stmt> Rows<'stmt> {
16 #[inline]
17 fn reset(&mut self) -> Result<()> {
18 if let Some(stmt) = self.stmt.take() {
19 stmt.reset()
20 } else {
21 Ok(())
22 }
23 }
24
25 #[expect(clippy::should_implement_trait)] #[inline]
39 pub fn next(&mut self) -> Result<Option<&Row<'stmt>>> {
40 self.advance()?;
41 Ok((*self).get())
42 }
43
44 #[inline]
56 pub fn map<F, B>(self, f: F) -> Map<'stmt, F>
57 where
58 F: FnMut(&Row<'_>) -> Result<B>,
59 {
60 Map { rows: self, f }
61 }
62
63 #[inline]
66 pub fn mapped<F, B>(self, f: F) -> MappedRows<'stmt, F>
67 where
68 F: FnMut(&Row<'_>) -> Result<B>,
69 {
70 MappedRows { rows: self, map: f }
71 }
72
73 #[inline]
77 pub fn and_then<F, T, E>(self, f: F) -> AndThenRows<'stmt, F>
78 where
79 F: FnMut(&Row<'_>) -> Result<T, E>,
80 {
81 AndThenRows { rows: self, map: f }
82 }
83
84 #[must_use]
86 pub fn as_ref(&self) -> Option<&Statement<'stmt>> {
87 self.stmt
88 }
89}
90
91impl<'stmt> Rows<'stmt> {
92 #[inline]
93 pub(crate) fn new(stmt: &'stmt Statement<'stmt>) -> Self {
94 Rows {
95 stmt: Some(stmt),
96 row: None,
97 }
98 }
99
100 #[inline]
101 pub(crate) fn get_expected_row(&mut self) -> Result<&Row<'stmt>> {
102 match self.next()? {
103 Some(row) => Ok(row),
104 None => Err(Error::QueryReturnedNoRows),
105 }
106 }
107}
108
109impl Drop for Rows<'_> {
110 #[expect(unused_must_use)]
111 #[inline]
112 fn drop(&mut self) {
113 self.reset();
114 }
115}
116
117#[must_use = "iterators are lazy and do nothing unless consumed"]
120pub struct Map<'stmt, F> {
121 rows: Rows<'stmt>,
122 f: F,
123}
124
125impl<F, B> FallibleIterator for Map<'_, F>
126where
127 F: FnMut(&Row<'_>) -> Result<B>,
128{
129 type Error = Error;
130 type Item = B;
131
132 #[inline]
133 fn next(&mut self) -> Result<Option<B>> {
134 match self.rows.next()? {
135 Some(v) => Ok(Some((self.f)(v)?)),
136 None => Ok(None),
137 }
138 }
139}
140
141#[must_use = "iterators are lazy and do nothing unless consumed"]
146pub struct MappedRows<'stmt, F> {
147 rows: Rows<'stmt>,
148 map: F,
149}
150
151impl<T, F> Iterator for MappedRows<'_, F>
152where
153 F: FnMut(&Row<'_>) -> Result<T>,
154{
155 type Item = Result<T>;
156
157 #[inline]
158 fn next(&mut self) -> Option<Result<T>> {
159 let map = &mut self.map;
160 self.rows
161 .next()
162 .transpose()
163 .map(|row_result| row_result.and_then(map))
164 }
165}
166
167#[must_use = "iterators are lazy and do nothing unless consumed"]
170pub struct AndThenRows<'stmt, F> {
171 rows: Rows<'stmt>,
172 map: F,
173}
174
175impl<T, E, F> Iterator for AndThenRows<'_, F>
176where
177 E: From<Error>,
178 F: FnMut(&Row<'_>) -> Result<T, E>,
179{
180 type Item = Result<T, E>;
181
182 #[inline]
183 fn next(&mut self) -> Option<Self::Item> {
184 let map = &mut self.map;
185 self.rows
186 .next()
187 .transpose()
188 .map(|row_result| row_result.map_err(E::from).and_then(map))
189 }
190}
191
192impl<'stmt> FallibleStreamingIterator for Rows<'stmt> {
211 type Error = Error;
212 type Item = Row<'stmt>;
213
214 #[inline]
215 fn advance(&mut self) -> Result<()> {
216 if let Some(stmt) = self.stmt {
217 match stmt.step() {
218 Ok(true) => {
219 self.row = Some(Row { stmt });
220 Ok(())
221 }
222 Ok(false) => {
223 let r = self.reset();
224 self.row = None;
225 r
226 }
227 Err(e) => {
228 let _ = self.reset(); self.row = None;
230 Err(e)
231 }
232 }
233 } else {
234 self.row = None;
235 Ok(())
236 }
237 }
238
239 #[inline]
240 fn get(&self) -> Option<&Row<'stmt>> {
241 self.row.as_ref()
242 }
243}
244
245pub struct Row<'stmt> {
247 pub(crate) stmt: &'stmt Statement<'stmt>,
248}
249
250impl Row<'_> {
251 #[track_caller]
264 pub fn get_unwrap<I: RowIndex, T: FromSql>(&self, idx: I) -> T {
265 self.get(idx).unwrap()
266 }
267
268 #[track_caller]
285 pub fn get<I: RowIndex, T: FromSql>(&self, idx: I) -> Result<T> {
286 let idx = idx.idx(self.stmt)?;
287 let value = self.stmt.value_ref(idx);
288 FromSql::column_result(value).map_err(|err| match err {
289 FromSqlError::InvalidType => Error::InvalidColumnType(
290 idx,
291 self.stmt.column_name_unwrap(idx).into(),
292 value.data_type(),
293 ),
294 FromSqlError::OutOfRange(i) => Error::IntegralValueOutOfRange(idx, i),
295 FromSqlError::Utf8Error(err) => Error::Utf8Error(idx, err),
296 FromSqlError::Other(err) => {
297 Error::FromSqlConversionFailure(idx, value.data_type(), err)
298 }
299 FromSqlError::InvalidBlobSize { .. } => {
300 Error::FromSqlConversionFailure(idx, value.data_type(), Box::new(err))
301 }
302 })
303 }
304
305 pub fn get_ref<I: RowIndex>(&self, idx: I) -> Result<ValueRef<'_>> {
321 let idx = idx.idx(self.stmt)?;
322 let val_ref = self.stmt.value_ref(idx);
326 Ok(val_ref)
327 }
328
329 #[track_caller]
345 pub fn get_ref_unwrap<I: RowIndex>(&self, idx: I) -> ValueRef<'_> {
346 self.get_ref(idx).unwrap()
347 }
348
349 #[cfg(feature = "pointer")]
353 pub unsafe fn get_pointer<I: RowIndex, T: 'static>(
354 &self,
355 idx: I,
356 ptr_type: &'static std::ffi::CStr,
357 ) -> Result<Option<&T>> {
358 let idx = idx.idx(self.stmt)?;
359 debug_assert_eq!(self.stmt.stmt.column_type(idx), super::ffi::SQLITE_NULL);
360 unsafe {
361 let sv = super::ffi::sqlite3_column_value(self.stmt.stmt.ptr(), idx as std::ffi::c_int);
362 Ok(if sv.is_null() {
363 None
364 } else {
365 super::ffi::sqlite3_value_pointer(sv, ptr_type.as_ptr())
366 .cast::<T>()
367 .as_ref()
368 })
369 }
370 }
371}
372
373impl<'stmt> AsRef<Statement<'stmt>> for Row<'stmt> {
374 fn as_ref(&self) -> &Statement<'stmt> {
375 self.stmt
376 }
377}
378
379impl std::fmt::Debug for Row<'_> {
383 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
384 let mut dm = f.debug_map();
385 for c in 0..self.stmt.column_count() {
386 let name = self.stmt.column_name(c).expect("valid column index");
387 dm.key(&name);
388 let value = self.get_ref(c);
389 match value {
390 Ok(value) => {
391 let dt = value.data_type();
392 match value {
393 ValueRef::Null => {
394 dm.value(&(dt, ()));
395 }
396 ValueRef::Integer(i) => {
397 dm.value(&(dt, i));
398 }
399 ValueRef::Real(f) => {
400 dm.value(&(dt, f));
401 }
402 ValueRef::Text(s) => {
403 dm.value(&(dt, String::from_utf8_lossy(s)));
404 }
405 ValueRef::Blob(b) => {
406 dm.value(&(dt, b.len()));
407 }
408 }
409 }
410 Err(ref _err) => {
411 dm.value(&value);
412 }
413 }
414 }
415 dm.finish()
416 }
417}
418
419mod sealed {
420 pub trait Sealed {}
423 impl Sealed for usize {}
424 impl Sealed for &str {}
425}
426
427pub trait RowIndex: sealed::Sealed + Copy {
431 fn idx(&self, stmt: &Statement<'_>) -> Result<usize>;
434}
435
436impl RowIndex for usize {
437 #[inline]
438 fn idx(&self, stmt: &Statement<'_>) -> Result<usize> {
439 if *self >= stmt.column_count() {
440 Err(Error::InvalidColumnIndex(*self))
441 } else {
442 Ok(*self)
443 }
444 }
445}
446
447impl RowIndex for &'_ str {
448 #[inline]
449 fn idx(&self, stmt: &Statement<'_>) -> Result<usize> {
450 stmt.column_index(self)
451 }
452}
453
454macro_rules! tuple_try_from_row {
455 ($($field:ident),*) => {
456 impl<'a, $($field,)*> convert::TryFrom<&'a Row<'a>> for ($($field,)*) where $($field: FromSql,)* {
457 type Error = crate::Error;
458
459 #[allow(unused_assignments, unused_variables, unused_mut)]
462 fn try_from(row: &'a Row<'a>) -> Result<Self> {
463 let mut index = 0;
464 $(
465 #[expect(non_snake_case)]
466 let $field = row.get::<_, $field>(index)?;
467 index += 1;
468 )*
469 Ok(($($field,)*))
470 }
471 }
472 }
473}
474
475macro_rules! tuples_try_from_row {
476 () => {
477 tuple_try_from_row!();
479 };
480 ($first:ident $(, $remaining:ident)*) => {
481 tuple_try_from_row!($first $(, $remaining)*);
482 tuples_try_from_row!($($remaining),*);
483 };
484}
485
486tuples_try_from_row!(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P);
487
488#[cfg(all(test, not(miri)))]
489mod tests {
490 #[cfg(all(target_family = "wasm", target_os = "unknown"))]
491 use wasm_bindgen_test::wasm_bindgen_test as test;
492
493 use crate::{Connection, Result};
494
495 #[test]
496 fn test_try_from_row_for_tuple_1() -> Result<()> {
497 use crate::ToSql;
498 use std::convert::TryFrom as _;
499
500 let conn = Connection::open_in_memory()?;
501 conn.execute(
502 "CREATE TABLE test (a INTEGER)",
503 crate::params_from_iter(std::iter::empty::<&dyn ToSql>()),
504 )?;
505 conn.execute("INSERT INTO test VALUES (42)", [])?;
506 let val = conn.query_row("SELECT a FROM test", [], |row| <(u32,)>::try_from(row))?;
507 assert_eq!(val, (42,));
508 let fail = conn.query_row("SELECT a FROM test", [], |row| <(u32, u32)>::try_from(row));
509 fail.unwrap_err();
510 Ok(())
511 }
512
513 #[test]
514 fn test_try_from_row_for_tuple_2() -> Result<()> {
515 use std::convert::TryFrom as _;
516
517 let conn = Connection::open_in_memory()?;
518 conn.execute("CREATE TABLE test (a INTEGER, b INTEGER)", [])?;
519 conn.execute("INSERT INTO test VALUES (42, 47)", [])?;
520 let val = conn.query_row("SELECT a, b FROM test", [], |row| {
521 <(u32, u32)>::try_from(row)
522 })?;
523 assert_eq!(val, (42, 47));
524 let fail = conn.query_row("SELECT a, b FROM test", [], |row| {
525 <(u32, u32, u32)>::try_from(row)
526 });
527 fail.unwrap_err();
528 Ok(())
529 }
530
531 #[test]
532 fn test_try_from_row_for_tuple_16() -> Result<()> {
533 use std::convert::TryFrom as _;
534
535 let create_table = "CREATE TABLE test (
536 a INTEGER,
537 b INTEGER,
538 c INTEGER,
539 d INTEGER,
540 e INTEGER,
541 f INTEGER,
542 g INTEGER,
543 h INTEGER,
544 i INTEGER,
545 j INTEGER,
546 k INTEGER,
547 l INTEGER,
548 m INTEGER,
549 n INTEGER,
550 o INTEGER,
551 p INTEGER
552 )";
553
554 let insert_values = "INSERT INTO test VALUES (
555 0,
556 1,
557 2,
558 3,
559 4,
560 5,
561 6,
562 7,
563 8,
564 9,
565 10,
566 11,
567 12,
568 13,
569 14,
570 15
571 )";
572
573 type BigTuple = (
574 u32,
575 u32,
576 u32,
577 u32,
578 u32,
579 u32,
580 u32,
581 u32,
582 u32,
583 u32,
584 u32,
585 u32,
586 u32,
587 u32,
588 u32,
589 u32,
590 );
591
592 let conn = Connection::open_in_memory()?;
593 conn.execute(create_table, [])?;
594 conn.execute(insert_values, [])?;
595 let val = conn.query_row("SELECT * FROM test", [], |row| BigTuple::try_from(row))?;
596 assert_eq!(val.0, 0);
598 assert_eq!(val.1, 1);
599 assert_eq!(val.2, 2);
600 assert_eq!(val.3, 3);
601 assert_eq!(val.4, 4);
602 assert_eq!(val.5, 5);
603 assert_eq!(val.6, 6);
604 assert_eq!(val.7, 7);
605 assert_eq!(val.8, 8);
606 assert_eq!(val.9, 9);
607 assert_eq!(val.10, 10);
608 assert_eq!(val.11, 11);
609 assert_eq!(val.12, 12);
610 assert_eq!(val.13, 13);
611 assert_eq!(val.14, 14);
612 assert_eq!(val.15, 15);
613
614 Ok(())
616 }
617
618 #[test]
619 #[cfg(feature = "bundled")]
620 fn pathological_case() -> Result<()> {
621 let conn = Connection::open_in_memory()?;
622 conn.execute_batch(
623 "CREATE TABLE foo(x);
624 CREATE TRIGGER oops BEFORE INSERT ON foo BEGIN SELECT RAISE(FAIL, 'Boom'); END;",
625 )?;
626 let mut stmt = conn.prepare("INSERT INTO foo VALUES (0) RETURNING rowid;")?;
627 {
628 let iterator_count = stmt.query_map([], |_| Ok(()))?.count();
629 assert_eq!(1, iterator_count); use fallible_streaming_iterator::FallibleStreamingIterator as _;
631 let fallible_iterator_count = stmt.query([])?.count().unwrap_or(0);
632 assert_eq!(0, fallible_iterator_count);
633 }
634 {
635 let iterator_last = stmt.query_map([], |_| Ok(()))?.last();
636 assert!(iterator_last.is_some()); use fallible_iterator::FallibleIterator as _;
638 let fallible_iterator_last = stmt.query([])?.map(|_| Ok(())).last();
639 assert!(fallible_iterator_last.is_err());
640 }
641 Ok(())
642 }
643
644 #[test]
645 fn as_ref() -> Result<()> {
646 let conn = Connection::open_in_memory()?;
647 let mut stmt = conn.prepare("SELECT 'Lisa' as name, 1 as id")?;
648 let rows = stmt.query([])?;
649 assert_eq!(rows.as_ref().unwrap().column_count(), 2);
650 Ok(())
651 }
652
653 #[test]
654 fn debug() -> Result<()> {
655 let conn = Connection::open_in_memory()?;
656 let mut stmt = conn.prepare(
657 "SELECT 'Lisa' as name, 1 as id, 3.14 as pi, X'53514C697465' as blob, NULL as void",
658 )?;
659 let mut rows = stmt.query([])?;
660 let row = rows.next()?.unwrap();
661 let s = format!("{row:?}");
662 assert_eq!(
663 s,
664 r#"{"name": (Text, "Lisa"), "id": (Integer, 1), "pi": (Real, 3.14), "blob": (Blob, 6), "void": (Null, ())}"#
665 );
666 Ok(())
667 }
668
669 #[test]
670 #[cfg(feature = "pointer")]
671 fn test_pointer() -> Result<()> {
672 use crate::ffi::fts5_api;
673 use crate::types::ToSqlOutput;
674 const PTR_TYPE: &std::ffi::CStr = c"fts5_api_ptr";
675 let p_ret: *mut fts5_api = std::ptr::null_mut();
676 let ptr = ToSqlOutput::Pointer((&p_ret as *const *mut fts5_api as _, PTR_TYPE, None));
677 let db = Connection::open_in_memory()?;
678 db.query_row("SELECT fts5(?)", [ptr], |_| Ok(()))?;
679 assert!(!p_ret.is_null());
680 Ok(())
681 }
682}