use fallible_iterator::FallibleIterator;
use fallible_streaming_iterator::FallibleStreamingIterator;
use std::convert;
use super::{Error, Result, Statement};
use crate::types::{FromSql, FromSqlError, ValueRef};
pub struct Rows<'stmt> {
pub(crate) stmt: Option<&'stmt Statement<'stmt>>,
row: Option<Row<'stmt>>,
}
impl<'stmt> Rows<'stmt> {
fn reset(&mut self) {
if let Some(stmt) = self.stmt.take() {
stmt.reset();
}
}
#[allow(clippy::should_implement_trait)]
pub fn next(&mut self) -> Result<Option<&Row<'stmt>>> {
self.advance()?;
Ok((*self).get())
}
pub fn map<F, B>(self, f: F) -> Map<'stmt, F>
where
F: FnMut(&Row<'_>) -> Result<B>,
{
Map { rows: self, f }
}
pub fn mapped<F, B>(self, f: F) -> MappedRows<'stmt, F>
where
F: FnMut(&Row<'_>) -> Result<B>,
{
MappedRows { rows: self, map: f }
}
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 }
}
}
impl<'stmt> Rows<'stmt> {
pub(crate) fn new(stmt: &'stmt Statement<'stmt>) -> Rows<'stmt> {
Rows {
stmt: Some(stmt),
row: None,
}
}
pub(crate) fn get_expected_row(&mut self) -> Result<&Row<'stmt>> {
match self.next()? {
Some(row) => Ok(row),
None => Err(Error::QueryReturnedNoRows),
}
}
}
impl Drop for Rows<'_> {
fn drop(&mut self) {
self.reset();
}
}
pub struct Map<'stmt, F> {
rows: Rows<'stmt>,
f: F,
}
impl<F, B> FallibleIterator for Map<'_, F>
where
F: FnMut(&Row<'_>) -> Result<B>,
{
type Error = Error;
type Item = B;
fn next(&mut self) -> Result<Option<B>> {
match self.rows.next()? {
Some(v) => Ok(Some((self.f)(v)?)),
None => Ok(None),
}
}
}
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>;
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)))
}
}
pub struct AndThenRows<'stmt, F> {
rows: Rows<'stmt>,
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>,
F: FnMut(&Row<'_>) -> Result<T, E>,
{
type Item = Result<T, E>;
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)))
}
}
impl<'stmt> FallibleStreamingIterator for Rows<'stmt> {
type Error = Error;
type Item = Row<'stmt>;
fn advance(&mut self) -> Result<()> {
match self.stmt {
Some(ref stmt) => match stmt.step() {
Ok(true) => {
self.row = Some(Row { stmt });
Ok(())
}
Ok(false) => {
self.reset();
self.row = None;
Ok(())
}
Err(e) => {
self.reset();
self.row = None;
Err(e)
}
},
None => {
self.row = None;
Ok(())
}
}
}
fn get(&self) -> Option<&Row<'stmt>> {
self.row.as_ref()
}
}
pub struct Row<'stmt> {
pub(crate) stmt: &'stmt Statement<'stmt>,
}
impl<'stmt> Row<'stmt> {
pub fn get_unwrap<I: RowIndex, T: FromSql>(&self, idx: I) -> T {
self.get(idx).unwrap()
}
pub fn get<I: RowIndex, T: FromSql>(&self, idx: I) -> Result<T> {
let idx = idx.idx(self.stmt)?;
let value = self.stmt.value_ref(idx);
FromSql::column_result(value).map_err(|err| match err {
FromSqlError::InvalidType => Error::InvalidColumnType(
idx,
self.stmt.column_name_unwrap(idx).into(),
value.data_type(),
),
FromSqlError::OutOfRange(i) => Error::IntegralValueOutOfRange(idx, i),
FromSqlError::Other(err) => {
Error::FromSqlConversionFailure(idx as usize, value.data_type(), 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(),
),
})
}
pub fn get_raw_checked<I: RowIndex>(&self, idx: I) -> Result<ValueRef<'_>> {
let idx = idx.idx(self.stmt)?;
let val_ref = self.stmt.value_ref(idx);
Ok(val_ref)
}
pub fn get_raw<I: RowIndex>(&self, idx: I) -> ValueRef<'_> {
self.get_raw_checked(idx).unwrap()
}
}
pub trait RowIndex {
fn idx(&self, stmt: &Statement<'_>) -> Result<usize>;
}
impl RowIndex for usize {
#[inline]
fn idx(&self, stmt: &Statement<'_>) -> Result<usize> {
if *self >= stmt.column_count() {
Err(Error::InvalidColumnIndex(*self))
} else {
Ok(*self)
}
}
}
impl RowIndex for &'_ str {
#[inline]
fn idx(&self, stmt: &Statement<'_>) -> Result<usize> {
stmt.column_index(*self)
}
}
macro_rules! tuple_try_from_row {
($($field:ident),*) => {
impl<'a, $($field,)*> convert::TryFrom<&'a Row<'a>> for ($($field,)*) where $($field: FromSql,)* {
type Error = crate::Error;
#[allow(unused_assignments, unused_variables, unused_mut)]
fn try_from(row: &'a Row<'a>) -> Result<Self> {
let mut index = 0;
$(
#[allow(non_snake_case)]
let $field = row.get::<_, $field>(index)?;
index += 1;
)*
Ok(($($field,)*))
}
}
}
}
macro_rules! tuples_try_from_row {
() => {
tuple_try_from_row!();
};
($first:ident $(, $remaining:ident)*) => {
tuple_try_from_row!($first $(, $remaining)*);
tuples_try_from_row!($($remaining),*);
};
}
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)]
#[test]
fn test_try_from_row_for_tuple_1() {
use crate::{Connection, ToSql};
use std::convert::TryFrom;
let conn = Connection::open_in_memory().expect("failed to create in-memoory database");
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");
assert_eq!(val, (42,));
let fail = conn.query_row(
"SELECT a FROM test",
std::iter::empty::<&dyn ToSql>(),
|row| <(u32, u32)>::try_from(row),
);
assert!(fail.is_err());
}
#[test]
fn test_try_from_row_for_tuple_2() {
use crate::{Connection, ToSql};
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");
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),
);
assert!(fail.is_err());
}
#[test]
fn test_try_from_row_for_tuple_16() {
use crate::{Connection, ToSql};
use std::convert::TryFrom;
let create_table = "CREATE TABLE test (
a INTEGER,
b INTEGER,
c INTEGER,
d INTEGER,
e INTEGER,
f INTEGER,
g INTEGER,
h INTEGER,
i INTEGER,
j INTEGER,
k INTEGER,
l INTEGER,
m INTEGER,
n INTEGER,
o INTEGER,
p INTEGER
)";
let insert_values = "INSERT INTO test VALUES (
0,
1,
2,
3,
4,
5,
6,
7,
8,
9,
10,
11,
12,
13,
14,
15
)";
type BigTuple = (
u32,
u32,
u32,
u32,
u32,
u32,
u32,
u32,
u32,
u32,
u32,
u32,
u32,
u32,
u32,
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");
assert_eq!(val.0, 0);
assert_eq!(val.1, 1);
assert_eq!(val.2, 2);
assert_eq!(val.3, 3);
assert_eq!(val.4, 4);
assert_eq!(val.5, 5);
assert_eq!(val.6, 6);
assert_eq!(val.7, 7);
assert_eq!(val.8, 8);
assert_eq!(val.9, 9);
assert_eq!(val.10, 10);
assert_eq!(val.11, 11);
assert_eq!(val.12, 12);
assert_eq!(val.13, 13);
assert_eq!(val.14, 14);
assert_eq!(val.15, 15);
}
}