diff --git a/examples/Cargo.toml b/examples/Cargo.toml index d6cf6e356d..66e1e2d47e 100644 --- a/examples/Cargo.toml +++ b/examples/Cargo.toml @@ -10,7 +10,7 @@ futures = "0.3.6" openssl = "0.10.32" rustyline = "9" rustyline-derive = "0.6" -scylla = {path = "../scylla", features = ["ssl", "cloud", "chrono", "time", "num-bigint-03"]} +scylla = {path = "../scylla", features = ["ssl", "cloud", "chrono", "time", "num-bigint-03", "num-bigint-04"]} tokio = {version = "1.1.0", features = ["full"]} tracing = "0.1.25" tracing-subscriber = { version = "0.3.14", features = ["env-filter"] } diff --git a/scylla-cql/Cargo.toml b/scylla-cql/Cargo.toml index 4c0d400b93..d36a340e3c 100644 --- a/scylla-cql/Cargo.toml +++ b/scylla-cql/Cargo.toml @@ -21,6 +21,7 @@ uuid = "1.0" thiserror = "1.0" bigdecimal = "0.2.0" num-bigint-03 = { package = "num-bigint", version = "0.3" } +num-bigint-04 = { package = "num-bigint", version = "0.4", optional = true } chrono = { version = "0.4.27", default-features = false, optional = true } lz4_flex = { version = "0.11.1" } async-trait = "0.1.57" @@ -41,4 +42,5 @@ secret = ["secrecy"] time = ["dep:time"] chrono = ["dep:chrono"] num-bigint-03 = [] -full-serialization = ["chrono", "time", "secret", "num-bigint-03"] +num-bigint-04 = ["dep:num-bigint-04"] +full-serialization = ["chrono", "time", "secret", "num-bigint-03", "num-bigint-04"] diff --git a/scylla-cql/src/frame/response/cql_to_rust.rs b/scylla-cql/src/frame/response/cql_to_rust.rs index ff02f351c0..f0f64632d1 100644 --- a/scylla-cql/src/frame/response/cql_to_rust.rs +++ b/scylla-cql/src/frame/response/cql_to_rust.rs @@ -156,6 +156,16 @@ impl FromCqlVal for num_bigint_03::BigInt { } } +#[cfg(feature = "num-bigint-04")] +impl FromCqlVal for num_bigint_04::BigInt { + fn from_cql(cql_val: CqlValue) -> Result { + match cql_val { + CqlValue::Varint(cql_varint) => Ok(cql_varint.into()), + _ => Err(FromCqlValError::BadCqlType), + } + } +} + #[cfg(feature = "chrono")] impl FromCqlVal for NaiveDate { fn from_cql(cql_val: CqlValue) -> Result { @@ -467,7 +477,7 @@ mod tests { #[cfg(feature = "num-bigint-03")] #[test] - fn varint_from_cql() { + fn varint03_from_cql() { use num_bigint_03::ToBigInt; let big_int = 0.to_bigint().unwrap(); @@ -477,6 +487,18 @@ mod tests { ); } + #[cfg(feature = "num-bigint-04")] + #[test] + fn varint04_from_cql() { + use num_bigint_04::ToBigInt; + + let big_int = 0.to_bigint().unwrap(); + assert_eq!( + Ok(big_int), + num_bigint_04::BigInt::from_cql(CqlValue::Varint(0.to_bigint().unwrap().into())) + ); + } + #[test] fn decimal_from_cql() { let decimal = BigDecimal::from_str("123.4").unwrap(); diff --git a/scylla-cql/src/frame/response/result.rs b/scylla-cql/src/frame/response/result.rs index 1232f8cb4e..2fc0181ec8 100644 --- a/scylla-cql/src/frame/response/result.rs +++ b/scylla-cql/src/frame/response/result.rs @@ -1027,7 +1027,7 @@ mod tests { #[cfg(feature = "num-bigint-03")] #[test] - fn test_varint() { + fn test_varint03() { use num_bigint_03::ToBigInt; struct Test<'a> { @@ -1090,6 +1090,71 @@ mod tests { } } + #[cfg(feature = "num-bigint-04")] + #[test] + fn test_varint04() { + use num_bigint_04::ToBigInt; + + struct Test<'a> { + value: num_bigint_04::BigInt, + encoding: &'a [u8], + } + + /* + Table taken from CQL Binary Protocol v4 spec + + Value | Encoding + ------|--------- + 0 | 0x00 + 1 | 0x01 + 127 | 0x7F + 128 | 0x0080 + 129 | 0x0081 + -1 | 0xFF + -128 | 0x80 + -129 | 0xFF7F + */ + let tests = [ + Test { + value: 0.to_bigint().unwrap(), + encoding: &[0x00], + }, + Test { + value: 1.to_bigint().unwrap(), + encoding: &[0x01], + }, + Test { + value: 127.to_bigint().unwrap(), + encoding: &[0x7F], + }, + Test { + value: 128.to_bigint().unwrap(), + encoding: &[0x00, 0x80], + }, + Test { + value: 129.to_bigint().unwrap(), + encoding: &[0x00, 0x81], + }, + Test { + value: (-1).to_bigint().unwrap(), + encoding: &[0xFF], + }, + Test { + value: (-128).to_bigint().unwrap(), + encoding: &[0x80], + }, + Test { + value: (-129).to_bigint().unwrap(), + encoding: &[0xFF, 0x7F], + }, + ]; + + for t in tests.iter() { + let value = super::deser_cql_value(&ColumnType::Varint, &mut &*t.encoding).unwrap(); + assert_eq!(CqlValue::Varint(t.value.clone().into()), value); + } + } + #[test] fn test_decimal() { struct Test<'a> { diff --git a/scylla-cql/src/frame/value.rs b/scylla-cql/src/frame/value.rs index a45912ddb4..c097f63a6b 100644 --- a/scylla-cql/src/frame/value.rs +++ b/scylla-cql/src/frame/value.rs @@ -99,6 +99,20 @@ impl From for num_bigint_03::BigInt { } } +#[cfg(feature = "num-bigint-04")] +impl From for CqlVarint { + fn from(value: num_bigint_04::BigInt) -> Self { + Self(value.to_signed_bytes_be()) + } +} + +#[cfg(feature = "num-bigint-04")] +impl From for num_bigint_04::BigInt { + fn from(val: CqlVarint) -> Self { + num_bigint_04::BigInt::from_signed_bytes_be(&val.0) + } +} + /// 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. @@ -769,6 +783,19 @@ impl Value for num_bigint_03::BigInt { } } +#[cfg(feature = "num-bigint-04")] +impl Value for num_bigint_04::BigInt { + fn serialize(&self, buf: &mut Vec) -> Result<(), ValueTooBig> { + let serialized = self.to_signed_bytes_be(); + let serialized_len: i32 = serialized.len().try_into().map_err(|_| ValueTooBig)?; + + buf.put_i32(serialized_len); + buf.extend_from_slice(&serialized); + + Ok(()) + } +} + impl Value for &str { fn serialize(&self, buf: &mut Vec) -> Result<(), ValueTooBig> { let str_bytes: &[u8] = self.as_bytes(); diff --git a/scylla-cql/src/frame/value_tests.rs b/scylla-cql/src/frame/value_tests.rs index 9b104faa9d..18ff1e3803 100644 --- a/scylla-cql/src/frame/value_tests.rs +++ b/scylla-cql/src/frame/value_tests.rs @@ -124,6 +124,32 @@ fn bigint03_serialization() { } } +#[cfg(feature = "num-bigint-04")] +#[test] +fn bigint04_serialization() { + let cases_from_the_spec: &[(i64, Vec)] = &[ + (0, vec![0x00]), + (1, vec![0x01]), + (127, vec![0x7F]), + (128, vec![0x00, 0x80]), + (129, vec![0x00, 0x81]), + (-1, vec![0xFF]), + (-128, vec![0x80]), + (-129, vec![0xFF, 0x7F]), + ]; + + for (i, b) in cases_from_the_spec { + let x = num_bigint_04::BigInt::from(*i); + let b_with_len = (b.len() as i32) + .to_be_bytes() + .iter() + .chain(b) + .cloned() + .collect::>(); + assert_eq!(serialized(x, ColumnType::Varint), b_with_len); + } +} + #[test] fn bigdecimal_serialization() { // Bigint cases diff --git a/scylla-cql/src/types/serialize/value.rs b/scylla-cql/src/types/serialize/value.rs index 30234165c8..31160d7fd2 100644 --- a/scylla-cql/src/types/serialize/value.rs +++ b/scylla-cql/src/types/serialize/value.rs @@ -246,6 +246,16 @@ impl SerializeCql for num_bigint_03::BigInt { .map_err(|_| mk_ser_err::(typ, BuiltinSerializationErrorKind::SizeOverflow))? }); } +#[cfg(feature = "num-bigint-04")] +impl SerializeCql for num_bigint_04::BigInt { + impl_serialize_via_writer!(|me, typ, writer| { + exact_type_check!(typ, Varint); + // TODO: See above comment for num-bigint-03. + writer + .set_value(me.to_signed_bytes_be().as_slice()) + .map_err(|_| mk_ser_err::(typ, BuiltinSerializationErrorKind::SizeOverflow))? + }); +} impl SerializeCql for &str { impl_serialize_via_writer!(|me, typ, writer| { exact_type_check!(typ, Ascii, Text); diff --git a/scylla/Cargo.toml b/scylla/Cargo.toml index c7122ebf3d..2c12a81648 100644 --- a/scylla/Cargo.toml +++ b/scylla/Cargo.toml @@ -21,7 +21,8 @@ secret = ["scylla-cql/secret"] chrono = ["scylla-cql/chrono"] time = ["scylla-cql/time"] num-bigint-03 = ["scylla-cql/num-bigint-03"] -full-serialization = ["chrono", "time", "secret", "num-bigint-03"] +num-bigint-04 = ["scylla-cql/num-bigint-04"] +full-serialization = ["chrono", "time", "secret", "num-bigint-03", "num-bigint-04"] [dependencies] scylla-macros = { version = "0.3.0", path = "../scylla-macros" } @@ -66,6 +67,7 @@ tracing-subscriber = { version = "0.3.14", features = ["env-filter"] } assert_matches = "1.5.0" rand_chacha = "0.3.1" time = "0.3" +num-bigint-04 = { package = "num-bigint", version = "0.4" } [[bench]] name = "benchmark" diff --git a/scylla/src/transport/cql_types_test.rs b/scylla/src/transport/cql_types_test.rs index fe67f0ed35..3d4fc6c1c6 100644 --- a/scylla/src/transport/cql_types_test.rs +++ b/scylla/src/transport/cql_types_test.rs @@ -105,7 +105,7 @@ where #[cfg(feature = "num-bigint-03")] #[tokio::test] -async fn test_varint() { +async fn test_varint03() { let tests = [ "0", "1", @@ -122,6 +122,25 @@ async fn test_varint() { run_tests::(&tests, "varint").await; } +#[cfg(feature = "num-bigint-04")] +#[tokio::test] +async fn test_varint04() { + let tests = [ + "0", + "1", + "127", + "128", + "129", + "-1", + "-128", + "-129", + "123456789012345678901234567890", + "-123456789012345678901234567890", + ]; + + run_tests::(&tests, "varint").await; +} + #[tokio::test] async fn test_cql_varint() { let tests = [