Skip to content

Commit

Permalink
Add UDT support for prepared statements
Browse files Browse the repository at this point in the history
Update the latte's 'to_scylla_value' function (which is used by
the 'execute_prepared' one) to support CQL UserDefinedTypes.

If we create UDT like following:

  let KEYSPACE = "fooks"
  let UDT_NAME = "fooudt"
  context.execute(`CREATE TYPE IF NOT EXISTS ${KEYSPACE}.${UDT_NAME} (
    id bigint, textk1 text, textk2 text)`).await?;

And then use it in some table.
Then, with this change, we can use the 'rune::runtime::Object' object
defined like following:

  let udtexample = #{
    "_keyspace": KEYSPACE, "_type_name": UDT_NAME,
    "id": fooid, "textk1": textv1, "textk2": textv2
  };

in an 'execute_prepared' function.

Note that '_keyspace' and '_type_name' are special and required fields
which are used to satisfy the Scylla Rust driver's
"CqlValue::UserDefinedType" object structure:

  pub enum CqlValue {
    ...
    UserDefinedType {
        keyspace: String,
        type_name: String,
        /// Order of `fields` vector must match the order of fields as defined in the UDT. The
        /// driver does not check it by itself, so incorrect data will be written if the order is
        /// wrong.
        fields: Vec<(String, Option<CqlValue>)>,
    },
    ...

Also, note that for proper workability of various sub-types of the UDT
such as `bigint` for the example `id` field mentioned above need to use
Scylla-rust driver '0.11' or newer.
  • Loading branch information
vponomaryov authored and pkolaczk committed Feb 25, 2024
1 parent ddd9a04 commit 0c37536
Showing 1 changed file with 31 additions and 0 deletions.
31 changes: 31 additions & 0 deletions src/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -394,6 +394,37 @@ mod bind {
let elements = v.as_ref().iter().map(to_scylla_value).try_collect()?;
Ok(CqlValue::List(elements))
}
Value::Object(v) => {
let borrowed = v.borrow_ref().unwrap();

// // Get value of "_keyspace" key or set default value
let keyspace = match borrowed.get_value::<str, String>("_keyspace") {
Ok(Some(value)) => value,
_ => "unknown".to_string(),
};

// // Get value of "_type_name" key or set default value
let type_name = match borrowed.get_value::<str, String>("_type_name") {
Ok(Some(value)) => value,
_ => "unknown".to_string(),
};

let keys = borrowed.keys();
let values: Result<Vec<Option<CqlValue>>, _> = borrowed.values()
.map(|value| to_scylla_value(&value.clone())
.map(Some)).collect();
let fields: Vec<(String, Option<CqlValue>)> = keys.into_iter()
.zip(values?.into_iter())

Check failure on line 417 in src/context.rs

View workflow job for this annotation

GitHub Actions / Clippy

explicit call to `.into_iter()` in function argument accepting `IntoIterator`
.filter(|&(key, _)| key != "_keyspace" && key != "_type_name")
.map(|(key, value)| (key.to_string(), value))
.collect();
let udt = CqlValue::UserDefinedType{
keyspace: keyspace,

Check failure on line 422 in src/context.rs

View workflow job for this annotation

GitHub Actions / Clippy

redundant field names in struct initialization
type_name: type_name,

Check failure on line 423 in src/context.rs

View workflow job for this annotation

GitHub Actions / Clippy

redundant field names in struct initialization
fields: fields,

Check failure on line 424 in src/context.rs

View workflow job for this annotation

GitHub Actions / Clippy

redundant field names in struct initialization
};
Ok(udt)
},
Value::Any(obj) => {
let obj = obj.borrow_ref().unwrap();
let h = obj.type_hash();
Expand Down

0 comments on commit 0c37536

Please sign in to comment.