scylla_cql/deserialize/
row.rs

1//! Provides types for dealing with row deserialization.
2//!
3//! Those yield raw values, whose deserialization is handled by
4//! the `value` module.
5
6use std::fmt::Display;
7
8use thiserror::Error;
9
10use super::value::DeserializeValue;
11use super::{make_error_replace_rust_name, DeserializationError, FrameSlice, TypeCheckError};
12use crate::frame::response::result::{ColumnSpec, ColumnType};
13use crate::value::{CqlValue, Row};
14
15/// Represents a raw, unparsed column value.
16#[non_exhaustive]
17pub struct RawColumn<'frame, 'metadata> {
18    /// Index of the column in the row.
19    pub index: usize,
20
21    /// Specification of the column, including its name and type.
22    pub spec: &'metadata ColumnSpec<'metadata>,
23
24    /// Slice of the frame that contains the serialized value of the column.
25    /// None means that the column is null.
26    pub slice: Option<FrameSlice<'frame>>,
27}
28
29/// Iterates over columns of a single row.
30#[derive(Clone, Debug)]
31pub struct ColumnIterator<'frame, 'metadata> {
32    specs: std::iter::Enumerate<std::slice::Iter<'metadata, ColumnSpec<'metadata>>>,
33    slice: FrameSlice<'frame>,
34}
35
36impl<'frame, 'metadata> ColumnIterator<'frame, 'metadata> {
37    /// Creates a new iterator over a single row.
38    ///
39    /// - `specs` - information about columns of the serialized response,
40    /// - `slice` - a [FrameSlice] which points to the serialized row.
41    #[inline]
42    pub fn new(specs: &'metadata [ColumnSpec<'metadata>], slice: FrameSlice<'frame>) -> Self {
43        Self {
44            specs: specs.iter().enumerate(),
45            slice,
46        }
47    }
48
49    /// Returns the remaining number of columns that this iterator is expected
50    /// to return.
51    #[inline]
52    pub fn columns_remaining(&self) -> usize {
53        self.specs.len()
54    }
55}
56
57impl<'frame, 'metadata> Iterator for ColumnIterator<'frame, 'metadata> {
58    type Item = Result<RawColumn<'frame, 'metadata>, DeserializationError>;
59
60    #[inline]
61    fn next(&mut self) -> Option<Self::Item> {
62        let (column_index, spec) = self.specs.next()?;
63        Some(
64            self.slice
65                .read_cql_bytes()
66                .map(|slice| RawColumn {
67                    index: column_index,
68                    spec,
69                    slice,
70                })
71                .map_err(|err| {
72                    mk_deser_err::<Self>(
73                        BuiltinDeserializationErrorKind::RawColumnDeserializationFailed {
74                            column_index,
75                            column_name: spec.name().to_owned(),
76                            err: DeserializationError::new(err),
77                        },
78                    )
79                }),
80        )
81    }
82
83    #[inline]
84    fn size_hint(&self) -> (usize, Option<usize>) {
85        self.specs.size_hint()
86    }
87}
88
89/// A type that can be deserialized from a row that was returned from a query.
90///
91/// For tips on how to write a custom implementation of this trait, see the
92/// documentation of the parent module.
93///
94/// The crate also provides a derive macro which allows to automatically
95/// implement the trait for a custom type. For more details on what the macro
96/// is capable of, see its documentation.
97pub trait DeserializeRow<'frame, 'metadata>
98where
99    Self: Sized,
100{
101    /// Checks that the schema of the result matches what this type expects.
102    ///
103    /// This function can check whether column types and names match the
104    /// expectations.
105    fn type_check(specs: &[ColumnSpec]) -> Result<(), TypeCheckError>;
106
107    /// Deserializes a row from given column iterator.
108    ///
109    /// This function can assume that the driver called `type_check` to verify
110    /// the row's type. Note that `deserialize` is not an unsafe function,
111    /// so it should not use the assumption about `type_check` being called
112    /// as an excuse to run `unsafe` code.
113    fn deserialize(row: ColumnIterator<'frame, 'metadata>) -> Result<Self, DeserializationError>;
114}
115
116// raw deserialization as ColumnIterator
117
118// What is the purpose of implementing DeserializeRow for ColumnIterator?
119//
120// Sometimes users might be interested in operating on ColumnIterator directly.
121// Implementing DeserializeRow for it allows us to simplify our interface. For example,
122// we have `QueryResult::rows<T: DeserializeRow>()` - you can put T = ColumnIterator
123// instead of having a separate rows_raw function or something like this.
124impl<'frame, 'metadata> DeserializeRow<'frame, 'metadata> for ColumnIterator<'frame, 'metadata> {
125    #[inline]
126    fn type_check(_specs: &[ColumnSpec]) -> Result<(), TypeCheckError> {
127        Ok(())
128    }
129
130    #[inline]
131    fn deserialize(row: ColumnIterator<'frame, 'metadata>) -> Result<Self, DeserializationError> {
132        Ok(row)
133    }
134}
135
136make_error_replace_rust_name!(
137    pub(self),
138    _typck_error_replace_rust_name,
139    TypeCheckError,
140    BuiltinTypeCheckError
141);
142
143make_error_replace_rust_name!(
144    pub,
145    deser_error_replace_rust_name,
146    DeserializationError,
147    BuiltinDeserializationError
148);
149
150// legacy/dynamic deserialization as Row
151//
152/// While no longer encouraged (because the new framework encourages deserializing
153/// directly into desired types, entirely bypassing [CqlValue]), this can be indispensable
154/// for some use cases, i.e. those involving dynamic parsing (ORMs?).
155impl<'frame, 'metadata> DeserializeRow<'frame, 'metadata> for Row {
156    #[inline]
157    fn type_check(_specs: &[ColumnSpec]) -> Result<(), TypeCheckError> {
158        // CqlValues accept all types, no type checking needed.
159        Ok(())
160    }
161
162    #[inline]
163    fn deserialize(
164        mut row: ColumnIterator<'frame, 'metadata>,
165    ) -> Result<Self, DeserializationError> {
166        let mut columns = Vec::with_capacity(row.size_hint().0);
167        while let Some(column) = row
168            .next()
169            .transpose()
170            .map_err(deser_error_replace_rust_name::<Self>)?
171        {
172            columns.push(
173                <Option<CqlValue>>::deserialize(column.spec.typ(), column.slice).map_err(
174                    |err| {
175                        mk_deser_err::<Self>(
176                            BuiltinDeserializationErrorKind::ColumnDeserializationFailed {
177                                column_index: column.index,
178                                column_name: column.spec.name().to_owned(),
179                                err,
180                            },
181                        )
182                    },
183                )?,
184            );
185        }
186        Ok(Self { columns })
187    }
188}
189
190// tuples
191//
192/// This is the new encouraged way for deserializing a row.
193/// If only you know the exact column types in advance, you had better deserialize the row
194/// to a tuple. The new deserialization framework will take care of all type checking
195/// and needed conversions, issuing meaningful errors in case something goes wrong.
196macro_rules! impl_tuple {
197    ($($Ti:ident),*; $($idx:literal),*; $($idf:ident),*) => {
198        impl<'frame, 'metadata, $($Ti),*> DeserializeRow<'frame, 'metadata> for ($($Ti,)*)
199        where
200            $($Ti: DeserializeValue<'frame, 'metadata>),*
201        {
202            fn type_check(specs: &[ColumnSpec]) -> Result<(), TypeCheckError> {
203                const TUPLE_LEN: usize = (&[$($idx),*] as &[i32]).len();
204
205                let column_types_iter = || specs.iter().map(|spec| spec.typ().clone().into_owned());
206                if let [$($idf),*] = &specs {
207                    $(
208                        <$Ti as DeserializeValue<'frame, 'metadata>>::type_check($idf.typ())
209                            .map_err(|err| mk_typck_err::<Self>(column_types_iter(), BuiltinTypeCheckErrorKind::ColumnTypeCheckFailed {
210                                column_index: $idx,
211                                column_name: specs[$idx].name().to_owned(),
212                                err
213                            }))?;
214                    )*
215                    Ok(())
216                } else {
217                    Err(mk_typck_err::<Self>(column_types_iter(), BuiltinTypeCheckErrorKind::WrongColumnCount {
218                        rust_cols: TUPLE_LEN, cql_cols: specs.len()
219                    }))
220                }
221            }
222
223            fn deserialize(mut row: ColumnIterator<'frame, 'metadata>) -> Result<Self, DeserializationError> {
224                const TUPLE_LEN: usize = (&[$($idx),*] as &[i32]).len();
225
226                let ret = (
227                    $({
228                        let column = row.next().unwrap_or_else(|| unreachable!(
229                            "Typecheck should have prevented this scenario! Column count mismatch: rust type {}, cql row {}",
230                            TUPLE_LEN,
231                            $idx
232                        )).map_err(deser_error_replace_rust_name::<Self>)?;
233
234                        <$Ti as DeserializeValue<'frame, 'metadata>>::deserialize(column.spec.typ(), column.slice)
235                            .map_err(|err| mk_deser_err::<Self>(BuiltinDeserializationErrorKind::ColumnDeserializationFailed {
236                                column_index: column.index,
237                                column_name: column.spec.name().to_owned(),
238                                err,
239                            }))?
240                    },)*
241                );
242                assert!(
243                    row.next().is_none(),
244                    "Typecheck should have prevented this scenario! Column count mismatch: rust type {}, cql row is bigger",
245                    TUPLE_LEN,
246                );
247                Ok(ret)
248            }
249        }
250    }
251}
252
253use super::value::impl_tuple_multiple;
254
255// Implements row-to-tuple deserialization for all tuple sizes up to 16.
256impl_tuple_multiple!(
257    T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15;
258    0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15;
259    t0, t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11, t12, t13, t14, t15
260);
261
262// Error facilities
263
264/// Failed to type check incoming result column types again given Rust type,
265/// one of the types having support built into the driver.
266#[derive(Debug, Error, Clone)]
267#[error("Failed to type check the Rust type {rust_name} against CQL column types {cql_types:?} : {kind}")]
268pub struct BuiltinTypeCheckError {
269    /// Name of the Rust type used to represent the values.
270    pub rust_name: &'static str,
271
272    /// The CQL types of the values that the Rust type was being deserialized from.
273    pub cql_types: Vec<ColumnType<'static>>,
274
275    /// Detailed information about the failure.
276    pub kind: BuiltinTypeCheckErrorKind,
277}
278
279// Not part of the public API; used in derive macros.
280#[doc(hidden)]
281pub fn mk_typck_err<T>(
282    cql_types: impl IntoIterator<Item = ColumnType<'static>>,
283    kind: impl Into<BuiltinTypeCheckErrorKind>,
284) -> TypeCheckError {
285    mk_typck_err_named(std::any::type_name::<T>(), cql_types, kind)
286}
287
288fn mk_typck_err_named(
289    name: &'static str,
290    cql_types: impl IntoIterator<Item = ColumnType<'static>>,
291    kind: impl Into<BuiltinTypeCheckErrorKind>,
292) -> TypeCheckError {
293    TypeCheckError::new(BuiltinTypeCheckError {
294        rust_name: name,
295        cql_types: Vec::from_iter(cql_types),
296        kind: kind.into(),
297    })
298}
299
300/// Describes why type checking incoming result column types again given Rust type failed.
301#[derive(Debug, Clone)]
302#[non_exhaustive]
303pub enum BuiltinTypeCheckErrorKind {
304    /// The Rust type expects `rust_cols` columns, but the statement operates on `cql_cols`.
305    WrongColumnCount {
306        /// The number of values that the Rust type provides.
307        rust_cols: usize,
308
309        /// The number of columns that the statement operates on.
310        cql_cols: usize,
311    },
312
313    /// The CQL row contains a column for which a corresponding field is not found
314    /// in the Rust type.
315    ColumnWithUnknownName {
316        /// Index of the excess column.
317        column_index: usize,
318
319        /// Name of the column that is present in CQL row but not in the Rust type.
320        column_name: String,
321    },
322
323    /// Several values required by the Rust type are not provided by the DB.
324    ValuesMissingForColumns {
325        /// Names of the columns in the Rust type for which the DB doesn't
326        /// provide value.
327        column_names: Vec<&'static str>,
328    },
329
330    /// A different column name was expected at given position.
331    ColumnNameMismatch {
332        /// Index of the field determining the expected name.
333        field_index: usize,
334
335        /// Index of the column having mismatched name.
336        column_index: usize,
337
338        /// Name of the column, as expected by the Rust type.
339        rust_column_name: &'static str,
340
341        /// Name of the column for which the DB requested a value.
342        db_column_name: String,
343    },
344
345    /// Column type check failed between Rust type and DB type at given position (=in given column).
346    ColumnTypeCheckFailed {
347        /// Index of the column.
348        column_index: usize,
349
350        /// Name of the column, as provided by the DB.
351        column_name: String,
352
353        /// Inner type check error due to the type mismatch.
354        err: TypeCheckError,
355    },
356
357    /// Duplicated column in DB metadata.
358    DuplicatedColumn {
359        /// Column index of the second occurrence of the column with the same name.
360        column_index: usize,
361
362        /// The name of the duplicated column.
363        column_name: &'static str,
364    },
365}
366
367impl Display for BuiltinTypeCheckErrorKind {
368    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
369        match self {
370            BuiltinTypeCheckErrorKind::WrongColumnCount {
371                rust_cols,
372                cql_cols,
373            } => {
374                write!(f, "wrong column count: the statement operates on {cql_cols} columns, but the given rust types contains {rust_cols}")
375            }
376            BuiltinTypeCheckErrorKind::ColumnWithUnknownName { column_name, column_index } => {
377                write!(
378                    f,
379                    "the CQL row contains a column {column_name} at column index {column_index}, but the corresponding field is not found in the Rust type",
380                )
381            }
382            BuiltinTypeCheckErrorKind::ValuesMissingForColumns { column_names } => {
383                write!(
384                    f,
385                    "values for columns {column_names:?} are missing from the DB data but are required by the Rust type"
386                )
387            },
388            BuiltinTypeCheckErrorKind::ColumnNameMismatch {
389                field_index,
390                column_index,rust_column_name,
391                db_column_name
392            } => write!(
393                f,
394                "expected column with name {db_column_name} at column index {column_index}, but the Rust field name at corresponding field index {field_index} is {rust_column_name}",
395            ),
396            BuiltinTypeCheckErrorKind::ColumnTypeCheckFailed {
397                column_index,
398                column_name,
399                err,
400            } => write!(
401                f,
402                "mismatched types in column {column_name} at index {column_index}: {err}"
403            ),
404            BuiltinTypeCheckErrorKind::DuplicatedColumn { column_name, column_index } => write!(
405                f,
406                "column {column_name} occurs more than once in DB metadata; second occurrence is at column index {column_index}",
407            ),
408        }
409    }
410}
411
412/// Failed to deserialize a row from the DB response, represented by one of the types
413/// built into the driver.
414#[derive(Debug, Error, Clone)]
415#[error("Failed to deserialize query result row {rust_name}: {kind}")]
416pub struct BuiltinDeserializationError {
417    /// Name of the Rust type used to represent the row.
418    pub rust_name: &'static str,
419
420    /// Detailed information about the failure.
421    pub kind: BuiltinDeserializationErrorKind,
422}
423
424// Not part of the public API; used in derive macros.
425#[doc(hidden)]
426pub fn mk_deser_err<T>(kind: impl Into<BuiltinDeserializationErrorKind>) -> DeserializationError {
427    mk_deser_err_named(std::any::type_name::<T>(), kind)
428}
429
430fn mk_deser_err_named(
431    name: &'static str,
432    kind: impl Into<BuiltinDeserializationErrorKind>,
433) -> DeserializationError {
434    DeserializationError::new(BuiltinDeserializationError {
435        rust_name: name,
436        kind: kind.into(),
437    })
438}
439
440/// Describes why deserializing a result row failed.
441#[derive(Debug, Clone)]
442#[non_exhaustive]
443pub enum BuiltinDeserializationErrorKind {
444    /// One of the columns failed to deserialize.
445    ColumnDeserializationFailed {
446        /// Index of the column that failed to deserialize.
447        column_index: usize,
448
449        /// Name of the column that failed to deserialize.
450        column_name: String,
451
452        /// The error that caused the column deserialization to fail.
453        err: DeserializationError,
454    },
455
456    /// One of the raw columns failed to deserialize, most probably
457    /// due to the invalid column structure inside a row in the frame.
458    RawColumnDeserializationFailed {
459        /// Index of the raw column that failed to deserialize.
460        column_index: usize,
461
462        /// Name of the raw column that failed to deserialize.
463        column_name: String,
464
465        /// The error that caused the raw column deserialization to fail.
466        err: DeserializationError,
467    },
468}
469
470impl Display for BuiltinDeserializationErrorKind {
471    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
472        match self {
473            BuiltinDeserializationErrorKind::ColumnDeserializationFailed {
474                column_index,
475                column_name,
476                err,
477            } => {
478                write!(
479                    f,
480                    "failed to deserialize column {column_name} at index {column_index}: {err}"
481                )
482            }
483            BuiltinDeserializationErrorKind::RawColumnDeserializationFailed {
484                column_index,
485                column_name,
486                err,
487            } => {
488                write!(
489                    f,
490                    "failed to deserialize raw column {column_name} at index {column_index} (most probably due to invalid column structure inside a row): {err}"
491                )
492            }
493        }
494    }
495}
496
497#[cfg(test)]
498#[path = "row_tests.rs"]
499pub(crate) mod tests;
500
501/// ```compile_fail
502///
503/// #[derive(scylla_macros::DeserializeRow)]
504/// #[scylla(crate = scylla_cql, skip_name_checks)]
505/// struct TestRow {}
506/// ```
507fn _test_struct_deserialization_name_check_skip_requires_enforce_order() {}
508
509/// ```compile_fail
510///
511/// #[derive(scylla_macros::DeserializeRow)]
512/// #[scylla(crate = scylla_cql, skip_name_checks)]
513/// struct TestRow {
514///     #[scylla(rename = "b")]
515///     a: i32,
516/// }
517/// ```
518fn _test_struct_deserialization_skip_name_check_conflicts_with_rename() {}
519
520/// ```compile_fail
521///
522/// #[derive(scylla_macros::DeserializeRow)]
523/// #[scylla(crate = scylla_cql)]
524/// struct TestRow {
525///     #[scylla(rename = "b")]
526///     a: i32,
527///     b: String,
528/// }
529/// ```
530fn _test_struct_deserialization_skip_rename_collision_with_field() {}
531
532/// ```compile_fail
533///
534/// #[derive(scylla_macros::DeserializeRow)]
535/// #[scylla(crate = scylla_cql)]
536/// struct TestRow {
537///     #[scylla(rename = "c")]
538///     a: i32,
539///     #[scylla(rename = "c")]
540///     b: String,
541/// }
542/// ```
543fn _test_struct_deserialization_rename_collision_with_another_rename() {}