Skip to content

Commit

Permalink
Docs: Update informations about serialization
Browse files Browse the repository at this point in the history
  • Loading branch information
Lorak-mmk committed Dec 14, 2023
1 parent 84be507 commit dd79e3e
Show file tree
Hide file tree
Showing 2 changed files with 49 additions and 17 deletions.
25 changes: 17 additions & 8 deletions docs/source/data-types/udt.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,17 +8,25 @@ For example let's say `my_type` was created using this query:
CREATE TYPE ks.my_type (int_val int, text_val text)
```

To use this type in the driver, create a matching struct and derive `IntoUserType` and `FromUserType`:
To use this type in the driver, create a matching struct and derive:
- `SerializeCql`: in order to be able to use this struct in query parameters. \
This macro requires fields of UDT and struct to have matching names.
- `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 `SerializeCql` and `FromUserType` requirements is \
a temporary situation - in the future `FromUserType` (or the macro that \
replaces it) will also require matching names.

```rust
# extern crate scylla;
# async fn check_only_compiles() {
use scylla::macros::{FromUserType, IntoUserType};
use scylla::macros::{FromUserType, SerializeCql};

// Define a custom struct that matches the User Defined Type created earlier.
// Fields must be in the same order as they are in the database.
// Fields must be in the same order as they are in the database and also
// have the same names.
// Wrapping a field in Option will gracefully handle null field values.
#[derive(Debug, IntoUserType, FromUserType)]
#[derive(Debug, FromUserType, SerializeCql)]
struct MyType {
int_val: i32,
text_val: Option<String>,
Expand All @@ -27,8 +35,9 @@ struct MyType {
```

> ***Important***\
> Fields in the Rust struct must be defined in the same order as they are in the database.
> When sending and receiving values, the driver will (de)serialize fields one after another, without looking at field names.
> Fields in the Rust struct must be defined in the same order and with the same names as they are in the database.
> When receiving values, the driver will (de)serialize fields one after another, without looking at field names.
> When sending values, the driver will serialize the fields in the order defined by the UDT, matching Rust fields by name.
Now it can be sent and received just like any other CQL value:
```rust
Expand All @@ -37,10 +46,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<dyn Error>> {
use scylla::IntoTypedRows;
use scylla::macros::{FromUserType, IntoUserType, SerializeCql};
use scylla::macros::{FromUserType, SerializeCql};
use scylla::cql_to_rust::FromCqlVal;

#[derive(Debug, IntoUserType, FromUserType, SerializeCql)]
#[derive(Debug, FromUserType, SerializeCql)]
struct MyType {
int_val: i32,
text_val: Option<String>,
Expand Down
41 changes: 32 additions & 9 deletions docs/source/queries/values.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,14 @@ Each `?` in query text will be filled with the matching value.

> **Never** pass values by adding strings, this could lead to [SQL Injection](https://en.wikipedia.org/wiki/SQL_injection)
Each list of values to send in a query must implement the trait `ValueList`.\
Each list of values to send in a query must implement the trait `SerializeRow`.\
By default this can be a slice `&[]`, a tuple `()` (max 16 elements) of values to send,
or a custom struct which derives from `ValueList`.
or a custom struct which derives from `SerializeRow`.

A few examples:
```rust
# extern crate scylla;
# use scylla::{Session, ValueList, SerializeRow, frame::response::result::CqlValue};
# use scylla::{Session, SerializeRow, frame::response::result::CqlValue};
# use std::error::Error;
# use std::collections::HashMap;
# async fn check_only_compiles(session: &Session) -> Result<(), Box<dyn Error>> {
Expand All @@ -33,22 +33,45 @@ session
.await?;

// Sending an integer and a string using a named struct.
// The values will be passed in the order from the struct definition
#[derive(ValueList, SerializeRow)]
// Names of fields must match names of columns in request,
// but having them in the same order is not required.
// If the fields are in the same order, you can use attribute:
// `#[scylla(flavor = "enforce_order")]`
// in order to skip sorting the fields and just check if they
// are in the same order. See documentation of this macro
// for more information.
#[derive(SerializeRow)]
struct IntString {
first_col: i32,
second_col: String,
a: i32,
b: String,
}

let int_string = IntString {
first_col: 42_i32,
second_col: "hello".to_owned(),
a: 42_i32,
b: "hello".to_owned(),
};

session
.query("INSERT INTO ks.tab (a, b) VALUES(?, ?)", int_string)
.await?;

// You can use named bind markers in query if you want
// your names in struct to be different than column names.
#[derive(SerializeRow)]
struct IntStringCustom {
first_value: i32,
second_value: String,
}

let int_string_custom = IntStringCustom {
first_value: 42_i32,
second_value: "hello".to_owned(),
};

session
.query("INSERT INTO ks.tab (a, b) VALUES(:first_value, :second_value)", int_string_custom)
.await?;

// Sending a single value as a tuple requires a trailing coma (Rust syntax):
session.query("INSERT INTO ks.tab (a) VALUES(?)", (2_i32,)).await?;

Expand Down

0 comments on commit dd79e3e

Please sign in to comment.