Skip to content

Commit

Permalink
value: introduce CqlDecimalBorrowed
Browse files Browse the repository at this point in the history
Has the same semantics as CqlDecimal, but borrows the bytes.
  • Loading branch information
muzarski committed Dec 11, 2024
1 parent c53a8d4 commit f3fc875
Show file tree
Hide file tree
Showing 5 changed files with 97 additions and 6 deletions.
2 changes: 1 addition & 1 deletion docs/source/data-types/decimal.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@

## value::CqlDecimal

Without any feature flags, the user can interact with `decimal` type by making use of `value::CqlDecimal` which is a very simple wrapper representing the value as signed binary number in big-endian order with a 32-bit scale.
Without any feature flags, the user can interact with `decimal` type by making use of `value::CqlDecimal` or `value::CqlDecimalBorrowed` which are very simple wrappers representing the value as signed binary number in big-endian order with a 32-bit scale.

```rust
# extern crate scylla;
Expand Down
52 changes: 52 additions & 0 deletions scylla-cql/src/frame/value.rs
Original file line number Diff line number Diff line change
Expand Up @@ -454,6 +454,9 @@ impl From<CqlVarintBorrowed<'_>> for num_bigint_04::BigInt {
/// - a [`CqlVarint`] value
/// - 32-bit integer which determines the position of the decimal point
///
/// This struct holds owned bytes. If you wish to borrow the bytes instead,
/// see [`CqlDecimalBorrowed`] documentation.
///
/// The type is not very useful in most use cases.
/// However, users can make use of more complex types
/// such as `bigdecimal::BigDecimal` (v0.4).
Expand All @@ -470,6 +473,20 @@ pub struct CqlDecimal {
scale: i32,
}

/// Borrowed version of native CQL `decimal` representation.
///
/// Represented as a pair:
/// - a [`CqlVarintBorrowed`] value
/// - 32-bit integer which determines the position of the decimal point
///
/// Refer to the documentation of [`CqlDecimal`].
/// Especially, see the disclaimer about [non-normalized values](CqlDecimal#db-data-format).
#[derive(Clone, PartialEq, Eq, Debug)]
pub struct CqlDecimalBorrowed<'b> {
int_val: CqlVarintBorrowed<'b>,
scale: i32,
}

/// Constructors
impl CqlDecimal {
/// Creates a [`CqlDecimal`] from an array of bytes
Expand All @@ -492,6 +509,20 @@ impl CqlDecimal {
}
}

/// Constructors
impl<'b> CqlDecimalBorrowed<'b> {
/// Creates a [`CqlDecimalBorrowed`] from a slice of bytes
/// representing [`CqlVarintBorrowed`] and a 32-bit scale.
///
/// See: disclaimer about [non-normalized values](CqlVarint#db-data-format).
pub fn from_signed_be_bytes_slice_and_exponent(bytes: &'b [u8], scale: i32) -> Self {
Self {
int_val: CqlVarintBorrowed::from_signed_bytes_be_slice(bytes),
scale,
}
}
}

/// Conversion to raw bytes
impl CqlDecimal {
/// Returns a slice of bytes in two's complement
Expand All @@ -507,6 +538,15 @@ impl CqlDecimal {
}
}

/// Conversion to raw bytes
impl CqlDecimalBorrowed<'_> {
/// Returns a slice of bytes in two's complement
/// binary big-endian representation and a scale.
pub fn as_signed_be_bytes_slice_and_exponent(&self) -> (&[u8], i32) {
(self.int_val.as_signed_bytes_be_slice(), self.scale)
}
}

#[cfg(feature = "bigdecimal-04")]
impl From<CqlDecimal> for bigdecimal_04::BigDecimal {
fn from(value: CqlDecimal) -> Self {
Expand All @@ -519,6 +559,18 @@ impl From<CqlDecimal> for bigdecimal_04::BigDecimal {
}
}

#[cfg(feature = "bigdecimal-04")]
impl From<CqlDecimalBorrowed<'_>> for bigdecimal_04::BigDecimal {
fn from(value: CqlDecimalBorrowed) -> Self {
Self::from((
bigdecimal_04::num_bigint::BigInt::from_signed_bytes_be(
value.int_val.as_signed_bytes_be_slice(),
),
value.scale as i64,
))
}
}

#[cfg(feature = "bigdecimal-04")]
impl TryFrom<bigdecimal_04::BigDecimal> for CqlDecimal {
type Error = <i64 as TryInto<i32>>::Error;
Expand Down
23 changes: 22 additions & 1 deletion scylla-cql/src/types/deserialize/value.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,15 @@ use std::fmt::Display;
use thiserror::Error;

use super::{make_error_replace_rust_name, DeserializationError, FrameSlice, TypeCheckError};
use crate::frame::response::result::{deser_cql_value, ColumnType, CqlValue};
use crate::frame::types;
use crate::frame::value::{
Counter, CqlDate, CqlDecimal, CqlDuration, CqlTime, CqlTimestamp, CqlTimeuuid, CqlVarint,
};
use crate::frame::{frame_errors::LowLevelDeserializationError, value::CqlVarintBorrowed};
use crate::frame::{
response::result::{deser_cql_value, ColumnType, CqlValue},
value::CqlDecimalBorrowed,
};

/// A type that can be deserialized from a column value inside a row that was
/// returned from a query.
Expand Down Expand Up @@ -269,6 +272,24 @@ impl_emptiable_strict_type!(
}
);

impl_emptiable_strict_type!(
CqlDecimalBorrowed<'b>,
Decimal,
|typ: &'metadata ColumnType<'metadata>, v: Option<FrameSlice<'frame>>| {
let mut val = ensure_not_null_slice::<Self>(typ, v)?;
let scale = types::read_int(&mut val).map_err(|err| {
mk_deser_err::<Self>(
typ,
BuiltinDeserializationErrorKind::BadDecimalScale(err.into()),
)
})?;
Ok(CqlDecimalBorrowed::from_signed_be_bytes_slice_and_exponent(
val, scale,
))
},
'b
);

#[cfg(feature = "bigdecimal-04")]
impl_emptiable_strict_type!(
bigdecimal_04::BigDecimal,
Expand Down
10 changes: 8 additions & 2 deletions scylla-cql/src/types/deserialize/value_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,8 @@ use std::net::{IpAddr, Ipv4Addr, Ipv6Addr};

use crate::frame::response::result::{ColumnType, CqlValue};
use crate::frame::value::{
Counter, CqlDate, CqlDecimal, CqlDuration, CqlTime, CqlTimestamp, CqlTimeuuid, CqlVarint,
CqlVarintBorrowed,
Counter, CqlDate, CqlDecimal, CqlDecimalBorrowed, CqlDuration, CqlTime, CqlTimestamp,
CqlTimeuuid, CqlVarint, CqlVarintBorrowed,
};
use crate::types::deserialize::value::{TupleDeserializationErrorKind, TupleTypeCheckErrorKind};
use crate::types::deserialize::{DeserializationError, FrameSlice, TypeCheckError};
Expand Down Expand Up @@ -187,6 +187,12 @@ fn test_varlen_numbers() {
&mut Bytes::new(),
);

assert_ser_de_identity(
&ColumnType::Decimal,
&CqlDecimalBorrowed::from_signed_be_bytes_slice_and_exponent(b"Ala ma kota", 42),
&mut Bytes::new(),
);

#[cfg(feature = "bigdecimal-04")]
assert_ser_de_identity(
&ColumnType::Decimal,
Expand Down
16 changes: 14 additions & 2 deletions scylla-cql/src/types/serialize/value.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,8 @@ use uuid::Uuid;
use crate::frame::response::result::{ColumnType, CqlValue};
use crate::frame::types::vint_encode;
use crate::frame::value::{
Counter, CqlDate, CqlDecimal, CqlDuration, CqlTime, CqlTimestamp, CqlTimeuuid, CqlVarint,
CqlVarintBorrowed, MaybeUnset, Unset, Value,
Counter, CqlDate, CqlDecimal, CqlDecimalBorrowed, CqlDuration, CqlTime, CqlTimestamp,
CqlTimeuuid, CqlVarint, CqlVarintBorrowed, MaybeUnset, Unset, Value,
};

#[cfg(feature = "chrono-04")]
Expand Down Expand Up @@ -124,6 +124,18 @@ impl SerializeValue for CqlDecimal {
.map_err(|_| mk_ser_err::<Self>(typ, BuiltinSerializationErrorKind::SizeOverflow))?
});
}
impl SerializeValue for CqlDecimalBorrowed<'_> {
impl_serialize_via_writer!(|me, typ, writer| {
exact_type_check!(typ, Decimal);
let mut builder = writer.into_value_builder();
let (bytes, scale) = me.as_signed_be_bytes_slice_and_exponent();
builder.append_bytes(&scale.to_be_bytes());
builder.append_bytes(bytes);
builder
.finish()
.map_err(|_| mk_ser_err::<Self>(typ, BuiltinSerializationErrorKind::SizeOverflow))?
});
}
#[cfg(feature = "bigdecimal-04")]
impl SerializeValue for bigdecimal_04::BigDecimal {
impl_serialize_via_writer!(|me, typ, writer| {
Expand Down

0 comments on commit f3fc875

Please sign in to comment.