11//! Iterators over rows returned by paged queries
22
33use std:: future:: Future ;
4- use std:: mem;
54use std:: net:: SocketAddr ;
65use std:: ops:: ControlFlow ;
76use std:: pin:: Pin ;
87use std:: sync:: Arc ;
98use std:: task:: { Context , Poll } ;
109
1110use futures:: Stream ;
12- use scylla_cql:: frame:: request :: query :: PagingStateResponse ;
11+ use scylla_cql:: frame:: frame_errors :: RowsParseError ;
1312use scylla_cql:: frame:: response:: result:: RawMetadataAndRawRows ;
1413use scylla_cql:: frame:: response:: NonErrorResponse ;
14+ use scylla_cql:: types:: deserialize:: result:: RawRowLendingIterator ;
15+ use scylla_cql:: types:: deserialize:: row:: { ColumnIterator , DeserializeRow } ;
1516use scylla_cql:: types:: serialize:: row:: SerializedValues ;
1617use std:: result:: Result ;
1718use thiserror:: Error ;
1819use tokio:: sync:: mpsc;
1920
2021use super :: execution_profile:: ExecutionProfileInner ;
22+ use super :: query_result:: ColumnSpecs ;
2123use super :: session:: RequestSpan ;
2224use crate :: cql_to_rust:: { FromRow , FromRowError } ;
2325
2426use crate :: frame:: response:: {
2527 result,
26- result:: { ColumnSpec , Row , Rows } ,
28+ result:: { ColumnSpec , Row } ,
2729} ;
2830use crate :: history:: { self , HistoryListener } ;
2931use crate :: statement:: { prepared_statement:: PreparedStatement , query:: Query } ;
@@ -535,46 +537,70 @@ where
535537 }
536538}
537539
538- /// Iterator over rows returned by paged queries\
539- /// Allows to easily access rows without worrying about handling multiple pages
540- pub struct LegacyRowIterator {
541- current_row_idx : usize ,
542- current_page : Rows ,
540+ /// An intermediate object that allows to construct an iterator over a query
541+ /// that is asynchronously paged in the background.
542+ ///
543+ /// TODO: implement and describe the new API
544+ ///
545+ /// A pre-0.15.0 interface is also available, although deprecated:
546+ /// `into_legacy()` method converts QueryPager to LegacyRowIterator,
547+ /// enabling Stream'ed operation on rows being eagerly deserialized
548+ /// to the middle-man [Row] type. This is inefficient, especially if
549+ /// [Row] is not the intended target type.
550+ pub struct QueryPager {
551+ current_page : RawRowLendingIterator ,
543552 page_receiver : mpsc:: Receiver < Result < ReceivedPage , QueryError > > ,
544553 tracing_ids : Vec < Uuid > ,
545554}
546555
547- /// Fetching pages is asynchronous so `RowIterator` does not implement the `Iterator` trait.\
548- /// Instead it uses the asynchronous `Stream` trait
549- impl Stream for LegacyRowIterator {
550- type Item = Result < Row , QueryError > ;
556+ // QueryPager is not an iterator or a stream! However, it implements
557+ // a `next()` method that returns a [ColumnIterator], which can be used
558+ // to manually deserialize a row.
559+ // The `ColumnIterator` borrows from the `QueryPager`, and the [futures::Stream] trait
560+ // does not allow for such a pattern. Lending streams are not a thing yet.
561+ impl QueryPager {
562+ /// Returns the next item (`ColumnIterator`) from the stream.
563+ ///
564+ /// This can be used with `type_check() for manual deserialization - see example below.
565+ ///
566+ /// This is not a part of the `Stream` interface because the returned iterator
567+ /// borrows from self.
568+ ///
569+ /// This is cancel-safe.
570+ async fn next ( & mut self ) -> Option < Result < ColumnIterator , QueryError > > {
571+ let res = std:: future:: poll_fn ( |cx| Pin :: new ( & mut * self ) . poll_fill_page ( cx) ) . await ;
572+ match res {
573+ Some ( Ok ( ( ) ) ) => { }
574+ Some ( Err ( err) ) => return Some ( Err ( err) ) ,
575+ None => return None ,
576+ }
551577
552- fn poll_next ( self : Pin < & mut Self > , cx : & mut Context < ' _ > ) -> Poll < Option < Self :: Item > > {
553- self . poll_next_internal ( cx)
578+ // We are guaranteed here to have a non-empty page, so unwrap
579+ Some (
580+ self . current_page
581+ . next ( )
582+ . unwrap ( )
583+ . map_err ( |e| RowsParseError :: from ( e) . into ( ) ) ,
584+ )
554585 }
555- }
556586
557- impl LegacyRowIterator {
558- fn poll_next_internal (
559- mut self : Pin < & mut Self > ,
587+ /// Tries to acquire a non-empty page, if current page is exhausted.
588+ fn poll_fill_page < ' r > (
589+ mut self : Pin < & ' r mut Self > ,
560590 cx : & mut Context < ' _ > ,
561- ) -> Poll < Option < Result < Row , QueryError > > > {
562- if self . as_ref ( ) . is_current_page_exhausted ( ) {
563- ready_some_ok ! ( self . as_mut ( ) . poll_next_page ( cx ) ) ;
591+ ) -> Poll < Option < Result < ( ) , QueryError > > > {
592+ if ! self . is_current_page_exhausted ( ) {
593+ return Poll :: Ready ( Some ( Ok ( ( ) ) ) ) ;
564594 }
565-
566- let mut s = self . as_mut ( ) ;
567-
568- let idx = s . current_row_idx ;
569- if idx < s . current_page . rows . len ( ) {
570- let row = mem :: take ( & mut s . current_page . rows [ idx ] ) ;
571- s . current_row_idx += 1 ;
572- return Poll :: Ready ( Some ( Ok ( row ) ) ) ;
595+ ready_some_ok ! ( self . as_mut ( ) . poll_next_page ( cx ) ) ;
596+ if self . is_current_page_exhausted ( ) {
597+ // We most likely got a zero-sized page.
598+ // Try again later.
599+ cx . waker ( ) . wake_by_ref ( ) ;
600+ Poll :: Pending
601+ } else {
602+ Poll :: Ready ( Some ( Ok ( ( ) ) ) )
573603 }
574- // We probably got a zero-sized page
575- // Yield, but tell that we are ready
576- cx. waker ( ) . wake_by_ref ( ) ;
577- Poll :: Pending
578604 }
579605
580606 /// Makes an attempt to acquire the next page (which may be empty).
@@ -589,18 +615,8 @@ impl LegacyRowIterator {
589615 let mut s = self . as_mut ( ) ;
590616
591617 let received_page = ready_some_ok ! ( Pin :: new( & mut s. page_receiver) . poll_recv( cx) ) ;
592- let rows = match received_page
593- . rows
594- // As RowIteratorWorker manages paging itself, the paging state response
595- // returned to the user is always NoMorePages. It used to be so before
596- // the deserialization refactor, too.
597- . into_legacy_rows ( PagingStateResponse :: NoMorePages )
598- {
599- Ok ( rows) => rows,
600- Err ( err) => return Poll :: Ready ( Some ( Err ( err. into ( ) ) ) ) ,
601- } ;
602- s. current_page = rows;
603- s. current_row_idx = 0 ;
618+ let raw_rows_with_deserialized_metadata = received_page. rows . deserialize_metadata ( ) ?;
619+ s. current_page = RawRowLendingIterator :: new ( raw_rows_with_deserialized_metadata) ;
604620
605621 if let Some ( tracing_id) = received_page. tracing_id {
606622 s. tracing_ids . push ( tracing_id) ;
@@ -609,12 +625,13 @@ impl LegacyRowIterator {
609625 Poll :: Ready ( Some ( Ok ( ( ) ) ) )
610626 }
611627
612- /// Converts this iterator into an iterator over rows parsed as given type
613- pub fn into_typed < RowT : FromRow > ( self ) -> LegacyTypedRowIterator < RowT > {
614- LegacyTypedRowIterator {
615- row_iterator : self ,
616- phantom_data : Default :: default ( ) ,
617- }
628+ /// Converts this iterator into an iterator over rows parsed as given type,
629+ /// using the legacy deserialization framework.
630+ /// This is inefficient, because all rows are being eagerly deserialized
631+ /// to a middle-man [Row] type.
632+ #[ inline]
633+ pub fn into_legacy ( self ) -> LegacyRowIterator {
634+ LegacyRowIterator { raw_iterator : self }
618635 }
619636
620637 pub ( crate ) async fn new_for_query (
@@ -888,16 +905,13 @@ impl LegacyRowIterator {
888905 // to the channel (the PageSendAttemptedProof helps enforce this)
889906 // - That future is polled in a tokio::task which isn't going to be
890907 // cancelled
891- let pages_received = receiver. recv ( ) . await . unwrap ( ) ?;
892- let rows = pages_received
893- . rows
894- . into_legacy_rows ( PagingStateResponse :: NoMorePages ) ?;
908+ let page_received = receiver. recv ( ) . await . unwrap ( ) ?;
909+ let raw_rows_with_deserialized_metadata = page_received. rows . deserialize_metadata ( ) ?;
895910
896911 Ok ( Self {
897- current_row_idx : 0 ,
898- current_page : rows,
912+ current_page : RawRowLendingIterator :: new ( raw_rows_with_deserialized_metadata) ,
899913 page_receiver : receiver,
900- tracing_ids : if let Some ( tracing_id) = pages_received . tracing_id {
914+ tracing_ids : if let Some ( tracing_id) = page_received . tracing_id {
901915 vec ! [ tracing_id]
902916 } else {
903917 Vec :: new ( )
@@ -906,17 +920,63 @@ impl LegacyRowIterator {
906920 }
907921
908922 /// If tracing was enabled returns tracing ids of all finished page queries
909- pub fn get_tracing_ids ( & self ) -> & [ Uuid ] {
923+ #[ inline]
924+ pub fn tracing_ids ( & self ) -> & [ Uuid ] {
910925 & self . tracing_ids
911926 }
912927
913928 /// Returns specification of row columns
914- pub fn get_column_specs ( & self ) -> & [ ColumnSpec < ' _ > ] {
915- self . current_page . metadata . inner ( ) . col_specs ( )
929+ #[ inline]
930+ pub fn column_specs ( & self ) -> ColumnSpecs < ' _ > {
931+ ColumnSpecs :: new ( self . current_page . metadata ( ) . col_specs ( ) )
916932 }
917933
918934 fn is_current_page_exhausted ( & self ) -> bool {
919- self . current_row_idx >= self . current_page . rows . len ( )
935+ self . current_page . rows_remaining ( ) == 0
936+ }
937+ }
938+
939+ /// Iterator over rows returned by paged queries.
940+ ///
941+ /// Allows to easily access rows without worrying about handling multiple pages.
942+ pub struct LegacyRowIterator {
943+ raw_iterator : QueryPager ,
944+ }
945+
946+ impl Stream for LegacyRowIterator {
947+ type Item = Result < Row , QueryError > ;
948+
949+ fn poll_next ( mut self : Pin < & mut Self > , cx : & mut Context < ' _ > ) -> Poll < Option < Self :: Item > > {
950+ let mut s = self . as_mut ( ) ;
951+
952+ let next_fut = s. raw_iterator . next ( ) ;
953+ futures:: pin_mut!( next_fut) ;
954+
955+ let next_column_iter = ready_some_ok ! ( next_fut. poll( cx) ) ;
956+
957+ let next_ready_row =
958+ Row :: deserialize ( next_column_iter) . map_err ( |e| RowsParseError :: from ( e) . into ( ) ) ;
959+
960+ Poll :: Ready ( Some ( next_ready_row) )
961+ }
962+ }
963+
964+ impl LegacyRowIterator {
965+ /// If tracing was enabled returns tracing ids of all finished page queries
966+ pub fn get_tracing_ids ( & self ) -> & [ Uuid ] {
967+ self . raw_iterator . tracing_ids ( )
968+ }
969+
970+ /// Returns specification of row columns
971+ pub fn get_column_specs ( & self ) -> & [ ColumnSpec < ' _ > ] {
972+ self . raw_iterator . column_specs ( ) . inner ( )
973+ }
974+
975+ pub fn into_typed < RowT > ( self ) -> LegacyTypedRowIterator < RowT > {
976+ LegacyTypedRowIterator {
977+ row_iterator : self ,
978+ _phantom_data : Default :: default ( ) ,
979+ }
920980 }
921981}
922982
@@ -925,16 +985,18 @@ impl LegacyRowIterator {
925985/// Returned by `RowIterator::into_typed`
926986pub struct LegacyTypedRowIterator < RowT > {
927987 row_iterator : LegacyRowIterator ,
928- phantom_data : std:: marker:: PhantomData < RowT > ,
988+ _phantom_data : std:: marker:: PhantomData < RowT > ,
929989}
930990
931991impl < RowT > LegacyTypedRowIterator < RowT > {
932992 /// If tracing was enabled returns tracing ids of all finished page queries
993+ #[ inline]
933994 pub fn get_tracing_ids ( & self ) -> & [ Uuid ] {
934995 self . row_iterator . get_tracing_ids ( )
935996 }
936997
937998 /// Returns specification of row columns
999+ #[ inline]
9381000 pub fn get_column_specs ( & self ) -> & [ ColumnSpec < ' _ > ] {
9391001 self . row_iterator . get_column_specs ( )
9401002 }
0 commit comments