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 a298888164..76adedcb91 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", optional = true } +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 = ["dep: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 ba40b88f98..44da95097f 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 813661b37a..834654ee94 100644 --- a/scylla-cql/src/frame/response/result.rs +++ b/scylla-cql/src/frame/response/result.rs @@ -1025,16 +1025,14 @@ mod tests { assert_eq!(double_serialize, CqlValue::Double(double)); } - #[cfg(feature = "num-bigint-03")] - #[test] - fn test_varint() { - use num_bigint_03::ToBigInt; - - struct Test<'a> { - value: num_bigint_03::BigInt, - encoding: &'a [u8], - } + #[cfg(any(feature = "num-bigint-03", feature = "num-bigint-04"))] + struct VarintTestCase { + value: i32, + encoding: Vec, + } + #[cfg(any(feature = "num-bigint-03", feature = "num-bigint-04"))] + fn varint_test_cases_from_spec() -> Vec { /* Table taken from CQL Binary Protocol v4 spec @@ -1049,44 +1047,65 @@ mod tests { -128 | 0x80 -129 | 0xFF7F */ - let tests = [ - Test { - value: 0.to_bigint().unwrap(), - encoding: &[0x00], + vec![ + VarintTestCase { + value: 0, + encoding: vec![0x00], }, - Test { - value: 1.to_bigint().unwrap(), - encoding: &[0x01], + VarintTestCase { + value: 1, + encoding: vec![0x01], }, - Test { - value: 127.to_bigint().unwrap(), - encoding: &[0x7F], + VarintTestCase { + value: 127, + encoding: vec![0x7F], }, - Test { - value: 128.to_bigint().unwrap(), - encoding: &[0x00, 0x80], + VarintTestCase { + value: 128, + encoding: vec![0x00, 0x80], }, - Test { - value: 129.to_bigint().unwrap(), - encoding: &[0x00, 0x81], + VarintTestCase { + value: 129, + encoding: vec![0x00, 0x81], }, - Test { - value: (-1).to_bigint().unwrap(), - encoding: &[0xFF], + VarintTestCase { + value: -1, + encoding: vec![0xFF], }, - Test { - value: (-128).to_bigint().unwrap(), - encoding: &[0x80], + VarintTestCase { + value: -128, + encoding: vec![0x80], }, - Test { - value: (-129).to_bigint().unwrap(), - encoding: &[0xFF, 0x7F], + VarintTestCase { + value: -129, + encoding: vec![0xFF, 0x7F], }, - ]; + ] + } + + #[cfg(feature = "num-bigint-03")] + #[test] + fn test_bigint03() { + use num_bigint_03::ToBigInt; + + let tests = varint_test_cases_from_spec(); + + for t in tests.iter() { + let value = super::deser_cql_value(&ColumnType::Varint, &mut &*t.encoding).unwrap(); + assert_eq!(CqlValue::Varint(t.value.to_bigint().unwrap().into()), value); + } + } + + #[cfg(feature = "num-bigint-04")] + #[test] + fn test_bigint04() { + use num_bigint_04::ToBigInt; + + let tests = varint_test_cases_from_spec(); 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); + assert_eq!(CqlValue::Varint(t.value.to_bigint().unwrap().into()), value); } } diff --git a/scylla-cql/src/frame/value.rs b/scylla-cql/src/frame/value.rs index 47a0994e4e..7d4dbc0835 100644 --- a/scylla-cql/src/frame/value.rs +++ b/scylla-cql/src/frame/value.rs @@ -114,6 +114,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. @@ -784,6 +798,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 66d383a2c0..79cda0cfbf 100644 --- a/scylla-cql/src/frame/value_tests.rs +++ b/scylla-cql/src/frame/value_tests.rs @@ -97,10 +97,8 @@ fn cql_varint_serialization() { } } -#[cfg(feature = "num-bigint-03")] -#[test] -fn bigint03_serialization() { - let cases_from_the_spec: &[(i64, Vec)] = &[ +fn varint_test_cases_from_spec() -> Vec<(i64, Vec)> { + vec![ (0, vec![0x00]), (1, vec![0x01]), (127, vec![0x7F]), @@ -109,10 +107,18 @@ fn bigint03_serialization() { (-1, vec![0xFF]), (-128, vec![0x80]), (-129, vec![0xFF, 0x7F]), - ]; + ] +} + +#[cfg(any(feature = "num-bigint-03", feature = "num-bigint-04"))] +fn generic_num_bigint_serialization() +where + B: From + Value + SerializeCql, +{ + let cases_from_the_spec: &[(i64, Vec)] = &varint_test_cases_from_spec(); for (i, b) in cases_from_the_spec { - let x = num_bigint_03::BigInt::from(*i); + let x = B::from(*i); let b_with_len = (b.len() as i32) .to_be_bytes() .iter() @@ -123,19 +129,22 @@ fn bigint03_serialization() { } } +#[cfg(feature = "num-bigint-03")] +#[test] +fn bigint03_serialization() { + generic_num_bigint_serialization::() +} + +#[cfg(feature = "num-bigint-04")] +#[test] +fn bigint04_serialization() { + generic_num_bigint_serialization::() +} + #[test] fn bigdecimal_serialization() { // Bigint cases - 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]), - ]; + let cases_from_the_spec: &[(i64, Vec)] = &varint_test_cases_from_spec(); for exponent in -10_i32..10_i32 { for (digits, serialized_digits) in cases_from_the_spec { diff --git a/scylla-cql/src/types/serialize/value.rs b/scylla-cql/src/types/serialize/value.rs index 68e7080619..e61ab8be83 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 81d33bcac1..2d59482296 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" } @@ -58,6 +59,7 @@ socket2 = { version = "0.5.3", features = ["all"] } [dev-dependencies] num-bigint-03 = { package = "num-bigint", version = "0.3" } +num-bigint-04 = { package = "num-bigint", version = "0.4" } scylla-proxy = { version = "0.0.3", path = "../scylla-proxy" } ntest = "0.9.0" criterion = "0.4" # Note: v0.5 needs at least rust 1.70.0 diff --git a/scylla/src/transport/cql_types_test.rs b/scylla/src/transport/cql_types_test.rs index c58a48ddcc..36368479f1 100644 --- a/scylla/src/transport/cql_types_test.rs +++ b/scylla/src/transport/cql_types_test.rs @@ -103,10 +103,9 @@ where } } -#[cfg(feature = "num-bigint-03")] -#[tokio::test] -async fn test_varint() { - let tests = [ +#[cfg(any(feature = "num-bigint-03", feature = "num-bigint-04"))] +fn varint_test_cases() -> Vec<&'static str> { + vec![ "0", "1", "127", @@ -117,11 +116,23 @@ async fn test_varint() { "-129", "123456789012345678901234567890", "-123456789012345678901234567890", - ]; + ] +} +#[cfg(feature = "num-bigint-03")] +#[tokio::test] +async fn test_varint03() { + let tests = varint_test_cases(); run_tests::(&tests, "varint").await; } +#[cfg(feature = "num-bigint-04")] +#[tokio::test] +async fn test_varint04() { + let tests = varint_test_cases(); + run_tests::(&tests, "varint").await; +} + #[tokio::test] async fn test_cql_varint() { let tests = [