Skip to content

Commit

Permalink
update comment
Browse files Browse the repository at this point in the history
fixing docs

Updated round to precision and added unit tests

not using disallowed method

making RangeContainsElem check same for both Timestamp and TimestampTz

Updated test, added 0 precision

fix fmt

fix sqllogictests

Fixing error message

more tests

fix tests?

fix test

adding to scalar type enumerate and interesting datums

test

moar tests!!

test!!!!

and moar

added new tests as per review comments

adding more test cases

Keeping CastDateToString
  • Loading branch information
Mouli Mukherjee committed Sep 7, 2023
1 parent 9e85ce0 commit e1a64b1
Show file tree
Hide file tree
Showing 17 changed files with 301 additions and 85 deletions.
5 changes: 2 additions & 3 deletions misc/dbt-materialize/tests/adapter/test_basic.py
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,6 @@ class TestSimpleMaterializationsMaterialize(BaseSimpleMaterializations):
# Custom base test that removes the incremental portion and overrides the expected relations

def test_base(self, project):

# seed command
results = run_dbt(["seed"])
# seed result length
Expand Down Expand Up @@ -170,7 +169,7 @@ def expected_catalog(self, project, profile_user):
role="materialize",
id_type="integer",
text_type="text",
time_type="timestamp",
time_type="timestamp without time zone",
view_type="view",
table_type="materializedview",
model_stats=no_stats(),
Expand All @@ -185,7 +184,7 @@ def expected_catalog(self, project, profile_user):
role="materialize",
id_type="integer",
text_type="text",
time_type="timestamp",
time_type="timestamp without time zone",
bigint_type="bigint",
view_type="view",
table_type="materializedview",
Expand Down
2 changes: 1 addition & 1 deletion misc/dbt-materialize/tests/adapter/test_seed.py
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@
- name: a_date
tests:
- column_type:
type: timestamp
type: timestamp without time zone
- name: looks_like_a_date
tests:
- column_type:
Expand Down
4 changes: 2 additions & 2 deletions misc/dbt-materialize/tests/adapter/test_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -72,8 +72,8 @@ class TestCurrentTimestamps(BaseCurrentTimestamps):
def expected_schema(self):
return {
"current_timestamp": "timestamp with time zone",
"current_timestamp_in_utc_backcompat": "timestamp",
"current_timestamp_backcompat": "timestamp",
"current_timestamp_in_utc_backcompat": "timestamp without time zone",
"current_timestamp_backcompat": "timestamp without time zone",
}

pass
Expand Down
2 changes: 1 addition & 1 deletion src/expr-test-util/tests/testdata/tospec
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,6 @@ cat
ok

rel-to-test
(Reduce (Filter (Join [(get u6) (get u16) (get u19) (get u26) (get u34) (get u31) (get u37)] [] Unimplemented) [(CallBinary Eq #0 #25) (CallBinary Eq #1 #23) (CallBinary Eq #2 #24) (CallBinary Eq #21 (CallUnary CastInt16ToInt32 #61)) (CallBinary Eq #22 #30) (CallBinary Eq #23 #31) (CallBinary Eq #24 #32) (CallBinary Eq #32 #41) (CallBinary Eq #34 #40) (CallBinary Eq #57 (CallUnary CastInt16ToInt32 #58)) (CallBinary Eq #61 #65) (CallBinary Eq #67 #69) (CallBinary Eq #70 ("EUROPE" String)) (CallBinary Gte (CallUnary CastDateToTimestamp #26) ("2007-01-02 00:00:00" Timestamp))]) [#66] [(SumNumeric #38 false)] false null)
(Reduce (Filter (Join [(get u6) (get u16) (get u19) (get u26) (get u34) (get u31) (get u37)] [] Unimplemented) [(CallBinary Eq #0 #25) (CallBinary Eq #1 #23) (CallBinary Eq #2 #24) (CallBinary Eq #21 (CallUnary CastInt16ToInt32 #61)) (CallBinary Eq #22 #30) (CallBinary Eq #23 #31) (CallBinary Eq #24 #32) (CallBinary Eq #32 #41) (CallBinary Eq #34 #40) (CallBinary Eq #57 (CallUnary CastInt16ToInt32 #58)) (CallBinary Eq #61 #65) (CallBinary Eq #67 #69) (CallBinary Eq #70 ("EUROPE" String)) (CallBinary Gte (CallUnary (CastDateToString NULL) #26) ("2007-01-02 00:00:00" Timestamp))]) [#66] [(SumNumeric #38 false)] false null)
----
ok
2 changes: 1 addition & 1 deletion src/expr/src/scalar/func.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2557,7 +2557,7 @@ impl BinaryFunc {
ScalarType::Timestamp { .. } => {
contains_range_elem::<CheckedTimestamp<NaiveDateTime>>(a, b)
}
ScalarType::TimestampTz { precision: None } => {
ScalarType::TimestampTz { .. } => {
contains_range_elem::<CheckedTimestamp<DateTime<Utc>>>(a, b)
}
_ => unreachable!(),
Expand Down
2 changes: 1 addition & 1 deletion src/expr/src/scalar/func/impls/timestamp.rs
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@ impl<'a> EagerUnaryFunc<'a> for CastTimestampToTimestampTz {

fn preserves_uniqueness(&self) -> bool {
// Only preserves uniqueness if the output type is as precise as the
// input type, but we're not provided in the input type here.
// input type, but we're not provided the input type here.
false
}

Expand Down
112 changes: 101 additions & 11 deletions src/repr/src/adt/timestamp.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,6 @@ use std::time::Duration as StdDuration;
use ::chrono::{
DateTime, Datelike, Days, Duration, Months, NaiveDate, NaiveDateTime, NaiveTime, Utc,
};
use chrono::DurationRound;
use mz_lowertest::MzReflect;
use mz_ore::cast::{self, CastFrom};
use mz_proto::{ProtoType, RustType, TryFromProtoError};
Expand Down Expand Up @@ -126,14 +125,16 @@ impl RustType<ProtoOptionalTimestampPrecision> for Option<TimestampPrecision> {

/// The error returned when constructing a [`VarCharMaxLength`] from an invalid
/// value.
///
/// [`VarCharMaxLength`]: crate::adt::varchar::VarCharMaxLength
#[derive(Debug, Clone)]
pub struct InvalidTimestampPrecisionError;

impl fmt::Display for InvalidTimestampPrecisionError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(
f,
"precision for type timestamp or timestamptz must be between 1 and {}",
"precision for type timestamp or timestamptz must be between 0 and {}",
MAX_PRECISION
)
}
Expand Down Expand Up @@ -799,17 +800,40 @@ impl<T: TimestampLike> CheckedTimestamp<T> {
/// Rounds the timestamp to the specified number of digits of precision.
pub fn round_to_precision(&mut self, precision: Option<TimestampPrecision>) {
if let Some(precision) = precision {
let power = 9_u32
// maximum precision is micros
let power = 6_u32
.checked_sub(precision.into_u8().into())
.expect("precision fits in nanos");
let round_to = Duration::from_std(StdDuration::from_nanos(10_u64.pow(power)))
.expect("duration fits in chrono duration");
let sec = Duration::from_std(StdDuration::from_secs(1))
.expect("precision fits in micros");
let round_to = Duration::from_std(StdDuration::from_micros(10_u64.pow(power)))
.expect("duration fits in chrono duration");
let dt = self.date_time();
let dt = dt + sec;
let dt = dt.duration_round(round_to).expect("rounding is valid");
let dt = dt - sec;
let original = self.date_time();

// this is copied from [`chrono::round::duration_round`]
// but using microseconds instead of nanoseconds precision
let new_dt = round_to.num_microseconds().map(|span| {
let stamp = original.timestamp_micros();

if span == 0 {
return original;
}
let delta_down = stamp % span;
if delta_down == 0 {
original
} else {
let (delta_up, delta_down) = if delta_down < 0 {
(delta_down.abs(), span - delta_down.abs())
} else {
(span - delta_down, delta_down)
};
if delta_up <= delta_down {
original + Duration::microseconds(delta_up)
} else {
original - Duration::microseconds(delta_down)
}
}
});

let dt = new_dt.expect("rounding is valid in micros");
self.t = T::from_date_time(dt)
}
}
Expand Down Expand Up @@ -968,6 +992,72 @@ mod test {
assert_eq!(result, Interval::new(-months, 0, 0));
}

fn assert_round_to_precision(
mut dt: CheckedTimestamp<NaiveDateTime>,
precision: u8,
expected: i64,
) {
dt.round_to_precision(Some(TimestampPrecision(precision)));
assert_eq!(expected, dt.timestamp_micros());
}

#[mz_ore::test]
fn test_round_to_precision() {
let date = CheckedTimestamp::try_from(
NaiveDate::from_ymd_opt(1970, 1, 1)
.unwrap()
.and_hms_nano_opt(0, 0, 0, 123456789)
.unwrap(),
)
.unwrap();
assert_round_to_precision(date, 0, 0);
assert_round_to_precision(date, 1, 100000);
assert_round_to_precision(date, 2, 120000);
assert_round_to_precision(date, 3, 123000);
assert_round_to_precision(date, 4, 123500);
assert_round_to_precision(date, 5, 123460);
assert_round_to_precision(date, 6, 123456);

let low =
CheckedTimestamp::try_from(LOW_DATE.and_hms_nano_opt(0, 0, 0, 123456789).unwrap())
.unwrap();
assert_round_to_precision(low, 0, -210863606400000000);
assert_round_to_precision(low, 1, -210863606399900000);
assert_round_to_precision(low, 2, -210863606399880000);
assert_round_to_precision(low, 3, -210863606399877000);
assert_round_to_precision(low, 4, -210863606399876500);
assert_round_to_precision(low, 5, -210863606399876540);
assert_round_to_precision(low, 6, -210863606399876544);

let high =
CheckedTimestamp::try_from(HIGH_DATE.and_hms_nano_opt(0, 0, 0, 123456789).unwrap())
.unwrap();
assert_round_to_precision(high, 0, 8210298326400000000);
assert_round_to_precision(high, 1, 8210298326400100000);
assert_round_to_precision(high, 2, 8210298326400120000);
assert_round_to_precision(high, 3, 8210298326400123000);
assert_round_to_precision(high, 4, 8210298326400123500);
assert_round_to_precision(high, 5, 8210298326400123460);
assert_round_to_precision(high, 6, 8210298326400123456);
}

#[mz_ore::test]
fn test_precision_edge_cases() {
let result = mz_ore::panic::catch_unwind(|| {
let mut date =
CheckedTimestamp::try_from(NaiveDateTime::from_timestamp_micros(123456).unwrap())
.unwrap();
date.round_to_precision(Some(TimestampPrecision(7)));
});
assert!(result.is_err());

let mut date =
CheckedTimestamp::try_from(NaiveDateTime::from_timestamp_micros(123456).unwrap())
.unwrap();
date.round_to_precision(None);
assert_eq!(123456, date.timestamp_micros());
}

proptest! {
#[mz_ore::test]
#[cfg_attr(miri, ignore)] // slow
Expand Down
24 changes: 24 additions & 0 deletions src/repr/src/scalar.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2933,6 +2933,13 @@ impl<'a> ScalarType {
.try_into()
.unwrap(),
),
// nano seconds
Datum::Timestamp(
NaiveDateTime::from_timestamp_opt(0, 123456789)
.unwrap()
.try_into()
.unwrap(),
),
// Leap second
Datum::Timestamp(
CheckedTimestamp::from_timestamplike(
Expand Down Expand Up @@ -2972,6 +2979,15 @@ impl<'a> ScalarType {
.try_into()
.unwrap(),
),
// nano seconds
Datum::TimestampTz(
DateTime::from_utc(
NaiveDateTime::from_timestamp_opt(0, 123456789).unwrap(),
Utc,
)
.try_into()
.unwrap(),
),
])
});
static INTERVAL: Lazy<Row> = Lazy::new(|| {
Expand Down Expand Up @@ -3160,9 +3176,17 @@ impl<'a> ScalarType {
ScalarType::Timestamp {
precision: Some(TimestampPrecision(crate::adt::timestamp::MAX_PRECISION)),
},
ScalarType::Timestamp {
precision: Some(TimestampPrecision(0)),
},
ScalarType::Timestamp { precision: None },
ScalarType::TimestampTz {
precision: Some(TimestampPrecision(crate::adt::timestamp::MAX_PRECISION)),
},
ScalarType::TimestampTz {
precision: Some(TimestampPrecision(0)),
},
ScalarType::TimestampTz { precision: None },
ScalarType::Interval,
ScalarType::PgLegacyChar,
ScalarType::Bytes,
Expand Down
2 changes: 1 addition & 1 deletion test/debezium/sql-server/40-check-types.td
Original file line number Diff line number Diff line change
Expand Up @@ -33,4 +33,4 @@ $ schema-registry-wait subject=sql-server.dbo.types_table-value


> SELECT pg_typeof(decimal_col), pg_typeof(datetimeoffset_col), pg_typeof(datetime2_col), pg_typeof(smalldatetime_col), pg_typeof(bit_col) FROM types_table;
numeric text bigint timestamp boolean
numeric text bigint "timestamp without time zone" boolean
2 changes: 1 addition & 1 deletion test/pg-cdc/types-array.td
Original file line number Diff line number Diff line change
Expand Up @@ -261,7 +261,7 @@ time[]

# read timestamp
> SELECT pg_typeof(c) FROM t_timestamp_array LIMIT 1;
"timestamp with time zone[]"
"timestamp without time zone[]"

> SELECT * FROM t_timestamp_array;
"{2007-02-01 15:04:05,2007-02-01 15:04:06}"
Expand Down
Loading

0 comments on commit e1a64b1

Please sign in to comment.