scylla_cql/deserialize/
result.rs

1//! Provides types for dealing with query result deserialization.
2//!
3//! Those yield raw rows, whose deserialization is handled by
4//! the `row` module.
5
6use bytes::Bytes;
7
8use crate::frame::response::result::{
9    ColumnSpec, DeserializedMetadataAndRawRows, ResultMetadata, ResultMetadataHolder,
10};
11
12use super::row::{mk_deser_err, BuiltinDeserializationErrorKind, ColumnIterator, DeserializeRow};
13use super::{DeserializationError, FrameSlice, TypeCheckError};
14use std::marker::PhantomData;
15
16/// Iterates over the whole result, returning raw rows.
17#[derive(Debug)]
18pub struct RawRowIterator<'frame, 'metadata> {
19    specs: &'metadata [ColumnSpec<'metadata>],
20    remaining: usize,
21    slice: FrameSlice<'frame>,
22}
23
24impl<'frame, 'metadata> RawRowIterator<'frame, 'metadata> {
25    /// Creates a new iterator over raw rows from a serialized response.
26    ///
27    /// - `remaining` - number of the remaining rows in the serialized response,
28    /// - `specs` - information about columns of the serialized response,
29    /// - `slice` - a [FrameSlice] that points to the serialized rows data.
30    #[inline]
31    pub fn new(
32        remaining: usize,
33        specs: &'metadata [ColumnSpec<'metadata>],
34        slice: FrameSlice<'frame>,
35    ) -> Self {
36        Self {
37            specs,
38            remaining,
39            slice,
40        }
41    }
42
43    /// Returns information about the columns of rows that are iterated over.
44    #[inline]
45    pub fn specs(&self) -> &'metadata [ColumnSpec<'metadata>] {
46        self.specs
47    }
48
49    /// Returns the remaining number of rows that this iterator is supposed
50    /// to return.
51    #[inline]
52    pub fn rows_remaining(&self) -> usize {
53        self.remaining
54    }
55}
56
57impl<'frame, 'metadata> Iterator for RawRowIterator<'frame, 'metadata> {
58    type Item = Result<ColumnIterator<'frame, 'metadata>, DeserializationError>;
59
60    #[inline]
61    fn next(&mut self) -> Option<Self::Item> {
62        self.remaining = self.remaining.checked_sub(1)?;
63
64        let iter = ColumnIterator::new(self.specs, self.slice);
65
66        // Skip the row here, manually
67        for (column_index, spec) in self.specs.iter().enumerate() {
68            if let Err(err) = self.slice.read_cql_bytes() {
69                return Some(Err(mk_deser_err::<Self>(
70                    BuiltinDeserializationErrorKind::RawColumnDeserializationFailed {
71                        column_index,
72                        column_name: spec.name().to_owned(),
73                        err: DeserializationError::new(err),
74                    },
75                )));
76            }
77        }
78
79        Some(Ok(iter))
80    }
81
82    #[inline]
83    fn size_hint(&self) -> (usize, Option<usize>) {
84        // The iterator will always return exactly `self.remaining`
85        // elements: Oks until an error is encountered and then Errs
86        // containing that same first encountered error.
87        (self.remaining, Some(self.remaining))
88    }
89}
90
91/// A typed version of [RawRowIterator] which deserializes the rows before
92/// returning them.
93#[derive(Debug)]
94pub struct TypedRowIterator<'frame, 'metadata, R> {
95    inner: RawRowIterator<'frame, 'metadata>,
96    _phantom: PhantomData<R>,
97}
98
99impl<'frame, 'metadata, R> TypedRowIterator<'frame, 'metadata, R>
100where
101    R: DeserializeRow<'frame, 'metadata>,
102{
103    /// Creates a new [TypedRowIterator] from given [RawRowIterator].
104    ///
105    /// Calls `R::type_check` and fails if the type check fails.
106    #[inline]
107    pub fn new(raw: RawRowIterator<'frame, 'metadata>) -> Result<Self, TypeCheckError> {
108        R::type_check(raw.specs())?;
109        Ok(Self {
110            inner: raw,
111            _phantom: PhantomData,
112        })
113    }
114
115    /// Returns information about the columns of rows that are iterated over.
116    #[inline]
117    pub fn specs(&self) -> &'metadata [ColumnSpec<'metadata>] {
118        self.inner.specs()
119    }
120
121    /// Returns the remaining number of rows that this iterator is supposed
122    /// to return.
123    #[inline]
124    pub fn rows_remaining(&self) -> usize {
125        self.inner.rows_remaining()
126    }
127}
128
129impl<'frame, 'metadata, R> Iterator for TypedRowIterator<'frame, 'metadata, R>
130where
131    R: DeserializeRow<'frame, 'metadata>,
132{
133    type Item = Result<R, DeserializationError>;
134
135    #[inline]
136    fn next(&mut self) -> Option<Self::Item> {
137        self.inner
138            .next()
139            .map(|raw| raw.and_then(|raw| R::deserialize(raw)))
140    }
141
142    #[inline]
143    fn size_hint(&self) -> (usize, Option<usize>) {
144        self.inner.size_hint()
145    }
146}
147
148// Technically not an iterator because it returns items that borrow from it,
149// and the std Iterator interface does not allow for that.
150/// A _lending_ iterator over serialized rows.
151///
152/// This type is similar to `RawRowIterator`, but keeps ownership of the serialized
153/// result. Because it returns `ColumnIterator`s that need to borrow from it,
154/// it does not implement the `Iterator` trait (there is no type in the standard
155/// library to represent this concept yet).
156#[derive(Debug)]
157pub struct RawRowLendingIterator {
158    metadata: ResultMetadataHolder,
159    remaining: usize,
160    at: usize,
161    raw_rows: Bytes,
162}
163
164impl RawRowLendingIterator {
165    /// Creates a new `RawRowLendingIterator`, consuming given `RawRows`.
166    #[inline]
167    pub fn new(raw_rows: DeserializedMetadataAndRawRows) -> Self {
168        let (metadata, rows_count, raw_rows) = raw_rows.into_inner();
169        Self {
170            metadata,
171            remaining: rows_count,
172            at: 0,
173            raw_rows,
174        }
175    }
176
177    /// Returns a `ColumnIterator` that represents the next row.
178    ///
179    /// Note: the `ColumnIterator` borrows from the `RawRowLendingIterator`.
180    /// The column iterator must be consumed before the rows iterator can
181    /// continue.
182    #[inline]
183    #[expect(clippy::should_implement_trait)] // https://github.com/rust-lang/rust-clippy/issues/5004
184    pub fn next(&mut self) -> Option<Result<ColumnIterator, DeserializationError>> {
185        self.remaining = self.remaining.checked_sub(1)?;
186
187        // First create the slice encompassing the whole frame.
188        let mut remaining_frame = FrameSlice::new(&self.raw_rows);
189        // Then slice it to encompass the remaining suffix of the frame.
190        *remaining_frame.as_slice_mut() = &remaining_frame.as_slice()[self.at..];
191        // Ideally, we would prefer to preserve the FrameSlice between calls to `next()`,
192        // but borrowing from oneself is impossible, so we have to recreate it this way.
193
194        let iter = ColumnIterator::new(self.metadata.inner().col_specs(), remaining_frame);
195
196        // Skip the row here, manually
197        for (column_index, spec) in self.metadata.inner().col_specs().iter().enumerate() {
198            let remaining_frame_len_before_column_read = remaining_frame.as_slice().len();
199            if let Err(err) = remaining_frame.read_cql_bytes() {
200                return Some(Err(mk_deser_err::<Self>(
201                    BuiltinDeserializationErrorKind::RawColumnDeserializationFailed {
202                        column_index,
203                        column_name: spec.name().to_owned(),
204                        err: DeserializationError::new(err),
205                    },
206                )));
207            } else {
208                let remaining_frame_len_after_column_read = remaining_frame.as_slice().len();
209                self.at +=
210                    remaining_frame_len_before_column_read - remaining_frame_len_after_column_read;
211            }
212        }
213
214        Some(Ok(iter))
215    }
216
217    /// Returns the bounds on the remaining length of the iterator.
218    ///
219    /// This is analogous to [Iterator::size_hint].
220    #[inline]
221    pub fn size_hint(&self) -> (usize, Option<usize>) {
222        // next() is written in a way that it does not progress on error, so once an error
223        // is encountered, the same error keeps being returned until `self.remaining`
224        // elements are yielded in total.
225        (self.remaining, Some(self.remaining))
226    }
227
228    /// Returns the metadata associated with the response (paging state and
229    /// column specifications).
230    #[inline]
231    pub fn metadata(&self) -> &ResultMetadata<'_> {
232        self.metadata.inner()
233    }
234
235    /// Returns the remaining number of rows that this iterator is expected
236    /// to produce.
237    #[inline]
238    pub fn rows_remaining(&self) -> usize {
239        self.remaining
240    }
241}
242
243#[cfg(test)]
244mod tests {
245
246    use bytes::Bytes;
247    use std::ops::Deref;
248    use std::sync::LazyLock;
249
250    use crate::frame::response::result::{
251        ColumnSpec, ColumnType, DeserializedMetadataAndRawRows, NativeType, ResultMetadata,
252    };
253
254    use super::super::tests::{serialize_cells, spec, CELL1, CELL2};
255    use super::{
256        ColumnIterator, DeserializationError, FrameSlice, RawRowIterator, RawRowLendingIterator,
257        TypedRowIterator,
258    };
259
260    trait LendingIterator {
261        type Item<'borrow>
262        where
263            Self: 'borrow;
264        fn lend_next(&mut self) -> Option<Result<Self::Item<'_>, DeserializationError>>;
265    }
266
267    impl<'frame, 'metadata> LendingIterator for RawRowIterator<'frame, 'metadata> {
268        type Item<'borrow>
269            = ColumnIterator<'borrow, 'borrow>
270        where
271            Self: 'borrow;
272
273        fn lend_next(&mut self) -> Option<Result<ColumnIterator<'_, '_>, DeserializationError>> {
274            self.next()
275        }
276    }
277
278    impl LendingIterator for RawRowLendingIterator {
279        type Item<'borrow> = ColumnIterator<'borrow, 'borrow>;
280
281        fn lend_next(&mut self) -> Option<Result<ColumnIterator<'_, '_>, DeserializationError>> {
282            self.next()
283        }
284    }
285
286    #[test]
287    fn test_row_iterators_basic_parse() {
288        // Those statics are required because of a compiler bug-limitation about GATs:
289        // https://blog.rust-lang.org/2022/10/28/gats-stabilization.html#implied-static-requirement-from-higher-ranked-trait-bounds
290        // the following type higher-ranked lifetime constraint implies 'static lifetime.
291        //
292        // I: for<'item> LendingIterator<Item<'item> = ColumnIterator<'item>>,
293        //
294        // The bug is said to be a lot of effort to fix, so in tests let's just use `LazyLock`
295        // to obtain 'static references.
296
297        static SPECS: &[ColumnSpec<'static>] = &[
298            spec("b1", ColumnType::Native(NativeType::Blob)),
299            spec("b2", ColumnType::Native(NativeType::Blob)),
300        ];
301        static RAW_DATA: LazyLock<Bytes> =
302            LazyLock::new(|| serialize_cells([Some(CELL1), Some(CELL2), Some(CELL2), Some(CELL1)]));
303        let raw_data = RAW_DATA.deref();
304        let specs = SPECS;
305
306        let row_iter = RawRowIterator::new(2, specs, FrameSlice::new(raw_data));
307        let lending_row_iter =
308            RawRowLendingIterator::new(DeserializedMetadataAndRawRows::new_for_test(
309                ResultMetadata::new_for_test(specs.len(), specs.to_vec()),
310                2,
311                raw_data.clone(),
312            ));
313        check(row_iter);
314        check(lending_row_iter);
315
316        fn check<I>(mut iter: I)
317        where
318            I: for<'item> LendingIterator<Item<'item> = ColumnIterator<'item, 'item>>,
319        {
320            let mut row1 = iter.lend_next().unwrap().unwrap();
321            let c11 = row1.next().unwrap().unwrap();
322            assert_eq!(c11.slice.unwrap().as_slice(), CELL1);
323            let c12 = row1.next().unwrap().unwrap();
324            assert_eq!(c12.slice.unwrap().as_slice(), CELL2);
325            assert!(row1.next().is_none());
326
327            let mut row2 = iter.lend_next().unwrap().unwrap();
328            let c21 = row2.next().unwrap().unwrap();
329            assert_eq!(c21.slice.unwrap().as_slice(), CELL2);
330            let c22 = row2.next().unwrap().unwrap();
331            assert_eq!(c22.slice.unwrap().as_slice(), CELL1);
332            assert!(row2.next().is_none());
333
334            assert!(iter.lend_next().is_none());
335        }
336    }
337
338    #[test]
339    fn test_row_iterators_too_few_rows() {
340        static SPECS: &[ColumnSpec<'static>] = &[
341            spec("b1", ColumnType::Native(NativeType::Blob)),
342            spec("b2", ColumnType::Native(NativeType::Blob)),
343        ];
344        static RAW_DATA: LazyLock<Bytes> =
345            LazyLock::new(|| serialize_cells([Some(CELL1), Some(CELL2)]));
346
347        let raw_data = RAW_DATA.deref();
348        let specs = SPECS;
349
350        let row_iter = RawRowIterator::new(2, specs, FrameSlice::new(raw_data));
351        let lending_row_iter =
352            RawRowLendingIterator::new(DeserializedMetadataAndRawRows::new_for_test(
353                ResultMetadata::new_for_test(specs.len(), specs.to_vec()),
354                2,
355                raw_data.clone(),
356            ));
357        check(row_iter);
358        check(lending_row_iter);
359
360        fn check<I>(mut iter: I)
361        where
362            I: for<'item> LendingIterator<Item<'item> = ColumnIterator<'item, 'item>>,
363        {
364            iter.lend_next().unwrap().unwrap();
365            iter.lend_next().unwrap().unwrap_err();
366        }
367    }
368
369    #[test]
370    fn test_typed_row_iterator_basic_parse() {
371        let raw_data = serialize_cells([Some(CELL1), Some(CELL2), Some(CELL2), Some(CELL1)]);
372        let specs = [
373            spec("b1", ColumnType::Native(NativeType::Blob)),
374            spec("b2", ColumnType::Native(NativeType::Blob)),
375        ];
376        let iter = RawRowIterator::new(2, &specs, FrameSlice::new(&raw_data));
377        let mut iter = TypedRowIterator::<'_, '_, (&[u8], Vec<u8>)>::new(iter).unwrap();
378
379        let (c11, c12) = iter.next().unwrap().unwrap();
380        assert_eq!(c11, CELL1);
381        assert_eq!(c12, CELL2);
382
383        let (c21, c22) = iter.next().unwrap().unwrap();
384        assert_eq!(c21, CELL2);
385        assert_eq!(c22, CELL1);
386
387        assert!(iter.next().is_none());
388    }
389
390    #[test]
391    fn test_typed_row_iterator_wrong_type() {
392        let raw_data = Bytes::new();
393        let specs = [
394            spec("b1", ColumnType::Native(NativeType::Blob)),
395            spec("b2", ColumnType::Native(NativeType::Blob)),
396        ];
397        let iter = RawRowIterator::new(0, &specs, FrameSlice::new(&raw_data));
398        assert!(TypedRowIterator::<'_, '_, (i32, i64)>::new(iter).is_err());
399    }
400}