scylla_cql/deserialize/
row.rs

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