diff options
Diffstat (limited to 'src/row.rs')
-rw-r--r-- | src/row.rs | 223 |
1 files changed, 106 insertions, 117 deletions
@@ -13,6 +13,7 @@ pub struct Rows<'stmt> { } impl<'stmt> Rows<'stmt> { + #[inline] fn reset(&mut self) { if let Some(stmt) = self.stmt.take() { stmt.reset(); @@ -28,9 +29,11 @@ impl<'stmt> Rows<'stmt> { /// This interface is not compatible with Rust's `Iterator` trait, because /// the lifetime of the returned row is tied to the lifetime of `self`. /// This is a fallible "streaming iterator". For a more natural interface, - /// consider using `query_map` or `query_and_then` instead, which + /// consider using [`query_map`](crate::Statement::query_map) or + /// [`query_and_then`](crate::Statement::query_and_then) instead, which /// return types that implement `Iterator`. #[allow(clippy::should_implement_trait)] // cannot implement Iterator + #[inline] pub fn next(&mut self) -> Result<Option<&Row<'stmt>>> { self.advance()?; Ok((*self).get()) @@ -40,13 +43,14 @@ impl<'stmt> Rows<'stmt> { /// implements `FallibleIterator`. /// ```rust,no_run /// use fallible_iterator::FallibleIterator; - /// # use rusqlite::{Result, Statement, NO_PARAMS}; + /// # use rusqlite::{Result, Statement}; /// fn query(stmt: &mut Statement) -> Result<Vec<i64>> { - /// let rows = stmt.query(NO_PARAMS)?; + /// let rows = stmt.query([])?; /// rows.map(|r| r.get(0)).collect() /// } /// ``` // FIXME Hide FallibleStreamingIterator::map + #[inline] pub fn map<F, B>(self, f: F) -> Map<'stmt, F> where F: FnMut(&Row<'_>) -> Result<B>, @@ -56,6 +60,7 @@ impl<'stmt> Rows<'stmt> { /// Map over this `Rows`, converting it to a [`MappedRows`], which /// implements `Iterator`. + #[inline] pub fn mapped<F, B>(self, f: F) -> MappedRows<'stmt, F> where F: FnMut(&Row<'_>) -> Result<B>, @@ -66,15 +71,23 @@ impl<'stmt> Rows<'stmt> { /// Map over this `Rows` with a fallible function, converting it to a /// [`AndThenRows`], which implements `Iterator` (instead of /// `FallibleStreamingIterator`). + #[inline] pub fn and_then<F, T, E>(self, f: F) -> AndThenRows<'stmt, F> where F: FnMut(&Row<'_>) -> Result<T, E>, { AndThenRows { rows: self, map: f } } + + /// Give access to the underlying statement + #[must_use] + pub fn as_ref(&self) -> Option<&Statement<'stmt>> { + self.stmt + } } impl<'stmt> Rows<'stmt> { + #[inline] pub(crate) fn new(stmt: &'stmt Statement<'stmt>) -> Rows<'stmt> { Rows { stmt: Some(stmt), @@ -82,6 +95,7 @@ impl<'stmt> Rows<'stmt> { } } + #[inline] pub(crate) fn get_expected_row(&mut self) -> Result<&Row<'stmt>> { match self.next()? { Some(row) => Ok(row), @@ -91,12 +105,14 @@ impl<'stmt> Rows<'stmt> { } impl Drop for Rows<'_> { + #[inline] fn drop(&mut self) { self.reset(); } } -/// `F` is used to tranform the _streaming_ iterator into a _fallible_ iterator. +/// `F` is used to transform the _streaming_ iterator into a _fallible_ +/// iterator. #[must_use = "iterators are lazy and do nothing unless consumed"] pub struct Map<'stmt, F> { rows: Rows<'stmt>, @@ -110,6 +126,7 @@ where type Error = Error; type Item = B; + #[inline] fn next(&mut self) -> Result<Option<B>> { match self.rows.next()? { Some(v) => Ok(Some((self.f)(v)?)), @@ -120,34 +137,27 @@ where /// An iterator over the mapped resulting rows of a query. /// -/// `F` is used to tranform the _streaming_ iterator into a _standard_ iterator. +/// `F` is used to transform the _streaming_ iterator into a _standard_ +/// iterator. #[must_use = "iterators are lazy and do nothing unless consumed"] pub struct MappedRows<'stmt, F> { rows: Rows<'stmt>, map: F, } -impl<'stmt, T, F> MappedRows<'stmt, F> -where - F: FnMut(&Row<'_>) -> Result<T>, -{ - pub(crate) fn new(rows: Rows<'stmt>, f: F) -> MappedRows<'stmt, F> { - MappedRows { rows, map: f } - } -} - impl<T, F> Iterator for MappedRows<'_, F> where F: FnMut(&Row<'_>) -> Result<T>, { type Item = Result<T>; + #[inline] fn next(&mut self) -> Option<Result<T>> { let map = &mut self.map; self.rows .next() .transpose() - .map(|row_result| row_result.and_then(|row| (map)(&row))) + .map(|row_result| row_result.and_then(map)) } } @@ -159,15 +169,6 @@ pub struct AndThenRows<'stmt, F> { map: F, } -impl<'stmt, T, E, F> AndThenRows<'stmt, F> -where - F: FnMut(&Row<'_>) -> Result<T, E>, -{ - pub(crate) fn new(rows: Rows<'stmt>, f: F) -> AndThenRows<'stmt, F> { - AndThenRows { rows, map: f } - } -} - impl<T, E, F> Iterator for AndThenRows<'_, F> where E: convert::From<Error>, @@ -175,27 +176,28 @@ where { type Item = Result<T, E>; + #[inline] fn next(&mut self) -> Option<Self::Item> { let map = &mut self.map; self.rows .next() .transpose() - .map(|row_result| row_result.map_err(E::from).and_then(|row| (map)(&row))) + .map(|row_result| row_result.map_err(E::from).and_then(map)) } } /// `FallibleStreamingIterator` differs from the standard library's `Iterator` /// in two ways: -/// * each call to `next` (sqlite3_step) can fail. +/// * each call to `next` (`sqlite3_step`) can fail. /// * returned `Row` is valid until `next` is called again or `Statement` is /// reset or finalized. /// /// While these iterators cannot be used with Rust `for` loops, `while let` /// loops offer a similar level of ergonomics: /// ```rust,no_run -/// # use rusqlite::{Result, Statement, NO_PARAMS}; +/// # use rusqlite::{Result, Statement}; /// fn query(stmt: &mut Statement) -> Result<()> { -/// let mut rows = stmt.query(NO_PARAMS)?; +/// let mut rows = stmt.query([])?; /// while let Some(row) = rows.next()? { /// // scan columns value /// } @@ -206,9 +208,10 @@ impl<'stmt> FallibleStreamingIterator for Rows<'stmt> { type Error = Error; type Item = Row<'stmt>; + #[inline] fn advance(&mut self) -> Result<()> { - match self.stmt { - Some(ref stmt) => match stmt.step() { + if let Some(stmt) = self.stmt { + match stmt.step() { Ok(true) => { self.row = Some(Row { stmt }); Ok(()) @@ -223,14 +226,14 @@ impl<'stmt> FallibleStreamingIterator for Rows<'stmt> { self.row = None; Err(e) } - }, - None => { - self.row = None; - Ok(()) } + } else { + self.row = None; + Ok(()) } } + #[inline] fn get(&self) -> Option<&Row<'stmt>> { self.row.as_ref() } @@ -246,7 +249,7 @@ impl<'stmt> Row<'stmt> { /// /// ## Failure /// - /// Panics if calling `row.get(idx)` would return an error, + /// Panics if calling [`row.get(idx)`](Row::get) would return an error, /// including: /// /// * If the underlying SQLite column type is not a valid type as a source @@ -285,20 +288,11 @@ impl<'stmt> Row<'stmt> { ), FromSqlError::OutOfRange(i) => Error::IntegralValueOutOfRange(idx, i), FromSqlError::Other(err) => { - Error::FromSqlConversionFailure(idx as usize, value.data_type(), err) + Error::FromSqlConversionFailure(idx, value.data_type(), err) + } + FromSqlError::InvalidBlobSize { .. } => { + Error::FromSqlConversionFailure(idx, value.data_type(), Box::new(err)) } - #[cfg(feature = "i128_blob")] - FromSqlError::InvalidI128Size(_) => Error::InvalidColumnType( - idx, - self.stmt.column_name_unwrap(idx).into(), - value.data_type(), - ), - #[cfg(feature = "uuid")] - FromSqlError::InvalidUuidSize(_) => Error::InvalidColumnType( - idx, - self.stmt.column_name_unwrap(idx).into(), - value.data_type(), - ), }) } @@ -308,7 +302,7 @@ impl<'stmt> Row<'stmt> { /// This `ValueRef` is valid only as long as this Row, which is enforced by /// it's lifetime. This means that while this method is completely safe, /// it can be somewhat difficult to use, and most callers will be better - /// served by `get` or `get`. + /// served by [`get`](Row::get) or [`get_unwrap`](Row::get_unwrap). /// /// ## Failure /// @@ -317,7 +311,7 @@ impl<'stmt> Row<'stmt> { /// /// Returns an `Error::InvalidColumnName` if `idx` is not a valid column /// name for this row. - pub fn get_raw_checked<I: RowIndex>(&self, idx: I) -> Result<ValueRef<'_>> { + pub fn get_ref<I: RowIndex>(&self, idx: I) -> Result<ValueRef<'_>> { let idx = idx.idx(self.stmt)?; // Narrowing from `ValueRef<'stmt>` (which `self.stmt.value_ref(idx)` // returns) to `ValueRef<'a>` is needed because it's only valid until @@ -332,22 +326,52 @@ impl<'stmt> Row<'stmt> { /// This `ValueRef` is valid only as long as this Row, which is enforced by /// it's lifetime. This means that while this method is completely safe, /// it can be difficult to use, and most callers will be better served by - /// `get` or `get`. + /// [`get`](Row::get) or [`get_unwrap`](Row::get_unwrap). /// /// ## Failure /// - /// Panics if calling `row.get_raw_checked(idx)` would return an error, - /// including: + /// Panics if calling [`row.get_ref(idx)`](Row::get_ref) would return an + /// error, including: /// /// * If `idx` is outside the range of columns in the returned query. /// * If `idx` is not a valid column name for this row. + pub fn get_ref_unwrap<I: RowIndex>(&self, idx: I) -> ValueRef<'_> { + self.get_ref(idx).unwrap() + } + + /// Renamed to [`get_ref`](Row::get_ref). + #[deprecated = "Use [`get_ref`](Row::get_ref) instead."] + #[inline] + pub fn get_raw_checked<I: RowIndex>(&self, idx: I) -> Result<ValueRef<'_>> { + self.get_ref(idx) + } + + /// Renamed to [`get_ref_unwrap`](Row::get_ref_unwrap). + #[deprecated = "Use [`get_ref_unwrap`](Row::get_ref_unwrap) instead."] + #[inline] pub fn get_raw<I: RowIndex>(&self, idx: I) -> ValueRef<'_> { - self.get_raw_checked(idx).unwrap() + self.get_ref_unwrap(idx) } } +impl<'stmt> AsRef<Statement<'stmt>> for Row<'stmt> { + fn as_ref(&self) -> &Statement<'stmt> { + self.stmt + } +} + +mod sealed { + /// This trait exists just to ensure that the only impls of `trait Params` + /// that are allowed are ones in this crate. + pub trait Sealed {} + impl Sealed for usize {} + impl Sealed for &str {} +} + /// A trait implemented by types that can index into columns of a row. -pub trait RowIndex { +/// +/// It is only implemented for `usize` and `&str`. +pub trait RowIndex: sealed::Sealed { /// Returns the index of the appropriate column, or `None` if no such /// column exists. fn idx(&self, stmt: &Statement<'_>) -> Result<usize>; @@ -408,74 +432,46 @@ tuples_try_from_row!(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P); #[cfg(test)] mod tests { #![allow(clippy::redundant_closure)] // false positives due to lifetime issues; clippy issue #5594 + use crate::{Connection, Result}; #[test] - fn test_try_from_row_for_tuple_1() { - use crate::{Connection, ToSql}; + fn test_try_from_row_for_tuple_1() -> Result<()> { + use crate::ToSql; use std::convert::TryFrom; - let conn = Connection::open_in_memory().expect("failed to create in-memoory database"); + let conn = Connection::open_in_memory()?; conn.execute( "CREATE TABLE test (a INTEGER)", - std::iter::empty::<&dyn ToSql>(), - ) - .expect("failed to create table"); - conn.execute( - "INSERT INTO test VALUES (42)", - std::iter::empty::<&dyn ToSql>(), - ) - .expect("failed to insert value"); - let val = conn - .query_row( - "SELECT a FROM test", - std::iter::empty::<&dyn ToSql>(), - |row| <(u32,)>::try_from(row), - ) - .expect("failed to query row"); + crate::params_from_iter(std::iter::empty::<&dyn ToSql>()), + )?; + conn.execute("INSERT INTO test VALUES (42)", [])?; + let val = conn.query_row("SELECT a FROM test", [], |row| <(u32,)>::try_from(row))?; assert_eq!(val, (42,)); - let fail = conn.query_row( - "SELECT a FROM test", - std::iter::empty::<&dyn ToSql>(), - |row| <(u32, u32)>::try_from(row), - ); + let fail = conn.query_row("SELECT a FROM test", [], |row| <(u32, u32)>::try_from(row)); assert!(fail.is_err()); + Ok(()) } #[test] - fn test_try_from_row_for_tuple_2() { - use crate::{Connection, ToSql}; + fn test_try_from_row_for_tuple_2() -> Result<()> { use std::convert::TryFrom; - let conn = Connection::open_in_memory().expect("failed to create in-memoory database"); - conn.execute( - "CREATE TABLE test (a INTEGER, b INTEGER)", - std::iter::empty::<&dyn ToSql>(), - ) - .expect("failed to create table"); - conn.execute( - "INSERT INTO test VALUES (42, 47)", - std::iter::empty::<&dyn ToSql>(), - ) - .expect("failed to insert value"); - let val = conn - .query_row( - "SELECT a, b FROM test", - std::iter::empty::<&dyn ToSql>(), - |row| <(u32, u32)>::try_from(row), - ) - .expect("failed to query row"); + let conn = Connection::open_in_memory()?; + conn.execute("CREATE TABLE test (a INTEGER, b INTEGER)", [])?; + conn.execute("INSERT INTO test VALUES (42, 47)", [])?; + let val = conn.query_row("SELECT a, b FROM test", [], |row| { + <(u32, u32)>::try_from(row) + })?; assert_eq!(val, (42, 47)); - let fail = conn.query_row( - "SELECT a, b FROM test", - std::iter::empty::<&dyn ToSql>(), - |row| <(u32, u32, u32)>::try_from(row), - ); + let fail = conn.query_row("SELECT a, b FROM test", [], |row| { + <(u32, u32, u32)>::try_from(row) + }); assert!(fail.is_err()); + Ok(()) } #[test] - fn test_try_from_row_for_tuple_16() { - use crate::{Connection, ToSql}; + fn test_try_from_row_for_tuple_16() -> Result<()> { use std::convert::TryFrom; let create_table = "CREATE TABLE test ( @@ -535,18 +531,10 @@ mod tests { u32, ); - let conn = Connection::open_in_memory().expect("failed to create in-memoory database"); - conn.execute(create_table, std::iter::empty::<&dyn ToSql>()) - .expect("failed to create table"); - conn.execute(insert_values, std::iter::empty::<&dyn ToSql>()) - .expect("failed to insert value"); - let val = conn - .query_row( - "SELECT * FROM test", - std::iter::empty::<&dyn ToSql>(), - |row| BigTuple::try_from(row), - ) - .expect("failed to query row"); + let conn = Connection::open_in_memory()?; + conn.execute(create_table, [])?; + conn.execute(insert_values, [])?; + let val = conn.query_row("SELECT * FROM test", [], |row| BigTuple::try_from(row))?; // Debug is not implemented for tuples of 16 assert_eq!(val.0, 0); assert_eq!(val.1, 1); @@ -566,5 +554,6 @@ mod tests { assert_eq!(val.15, 15); // We don't test one bigger because it's unimplemented + Ok(()) } } |