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#[derive(Debug, Clone, Copy)]
16pub struct ColumnSpecs<'slice, 'spec> {
17 specs: &'slice [ColumnSpec<'spec>],
18}
19
20impl<'slice, 'spec> ColumnSpecs<'slice, 'spec> {
21 pub fn new(specs: &'slice [ColumnSpec<'spec>]) -> Self {
23 Self { specs }
24 }
25
26 pub fn as_slice(&self) -> &'slice [ColumnSpec<'spec>] {
28 self.specs
29 }
30
31 #[allow(clippy::len_without_is_empty)]
33 #[inline]
34 pub fn len(&self) -> usize {
35 self.specs.len()
36 }
37
38 #[inline]
40 pub fn get_by_index(&self, k: usize) -> Option<&'slice ColumnSpec<'spec>> {
41 self.specs.get(k)
42 }
43
44 #[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 #[inline]
56 pub fn iter(&self) -> impl Iterator<Item = &'slice ColumnSpec<'spec>> {
57 self.specs.iter()
58 }
59}
60
61#[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 #[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 #[inline]
109 pub fn warnings(&self) -> impl Iterator<Item = &str> {
110 self.warnings.iter().map(String::as_str)
111 }
112
113 #[inline]
115 pub fn tracing_id(&self) -> Option<Uuid> {
116 self.tracing_id
117 }
118
119 #[inline]
121 pub fn is_rows(&self) -> bool {
122 self.raw_metadata_and_rows.is_some()
123 }
124
125 #[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 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#[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 #[inline]
230 pub fn warnings(&self) -> impl Iterator<Item = &str> {
231 self.warnings.iter().map(String::as_str)
232 }
233
234 #[inline]
236 pub fn tracing_id(&self) -> Option<Uuid> {
237 self.tracing_id
238 }
239
240 #[inline]
242 pub fn rows_num(&self) -> usize {
243 self.raw_rows_with_metadata.rows_count()
244 }
245
246 #[inline]
248 pub fn rows_bytes_size(&self) -> usize {
249 self.raw_rows_with_metadata.rows_bytes_size()
250 }
251
252 #[inline]
254 pub fn column_specs(&self) -> ColumnSpecs<'_, '_> {
255 ColumnSpecs::new(self.raw_rows_with_metadata.metadata().col_specs())
256 }
257
258 #[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 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 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 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#[derive(Debug, Error, Clone)]
350pub enum IntoRowsResultError {
351 #[error("Result is not of Rows kind")]
353 ResultNotRows(QueryResult),
354
355 #[error(transparent)]
358 ResultMetadataLazyDeserializationError(#[from] ResultMetadataAndRowsCountParseError),
359}
360
361#[derive(Debug, Error)]
363pub enum RowsError {
364 #[error("Type check failed: {0}")]
366 TypeCheckFailed(#[from] TypeCheckError),
367}
368
369#[derive(Debug, Error)]
371pub enum MaybeFirstRowError {
372 #[error("Type check failed: {0}")]
374 TypeCheckFailed(#[from] TypeCheckError),
375
376 #[error("Deserialization failed: {0}")]
378 DeserializationFailed(#[from] DeserializationError),
379}
380
381#[derive(Debug, Error)]
383pub enum FirstRowError {
384 #[error("The request response was of Rows type, but no rows were returned")]
386 RowsEmpty,
387
388 #[error("Type check failed: {0}")]
390 TypeCheckFailed(#[from] TypeCheckError),
391
392 #[error("Deserialization failed: {0}")]
394 DeserializationFailed(#[from] DeserializationError),
395}
396
397#[derive(Debug, Error, Clone)]
399pub enum SingleRowError {
400 #[error("Expected a single row, but got {0} rows")]
402 UnexpectedRowCount(usize),
403
404 #[error("Type check failed: {0}")]
406 TypeCheckFailed(#[from] TypeCheckError),
407
408 #[error("Deserialization failed: {0}")]
410 DeserializationFailed(#[from] DeserializationError),
411}
412
413#[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 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 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 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 {
506 {
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 {
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 {
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 {
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 {
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 {
561 {
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 {
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 {
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 {
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 {
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 {
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 {
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 {
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 {
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 {
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 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 {
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}