2019-03-10 19:58:20 +08:00
|
|
|
use fallible_iterator::FallibleIterator;
|
|
|
|
use fallible_streaming_iterator::FallibleStreamingIterator;
|
2020-04-07 05:43:06 +08:00
|
|
|
use std::convert;
|
2017-03-09 05:57:43 +08:00
|
|
|
|
2019-03-10 19:58:20 +08:00
|
|
|
use super::{Error, Result, Statement};
|
2018-10-31 03:11:35 +08:00
|
|
|
use crate::types::{FromSql, FromSqlError, ValueRef};
|
2017-03-09 05:57:43 +08:00
|
|
|
|
|
|
|
/// An handle for the resulting rows of a query.
|
|
|
|
pub struct Rows<'stmt> {
|
2019-03-20 03:33:36 +08:00
|
|
|
pub(crate) stmt: Option<&'stmt Statement<'stmt>>,
|
2019-02-03 21:01:42 +08:00
|
|
|
row: Option<Row<'stmt>>,
|
2017-03-09 05:57:43 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
impl<'stmt> Rows<'stmt> {
|
|
|
|
fn reset(&mut self) {
|
|
|
|
if let Some(stmt) = self.stmt.take() {
|
|
|
|
stmt.reset();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-03-10 18:12:14 +08:00
|
|
|
/// Attempt to get the next row from the query. Returns `Ok(Some(Row))` if
|
|
|
|
/// there is another row, `Err(...)` if there was an error
|
|
|
|
/// getting the next row, and `Ok(None)` if all rows have been retrieved.
|
2017-03-09 05:57:43 +08:00
|
|
|
///
|
|
|
|
/// ## Note
|
|
|
|
///
|
2018-08-17 00:29:46 +08:00
|
|
|
/// This interface is not compatible with Rust's `Iterator` trait, because
|
|
|
|
/// the lifetime of the returned row is tied to the lifetime of `self`.
|
2019-03-10 18:12:14 +08:00
|
|
|
/// This is a fallible "streaming iterator". For a more natural interface,
|
2018-08-17 00:29:46 +08:00
|
|
|
/// consider using `query_map` or `query_and_then` instead, which
|
|
|
|
/// return types that implement `Iterator`.
|
2018-12-07 02:00:55 +08:00
|
|
|
#[allow(clippy::should_implement_trait)] // cannot implement Iterator
|
2019-03-10 18:12:14 +08:00
|
|
|
pub fn next(&mut self) -> Result<Option<&Row<'stmt>>> {
|
|
|
|
self.advance()?;
|
|
|
|
Ok((*self).get())
|
2017-03-09 05:57:43 +08:00
|
|
|
}
|
2019-03-10 19:58:20 +08:00
|
|
|
|
|
|
|
pub fn map<F, B>(self, f: F) -> Map<'stmt, F>
|
|
|
|
where
|
|
|
|
F: FnMut(&Row<'_>) -> Result<B>,
|
|
|
|
{
|
2019-03-22 02:13:55 +08:00
|
|
|
Map { rows: self, f }
|
2019-03-10 19:58:20 +08:00
|
|
|
}
|
2020-04-07 04:01:39 +08:00
|
|
|
|
|
|
|
/// Map over this `Rows`, converting it to a [`MappedRows`], which
|
|
|
|
/// implements `Iterator`.
|
|
|
|
pub fn mapped<F, B>(self, f: F) -> MappedRows<'stmt, F>
|
|
|
|
where
|
|
|
|
F: FnMut(&Row<'_>) -> Result<B>,
|
|
|
|
{
|
|
|
|
MappedRows { rows: self, map: f }
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Map over this `Rows` with a fallible function, converting it to a
|
|
|
|
/// [`AndThenRows`], which implements `Iterator` (instead of
|
|
|
|
/// `FallibleStreamingIterator`).
|
|
|
|
pub fn and_then<F, T, E>(self, f: F) -> AndThenRows<'stmt, F>
|
|
|
|
where
|
2020-04-07 05:43:06 +08:00
|
|
|
F: FnMut(&Row<'_>) -> Result<T, E>,
|
2020-04-07 04:01:39 +08:00
|
|
|
{
|
|
|
|
AndThenRows { rows: self, map: f }
|
|
|
|
}
|
2017-03-09 05:57:43 +08:00
|
|
|
}
|
|
|
|
|
2018-08-01 04:17:17 +08:00
|
|
|
impl<'stmt> Rows<'stmt> {
|
|
|
|
pub(crate) fn new(stmt: &'stmt Statement<'stmt>) -> Rows<'stmt> {
|
2019-02-03 21:01:42 +08:00
|
|
|
Rows {
|
|
|
|
stmt: Some(stmt),
|
|
|
|
row: None,
|
|
|
|
}
|
2017-03-09 05:57:43 +08:00
|
|
|
}
|
|
|
|
|
2019-02-03 21:01:42 +08:00
|
|
|
pub(crate) fn get_expected_row(&mut self) -> Result<&Row<'stmt>> {
|
|
|
|
match self.next()? {
|
|
|
|
Some(row) => Ok(row),
|
2017-03-09 05:57:43 +08:00
|
|
|
None => Err(Error::QueryReturnedNoRows),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-02-03 18:02:38 +08:00
|
|
|
impl Drop for Rows<'_> {
|
2017-03-09 05:57:43 +08:00
|
|
|
fn drop(&mut self) {
|
|
|
|
self.reset();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-03-10 19:58:20 +08:00
|
|
|
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),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-03-09 05:57:43 +08:00
|
|
|
/// An iterator over the mapped resulting rows of a query.
|
|
|
|
pub struct MappedRows<'stmt, F> {
|
|
|
|
rows: Rows<'stmt>,
|
|
|
|
map: F,
|
|
|
|
}
|
|
|
|
|
2018-08-01 04:17:17 +08:00
|
|
|
impl<'stmt, T, F> MappedRows<'stmt, F>
|
2018-08-11 18:48:21 +08:00
|
|
|
where
|
2019-03-10 18:12:14 +08:00
|
|
|
F: FnMut(&Row<'_>) -> Result<T>,
|
2017-03-09 05:57:43 +08:00
|
|
|
{
|
2018-08-01 04:17:17 +08:00
|
|
|
pub(crate) fn new(rows: Rows<'stmt>, f: F) -> MappedRows<'stmt, F> {
|
2018-05-05 01:55:55 +08:00
|
|
|
MappedRows { rows, map: f }
|
2017-03-09 05:57:43 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-02-03 18:02:38 +08:00
|
|
|
impl<T, F> Iterator for MappedRows<'_, F>
|
2018-08-11 18:48:21 +08:00
|
|
|
where
|
2019-03-10 18:12:14 +08:00
|
|
|
F: FnMut(&Row<'_>) -> Result<T>,
|
2017-03-09 05:57:43 +08:00
|
|
|
{
|
|
|
|
type Item = Result<T>;
|
|
|
|
|
|
|
|
fn next(&mut self) -> Option<Result<T>> {
|
|
|
|
let map = &mut self.map;
|
2017-04-08 01:43:24 +08:00
|
|
|
self.rows
|
|
|
|
.next()
|
2019-02-03 21:01:42 +08:00
|
|
|
.transpose()
|
2019-02-22 03:48:09 +08:00
|
|
|
.map(|row_result| row_result.and_then(|row| (map)(&row)))
|
2017-03-09 05:57:43 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// An iterator over the mapped resulting rows of a query, with an Error type
|
|
|
|
/// unifying with Error.
|
|
|
|
pub struct AndThenRows<'stmt, F> {
|
|
|
|
rows: Rows<'stmt>,
|
|
|
|
map: F,
|
|
|
|
}
|
|
|
|
|
2018-08-01 04:17:17 +08:00
|
|
|
impl<'stmt, T, E, F> AndThenRows<'stmt, F>
|
2018-08-11 18:48:21 +08:00
|
|
|
where
|
2020-04-07 05:43:06 +08:00
|
|
|
F: FnMut(&Row<'_>) -> Result<T, E>,
|
2017-03-09 05:57:43 +08:00
|
|
|
{
|
2018-08-01 04:17:17 +08:00
|
|
|
pub(crate) fn new(rows: Rows<'stmt>, f: F) -> AndThenRows<'stmt, F> {
|
2018-05-05 01:55:55 +08:00
|
|
|
AndThenRows { rows, map: f }
|
2017-03-09 05:57:43 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-02-03 18:02:38 +08:00
|
|
|
impl<T, E, F> Iterator for AndThenRows<'_, F>
|
2018-08-11 18:48:21 +08:00
|
|
|
where
|
|
|
|
E: convert::From<Error>,
|
2020-04-07 05:43:06 +08:00
|
|
|
F: FnMut(&Row<'_>) -> Result<T, E>,
|
2017-03-09 05:57:43 +08:00
|
|
|
{
|
2020-04-07 05:43:06 +08:00
|
|
|
type Item = Result<T, E>;
|
2017-03-09 05:57:43 +08:00
|
|
|
|
|
|
|
fn next(&mut self) -> Option<Self::Item> {
|
|
|
|
let map = &mut self.map;
|
2017-04-08 01:43:24 +08:00
|
|
|
self.rows
|
|
|
|
.next()
|
2019-02-03 21:01:42 +08:00
|
|
|
.transpose()
|
2017-04-08 01:43:24 +08:00
|
|
|
.map(|row_result| row_result.map_err(E::from).and_then(|row| (map)(&row)))
|
2017-03-09 05:57:43 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-02-03 21:01:42 +08:00
|
|
|
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()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-03-09 05:57:43 +08:00
|
|
|
/// A single result row of a query.
|
2019-02-03 21:01:42 +08:00
|
|
|
pub struct Row<'stmt> {
|
2019-03-20 03:33:36 +08:00
|
|
|
pub(crate) stmt: &'stmt Statement<'stmt>,
|
2017-03-09 05:57:43 +08:00
|
|
|
}
|
|
|
|
|
2019-02-03 21:01:42 +08:00
|
|
|
impl<'stmt> Row<'stmt> {
|
2017-03-09 05:57:43 +08:00
|
|
|
/// Get the value of a particular column of the result row.
|
|
|
|
///
|
|
|
|
/// ## Failure
|
|
|
|
///
|
2019-02-22 03:48:09 +08:00
|
|
|
/// Panics if calling `row.get(idx)` would return an error,
|
2018-08-17 00:29:46 +08:00
|
|
|
/// including:
|
2017-03-09 05:57:43 +08:00
|
|
|
///
|
2019-02-22 03:48:09 +08:00
|
|
|
/// * If the underlying SQLite column type is not a valid type as a source
|
|
|
|
/// for `T`
|
2019-02-02 22:18:11 +08:00
|
|
|
/// * If the underlying SQLite integral value is outside the range
|
|
|
|
/// representable by `T`
|
|
|
|
/// * If `idx` is outside the range of columns in the returned query
|
2019-02-22 03:48:09 +08:00
|
|
|
pub fn get_unwrap<I: RowIndex, T: FromSql>(&self, idx: I) -> T {
|
|
|
|
self.get(idx).unwrap()
|
2017-03-09 05:57:43 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
/// Get the value of a particular column of the result row.
|
|
|
|
///
|
|
|
|
/// ## Failure
|
|
|
|
///
|
|
|
|
/// Returns an `Error::InvalidColumnType` if the underlying SQLite column
|
|
|
|
/// type is not a valid type as a source for `T`.
|
|
|
|
///
|
2018-08-17 00:29:46 +08:00
|
|
|
/// Returns an `Error::InvalidColumnIndex` if `idx` is outside the valid
|
|
|
|
/// column range for this row.
|
2017-03-09 05:57:43 +08:00
|
|
|
///
|
2018-08-17 00:29:46 +08:00
|
|
|
/// Returns an `Error::InvalidColumnName` if `idx` is not a valid column
|
|
|
|
/// name for this row.
|
Add a feature for storing i128 as blobs.
This is behind the `i128_blob` feature.
Blobs are stored as 16 byte big-endian values, with their most significant bit
flipped. This is so that sorting, comparison, etc all work properly, even with
negative numbers. This also allows the representation to be stable across
different computers.
It's possible that the `FromSql` implementation should handle the case that the
real value is stored in an integer. I didn't do this, but would be willing to
make the change. I don't think we should store them this way though, since I
don't think users would be able to sort/compare them sanely.
Support for `u128` is not implemented, as comparison with i128 values would work
strangely. This also is consistent with `u64` not being allowed, not that I
think that would be reason enough on it's own.
The `byteorder` crate is used if this feature is flipped, as it's quite small
and implements things more or less optimally. If/when `i128::{to,from}_be_bytes`
gets stabilized (https://github.com/rust-lang/rust/issues/52963), we should
probably use that instead.
2018-10-09 03:04:07 +08:00
|
|
|
///
|
|
|
|
/// If the result type is i128 (which requires the `i128_blob` feature to be
|
|
|
|
/// enabled), and the underlying SQLite column is a blob whose size is not
|
|
|
|
/// 16 bytes, `Error::InvalidColumnType` will also be returned.
|
2019-02-22 03:48:09 +08:00
|
|
|
pub fn get<I: RowIndex, T: FromSql>(&self, idx: I) -> Result<T> {
|
2018-10-31 03:11:35 +08:00
|
|
|
let idx = idx.idx(self.stmt)?;
|
2017-03-09 05:57:43 +08:00
|
|
|
let value = self.stmt.value_ref(idx);
|
|
|
|
FromSql::column_result(value).map_err(|err| match err {
|
2019-08-31 18:17:48 +08:00
|
|
|
FromSqlError::InvalidType => Error::InvalidColumnType(
|
|
|
|
idx,
|
|
|
|
self.stmt.column_name_unwrap(idx).into(),
|
|
|
|
value.data_type(),
|
|
|
|
),
|
2018-08-11 18:48:21 +08:00
|
|
|
FromSqlError::OutOfRange(i) => Error::IntegralValueOutOfRange(idx, i),
|
|
|
|
FromSqlError::Other(err) => {
|
2017-03-09 05:57:43 +08:00
|
|
|
Error::FromSqlConversionFailure(idx as usize, value.data_type(), err)
|
|
|
|
}
|
Add a feature for storing i128 as blobs.
This is behind the `i128_blob` feature.
Blobs are stored as 16 byte big-endian values, with their most significant bit
flipped. This is so that sorting, comparison, etc all work properly, even with
negative numbers. This also allows the representation to be stable across
different computers.
It's possible that the `FromSql` implementation should handle the case that the
real value is stored in an integer. I didn't do this, but would be willing to
make the change. I don't think we should store them this way though, since I
don't think users would be able to sort/compare them sanely.
Support for `u128` is not implemented, as comparison with i128 values would work
strangely. This also is consistent with `u64` not being allowed, not that I
think that would be reason enough on it's own.
The `byteorder` crate is used if this feature is flipped, as it's quite small
and implements things more or less optimally. If/when `i128::{to,from}_be_bytes`
gets stabilized (https://github.com/rust-lang/rust/issues/52963), we should
probably use that instead.
2018-10-09 03:04:07 +08:00
|
|
|
#[cfg(feature = "i128_blob")]
|
2019-08-31 18:17:48 +08:00
|
|
|
FromSqlError::InvalidI128Size(_) => Error::InvalidColumnType(
|
|
|
|
idx,
|
|
|
|
self.stmt.column_name_unwrap(idx).into(),
|
|
|
|
value.data_type(),
|
|
|
|
),
|
2019-04-09 02:19:42 +08:00
|
|
|
#[cfg(feature = "uuid")]
|
2019-08-31 18:17:48 +08:00
|
|
|
FromSqlError::InvalidUuidSize(_) => Error::InvalidColumnType(
|
|
|
|
idx,
|
|
|
|
self.stmt.column_name_unwrap(idx).into(),
|
|
|
|
value.data_type(),
|
|
|
|
),
|
2018-08-11 18:48:21 +08:00
|
|
|
})
|
2017-03-09 05:57:43 +08:00
|
|
|
}
|
|
|
|
|
2018-10-19 02:59:30 +08:00
|
|
|
/// Get the value of a particular column of the result row as a `ValueRef`,
|
|
|
|
/// allowing data to be read out of a row without copying.
|
|
|
|
///
|
|
|
|
/// 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
|
2019-02-22 03:48:09 +08:00
|
|
|
/// served by `get` or `get`.
|
2018-10-19 02:59:30 +08:00
|
|
|
///
|
|
|
|
/// ## Failure
|
|
|
|
///
|
|
|
|
/// Returns an `Error::InvalidColumnIndex` if `idx` is outside the valid
|
|
|
|
/// column range for this row.
|
|
|
|
///
|
|
|
|
/// Returns an `Error::InvalidColumnName` if `idx` is not a valid column
|
|
|
|
/// name for this row.
|
2019-02-03 21:01:42 +08:00
|
|
|
pub fn get_raw_checked<I: RowIndex>(&self, idx: I) -> Result<ValueRef<'_>> {
|
2018-10-19 02:59:30 +08:00
|
|
|
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
|
|
|
|
// the next call to sqlite3_step.
|
|
|
|
let val_ref = self.stmt.value_ref(idx);
|
|
|
|
Ok(val_ref)
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Get the value of a particular column of the result row as a `ValueRef`,
|
|
|
|
/// allowing data to be read out of a row without copying.
|
|
|
|
///
|
|
|
|
/// 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
|
2019-02-22 03:48:09 +08:00
|
|
|
/// `get` or `get`.
|
2018-10-19 02:59:30 +08:00
|
|
|
///
|
|
|
|
/// ## Failure
|
|
|
|
///
|
|
|
|
/// Panics if calling `row.get_raw_checked(idx)` would return an error,
|
|
|
|
/// including:
|
|
|
|
///
|
2019-02-02 22:18:11 +08:00
|
|
|
/// * If `idx` is outside the range of columns in the returned query.
|
|
|
|
/// * If `idx` is not a valid column name for this row.
|
2019-02-03 21:01:42 +08:00
|
|
|
pub fn get_raw<I: RowIndex>(&self, idx: I) -> ValueRef<'_> {
|
2018-10-19 02:59:30 +08:00
|
|
|
self.get_raw_checked(idx).unwrap()
|
|
|
|
}
|
2017-03-09 05:57:43 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
/// A trait implemented by types that can index into columns of a row.
|
|
|
|
pub trait RowIndex {
|
|
|
|
/// Returns the index of the appropriate column, or `None` if no such
|
|
|
|
/// column exists.
|
2018-12-08 04:57:04 +08:00
|
|
|
fn idx(&self, stmt: &Statement<'_>) -> Result<usize>;
|
2017-03-09 05:57:43 +08:00
|
|
|
}
|
|
|
|
|
2018-05-24 03:04:13 +08:00
|
|
|
impl RowIndex for usize {
|
2017-03-09 05:57:43 +08:00
|
|
|
#[inline]
|
2018-12-08 04:57:04 +08:00
|
|
|
fn idx(&self, stmt: &Statement<'_>) -> Result<usize> {
|
2018-05-24 03:04:13 +08:00
|
|
|
if *self >= stmt.column_count() {
|
2017-03-09 05:57:43 +08:00
|
|
|
Err(Error::InvalidColumnIndex(*self))
|
|
|
|
} else {
|
|
|
|
Ok(*self)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-02-03 18:02:38 +08:00
|
|
|
impl RowIndex for &'_ str {
|
2017-03-09 05:57:43 +08:00
|
|
|
#[inline]
|
2018-12-08 04:57:04 +08:00
|
|
|
fn idx(&self, stmt: &Statement<'_>) -> Result<usize> {
|
2017-03-09 05:57:43 +08:00
|
|
|
stmt.column_index(*self)
|
|
|
|
}
|
|
|
|
}
|
2020-05-13 17:47:29 +08:00
|
|
|
|
|
|
|
macro_rules! tuple_try_from_row {
|
|
|
|
($($field:ident),*) => {
|
2020-05-17 12:37:25 +08:00
|
|
|
impl<'a, $($field,)*> convert::TryFrom<&'a Row<'a>> for ($($field,)*) where $($field: FromSql,)* {
|
2020-05-13 17:47:29 +08:00
|
|
|
type Error = crate::Error;
|
|
|
|
|
|
|
|
// we end with index += 1, which rustc warns about
|
|
|
|
// unused_variables and unused_mut are allowed for ()
|
|
|
|
#[allow(unused_assignments, unused_variables, unused_mut)]
|
2020-05-17 12:37:25 +08:00
|
|
|
fn try_from(row: &'a Row<'a>) -> Result<Self> {
|
2020-05-13 17:47:29 +08:00
|
|
|
let mut index = 0;
|
|
|
|
$(
|
|
|
|
#[allow(non_snake_case)]
|
|
|
|
let $field = row.get::<_, $field>(index)?;
|
|
|
|
index += 1;
|
|
|
|
)*
|
|
|
|
Ok(($($field,)*))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
macro_rules! tuples_try_from_row {
|
|
|
|
() => {
|
|
|
|
// not very useful, but maybe some other macro users will find this helpful
|
|
|
|
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 {
|
2020-05-17 12:37:25 +08:00
|
|
|
#![allow(clippy::redundant_closure)] // false positives due to lifetime issues; clippy issue #5594
|
|
|
|
|
2020-05-13 17:47:29 +08:00
|
|
|
#[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");
|
|
|
|
// Debug is not implemented for tuples of 16
|
|
|
|
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);
|
|
|
|
|
|
|
|
// We don't test one bigger because it's unimplemented
|
|
|
|
}
|
|
|
|
}
|