Repository: scylladb/scylla-rust-driver
Pull request #492 (2022-08-03 08:01:58)

Separated CQL ser/deser code from driver logic
Another crate was created in order to allow reuse of low-level code
in other projects (including under-progress proxy). The whole `frame`
module was made separate, along with structs regarding transport
errors. Some tests that require driver transport logic as dependency
were moved from `frame` to `transport`.

## Pre-review checklist

<!--
    Make sure you took care of the issues on the list.
    Put 'x' into those boxes which apply.
    You can also create the PR now and click on all relevant checkboxes.
    See CONTRIBUTING.md for more details.
-->

- [x] I have split my patch into logically separate commits.
- [x] All commit messages clearly explain what they change and why.
- [x] I added relevant tests for new features and bug fixes.
- [x] All commits compile, pass static checks and pass test.
- [x] PR description sums up the changes and reasons why they should be introduced.
- [x] I added appropriate `Fixes:` annotations to PR description.


psarna: Just to clarify - this PR is only about code movement, no logic was changed, right?
wprzytula: > Just to clarify - this PR is only about code movement, no logic was changed, right?

I confirm. The sole purpose is to avoid circular dependency after adding a proxy crate, because the driver will depend on it for tests (and proxy would have to depend on driver to use its cql ser/deser code).

Patch:
diff --git a/scylla-cql/Cargo.toml b/scylla-cql/Cargo.toml
new file mode 100644
index 00000000..8f86a7cf
--- /dev/null
+++ b/scylla-cql/Cargo.toml
@@ -0,0 +1,24 @@
+[package]
+name = "scylla-cql"
+version = "0.0.1"
+edition = "2021"
+description = "CQL data types and primitives, for interacting with Scylla."
+repository = "https://github.com/scylladb/scylla-rust-driver"
+readme = "../README.md"
+keywords = ["database", "scylla", "cql", "cassandra"]
+categories = ["database"]
+license = "MIT OR Apache-2.0"
+
+[dependencies]
+scylla-macros = { version = "0.1.1", path = "../scylla-macros"}
+byteorder = "1.3.4"
+bytes = "1.0.1"
+num_enum = "0.5"
+tokio = "1.12"
+snap = "1.0"
+uuid = "1.0"
+thiserror = "1.0"
+bigdecimal = "0.2.0"
+num-bigint = "0.3"
+chrono = "0.4"
+lz4_flex = { version = "0.9.2" }
diff --git a/scylla/src/transport/errors.rs b/scylla-cql/src/errors.rs
similarity index 99%
rename from scylla/src/transport/errors.rs
rename to scylla-cql/src/errors.rs
index 489208c6..175b66eb 100644
--- a/scylla/src/transport/errors.rs
+++ b/scylla-cql/src/errors.rs
@@ -374,7 +374,7 @@ impl QueryError {
     /// This is caused by one of the following:
     /// - The source address is already used by another socket,
     /// - The source address is reserved and the process does not have sufficient privileges to use it.
-    pub(crate) fn is_address_unavailable_for_use(&self) -> bool {
+    pub fn is_address_unavailable_for_use(&self) -> bool {
         if let QueryError::IoError(io_error) = self {
             match io_error.kind() {
                 ErrorKind::AddrInUse | ErrorKind::PermissionDenied => return true,
@@ -405,8 +405,7 @@ impl From<&str> for WriteType {
 #[cfg(test)]
 mod tests {
     use super::{DbError, QueryError, WriteType};
-    use crate::frame::types::LegacyConsistency;
-    use crate::statement::Consistency;
+    use crate::frame::types::{Consistency, LegacyConsistency};
 
     #[test]
     fn write_type_from_str() {
diff --git a/scylla/src/frame/frame_errors.rs b/scylla-cql/src/frame/frame_errors.rs
similarity index 100%
rename from scylla/src/frame/frame_errors.rs
rename to scylla-cql/src/frame/frame_errors.rs
diff --git a/scylla/src/frame/mod.rs b/scylla-cql/src/frame/mod.rs
similarity index 90%
rename from scylla/src/frame/mod.rs
rename to scylla-cql/src/frame/mod.rs
index 64ce4852..c96963c8 100644
--- a/scylla/src/frame/mod.rs
+++ b/scylla-cql/src/frame/mod.rs
@@ -8,14 +8,7 @@ pub mod value;
 #[cfg(test)]
 mod value_tests;
 
-#[cfg(test)]
-mod cql_collections_test;
-
-#[cfg(test)]
-mod cql_types_test;
-
 use crate::frame::frame_errors::FrameError;
-use crate::transport::Compression;
 use bytes::{Buf, BufMut, Bytes};
 use tokio::io::{AsyncRead, AsyncReadExt};
 use uuid::Uuid;
@@ -33,6 +26,34 @@ pub const FLAG_TRACING: u8 = 0x02;
 pub const FLAG_CUSTOM_PAYLOAD: u8 = 0x04;
 pub const FLAG_WARNING: u8 = 0x08;
 
+// All of the Authenticators supported by Scylla
+#[derive(Debug, PartialEq)]
+pub enum Authenticator {
+    AllowAllAuthenticator,
+    PasswordAuthenticator,
+    CassandraPasswordAuthenticator,
+    CassandraAllowAllAuthenticator,
+    ScyllaTransitionalAuthenticator,
+}
+
+/// The wire protocol compression algorithm.
+#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Debug)]
+pub enum Compression {
+    /// LZ4 compression algorithm.
+    Lz4,
+    /// Snappy compression algorithm.
+    Snappy,
+}
+
+impl ToString for Compression {
+    fn to_string(&self) -> String {
+        match self {
+            Compression::Lz4 => "lz4".to_owned(),
+            Compression::Snappy => "snappy".to_owned(),
+        }
+    }
+}
+
 pub struct SerializedRequest {
     data: Vec<u8>,
 }
diff --git a/scylla/src/frame/request/auth_response.rs b/scylla-cql/src/frame/request/auth_response.rs
similarity index 97%
rename from scylla/src/frame/request/auth_response.rs
rename to scylla-cql/src/frame/request/auth_response.rs
index aebb4199..62e96f75 100644
--- a/scylla/src/frame/request/auth_response.rs
+++ b/scylla-cql/src/frame/request/auth_response.rs
@@ -3,7 +3,7 @@ use bytes::BufMut;
 use std::convert::TryInto;
 
 use crate::frame::request::{Request, RequestOpcode};
-use crate::transport::Authenticator;
+use crate::frame::Authenticator;
 
 // Implements Authenticate Response
 pub struct AuthResponse {
diff --git a/scylla/src/frame/request/batch.rs b/scylla-cql/src/frame/request/batch.rs
similarity index 100%
rename from scylla/src/frame/request/batch.rs
rename to scylla-cql/src/frame/request/batch.rs
diff --git a/scylla/src/frame/request/execute.rs b/scylla-cql/src/frame/request/execute.rs
similarity index 100%
rename from scylla/src/frame/request/execute.rs
rename to scylla-cql/src/frame/request/execute.rs
diff --git a/scylla/src/frame/request/mod.rs b/scylla-cql/src/frame/request/mod.rs
similarity index 100%
rename from scylla/src/frame/request/mod.rs
rename to scylla-cql/src/frame/request/mod.rs
diff --git a/scylla/src/frame/request/options.rs b/scylla-cql/src/frame/request/options.rs
similarity index 100%
rename from scylla/src/frame/request/options.rs
rename to scylla-cql/src/frame/request/options.rs
diff --git a/scylla/src/frame/request/prepare.rs b/scylla-cql/src/frame/request/prepare.rs
similarity index 100%
rename from scylla/src/frame/request/prepare.rs
rename to scylla-cql/src/frame/request/prepare.rs
diff --git a/scylla/src/frame/request/query.rs b/scylla-cql/src/frame/request/query.rs
similarity index 100%
rename from scylla/src/frame/request/query.rs
rename to scylla-cql/src/frame/request/query.rs
diff --git a/scylla/src/frame/request/register.rs b/scylla-cql/src/frame/request/register.rs
similarity index 100%
rename from scylla/src/frame/request/register.rs
rename to scylla-cql/src/frame/request/register.rs
diff --git a/scylla/src/frame/request/startup.rs b/scylla-cql/src/frame/request/startup.rs
similarity index 100%
rename from scylla/src/frame/request/startup.rs
rename to scylla-cql/src/frame/request/startup.rs
diff --git a/scylla/src/frame/response/authenticate.rs b/scylla-cql/src/frame/response/authenticate.rs
similarity index 100%
rename from scylla/src/frame/response/authenticate.rs
rename to scylla-cql/src/frame/response/authenticate.rs
diff --git a/scylla/src/frame/response/cql_to_rust.rs b/scylla-cql/src/frame/response/cql_to_rust.rs
similarity index 100%
rename from scylla/src/frame/response/cql_to_rust.rs
rename to scylla-cql/src/frame/response/cql_to_rust.rs
diff --git a/scylla/src/frame/response/error.rs b/scylla-cql/src/frame/response/error.rs
similarity index 98%
rename from scylla/src/frame/response/error.rs
rename to scylla-cql/src/frame/response/error.rs
index 6c510a95..61338ea6 100644
--- a/scylla/src/frame/response/error.rs
+++ b/scylla-cql/src/frame/response/error.rs
@@ -1,6 +1,6 @@
+use crate::errors::{DbError, QueryError, WriteType};
 use crate::frame::frame_errors::ParseError;
 use crate::frame::types;
-use crate::transport::errors::{DbError, QueryError, WriteType};
 use byteorder::ReadBytesExt;
 use bytes::Bytes;
 
@@ -85,9 +85,9 @@ impl From<Error> for QueryError {
 #[cfg(test)]
 mod tests {
     use super::Error;
+    use crate::errors::{DbError, WriteType};
     use crate::frame::types::LegacyConsistency;
-    use crate::statement::Consistency;
-    use crate::transport::errors::{DbError, WriteType};
+    use crate::Consistency;
     use bytes::Bytes;
     use std::convert::TryInto;
 
diff --git a/scylla-cql/src/lib.rs b/scylla-cql/src/lib.rs
new file mode 100644
index 00000000..af9f5f7a
--- /dev/null
+++ b/scylla-cql/src/lib.rs
@@ -0,0 +1,9 @@
+pub mod errors;
+pub mod frame;
+#[macro_use]
+pub mod macros;
+
+pub use crate::frame::response::cql_to_rust;
+pub use crate::frame::response::cql_to_rust::FromRow;
+
+pub use crate::frame::types::Consistency;
diff --git a/scylla/src/macros.rs b/scylla-cql/src/macros.rs
similarity index 100%
rename from scylla/src/macros.rs
rename to scylla-cql/src/macros.rs
diff --git a/scylla/Cargo.toml b/scylla/Cargo.toml
index a7391f8d..bd132a69 100644
--- a/scylla/Cargo.toml
+++ b/scylla/Cargo.toml
@@ -15,6 +15,7 @@ ssl = ["tokio-openssl", "openssl"]
 
 [dependencies]
 scylla-macros = { version = "0.1.1", path = "../scylla-macros"}
+scylla-cql = { version = "0.0.1", path = "../scylla-cql"}
 byteorder = "1.3.4"
 bytes = "1.0.1"
 futures = "0.3.6"
@@ -29,7 +30,7 @@ itertools = "0.10.0"
 bigdecimal = "0.2.0"
 num-bigint = "0.3"
 tracing = "0.1.25"
-chrono = "0.4"
+chrono = "0.4.20"
 openssl = { version = "0.10.32", optional = true }
 tokio-openssl = { version = "0.6.1", optional = true }
 arc-swap = "1.3.0"
diff --git a/scylla/src/lib.rs b/scylla/src/lib.rs
index 35421142..4ebb4b7d 100644
--- a/scylla/src/lib.rs
+++ b/scylla/src/lib.rs
@@ -91,10 +91,9 @@
 //! ```
 //! See the [book](https://rust-driver.docs.scylladb.com/stable/queries/result.html) for more receiving methods
 
-#[macro_use]
-pub mod macros;
+pub use scylla_cql::frame;
+pub use scylla_cql::macros::{self, *};
 
-pub mod frame;
 pub mod routing;
 pub mod statement;
 pub mod tracing;
@@ -102,7 +101,6 @@ pub mod transport;
 
 pub(crate) mod utils;
 
-pub use macros::*;
 pub use statement::batch;
 pub use statement::prepared_statement;
 pub use statement::query;
diff --git a/scylla/src/transport/mod.rs b/scylla/src/transport/mod.rs
index 38847585..f48497c9 100644
--- a/scylla/src/transport/mod.rs
+++ b/scylla/src/transport/mod.rs
@@ -2,7 +2,6 @@ pub(crate) mod caching_session;
 mod cluster;
 pub(crate) mod connection;
 mod connection_pool;
-pub mod errors;
 pub mod iterator;
 pub mod load_balancing;
 pub(crate) mod metrics;
@@ -14,39 +13,20 @@ pub mod session;
 pub mod session_builder;
 pub mod speculative_execution;
 pub mod topology;
+pub use crate::frame::{Authenticator, Compression};
+pub use scylla_cql::errors;
 
 #[cfg(test)]
 mod authenticate_test;
 #[cfg(test)]
-pub(crate) mod session_test;
+mod cql_collections_test;
+#[cfg(test)]
+mod session_test;
+
+#[cfg(test)]
+mod cql_types_test;
+#[cfg(test)]
+mod cql_value_test;
 
 pub use cluster::ClusterData;
 pub use node::Node;
-
-// All of the Authenticators supported by Scylla
-#[derive(Debug, PartialEq)]
-pub enum Authenticator {
-    AllowAllAuthenticator,
-    PasswordAuthenticator,
-    CassandraPasswordAuthenticator,
-    CassandraAllowAllAuthenticator,
-    ScyllaTransitionalAuthenticator,
-}
-
-/// The wire protocol compression algorithm.
-#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Debug)]
-pub enum Compression {
-    /// LZ4 compression algorithm.
-    Lz4,
-    /// Snappy compression algorithm.
-    Snappy,
-}
-
-impl ToString for Compression {
-    fn to_string(&self) -> String {
-        match self {
-            Compression::Lz4 => "lz4".to_owned(),
-            Compression::Snappy => "snappy".to_owned(),
-        }
-    }
-}


Test patch:
diff --git a/scylla/src/frame/response/event.rs b/scylla-cql/src/frame/response/event.rs
similarity index 100%
rename from scylla/src/frame/response/event.rs
rename to scylla-cql/src/frame/response/event.rs
diff --git a/scylla/src/frame/response/mod.rs b/scylla-cql/src/frame/response/mod.rs
similarity index 100%
rename from scylla/src/frame/response/mod.rs
rename to scylla-cql/src/frame/response/mod.rs
diff --git a/scylla/src/frame/response/result.rs b/scylla-cql/src/frame/response/result.rs
similarity index 100%
rename from scylla/src/frame/response/result.rs
rename to scylla-cql/src/frame/response/result.rs
diff --git a/scylla/src/frame/response/supported.rs b/scylla-cql/src/frame/response/supported.rs
similarity index 100%
rename from scylla/src/frame/response/supported.rs
rename to scylla-cql/src/frame/response/supported.rs
diff --git a/scylla/src/frame/server_event_type.rs b/scylla-cql/src/frame/server_event_type.rs
similarity index 100%
rename from scylla/src/frame/server_event_type.rs
rename to scylla-cql/src/frame/server_event_type.rs
diff --git a/scylla/src/frame/types.rs b/scylla-cql/src/frame/types.rs
similarity index 100%
rename from scylla/src/frame/types.rs
rename to scylla-cql/src/frame/types.rs
diff --git a/scylla/src/frame/value.rs b/scylla-cql/src/frame/value.rs
similarity index 100%
rename from scylla/src/frame/value.rs
rename to scylla-cql/src/frame/value.rs
diff --git a/scylla/src/frame/value_tests.rs b/scylla-cql/src/frame/value_tests.rs
similarity index 79%
rename from scylla/src/frame/value_tests.rs
rename to scylla-cql/src/frame/value_tests.rs
index 568b4ce9..7d5a8deb 100644
--- a/scylla/src/frame/value_tests.rs
+++ b/scylla-cql/src/frame/value_tests.rs
@@ -1,15 +1,10 @@
-use super::{
-    response::result::CqlValue,
-    value::{
-        BatchValues, Date, MaybeUnset, SerializeValuesError, SerializedValues, Time, Timestamp,
-        Unset, Value, ValueList, ValueTooBig,
-    },
+use super::value::{
+    BatchValues, Date, MaybeUnset, SerializeValuesError, SerializedValues, Time, Timestamp, Unset,
+    Value, ValueList, ValueTooBig,
 };
-use crate::frame::value::CqlDuration;
-use crate::{Session, SessionBuilder};
 use bytes::BufMut;
 use chrono::{Duration, NaiveDate};
-use std::{borrow::Cow, convert::TryInto, env};
+use std::{borrow::Cow, convert::TryInto};
 use uuid::Uuid;
 
 fn serialized(val: impl Value) -> Vec<u8> {
@@ -651,148 +646,3 @@ fn ref_batch_values() {
         assert_eq!(request, vec![0, 2, 0, 0, 0, 1, 1, 0, 0, 0, 1, 2]);
     }
 }
-
-#[tokio::test]
-async fn test_cqlvalue_udt() {
-    let uri = env::var("SCYLLA_URI").unwrap_or_else(|_| "127.0.0.1:9042".to_string());
-    let session: Session = SessionBuilder::new().known_node(uri).build().await.unwrap();
-    let ks = crate::transport::session_test::unique_name();
-    session
-        .query(
-            format!(
-                "CREATE KEYSPACE IF NOT EXISTS {} WITH REPLICATION = \
-            {{'class' : 'SimpleStrategy', 'replication_factor' : 1}}",
-                ks
-            ),
-            &[],
-        )
-        .await
-        .unwrap();
-    session.use_keyspace(&ks, false).await.unwrap();
-
-    session
-        .query(
-            "CREATE TYPE IF NOT EXISTS cqlvalue_udt_type (int_val int, text_val text)",
-            &[],
-        )
-        .await
-        .unwrap();
-    session
-        .query(
-            "CREATE TABLE IF NOT EXISTS cqlvalue_udt_test (k int, my cqlvalue_udt_type, primary key (k))",
-            &[],
-        )
-        .await
-        .unwrap();
-
-    let udt_cql_value = CqlValue::UserDefinedType {
-        keyspace: ks,
-        type_name: "cqlvalue_udt_type".to_string(),
-        fields: vec![
-            ("int_val".to_string(), Some(CqlValue::Int(42))),
-            ("text_val".to_string(), Some(CqlValue::Text("hi".into()))),
-        ],
-    };
-
-    session
-        .query(
-            "INSERT INTO cqlvalue_udt_test (k, my) VALUES (5, ?)",
-            (&udt_cql_value,),
-        )
-        .await
-        .unwrap();
-
-    let rows = session
-        .query("SELECT my FROM cqlvalue_udt_test", &[])
-        .await
-        .unwrap()
-        .rows
-        .unwrap();
-
-    assert_eq!(rows.len(), 1);
-    assert_eq!(rows[0].columns.len(), 1);
-
-    let received_udt_cql_value = rows[0].columns[0].as_ref().unwrap();
-
-    assert_eq!(received_udt_cql_value, &udt_cql_value);
-}
-
-#[tokio::test]
-async fn test_cqlvalue_duration() {
-    let uri = env::var("SCYLLA_URI").unwrap_or_else(|_| "127.0.0.1:9042".to_string());
-    let session: Session = SessionBuilder::new().known_node(uri).build().await.unwrap();
-
-    let ks = crate::transport::session_test::unique_name();
-    session
-        .query(
-            format!(
-                "CREATE KEYSPACE IF NOT EXISTS {} WITH REPLICATION = \
-                {{'class' : 'SimpleStrategy', 'replication_factor' : 1}}",
-                ks
-            ),
-            &[],
-        )
-        .await
-        .unwrap();
-    session.use_keyspace(&ks, false).await.unwrap();
-
-    let duration_cql_value = CqlValue::Duration(CqlDuration {
-        months: 6,
-        days: 9,
-        nanoseconds: 21372137,
-    });
-
-    let fixture_queries = vec![
-        ("CREATE TABLE IF NOT EXISTS cqlvalue_duration_test (pk int, ck int, v duration, primary key (pk, ck))", vec![],),
-        ("INSERT INTO cqlvalue_duration_test (pk, ck, v) VALUES (0, 0, ?)", vec![&duration_cql_value,],),
-        ("INSERT INTO cqlvalue_duration_test (pk, ck, v) VALUES (0, 1, 89h4m48s)", vec![],),
-        ("INSERT INTO cqlvalue_duration_test (pk, ck, v) VALUES (0, 2, PT89H8M53S)", vec![],),
-        ("INSERT INTO cqlvalue_duration_test (pk, ck, v) VALUES (0, 3, P0000-00-00T89:09:09)", vec![],),
-    ];
-
-    for query in fixture_queries {
-        session.query(query.0, query.1).await.unwrap();
-    }
-
-    let rows = session
-        .query(
-            "SELECT v FROM cqlvalue_duration_test WHERE pk = ?",
-            (CqlValue::Int(0),),
-        )
-        .await
-        .unwrap()
-        .rows
-        .unwrap();
-
-    assert_eq!(rows.len(), 4);
-    assert_eq!(rows[0].columns.len(), 1);
-
-    assert_eq!(rows[0].columns[0].as_ref().unwrap(), &duration_cql_value);
-
-    assert_eq!(
-        rows[1].columns[0].as_ref().unwrap(),
-        &CqlValue::Duration(CqlDuration {
-            months: 0,
-            days: 0,
-            nanoseconds: 320_688_000_000_000,
-        })
-    );
-
-    assert_eq!(
-        rows[2].columns[0].as_ref().unwrap(),
-        &CqlValue::Duration(CqlDuration {
-            months: 0,
-            days: 0,
-            nanoseconds: 320_933_000_000_000,
-        })
-    );
-
-    assert_eq!(
-        rows[3].columns[0].as_ref().unwrap(),
-        &CqlValue::Duration(CqlDuration {
-            months: 0,
-            days: 0,
-            nanoseconds: 320_949_000_000_000,
-        })
-    );
-}
diff --git a/scylla/src/frame/cql_collections_test.rs b/scylla/src/transport/cql_collections_test.rs
similarity index 100%
rename from scylla/src/frame/cql_collections_test.rs
rename to scylla/src/transport/cql_collections_test.rs
diff --git a/scylla/src/frame/cql_types_test.rs b/scylla/src/transport/cql_types_test.rs
similarity index 99%
rename from scylla/src/frame/cql_types_test.rs
rename to scylla/src/transport/cql_types_test.rs
index 4f67814a..f9a74c60 100644
--- a/scylla/src/frame/cql_types_test.rs
+++ b/scylla/src/transport/cql_types_test.rs
@@ -200,10 +200,10 @@ async fn test_counter() {
 async fn test_naive_date() {
     let session: Session = init_test("naive_date", "date").await;
 
-    let min_naive_date: NaiveDate = chrono::naive::MIN_DATE;
+    let min_naive_date: NaiveDate = NaiveDate::MIN;
     assert_eq!(min_naive_date, NaiveDate::from_ymd(-262144, 1, 1));
 
-    let max_naive_date: NaiveDate = chrono::naive::MAX_DATE;
+    let max_naive_date: NaiveDate = NaiveDate::MAX;
     assert_eq!(max_naive_date, NaiveDate::from_ymd(262143, 12, 31));
 
     let tests = [
diff --git a/scylla/src/transport/cql_value_test.rs b/scylla/src/transport/cql_value_test.rs
new file mode 100644
index 00000000..922ac3a6
--- /dev/null
+++ b/scylla/src/transport/cql_value_test.rs
@@ -0,0 +1,150 @@
+use std::env;
+
+use crate::frame::{response::result::CqlValue, value::CqlDuration};
+
+use crate::{Session, SessionBuilder};
+
+#[tokio::test]
+async fn test_cqlvalue_udt() {
+    let uri = env::var("SCYLLA_URI").unwrap_or_else(|_| "127.0.0.1:9042".to_string());
+    let session: Session = SessionBuilder::new().known_node(uri).build().await.unwrap();
+    let ks = crate::transport::session_test::unique_name();
+    session
+        .query(
+            format!(
+                "CREATE KEYSPACE IF NOT EXISTS {} WITH REPLICATION = \
+            {{'class' : 'SimpleStrategy', 'replication_factor' : 1}}",
+                ks
+            ),
+            &[],
+        )
+        .await
+        .unwrap();
+    session.use_keyspace(&ks, false).await.unwrap();
+
+    session
+        .query(
+            "CREATE TYPE IF NOT EXISTS cqlvalue_udt_type (int_val int, text_val text)",
+            &[],
+        )
+        .await
+        .unwrap();
+    session
+        .query(
+            "CREATE TABLE IF NOT EXISTS cqlvalue_udt_test (k int, my cqlvalue_udt_type, primary key (k))",
+            &[],
+        )
+        .await
+        .unwrap();
+
+    let udt_cql_value = CqlValue::UserDefinedType {
+        keyspace: ks,
+        type_name: "cqlvalue_udt_type".to_string(),
+        fields: vec![
+            ("int_val".to_string(), Some(CqlValue::Int(42))),
+            ("text_val".to_string(), Some(CqlValue::Text("hi".into()))),
+        ],
+    };
+
+    session
+        .query(
+            "INSERT INTO cqlvalue_udt_test (k, my) VALUES (5, ?)",
+            (&udt_cql_value,),
+        )
+        .await
+        .unwrap();
+
+    let rows = session
+        .query("SELECT my FROM cqlvalue_udt_test", &[])
+        .await
+        .unwrap()
+        .rows
+        .unwrap();
+
+    assert_eq!(rows.len(), 1);
+    assert_eq!(rows[0].columns.len(), 1);
+
+    let received_udt_cql_value = rows[0].columns[0].as_ref().unwrap();
+
+    assert_eq!(received_udt_cql_value, &udt_cql_value);
+}
+
+#[tokio::test]
+async fn test_cqlvalue_duration() {
+    let uri = env::var("SCYLLA_URI").unwrap_or_else(|_| "127.0.0.1:9042".to_string());
+    let session: Session = SessionBuilder::new().known_node(uri).build().await.unwrap();
+
+    let ks = crate::transport::session_test::unique_name();
+    session
+        .query(
+            format!(
+                "CREATE KEYSPACE IF NOT EXISTS {} WITH REPLICATION = \
+                {{'class' : 'SimpleStrategy', 'replication_factor' : 1}}",
+                ks
+            ),
+            &[],
+        )
+        .await
+        .unwrap();
+    session.use_keyspace(&ks, false).await.unwrap();
+
+    let duration_cql_value = CqlValue::Duration(CqlDuration {
+        months: 6,
+        days: 9,
+        nanoseconds: 21372137,
+    });
+
+    let fixture_queries = vec![
+        ("CREATE TABLE IF NOT EXISTS cqlvalue_duration_test (pk int, ck int, v duration, primary key (pk, ck))", vec![],),
+        ("INSERT INTO cqlvalue_duration_test (pk, ck, v) VALUES (0, 0, ?)", vec![&duration_cql_value,],),
+        ("INSERT INTO cqlvalue_duration_test (pk, ck, v) VALUES (0, 1, 89h4m48s)", vec![],),
+        ("INSERT INTO cqlvalue_duration_test (pk, ck, v) VALUES (0, 2, PT89H8M53S)", vec![],),
+        ("INSERT INTO cqlvalue_duration_test (pk, ck, v) VALUES (0, 3, P0000-00-00T89:09:09)", vec![],),
+    ];
+
+    for query in fixture_queries {
+        session.query(query.0, query.1).await.unwrap();
+    }
+
+    let rows = session
+        .query(
+            "SELECT v FROM cqlvalue_duration_test WHERE pk = ?",
+            (CqlValue::Int(0),),
+        )
+        .await
+        .unwrap()
+        .rows
+        .unwrap();
+
+    assert_eq!(rows.len(), 4);
+    assert_eq!(rows[0].columns.len(), 1);
+
+    assert_eq!(rows[0].columns[0].as_ref().unwrap(), &duration_cql_value);
+
+    assert_eq!(
+        rows[1].columns[0].as_ref().unwrap(),
+        &CqlValue::Duration(CqlDuration {
+            months: 0,
+            days: 0,
+            nanoseconds: 320_688_000_000_000,
+        })
+    );
+
+    assert_eq!(
+        rows[2].columns[0].as_ref().unwrap(),
+        &CqlValue::Duration(CqlDuration {
+            months: 0,
+            days: 0,
+            nanoseconds: 320_933_000_000_000,
+        })
+    );
+
+    assert_eq!(
+        rows[3].columns[0].as_ref().unwrap(),
+        &CqlValue::Duration(CqlDuration {
+            months: 0,
+            days: 0,
+            nanoseconds: 320_949_000_000_000,
+        })
+    );
+}