mirror of
https://github.com/launchbadge/sqlx.git
synced 2025-12-30 13:20:59 +00:00
338 lines
9.5 KiB
Rust
338 lines
9.5 KiB
Rust
//! Contains the `ColumnIndex`, `Row`, and `FromRow` traits.
|
|
|
|
use crate::database::{Database, HasRawValue};
|
|
use crate::decode::Decode;
|
|
use crate::types::Type;
|
|
|
|
/// A type that can be used to index into a [`Row`].
|
|
///
|
|
/// The [`get`] and [`try_get`] methods of [`Row`] accept any type that implements `ColumnIndex`.
|
|
/// This trait is implemented for strings which are used to look up a column by name, and for
|
|
/// `usize` which is used as a positional index into the row.
|
|
///
|
|
/// This trait is sealed and cannot be implemented for types outside of SQLx.
|
|
///
|
|
/// [`Row`]: trait.Row.html
|
|
/// [`get`]: trait.Row.html#method.get
|
|
/// [`try_get`]: trait.Row.html#method.try_get
|
|
pub trait ColumnIndex<'c, R>
|
|
where
|
|
Self: private_column_index::Sealed,
|
|
R: Row<'c> + ?Sized,
|
|
{
|
|
/// Returns a valid positional index into the row, [`ColumnIndexOutOfBounds`], or,
|
|
/// [`ColumnNotFound`].
|
|
///
|
|
/// [`ColumnNotFound`]: ../enum.Error.html#variant.ColumnNotFound
|
|
/// [`ColumnIndexOutOfBounds`]: ../enum.Error.html#variant.ColumnIndexOutOfBounds
|
|
fn index(&self, row: &R) -> crate::Result<R::Database, usize>;
|
|
}
|
|
|
|
impl<'c, R, I> ColumnIndex<'c, R> for &'_ I
|
|
where
|
|
R: Row<'c>,
|
|
I: ColumnIndex<'c, R>,
|
|
{
|
|
#[inline]
|
|
fn index(&self, row: &R) -> crate::Result<<R as Row<'c>>::Database, usize> {
|
|
(**self).index(row)
|
|
}
|
|
}
|
|
|
|
// Prevent users from implementing the `ColumnIndex` trait.
|
|
mod private_column_index {
|
|
pub trait Sealed {}
|
|
impl Sealed for usize {}
|
|
impl Sealed for str {}
|
|
impl<T> Sealed for &'_ T where T: Sealed {}
|
|
}
|
|
|
|
/// Represents a single row from the database.
|
|
///
|
|
/// Applications should not generally need to use this trait. Values of this trait are only
|
|
/// encountered when manually implementing [`FromRow`] (as opposed to deriving) or iterating
|
|
/// a [`Cursor`] (returned from [`Query::fetch`]).
|
|
///
|
|
/// This trait is sealed and cannot be implemented for types outside of SQLx.
|
|
///
|
|
/// [`FromRow`]: crate::row::FromRow
|
|
/// [`Cursor`]: crate::cursor::Cursor
|
|
/// [`Query::fetch`]: crate::query::Query::fetch
|
|
pub trait Row<'c>
|
|
where
|
|
Self: private_row::Sealed + Unpin + Send,
|
|
{
|
|
/// The `Database` this `Row` is implemented for.
|
|
type Database: Database;
|
|
|
|
/// Returns `true` if this row has no columns.
|
|
#[inline]
|
|
fn is_empty(&self) -> bool {
|
|
self.len() == 0
|
|
}
|
|
|
|
/// Returns the number of columns in this row.
|
|
fn len(&self) -> usize;
|
|
|
|
/// Index into the database row and decode a single value.
|
|
///
|
|
/// A string index can be used to access a column by name and a `usize` index
|
|
/// can be used to access a column by position.
|
|
///
|
|
/// ```rust,ignore
|
|
/// # let mut cursor = sqlx::query("SELECT id, name FROM users")
|
|
/// # .fetch(&mut conn);
|
|
/// #
|
|
/// # let row = cursor.next().await?.unwrap();
|
|
/// #
|
|
/// let id: i32 = row.get("id"); // a column named "id"
|
|
/// let name: &str = row.get(1); // the second column in the result
|
|
/// ```
|
|
///
|
|
/// # Panics
|
|
/// Panics if the column does not exist or its value cannot be decoded into the requested type.
|
|
/// See [`try_get`](#method.try_get) for a non-panicking version.
|
|
#[inline]
|
|
fn get<T, I>(&self, index: I) -> T
|
|
where
|
|
T: Type<Self::Database>,
|
|
I: ColumnIndex<'c, Self>,
|
|
T: Decode<'c, Self::Database>,
|
|
{
|
|
self.try_get::<T, I>(index).unwrap()
|
|
}
|
|
|
|
/// Index into the database row and decode a single value.
|
|
///
|
|
/// A string index can be used to access a column by name and a `usize` index
|
|
/// can be used to access a column by position.
|
|
///
|
|
/// ```rust,ignore
|
|
/// # let mut cursor = sqlx::query("SELECT id, name FROM users")
|
|
/// # .fetch(&mut conn);
|
|
/// #
|
|
/// # let row = cursor.next().await?.unwrap();
|
|
/// #
|
|
/// let id: i32 = row.try_get("id")?; // a column named "id"
|
|
/// let name: &str = row.try_get(1)?; // the second column in the result
|
|
/// ```
|
|
///
|
|
/// # Errors
|
|
/// * [`ColumnNotFound`] if the column by the given name was not found.
|
|
/// * [`ColumnIndexOutOfBounds`] if the `usize` index was greater than the number of columns in the row.
|
|
/// * [`Decode`] if the value could not be decoded into the requested type.
|
|
///
|
|
/// [`Decode`]: crate::Error::Decode
|
|
/// [`ColumnNotFound`]: crate::Error::ColumnNotFound
|
|
/// [`ColumnIndexOutOfBounds`]: crate::Error::ColumnIndexOutOfBounds
|
|
#[inline]
|
|
fn try_get<T, I>(&self, index: I) -> crate::Result<Self::Database, T>
|
|
where
|
|
T: Type<Self::Database>,
|
|
I: ColumnIndex<'c, Self>,
|
|
T: Decode<'c, Self::Database>,
|
|
{
|
|
Ok(Decode::decode(self.try_get_raw(index)?)?)
|
|
}
|
|
|
|
#[doc(hidden)]
|
|
fn try_get_raw<I>(
|
|
&self,
|
|
index: I,
|
|
) -> crate::Result<Self::Database, <Self::Database as HasRawValue<'c>>::RawValue>
|
|
where
|
|
I: ColumnIndex<'c, Self>;
|
|
}
|
|
|
|
// Prevent users from implementing the `Row` trait.
|
|
pub(crate) mod private_row {
|
|
pub trait Sealed {}
|
|
}
|
|
|
|
/// A record that can be built from a row returned by the database.
|
|
///
|
|
/// In order to use [`query_as`] the output type must implement `FromRow`.
|
|
///
|
|
/// # Deriving
|
|
/// This trait can be automatically derived by SQLx for any struct. The generated implementation
|
|
/// will consist of a sequence of calls to [`Row::try_get`] using the name from each
|
|
/// struct field.
|
|
///
|
|
/// ```rust,ignore
|
|
/// #[derive(sqlx::FromRow)]
|
|
/// struct User {
|
|
/// id: i32,
|
|
/// name: String,
|
|
/// }
|
|
/// ```
|
|
///
|
|
/// [`query_as`]: crate::query_as
|
|
/// [`Row::try_get`]: crate::row::Row::try_get
|
|
pub trait FromRow<'c, R>
|
|
where
|
|
Self: Sized,
|
|
R: Row<'c>,
|
|
{
|
|
#[allow(missing_docs)]
|
|
fn from_row(row: &R) -> crate::Result<R::Database, Self>;
|
|
}
|
|
|
|
// Macros to help unify the internal implementations as a good chunk
|
|
// is very similar
|
|
|
|
#[allow(unused_macros)]
|
|
macro_rules! impl_from_row_for_tuple {
|
|
($db:ident, $r:ident; $( ($idx:tt) -> $T:ident );+;) => {
|
|
impl<'c, $($T,)+> crate::row::FromRow<'c, $r<'c>> for ($($T,)+)
|
|
where
|
|
$($T: 'c,)+
|
|
$($T: crate::types::Type<$db>,)+
|
|
$($T: crate::decode::Decode<'c, $db>,)+
|
|
{
|
|
#[inline]
|
|
fn from_row(row: &$r<'c>) -> crate::Result<$db, Self> {
|
|
use crate::row::Row;
|
|
|
|
Ok(($(row.try_get($idx as usize)?,)+))
|
|
}
|
|
}
|
|
};
|
|
}
|
|
|
|
#[allow(unused_macros)]
|
|
macro_rules! impl_from_row_for_tuples {
|
|
($db:ident, $r:ident) => {
|
|
impl_from_row_for_tuple!($db, $r;
|
|
(0) -> T1;
|
|
);
|
|
|
|
impl_from_row_for_tuple!($db, $r;
|
|
(0) -> T1;
|
|
(1) -> T2;
|
|
);
|
|
|
|
impl_from_row_for_tuple!($db, $r;
|
|
(0) -> T1;
|
|
(1) -> T2;
|
|
(2) -> T3;
|
|
);
|
|
|
|
impl_from_row_for_tuple!($db, $r;
|
|
(0) -> T1;
|
|
(1) -> T2;
|
|
(2) -> T3;
|
|
(3) -> T4;
|
|
);
|
|
|
|
impl_from_row_for_tuple!($db, $r;
|
|
(0) -> T1;
|
|
(1) -> T2;
|
|
(2) -> T3;
|
|
(3) -> T4;
|
|
(4) -> T5;
|
|
);
|
|
|
|
impl_from_row_for_tuple!($db, $r;
|
|
(0) -> T1;
|
|
(1) -> T2;
|
|
(2) -> T3;
|
|
(3) -> T4;
|
|
(4) -> T5;
|
|
(5) -> T6;
|
|
);
|
|
|
|
impl_from_row_for_tuple!($db, $r;
|
|
(0) -> T1;
|
|
(1) -> T2;
|
|
(2) -> T3;
|
|
(3) -> T4;
|
|
(4) -> T5;
|
|
(5) -> T6;
|
|
(6) -> T7;
|
|
);
|
|
|
|
impl_from_row_for_tuple!($db, $r;
|
|
(0) -> T1;
|
|
(1) -> T2;
|
|
(2) -> T3;
|
|
(3) -> T4;
|
|
(4) -> T5;
|
|
(5) -> T6;
|
|
(6) -> T7;
|
|
(7) -> T8;
|
|
);
|
|
|
|
impl_from_row_for_tuple!($db, $r;
|
|
(0) -> T1;
|
|
(1) -> T2;
|
|
(2) -> T3;
|
|
(3) -> T4;
|
|
(4) -> T5;
|
|
(5) -> T6;
|
|
(6) -> T7;
|
|
(7) -> T8;
|
|
(8) -> T9;
|
|
);
|
|
};
|
|
}
|
|
|
|
#[allow(unused_macros)]
|
|
macro_rules! impl_map_row_for_row {
|
|
($DB:ident, $R:ident) => {
|
|
impl<O: Unpin, F> crate::query::TryMapRow<$DB> for F
|
|
where
|
|
F: for<'c> FnMut($R<'c>) -> crate::Result<$DB, O>,
|
|
{
|
|
type Output = O;
|
|
|
|
fn try_map_row(&mut self, row: $R) -> crate::Result<$DB, O> {
|
|
(self)(row)
|
|
}
|
|
}
|
|
};
|
|
}
|
|
|
|
#[allow(unused_macros)]
|
|
macro_rules! impl_column_index_for_row {
|
|
($R:ident) => {
|
|
impl<'c> crate::row::ColumnIndex<'c, $R<'c>> for usize {
|
|
fn index(
|
|
&self,
|
|
row: &$R<'c>,
|
|
) -> crate::Result<<$R<'c> as crate::row::Row<'c>>::Database, usize> {
|
|
let len = crate::row::Row::len(row);
|
|
|
|
if *self >= len {
|
|
return Err(crate::Error::ColumnIndexOutOfBounds { len, index: *self });
|
|
}
|
|
|
|
Ok(*self)
|
|
}
|
|
}
|
|
|
|
impl<'c> crate::row::ColumnIndex<'c, $R<'c>> for str {
|
|
fn index(
|
|
&self,
|
|
row: &$R<'c>,
|
|
) -> crate::Result<<$R<'c> as crate::row::Row<'c>>::Database, usize> {
|
|
row.columns
|
|
.get(self)
|
|
.ok_or_else(|| crate::Error::ColumnNotFound((*self).into()))
|
|
.map(|&index| index as usize)
|
|
}
|
|
}
|
|
};
|
|
}
|
|
|
|
#[allow(unused_macros)]
|
|
macro_rules! impl_from_row_for_row {
|
|
($R:ident) => {
|
|
impl<'c> crate::row::FromRow<'c, $R<'c>> for $R<'c> {
|
|
#[inline]
|
|
fn from_row(row: $R<'c>) -> crate::Result<$R::Database, Self> {
|
|
Ok(row)
|
|
}
|
|
}
|
|
};
|
|
}
|