blob: daf3f970fdcc803698d78191e9ca987f565c6c20 [file] [log] [blame]
//! Contains the `Row` trait
use crate::backend::Backend;
use crate::deserialize;
use deserialize::FromSql;
use std::ops::Range;
#[cfg(feature = "i-implement-a-third-party-backend-and-opt-into-breaking-changes")]
#[doc(inline)]
pub use self::private::{PartialRow, RowSealed};
#[cfg(not(feature = "i-implement-a-third-party-backend-and-opt-into-breaking-changes"))]
#[allow(unused_imports)]
pub(crate) use self::private::{PartialRow, RowSealed};
/// Representing a way to index into database rows
///
/// * Crates using existing backends should use existing implementations of
/// this traits. Diesel provides `RowIndex<usize>` and `RowIndex<&str>` for
/// all built-in backends
///
/// * Crates implementing custom backends need to provide `RowIndex<usize>` and
/// `RowIndex<&str>` impls for their [`Row`] type.
///
pub trait RowIndex<I> {
/// Get the numeric index inside the current row for the provided index value
fn idx(&self, idx: I) -> Option<usize>;
}
#[doc(hidden)]
#[cfg(all(feature = "with-deprecated", not(feature = "without-deprecated")))]
#[deprecated(note = "Use `Row::Field` directly instead")]
pub type FieldRet<'a, R, DB> = <R as self::private::RowLifetimeHelper<DB>>::Field<'a>;
/// Represents a single database row.
///
/// This trait is used as an argument to [`FromSqlRow`].
///
/// [`FromSqlRow`]: crate::deserialize::FromSqlRow
pub trait Row<'a, DB: Backend>:
RowIndex<usize> + for<'b> RowIndex<&'b str> + self::private::RowSealed + Sized
{
/// Field type returned by a `Row` implementation
///
/// * Crates using existing backends should not concern themself with the
/// concrete type of this associated type.
///
/// * Crates implementing custom backends should provide their own type
/// meeting the required trait bounds
type Field<'f>: Field<'f, DB>
where
'a: 'f,
Self: 'f;
/// Return type of `PartialRow`
///
/// For all implementations, beside of the `Row` implementation on `PartialRow` itself
/// this should be `Self`.
#[cfg_attr(
not(feature = "i-implement-a-third-party-backend-and-opt-into-breaking-changes"),
doc(hidden)
)]
type InnerPartialRow: Row<'a, DB>;
/// Get the number of fields in the current row
fn field_count(&self) -> usize;
/// Get the field with the provided index from the row.
///
/// Returns `None` if there is no matching field for the given index
fn get<'b, I>(&'b self, idx: I) -> Option<Self::Field<'b>>
where
'a: 'b,
Self: RowIndex<I>;
/// Get a deserialized value with the provided index from the row.
fn get_value<ST, T, I>(&self, idx: I) -> crate::deserialize::Result<T>
where
Self: RowIndex<I>,
T: FromSql<ST, DB>,
{
let field = self.get(idx).ok_or(crate::result::UnexpectedEndOfRow)?;
<T as FromSql<ST, DB>>::from_nullable_sql(field.value())
}
/// Returns a wrapping row that allows only to access fields, where the index is part of
/// the provided range.
#[cfg_attr(
not(feature = "i-implement-a-third-party-backend-and-opt-into-breaking-changes"),
doc(hidden)
)]
fn partial_row(&self, range: Range<usize>) -> PartialRow<'_, Self::InnerPartialRow>;
}
/// Represents a single field in a database row.
///
/// This trait allows retrieving information on the name of the colum and on the value of the
/// field.
pub trait Field<'a, DB: Backend> {
/// The name of the current field
///
/// Returns `None` if it's an unnamed field
fn field_name(&self) -> Option<&str>;
/// Get the value representing the current field in the raw representation
/// as it is transmitted by the database
fn value(&self) -> Option<DB::RawValue<'_>>;
/// Checks whether this field is null or not.
fn is_null(&self) -> bool {
self.value().is_none()
}
}
/// Represents a row of a SQL query, where the values are accessed by name
/// rather than by index.
///
/// This trait is used by implementations of
/// [`QueryableByName`](crate::deserialize::QueryableByName)
pub trait NamedRow<'a, DB: Backend>: Row<'a, DB> {
/// Retrieve and deserialize a single value from the query
///
/// Note that `ST` *must* be the exact type of the value with that name in
/// the query. The compiler will not be able to verify that you have
/// provided the correct type. If there is a mismatch, you may receive an
/// incorrect value, or a runtime error.
///
/// If two or more fields in the query have the given name, the result of
/// this function is undefined.
fn get<ST, T>(&self, column_name: &str) -> deserialize::Result<T>
where
T: FromSql<ST, DB>;
}
impl<'a, R, DB> NamedRow<'a, DB> for R
where
R: Row<'a, DB>,
DB: Backend,
{
fn get<ST, T>(&self, column_name: &str) -> deserialize::Result<T>
where
T: FromSql<ST, DB>,
{
let field = Row::get(self, column_name)
.ok_or_else(|| format!("Column `{column_name}` was not present in query"))?;
T::from_nullable_sql(field.value())
}
}
// These traits are not part of the public API
// because:
// * we want to control who can implement `Row` (for `RowSealed`)
// * `PartialRow` is an implementation detail
// * `RowLifetimeHelper` is an internal backward compatibility helper
pub(crate) mod private {
use super::*;
/// This trait restricts who can implement `Row`
#[cfg_attr(
doc_cfg,
doc(cfg(feature = "i-implement-a-third-party-backend-and-opt-into-breaking-changes"))
)]
pub trait RowSealed {}
/// A row type that wraps an inner row
///
/// This type only allows to access fields of the inner row, whose index is
/// part of `range`. This type is used by diesel internally to implement
/// [`FromStaticSqlRow`](crate::deserialize::FromStaticSqlRow).
///
/// Indexing via `usize` starts with 0 for this row type. The index is then shifted
/// by `self.range.start` to match the corresponding field in the underlying row.
#[derive(Debug)]
#[cfg_attr(
feature = "i-implement-a-third-party-backend-and-opt-into-breaking-changes",
cfg(feature = "i-implement-a-third-party-backend-and-opt-into-breaking-changes")
)]
pub struct PartialRow<'a, R> {
inner: &'a R,
range: Range<usize>,
}
impl<'a, R> PartialRow<'a, R> {
/// Create a new [`PartialRow`] instance based on an inner
/// row and a range of field that should be part of the constructed
/// wrapper.
///
/// See the documentation of [`PartialRow`] for details.
pub fn new<'b, DB>(inner: &'a R, range: Range<usize>) -> Self
where
R: Row<'b, DB>,
DB: Backend,
{
let range_lower = std::cmp::min(range.start, inner.field_count());
let range_upper = std::cmp::min(range.end, inner.field_count());
Self {
inner,
range: range_lower..range_upper,
}
}
}
impl<'a, R> RowSealed for PartialRow<'a, R> {}
impl<'a, 'b, DB, R> Row<'a, DB> for PartialRow<'b, R>
where
DB: Backend,
R: Row<'a, DB>,
{
type Field<'f> = R::Field<'f> where 'a: 'f, R: 'f, Self: 'f;
type InnerPartialRow = R;
fn field_count(&self) -> usize {
self.range.len()
}
fn get<'c, I>(&'c self, idx: I) -> Option<Self::Field<'c>>
where
'a: 'c,
Self: RowIndex<I>,
{
let idx = self.idx(idx)?;
self.inner.get(idx)
}
fn partial_row(&self, range: Range<usize>) -> PartialRow<'_, R> {
let range_upper_bound = std::cmp::min(self.range.end, self.range.start + range.end);
let range = (self.range.start + range.start)..range_upper_bound;
PartialRow {
inner: self.inner,
range,
}
}
}
impl<'a, 'b, R> RowIndex<&'a str> for PartialRow<'b, R>
where
R: RowIndex<&'a str>,
{
fn idx(&self, idx: &'a str) -> Option<usize> {
let idx = self.inner.idx(idx)?;
if self.range.contains(&idx) {
Some(idx)
} else {
None
}
}
}
impl<'a, R> RowIndex<usize> for PartialRow<'a, R>
where
R: RowIndex<usize>,
{
fn idx(&self, idx: usize) -> Option<usize> {
let idx = self.inner.idx(idx + self.range.start)?;
if self.range.contains(&idx) {
Some(idx)
} else {
None
}
}
}
// These impls are only there for backward compatibility reasons
// Remove them on the next breaking release
#[cfg(all(feature = "with-deprecated", not(feature = "without-deprecated")))]
#[allow(unreachable_pub)]
pub trait RowLifetimeHelper<DB>: for<'a> super::Row<'a, DB>
where
DB: Backend,
{
type Field<'f>
where
Self: 'f;
}
#[cfg(all(feature = "with-deprecated", not(feature = "without-deprecated")))]
impl<R, DB> RowLifetimeHelper<DB> for R
where
DB: Backend,
for<'a> R: super::Row<'a, DB>,
{
type Field<'f> = <R as super::Row<'f, DB>>::Field<'f> where R: 'f;
}
}