scylla/response/
query_result.rs

1use std::fmt::Debug;
2
3use thiserror::Error;
4use uuid::Uuid;
5
6use scylla_cql::deserialize::result::TypedRowIterator;
7use scylla_cql::deserialize::row::DeserializeRow;
8use scylla_cql::deserialize::{DeserializationError, TypeCheckError};
9use scylla_cql::frame::frame_errors::ResultMetadataAndRowsCountParseError;
10use scylla_cql::frame::response::result::{
11    ColumnSpec, DeserializedMetadataAndRawRows, RawMetadataAndRawRows,
12};
13
14/// A view over specification of columns returned by the database.
15#[derive(Debug, Clone, Copy)]
16pub struct ColumnSpecs<'slice, 'spec> {
17    specs: &'slice [ColumnSpec<'spec>],
18}
19
20impl<'slice, 'spec> ColumnSpecs<'slice, 'spec> {
21    /// Creates new [`ColumnSpecs`] wrapper from a slice.
22    pub fn new(specs: &'slice [ColumnSpec<'spec>]) -> Self {
23        Self { specs }
24    }
25
26    /// Returns a slice of col specs encompassed by this struct.
27    pub fn as_slice(&self) -> &'slice [ColumnSpec<'spec>] {
28        self.specs
29    }
30
31    /// Returns number of columns.
32    #[allow(clippy::len_without_is_empty)]
33    #[inline]
34    pub fn len(&self) -> usize {
35        self.specs.len()
36    }
37
38    /// Returns specification of k-th column returned from the database.
39    #[inline]
40    pub fn get_by_index(&self, k: usize) -> Option<&'slice ColumnSpec<'spec>> {
41        self.specs.get(k)
42    }
43
44    /// Returns specification of the column with given name returned from the database.
45    #[inline]
46    pub fn get_by_name(&self, name: &str) -> Option<(usize, &'slice ColumnSpec<'spec>)> {
47        self.specs
48            .iter()
49            .enumerate()
50            .find(|(_idx, spec)| spec.name() == name)
51    }
52
53    /// Returns iterator over specification of columns returned from the database,
54    /// ordered by column order in the response.
55    #[inline]
56    pub fn iter(&self) -> impl Iterator<Item = &'slice ColumnSpec<'spec>> {
57        self.specs.iter()
58    }
59}
60
61/// Result of a single request to the database. It represents any kind of Result frame.
62///
63/// The received rows and metadata, which are present if the frame is of Result:Rows kind,
64/// are kept in a raw binary form. To deserialize and access them, transform `QueryResult`
65/// to [QueryRowsResult] by calling [QueryResult::into_rows_result].
66///
67/// NOTE: this is a result of a single CQL request. If you use paging for your query,
68/// this will contain exactly one page.
69#[derive(Debug, Clone)]
70pub struct QueryResult {
71    raw_metadata_and_rows: Option<RawMetadataAndRawRows>,
72    tracing_id: Option<Uuid>,
73    warnings: Vec<String>,
74}
75
76impl QueryResult {
77    pub(crate) fn new(
78        raw_rows: Option<RawMetadataAndRawRows>,
79        tracing_id: Option<Uuid>,
80        warnings: Vec<String>,
81    ) -> Self {
82        Self {
83            raw_metadata_and_rows: raw_rows,
84            tracing_id,
85            warnings,
86        }
87    }
88
89    // Preferred to implementing Default, because users shouldn't be able to create
90    // an empty QueryResult.
91    //
92    // For now unused, but it will be used once Session's API is migrated
93    // to the new QueryResult.
94    #[allow(dead_code)]
95    pub(crate) fn mock_empty() -> Self {
96        Self {
97            raw_metadata_and_rows: None,
98            tracing_id: None,
99            warnings: Vec::new(),
100        }
101    }
102
103    pub(crate) fn raw_metadata_and_rows(&self) -> Option<&RawMetadataAndRawRows> {
104        self.raw_metadata_and_rows.as_ref()
105    }
106
107    /// Warnings emitted by the database.
108    #[inline]
109    pub fn warnings(&self) -> impl Iterator<Item = &str> {
110        self.warnings.iter().map(String::as_str)
111    }
112
113    /// Tracing ID associated with this CQL request.
114    #[inline]
115    pub fn tracing_id(&self) -> Option<Uuid> {
116        self.tracing_id
117    }
118
119    /// Returns a bool indicating the current response is of Rows type.
120    #[inline]
121    pub fn is_rows(&self) -> bool {
122        self.raw_metadata_and_rows.is_some()
123    }
124
125    /// Returns `Ok` for a request's result that shouldn't contain any rows.\
126    /// Will return `Ok` for `INSERT` result, but a `SELECT` result, even an empty one, will cause an error.\
127    /// Opposite of [QueryResult::into_rows_result].
128    #[inline]
129    pub fn result_not_rows(&self) -> Result<(), ResultNotRowsError> {
130        match &self.raw_metadata_and_rows {
131            Some(_) => Err(ResultNotRowsError),
132            None => Ok(()),
133        }
134    }
135
136    /// Transforms itself into the Rows result type to enable deserializing rows.
137    /// Deserializes result metadata and allocates it.
138    ///
139    /// Returns an error if the response is not of Rows kind or metadata deserialization failed.
140    ///
141    /// ```rust
142    /// # use scylla::response::query_result::{QueryResult, QueryRowsResult};
143    /// # fn example(query_result: QueryResult) -> Result<(), Box<dyn std::error::Error>> {
144    /// let rows_result = query_result.into_rows_result()?;
145    ///
146    /// let mut rows_iter = rows_result.rows::<(i32, &str)>()?;
147    /// while let Some((num, text)) = rows_iter.next().transpose()? {
148    ///     // do something with `num` and `text``
149    /// }
150    ///
151    /// Ok(())
152    /// # }
153    ///
154    /// ```
155    ///
156    /// If the response is not of Rows kind, the original [`QueryResult`] (self) is
157    /// returned back to the user in the error type. See [`IntoRowsResultError`] documentation.
158    ///
159    /// ```rust
160    /// # use scylla::response::query_result::{QueryResult, QueryRowsResult, IntoRowsResultError};
161    /// # fn example(non_rows_query_result: QueryResult) -> Result<(), Box<dyn std::error::Error>> {
162    /// let err = non_rows_query_result.into_rows_result().unwrap_err();
163    ///
164    /// match err {
165    ///     IntoRowsResultError::ResultNotRows(query_result) => {
166    ///         // do something with original `query_result`
167    ///     }
168    ///     _ => {
169    ///         // deserialization failed - query result is not recovered
170    ///     }
171    /// }
172    ///
173    /// Ok(())
174    /// # }
175    /// ```
176    pub fn into_rows_result(self) -> Result<QueryRowsResult, IntoRowsResultError> {
177        let Some(raw_metadata_and_rows) = self.raw_metadata_and_rows else {
178            return Err(IntoRowsResultError::ResultNotRows(self));
179        };
180        let tracing_id = self.tracing_id;
181        let warnings = self.warnings;
182
183        let raw_rows_with_metadata = raw_metadata_and_rows.deserialize_metadata()?;
184        Ok(QueryRowsResult {
185            raw_rows_with_metadata,
186            warnings,
187            tracing_id,
188        })
189    }
190}
191
192/// Enables deserialization of rows received from the database in a [`QueryResult`].
193///
194/// Upon creation, it deserializes result metadata and allocates it.
195///
196/// This struct provides generic methods which enable typed access to the data,
197/// by deserializing rows on the fly to the type provided as a type parameter.
198/// Those methods are:
199/// - [rows()](QueryRowsResult::rows) - for iterating through rows,
200/// - [first_row()](QueryRowsResult::first_row) and
201///   [maybe_first_row()](QueryRowsResult::maybe_first_row) -
202///   for accessing the first row,
203/// - [single_row()](QueryRowsResult::single_row) - for accessing the first row,
204///   additionally asserting that it's the only one in the response.
205///
206/// ```rust
207/// # use scylla::response::query_result::QueryResult;
208/// # fn example(query_result: QueryResult) -> Result<(), Box<dyn std::error::Error>> {
209/// let rows_result = query_result.into_rows_result()?;
210///
211/// let mut rows_iter = rows_result.rows::<(i32, &str)>()?;
212/// while let Some((num, text)) = rows_iter.next().transpose()? {
213///     // do something with `num` and `text``
214/// }
215///
216/// Ok(())
217/// # }
218///
219/// ```
220#[derive(Debug)]
221pub struct QueryRowsResult {
222    raw_rows_with_metadata: DeserializedMetadataAndRawRows,
223    tracing_id: Option<Uuid>,
224    warnings: Vec<String>,
225}
226
227impl QueryRowsResult {
228    /// Warnings emitted by the database.
229    #[inline]
230    pub fn warnings(&self) -> impl Iterator<Item = &str> {
231        self.warnings.iter().map(String::as_str)
232    }
233
234    /// Tracing ID associated with this CQL request.
235    #[inline]
236    pub fn tracing_id(&self) -> Option<Uuid> {
237        self.tracing_id
238    }
239
240    /// Returns the number of received rows.
241    #[inline]
242    pub fn rows_num(&self) -> usize {
243        self.raw_rows_with_metadata.rows_count()
244    }
245
246    /// Returns the size of the serialized rows.
247    #[inline]
248    pub fn rows_bytes_size(&self) -> usize {
249        self.raw_rows_with_metadata.rows_bytes_size()
250    }
251
252    /// Returns column specifications.
253    #[inline]
254    pub fn column_specs(&self) -> ColumnSpecs<'_, '_> {
255        ColumnSpecs::new(self.raw_rows_with_metadata.metadata().col_specs())
256    }
257
258    /// Returns an iterator over the received rows.
259    ///
260    /// Returns an error if the rows in the response are of incorrect type.
261    #[inline]
262    pub fn rows<'frame, R: DeserializeRow<'frame, 'frame>>(
263        &'frame self,
264    ) -> Result<TypedRowIterator<'frame, 'frame, R>, RowsError> {
265        self.raw_rows_with_metadata
266            .rows_iter()
267            .map_err(RowsError::TypeCheckFailed)
268    }
269
270    /// Returns `Option<R>` containing the first row of the result.
271    ///
272    /// Fails when the the rows in the response are of incorrect type,
273    /// or when the deserialization fails.
274    pub fn maybe_first_row<'frame, R: DeserializeRow<'frame, 'frame>>(
275        &'frame self,
276    ) -> Result<Option<R>, MaybeFirstRowError> {
277        self.rows::<R>()
278            .map_err(|err| match err {
279                RowsError::TypeCheckFailed(typck_err) => {
280                    MaybeFirstRowError::TypeCheckFailed(typck_err)
281                }
282            })?
283            .next()
284            .transpose()
285            .map_err(MaybeFirstRowError::DeserializationFailed)
286    }
287
288    /// Returns the first row of the received result.
289    ///
290    /// When the first row is not available, returns an error.
291    /// Fails when the the rows in the response are of incorrect type,
292    /// or when the deserialization fails.
293    pub fn first_row<'frame, R: DeserializeRow<'frame, 'frame>>(
294        &'frame self,
295    ) -> Result<R, FirstRowError> {
296        match self.maybe_first_row::<R>() {
297            Ok(Some(row)) => Ok(row),
298            Ok(None) => Err(FirstRowError::RowsEmpty),
299            Err(MaybeFirstRowError::TypeCheckFailed(err)) => {
300                Err(FirstRowError::TypeCheckFailed(err))
301            }
302            Err(MaybeFirstRowError::DeserializationFailed(err)) => {
303                Err(FirstRowError::DeserializationFailed(err))
304            }
305        }
306    }
307
308    /// Returns the only received row.
309    ///
310    /// Fails if the result is anything else than a single row.
311    /// Fails when the the rows in the response are of incorrect type,
312    /// or when the deserialization fails.
313    pub fn single_row<'frame, R: DeserializeRow<'frame, 'frame>>(
314        &'frame self,
315    ) -> Result<R, SingleRowError> {
316        match self.rows::<R>() {
317            Ok(mut rows) => match rows.next() {
318                Some(Ok(row)) => {
319                    if rows.rows_remaining() != 0 {
320                        return Err(SingleRowError::UnexpectedRowCount(
321                            rows.rows_remaining() + 1,
322                        ));
323                    }
324                    Ok(row)
325                }
326                Some(Err(err)) => Err(SingleRowError::DeserializationFailed(err)),
327                None => Err(SingleRowError::UnexpectedRowCount(0)),
328            },
329            Err(RowsError::TypeCheckFailed(err)) => Err(SingleRowError::TypeCheckFailed(err)),
330        }
331    }
332
333    #[cfg(cpp_rust_unstable)]
334    pub fn into_inner(self) -> (DeserializedMetadataAndRawRows, Option<Uuid>, Vec<String>) {
335        let Self {
336            raw_rows_with_metadata,
337            tracing_id,
338            warnings,
339        } = self;
340
341        (raw_rows_with_metadata, tracing_id, warnings)
342    }
343}
344
345/// An error returned by [`QueryResult::into_rows_result`]
346///
347/// The `ResultNotRows` variant contains original [`QueryResult`],
348/// which otherwise would be consumed and lost.
349#[derive(Debug, Error, Clone)]
350pub enum IntoRowsResultError {
351    /// Result is not of Rows kind
352    #[error("Result is not of Rows kind")]
353    ResultNotRows(QueryResult),
354
355    // transparent because the underlying error provides enough context.
356    /// Failed to lazily deserialize result metadata.
357    #[error(transparent)]
358    ResultMetadataLazyDeserializationError(#[from] ResultMetadataAndRowsCountParseError),
359}
360
361/// An error returned by [`QueryRowsResult::rows`].
362#[derive(Debug, Error)]
363pub enum RowsError {
364    /// Type check failed
365    #[error("Type check failed: {0}")]
366    TypeCheckFailed(#[from] TypeCheckError),
367}
368
369/// An error returned by [`QueryRowsResult::maybe_first_row`].
370#[derive(Debug, Error)]
371pub enum MaybeFirstRowError {
372    /// Type check failed
373    #[error("Type check failed: {0}")]
374    TypeCheckFailed(#[from] TypeCheckError),
375
376    /// Deserialization failed
377    #[error("Deserialization failed: {0}")]
378    DeserializationFailed(#[from] DeserializationError),
379}
380
381/// An error returned by [`QueryRowsResult::first_row`].
382#[derive(Debug, Error)]
383pub enum FirstRowError {
384    /// The request response was of Rows type, but no rows were returned
385    #[error("The request response was of Rows type, but no rows were returned")]
386    RowsEmpty,
387
388    /// Type check failed
389    #[error("Type check failed: {0}")]
390    TypeCheckFailed(#[from] TypeCheckError),
391
392    /// Deserialization failed
393    #[error("Deserialization failed: {0}")]
394    DeserializationFailed(#[from] DeserializationError),
395}
396
397/// An error returned by [`QueryRowsResult::single_row`].
398#[derive(Debug, Error, Clone)]
399pub enum SingleRowError {
400    /// Expected one row, but got a different count
401    #[error("Expected a single row, but got {0} rows")]
402    UnexpectedRowCount(usize),
403
404    /// Type check failed
405    #[error("Type check failed: {0}")]
406    TypeCheckFailed(#[from] TypeCheckError),
407
408    /// Deserialization failed
409    #[error("Deserialization failed: {0}")]
410    DeserializationFailed(#[from] DeserializationError),
411}
412
413/// An error returned by [`QueryResult::result_not_rows`].
414///
415/// It indicates that response to the request was, unexpectedly, of Rows kind.
416#[derive(Debug, Error)]
417#[error("The request response was, unexpectedly, of Rows kind")]
418pub struct ResultNotRowsError;
419
420#[cfg(test)]
421mod tests {
422    use assert_matches::assert_matches;
423    use bytes::{Bytes, BytesMut};
424    use itertools::Itertools as _;
425    use scylla_cql::frame::response::result::{ColumnType, ResultMetadata};
426    use scylla_cql::frame::response::result::{NativeType, TableSpec};
427    use scylla_cql::frame::types;
428
429    use super::*;
430
431    const TABLE_SPEC: TableSpec<'static> = TableSpec::borrowed("ks", "tbl");
432
433    fn column_spec_infinite_iter() -> impl Iterator<Item = ColumnSpec<'static>> {
434        (0..).map(|k| {
435            ColumnSpec::owned(
436                format!("col_{}", k),
437                match k % 3 {
438                    0 => ColumnType::Native(NativeType::Ascii),
439                    1 => ColumnType::Native(NativeType::Boolean),
440                    2 => ColumnType::Native(NativeType::Float),
441                    _ => unreachable!(),
442                },
443                TABLE_SPEC,
444            )
445        })
446    }
447
448    #[test]
449    fn test_query_result() {
450        fn serialize_cells(cells: impl IntoIterator<Item = Option<impl AsRef<[u8]>>>) -> Bytes {
451            let mut bytes = BytesMut::new();
452            for cell in cells {
453                types::write_bytes_opt(cell, &mut bytes).unwrap();
454            }
455            bytes.freeze()
456        }
457
458        fn sample_result_metadata(cols: usize) -> ResultMetadata<'static> {
459            ResultMetadata::new_for_test(cols, column_spec_infinite_iter().take(cols).collect())
460        }
461
462        fn sample_raw_rows(cols: usize, rows: usize) -> RawMetadataAndRawRows {
463            let metadata = sample_result_metadata(cols);
464
465            static STRING: &[u8] = "MOCK".as_bytes();
466            static BOOLEAN: &[u8] = &(true as i8).to_be_bytes();
467            static FLOAT: &[u8] = &12341_i32.to_be_bytes();
468            let cells = metadata.col_specs().iter().map(|spec| match spec.typ() {
469                ColumnType::Native(NativeType::Ascii) => STRING,
470                ColumnType::Native(NativeType::Boolean) => BOOLEAN,
471                ColumnType::Native(NativeType::Float) => FLOAT,
472                _ => unreachable!(),
473            });
474            let bytes = serialize_cells(cells.map(Some));
475            RawMetadataAndRawRows::new_for_test(None, Some(metadata), false, rows, &bytes).unwrap()
476        }
477
478        // Used to trigger DeserializationError.
479        fn sample_raw_rows_invalid_bytes(cols: usize, rows: usize) -> RawMetadataAndRawRows {
480            let metadata = sample_result_metadata(cols);
481
482            RawMetadataAndRawRows::new_for_test(None, Some(metadata), false, rows, &[]).unwrap()
483        }
484
485        // Check tracing ID
486        for tracing_id in [None, Some(Uuid::from_u128(0x_feed_dead))] {
487            for raw_rows in [None, Some(sample_raw_rows(7, 6))] {
488                let qr = QueryResult::new(raw_rows, tracing_id, vec![]);
489                assert_eq!(qr.tracing_id(), tracing_id);
490            }
491        }
492
493        // Check warnings
494        for raw_rows in [None, Some(sample_raw_rows(7, 6))] {
495            let warnings = &["Ooops", "Meltdown..."];
496            let qr = QueryResult::new(
497                raw_rows,
498                None,
499                warnings.iter().copied().map(String::from).collect(),
500            );
501            assert_eq!(qr.warnings().collect_vec(), warnings);
502        }
503
504        // Check col specs
505        {
506            // Not RESULT::Rows response -> no column specs
507            {
508                let rqr = QueryResult::new(None, None, Vec::new());
509                let qr = rqr.into_rows_result();
510                assert_matches!(qr, Err(IntoRowsResultError::ResultNotRows(_)));
511            }
512
513            // RESULT::Rows response -> some column specs
514            {
515                let n = 5;
516                let metadata = sample_result_metadata(n);
517                let rr = RawMetadataAndRawRows::new_for_test(None, Some(metadata), false, 0, &[])
518                    .unwrap();
519                let rqr = QueryResult::new(Some(rr), None, Vec::new());
520                let qr = rqr.into_rows_result().unwrap();
521                let column_specs = qr.column_specs();
522                assert_eq!(column_specs.len(), n);
523
524                // By index
525                {
526                    for (i, expected_col_spec) in column_spec_infinite_iter().enumerate().take(n) {
527                        assert_eq!(column_specs.get_by_index(i), Some(&expected_col_spec));
528                    }
529
530                    assert_matches!(column_specs.get_by_index(n), None);
531                }
532
533                // By name
534                {
535                    for (idx, expected_col_spec) in column_spec_infinite_iter().enumerate().take(n)
536                    {
537                        let name = expected_col_spec.name();
538                        assert_eq!(
539                            column_specs.get_by_name(name),
540                            Some((idx, &expected_col_spec))
541                        );
542                    }
543
544                    assert_matches!(column_specs.get_by_name("ala ma kota"), None);
545                }
546
547                // By iter
548                {
549                    for (got_view, expected_col_spec) in
550                        column_specs.iter().zip(column_spec_infinite_iter())
551                    {
552                        assert_eq!(got_view, &expected_col_spec);
553                    }
554                }
555            }
556        }
557
558        // rows(), maybe_rows(), result_not_rows(), first_row(), maybe_first_row(), single_row()
559        // All errors are checked.
560        {
561            // Not RESULT::Rows
562            {
563                let rqr = QueryResult::new(None, None, Vec::new());
564                let qr = rqr.into_rows_result();
565                assert_matches!(qr, Err(IntoRowsResultError::ResultNotRows(_)));
566            }
567
568            // RESULT::Rows with 0 rows
569            {
570                let rr = sample_raw_rows(1, 0);
571                let rqr = QueryResult::new(Some(rr), None, Vec::new());
572                assert_matches!(rqr.result_not_rows(), Err(ResultNotRowsError));
573
574                let qr = rqr.into_rows_result().unwrap();
575
576                // Type check error
577                {
578                    assert_matches!(qr.rows::<(i32,)>(), Err(RowsError::TypeCheckFailed(_)));
579
580                    assert_matches!(
581                        qr.first_row::<(i32,)>(),
582                        Err(FirstRowError::TypeCheckFailed(_))
583                    );
584                    assert_matches!(
585                        qr.maybe_first_row::<(i32,)>(),
586                        Err(MaybeFirstRowError::TypeCheckFailed(_))
587                    );
588
589                    assert_matches!(
590                        qr.single_row::<(i32,)>(),
591                        Err(SingleRowError::TypeCheckFailed(_))
592                    );
593                }
594
595                // Correct type
596                {
597                    assert_matches!(qr.rows::<(&str,)>(), Ok(_));
598
599                    assert_matches!(qr.first_row::<(&str,)>(), Err(FirstRowError::RowsEmpty));
600                    assert_matches!(qr.maybe_first_row::<(&str,)>(), Ok(None));
601
602                    assert_matches!(
603                        qr.single_row::<(&str,)>(),
604                        Err(SingleRowError::UnexpectedRowCount(0))
605                    );
606                }
607            }
608
609            // RESULT::Rows with 1 row
610            {
611                let rr_good_data = sample_raw_rows(2, 1);
612                let rr_bad_data = sample_raw_rows_invalid_bytes(2, 1);
613                let rqr_good_data = QueryResult::new(Some(rr_good_data), None, Vec::new());
614                let rqr_bad_data = QueryResult::new(Some(rr_bad_data), None, Vec::new());
615
616                for rqr in [&rqr_good_data, &rqr_bad_data] {
617                    assert_matches!(rqr.result_not_rows(), Err(ResultNotRowsError));
618                }
619
620                let qr_good_data = rqr_good_data.into_rows_result().unwrap();
621                let qr_bad_data = rqr_bad_data.into_rows_result().unwrap();
622
623                for qr in [&qr_good_data, &qr_bad_data] {
624                    // Type check error
625                    {
626                        assert_matches!(
627                            qr.rows::<(i32, i32)>(),
628                            Err(RowsError::TypeCheckFailed(_))
629                        );
630
631                        assert_matches!(
632                            qr.first_row::<(i32, i32)>(),
633                            Err(FirstRowError::TypeCheckFailed(_))
634                        );
635                        assert_matches!(
636                            qr.maybe_first_row::<(i32, i32)>(),
637                            Err(MaybeFirstRowError::TypeCheckFailed(_))
638                        );
639
640                        assert_matches!(
641                            qr.single_row::<(i32, i32)>(),
642                            Err(SingleRowError::TypeCheckFailed(_))
643                        );
644                    }
645                }
646
647                // Correct type
648                {
649                    assert_matches!(qr_good_data.rows::<(&str, bool)>(), Ok(_));
650                    assert_matches!(qr_bad_data.rows::<(&str, bool)>(), Ok(_));
651
652                    assert_matches!(qr_good_data.first_row::<(&str, bool)>(), Ok(_));
653                    assert_matches!(
654                        qr_bad_data.first_row::<(&str, bool)>(),
655                        Err(FirstRowError::DeserializationFailed(_))
656                    );
657                    assert_matches!(qr_good_data.maybe_first_row::<(&str, bool)>(), Ok(_));
658                    assert_matches!(
659                        qr_bad_data.maybe_first_row::<(&str, bool)>(),
660                        Err(MaybeFirstRowError::DeserializationFailed(_))
661                    );
662
663                    assert_matches!(qr_good_data.single_row::<(&str, bool)>(), Ok(_));
664                    assert_matches!(
665                        qr_bad_data.single_row::<(&str, bool)>(),
666                        Err(SingleRowError::DeserializationFailed(_))
667                    );
668                }
669            }
670
671            // RESULT::Rows with 2 rows
672            {
673                let rr = sample_raw_rows(2, 2);
674                let rqr = QueryResult::new(Some(rr), None, Vec::new());
675                assert_matches!(rqr.result_not_rows(), Err(ResultNotRowsError));
676
677                let qr = rqr.into_rows_result().unwrap();
678
679                // Type check error
680                {
681                    assert_matches!(qr.rows::<(i32, i32)>(), Err(RowsError::TypeCheckFailed(_)));
682
683                    assert_matches!(
684                        qr.first_row::<(i32, i32)>(),
685                        Err(FirstRowError::TypeCheckFailed(_))
686                    );
687                    assert_matches!(
688                        qr.maybe_first_row::<(i32, i32)>(),
689                        Err(MaybeFirstRowError::TypeCheckFailed(_))
690                    );
691
692                    assert_matches!(
693                        qr.single_row::<(i32, i32)>(),
694                        Err(SingleRowError::TypeCheckFailed(_))
695                    );
696                }
697
698                // Correct type
699                {
700                    assert_matches!(qr.rows::<(&str, bool)>(), Ok(_));
701
702                    assert_matches!(qr.first_row::<(&str, bool)>(), Ok(_));
703                    assert_matches!(qr.maybe_first_row::<(&str, bool)>(), Ok(_));
704
705                    assert_matches!(
706                        qr.single_row::<(&str, bool)>(),
707                        Err(SingleRowError::UnexpectedRowCount(2))
708                    );
709                }
710            }
711        }
712    }
713
714    #[test]
715    fn test_query_result_returns_self_if_not_rows() {
716        // Check tracing ID
717        for tracing_id in [None, Some(Uuid::from_u128(0x_feed_dead))] {
718            let qr = QueryResult::new(None, tracing_id, vec![]);
719            let err = qr.into_rows_result().unwrap_err();
720            match err {
721                IntoRowsResultError::ResultNotRows(query_result) => {
722                    assert_eq!(query_result.tracing_id, tracing_id)
723                }
724                IntoRowsResultError::ResultMetadataLazyDeserializationError(_) => {
725                    panic!("Expected ResultNotRows error")
726                }
727            }
728        }
729
730        // Check warnings
731        {
732            let warnings = &["Ooops", "Meltdown..."];
733            let qr = QueryResult::new(
734                None,
735                None,
736                warnings.iter().copied().map(String::from).collect(),
737            );
738            let err = qr.into_rows_result().unwrap_err();
739            match err {
740                IntoRowsResultError::ResultNotRows(query_result) => {
741                    assert_eq!(query_result.warnings().collect_vec(), warnings)
742                }
743                IntoRowsResultError::ResultMetadataLazyDeserializationError(_) => {
744                    panic!("Expected ResultNotRows error")
745                }
746            }
747        }
748    }
749}