Skip to content

Commit

Permalink
Merge pull request #11801 from lluki/platform_expr_proto_datetime
Browse files Browse the repository at this point in the history
  • Loading branch information
aalexandrov authored Apr 15, 2022
2 parents 0a6d7d7 + d02d3fa commit dcc7708
Show file tree
Hide file tree
Showing 10 changed files with 444 additions and 3 deletions.
2 changes: 2 additions & 0 deletions src/repr/build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,13 @@ fn main() {
prost_build::Config::new()
.compile_protos(
&[
"chrono.proto",
"row.proto",
"strconv.proto",
"relation_and_scalar.proto",
"adt/array.proto",
"adt/char.proto",
"adt/datetime.proto",
"adt/numeric.proto",
"adt/varchar.proto",
],
Expand Down
17 changes: 15 additions & 2 deletions src/repr/src/adt/datetime.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,13 +24,23 @@ use crate::adt::interval::Interval;

use mz_lowertest::MzReflect;

// The `Arbitrary` impls are only used during testing and we gate them
// behind `cfg(feature = "test-utils")`, so `proptest` can remain a dev-dependency.
// See https://github.com/MaterializeInc/materialize/pull/11717.
#[cfg(feature = "test-utils")]
use {
crate::chrono::any_fixed_offset, crate::chrono::any_timezone, proptest::prelude::*,
proptest_derive::Arbitrary,
};

/// Units of measurements associated with dates and times.
///
/// TODO(benesch): with enough thinking, this type could probably be merged with
/// `DateTimeField`.
#[derive(
Clone, Copy, Debug, PartialOrd, Ord, PartialEq, Eq, Hash, Serialize, Deserialize, MzReflect,
)]
#[cfg_attr(feature = "test-utils", derive(Arbitrary))]
pub enum DateTimeUnits {
Epoch,
Millennium,
Expand Down Expand Up @@ -326,10 +336,13 @@ impl DateTimeFieldValue {

/// Parsed timezone.
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, Serialize, Deserialize, MzReflect)]
#[cfg_attr(feature = "test-utils", derive(Arbitrary))]
pub enum Timezone {
#[serde(with = "fixed_offset_serde")]
FixedOffset(FixedOffset),
Tz(Tz),
FixedOffset(
#[cfg_attr(feature = "test-utils", proptest(strategy = "any_fixed_offset()"))] FixedOffset,
),
Tz(#[cfg_attr(feature = "test-utils", proptest(strategy = "any_timezone()"))] Tz),
}

// We need to implement Serialize and Deserialize traits to include Timezone in the UnaryFunc enum.
Expand Down
39 changes: 39 additions & 0 deletions src/repr/src/chrono.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
// Copyright Materialize, Inc. and contributors. All rights reserved.
//
// Use of this software is governed by the Business Source License
// included in the LICENSE file.
//
// As of the Change Date specified in that file, in accordance with
// the Business Source License, use of this software will be governed
// by the Apache License, Version 2.0.

#![cfg(feature = "test-utils")]

//! Custom [`proptest::strategy::Strategy`] implementations for the
//! [`chrono`] fields used inthe codebase.
//!
//! See the [`proptest`] docs[^1] for an example.
//!
//! [^1]: <https://altsysrq.github.io/proptest-book/proptest-derive/modifiers.html#strategy>
use chrono::{DateTime, Duration, FixedOffset, NaiveDateTime, Timelike, Utc};
use chrono_tz::{Tz, TZ_VARIANTS};
use proptest::prelude::Strategy;

pub fn any_naive_datetime() -> impl Strategy<Value = NaiveDateTime> {
use ::chrono::naive::{MAX_DATETIME, MIN_DATETIME};
(0..(MAX_DATETIME.nanosecond() - MIN_DATETIME.nanosecond()))
.prop_map(|x| MIN_DATETIME + Duration::nanoseconds(x as i64))
}

pub fn any_datetime() -> impl Strategy<Value = DateTime<Utc>> {
any_naive_datetime().prop_map(|x| DateTime::from_utc(x, Utc))
}

pub fn any_fixed_offset() -> impl Strategy<Value = FixedOffset> {
(-86_401..86_400).prop_map(FixedOffset::east)
}

pub fn any_timezone() -> impl Strategy<Value = Tz> {
(0..TZ_VARIANTS.len()).prop_map(|idx| *TZ_VARIANTS.get(idx).unwrap())
}
3 changes: 2 additions & 1 deletion src/repr/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,11 +28,12 @@ mod row;
mod scalar;

pub mod adt;
pub mod chrono;
pub mod proto;
pub mod strconv;
pub mod util;

pub use datum_vec::{DatumVec, DatumVecBorrow};
pub mod proto;
pub use relation::{ColumnName, ColumnType, NotNullViolation, RelationDesc, RelationType};
pub use row::{
datum_list_size, datum_size, datums_size, row_size, DatumList, DatumMap, Row, RowArena,
Expand Down
48 changes: 48 additions & 0 deletions src/repr/src/proto/adt/datetime.proto
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
// Copyright Materialize, Inc. and contributors. All rights reserved.
//
// Use of this software is governed by the Business Source License
// included in the LICENSE file.
//
// As of the Change Date specified in that file, in accordance with
// the Business Source License, use of this software will be governed
// by the Apache License, Version 2.0.

syntax = "proto3";

package adt.datetime;

import "chrono.proto";
import "google/protobuf/empty.proto";

message ProtoTimezone {
oneof kind {
chrono.ProtoFixedOffset fixed_offset = 1;
chrono.ProtoTz tz = 2;
}
}

message ProtoDateTimeUnits {
oneof kind {
google.protobuf.Empty epoch = 1;
google.protobuf.Empty millennium = 2;
google.protobuf.Empty century = 3;
google.protobuf.Empty decade = 4;
google.protobuf.Empty year = 5;
google.protobuf.Empty quarter = 6;
google.protobuf.Empty week = 7;
google.protobuf.Empty month = 8;
google.protobuf.Empty hour = 9;
google.protobuf.Empty day = 10;
google.protobuf.Empty day_of_week = 11;
google.protobuf.Empty day_of_year = 12;
google.protobuf.Empty iso_day_of_week = 13;
google.protobuf.Empty iso_day_of_year = 14;
google.protobuf.Empty minute = 15;
google.protobuf.Empty second = 16;
google.protobuf.Empty milliseconds = 17;
google.protobuf.Empty microseconds = 18;
google.protobuf.Empty timezone = 19;
google.protobuf.Empty timezone_hour = 20;
google.protobuf.Empty timezone_minute = 21;
}
}
126 changes: 126 additions & 0 deletions src/repr/src/proto/adt/datetime.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
// Copyright Materialize, Inc. and contributors. All rights reserved.
//
// Use of this software is governed by the Business Source License
// included in the LICENSE file.
//
// As of the Change Date specified in that file, in accordance with
// the Business Source License, use of this software will be governed
// by the Apache License, Version 2.0.

//! Protobuf structs mirroring [`crate::adt::datetime`].
include!(concat!(env!("OUT_DIR"), "/adt.datetime.rs"));

use super::super::{ProtoRepr, TryFromProtoError};
use crate::adt::datetime::Timezone::*;
use crate::adt::datetime::{DateTimeUnits, Timezone};
use chrono::FixedOffset;
use chrono_tz::Tz;

impl From<&Timezone> for ProtoTimezone {
fn from(value: &Timezone) -> Self {
use proto_timezone::Kind;
ProtoTimezone {
kind: Some(match value {
FixedOffset(fo) => Kind::FixedOffset(fo.into_proto()),
Tz(tz) => Kind::Tz(tz.into_proto()),
}),
}
}
}

impl TryFrom<ProtoTimezone> for Timezone {
type Error = TryFromProtoError;

fn try_from(value: ProtoTimezone) -> Result<Self, Self::Error> {
use proto_timezone::Kind;
let kind = value
.kind
.ok_or_else(|| TryFromProtoError::MissingField("ProtoTimezone::kind".into()))?;
Ok(match kind {
Kind::FixedOffset(pof) => Timezone::FixedOffset(FixedOffset::from_proto(pof)?),
Kind::Tz(ptz) => Timezone::Tz(Tz::from_proto(ptz)?),
})
}
}

impl From<&DateTimeUnits> for ProtoDateTimeUnits {
fn from(value: &DateTimeUnits) -> Self {
use proto_date_time_units::Kind;
ProtoDateTimeUnits {
kind: Some(match value {
DateTimeUnits::Epoch => Kind::Epoch(()),
DateTimeUnits::Millennium => Kind::Millennium(()),
DateTimeUnits::Century => Kind::Century(()),
DateTimeUnits::Decade => Kind::Decade(()),
DateTimeUnits::Year => Kind::Year(()),
DateTimeUnits::Quarter => Kind::Quarter(()),
DateTimeUnits::Week => Kind::Week(()),
DateTimeUnits::Month => Kind::Month(()),
DateTimeUnits::Hour => Kind::Hour(()),
DateTimeUnits::Day => Kind::Day(()),
DateTimeUnits::DayOfWeek => Kind::DayOfWeek(()),
DateTimeUnits::DayOfYear => Kind::DayOfYear(()),
DateTimeUnits::IsoDayOfWeek => Kind::IsoDayOfWeek(()),
DateTimeUnits::IsoDayOfYear => Kind::IsoDayOfYear(()),
DateTimeUnits::Minute => Kind::Minute(()),
DateTimeUnits::Second => Kind::Second(()),
DateTimeUnits::Milliseconds => Kind::Milliseconds(()),
DateTimeUnits::Microseconds => Kind::Microseconds(()),
DateTimeUnits::Timezone => Kind::Timezone(()),
DateTimeUnits::TimezoneHour => Kind::TimezoneHour(()),
DateTimeUnits::TimezoneMinute => Kind::TimezoneMinute(()),
}),
}
}
}

impl TryFrom<ProtoDateTimeUnits> for DateTimeUnits {
type Error = TryFromProtoError;

fn try_from(value: ProtoDateTimeUnits) -> Result<Self, Self::Error> {
use proto_date_time_units::Kind;
let kind = value
.kind
.ok_or_else(|| TryFromProtoError::MissingField("ProtoDateTimeUnits.kind".into()))?;
Ok(match kind {
Kind::Epoch(_) => DateTimeUnits::Epoch,
Kind::Millennium(_) => DateTimeUnits::Millennium,
Kind::Century(_) => DateTimeUnits::Century,
Kind::Decade(_) => DateTimeUnits::Decade,
Kind::Year(_) => DateTimeUnits::Year,
Kind::Quarter(_) => DateTimeUnits::Quarter,
Kind::Week(_) => DateTimeUnits::Week,
Kind::Month(_) => DateTimeUnits::Month,
Kind::Hour(_) => DateTimeUnits::Hour,
Kind::Day(_) => DateTimeUnits::Day,
Kind::DayOfWeek(_) => DateTimeUnits::DayOfWeek,
Kind::DayOfYear(_) => DateTimeUnits::DayOfYear,
Kind::IsoDayOfWeek(_) => DateTimeUnits::IsoDayOfWeek,
Kind::IsoDayOfYear(_) => DateTimeUnits::IsoDayOfYear,
Kind::Minute(_) => DateTimeUnits::Minute,
Kind::Second(_) => DateTimeUnits::Second,
Kind::Milliseconds(_) => DateTimeUnits::Milliseconds,
Kind::Microseconds(_) => DateTimeUnits::Microseconds,
Kind::Timezone(_) => DateTimeUnits::Timezone,
Kind::TimezoneHour(_) => DateTimeUnits::TimezoneHour,
Kind::TimezoneMinute(_) => DateTimeUnits::TimezoneMinute,
})
}
}

#[cfg(test)]
mod tests {
use super::*;
use crate::proto::protobuf_roundtrip;
use proptest::prelude::*;

proptest! {
#[test]
fn datetimeunits_serialization_roundtrip(expect in any::<DateTimeUnits>() ) {
let actual = protobuf_roundtrip::<_, ProtoDateTimeUnits>(&expect);
assert!(actual.is_ok());
assert_eq!(actual.unwrap(), expect);
}
}
}
1 change: 1 addition & 0 deletions src/repr/src/proto/adt/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,5 +11,6 @@
pub mod array;
pub mod char;
pub mod datetime;
pub mod numeric;
pub mod varchar;
35 changes: 35 additions & 0 deletions src/repr/src/proto/chrono.proto
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
// Copyright Materialize, Inc. and contributors. All rights reserved.
//
// Use of this software is governed by the Business Source License
// included in the LICENSE file.
//
// As of the Change Date specified in that file, in accordance with
// the Business Source License, use of this software will be governed
// by the Apache License, Version 2.0.

syntax = "proto3";

package chrono;

message ProtoTz {
string name = 1;
}

message ProtoNaiveDate {
int32 year = 1;
uint32 ordinal = 2;
}

message ProtoNaiveTime {
uint32 secs = 1;
uint32 frac = 2;
}

message ProtoNaiveDateTime {
ProtoNaiveDate date = 1;
ProtoNaiveTime time = 2;
}

message ProtoFixedOffset {
int32 local_minus_utc = 1;
}
Loading

0 comments on commit dcc7708

Please sign in to comment.