aboutsummaryrefslogtreecommitdiff
path: root/src/row.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/row.rs')
-rw-r--r--src/row.rs223
1 files changed, 106 insertions, 117 deletions
diff --git a/src/row.rs b/src/row.rs
index 36aa1a6..c766e50 100644
--- a/src/row.rs
+++ b/src/row.rs
@@ -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(())
}
}