Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Parametrize Deserialize{Value,Row} with two lifetimes: 'frame and 'metadata separately #1101

Merged
2 changes: 1 addition & 1 deletion scylla-cql/src/frame/response/result.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1013,7 +1013,7 @@ pub fn deser_cql_value(
CqlValue::List(l)
}
Map(_key_type, _value_type) => {
let iter = MapIterator::<'_, CqlValue, CqlValue>::deserialize(typ, v)?;
let iter = MapIterator::<'_, '_, CqlValue, CqlValue>::deserialize(typ, v)?;
let m: Vec<(CqlValue, CqlValue)> = iter.collect::<StdResult<_, _>>()?;
CqlValue::Map(m)
}
Expand Down
58 changes: 50 additions & 8 deletions scylla-cql/src/types/deserialize/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,56 @@
//!
//! Deserialization is based on two traits:
//!
//! - A type that implements `DeserializeValue<'frame>` can be deserialized
//! - A type that implements `DeserializeValue<'frame, 'metadata>` can be deserialized
//! from a single _CQL value_ - i.e. an element of a row in the query result,
//! - A type that implements `DeserializeRow<'frame>` can be deserialized
//! - A type that implements `DeserializeRow<'frame, 'metadata>` can be deserialized
//! from a single _row_ of a query result.
//!
//! Those traits are quite similar to each other, both in the idea behind them
//! and the interface that they expose.
//!
//! It's important to understand what is a _deserialized type_. It's not just
//! an implementor of Deserialize{Value, Row}; there are some implementors of
//! `Deserialize{Value, Row}` who are not yet final types, but **partially**
//! deserialized types that support further deserialization - _type
//! deserializers_, such as `ListlikeIterator`, `UdtIterator` or `ColumnIterator`.
//!
//! # Lifetime parameters
//!
//! - `'frame` is the lifetime of the frame. Any deserialized type that is going to borrow
//! from the frame must have its lifetime bound by `'frame`.
//! - `'metadata` is the lifetime of the result metadata. As result metadata is only needed
//! for the very deserialization process and the **final** deserialized types (i.e. those
//! that are not going to deserialize anything else, opposite of e.g. `MapIterator`) can
//! later live independently of the metadata, this is different from `'frame`.
//!
//! _Type deserializers_, as they still need to deserialize some type, are naturally bound
//! by 'metadata lifetime. However, final types are completely deserialized, so they should
//! not be bound by 'metadata - only by 'frame.
//!
//! Rationale:
//! `DeserializeValue` requires two types of data in order to perform
//! deserialization:
//! 1) a reference to the CQL frame (a FrameSlice),
//! 2) the type of the column being deserialized, being part of the
//! ResultMetadata.
//!
//! Similarly, `DeserializeRow` requires two types of data in order to
//! perform deserialization:
//! 1) a reference to the CQL frame (a FrameSlice),
//! 2) a slice of specifications of all columns in the row, being part of
//! the ResultMetadata.
//!
//! When deserializing owned types, both the frame and the metadata can have
//! any lifetime and it's not important. When deserializing borrowed types,
//! however, they borrow from the frame, so their lifetime must necessarily
//! be bound by the lifetime of the frame. Metadata is only needed for the
//! deserialization, so its lifetime does not abstractly bound the
//! deserialized value. Not to unnecessarily shorten the deserialized
//! values' lifetime to the metadata's lifetime (due to unification of
//! metadata's and frame's lifetime in value deserializers), a separate
//! lifetime parameter is introduced for result metadata: `'metadata`.
//!
//! # `type_check` and `deserialize`
//!
//! The deserialization process is divided into two parts: type checking and
Expand Down Expand Up @@ -57,7 +99,7 @@
//! #[error("Expected non-null")]
//! ExpectedNonNull,
//! }
//! impl<'frame> DeserializeValue<'frame> for MyVec {
//! impl<'frame, 'metadata> DeserializeValue<'frame, 'metadata> for MyVec {
//! fn type_check(typ: &ColumnType) -> Result<(), TypeCheckError> {
//! if let ColumnType::Blob = typ {
//! return Ok(());
Expand All @@ -66,7 +108,7 @@
//! }
//!
//! fn deserialize(
//! _typ: &'frame ColumnType,
//! _typ: &'metadata ColumnType<'metadata>,
//! v: Option<FrameSlice<'frame>>,
//! ) -> Result<Self, DeserializationError> {
//! v.ok_or_else(|| DeserializationError::new(MyDeserError::ExpectedNonNull))
Expand Down Expand Up @@ -98,7 +140,7 @@
//! #[error("Expected non-null")]
//! ExpectedNonNull,
//! }
//! impl<'a, 'frame> DeserializeValue<'frame> for MySlice<'a>
//! impl<'a, 'frame, 'metadata> DeserializeValue<'frame, 'metadata> for MySlice<'a>
//! where
//! 'frame: 'a,
//! {
Expand All @@ -110,7 +152,7 @@
//! }
//!
//! fn deserialize(
//! _typ: &'frame ColumnType,
//! _typ: &'metadata ColumnType<'metadata>,
//! v: Option<FrameSlice<'frame>>,
//! ) -> Result<Self, DeserializationError> {
//! v.ok_or_else(|| DeserializationError::new(MyDeserError::ExpectedNonNull))
Expand Down Expand Up @@ -150,7 +192,7 @@
//! #[error("Expected non-null")]
//! ExpectedNonNull,
//! }
//! impl<'frame> DeserializeValue<'frame> for MyBytes {
//! impl<'frame, 'metadata> DeserializeValue<'frame, 'metadata> for MyBytes {
//! fn type_check(typ: &ColumnType) -> Result<(), TypeCheckError> {
//! if let ColumnType::Blob = typ {
//! return Ok(());
Expand All @@ -159,7 +201,7 @@
//! }
//!
//! fn deserialize(
//! _typ: &'frame ColumnType,
//! _typ: &'metadata ColumnType<'metadata>,
//! v: Option<FrameSlice<'frame>>,
//! ) -> Result<Self, DeserializationError> {
//! v.ok_or_else(|| DeserializationError::new(MyDeserError::ExpectedNonNull))
Expand Down
38 changes: 21 additions & 17 deletions scylla-cql/src/types/deserialize/result.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,20 +6,24 @@ use std::marker::PhantomData;

/// Iterates over the whole result, returning rows.
#[derive(Debug)]
pub struct RowIterator<'frame> {
specs: &'frame [ColumnSpec<'frame>],
pub struct RowIterator<'frame, 'metadata> {
specs: &'metadata [ColumnSpec<'metadata>],
remaining: usize,
slice: FrameSlice<'frame>,
}

impl<'frame> RowIterator<'frame> {
impl<'frame, 'metadata> RowIterator<'frame, 'metadata> {
/// Creates a new iterator over rows from a serialized response.
///
/// - `remaining` - number of the remaining rows in the serialized response,
/// - `specs` - information about columns of the serialized response,
/// - `slice` - a [FrameSlice] that points to the serialized rows data.
#[inline]
pub fn new(remaining: usize, specs: &'frame [ColumnSpec], slice: FrameSlice<'frame>) -> Self {
pub fn new(
remaining: usize,
specs: &'metadata [ColumnSpec<'metadata>],
slice: FrameSlice<'frame>,
) -> Self {
Self {
specs,
remaining,
Expand All @@ -29,7 +33,7 @@ impl<'frame> RowIterator<'frame> {

/// Returns information about the columns of rows that are iterated over.
#[inline]
pub fn specs(&self) -> &'frame [ColumnSpec] {
pub fn specs(&self) -> &'metadata [ColumnSpec<'metadata>] {
self.specs
}

Expand All @@ -41,8 +45,8 @@ impl<'frame> RowIterator<'frame> {
}
}

impl<'frame> Iterator for RowIterator<'frame> {
type Item = Result<ColumnIterator<'frame>, DeserializationError>;
impl<'frame, 'metadata> Iterator for RowIterator<'frame, 'metadata> {
type Item = Result<ColumnIterator<'frame, 'metadata>, DeserializationError>;

#[inline]
fn next(&mut self) -> Option<Self::Item> {
Expand Down Expand Up @@ -78,20 +82,20 @@ impl<'frame> Iterator for RowIterator<'frame> {
/// A typed version of [RowIterator] which deserializes the rows before
/// returning them.
#[derive(Debug)]
pub struct TypedRowIterator<'frame, R> {
inner: RowIterator<'frame>,
pub struct TypedRowIterator<'frame, 'metadata, R> {
inner: RowIterator<'frame, 'metadata>,
_phantom: PhantomData<R>,
}

impl<'frame, R> TypedRowIterator<'frame, R>
impl<'frame, 'metadata, R> TypedRowIterator<'frame, 'metadata, R>
where
R: DeserializeRow<'frame>,
R: DeserializeRow<'frame, 'metadata>,
{
/// Creates a new [TypedRowIterator] from given [RowIterator].
///
/// Calls `R::type_check` and fails if the type check fails.
#[inline]
pub fn new(raw: RowIterator<'frame>) -> Result<Self, TypeCheckError> {
pub fn new(raw: RowIterator<'frame, 'metadata>) -> Result<Self, TypeCheckError> {
R::type_check(raw.specs())?;
Ok(Self {
inner: raw,
Expand All @@ -101,7 +105,7 @@ where

/// Returns information about the columns of rows that are iterated over.
#[inline]
pub fn specs(&self) -> &'frame [ColumnSpec] {
pub fn specs(&self) -> &'metadata [ColumnSpec<'metadata>] {
self.inner.specs()
}

Expand All @@ -113,9 +117,9 @@ where
}
}

impl<'frame, R> Iterator for TypedRowIterator<'frame, R>
impl<'frame, 'metadata, R> Iterator for TypedRowIterator<'frame, 'metadata, R>
where
R: DeserializeRow<'frame>,
R: DeserializeRow<'frame, 'metadata>,
{
type Item = Result<R, DeserializationError>;

Expand Down Expand Up @@ -179,7 +183,7 @@ mod tests {
let raw_data = serialize_cells([Some(CELL1), Some(CELL2), Some(CELL2), Some(CELL1)]);
let specs = [spec("b1", ColumnType::Blob), spec("b2", ColumnType::Blob)];
let iter = RowIterator::new(2, &specs, FrameSlice::new(&raw_data));
let mut iter = TypedRowIterator::<'_, (&[u8], Vec<u8>)>::new(iter).unwrap();
let mut iter = TypedRowIterator::<'_, '_, (&[u8], Vec<u8>)>::new(iter).unwrap();

let (c11, c12) = iter.next().unwrap().unwrap();
assert_eq!(c11, CELL1);
Expand All @@ -197,6 +201,6 @@ mod tests {
let raw_data = Bytes::new();
let specs = [spec("b1", ColumnType::Blob), spec("b2", ColumnType::Blob)];
let iter = RowIterator::new(0, &specs, FrameSlice::new(&raw_data));
assert!(TypedRowIterator::<'_, (i32, i64)>::new(iter).is_err());
assert!(TypedRowIterator::<'_, '_, (i32, i64)>::new(iter).is_err());
}
}
43 changes: 24 additions & 19 deletions scylla-cql/src/types/deserialize/row.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,26 +10,29 @@ use crate::frame::response::result::{ColumnSpec, ColumnType, CqlValue, Row};

/// Represents a raw, unparsed column value.
#[non_exhaustive]
pub struct RawColumn<'frame> {
pub struct RawColumn<'frame, 'metadata> {
pub index: usize,
pub spec: &'frame ColumnSpec<'frame>,
pub spec: &'metadata ColumnSpec<'metadata>,
pub slice: Option<FrameSlice<'frame>>,
}

/// Iterates over columns of a single row.
#[derive(Clone, Debug)]
pub struct ColumnIterator<'frame> {
specs: std::iter::Enumerate<std::slice::Iter<'frame, ColumnSpec<'frame>>>,
pub struct ColumnIterator<'frame, 'metadata> {
specs: std::iter::Enumerate<std::slice::Iter<'metadata, ColumnSpec<'metadata>>>,
slice: FrameSlice<'frame>,
}

impl<'frame> ColumnIterator<'frame> {
impl<'frame, 'metadata> ColumnIterator<'frame, 'metadata> {
/// Creates a new iterator over a single row.
///
/// - `specs` - information about columns of the serialized response,
/// - `slice` - a [FrameSlice] which points to the serialized row.
#[inline]
pub(crate) fn new(specs: &'frame [ColumnSpec], slice: FrameSlice<'frame>) -> Self {
pub(crate) fn new(
specs: &'metadata [ColumnSpec<'metadata>],
slice: FrameSlice<'frame>,
) -> Self {
Self {
specs: specs.iter().enumerate(),
slice,
Expand All @@ -44,8 +47,8 @@ impl<'frame> ColumnIterator<'frame> {
}
}

impl<'frame> Iterator for ColumnIterator<'frame> {
type Item = Result<RawColumn<'frame>, DeserializationError>;
impl<'frame, 'metadata> Iterator for ColumnIterator<'frame, 'metadata> {
type Item = Result<RawColumn<'frame, 'metadata>, DeserializationError>;

#[inline]
fn next(&mut self) -> Option<Self::Item> {
Expand Down Expand Up @@ -84,7 +87,7 @@ impl<'frame> Iterator for ColumnIterator<'frame> {
/// The crate also provides a derive macro which allows to automatically
/// implement the trait for a custom type. For more details on what the macro
/// is capable of, see its documentation.
pub trait DeserializeRow<'frame>
pub trait DeserializeRow<'frame, 'metadata>
where
Self: Sized,
{
Expand All @@ -100,7 +103,7 @@ where
/// the row's type. Note that `deserialize` is not an unsafe function,
/// so it should not use the assumption about `type_check` being called
/// as an excuse to run `unsafe` code.
fn deserialize(row: ColumnIterator<'frame>) -> Result<Self, DeserializationError>;
fn deserialize(row: ColumnIterator<'frame, 'metadata>) -> Result<Self, DeserializationError>;
}

// raw deserialization as ColumnIterator
Expand All @@ -111,14 +114,14 @@ where
// Implementing DeserializeRow for it allows us to simplify our interface. For example,
// we have `QueryResult::rows<T: DeserializeRow>()` - you can put T = ColumnIterator
// instead of having a separate rows_raw function or something like this.
impl<'frame> DeserializeRow<'frame> for ColumnIterator<'frame> {
impl<'frame, 'metadata> DeserializeRow<'frame, 'metadata> for ColumnIterator<'frame, 'metadata> {
#[inline]
fn type_check(_specs: &[ColumnSpec]) -> Result<(), TypeCheckError> {
Ok(())
}

#[inline]
fn deserialize(row: ColumnIterator<'frame>) -> Result<Self, DeserializationError> {
fn deserialize(row: ColumnIterator<'frame, 'metadata>) -> Result<Self, DeserializationError> {
Ok(row)
}
}
Expand All @@ -140,15 +143,17 @@ make_error_replace_rust_name!(
/// While no longer encouraged (because the new framework encourages deserializing
/// directly into desired types, entirely bypassing [CqlValue]), this can be indispensable
/// for some use cases, i.e. those involving dynamic parsing (ORMs?).
impl<'frame> DeserializeRow<'frame> for Row {
impl<'frame, 'metadata> DeserializeRow<'frame, 'metadata> for Row {
#[inline]
fn type_check(_specs: &[ColumnSpec]) -> Result<(), TypeCheckError> {
// CqlValues accept all types, no type checking needed.
Ok(())
}

#[inline]
fn deserialize(mut row: ColumnIterator<'frame>) -> Result<Self, DeserializationError> {
fn deserialize(
mut row: ColumnIterator<'frame, 'metadata>,
) -> Result<Self, DeserializationError> {
let mut columns = Vec::with_capacity(row.size_hint().0);
while let Some(column) = row
.next()
Expand Down Expand Up @@ -181,17 +186,17 @@ impl<'frame> DeserializeRow<'frame> for Row {
/// and needed conversions, issuing meaningful errors in case something goes wrong.
macro_rules! impl_tuple {
($($Ti:ident),*; $($idx:literal),*; $($idf:ident),*) => {
impl<'frame, $($Ti),*> DeserializeRow<'frame> for ($($Ti,)*)
impl<'frame, 'metadata, $($Ti),*> DeserializeRow<'frame, 'metadata> for ($($Ti,)*)
where
$($Ti: DeserializeValue<'frame>),*
$($Ti: DeserializeValue<'frame, 'metadata>),*
{
fn type_check(specs: &[ColumnSpec]) -> Result<(), TypeCheckError> {
const TUPLE_LEN: usize = (&[$($idx),*] as &[i32]).len();

let column_types_iter = || specs.iter().map(|spec| spec.typ().clone().into_owned());
if let [$($idf),*] = &specs {
$(
<$Ti as DeserializeValue<'frame>>::type_check($idf.typ())
<$Ti as DeserializeValue<'frame, 'metadata>>::type_check($idf.typ())
.map_err(|err| mk_typck_err::<Self>(column_types_iter(), BuiltinTypeCheckErrorKind::ColumnTypeCheckFailed {
column_index: $idx,
column_name: specs[$idx].name().to_owned(),
Expand All @@ -206,7 +211,7 @@ macro_rules! impl_tuple {
}
}

fn deserialize(mut row: ColumnIterator<'frame>) -> Result<Self, DeserializationError> {
fn deserialize(mut row: ColumnIterator<'frame, 'metadata>) -> Result<Self, DeserializationError> {
const TUPLE_LEN: usize = (&[$($idx),*] as &[i32]).len();

let ret = (
Expand All @@ -217,7 +222,7 @@ macro_rules! impl_tuple {
$idx
)).map_err(deser_error_replace_rust_name::<Self>)?;

<$Ti as DeserializeValue<'frame>>::deserialize(column.spec.typ(), column.slice)
<$Ti as DeserializeValue<'frame, 'metadata>>::deserialize(column.spec.typ(), column.slice)
.map_err(|err| mk_deser_err::<Self>(BuiltinDeserializationErrorKind::ColumnDeserializationFailed {
column_index: column.index,
column_name: column.spec.name().to_owned(),
Expand Down
Loading
Loading