diff --git a/docs/source/SUMMARY.md b/docs/source/SUMMARY.md index 051aca6062..63639364fa 100644 --- a/docs/source/SUMMARY.md +++ b/docs/source/SUMMARY.md @@ -39,7 +39,8 @@ - [Counter](data-types/counter.md) - [Blob](data-types/blob.md) - [Inet](data-types/inet.md) - - [Uuid, Timeuuid](data-types/uuid.md) + - [Uuid](data-types/uuid.md) + - [Timeuuid](data-types/timeuuid.md) - [Date](data-types/date.md) - [Time](data-types/time.md) - [Timestamp](data-types/timestamp.md) diff --git a/docs/source/data-types/data-types.md b/docs/source/data-types/data-types.md index 6fe7c70e07..7962e5680e 100644 --- a/docs/source/data-types/data-types.md +++ b/docs/source/data-types/data-types.md @@ -20,7 +20,8 @@ Database types and their Rust equivalents: * `Counter` <----> `value::Counter` * `Blob` <----> `Vec` * `Inet` <----> `std::net::IpAddr` -* `Uuid`, `Timeuuid` <----> `uuid::Uuid` +* `Uuid` <----> `uuid::Uuid` +* `Timeuuid` <----> `value::CqlTimeuuid` * `Date` <----> `value::CqlDate`, `chrono::NaiveDate`, `time::Date` * `Time` <----> `value::CqlTime`, `chrono::NaiveTime`, `time::Time` * `Timestamp` <----> `value::CqlTimestamp`, `chrono::DateTime`, `time::OffsetDateTime` @@ -45,6 +46,7 @@ Database types and their Rust equivalents: blob inet uuid + timeuuid date time timestamp diff --git a/docs/source/data-types/timeuuid.md b/docs/source/data-types/timeuuid.md new file mode 100644 index 0000000000..2e4679f9e8 --- /dev/null +++ b/docs/source/data-types/timeuuid.md @@ -0,0 +1,30 @@ +# Timeuuid + +`Timeuuid` is represented as `value::CqlTimeuuid`. +`value::CqlTimeuuid` is a wrapper for `uuid::Uuid` with custom ordering logic +which follows Scylla/Cassandra semantics. + +```rust +# extern crate scylla; +# use scylla::Session; +# use std::error::Error; +# use std::str::FromStr; +# async fn check_only_compiles(session: &Session) -> Result<(), Box> { +use scylla::IntoTypedRows; +use scylla::frame::value::CqlTimeuuid; + +// Insert some timeuuid into the table +let to_insert: CqlTimeuuid = CqlTimeuuid::from_str("8e14e760-7fa8-11eb-bc66-000000000001")?; +session + .query("INSERT INTO keyspace.table (a) VALUES(?)", (to_insert,)) + .await?; + +// Read timeuuid from the table +if let Some(rows) = session.query("SELECT a FROM keyspace.table", &[]).await?.rows { + for row in rows.into_typed::<(CqlTimeuuid,)>() { + let (timeuuid_value,): (CqlTimeuuid,) = row?; + } +} +# Ok(()) +# } +``` \ No newline at end of file diff --git a/docs/source/data-types/uuid.md b/docs/source/data-types/uuid.md index c3cfde2725..84ab1c2d1a 100644 --- a/docs/source/data-types/uuid.md +++ b/docs/source/data-types/uuid.md @@ -1,6 +1,6 @@ -# Uuid, Timeuuid +# Uuid -`Uuid` and `Timeuuid` are represented as `uuid::Uuid` +`Uuid` is represented as `uuid::Uuid`. ```rust # extern crate scylla; @@ -11,13 +11,13 @@ use scylla::IntoTypedRows; use uuid::Uuid; -// Insert some uuid/timeuuid into the table +// Insert some uuid into the table let to_insert: Uuid = Uuid::parse_str("8e14e760-7fa8-11eb-bc66-000000000001")?; session .query("INSERT INTO keyspace.table (a) VALUES(?)", (to_insert,)) .await?; -// Read uuid/timeuuid from the table +// Read uuid from the table if let Some(rows) = session.query("SELECT a FROM keyspace.table", &[]).await?.rows { for row in rows.into_typed::<(Uuid,)>() { let (uuid_value,): (Uuid,) = row?; diff --git a/scylla-cql/src/frame/response/cql_to_rust.rs b/scylla-cql/src/frame/response/cql_to_rust.rs index 19fc72cbe2..fb9b5bf249 100644 --- a/scylla-cql/src/frame/response/cql_to_rust.rs +++ b/scylla-cql/src/frame/response/cql_to_rust.rs @@ -1,5 +1,5 @@ use super::result::{CqlValue, Row}; -use crate::frame::value::{Counter, CqlDate, CqlDuration, CqlTime, CqlTimestamp}; +use crate::frame::value::{Counter, CqlDate, CqlDuration, CqlTime, CqlTimestamp, CqlTimeuuid}; use bigdecimal::BigDecimal; use num_bigint::BigInt; use std::collections::{BTreeMap, BTreeSet, HashMap, HashSet}; @@ -134,6 +134,7 @@ impl_from_cql_value_from_method!(String, into_string); // String::from_cql, into_blob); // Vec::from_cql impl_from_cql_value_from_method!(IpAddr, as_inet); // IpAddr::from_cql impl_from_cql_value_from_method!(Uuid, as_uuid); // Uuid::from_cql +impl_from_cql_value_from_method!(CqlTimeuuid, as_timeuuid); // CqlTimeuuid::from_cql impl_from_cql_value_from_method!(BigDecimal, into_decimal); // BigDecimal::from_cql impl_from_cql_value_from_method!(CqlDuration, as_cql_duration); // CqlDuration::from_cql impl_from_cql_value_from_method!(CqlDate, as_cql_date); // CqlDate::from_cql @@ -390,7 +391,7 @@ impl_tuple_from_cql!(T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14 mod tests { use super::{CqlValue, FromCqlVal, FromCqlValError, FromRow, FromRowError, Row}; use crate as scylla; - use crate::frame::value::{Counter, CqlDate, CqlDuration, CqlTime, CqlTimestamp}; + use crate::frame::value::{Counter, CqlDate, CqlDuration, CqlTime, CqlTimestamp, CqlTimeuuid}; use crate::macros::FromRow; use bigdecimal::BigDecimal; use num_bigint::{BigInt, ToBigInt}; @@ -753,7 +754,9 @@ mod tests { #[test] fn uuid_from_cql() { - let test_uuid: Uuid = Uuid::parse_str("8e14e760-7fa8-11eb-bc66-000000000001").unwrap(); + let uuid_str = "8e14e760-7fa8-11eb-bc66-000000000001"; + let test_uuid: Uuid = Uuid::parse_str(uuid_str).unwrap(); + let test_time_uuid = CqlTimeuuid::from_str(uuid_str).unwrap(); assert_eq!( test_uuid, @@ -761,8 +764,8 @@ mod tests { ); assert_eq!( - test_uuid, - Uuid::from_cql(CqlValue::Timeuuid(test_uuid)).unwrap() + test_time_uuid, + CqlTimeuuid::from_cql(CqlValue::Timeuuid(test_time_uuid)).unwrap() ); } diff --git a/scylla-cql/src/frame/response/result.rs b/scylla-cql/src/frame/response/result.rs index f516d6e510..0833cda62a 100644 --- a/scylla-cql/src/frame/response/result.rs +++ b/scylla-cql/src/frame/response/result.rs @@ -1,7 +1,7 @@ use crate::cql_to_rust::{FromRow, FromRowError}; use crate::frame::response::event::SchemaChangeEvent; use crate::frame::types::vint_decode; -use crate::frame::value::{Counter, CqlDate, CqlDuration, CqlTime, CqlTimestamp}; +use crate::frame::value::{Counter, CqlDate, CqlDuration, CqlTime, CqlTimestamp, CqlTimeuuid}; use crate::frame::{frame_errors::ParseError, types}; use bigdecimal::BigDecimal; use byteorder::{BigEndian, ReadBytesExt}; @@ -110,7 +110,7 @@ pub enum CqlValue { TinyInt(i8), /// Nanoseconds since midnight Time(CqlTime), - Timeuuid(Uuid), + Timeuuid(CqlTimeuuid), Tuple(Vec>), Uuid(Uuid), Varint(BigInt), @@ -231,7 +231,6 @@ impl CqlValue { pub fn as_uuid(&self) -> Option { match self { Self::Uuid(u) => Some(*u), - Self::Timeuuid(u) => Some(*u), _ => None, } } @@ -285,7 +284,7 @@ impl CqlValue { } } - pub fn as_timeuuid(&self) -> Option { + pub fn as_timeuuid(&self) -> Option { match self { Self::Timeuuid(u) => Some(*u), _ => None, @@ -770,7 +769,7 @@ pub fn deser_cql_value(typ: &ColumnType, buf: &mut &[u8]) -> StdResult { let months = i32::try_from(vint_decode(buf)?)?; @@ -966,7 +965,7 @@ pub fn deserialize(buf: &mut &[u8]) -> StdResult { #[cfg(test)] mod tests { use crate as scylla; - use crate::frame::value::{Counter, CqlDate, CqlDuration, CqlTime, CqlTimestamp}; + use crate::frame::value::{Counter, CqlDate, CqlDuration, CqlTime, CqlTimestamp, CqlTimeuuid}; use bigdecimal::BigDecimal; use num_bigint::BigInt; use num_bigint::ToBigInt; @@ -993,9 +992,10 @@ mod tests { let uuid_serialize = super::deser_cql_value(&ColumnType::Uuid, uuid_slice).unwrap(); assert_eq!(uuid_serialize, CqlValue::Uuid(my_uuid)); + let my_timeuuid = CqlTimeuuid::from_str("00000000000000000000000000000001").unwrap(); let time_uuid_serialize = super::deser_cql_value(&ColumnType::Timeuuid, uuid_slice).unwrap(); - assert_eq!(time_uuid_serialize, CqlValue::Timeuuid(my_uuid)); + assert_eq!(time_uuid_serialize, CqlValue::Timeuuid(my_timeuuid)); let my_ip = "::1".parse().unwrap(); let ip_buf: Vec = vec![0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1]; @@ -1754,7 +1754,7 @@ mod tests { match cql_val { CqlValue::Timeuuid(uuid) => { assert_eq!(uuid.as_bytes(), uuid_bytes); - assert_eq!(Uuid::parse_str(uuid_str).unwrap(), uuid); + assert_eq!(CqlTimeuuid::from_str(uuid_str).unwrap(), uuid); } _ => panic!("Timeuuid parsed as wrong CqlValue"), } diff --git a/scylla-cql/src/frame/value.rs b/scylla-cql/src/frame/value.rs index 3c17c8629f..22a66f6732 100644 --- a/scylla-cql/src/frame/value.rs +++ b/scylla-cql/src/frame/value.rs @@ -51,6 +51,170 @@ pub enum MaybeUnset { Set(V), } +/// Represents timeuuid (uuid V1) value +/// +/// This type has custom comparison logic which follows Scylla/Cassandra semantics. +/// For details, see [`Ord` implementation](#impl-Ord-for-CqlTimeuuid). +#[derive(Debug, Clone, Copy, Eq)] +pub struct CqlTimeuuid(Uuid); + +/// [`Uuid`] delegate methods +impl CqlTimeuuid { + pub fn as_bytes(&self) -> &[u8; 16] { + self.0.as_bytes() + } + + pub fn as_u128(&self) -> u128 { + self.0.as_u128() + } + + pub fn as_fields(&self) -> (u32, u16, u16, &[u8; 8]) { + self.0.as_fields() + } + + pub fn as_u64_pair(&self) -> (u64, u64) { + self.0.as_u64_pair() + } + + pub fn from_slice(b: &[u8]) -> Result { + Ok(Self(Uuid::from_slice(b)?)) + } + + pub fn from_slice_le(b: &[u8]) -> Result { + Ok(Self(Uuid::from_slice_le(b)?)) + } + + pub fn from_bytes(bytes: [u8; 16]) -> Self { + Self(Uuid::from_bytes(bytes)) + } + + pub fn from_bytes_le(bytes: [u8; 16]) -> Self { + Self(Uuid::from_bytes_le(bytes)) + } + + pub fn from_fields(d1: u32, d2: u16, d3: u16, d4: &[u8; 8]) -> Self { + Self(Uuid::from_fields(d1, d2, d3, d4)) + } + + pub fn from_fields_le(d1: u32, d2: u16, d3: u16, d4: &[u8; 8]) -> Self { + Self(Uuid::from_fields_le(d1, d2, d3, d4)) + } + + pub fn from_u128(v: u128) -> Self { + Self(Uuid::from_u128(v)) + } + + pub fn from_u128_le(v: u128) -> Self { + Self(Uuid::from_u128_le(v)) + } + + pub fn from_u64_pair(high_bits: u64, low_bits: u64) -> Self { + Self(Uuid::from_u64_pair(high_bits, low_bits)) + } +} + +impl CqlTimeuuid { + /// Read 8 most significant bytes of timeuuid from serialized bytes + fn msb(&self) -> u64 { + // Scylla and Cassandra use a standard UUID memory layout for MSB: + // 4 bytes 2 bytes 2 bytes + // time_low - time_mid - time_hi_and_version + let bytes = self.0.as_bytes(); + ((bytes[6] & 0x0F) as u64) << 56 + | (bytes[7] as u64) << 48 + | (bytes[4] as u64) << 40 + | (bytes[5] as u64) << 32 + | (bytes[0] as u64) << 24 + | (bytes[1] as u64) << 16 + | (bytes[2] as u64) << 8 + | (bytes[3] as u64) + } + + fn lsb(&self) -> u64 { + let bytes = self.0.as_bytes(); + (bytes[8] as u64) << 56 + | (bytes[9] as u64) << 48 + | (bytes[10] as u64) << 40 + | (bytes[11] as u64) << 32 + | (bytes[12] as u64) << 24 + | (bytes[13] as u64) << 16 + | (bytes[14] as u64) << 8 + | (bytes[15] as u64) + } + + fn lsb_signed(&self) -> u64 { + self.lsb() ^ 0x8080808080808080 + } +} + +impl std::str::FromStr for CqlTimeuuid { + type Err = uuid::Error; + + fn from_str(s: &str) -> Result { + Ok(Self(Uuid::from_str(s)?)) + } +} + +impl std::fmt::Display for CqlTimeuuid { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{}", self.0) + } +} + +impl AsRef for CqlTimeuuid { + fn as_ref(&self) -> &Uuid { + &self.0 + } +} + +impl From for Uuid { + fn from(value: CqlTimeuuid) -> Self { + value.0 + } +} + +impl From for CqlTimeuuid { + fn from(value: Uuid) -> Self { + Self(value) + } +} + +/// Compare two values of timeuuid type. +/// +/// Cassandra legacy requires: +/// - converting 8 most significant bytes to date, which is then compared. +/// - masking off UUID version from the 8 ms-bytes during compare, to +/// treat possible non-version-1 UUID the same way as UUID. +/// - using signed compare for least significant bits. +impl Ord for CqlTimeuuid { + fn cmp(&self, other: &Self) -> std::cmp::Ordering { + let mut res = self.msb().cmp(&other.msb()); + if let std::cmp::Ordering::Equal = res { + res = self.lsb_signed().cmp(&other.lsb_signed()); + } + res + } +} + +impl PartialOrd for CqlTimeuuid { + fn partial_cmp(&self, other: &Self) -> Option { + Some(self.cmp(other)) + } +} + +impl PartialEq for CqlTimeuuid { + fn eq(&self, other: &Self) -> bool { + self.cmp(other) == std::cmp::Ordering::Equal + } +} + +impl std::hash::Hash for CqlTimeuuid { + fn hash(&self, state: &mut H) { + self.lsb_signed().hash(state); + self.msb().hash(state); + } +} + /// Native CQL date representation that allows for a bigger range of dates (-262145-1-1 to 262143-12-31). /// /// Represented as number of days since -5877641-06-23 i.e. 2^31 days before unix epoch. @@ -696,6 +860,12 @@ impl Value for Uuid { } } +impl Value for CqlTimeuuid { + fn serialize(&self, buf: &mut Vec) -> Result<(), ValueTooBig> { + self.0.serialize(buf) + } +} + impl Value for BigInt { fn serialize(&self, buf: &mut Vec) -> Result<(), ValueTooBig> { let serialized = self.to_signed_bytes_be(); diff --git a/scylla-cql/src/frame/value_tests.rs b/scylla-cql/src/frame/value_tests.rs index cb6a94ee49..e168e8c4fb 100644 --- a/scylla-cql/src/frame/value_tests.rs +++ b/scylla-cql/src/frame/value_tests.rs @@ -1,3 +1,4 @@ +use crate::frame::value::CqlTimeuuid; use crate::frame::{response::result::CqlValue, types::RawValue, value::LegacyBatchValuesIterator}; use crate::types::serialize::batch::{BatchValues, BatchValuesIterator, LegacyBatchValuesAdapter}; use crate::types::serialize::row::{RowSerializationContext, SerializeRow}; @@ -12,9 +13,11 @@ use super::value::{ use bigdecimal::BigDecimal; use bytes::BufMut; use num_bigint::BigInt; +use std::collections::hash_map::DefaultHasher; use std::collections::{BTreeMap, BTreeSet, HashMap, HashSet}; -use std::hash::{BuildHasherDefault, Hasher}; +use std::hash::{BuildHasherDefault, Hash, Hasher}; use std::net::{IpAddr, Ipv4Addr, Ipv6Addr}; +use std::str::FromStr; use std::{borrow::Cow, convert::TryInto}; use uuid::Uuid; @@ -538,6 +541,24 @@ fn timeuuid_serialization() { } } +#[test] +fn timeuuid_ordering_properties() { + let x = CqlTimeuuid::from_str("00000000-0000-1000-8080-808080808080").unwrap(); + let y = CqlTimeuuid::from_str("00000000-0000-2000-8080-808080808080").unwrap(); + + let cmp_res = x.cmp(&y); + assert_eq!(std::cmp::Ordering::Equal, cmp_res); + + assert_eq!(x, y); + + let compute_hash = |x: &CqlTimeuuid| { + let mut hasher = DefaultHasher::new(); + x.hash(&mut hasher); + hasher.finish() + }; + assert_eq!(compute_hash(&x), compute_hash(&y)); +} + #[test] fn cqlduration_serialization() { let duration = CqlDuration { diff --git a/scylla-cql/src/types/serialize/value.rs b/scylla-cql/src/types/serialize/value.rs index 9e5a691be6..8abe509585 100644 --- a/scylla-cql/src/types/serialize/value.rs +++ b/scylla-cql/src/types/serialize/value.rs @@ -20,7 +20,7 @@ use secrecy::{ExposeSecret, Secret, Zeroize}; use crate::frame::response::result::{ColumnType, CqlValue}; use crate::frame::types::vint_encode; use crate::frame::value::{ - Counter, CqlDate, CqlDuration, CqlTime, CqlTimestamp, MaybeUnset, Unset, Value, + Counter, CqlDate, CqlDuration, CqlTime, CqlTimestamp, CqlTimeuuid, MaybeUnset, Unset, Value, }; #[cfg(feature = "chrono")] @@ -223,7 +223,13 @@ impl SerializeCql for f64 { } impl SerializeCql for Uuid { impl_serialize_via_writer!(|me, typ, writer| { - exact_type_check!(typ, Uuid, Timeuuid); + exact_type_check!(typ, Uuid); + writer.set_value(me.as_bytes().as_ref()).unwrap() + }); +} +impl SerializeCql for CqlTimeuuid { + impl_serialize_via_writer!(|me, typ, writer| { + exact_type_check!(typ, Timeuuid); writer.set_value(me.as_bytes().as_ref()).unwrap() }); } diff --git a/scylla/src/tracing.rs b/scylla/src/tracing.rs index 7753e4bd91..53019e7865 100644 --- a/scylla/src/tracing.rs +++ b/scylla/src/tracing.rs @@ -1,7 +1,7 @@ use itertools::Itertools; +use scylla_cql::frame::value::CqlTimeuuid; use std::collections::HashMap; use std::net::IpAddr; -use uuid::Uuid; use crate::cql_to_rust::{FromRow, FromRowError}; use crate::frame::response::result::Row; @@ -26,7 +26,7 @@ pub struct TracingInfo { /// A single event happening during a traced query #[derive(Debug, Clone, PartialEq, Eq)] pub struct TracingEvent { - pub event_id: Uuid, + pub event_id: CqlTimeuuid, pub activity: Option, pub source: Option, pub source_elapsed: Option, @@ -85,7 +85,7 @@ impl FromRow for TracingInfo { impl FromRow for TracingEvent { fn from_row(row: Row) -> Result { let (event_id, activity, source, source_elapsed, thread) = <( - Uuid, + CqlTimeuuid, Option, Option, Option, diff --git a/scylla/src/transport/cql_types_test.rs b/scylla/src/transport/cql_types_test.rs index 1ab0997728..d90a169268 100644 --- a/scylla/src/transport/cql_types_test.rs +++ b/scylla/src/transport/cql_types_test.rs @@ -8,14 +8,15 @@ use crate::transport::session::IntoTypedRows; use crate::transport::session::Session; use crate::utils::test_utils::unique_keyspace_name; use bigdecimal::BigDecimal; +use itertools::Itertools; use num_bigint::BigInt; +use scylla_cql::frame::value::CqlTimeuuid; use scylla_cql::types::serialize::value::SerializeCql; use scylla_macros::SerializeCql; use std::cmp::PartialEq; use std::fmt::Debug; use std::net::{IpAddr, Ipv4Addr, Ipv6Addr}; use std::str::FromStr; -use uuid::Uuid; // Used to prepare a table for test // Creates a new keyspace @@ -1111,13 +1112,13 @@ async fn test_timeuuid() { .await .unwrap(); - let (read_timeuuid,): (Uuid,) = session + let (read_timeuuid,): (CqlTimeuuid,) = session .query("SELECT val from timeuuid_tests", &[]) .await .unwrap() .rows .unwrap() - .into_typed::<(Uuid,)>() + .into_typed::<(CqlTimeuuid,)>() .next() .unwrap() .unwrap(); @@ -1125,7 +1126,7 @@ async fn test_timeuuid() { assert_eq!(read_timeuuid.as_bytes(), timeuuid_bytes); // Insert timeuuid as a bound value and verify that it matches - let test_uuid: Uuid = Uuid::from_slice(timeuuid_bytes.as_ref()).unwrap(); + let test_uuid: CqlTimeuuid = CqlTimeuuid::from_slice(timeuuid_bytes.as_ref()).unwrap(); session .query( "INSERT INTO timeuuid_tests (id, val) VALUES (0, ?)", @@ -1134,13 +1135,13 @@ async fn test_timeuuid() { .await .unwrap(); - let (read_timeuuid,): (Uuid,) = session + let (read_timeuuid,): (CqlTimeuuid,) = session .query("SELECT val from timeuuid_tests", &[]) .await .unwrap() .rows .unwrap() - .into_typed::<(Uuid,)>() + .into_typed::<(CqlTimeuuid,)>() .next() .unwrap() .unwrap(); @@ -1149,6 +1150,84 @@ async fn test_timeuuid() { } } +#[tokio::test] +async fn test_timeuuid_ordering() { + let session: Session = create_new_session_builder().build().await.unwrap(); + let ks = unique_keyspace_name(); + + session + .query( + format!( + "CREATE KEYSPACE IF NOT EXISTS {} WITH REPLICATION = \ + {{'class' : 'NetworkTopologyStrategy', 'replication_factor' : 1}}", + ks + ), + &[], + ) + .await + .unwrap(); + session.use_keyspace(ks, false).await.unwrap(); + + session + .query( + "CREATE TABLE tab (p int, t timeuuid, PRIMARY KEY (p, t))", + (), + ) + .await + .unwrap(); + + // Timeuuid values, sorted in the same order as Scylla/Cassandra sorts them. + let sorted_timeuuid_vals: Vec = vec![ + CqlTimeuuid::from_str("00000000-0000-1000-8080-808080808080").unwrap(), + CqlTimeuuid::from_str("00000000-0000-1000-ffff-ffffffffffff").unwrap(), + CqlTimeuuid::from_str("00000000-0000-1000-0000-000000000000").unwrap(), + CqlTimeuuid::from_str("fed35080-0efb-11ee-a1ca-00006490e9a4").unwrap(), + CqlTimeuuid::from_str("00000257-0efc-11ee-9547-00006490e9a6").unwrap(), + CqlTimeuuid::from_str("ffffffff-ffff-1fff-ffff-ffffffffffef").unwrap(), + CqlTimeuuid::from_str("ffffffff-ffff-1fff-ffff-ffffffffffff").unwrap(), + CqlTimeuuid::from_str("ffffffff-ffff-1fff-0000-000000000000").unwrap(), + CqlTimeuuid::from_str("ffffffff-ffff-1fff-7f7f-7f7f7f7f7f7f").unwrap(), + ]; + + // Generate all permutations. + let perms = Itertools::permutations(sorted_timeuuid_vals.iter(), sorted_timeuuid_vals.len()) + .collect::>(); + // Ensure that all of the permutations were generated. + assert_eq!(362880, perms.len()); + + // Verify that Scylla really sorts timeuuids as defined in sorted_timeuuid_vals + let prepared = session + .prepare("INSERT INTO tab (p, t) VALUES (0, ?)") + .await + .unwrap(); + for timeuuid_val in &perms[0] { + session.execute(&prepared, (timeuuid_val,)).await.unwrap(); + } + + let scylla_order_timeuuids: Vec = session + .query("SELECT t FROM tab WHERE p = 0", ()) + .await + .unwrap() + .rows_typed::<(CqlTimeuuid,)>() + .unwrap() + .map(|r| r.unwrap().0) + .collect(); + + assert_eq!(sorted_timeuuid_vals, scylla_order_timeuuids); + + for perm in perms { + // Test if rust timeuuid values are sorted in the same way as in Scylla + let mut rust_sorted_timeuuids: Vec = perm + .clone() + .into_iter() + .map(|x| x.to_owned()) + .collect::>(); + rust_sorted_timeuuids.sort(); + + assert_eq!(sorted_timeuuid_vals, rust_sorted_timeuuids); + } +} + #[tokio::test] async fn test_inet() { let session: Session = init_test("inet_tests", "inet").await;