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