Skip to content

Commit 847e44d

Browse files
authored
Merge pull request scylladb#1148 from muzarski/cql-varint-decimal-borrowed
value: `Cql[Varint/Decimal]Borrowed`
2 parents 86904de + 5808f5a commit 847e44d

File tree

6 files changed

+239
-33
lines changed

6 files changed

+239
-33
lines changed

docs/source/data-types/decimal.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33

44
## value::CqlDecimal
55

6-
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.
6+
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.
77

88
```rust
99
# extern crate scylla;

docs/source/data-types/varint.md

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,7 @@ To make use of `num_bigint::BigInt` type, user should enable one of the availabl
77

88
## value::CqlVarint
99

10-
Without any feature flags, the user can interact with `Varint` type by making use of `value::CqlVarint` which
11-
is a very simple wrapper representing the value as signed binary number in big-endian order.
10+
Without any feature flags, the user can interact with `Varint` type by making use of `value::CqlVarint` or `value::CqlVarintBorrowed` which are very simple wrappers representing the value as signed binary number in big-endian order.
1211

1312
## Example
1413

scylla-cql/src/frame/value.rs

Lines changed: 168 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -210,6 +210,9 @@ impl std::hash::Hash for CqlTimeuuid {
210210
/// The library support (e.g. conversion from [`CqlValue`]) for these types is
211211
/// enabled via `num-bigint-03` and `num-bigint-04` crate features.
212212
///
213+
/// This struct holds owned bytes. If you wish to borrow the bytes instead,
214+
/// see [`CqlVarintBorrowed`] documentation.
215+
///
213216
/// # DB data format
214217
/// Notice that [constructors](CqlVarint#impl-CqlVarint)
215218
/// don't perform any normalization on the provided data.
@@ -223,6 +226,13 @@ impl std::hash::Hash for CqlTimeuuid {
223226
#[derive(Clone, Eq, Debug)]
224227
pub struct CqlVarint(Vec<u8>);
225228

229+
/// A borrowed version of native CQL `varint` representation.
230+
///
231+
/// Refer to the documentation of [`CqlVarint`].
232+
/// Especially, see the disclaimer about [non-normalized values](CqlVarint#db-data-format).
233+
#[derive(Clone, Eq, Debug)]
234+
pub struct CqlVarintBorrowed<'b>(&'b [u8]);
235+
226236
/// Constructors from bytes
227237
impl CqlVarint {
228238
/// Creates a [`CqlVarint`] from an array of bytes in
@@ -242,6 +252,17 @@ impl CqlVarint {
242252
}
243253
}
244254

255+
/// Constructors from bytes
256+
impl<'b> CqlVarintBorrowed<'b> {
257+
/// Creates a [`CqlVarintBorrowed`] from a slice of bytes in
258+
/// two's complement binary big-endian representation.
259+
///
260+
/// See: disclaimer about [non-normalized values](CqlVarint#db-data-format).
261+
pub fn from_signed_bytes_be_slice(digits: &'b [u8]) -> Self {
262+
Self(digits)
263+
}
264+
}
265+
245266
/// Conversion to bytes
246267
impl CqlVarint {
247268
/// Converts [`CqlVarint`] to an array of bytes in two's
@@ -257,9 +278,39 @@ impl CqlVarint {
257278
}
258279
}
259280

260-
impl CqlVarint {
281+
/// Conversion to bytes
282+
impl CqlVarintBorrowed<'_> {
283+
/// Returns a slice of bytes in two's complement
284+
/// binary big-endian representation.
285+
pub fn as_signed_bytes_be_slice(&self) -> &[u8] {
286+
self.0
287+
}
288+
}
289+
290+
/// An internal utility trait used to implement [`AsNormalizedVarintSlice`]
291+
/// for both [`CqlVarint`] and [`CqlVarintBorrowed`].
292+
trait AsVarintSlice {
293+
fn as_slice(&self) -> &[u8];
294+
}
295+
impl AsVarintSlice for CqlVarint {
296+
fn as_slice(&self) -> &[u8] {
297+
self.as_signed_bytes_be_slice()
298+
}
299+
}
300+
impl AsVarintSlice for CqlVarintBorrowed<'_> {
301+
fn as_slice(&self) -> &[u8] {
302+
self.as_signed_bytes_be_slice()
303+
}
304+
}
305+
306+
/// An internal utility trait used to implement [`PartialEq`] and [`std::hash::Hash`]
307+
/// for [`CqlVarint`] and [`CqlVarintBorrowed`].
308+
trait AsNormalizedVarintSlice {
309+
fn as_normalized_slice(&self) -> &[u8];
310+
}
311+
impl<V: AsVarintSlice> AsNormalizedVarintSlice for V {
261312
fn as_normalized_slice(&self) -> &[u8] {
262-
let digits = self.0.as_slice();
313+
let digits = self.as_slice();
263314
if digits.is_empty() {
264315
// num-bigint crate normalizes empty vector to 0.
265316
// We will follow the same approach.
@@ -293,6 +344,58 @@ impl CqlVarint {
293344
}
294345
}
295346

347+
/// Compares two [`CqlVarint`] values after normalization.
348+
///
349+
/// # Example
350+
///
351+
/// ```rust
352+
/// # use scylla_cql::frame::value::CqlVarint;
353+
/// let non_normalized_bytes = vec![0x00, 0x01];
354+
/// let normalized_bytes = vec![0x01];
355+
/// assert_eq!(
356+
/// CqlVarint::from_signed_bytes_be(non_normalized_bytes),
357+
/// CqlVarint::from_signed_bytes_be(normalized_bytes)
358+
/// );
359+
/// ```
360+
impl PartialEq for CqlVarint {
361+
fn eq(&self, other: &Self) -> bool {
362+
self.as_normalized_slice() == other.as_normalized_slice()
363+
}
364+
}
365+
366+
/// Computes the hash of normalized [`CqlVarint`].
367+
impl std::hash::Hash for CqlVarint {
368+
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
369+
self.as_normalized_slice().hash(state)
370+
}
371+
}
372+
373+
/// Compares two [`CqlVarintBorrowed`] values after normalization.
374+
///
375+
/// # Example
376+
///
377+
/// ```rust
378+
/// # use scylla_cql::frame::value::CqlVarintBorrowed;
379+
/// let non_normalized_bytes = &[0x00, 0x01];
380+
/// let normalized_bytes = &[0x01];
381+
/// assert_eq!(
382+
/// CqlVarintBorrowed::from_signed_bytes_be_slice(non_normalized_bytes),
383+
/// CqlVarintBorrowed::from_signed_bytes_be_slice(normalized_bytes)
384+
/// );
385+
/// ```
386+
impl PartialEq for CqlVarintBorrowed<'_> {
387+
fn eq(&self, other: &Self) -> bool {
388+
self.as_normalized_slice() == other.as_normalized_slice()
389+
}
390+
}
391+
392+
/// Computes the hash of normalized [`CqlVarintBorrowed`].
393+
impl std::hash::Hash for CqlVarintBorrowed<'_> {
394+
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
395+
self.as_normalized_slice().hash(state)
396+
}
397+
}
398+
296399
#[cfg(feature = "num-bigint-03")]
297400
impl From<num_bigint_03::BigInt> for CqlVarint {
298401
fn from(value: num_bigint_03::BigInt) -> Self {
@@ -307,6 +410,13 @@ impl From<CqlVarint> for num_bigint_03::BigInt {
307410
}
308411
}
309412

413+
#[cfg(feature = "num-bigint-03")]
414+
impl From<CqlVarintBorrowed<'_>> for num_bigint_03::BigInt {
415+
fn from(val: CqlVarintBorrowed<'_>) -> Self {
416+
num_bigint_03::BigInt::from_signed_bytes_be(val.0)
417+
}
418+
}
419+
310420
#[cfg(feature = "num-bigint-04")]
311421
impl From<num_bigint_04::BigInt> for CqlVarint {
312422
fn from(value: num_bigint_04::BigInt) -> Self {
@@ -321,29 +431,10 @@ impl From<CqlVarint> for num_bigint_04::BigInt {
321431
}
322432
}
323433

324-
/// Compares two [`CqlVarint`] values after normalization.
325-
///
326-
/// # Example
327-
///
328-
/// ```rust
329-
/// # use scylla_cql::frame::value::CqlVarint;
330-
/// let non_normalized_bytes = vec![0x00, 0x01];
331-
/// let normalized_bytes = vec![0x01];
332-
/// assert_eq!(
333-
/// CqlVarint::from_signed_bytes_be(non_normalized_bytes),
334-
/// CqlVarint::from_signed_bytes_be(normalized_bytes)
335-
/// );
336-
/// ```
337-
impl PartialEq for CqlVarint {
338-
fn eq(&self, other: &Self) -> bool {
339-
self.as_normalized_slice() == other.as_normalized_slice()
340-
}
341-
}
342-
343-
/// Computes the hash of normalized [`CqlVarint`].
344-
impl std::hash::Hash for CqlVarint {
345-
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
346-
self.as_normalized_slice().hash(state)
434+
#[cfg(feature = "num-bigint-04")]
435+
impl From<CqlVarintBorrowed<'_>> for num_bigint_04::BigInt {
436+
fn from(val: CqlVarintBorrowed<'_>) -> Self {
437+
num_bigint_04::BigInt::from_signed_bytes_be(val.0)
347438
}
348439
}
349440

@@ -353,6 +444,9 @@ impl std::hash::Hash for CqlVarint {
353444
/// - a [`CqlVarint`] value
354445
/// - 32-bit integer which determines the position of the decimal point
355446
///
447+
/// This struct holds owned bytes. If you wish to borrow the bytes instead,
448+
/// see [`CqlDecimalBorrowed`] documentation.
449+
///
356450
/// The type is not very useful in most use cases.
357451
/// However, users can make use of more complex types
358452
/// such as `bigdecimal::BigDecimal` (v0.4).
@@ -369,6 +463,20 @@ pub struct CqlDecimal {
369463
scale: i32,
370464
}
371465

466+
/// Borrowed version of native CQL `decimal` representation.
467+
///
468+
/// Represented as a pair:
469+
/// - a [`CqlVarintBorrowed`] value
470+
/// - 32-bit integer which determines the position of the decimal point
471+
///
472+
/// Refer to the documentation of [`CqlDecimal`].
473+
/// Especially, see the disclaimer about [non-normalized values](CqlDecimal#db-data-format).
474+
#[derive(Clone, PartialEq, Eq, Debug)]
475+
pub struct CqlDecimalBorrowed<'b> {
476+
int_val: CqlVarintBorrowed<'b>,
477+
scale: i32,
478+
}
479+
372480
/// Constructors
373481
impl CqlDecimal {
374482
/// Creates a [`CqlDecimal`] from an array of bytes
@@ -391,6 +499,20 @@ impl CqlDecimal {
391499
}
392500
}
393501

502+
/// Constructors
503+
impl<'b> CqlDecimalBorrowed<'b> {
504+
/// Creates a [`CqlDecimalBorrowed`] from a slice of bytes
505+
/// representing [`CqlVarintBorrowed`] and a 32-bit scale.
506+
///
507+
/// See: disclaimer about [non-normalized values](CqlVarint#db-data-format).
508+
pub fn from_signed_be_bytes_slice_and_exponent(bytes: &'b [u8], scale: i32) -> Self {
509+
Self {
510+
int_val: CqlVarintBorrowed::from_signed_bytes_be_slice(bytes),
511+
scale,
512+
}
513+
}
514+
}
515+
394516
/// Conversion to raw bytes
395517
impl CqlDecimal {
396518
/// Returns a slice of bytes in two's complement
@@ -406,6 +528,15 @@ impl CqlDecimal {
406528
}
407529
}
408530

531+
/// Conversion to raw bytes
532+
impl CqlDecimalBorrowed<'_> {
533+
/// Returns a slice of bytes in two's complement
534+
/// binary big-endian representation and a scale.
535+
pub fn as_signed_be_bytes_slice_and_exponent(&self) -> (&[u8], i32) {
536+
(self.int_val.as_signed_bytes_be_slice(), self.scale)
537+
}
538+
}
539+
409540
#[cfg(feature = "bigdecimal-04")]
410541
impl From<CqlDecimal> for bigdecimal_04::BigDecimal {
411542
fn from(value: CqlDecimal) -> Self {
@@ -418,6 +549,18 @@ impl From<CqlDecimal> for bigdecimal_04::BigDecimal {
418549
}
419550
}
420551

552+
#[cfg(feature = "bigdecimal-04")]
553+
impl From<CqlDecimalBorrowed<'_>> for bigdecimal_04::BigDecimal {
554+
fn from(value: CqlDecimalBorrowed) -> Self {
555+
Self::from((
556+
bigdecimal_04::num_bigint::BigInt::from_signed_bytes_be(
557+
value.int_val.as_signed_bytes_be_slice(),
558+
),
559+
value.scale as i64,
560+
))
561+
}
562+
}
563+
421564
#[cfg(feature = "bigdecimal-04")]
422565
impl TryFrom<bigdecimal_04::BigDecimal> for CqlDecimal {
423566
type Error = <i64 as TryInto<i32>>::Error;

scylla-cql/src/types/deserialize/value.rs

Lines changed: 33 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,12 +15,15 @@ use std::fmt::Display;
1515
use thiserror::Error;
1616

1717
use super::{make_error_replace_rust_name, DeserializationError, FrameSlice, TypeCheckError};
18-
use crate::frame::frame_errors::LowLevelDeserializationError;
19-
use crate::frame::response::result::{deser_cql_value, ColumnType, CqlValue};
2018
use crate::frame::types;
2119
use crate::frame::value::{
2220
Counter, CqlDate, CqlDecimal, CqlDuration, CqlTime, CqlTimestamp, CqlTimeuuid, CqlVarint,
2321
};
22+
use crate::frame::{frame_errors::LowLevelDeserializationError, value::CqlVarintBorrowed};
23+
use crate::frame::{
24+
response::result::{deser_cql_value, ColumnType, CqlValue},
25+
value::CqlDecimalBorrowed,
26+
};
2427

2528
/// A type that can be deserialized from a column value inside a row that was
2629
/// returned from a query.
@@ -222,6 +225,16 @@ impl_emptiable_strict_type!(
222225
}
223226
);
224227

228+
impl_emptiable_strict_type!(
229+
CqlVarintBorrowed<'b>,
230+
Varint,
231+
|typ: &'metadata ColumnType<'metadata>, v: Option<FrameSlice<'frame>>| {
232+
let val = ensure_not_null_slice::<Self>(typ, v)?;
233+
Ok(CqlVarintBorrowed::from_signed_bytes_be_slice(val))
234+
},
235+
'b
236+
);
237+
225238
#[cfg(feature = "num-bigint-03")]
226239
impl_emptiable_strict_type!(
227240
num_bigint_03::BigInt,
@@ -259,6 +272,24 @@ impl_emptiable_strict_type!(
259272
}
260273
);
261274

275+
impl_emptiable_strict_type!(
276+
CqlDecimalBorrowed<'b>,
277+
Decimal,
278+
|typ: &'metadata ColumnType<'metadata>, v: Option<FrameSlice<'frame>>| {
279+
let mut val = ensure_not_null_slice::<Self>(typ, v)?;
280+
let scale = types::read_int(&mut val).map_err(|err| {
281+
mk_deser_err::<Self>(
282+
typ,
283+
BuiltinDeserializationErrorKind::BadDecimalScale(err.into()),
284+
)
285+
})?;
286+
Ok(CqlDecimalBorrowed::from_signed_be_bytes_slice_and_exponent(
287+
val, scale,
288+
))
289+
},
290+
'b
291+
);
292+
262293
#[cfg(feature = "bigdecimal-04")]
263294
impl_emptiable_strict_type!(
264295
bigdecimal_04::BigDecimal,

scylla-cql/src/types/deserialize/value_tests.rs

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,8 @@ use std::net::{IpAddr, Ipv4Addr, Ipv6Addr};
1010

1111
use crate::frame::response::result::{ColumnType, CqlValue};
1212
use crate::frame::value::{
13-
Counter, CqlDate, CqlDecimal, CqlDuration, CqlTime, CqlTimestamp, CqlTimeuuid, CqlVarint,
13+
Counter, CqlDate, CqlDecimal, CqlDecimalBorrowed, CqlDuration, CqlTime, CqlTimestamp,
14+
CqlTimeuuid, CqlVarint, CqlVarintBorrowed,
1415
};
1516
use crate::types::deserialize::value::{TupleDeserializationErrorKind, TupleTypeCheckErrorKind};
1617
use crate::types::deserialize::{DeserializationError, FrameSlice, TypeCheckError};
@@ -159,6 +160,12 @@ fn test_varlen_numbers() {
159160
&mut Bytes::new(),
160161
);
161162

163+
assert_ser_de_identity(
164+
&ColumnType::Varint,
165+
&CqlVarintBorrowed::from_signed_bytes_be_slice(b"Ala ma kota"),
166+
&mut Bytes::new(),
167+
);
168+
162169
#[cfg(feature = "num-bigint-03")]
163170
assert_ser_de_identity(
164171
&ColumnType::Varint,
@@ -180,6 +187,12 @@ fn test_varlen_numbers() {
180187
&mut Bytes::new(),
181188
);
182189

190+
assert_ser_de_identity(
191+
&ColumnType::Decimal,
192+
&CqlDecimalBorrowed::from_signed_be_bytes_slice_and_exponent(b"Ala ma kota", 42),
193+
&mut Bytes::new(),
194+
);
195+
183196
#[cfg(feature = "bigdecimal-04")]
184197
assert_ser_de_identity(
185198
&ColumnType::Decimal,

0 commit comments

Comments
 (0)