From e38486bc69e15880a1370d1d09be603a0fbaf343 Mon Sep 17 00:00:00 2001 From: Piotr Dulikowski Date: Thu, 30 Mar 2023 08:23:53 +0200 Subject: [PATCH] book: adjust code examples to the new deserialization API MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Examples in the Book are now adjusted to the new deserialization API, and the book finally compile and the tests there pass. Note that this commit does not describe exhaustively the intended way of using the new deserialization framework; this is left for a follow-up. Co-authored-by: Wojciech Przytuła --- README.md | 6 +- docs/source/data-types/blob.md | 6 +- docs/source/data-types/collections.md | 20 ++--- docs/source/data-types/counter.md | 6 +- docs/source/data-types/date.md | 6 +- docs/source/data-types/decimal.md | 4 +- docs/source/data-types/duration.md | 2 +- docs/source/data-types/inet.md | 2 +- docs/source/data-types/primitive.md | 14 ++-- docs/source/data-types/text.md | 2 +- docs/source/data-types/time.md | 6 +- docs/source/data-types/timestamp.md | 6 +- docs/source/data-types/timeuuid.md | 4 +- docs/source/data-types/tuple.md | 2 +- docs/source/data-types/udt.md | 28 +++---- docs/source/data-types/uuid.md | 2 +- docs/source/data-types/varint.md | 2 +- docs/source/queries/paged.md | 28 ++++--- docs/source/queries/result.md | 107 +++++++++----------------- docs/source/queries/simple.md | 12 ++- docs/source/quickstart/example.md | 2 +- docs/source/tracing/basic.md | 6 +- docs/source/tracing/paged.md | 20 +++-- 23 files changed, 138 insertions(+), 155 deletions(-) diff --git a/README.md b/README.md index 09ae2e795f..9d1fded2dc 100644 --- a/README.md +++ b/README.md @@ -21,9 +21,9 @@ let uri = "127.0.0.1:9042"; let session: Session = SessionBuilder::new().known_node(uri).build().await?; -let raw_iter = session.query_iter("SELECT a, b, c FROM ks.t", &[]).await?; -let mut iter = raw_iter.into_typed::<(i32, i32, String)>(); -while let Some((a, b, c)) = iter.try_next().await? { +let query_pager = session.query_iter("SELECT a, b, c FROM ks.t", &[]).await?; +let mut stream = query_pager.rows_stream::<(i32, i32, String)>()?; +while let Some((a, b, c)) = stream.try_next().await? { println!("a, b, c: {}, {}, {}", a, b, c); } ``` diff --git a/docs/source/data-types/blob.md b/docs/source/data-types/blob.md index ee9d82dedb..4c445172c0 100644 --- a/docs/source/data-types/blob.md +++ b/docs/source/data-types/blob.md @@ -18,8 +18,10 @@ session .await?; // Read blobs from the table -let mut iter = session.query_iter("SELECT a FROM keyspace.table", &[]).await?.into_typed::<(Vec,)>(); -while let Some((blob_value,)) = iter.try_next().await? { +let mut stream = session.query_iter("SELECT a FROM keyspace.table", &[]) + .await? + .rows_stream::<(Vec,)>()?; +while let Some((blob_value,)) = stream.try_next().await? { println!("{:?}", blob_value); } # Ok(()) diff --git a/docs/source/data-types/collections.md b/docs/source/data-types/collections.md index 30ef7c818a..645164736a 100644 --- a/docs/source/data-types/collections.md +++ b/docs/source/data-types/collections.md @@ -18,8 +18,10 @@ session .await?; // Read a list of ints from the table -let mut iter = session.query_iter("SELECT a FROM keyspace.table", &[]).await?.into_typed::<(Vec,)>(); -while let Some((list_value,)) = iter.try_next().await? { +let mut stream = session.query_iter("SELECT a FROM keyspace.table", &[]) + .await? + .rows_stream::<(Vec,)>()?; +while let Some((list_value,)) = stream.try_next().await? { println!("{:?}", list_value); } # Ok(()) @@ -44,10 +46,10 @@ session .await?; // Read a set of ints from the table -let mut iter = session.query_iter("SELECT a FROM keyspace.table", &[]) +let mut stream = session.query_iter("SELECT a FROM keyspace.table", &[]) .await? - .into_typed::<(Vec,)>(); -while let Some((set_value,)) = iter.try_next().await? { + .rows_stream::<(Vec,)>()?; +while let Some((set_value,)) = stream.try_next().await? { println!("{:?}", set_value); } # Ok(()) @@ -72,7 +74,7 @@ session // Read a set of ints from the table let mut iter = session.query_iter("SELECT a FROM keyspace.table", &[]) .await? - .into_typed::<(HashSet,)>(); + .rows_stream::<(HashSet,)>()?; while let Some((set_value,)) = iter.try_next().await? { println!("{:?}", set_value); } @@ -98,7 +100,7 @@ session // Read a set of ints from the table let mut iter = session.query_iter("SELECT a FROM keyspace.table", &[]) .await? - .into_typed::<(BTreeSet,)>(); + .rows_stream::<(BTreeSet,)>()?; while let Some((set_value,)) = iter.try_next().await? { println!("{:?}", set_value); } @@ -129,7 +131,7 @@ session // Read a map from the table let mut iter = session.query_iter("SELECT a FROM keyspace.table", &[]) .await? - .into_typed::<(HashMap,)>(); + .rows_stream::<(HashMap,)>()?; while let Some((map_value,)) = iter.try_next().await? { println!("{:?}", map_value); } @@ -157,7 +159,7 @@ session // Read a map from the table let mut iter = session.query_iter("SELECT a FROM keyspace.table", &[]) .await? - .into_typed::<(BTreeMap,)>(); + .rows_stream::<(BTreeMap,)>()?; while let Some((map_value,)) = iter.try_next().await? { println!("{:?}", map_value); } diff --git a/docs/source/data-types/counter.md b/docs/source/data-types/counter.md index 8e4bb1410d..2968701501 100644 --- a/docs/source/data-types/counter.md +++ b/docs/source/data-types/counter.md @@ -18,10 +18,10 @@ session .await?; // Read counter from the table -let mut iter = session.query_iter("SELECT c FROM keyspace.table", &[]) +let mut stream = session.query_iter("SELECT c FROM keyspace.table", &[]) .await? - .into_typed::<(Counter,)>(); -while let Some((counter_value,)) = iter.try_next().await? { + .rows_stream::<(Counter,)>()?; +while let Some((counter_value,)) = stream.try_next().await? { let counter_int_value: i64 = counter_value.0; println!("{}", counter_int_value); } diff --git a/docs/source/data-types/date.md b/docs/source/data-types/date.md index 162e77f978..a0166db41e 100644 --- a/docs/source/data-types/date.md +++ b/docs/source/data-types/date.md @@ -32,7 +32,7 @@ session // Read raw Date from the table let mut iter = session.query_iter("SELECT a FROM keyspace.table", &[]) .await? - .into_typed::<(CqlDate,)>(); + .rows_stream::<(CqlDate,)>()?; while let Some((date_value,)) = iter.try_next().await? { // ... } @@ -68,7 +68,7 @@ session // Read NaiveDate from the table let mut iter = session.query_iter("SELECT a FROM keyspace.table", &[]) .await? - .into_typed::<(NaiveDate,)>(); + .rows_stream::<(NaiveDate,)>()?; while let Some((date_value,)) = iter.try_next().await? { // ... } @@ -104,7 +104,7 @@ session // Read Date from the table let mut iter = session.query_iter("SELECT a FROM keyspace.table", &[]) .await? - .into_typed::<(Date,)>(); + .rows_stream::<(Date,)>()?; while let Some((date_value,)) = iter.try_next().await? { // ... } diff --git a/docs/source/data-types/decimal.md b/docs/source/data-types/decimal.md index 8fff5fe1db..3ad7f9302f 100644 --- a/docs/source/data-types/decimal.md +++ b/docs/source/data-types/decimal.md @@ -25,7 +25,7 @@ session // Read a decimal from the table let mut iter = session.query_iter("SELECT a FROM keyspace.table", &[]) .await? - .into_typed::<(CqlDecimal,)>(); + .rows_stream::<(CqlDecimal,)>()?; while let Some((decimal_value,)) = iter.try_next().await? { println!("{:?}", decimal_value); } @@ -57,7 +57,7 @@ session // Read a decimal from the table let mut iter = session.query_iter("SELECT a FROM keyspace.table", &[]) .await? - .into_typed::<(BigDecimal,)>(); + .rows_stream::<(BigDecimal,)>()?; while let Some((decimal_value,)) = iter.try_next().await? { println!("{:?}", decimal_value); } diff --git a/docs/source/data-types/duration.md b/docs/source/data-types/duration.md index 1f81aeb0b7..ab46d8ac8a 100644 --- a/docs/source/data-types/duration.md +++ b/docs/source/data-types/duration.md @@ -19,7 +19,7 @@ session // Read duration from the table let mut iter = session.query_iter("SELECT a FROM keyspace.table", &[]) .await? - .into_typed::<(CqlDuration,)>(); + .rows_stream::<(CqlDuration,)>()?; while let Some((duration_value,)) = iter.try_next().await? { println!("{:?}", duration_value); } diff --git a/docs/source/data-types/inet.md b/docs/source/data-types/inet.md index bfbf07070f..eec39c1948 100644 --- a/docs/source/data-types/inet.md +++ b/docs/source/data-types/inet.md @@ -19,7 +19,7 @@ session // Read inet from the table let mut iter = session.query_iter("SELECT a FROM keyspace.table", &[]) .await? - .into_typed::<(IpAddr,)>(); + .rows_stream::<(IpAddr,)>()?; while let Some((inet_value,)) = iter.try_next().await? { println!("{:?}", inet_value); } diff --git a/docs/source/data-types/primitive.md b/docs/source/data-types/primitive.md index 53d671d14f..ab7032e62f 100644 --- a/docs/source/data-types/primitive.md +++ b/docs/source/data-types/primitive.md @@ -21,7 +21,7 @@ session // Read a bool from the table let mut iter = session.query_iter("SELECT a FROM keyspace.table", &[]) .await? - .into_typed::<(bool,)>(); + .rows_stream::<(bool,)>()?; while let Some((bool_value,)) = iter.try_next().await? { println!("{:?}", bool_value); } @@ -50,7 +50,7 @@ session // Read a tinyint from the table let mut iter = session.query_iter("SELECT a FROM keyspace.table", &[]) .await? - .into_typed::<(i8,)>(); + .rows_stream::<(i8,)>()?; while let Some((tinyint_value,)) = iter.try_next().await? { println!("{:?}", tinyint_value); } @@ -79,7 +79,7 @@ session // Read a smallint from the table let mut iter = session.query_iter("SELECT a FROM keyspace.table", &[]) .await? - .into_typed::<(i16,)>(); + .rows_stream::<(i16,)>()?; while let Some((smallint_value,)) = iter.try_next().await? { println!("{}", smallint_value); } @@ -108,7 +108,7 @@ session // Read an int from the table let mut iter = session.query_iter("SELECT a FROM keyspace.table", &[]) .await? - .into_typed::<(i32,)>(); + .rows_stream::<(i32,)>()?; while let Some((int_value,)) = iter.try_next().await? { println!("{}", int_value); } @@ -137,7 +137,7 @@ session // Read a bigint from the table let mut iter = session.query_iter("SELECT a FROM keyspace.table", &[]) .await? - .into_typed::<(i64,)>(); + .rows_stream::<(i64,)>()?; while let Some((bigint_value,)) = iter.try_next().await? { println!("{:?}", bigint_value); } @@ -166,7 +166,7 @@ session // Read a float from the table let mut iter = session.query_iter("SELECT a FROM keyspace.table", &[]) .await? - .into_typed::<(f32,)>(); + .rows_stream::<(f32,)>()?; while let Some((float_value,)) = iter.try_next().await? { println!("{:?}", float_value); } @@ -195,7 +195,7 @@ session // Read a double from the table let mut iter = session.query_iter("SELECT a FROM keyspace.table", &[]) .await? - .into_typed::<(f64,)>(); + .rows_stream::<(f64,)>()?; while let Some((double_value,)) = iter.try_next().await? { println!("{:?}", double_value); } diff --git a/docs/source/data-types/text.md b/docs/source/data-types/text.md index 8ef5f92342..b61ec2ba00 100644 --- a/docs/source/data-types/text.md +++ b/docs/source/data-types/text.md @@ -24,7 +24,7 @@ session // Read ascii/text/varchar from the table let mut iter = session.query_iter("SELECT a FROM keyspace.table", &[]) .await? - .into_typed::<(String,)>(); + .rows_stream::<(String,)>()?; while let Some((text_value,)) = iter.try_next().await? { println!("{}", text_value); } diff --git a/docs/source/data-types/time.md b/docs/source/data-types/time.md index be07b0a573..03c4a524bf 100644 --- a/docs/source/data-types/time.md +++ b/docs/source/data-types/time.md @@ -32,7 +32,7 @@ session // Read time from the table let mut iter = session.query_iter("SELECT a FROM keyspace.table", &[]) .await? - .into_typed::<(CqlTime,)>(); + .rows_stream::<(CqlTime,)>()?; while let Some((value,)) = iter.try_next().await? { // ... } @@ -68,7 +68,7 @@ session // Read time from the table let mut iter = session.query_iter("SELECT a FROM keyspace.table", &[]) .await? - .into_typed::<(NaiveTime,)>(); + .rows_stream::<(NaiveTime,)>()?; while let Some((time_value,)) = iter.try_next().await? { println!("{:?}", time_value); } @@ -102,7 +102,7 @@ session // Read time from the table let mut iter = session.query_iter("SELECT a FROM keyspace.table", &[]) .await? - .into_typed::<(Time,)>(); + .rows_stream::<(Time,)>()?; while let Some((time_value,)) = iter.try_next().await? { println!("{:?}", time_value); } diff --git a/docs/source/data-types/timestamp.md b/docs/source/data-types/timestamp.md index f820455a06..0ddbf118d0 100644 --- a/docs/source/data-types/timestamp.md +++ b/docs/source/data-types/timestamp.md @@ -33,7 +33,7 @@ session // Read timestamp from the table let mut iter = session.query_iter("SELECT a FROM keyspace.table", &[]) .await? - .into_typed::<(CqlTimestamp,)>(); + .rows_stream::<(CqlTimestamp,)>()?; while let Some((value,)) = iter.try_next().await? { // ... } @@ -73,7 +73,7 @@ session // Read timestamp from the table let mut iter = session.query_iter("SELECT a FROM keyspace.table", &[]) .await? - .into_typed::<(DateTime,)>(); + .rows_stream::<(DateTime,)>()?; while let Some((timestamp_value,)) = iter.try_next().await? { println!("{:?}", timestamp_value); } @@ -114,7 +114,7 @@ session // Read timestamp from the table let mut iter = session.query_iter("SELECT a FROM keyspace.table", &[]) .await? - .into_typed::<(OffsetDateTime,)>(); + .rows_stream::<(OffsetDateTime,)>()?; while let Some((timestamp_value,)) = iter.try_next().await? { println!("{:?}", timestamp_value); } diff --git a/docs/source/data-types/timeuuid.md b/docs/source/data-types/timeuuid.md index a947650454..f213255017 100644 --- a/docs/source/data-types/timeuuid.md +++ b/docs/source/data-types/timeuuid.md @@ -24,7 +24,7 @@ session // Read Timeuuid from the table let mut iter = session.query_iter("SELECT a FROM keyspace.table", &[]) .await? - .into_typed::<(CqlTimeuuid, )>(); + .rows_stream::<(CqlTimeuuid, )>()?; while let Some((timeuuid,)) = iter.try_next().await? { println!("Read a value from row: {}", timeuuid); @@ -68,7 +68,7 @@ session // Read Timeuuid from the table let mut iter = session.query_iter("SELECT a FROM keyspace.table", &[]) .await? - .into_typed::<(CqlTimeuuid, )>(); + .rows_stream::<(CqlTimeuuid, )>()?; while let Some((timeuuid,)) = iter.try_next().await? { println!("Read a value from row: {}", timeuuid); diff --git a/docs/source/data-types/tuple.md b/docs/source/data-types/tuple.md index 703b236b69..de6a0c9bd1 100644 --- a/docs/source/data-types/tuple.md +++ b/docs/source/data-types/tuple.md @@ -19,7 +19,7 @@ session // Read a tuple of int and string from the table let mut iter = session.query_iter("SELECT a FROM keyspace.table", &[]) .await? - .into_typed::<((i32, String),)>(); + .rows_stream::<((i32, String),)>()?; while let Some((tuple_value,)) = iter.try_next().await? { let int_value: i32 = tuple_value.0; let string_value: String = tuple_value.1; diff --git a/docs/source/data-types/udt.md b/docs/source/data-types/udt.md index f949d2fc36..4a8f91b137 100644 --- a/docs/source/data-types/udt.md +++ b/docs/source/data-types/udt.md @@ -10,12 +10,12 @@ CREATE TYPE ks.my_type (int_val int, text_val text) To use this type in the driver, create a matching struct and derive: - `SerializeValue`: in order to be able to use this struct in query parameters. \ - This macro requires fields of UDT and struct to have matching names, but the order - of the fields is not required to be the same. \ - Note: you can use different name using `rename` attribute - see `SerializeValue` macro documentation. -- `FromUserType`: in order to be able to use this struct in query results. \ - This macro requires fields of UDT and struct to be in the same *ORDER*. \ - This mismatch between `SerializeValue` and `FromUserType` requirements is a temporary situation - in the future `FromUserType` (or the macro that replaces it) will also require matching names. +- `DeserializeValue`: in order to be able to use this struct in query results. \ + +Both macros require fields of UDT and struct to have matching names, but the order +of the fields is not required to be the same. \ +Note: you can use different name using `rename` attribute - see `SerializeValue` +and `DeserializeValue` macros documentation. ```rust # extern crate scylla; @@ -35,13 +35,9 @@ struct MyType { ``` > ***Important***\ -> For deserialization, fields in the Rust struct must be defined in the same order as they are in the database. -> When receiving values, the driver will (de)serialize fields one after another, without looking at field names. - -> ***Important***\ -> For serialization, by default fields in the Rust struct must be defined with the same names as they are in the database. -> The driver will serialize the fields in the order defined by the UDT, matching Rust fields by name. -> You can change this behaviour using macro attributes, see `SerializeValue` macro documentation for more information. +> For (de)serialization, by default fields in the Rust struct must be defined with the same names as they are in the database. +> The driver will (de)serialize the fields in the order defined by the UDT, matching Rust fields by name. +> You can change this behaviour using macro attributes, see `SerializeValue`/`DeserializeValue` macro documentation for more information. Now it can be sent and received just like any other CQL value: ```rust @@ -51,10 +47,10 @@ Now it can be sent and received just like any other CQL value: # use std::error::Error; # async fn check_only_compiles(session: &Session) -> Result<(), Box> { use futures::TryStreamExt; -use scylla::macros::{FromUserType, SerializeValue}; +use scylla::macros::{DeserializeValue, SerializeValue}; use scylla::cql_to_rust::FromCqlVal; -#[derive(Debug, FromUserType, SerializeValue)] +#[derive(Debug, DeserializeValue, SerializeValue)] struct MyType { int_val: i32, text_val: Option, @@ -73,7 +69,7 @@ session // Read MyType from the table let mut iter = session.query_iter("SELECT a FROM keyspace.table", &[]) .await? - .into_typed::<(MyType,)>(); + .rows_stream::<(MyType,)>()?; while let Some((my_type_value,)) = iter.try_next().await? { println!("{:?}", my_type_value); } diff --git a/docs/source/data-types/uuid.md b/docs/source/data-types/uuid.md index 996735d4e1..f8fa95276b 100644 --- a/docs/source/data-types/uuid.md +++ b/docs/source/data-types/uuid.md @@ -21,7 +21,7 @@ session // Read uuid from the table let mut iter = session.query_iter("SELECT a FROM keyspace.table", &[]) .await? - .into_typed::<(Uuid,)>(); + .rows_stream::<(Uuid,)>()?; while let Some((uuid_value,)) = iter.try_next().await? { println!("{:?}", uuid_value); } diff --git a/docs/source/data-types/varint.md b/docs/source/data-types/varint.md index 7b68f066b4..59a515eb64 100644 --- a/docs/source/data-types/varint.md +++ b/docs/source/data-types/varint.md @@ -32,7 +32,7 @@ session // Read a varint from the table let mut iter = session.query_iter("SELECT a FROM keyspace.table", &[]) .await? - .into_typed::<(BigInt,)>(); + .rows_stream::<(BigInt,)>()?; while let Some((varint_value,)) = iter.try_next().await? { println!("{:?}", varint_value); } diff --git a/docs/source/queries/paged.md b/docs/source/queries/paged.md index 1c41f35ff4..516a149ab9 100644 --- a/docs/source/queries/paged.md +++ b/docs/source/queries/paged.md @@ -15,18 +15,26 @@ In fact, most SELECTs queries should be done with paging, to avoid big load on c > > Stay safe. Page your SELECTs. -## `RowIterator` +## `QueryPager` -The automated way to achieve that is `RowIterator`. It always fetches and enables access to one page, +The automated way to achieve that is `QueryPager`. It always fetches and enables access to one page, while prefetching the next one. This limits latency and is a convenient abstraction. > ***Note***\ -> `RowIterator` is quite heavy machinery, introducing considerable overhead. Therefore, +> `QueryPager` is quite heavy machinery, introducing considerable overhead. Therefore, > don't use it for statements that do not benefit from paging. In particular, avoid using it > for non-SELECTs. On API level, `Session::query_iter` and `Session::execute_iter` take a [simple query](simple.md) -or a [prepared query](prepared.md), respectively, and return an `async` iterator over result `Rows`. +or a [prepared query](prepared.md), respectively, and return a `QueryPager`. `QueryPager` needs +to be converted into typed `Stream` (by calling `QueryPager::rows_stream::`) in order to +deserialize rows. + +> ***Note***\ +> Due to lending stream limitations of Rust, `QueryPager` currently only enables deserialization +> of owned types (i.e., those with `'static` lifetime). If you want to deserialize borrowed types +> (such as slices, `&str`, etc.) in order to save allocations, you should use the manual paging +> method (described in a section **Manual Paging** below). > ***Warning***\ > In case of unprepared variant (`Session::query_iter`) if the values are not empty @@ -49,7 +57,7 @@ use futures::stream::StreamExt; let mut rows_stream = session .query_iter("SELECT a, b FROM ks.t", &[]) .await? - .into_typed::<(i32, i32)>(); + .rows_stream::<(i32, i32)>()?; while let Some(next_row_res) = rows_stream.next().await { let (a, b): (i32, i32) = next_row_res?; @@ -76,7 +84,7 @@ let prepared: PreparedStatement = session let mut rows_stream = session .execute_iter(prepared, &[]) .await? - .into_typed::<(i32, i32)>(); + .rows_stream::<(i32, i32)>()?; while let Some(next_row_res) = rows_stream.next().await { let (a, b): (i32, i32) = next_row_res?; @@ -194,10 +202,12 @@ loop { .execute_single_page(&paged_prepared, &[], paging_state) .await?; + let rows_res = res.into_rows_result()?.unwrap(); + println!( "Paging state response from the prepared statement execution: {:#?} ({} rows)", paging_state_response, - res.rows_num()?, + rows_res.rows_num(), ); match paging_state_response.into_paging_control_flow() { @@ -227,8 +237,8 @@ See [query types overview](queries.md). | Exposed Session API | `{query,execute}_unpaged` | `{query,execute}_single_page` | `{query,execute}_iter` | | Working | get all results in a single CQL frame, into a single Rust struct | get one page of results in a single CQL frame, into a single Rust struct | upon high-level iteration, fetch consecutive CQL frames and transparently iterate over their rows | | Cluster load | potentially **HIGH** for large results, beware! | normal | normal | -| Driver overhead | low - simple frame fetch | low - simple frame fetch | considerable - `RowIteratorWorker` is a separate tokio task | +| Driver overhead | low - simple frame fetch | low - simple frame fetch | considerable - `PagerWorker` is a separate tokio task | | Feature limitations | none | none | speculative execution not supported | | Driver memory footprint | potentially **BIG** - all results have to be stored at once! | small - only one page stored at a time | small - at most constant number of pages stored at a time | | Latency | potentially **BIG** - all results have to be generated at once! | considerable on page boundary - new page needs to be fetched | small - next page is always pre-fetched in background | -| Suitable operations | - in general: operations with empty result set (non-SELECTs)
- as possible optimisation: SELECTs with LIMIT clause | - for advanced users who prefer more control over paging, with less overhead of `RowIteratorWorker` | - in general: all SELECTs | \ No newline at end of file +| Suitable operations | - in general: operations with empty result set (non-SELECTs)
- as possible optimisation: SELECTs with LIMIT clause | - for advanced users who prefer more control over paging, with less overhead of `PagerWorker` | - in general: all SELECTs | \ No newline at end of file diff --git a/docs/source/queries/result.md b/docs/source/queries/result.md index 7b172ef133..5e88e15109 100644 --- a/docs/source/queries/result.md +++ b/docs/source/queries/result.md @@ -19,64 +19,18 @@ return a `QueryResult` with rows represented as `Option>`. > To sum up, **for SELECTs** (especially those that may return a lot of data) **prefer paged queries**, > e.g. with `Session::query_iter()` (see [Paged queries](paged.md)). -### Basic representation -`Row` is a basic representation of a received row. It can be used by itself, but it's a bit awkward to use: -```rust -# extern crate scylla; -# use scylla::Session; -# use std::error::Error; -# async fn check_only_compiles(session: &Session) -> Result<(), Box> { -if let Some(rows) = session.query_unpaged("SELECT a from ks.tab", &[]).await?.rows { - for row in rows { - let int_value: i32 = row.columns[0].as_ref().unwrap().as_int().unwrap(); - } -} -# Ok(()) -# } -``` - -### Parsing using `into_typed` -The driver provides a way to parse a row as a tuple of Rust types: -```rust -# extern crate scylla; -# use scylla::Session; -# use std::error::Error; -# async fn check_only_compiles(session: &Session) -> Result<(), Box> { -use scylla::IntoTypedRows; - -// Parse row as a single column containing an int value -if let Some(rows) = session.query_unpaged("SELECT a from ks.tab", &[]).await?.rows { - for row in rows { - let (int_value,): (i32,) = row.into_typed::<(i32,)>()?; - } -} - -// rows.into_typed() converts a Vec of Rows to an iterator of parsing results -if let Some(rows) = session.query_unpaged("SELECT a from ks.tab", &[]).await?.rows { - for row in rows.into_typed::<(i32,)>() { - let (int_value,): (i32,) = row?; - } -} - -// Parse row as two columns containing an int and text columns -if let Some(rows) = session.query_unpaged("SELECT a, b from ks.tab", &[]).await?.rows { - for row in rows.into_typed::<(i32, String)>() { - let (int_value, text_value): (i32, String) = row?; - } -} -# Ok(()) -# } -``` - ## Parsing using convenience methods -[`QueryResult`](https://docs.rs/scylla/latest/scylla/transport/query_result/struct.QueryResult.html) provides convenience methods for parsing rows. + +By calling [`QueryResult::into_rows_result`](https://docs.rs/scylla/latest/scylla/transport/query_result/struct.QueryResult.html#method.into_rows_result), +one can obtain [`QueryRowsResult`](https://docs.rs/scylla/latest/scylla/transport/query_result/struct.QueryRowsResult.html). +`QueryRowsResult` provides convenience methods for parsing rows. Here are a few of them: -* `rows_typed::()` - returns the rows parsed as the given type -* `maybe_first_row_typed::` - returns `Option` containing first row from the result -* `first_row_typed::` - same as `maybe_first_row`, but fails without the first row -* `single_row_typed::` - same as `first_row`, but fails when there is more than one row -* `result_not_rows()` - ensures that query response was not `rows`, helps avoid bugs +* `rows::()` - returns the rows parsed as the given type +* `maybe_first_row::()` - returns the first received row or `None` if there are no rows +* `first_row::()` - returns the first received row; fails if there are no rows +* `single_row::()` - same as `first_row`, but fails when there is more than one row +Additionally, [`QueryResult`](https://docs.rs/scylla/latest/scylla/transport/query_result/struct.QueryResult.html) has a method `result_not_rows()`, which ensures that query response was not `rows` and thus helps avoid bugs. ```rust # extern crate scylla; @@ -84,26 +38,31 @@ Here are a few of them: # use std::error::Error; # async fn check_only_compiles(session: &Session) -> Result<(), Box> { // Parse row as a single column containing an int value -let rows = session +let result = session .query_unpaged("SELECT a from ks.tab", &[]) .await? - .rows_typed::<(i32,)>()?; // Same as .rows()?.into_typed() -for row in rows { + .into_rows_result()? + .unwrap(); + +for row in result.rows::<(i32,)>()? { let (int_value,): (i32,) = row?; } -// maybe_first_row_typed gets the first row and parses it as the given type +// first_row gets the first row and parses it as the given type let first_int_val: Option<(i32,)> = session .query_unpaged("SELECT a from ks.tab", &[]) .await? - .maybe_first_row_typed::<(i32,)>()?; + .into_rows_result()? + .map(|res| res.first_row::<(i32,)>()) + .transpose()?; -// no_rows fails when the response is rows +// result_not_rows fails when the response is rows session.query_unpaged("INSERT INTO ks.tab (a) VALUES (0)", &[]).await?.result_not_rows()?; # Ok(()) # } ``` For more see [`QueryResult`](https://docs.rs/scylla/latest/scylla/transport/query_result/struct.QueryResult.html) +and [`QueryRowsResult`](https://docs.rs/scylla/latest/scylla/transport/query_result/struct.QueryRowsResult.html) ### `NULL` values `NULL` values will return an error when parsed as a Rust type. @@ -116,9 +75,12 @@ To properly handle `NULL` values parse column as an `Option<>`: use scylla::IntoTypedRows; // Parse row as two columns containing an int and text which might be null -if let Some(rows) = session.query_unpaged("SELECT a, b from ks.tab", &[]).await?.rows { - for row in rows.into_typed::<(i32, Option)>() { - let (int_value, str_or_null): (i32, Option) = row?; +if let Some(rows_result) = session.query_unpaged("SELECT a, b from ks.tab", &[]) + .await? + .into_rows_result()? +{ + for row in rows_result.rows::<(i32, Option<&str>)>()? { + let (int_value, str_or_null): (i32, Option<&str>) = row?; } } # Ok(()) @@ -130,7 +92,7 @@ It is possible to receive row as a struct with fields matching the columns.\ The struct must: * have the same number of fields as the number of queried columns * have field types matching the columns being received -* derive `FromRow` +* derive `DeserializeRow` Field names don't need to match column names. ```rust @@ -139,18 +101,21 @@ Field names don't need to match column names. # use std::error::Error; # async fn check_only_compiles(session: &Session) -> Result<(), Box> { use scylla::IntoTypedRows; -use scylla::macros::FromRow; -use scylla::frame::response::cql_to_rust::FromRow; +use scylla::macros::DeserializeRow; +use scylla::deserialize::DeserializeRow; -#[derive(FromRow)] +#[derive(DeserializeRow)] struct MyRow { age: i32, - name: Option + name: Option, } // Parse row as two columns containing an int and text which might be null -if let Some(rows) = session.query_unpaged("SELECT a, b from ks.tab", &[]).await?.rows { - for row in rows.into_typed::() { +if let Some(result_rows) = session.query_unpaged("SELECT a, b from ks.tab", &[]) + .await? + .into_rows_result()? +{ + for row in result_rows.rows::()? { let my_row: MyRow = row?; } } diff --git a/docs/source/queries/simple.md b/docs/source/queries/simple.md index f12e1cdc47..468c10c93a 100644 --- a/docs/source/queries/simple.md +++ b/docs/source/queries/simple.md @@ -75,8 +75,9 @@ Here the first `?` will be filled with `2` and the second with `"Some text"`. See [Query values](values.md) for more information about sending values in queries ### Query result -`Session::query_unpaged` returns `QueryResult` with rows represented as `Option>`.\ -Each row can be parsed as a tuple of rust types using `rows_typed`: +`Session::query_unpaged` returns `QueryResult`. +The result can then be operated on via helper methods which verify that the result is of appropriate type. +Here, we use the `rows` method to check that the response indeed contains rows with a single `int` column: ```rust # extern crate scylla; # use scylla::Session; @@ -100,8 +101,11 @@ use scylla::IntoTypedRows; // Query rows from the table and print them -let result = session.query_unpaged("SELECT a FROM ks.tab", &[]).await?; -let mut iter = result.rows_typed::<(i32,)>()?; +let result = session.query_unpaged("SELECT a FROM ks.tab", &[]) + .await? + .into_rows_result()? + .unwrap(); +let mut iter = result.rows::<(i32,)>()?; while let Some(read_row) = iter.next().transpose()? { println!("Read a value from row: {}", read_row.0); } diff --git a/docs/source/quickstart/example.md b/docs/source/quickstart/example.md index f01a761c4c..22e332b849 100644 --- a/docs/source/quickstart/example.md +++ b/docs/source/quickstart/example.md @@ -47,7 +47,7 @@ async fn main() -> Result<(), Box> { // Query rows from the table and print them let mut iter = session.query_iter("SELECT a FROM ks.extab", &[]) .await? - .into_typed::<(i32,)>(); + .rows_stream::<(i32,)>()?; while let Some(read_row) = iter.try_next().await? { println!("Read a value from row: {}", read_row.0); } diff --git a/docs/source/tracing/basic.md b/docs/source/tracing/basic.md index 0302478ebf..08417a1e09 100644 --- a/docs/source/tracing/basic.md +++ b/docs/source/tracing/basic.md @@ -20,7 +20,7 @@ let mut query: Query = Query::new("INSERT INTO ks.tab (a) VALUES(4)"); query.set_tracing(true); let res: QueryResult = session.query_unpaged(query, &[]).await?; -let tracing_id: Option = res.tracing_id; +let tracing_id: Option = res.tracing_id(); if let Some(id) = tracing_id { // Query tracing info from system_traces.sessions and system_traces.events @@ -52,7 +52,7 @@ let mut prepared: PreparedStatement = session prepared.set_tracing(true); let res: QueryResult = session.execute_unpaged(&prepared, &[]).await?; -let tracing_id: Option = res.tracing_id; +let tracing_id: Option = res.tracing_id(); if let Some(id) = tracing_id { // Query tracing info from system_traces.sessions and system_traces.events @@ -83,7 +83,7 @@ batch.append_statement("INSERT INTO ks.tab (a) VALUES(4)"); batch.set_tracing(true); let res: QueryResult = session.batch(&batch, ((),)).await?; -let tracing_id: Option = res.tracing_id; +let tracing_id: Option = res.tracing_id(); if let Some(id) = tracing_id { // Query tracing info from system_traces.sessions and system_traces.events diff --git a/docs/source/tracing/paged.md b/docs/source/tracing/paged.md index e69d4f3361..20975dfc3a 100644 --- a/docs/source/tracing/paged.md +++ b/docs/source/tracing/paged.md @@ -13,7 +13,6 @@ If tracing is enabled the row iterator will contain a list of tracing ids for al # use std::error::Error; # async fn check_only_compiles(session: &Session) -> Result<(), Box> { use scylla::query::Query; -use scylla::transport::iterator::RowIterator; use scylla::tracing::TracingInfo; use futures::StreamExt; use uuid::Uuid; @@ -23,13 +22,16 @@ let mut query: Query = Query::new("INSERT INTO ks.tab (a) VALUES(4)"); query.set_tracing(true); // Create a paged query iterator and fetch pages -let mut row_iterator: RowIterator = session.query_iter(query, &[]).await?; -while let Some(_row) = row_iterator.next().await { +let mut row_stream = session + .query_iter(query, &[]) + .await? + .rows_stream::<(i32,)>()?; +while let Some(_row) = row_stream.next().await { // Receive rows } // Now there are tracing ids for each performed query -let tracing_ids: &[Uuid] = row_iterator.get_tracing_ids(); +let tracing_ids: &[Uuid] = row_stream.tracing_ids(); for id in tracing_ids { // Query tracing info from system_traces.sessions and system_traces.events @@ -49,7 +51,6 @@ for id in tracing_ids { # use std::error::Error; # async fn check_only_compiles(session: &Session) -> Result<(), Box> { use scylla::prepared_statement::PreparedStatement; -use scylla::transport::iterator::RowIterator; use scylla::tracing::TracingInfo; use futures::StreamExt; use uuid::Uuid; @@ -63,13 +64,16 @@ let mut prepared: PreparedStatement = session prepared.set_tracing(true); // Create a paged query iterator and fetch pages -let mut row_iterator: RowIterator = session.execute_iter(prepared, &[]).await?; -while let Some(_row) = row_iterator.next().await { +let mut row_stream = session + .execute_iter(prepared, &[]) + .await? + .rows_stream::<(i32,)>()?; +while let Some(_row) = row_stream.next().await { // Receive rows } // Now there are tracing ids for each performed query -let tracing_ids: &[Uuid] = row_iterator.get_tracing_ids(); +let tracing_ids: &[Uuid] = row_stream.tracing_ids(); for id in tracing_ids { // Query tracing info from system_traces.sessions and system_traces.events