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 Item = B;
130 type Error = Error;
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 Item = Row<'stmt>;
212 type Error = Error;
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 let sv = super::ffi::sqlite3_column_value(self.stmt.stmt.ptr(), idx as std::ffi::c_int);
361 Ok(if sv.is_null() {
362 None
363 } else {
364 super::ffi::sqlite3_value_pointer(sv, ptr_type.as_ptr())
365 .cast::<T>()
366 .as_ref()
367 })
368 }
369}
370
371impl<'stmt> AsRef<Statement<'stmt>> for Row<'stmt> {
372 fn as_ref(&self) -> &Statement<'stmt> {
373 self.stmt
374 }
375}
376
377impl std::fmt::Debug for Row<'_> {
381 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
382 let mut dm = f.debug_map();
383 for c in 0..self.stmt.column_count() {
384 let name = self.stmt.column_name(c).expect("valid column index");
385 dm.key(&name);
386 let value = self.get_ref(c);
387 match value {
388 Ok(value) => {
389 let dt = value.data_type();
390 match value {
391 ValueRef::Null => {
392 dm.value(&(dt, ()));
393 }
394 ValueRef::Integer(i) => {
395 dm.value(&(dt, i));
396 }
397 ValueRef::Real(f) => {
398 dm.value(&(dt, f));
399 }
400 ValueRef::Text(s) => {
401 dm.value(&(dt, String::from_utf8_lossy(s)));
402 }
403 ValueRef::Blob(b) => {
404 dm.value(&(dt, b.len()));
405 }
406 }
407 }
408 Err(ref _err) => {
409 dm.value(&value);
410 }
411 }
412 }
413 dm.finish()
414 }
415}
416
417mod sealed {
418 pub trait Sealed {}
421 impl Sealed for usize {}
422 impl Sealed for &str {}
423}
424
425pub trait RowIndex: sealed::Sealed {
429 fn idx(&self, stmt: &Statement<'_>) -> Result<usize>;
432}
433
434impl RowIndex for usize {
435 #[inline]
436 fn idx(&self, stmt: &Statement<'_>) -> Result<usize> {
437 if *self >= stmt.column_count() {
438 Err(Error::InvalidColumnIndex(*self))
439 } else {
440 Ok(*self)
441 }
442 }
443}
444
445impl RowIndex for &'_ str {
446 #[inline]
447 fn idx(&self, stmt: &Statement<'_>) -> Result<usize> {
448 stmt.column_index(self)
449 }
450}
451
452macro_rules! tuple_try_from_row {
453 ($($field:ident),*) => {
454 impl<'a, $($field,)*> convert::TryFrom<&'a Row<'a>> for ($($field,)*) where $($field: FromSql,)* {
455 type Error = crate::Error;
456
457 #[allow(unused_assignments, unused_variables, unused_mut)]
460 fn try_from(row: &'a Row<'a>) -> Result<Self> {
461 let mut index = 0;
462 $(
463 #[expect(non_snake_case)]
464 let $field = row.get::<_, $field>(index)?;
465 index += 1;
466 )*
467 Ok(($($field,)*))
468 }
469 }
470 }
471}
472
473macro_rules! tuples_try_from_row {
474 () => {
475 tuple_try_from_row!();
477 };
478 ($first:ident $(, $remaining:ident)*) => {
479 tuple_try_from_row!($first $(, $remaining)*);
480 tuples_try_from_row!($($remaining),*);
481 };
482}
483
484tuples_try_from_row!(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P);
485
486#[cfg(test)]
487mod tests {
488 #[cfg(all(target_family = "wasm", target_os = "unknown"))]
489 use wasm_bindgen_test::wasm_bindgen_test as test;
490
491 use crate::{Connection, Result};
492
493 #[test]
494 fn test_try_from_row_for_tuple_1() -> Result<()> {
495 use crate::ToSql;
496 use std::convert::TryFrom;
497
498 let conn = Connection::open_in_memory()?;
499 conn.execute(
500 "CREATE TABLE test (a INTEGER)",
501 crate::params_from_iter(std::iter::empty::<&dyn ToSql>()),
502 )?;
503 conn.execute("INSERT INTO test VALUES (42)", [])?;
504 let val = conn.query_row("SELECT a FROM test", [], |row| <(u32,)>::try_from(row))?;
505 assert_eq!(val, (42,));
506 let fail = conn.query_row("SELECT a FROM test", [], |row| <(u32, u32)>::try_from(row));
507 fail.unwrap_err();
508 Ok(())
509 }
510
511 #[test]
512 fn test_try_from_row_for_tuple_2() -> Result<()> {
513 use std::convert::TryFrom;
514
515 let conn = Connection::open_in_memory()?;
516 conn.execute("CREATE TABLE test (a INTEGER, b INTEGER)", [])?;
517 conn.execute("INSERT INTO test VALUES (42, 47)", [])?;
518 let val = conn.query_row("SELECT a, b FROM test", [], |row| {
519 <(u32, u32)>::try_from(row)
520 })?;
521 assert_eq!(val, (42, 47));
522 let fail = conn.query_row("SELECT a, b FROM test", [], |row| {
523 <(u32, u32, u32)>::try_from(row)
524 });
525 fail.unwrap_err();
526 Ok(())
527 }
528
529 #[test]
530 fn test_try_from_row_for_tuple_16() -> Result<()> {
531 use std::convert::TryFrom;
532
533 let create_table = "CREATE TABLE test (
534 a INTEGER,
535 b INTEGER,
536 c INTEGER,
537 d INTEGER,
538 e INTEGER,
539 f INTEGER,
540 g INTEGER,
541 h INTEGER,
542 i INTEGER,
543 j INTEGER,
544 k INTEGER,
545 l INTEGER,
546 m INTEGER,
547 n INTEGER,
548 o INTEGER,
549 p INTEGER
550 )";
551
552 let insert_values = "INSERT INTO test VALUES (
553 0,
554 1,
555 2,
556 3,
557 4,
558 5,
559 6,
560 7,
561 8,
562 9,
563 10,
564 11,
565 12,
566 13,
567 14,
568 15
569 )";
570
571 type BigTuple = (
572 u32,
573 u32,
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 );
589
590 let conn = Connection::open_in_memory()?;
591 conn.execute(create_table, [])?;
592 conn.execute(insert_values, [])?;
593 let val = conn.query_row("SELECT * FROM test", [], |row| BigTuple::try_from(row))?;
594 assert_eq!(val.0, 0);
596 assert_eq!(val.1, 1);
597 assert_eq!(val.2, 2);
598 assert_eq!(val.3, 3);
599 assert_eq!(val.4, 4);
600 assert_eq!(val.5, 5);
601 assert_eq!(val.6, 6);
602 assert_eq!(val.7, 7);
603 assert_eq!(val.8, 8);
604 assert_eq!(val.9, 9);
605 assert_eq!(val.10, 10);
606 assert_eq!(val.11, 11);
607 assert_eq!(val.12, 12);
608 assert_eq!(val.13, 13);
609 assert_eq!(val.14, 14);
610 assert_eq!(val.15, 15);
611
612 Ok(())
614 }
615
616 #[test]
617 #[cfg(feature = "bundled")]
618 fn pathological_case() -> Result<()> {
619 let conn = Connection::open_in_memory()?;
620 conn.execute_batch(
621 "CREATE TABLE foo(x);
622 CREATE TRIGGER oops BEFORE INSERT ON foo BEGIN SELECT RAISE(FAIL, 'Boom'); END;",
623 )?;
624 let mut stmt = conn.prepare("INSERT INTO foo VALUES (0) RETURNING rowid;")?;
625 {
626 let iterator_count = stmt.query_map([], |_| Ok(()))?.count();
627 assert_eq!(1, iterator_count); use fallible_streaming_iterator::FallibleStreamingIterator;
629 let fallible_iterator_count = stmt.query([])?.count().unwrap_or(0);
630 assert_eq!(0, fallible_iterator_count);
631 }
632 {
633 let iterator_last = stmt.query_map([], |_| Ok(()))?.last();
634 assert!(iterator_last.is_some()); use fallible_iterator::FallibleIterator;
636 let fallible_iterator_last = stmt.query([])?.map(|_| Ok(())).last();
637 assert!(fallible_iterator_last.is_err());
638 }
639 Ok(())
640 }
641
642 #[test]
643 fn as_ref() -> Result<()> {
644 let conn = Connection::open_in_memory()?;
645 let mut stmt = conn.prepare("SELECT 'Lisa' as name, 1 as id")?;
646 let rows = stmt.query([])?;
647 assert_eq!(rows.as_ref().unwrap().column_count(), 2);
648 Ok(())
649 }
650
651 #[test]
652 fn debug() -> Result<()> {
653 let conn = Connection::open_in_memory()?;
654 let mut stmt = conn.prepare(
655 "SELECT 'Lisa' as name, 1 as id, 3.14 as pi, X'53514C697465' as blob, NULL as void",
656 )?;
657 let mut rows = stmt.query([])?;
658 let row = rows.next()?.unwrap();
659 let s = format!("{row:?}");
660 assert_eq!(
661 s,
662 r#"{"name": (Text, "Lisa"), "id": (Integer, 1), "pi": (Real, 3.14), "blob": (Blob, 6), "void": (Null, ())}"#
663 );
664 Ok(())
665 }
666
667 #[test]
668 #[cfg(feature = "pointer")]
669 fn test_pointer() -> Result<()> {
670 use crate::ffi::fts5_api;
671 use crate::types::ToSqlOutput;
672 const PTR_TYPE: &std::ffi::CStr = c"fts5_api_ptr";
673 let p_ret: *mut fts5_api = std::ptr::null_mut();
674 let ptr = ToSqlOutput::Pointer((&p_ret as *const *mut fts5_api as _, PTR_TYPE, None));
675 let db = Connection::open_in_memory()?;
676 db.query_row("SELECT fts5(?)", [ptr], |_| Ok(()))?;
677 assert!(!p_ret.is_null());
678 Ok(())
679 }
680}