Skip to main content

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