Skip to content

Commit e99b875

Browse files
committed
session_test: regression test empty collections deserialization
ScyllaDB does not distinguish empty collections from nulls. That is, INSERTing an empty collection is equivalent to nullifying the corresponding column. As pointed out in [#1001](#1001), it's a nice QOL feature to be able to deserialize empty CQL collections to empty Rust collections instead of `None::<RustCollection>`. A test is added that checks it.
1 parent a204a7b commit e99b875

File tree

1 file changed

+69
-3
lines changed

1 file changed

+69
-3
lines changed

scylla/src/transport/session_test.rs

Lines changed: 69 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
use crate::batch::{Batch, BatchStatement};
2+
use crate::deserialize::DeserializeValue;
23
use crate::prepared_statement::PreparedStatement;
34
use crate::query::Query;
45
use crate::retry_policy::{QueryInfo, RetryDecision, RetryPolicy, RetrySession};
@@ -25,12 +26,12 @@ use assert_matches::assert_matches;
2526
use futures::{FutureExt, StreamExt as _, TryStreamExt};
2627
use itertools::Itertools;
2728
use scylla_cql::frame::request::query::{PagingState, PagingStateResponse};
28-
use scylla_cql::frame::response::result::ColumnType;
29-
use scylla_cql::frame::response::result::Row;
29+
use scylla_cql::frame::response::result::{ColumnType, Row};
30+
use scylla_cql::frame::value::CqlVarint;
3031
use scylla_cql::types::serialize::row::{SerializeRow, SerializedValues};
3132
use scylla_cql::types::serialize::value::SerializeValue;
32-
use std::collections::BTreeSet;
3333
use std::collections::{BTreeMap, HashMap};
34+
use std::collections::{BTreeSet, HashSet};
3435
use std::sync::atomic::{AtomicBool, Ordering};
3536
use std::sync::Arc;
3637
use tokio::net::TcpListener;
@@ -3084,3 +3085,68 @@ async fn test_manual_primary_key_computation() {
30843085
.await;
30853086
}
30863087
}
3088+
3089+
/// ScyllaDB does not distinguish empty collections from nulls. That is, INSERTing an empty collection
3090+
/// is equivalent to nullifying the corresponding column.
3091+
/// As pointed out in [#1001](https://github.com/scylladb/scylla-rust-driver/issues/1001), it's a nice
3092+
/// QOL feature to be able to deserialize empty CQL collections to empty Rust collections instead of
3093+
/// `None::<RustCollection>`. This test checks that.
3094+
#[tokio::test]
3095+
async fn test_deserialize_empty_collections() {
3096+
// Setup session.
3097+
let ks = unique_keyspace_name();
3098+
let session = create_new_session_builder().build().await.unwrap();
3099+
session.query_unpaged(format!("CREATE KEYSPACE IF NOT EXISTS {} WITH REPLICATION = {{'class' : 'NetworkTopologyStrategy', 'replication_factor' : 1}}", ks), &[]).await.unwrap();
3100+
session.use_keyspace(&ks, true).await.unwrap();
3101+
3102+
async fn deserialize_empty_collection<
3103+
Collection: Default + for<'frame> DeserializeValue<'frame, 'frame> + SerializeValue,
3104+
>(
3105+
session: &Session,
3106+
collection_name: &str,
3107+
collection_type_params: &str,
3108+
) -> Collection {
3109+
// Create a table for the given collection type.
3110+
let table_name = "test_empty_".to_owned() + collection_name;
3111+
let query = format!(
3112+
"CREATE TABLE {} (n int primary key, c {}<{}>)",
3113+
table_name, collection_name, collection_type_params
3114+
);
3115+
session.query_unpaged(query, ()).await.unwrap();
3116+
3117+
// Populate the table with an empty collection, effectively inserting null as the collection.
3118+
session
3119+
.query_unpaged(
3120+
format!("INSERT INTO {} (n, c) VALUES (?, ?)", table_name,),
3121+
(0, Collection::default()),
3122+
)
3123+
.await
3124+
.unwrap();
3125+
3126+
let query_rows_result = session
3127+
.query_unpaged(format!("SELECT c FROM {}", table_name), ())
3128+
.await
3129+
.unwrap()
3130+
.into_rows_result()
3131+
.unwrap()
3132+
.unwrap();
3133+
let (collection,) = query_rows_result.first_row::<(Collection,)>().unwrap();
3134+
3135+
// Drop the table
3136+
collection
3137+
}
3138+
3139+
let list = deserialize_empty_collection::<Vec<i32>>(&session, "list", "int").await;
3140+
assert!(list.is_empty());
3141+
3142+
let set = deserialize_empty_collection::<HashSet<i64>>(&session, "set", "bigint").await;
3143+
assert!(set.is_empty());
3144+
3145+
let map = deserialize_empty_collection::<HashMap<bool, CqlVarint>>(
3146+
&session,
3147+
"map",
3148+
"boolean, varint",
3149+
)
3150+
.await;
3151+
assert!(map.is_empty());
3152+
}

0 commit comments

Comments
 (0)