Was this page helpful?
Query text is constant, but the values might change.
You can pass changing values to a query by specifying a list of variables as bound values.
Each ?
in query text will be filled with the matching value.
Never pass values by adding strings, this could lead to SQL Injection
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 SerializeRow
.
A few examples:
// Empty slice means that there are no values to send
session.query_unpaged("INSERT INTO ks.tab (a) VALUES(1)", &[]).await?;
// Empty tuple/unit also means that there are no values to send
session.query_unpaged("INSERT INTO ks.tab (a) VALUES(1)", ()).await?;
// Sending three integers using a slice:
session
.query_unpaged("INSERT INTO ks.tab (a, b, c) VALUES(?, ?, ?)", [1_i32, 2, 3].as_ref())
.await?;
// Sending an integer and a string using a tuple
session
.query_unpaged("INSERT INTO ks.tab (a, b) VALUES(?, ?)", (2_i32, "Some text"))
.await?;
// Sending an integer and a string using a named struct.
// 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 {
a: i32,
b: String,
}
let int_string = IntString {
a: 42_i32,
b: "hello".to_owned(),
};
session
.query_unpaged("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_unpaged("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_unpaged("INSERT INTO ks.tab (a) VALUES(?)", (2_i32,)).await?;
// Each value can also be sent using a reference:
session
.query_unpaged("INSERT INTO ks.tab (a, b) VALUES(?, ?)", &(&2_i32, &"Some text"))
.await?;
// A map of named values can also be provided:
let mut vals: HashMap<&str, CqlValue> = HashMap::new();
vals.insert("avalue", CqlValue::Text("hello".to_string()));
vals.insert("bvalue", CqlValue::Int(17));
session
.query_unpaged("INSERT INTO ks.tab (a, b) VALUES(:avalue, :bvalue)", &vals)
.await?;
NULL
values¶Null values can be sent using Option<>
- sending a None
will make the value NULL
:
let null_i32: Option<i32> = None;
session
.query_unpaged("INSERT INTO ks.tab (a) VALUES(?)", (null_i32,))
.await?;
Unset
values¶When performing an insert with values which might be NULL
, it’s better to use Unset
.
Database treats inserting NULL
as a delete operation and will generate a tombstone.
Using Unset
results in better performance:
use scylla::frame::value::{MaybeUnset, Unset};
// Inserting a null results in suboptimal performance
let null_i32: Option<i32> = None;
session
.query_unpaged("INSERT INTO ks.tab (a) VALUES(?)", (null_i32,))
.await?;
// Using MaybeUnset enum is better
let unset_i32: MaybeUnset<i32> = MaybeUnset::Unset;
session
.query_unpaged("INSERT INTO ks.tab (a) VALUES(?)", (unset_i32,))
.await?;
// If we are sure that a value should be unset we can simply use Unset
session
.query_unpaged("INSERT INTO ks.tab (a) VALUES(?)", (Unset,))
.await?;
See the issue for more information about Unset
See Data Types for instructions on sending other data types
Was this page helpful?