Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

serialization: implement new serialization traits for the currently supported types #862

Merged
merged 29 commits into from
Dec 5, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
33413a4
frame/value: introduce ValueOverflow error
piodul Nov 16, 2023
067a5af
frame/value_tests: exercise both old and new ser cql traits
piodul Oct 29, 2023
a07ee60
frame/value_tests: add tests for types that didn't have ones
piodul Nov 20, 2023
fc101d9
serialize/writers: make overflow errors easier to handle
piodul Nov 30, 2023
5790698
types/serialize: add a shorthand for constructing SerializationError
piodul Dec 4, 2023
44d4374
serialize/value: make fallbacks for `SerializeCql` explicit
piodul Oct 27, 2023
baa6284
serialize/value: impl SerializeCql for fixed size numbers
piodul Nov 20, 2023
6076aa5
serialize/value: impl SerializeCql for string types
piodul Nov 19, 2023
ee9cb2a
serialize/value: impl SerializeCql for byte containers
piodul Nov 19, 2023
9768381
serialize/value: impl SerializeCql for uuid/timeuuid
piodul Nov 19, 2023
3214d1d
serialize/value: impl SerializeCql for inet
piodul Nov 19, 2023
6ffc2c2
serialize/value: impl SerializeCql for dynamically sized numbers
piodul Nov 20, 2023
575f0d6
serialize/value: impl SerializeCql for time-related types
piodul Nov 20, 2023
ceb37e2
serialize/value: impl SerializeCql for sequence types
piodul Nov 19, 2023
491772f
serialize/value: impl SerializeCql for map types
piodul Nov 19, 2023
13cb25b
serialize/value: impl SerializeCql for tuples
piodul Nov 19, 2023
deb6a81
frame/value: don't require trait on MaybeUnset parameter
piodul Nov 3, 2023
ed6bfa4
serialize/value: impl SerializeCql for wrapper types
piodul Nov 19, 2023
7b0f3fd
response/result: add helper `supports_special_empty_value`
piodul Nov 19, 2023
1952100
serialize/value: impl SerializeCql for CqlValue
piodul Nov 19, 2023
4db6a5c
frame/value_tests: check that UDTs sort fields
piodul Nov 20, 2023
3bc8972
frame/value_test: exercise both old and new row ser traits
piodul Nov 17, 2023
025b89e
serialize/row: make fallbacks for `SerializeRow` explicit
piodul Nov 3, 2023
e65102c
serialize/row: impl SerializeRow for unit types
piodul Nov 20, 2023
6f7b086
serialize/row: impl SerializeRow for dynamically sized rows
piodul Nov 20, 2023
7b91d79
serialize/row: impl SerializeRow for map types
piodul Nov 20, 2023
bb46597
serialize/row: impl SerializeRow for reference
piodul Nov 20, 2023
0932444
serialize/row: impl SerializeRow for tuples
piodul Nov 20, 2023
0642eb5
serialize: provide macro for quick trait impl from legacy
piodul Dec 4, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 25 additions & 0 deletions scylla-cql/src/frame/response/result.rs
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,31 @@ pub enum CqlValue {
Varint(BigInt),
}

impl ColumnType {
// Returns true if the type allows a special, empty value in addition to its
// natural representation. For example, bigint represents a 32-bit integer,
// but it can also hold a 0-bit empty value.
//
// It looks like Cassandra 4.1.3 rejects empty values for some more types than
// Scylla: date, time, smallint and tinyint. We will only check against
// Scylla's set of types supported for empty values as it's smaller;
// with Cassandra, some rejects will just have to be rejected on the db side.
pub(crate) fn supports_special_empty_value(&self) -> bool {
#[allow(clippy::match_like_matches_macro)]
match self {
ColumnType::Counter
| ColumnType::Duration
| ColumnType::List(_)
| ColumnType::Map(_, _)
| ColumnType::Set(_)
| ColumnType::UserDefinedType { .. }
| ColumnType::Custom(_) => false,

_ => true,
}
}
}

impl CqlValue {
pub fn as_ascii(&self) -> Option<&String> {
match self {
Expand Down
46 changes: 26 additions & 20 deletions scylla-cql/src/frame/value.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,10 @@ pub trait Value {
#[error("Value too big to be sent in a request - max 2GiB allowed")]
pub struct ValueTooBig;

#[derive(Debug, Error, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
#[error("Value is too large to fit in the CQL type")]
pub struct ValueOverflow;

/// Represents an unset value
pub struct Unset;

Expand All @@ -40,7 +44,7 @@ pub struct Counter(pub i64);

/// Enum providing a way to represent a value that might be unset
#[derive(Clone, Copy)]
pub enum MaybeUnset<V: Value> {
pub enum MaybeUnset<V> {
Unset,
Set(V),
}
Expand Down Expand Up @@ -78,7 +82,7 @@ impl From<NaiveDate> for CqlDate {

#[cfg(feature = "chrono")]
impl TryInto<NaiveDate> for CqlDate {
type Error = ValueTooBig;
type Error = ValueOverflow;

fn try_into(self) -> Result<NaiveDate, Self::Error> {
let days_since_unix_epoch = self.0 as i64 - (1 << 31);
Expand All @@ -90,7 +94,7 @@ impl TryInto<NaiveDate> for CqlDate {
NaiveDate::from_yo_opt(1970, 1)
.unwrap()
.checked_add_signed(duration_since_unix_epoch)
.ok_or(ValueTooBig)
.ok_or(ValueOverflow)
}
}

Expand All @@ -103,19 +107,19 @@ impl From<DateTime<Utc>> for CqlTimestamp {

#[cfg(feature = "chrono")]
impl TryInto<DateTime<Utc>> for CqlTimestamp {
type Error = ValueTooBig;
type Error = ValueOverflow;

fn try_into(self) -> Result<DateTime<Utc>, Self::Error> {
match Utc.timestamp_millis_opt(self.0) {
chrono::LocalResult::Single(datetime) => Ok(datetime),
_ => Err(ValueTooBig),
_ => Err(ValueOverflow),
}
}
}

#[cfg(feature = "chrono")]
impl TryFrom<NaiveTime> for CqlTime {
type Error = ValueTooBig;
type Error = ValueOverflow;

fn try_from(value: NaiveTime) -> Result<Self, Self::Error> {
let nanos = value
Expand All @@ -127,23 +131,23 @@ impl TryFrom<NaiveTime> for CqlTime {
if nanos <= 86399999999999 {
Ok(Self(nanos))
} else {
Err(ValueTooBig)
Err(ValueOverflow)
}
}
}

#[cfg(feature = "chrono")]
impl TryInto<NaiveTime> for CqlTime {
type Error = ValueTooBig;
type Error = ValueOverflow;

fn try_into(self) -> Result<NaiveTime, Self::Error> {
let secs = (self.0 / 1_000_000_000)
.try_into()
.map_err(|_| ValueTooBig)?;
.map_err(|_| ValueOverflow)?;
let nanos = (self.0 % 1_000_000_000)
.try_into()
.map_err(|_| ValueTooBig)?;
NaiveTime::from_num_seconds_from_midnight_opt(secs, nanos).ok_or(ValueTooBig)
.map_err(|_| ValueOverflow)?;
NaiveTime::from_num_seconds_from_midnight_opt(secs, nanos).ok_or(ValueOverflow)
}
}

Expand All @@ -167,17 +171,17 @@ impl From<time::Date> for CqlDate {

#[cfg(feature = "time")]
impl TryInto<time::Date> for CqlDate {
type Error = ValueTooBig;
type Error = ValueOverflow;

fn try_into(self) -> Result<time::Date, Self::Error> {
const JULIAN_DAY_OFFSET: i64 =
(1 << 31) - time::OffsetDateTime::UNIX_EPOCH.date().to_julian_day() as i64;

let julian_days = (self.0 as i64 - JULIAN_DAY_OFFSET)
.try_into()
.map_err(|_| ValueTooBig)?;
.map_err(|_| ValueOverflow)?;

time::Date::from_julian_day(julian_days).map_err(|_| ValueTooBig)
time::Date::from_julian_day(julian_days).map_err(|_| ValueOverflow)
}
}

Expand Down Expand Up @@ -209,11 +213,11 @@ impl From<time::OffsetDateTime> for CqlTimestamp {

#[cfg(feature = "time")]
impl TryInto<time::OffsetDateTime> for CqlTimestamp {
type Error = ValueTooBig;
type Error = ValueOverflow;

fn try_into(self) -> Result<time::OffsetDateTime, Self::Error> {
time::OffsetDateTime::from_unix_timestamp_nanos(self.0 as i128 * 1_000_000)
.map_err(|_| ValueTooBig)
.map_err(|_| ValueOverflow)
}
}

Expand All @@ -231,7 +235,7 @@ impl From<time::Time> for CqlTime {

#[cfg(feature = "time")]
impl TryInto<time::Time> for CqlTime {
type Error = ValueTooBig;
type Error = ValueOverflow;

fn try_into(self) -> Result<time::Time, Self::Error> {
let h = self.0 / 3_600_000_000_000;
Expand All @@ -240,12 +244,12 @@ impl TryInto<time::Time> for CqlTime {
let n = self.0 % 1_000_000_000;

time::Time::from_hms_nano(
h.try_into().map_err(|_| ValueTooBig)?,
h.try_into().map_err(|_| ValueOverflow)?,
m as u8,
s as u8,
n as u32,
)
.map_err(|_| ValueTooBig)
.map_err(|_| ValueOverflow)
}
}

Expand Down Expand Up @@ -631,7 +635,9 @@ impl Value for time::OffsetDateTime {
#[cfg(feature = "chrono")]
impl Value for NaiveTime {
fn serialize(&self, buf: &mut Vec<u8>) -> Result<(), ValueTooBig> {
CqlTime::try_from(*self)?.serialize(buf)
CqlTime::try_from(*self)
.map_err(|_| ValueTooBig)?
.serialize(buf)
}
}

Expand Down
Loading