From b8c9c236ac5e18f794f495c9d114042de735c389 Mon Sep 17 00:00:00 2001 From: Xinhao Xu <84456268+xxhZs@users.noreply.github.com> Date: Mon, 30 Dec 2024 17:52:45 +0800 Subject: [PATCH] feat(types): support ns timestamp (#19827) Co-authored-by: congyi wang <58715567+wcy-fdu@users.noreply.github.com> --- Cargo.lock | 26 +++ e2e_test/batch/types/timestamp_ns.slt.part | 202 ++++++++++++++++++ src/common/Cargo.toml | 1 + src/common/src/types/datetime.rs | 114 +++++++--- src/expr/impl/src/scalar/extract.rs | 50 +++-- src/expr/impl/src/scalar/make_time.rs | 4 +- src/expr/impl/src/scalar/to_char.rs | 2 + .../compaction_group/hummock_version_ext.rs | 7 +- src/tests/regress/data/expected/time.out | 24 +-- src/tests/regress/data/sql/time.sql | 5 +- 10 files changed, 349 insertions(+), 86 deletions(-) create mode 100644 e2e_test/batch/types/timestamp_ns.slt.part diff --git a/Cargo.lock b/Cargo.lock index 38f0bddbb20bc..992103088be72 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -6684,6 +6684,31 @@ dependencies = [ "lazy_static", ] +[[package]] +name = "jiff" +version = "0.1.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db69f08d4fb10524cacdb074c10b296299d71274ddbc830a8ee65666867002e9" +dependencies = [ + "jiff-tzdb-platform", + "windows-sys 0.59.0", +] + +[[package]] +name = "jiff-tzdb" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91335e575850c5c4c673b9bd467b0e025f164ca59d0564f69d0c2ee0ffad4653" + +[[package]] +name = "jiff-tzdb-platform" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9835f0060a626fe59f160437bc725491a6af23133ea906500027d1bd2f8f4329" +dependencies = [ + "jiff-tzdb", +] + [[package]] name = "jni" version = "0.21.1" @@ -10674,6 +10699,7 @@ dependencies = [ "humantime", "itertools 0.13.0", "itoa", + "jiff", "jsonbb", "libc", "lru 0.7.6", diff --git a/e2e_test/batch/types/timestamp_ns.slt.part b/e2e_test/batch/types/timestamp_ns.slt.part new file mode 100644 index 0000000000000..6eb727f678380 --- /dev/null +++ b/e2e_test/batch/types/timestamp_ns.slt.part @@ -0,0 +1,202 @@ +statement ok +SET RW_IMPLICIT_FLUSH TO true; + +statement ok +create table t1(v1 int, v2 timestamp); + +statement ok +insert into t1 values(1,'2013-01-01 01:01:01.123456789'),(2,'2012-01-01 01:01:01.123456'),(3,'0000-01-01 01:01:01.123456789'),(4,'2213-01-01 01:01:01.123456789'),(5,null),(6,'2013-01-01 01:01:01.123456789'); + +query T rowsort +select * from t1; +---- +1 2013-01-01 01:01:01.123456789 +2 2012-01-01 01:01:01.123456 +3 0001-01-01 01:01:01.123456789 BC +4 2213-01-01 01:01:01.123456789 +5 NULL +6 2013-01-01 01:01:01.123456789 + +query T +select * from t1 where v2 is null; +---- +5 NULL + +query T rowsort +select v1, v2, +case + when extract(year from v2) < 2000 then 'Before 2000' + when extract(year from v2) >= 2000 and extract(year from v2) < 2100 then '21st Century' + else 'Future' +end as time_period +from t1; +---- +1 2013-01-01 01:01:01.123456789 21st Century +2 2012-01-01 01:01:01.123456 21st Century +3 0001-01-01 01:01:01.123456789 BC Before 2000 +4 2213-01-01 01:01:01.123456789 Future +5 NULL Future +6 2013-01-01 01:01:01.123456789 21st Century + +query T rowsort +select v1, v2, coalesce(v2, '1900-01-01 00:00:00') as coalesce_v2 from t1; +---- +1 2013-01-01 01:01:01.123456789 2013-01-01 01:01:01.123456789 +2 2012-01-01 01:01:01.123456 2012-01-01 01:01:01.123456 +3 0001-01-01 01:01:01.123456789 BC 0001-01-01 01:01:01.123456789 BC +4 2213-01-01 01:01:01.123456789 2213-01-01 01:01:01.123456789 +5 NULL 1900-01-01 00:00:00 +6 2013-01-01 01:01:01.123456789 2013-01-01 01:01:01.123456789 + +query T +select count(v2) as total_rows from t1; +---- +5 + +query T rowsort +select * from t1 where v2 >= '2012-01-01 01:01:01.123456'; +---- +1 2013-01-01 01:01:01.123456789 +2 2012-01-01 01:01:01.123456 +4 2213-01-01 01:01:01.123456789 +6 2013-01-01 01:01:01.123456789 + +query T rowsort +select v1, cast(v2 as date) as date_v2, cast(v2 as timestamp with time zone) as timestamptz_v2 from t1; +---- +1 2013-01-01 2013-01-01 01:01:01.123456+00:00 +2 2012-01-01 2012-01-01 01:01:01.123456+00:00 +3 0001-01-01 BC 0001-01-01 01:01:01.123456+00:00 BC +4 2213-01-01 2213-01-01 01:01:01.123456+00:00 +5 NULL NULL +6 2013-01-01 2013-01-01 01:01:01.123456+00:00 + +query T rowsort +select v1, date_trunc('day', v2) AS truncated_v2 from t1; +---- +1 2013-01-01 00:00:00 +2 2012-01-01 00:00:00 +3 0001-01-01 00:00:00 BC +4 2213-01-01 00:00:00 +5 NULL +6 2013-01-01 00:00:00 + +query T rowsort +select v1, v2 at time zone 'UTC' as v2_utc from t1; +---- +1 2013-01-01 01:01:01.123456+00:00 +2 2012-01-01 01:01:01.123456+00:00 +3 0001-01-01 01:01:01.123456+00:00 BC +4 2213-01-01 01:01:01.123456+00:00 +5 NULL +6 2013-01-01 01:01:01.123456+00:00 + +query T rowsort +select v1, to_char(v2, 'YYYY-MM-DD HH24:MI:SS.NS') as formatted_v2 from t1; +---- +1 2013-01-01 01:01:01.123456789 +2 2012-01-01 01:01:01.123456000 +3 0000-01-01 01:01:01.123456789 +4 2213-01-01 01:01:01.123456789 +5 NULL +6 2013-01-01 01:01:01.123456789 + +query T rowsort +select generate_series('2013-01-01 01:01:01.123456789'::timestamp,'2013-01-01 01:01:05.123456790'::timestamp, '1 s'); +---- +2013-01-01 01:01:01.123456789 +2013-01-01 01:01:02.123456789 +2013-01-01 01:01:03.123456789 +2013-01-01 01:01:04.123456789 +2013-01-01 01:01:05.123456789 + +query T rowsort +select DISTINCT v2 FROM t1; +---- +0001-01-01 01:01:01.123456789 BC +2012-01-01 01:01:01.123456 +2013-01-01 01:01:01.123456789 +2213-01-01 01:01:01.123456789 +NULL + +query T rowsort +select v2, count(*) from t1 group by v2; +---- +0001-01-01 01:01:01.123456789 BC 1 +2012-01-01 01:01:01.123456 1 +2013-01-01 01:01:01.123456789 2 +2213-01-01 01:01:01.123456789 1 +NULL 1 + +query T +select * from t1 order by v2 desc , v1 desc; +---- +5 NULL +4 2213-01-01 01:01:01.123456789 +6 2013-01-01 01:01:01.123456789 +1 2013-01-01 01:01:01.123456789 +2 2012-01-01 01:01:01.123456 +3 0001-01-01 01:01:01.123456789 BC + +query T rowsort +select max(v2) from t1; +---- +2213-01-01 01:01:01.123456789 + +query T rowsort +select v1, extract(epoch from v2) from t1; +---- +1 1357002061.123456789 +2 1325379661.123456000 +3 -62167215538.876544 +4 7668349261.123456789 +5 NULL +6 1357002061.123456789 + +query T rowsort +select v1, extract(second from v2) from t1; +---- +1 1.123456789 +2 1.123456000 +3 1.123456789 +4 1.123456789 +5 NULL +6 1.123456789 + +query T rowsort +select v1, extract(millisecond from v2) from t1; +---- +1 1123.456789 +2 1123.456000 +3 1123.456789 +4 1123.456789 +5 NULL +6 1123.456789 + +query T rowsort +select v1, extract(microsecond from v2) from t1; +---- +1 1123456.789 +2 1123456.000 +3 1123456.789 +4 1123456.789 +5 NULL +6 1123456.789 + +query T rowsort +select v1, extract(nanosecond from v2) from t1; +---- +1 1123456789 +2 1123456000 +3 1123456789 +4 1123456789 +5 NULL +6 1123456789 + +query T rowsort +select make_timestamp(2013, 01, 01, 01, 01, 1.123456789); +---- +2013-01-01 01:01:01.123456789 + +statement ok +drop table t1; \ No newline at end of file diff --git a/src/common/Cargo.toml b/src/common/Cargo.toml index 1fbf8e312185d..4e4a012f79fc0 100644 --- a/src/common/Cargo.toml +++ b/src/common/Cargo.toml @@ -49,6 +49,7 @@ http = "1" humantime = "2.1" itertools = { workspace = true } itoa = "1.0" +jiff = "0.1.15" jsonbb = { workspace = true } lru = { workspace = true } memcomparable = { version = "0.2", features = ["decimal"] } diff --git a/src/common/src/types/datetime.rs b/src/common/src/types/datetime.rs index 1b8d004c68b2b..dc148a3100fca 100644 --- a/src/common/src/types/datetime.rs +++ b/src/common/src/types/datetime.rs @@ -168,29 +168,18 @@ impl FromStr for Timestamp { type Err = InvalidParamsError; fn from_str(s: &str) -> Result { - if let Ok(res) = speedate::DateTime::parse_str_rfc3339(s) { - if res.time.tz_offset.is_some() { - return Err(ErrorKind::ParseTimestamp.into()); - } - Ok(Date::from_ymd_uncheck( - res.date.year as i32, - res.date.month as u32, - res.date.day as u32, - ) - .and_hms_micro_uncheck( - res.time.hour as u32, - res.time.minute as u32, - res.time.second as u32, - res.time.microsecond, - )) - } else { - let res = - speedate::Date::parse_str_rfc3339(s).map_err(|_| ErrorKind::ParseTimestamp)?; - Ok( - Date::from_ymd_uncheck(res.year as i32, res.month as u32, res.day as u32) - .and_hms_micro_uncheck(0, 0, 0, 0), - ) - } + let dt = s + .parse::() + .map_err(|_| ErrorKind::ParseTimestamp)?; + Ok( + Date::from_ymd_uncheck(dt.year() as i32, dt.month() as u32, dt.day() as u32) + .and_hms_nano_uncheck( + dt.hour() as u32, + dt.minute() as u32, + dt.second() as u32, + dt.subsec_nanosecond() as u32, + ), + ) } } @@ -267,7 +256,7 @@ enum ErrorKind { ParseDate, #[error("Can't cast string to time (expected format is HH:MM:SS[.D+{{up to 6 digits}}][Z] or HH:MM)")] ParseTime, - #[error("Can't cast string to timestamp (expected format is YYYY-MM-DD HH:MM:SS[.D+{{up to 6 digits}}] or YYYY-MM-DD HH:MM or YYYY-MM-DD or ISO 8601 format)")] + #[error("Can't cast string to timestamp (expected format is YYYY-MM-DD HH:MM:SS[.D+{{up to 9 digits}}] or YYYY-MM-DD HH:MM or YYYY-MM-DD or ISO 8601 format)")] ParseTimestamp, } @@ -422,6 +411,13 @@ impl Date { .and_time(Time::from_hms_micro_uncheck(hour, min, sec, micro).0), ) } + + pub fn and_hms_nano_uncheck(self, hour: u32, min: u32, sec: u32, nano: u32) -> Timestamp { + Timestamp::new( + self.0 + .and_time(Time::from_hms_nano_uncheck(hour, min, sec, nano).0), + ) + } } impl Time { @@ -485,6 +481,42 @@ impl Time { } } +// The first 64 bits of protobuf encoding for `Timestamp` type has 2 possible meanings. +// * When the highest 2 bits are `11` or `00` (i.e. values ranging from `0b1100...00` to `0b0011..11`), +// it is *microseconds* since 1970-01-01 midnight. 2^62 microseconds covers 146235 years. +// * When the highest 2 bits are `10` or `01`, we flip the second bit to get values from `0b1100...00` to `0b0011..11` again. +// It is *seconds* since 1970-01-01 midnight. It is then followed by another 32 bits as nanoseconds within a second. +// Since timestamp is negative when it is less than 1970-1-1, you need to take both cases into account(`11+00`` or `01+10``). +enum FirstI64 { + V0 { usecs: i64 }, + V1 { secs: i64 }, +} +impl FirstI64 { + pub fn to_protobuf(&self) -> i64 { + match self { + FirstI64::V0 { usecs } => *usecs, + FirstI64::V1 { secs } => secs ^ (0b01 << 62), + } + } + + pub fn from_protobuf(cur: &mut Cursor<&[u8]>) -> ArrayResult { + let value = cur + .read_i64::() + .context("failed to read i64 from Time buffer")?; + if Self::is_v1_format_state(value) { + let secs = value ^ (0b01 << 62); + Ok(FirstI64::V1 { secs }) + } else { + Ok(FirstI64::V0 { usecs: value }) + } + } + + fn is_v1_format_state(value: i64) -> bool { + let state = (value >> 62) & 0b11; + state == 0b10 || state == 0b01 + } +} + impl Timestamp { pub fn with_secs_nsecs(secs: i64, nsecs: u32) -> Result { Ok(Timestamp::new({ @@ -495,18 +527,34 @@ impl Timestamp { } pub fn from_protobuf(cur: &mut Cursor<&[u8]>) -> ArrayResult { - let micros = cur - .read_i64::() - .context("failed to read i64 from Timestamp buffer")?; - - Ok(Timestamp::with_micros(micros)?) + match FirstI64::from_protobuf(cur)? { + FirstI64::V0 { usecs } => Ok(Timestamp::with_micros(usecs)?), + FirstI64::V1 { secs } => { + let nsecs = cur + .read_u32::() + .context("failed to read u32 from Time buffer")?; + Ok(Timestamp::with_secs_nsecs(secs, nsecs)?) + } + } } - /// Although `Timestamp` takes 12 bytes, we drop 4 bytes in protobuf encoding. + // Since timestamp secs is much smaller than i64, we use the highest 2 bit to store the format information, which is compatible with the old format. + // New format: secs(i64) + nsecs(u32) + // Old format: micros(i64) pub fn to_protobuf(self, output: &mut T) -> ArrayResult { - output - .write(&(self.0.and_utc().timestamp_micros()).to_be_bytes()) - .map_err(Into::into) + let timestamp_size = output + .write( + &(FirstI64::V1 { + secs: self.0.and_utc().timestamp(), + } + .to_protobuf()) + .to_be_bytes(), + ) + .map_err(Into::::into)?; + let timestamp_subsec_nanos_size = output + .write(&(self.0.and_utc().timestamp_subsec_nanos()).to_be_bytes()) + .map_err(Into::::into)?; + Ok(timestamp_subsec_nanos_size + timestamp_size) } pub fn get_timestamp_nanos(&self) -> i64 { diff --git a/src/expr/impl/src/scalar/extract.rs b/src/expr/impl/src/scalar/extract.rs index 74a50cf6869ec..cf63d5ac25e5c 100644 --- a/src/expr/impl/src/scalar/extract.rs +++ b/src/expr/impl/src/scalar/extract.rs @@ -42,17 +42,18 @@ fn extract_from_datelike(date: impl Datelike, unit: Unit) -> Decimal { /// Extract field from `Timelike`. fn extract_from_timelike(time: impl Timelike, unit: Unit) -> Decimal { - let usecs = || time.second() as u64 * 1_000_000 + (time.nanosecond() / 1000) as u64; + let nanos = || time.second() as u64 * 1_000_000_000 + time.nanosecond() as u64; match unit { Hour => time.hour().into(), Minute => time.minute().into(), - Second => Decimal::from_i128_with_scale(usecs() as i128, 6), - Millisecond => Decimal::from_i128_with_scale(usecs() as i128, 3), - Microsecond => usecs().into(), + Second => Decimal::from_i128_with_scale(nanos() as i128, 9), + Millisecond => Decimal::from_i128_with_scale(nanos() as i128, 6), + Microsecond => Decimal::from_i128_with_scale(nanos() as i128, 3), + Nanosecond => nanos().into(), Epoch => { - let usecs = time.num_seconds_from_midnight() as u64 * 1_000_000 - + (time.nanosecond() / 1000) as u64; - Decimal::from_i128_with_scale(usecs as i128, 6) + let nanos = + time.num_seconds_from_midnight() as u64 * 1_000_000_000 + time.nanosecond() as u64; + Decimal::from_i128_with_scale(nanos as i128, 9) } u => unreachable!("invalid unit {:?} for time", u), } @@ -92,12 +93,20 @@ fn extract_from_time(time: Time, unit: &Unit) -> Decimal { fn extract_from_timestamp(timestamp: Timestamp, unit: &Unit) -> Decimal { match unit { Epoch => { - let epoch = timestamp.0.and_utc().timestamp_micros(); - Decimal::from_i128_with_scale(epoch as i128, 6) + if let Some(nanos) = timestamp.0.and_utc().timestamp_nanos_opt() { + Decimal::from_i128_with_scale(nanos as i128, 9) + } else { + let micros = timestamp.0.and_utc().timestamp_micros(); + Decimal::from_i128_with_scale(micros as i128, 6) + } } Julian => { - let epoch = - Decimal::from_i128_with_scale(timestamp.0.and_utc().timestamp_micros() as i128, 6); + let epoch = if let Some(nanos) = timestamp.0.and_utc().timestamp_nanos_opt() { + Decimal::from_i128_with_scale(nanos as i128, 9) + } else { + let epoch = timestamp.0.and_utc().timestamp_micros(); + Decimal::from_i128_with_scale(epoch as i128, 6) + }; epoch / (24 * 60 * 60).into() + 2_440_588.into() } _ if unit.is_date_unit() => extract_from_datelike(timestamp.0.date(), *unit), @@ -284,6 +293,7 @@ define_unit! { Second, Millisecond, Microsecond, + Nanosecond, Epoch, Julian, Timezone, @@ -307,7 +317,7 @@ impl Unit { const fn is_time_unit(self) -> bool { matches!( self, - Hour | Minute | Second | Millisecond | Microsecond | Epoch + Hour | Minute | Second | Millisecond | Microsecond | Nanosecond | Epoch ) } @@ -437,10 +447,10 @@ mod tests { let extract = |unit| extract_from_time(time, &unit).to_string(); assert_eq!(extract(Hour), "23"); assert_eq!(extract(Minute), "22"); - assert_eq!(extract(Second), "57.123450"); - assert_eq!(extract(Millisecond), "57123.450"); - assert_eq!(extract(Microsecond), "57123450"); - assert_eq!(extract(Epoch), "84177.123450"); + assert_eq!(extract(Second), "57.123450000"); + assert_eq!(extract(Millisecond), "57123.450000"); + assert_eq!(extract(Microsecond), "57123450.000"); + assert_eq!(extract(Epoch), "84177.123450000"); } #[test] @@ -464,10 +474,10 @@ mod tests { assert_eq!(extract(Doy), "326"); assert_eq!(extract(Hour), "12"); assert_eq!(extract(Minute), "4"); - assert_eq!(extract(Second), "2.575400"); - assert_eq!(extract(Millisecond), "2575.400"); - assert_eq!(extract(Microsecond), "2575400"); - assert_eq!(extract(Epoch), "1637582642.575400"); + assert_eq!(extract(Second), "2.575400000"); + assert_eq!(extract(Millisecond), "2575.400000"); + assert_eq!(extract(Microsecond), "2575400.000"); + assert_eq!(extract(Epoch), "1637582642.575400000"); assert_eq!(extract(Julian), "2459541.5028075856481481481481"); } diff --git a/src/expr/impl/src/scalar/make_time.rs b/src/expr/impl/src/scalar/make_time.rs index ae1ff58033eed..8db394d617271 100644 --- a/src/expr/impl/src/scalar/make_time.rs +++ b/src/expr/impl/src/scalar/make_time.rs @@ -43,8 +43,8 @@ fn make_naive_time(hour: i32, min: i32, sec: F64) -> Result { }); } let sec_u32 = sec.0.trunc() as u32; - let microsecond_u32 = ((sec.0 - sec.0.trunc()) * 1_000_000.0).round_ties_even() as u32; - NaiveTime::from_hms_micro_opt(hour as u32, min as u32, sec_u32, microsecond_u32).ok_or_else( + let nanosecond_u32 = ((sec.0 - sec.0.trunc()) * 1_000_000_000.0).round_ties_even() as u32; + NaiveTime::from_hms_nano_opt(hour as u32, min as u32, sec_u32, nanosecond_u32).ok_or_else( || ExprError::InvalidParam { name: "hour, min, sec", reason: format!("invalid time: {}:{}:{}", hour, min, sec).into(), diff --git a/src/expr/impl/src/scalar/to_char.rs b/src/expr/impl/src/scalar/to_char.rs index 7c9e859675d03..96fad6fc269c5 100644 --- a/src/expr/impl/src/scalar/to_char.rs +++ b/src/expr/impl/src/scalar/to_char.rs @@ -87,6 +87,8 @@ impl ChronoPattern { ("Mon", "%b"), ("DD", "%d"), ("dd", "%d"), + ("NS", "%9f"), + ("ns", "%9f"), ("US", "%6f"), ("us", "%6f"), ("MS", "%3f"), diff --git a/src/storage/hummock_sdk/src/compaction_group/hummock_version_ext.rs b/src/storage/hummock_sdk/src/compaction_group/hummock_version_ext.rs index 6575d69886968..a3a79b0d12b3c 100644 --- a/src/storage/hummock_sdk/src/compaction_group/hummock_version_ext.rs +++ b/src/storage/hummock_sdk/src/compaction_group/hummock_version_ext.rs @@ -494,8 +494,8 @@ impl HummockVersion { .member_table_ids .clone_from(&group_construct.table_ids); self.levels.insert(*compaction_group_id, new_levels); - let member_table_ids = if group_construct.version - >= CompatibilityVersion::NoMemberTableIds as _ + let member_table_ids = if group_construct.version() + >= CompatibilityVersion::NoMemberTableIds { self.state_table_info .compaction_group_member_table_ids(*compaction_group_id) @@ -508,8 +508,7 @@ impl HummockVersion { BTreeSet::from_iter(group_construct.table_ids.clone()) }; - if group_construct.version >= CompatibilityVersion::SplitGroupByTableId as _ - { + if group_construct.version() >= CompatibilityVersion::SplitGroupByTableId { let split_key = if group_construct.split_key.is_some() { Some(Bytes::from(group_construct.split_key.clone().unwrap())) } else { diff --git a/src/tests/regress/data/expected/time.out b/src/tests/regress/data/expected/time.out index 0ff5dfc8d2490..cbd7d4aad1ad5 100644 --- a/src/tests/regress/data/expected/time.out +++ b/src/tests/regress/data/expected/time.out @@ -135,25 +135,8 @@ HINT: Could not choose a best candidate operator. You might need to add explici DROP TABLE TIME_TBL; -- -- test EXTRACT +-- Since postgresql does not support nanosecond time, it is not tested here. We did a similar test in e2e_test/batch/types/timestamp_ns.slt -- -SELECT EXTRACT(MICROSECOND FROM TIME '13:30:25.575401'); - extract ----------- - 25575401 -(1 row) - -SELECT EXTRACT(MILLISECOND FROM TIME '13:30:25.575401'); - extract ------------ - 25575.401 -(1 row) - -SELECT EXTRACT(SECOND FROM TIME '13:30:25.575401'); - extract ------------ - 25.575401 -(1 row) - SELECT EXTRACT(MINUTE FROM TIME '13:30:25.575401'); extract --------- @@ -172,11 +155,6 @@ SELECT EXTRACT(FORTNIGHT FROM TIME '13:30:25.575401'); -- error ERROR: "time" units "fortnight" not recognized SELECT EXTRACT(TIMEZONE FROM TIME '13:30:25.575401'); -- error ERROR: "time" units "timezone" not recognized -SELECT EXTRACT(EPOCH FROM TIME '13:30:25.575401'); - extract --------------- - 48625.575401 -(1 row) -- date_part implementation is mostly the same as extract, so only -- test a few cases for additional coverage. diff --git a/src/tests/regress/data/sql/time.sql b/src/tests/regress/data/sql/time.sql index b5dd188be1ccc..558a989a88796 100644 --- a/src/tests/regress/data/sql/time.sql +++ b/src/tests/regress/data/sql/time.sql @@ -60,16 +60,13 @@ DROP TABLE TIME_TBL; -- -- test EXTRACT +-- Since postgresql does not support nanosecond time, it is not tested here. We did a similar test in e2e_test/batch/types/timestamp_ns.slt -- -SELECT EXTRACT(MICROSECOND FROM TIME '13:30:25.575401'); -SELECT EXTRACT(MILLISECOND FROM TIME '13:30:25.575401'); -SELECT EXTRACT(SECOND FROM TIME '13:30:25.575401'); SELECT EXTRACT(MINUTE FROM TIME '13:30:25.575401'); SELECT EXTRACT(HOUR FROM TIME '13:30:25.575401'); SELECT EXTRACT(DAY FROM TIME '13:30:25.575401'); -- error SELECT EXTRACT(FORTNIGHT FROM TIME '13:30:25.575401'); -- error SELECT EXTRACT(TIMEZONE FROM TIME '13:30:25.575401'); -- error -SELECT EXTRACT(EPOCH FROM TIME '13:30:25.575401'); -- date_part implementation is mostly the same as extract, so only -- test a few cases for additional coverage.