Skip to content

Commit

Permalink
Allow objects and structs as source of named query params
Browse files Browse the repository at this point in the history
  • Loading branch information
pkolaczk committed Jun 25, 2024
1 parent d28412e commit 930c590
Show file tree
Hide file tree
Showing 2 changed files with 74 additions and 12 deletions.
13 changes: 13 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,19 @@ pub async fn run(ctx, i) {
}
```
Query parameters can be bound and passed by names as well:
```rust
const INSERT = "my_insert";
pub async fn prepare(ctx) {
ctx.prepare(INSERT, "INSERT INTO test.test(id, data) VALUES (:id, :data)").await?;
}
pub async fn run(ctx, i) {
ctx.execute_prepared(INSERT, #{id: 5, data: "foo"}).await
}
```
### Populating the database
Read queries are more interesting when they return non-empty result sets.
Expand Down
73 changes: 61 additions & 12 deletions src/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -624,22 +624,31 @@ mod bind {
field_types,
},
) => {
let borrowed = v.borrow_ref().unwrap();
let mut fields = Vec::new();
for (field_name, field_type) in field_types {
let value = match borrowed.get_value(field_name) {
Err(_) => None,
Ok(None) => Some(CqlValue::Empty),
Ok(Some(value)) => Some(to_scylla_value(&value, field_type)?),
};
fields.push((field_name.to_string(), value))
}
let obj = v.borrow_ref().unwrap();
let fields = read_fields(|s| obj.get(s), field_types)?;
Ok(CqlValue::UserDefinedType {
keyspace: keyspace.to_string(),
type_name: type_name.to_string(),
fields,
})
}
(
Value::Struct(v),
ColumnType::UserDefinedType {
keyspace,
type_name,
field_types,
},
) => {
let obj = v.borrow_ref().unwrap();
let fields = read_fields(|s| obj.get(s), field_types)?;
Ok(CqlValue::UserDefinedType {
keyspace: keyspace.to_string(),
type_name: type_name.to_string(),
fields,
})
}

(Value::Any(obj), ColumnType::Uuid) => {
let obj = obj.borrow_ref().unwrap();
let h = obj.type_hash();
Expand Down Expand Up @@ -680,28 +689,68 @@ mod bind {
params: &Value,
types: &[ColumnSpec],
) -> Result<Vec<CqlValue>, CassError> {
let mut values = Vec::new();
match params {
Ok(match params {
Value::Tuple(tuple) => {
let mut values = Vec::new();
let tuple = tuple.borrow_ref().unwrap();
if tuple.len() != types.len() {
return Err(CassError(CassErrorKind::InvalidNumberOfQueryParams));
}
for (v, t) in tuple.iter().zip(types) {
values.push(to_scylla_value(v, &t.typ)?);
}
values
}
Value::Vec(vec) => {
let mut values = Vec::new();

let vec = vec.borrow_ref().unwrap();
for (v, t) in vec.iter().zip(types) {
values.push(to_scylla_value(v, &t.typ)?);
}
values
}
Value::Object(obj) => {
let obj = obj.borrow_ref().unwrap();
read_params(|f| obj.get(f), types)?
}
Value::Struct(obj) => {
let obj = obj.borrow_ref().unwrap();
read_params(|f| obj.get(f), types)?
}
other => {
return Err(CassError(CassErrorKind::InvalidQueryParamsObject(
other.type_info().unwrap(),
)));
}
})
}

fn read_params<'a, 'b>(
get_value: impl Fn(&String) -> Option<&'a Value>,
params: &[ColumnSpec],
) -> Result<Vec<CqlValue>, CassError> {
let mut values = Vec::with_capacity(params.len());
for column in params {
let value = match get_value(&column.name) {
Some(value) => to_scylla_value(value, &column.typ)?,
None => CqlValue::Empty,
};
values.push(value)
}
Ok(values)
}

fn read_fields<'a, 'b>(
get_value: impl Fn(&String) -> Option<&'a Value>,
fields: &[(String, ColumnType)],
) -> Result<Vec<(String, Option<CqlValue>)>, CassError> {
let mut values = Vec::with_capacity(fields.len());
for (field_name, field_type) in fields {
if let Some(value) = get_value(field_name) {
let value = Some(to_scylla_value(value, field_type)?);
values.push((field_name.to_string(), value))
};
}
Ok(values)
}
Expand Down

0 comments on commit 930c590

Please sign in to comment.