Skip to content

Commit ff62551

Browse files
authored
chore: adjust the storage method of timestamp_tz so that the timestamp value is retrieved directly. (#18974)
1 parent 06d4e1b commit ff62551

File tree

7 files changed

+109
-42
lines changed

7 files changed

+109
-42
lines changed

src/common/column/src/types/native.rs

Lines changed: 12 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -440,7 +440,6 @@ impl Neg for months_days_micros {
440440
}
441441
}
442442

443-
/// The in-memory representation of the MonthDayNano variant of the "Interval" logical type.
444443
#[derive(
445444
Debug,
446445
Copy,
@@ -460,12 +459,12 @@ pub struct timestamp_tz(pub i128);
460459

461460
impl Hash for timestamp_tz {
462461
fn hash<H: Hasher>(&self, state: &mut H) {
463-
self.total_micros().hash(state)
462+
self.timestamp().hash(state)
464463
}
465464
}
466465
impl PartialEq for timestamp_tz {
467466
fn eq(&self, other: &Self) -> bool {
468-
self.total_micros() == other.total_micros()
467+
self.timestamp() == other.timestamp()
469468
}
470469
}
471470
impl PartialOrd for timestamp_tz {
@@ -476,17 +475,18 @@ impl PartialOrd for timestamp_tz {
476475

477476
impl Ord for timestamp_tz {
478477
fn cmp(&self, other: &Self) -> Ordering {
479-
let total_micros = self.total_micros();
480-
let other_micros = other.total_micros();
481-
total_micros.cmp(&other_micros)
478+
let timestamp = self.timestamp();
479+
let other_micros = other.timestamp();
480+
timestamp.cmp(&other_micros)
482481
}
483482
}
484483

485484
impl timestamp_tz {
486485
pub const MICROS_PER_SECOND: i64 = 1_000_000;
487486

487+
#[inline]
488488
pub fn new(timestamp: i64, offset: i32) -> Self {
489-
let ts = timestamp as u64 as i128; // <- 中间加一次 u64 屏蔽符号位
489+
let ts = timestamp as u64 as i128;
490490
let off = (offset as i128) << 64;
491491
Self(off | ts)
492492
}
@@ -507,32 +507,19 @@ impl timestamp_tz {
507507
}
508508

509509
#[inline]
510-
pub fn hours_offset(&self) -> i8 {
511-
(self.seconds_offset() / 3600) as i8
510+
pub fn micros_offset_inner(seconds: i64) -> Option<i64> {
511+
seconds.checked_mul(Self::MICROS_PER_SECOND)
512512
}
513513

514514
#[inline]
515-
pub fn total_micros(&self) -> i64 {
516-
self.try_total_micros().unwrap_or_else(|| {
517-
error!(
518-
"interval is out of range: timestamp={}, offset={}",
519-
self.timestamp(),
520-
self.seconds_offset()
521-
);
522-
0
523-
})
524-
}
525-
526-
#[inline]
527-
pub fn try_total_micros(&self) -> Option<i64> {
528-
let offset_micros = self.micros_offset()?;
529-
self.timestamp().checked_sub(offset_micros)
515+
pub fn hours_offset(&self) -> i8 {
516+
(self.seconds_offset() / 3600) as i8
530517
}
531518
}
532519

533520
impl Display for timestamp_tz {
534521
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
535-
let timestamp = Timestamp::from_microsecond(self.total_micros()).unwrap();
522+
let timestamp = Timestamp::from_microsecond(self.timestamp()).unwrap();
536523

537524
let offset = tz::Offset::from_seconds(self.seconds_offset()).unwrap();
538525
let string = strtime::format(

src/query/expression/src/row_encoding.rs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
// limitations under the License.
1414

1515
use databend_common_column::types::months_days_micros;
16+
use databend_common_column::types::timestamp_tz;
1617

1718
use crate::types::i256;
1819
use crate::types::F32;
@@ -103,3 +104,11 @@ impl FixedLengthEncoding for months_days_micros {
103104
self.total_micros().encode()
104105
}
105106
}
107+
108+
impl FixedLengthEncoding for timestamp_tz {
109+
type Encoded = [u8; 8];
110+
111+
fn encode(self) -> [u8; 8] {
112+
self.timestamp().encode()
113+
}
114+
}

src/query/expression/src/types/timestamp_tz.rs

Lines changed: 35 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -148,14 +148,39 @@ pub fn string_to_timestamp_tz<'a, F: FnOnce() -> &'a TimeZone>(
148148
.or_else(|_| fmt::strtime::parse("%Y-%m-%d %H:%M:%S%.f %z", ts_str))
149149
.or_else(|_| fmt::strtime::parse("%Y-%m-%d %H:%M:%S%.f %:z", ts_str))
150150
.or_else(|_| fmt::strtime::parse("%Y-%m-%d %H:%M:%S%.f", ts_str))?;
151-
let datetime = time.to_datetime()?;
152-
let timestamp = tz::offset(0).to_timestamp(datetime)?;
153-
let offset = time
154-
.offset()
155-
.unwrap_or_else(|| fn_tz().to_offset(timestamp));
156-
157-
Ok(timestamp_tz::new(
158-
timestamp.as_microsecond(),
159-
offset.seconds(),
160-
))
151+
match time.offset() {
152+
None => {
153+
let datetime = time.to_datetime()?;
154+
let timestamp = tz::offset(0).to_timestamp(datetime)?;
155+
let offset = fn_tz().to_offset(timestamp);
156+
157+
Ok(timestamp_tz::new(
158+
timestamp.as_microsecond() - (offset.seconds() as i64 * 1_000_000),
159+
offset.seconds(),
160+
))
161+
}
162+
Some(offset) => {
163+
let timestamp = time.to_timestamp()?;
164+
165+
Ok(timestamp_tz::new(
166+
timestamp.as_microsecond(),
167+
offset.seconds(),
168+
))
169+
}
170+
}
171+
}
172+
173+
#[cfg(test)]
174+
mod tests {
175+
use super::*;
176+
177+
#[test]
178+
fn stores_utc_in_timestamp_field() {
179+
let tz = TimeZone::get("Asia/Shanghai").unwrap();
180+
let value = string_to_timestamp_tz(b"2021-12-20 17:01:01 +0800", || &tz).expect("parse tz");
181+
assert_eq!(value.seconds_offset(), 28_800);
182+
// timestamp() keeps the UTC instant (09:01:01).
183+
assert_eq!(value.timestamp(), 1_639_990_861_000_000);
184+
assert_eq!(value.to_string(), "2021-12-20 17:01:01.000000 +0800");
185+
}
161186
}

src/query/functions/src/scalars/timestamp/src/datetime.rs

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -201,8 +201,8 @@ fn timestamp_tz_domain_to_timestamp_domain(
201201
domain: &SimpleDomain<timestamp_tz>,
202202
) -> Option<SimpleDomain<i64>> {
203203
Some(SimpleDomain {
204-
min: domain.min.total_micros(),
205-
max: domain.max.total_micros(),
204+
min: domain.min.timestamp(),
205+
max: domain.max.timestamp(),
206206
})
207207
}
208208

@@ -689,7 +689,10 @@ fn register_timestamp_to_timestamp_tz(registry: &mut FunctionRegistry) {
689689
}
690690
};
691691
let offset = ctx.func_ctx.tz.to_offset(ts);
692-
let ts_tz = timestamp_tz::new(val, offset.seconds());
692+
let ts_tz = timestamp_tz::new(
693+
val - (offset.seconds() as i64 * 1_000_000),
694+
offset.seconds(),
695+
);
693696

694697
output.push(ts_tz)
695698
})(val, ctx)
@@ -712,7 +715,7 @@ fn register_timestamp_tz_to_timestamp(registry: &mut FunctionRegistry) {
712715
ctx: &mut EvalContext,
713716
) -> Value<TimestampType> {
714717
vectorize_with_builder_1_arg::<TimestampTzType, TimestampType>(|val, output, _ctx| {
715-
output.push(val.total_micros())
718+
output.push(val.timestamp())
716719
})(val, ctx)
717720
}
718721
}

src/query/functions/src/scalars/timestamp/src/interval.rs

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -145,7 +145,9 @@ fn register_interval_add_sub_mul(registry: &mut FunctionRegistry) {
145145
return;
146146
}
147147
};
148-
eval_timestamp_plus(a, b, output, ctx, |input| input.timestamp(), |result| timestamp_tz::new(result, a.seconds_offset()), TimeZone::fixed(offset));
148+
eval_timestamp_plus(a, b, output, ctx, |input| input.timestamp(), |result| {
149+
timestamp_tz::new(result, a.seconds_offset())
150+
}, TimeZone::fixed(offset));
149151
},
150152
),
151153
);
@@ -183,7 +185,9 @@ fn register_interval_add_sub_mul(registry: &mut FunctionRegistry) {
183185
return;
184186
}
185187
};
186-
eval_timestamp_plus(a, b, output, ctx, |input| input.timestamp(), |result| timestamp_tz::new(result, a.seconds_offset()), TimeZone::fixed(offset));
188+
eval_timestamp_plus(a, b, output, ctx, |input| input.timestamp(), |result| {
189+
timestamp_tz::new(result, a.seconds_offset())
190+
}, TimeZone::fixed(offset));
187191
},
188192
),
189193
);
@@ -235,7 +239,9 @@ fn register_interval_add_sub_mul(registry: &mut FunctionRegistry) {
235239
return;
236240
}
237241
};
238-
eval_timestamp_minus(a, b, output, ctx, |input| input.timestamp(), |result| timestamp_tz::new(result, a.seconds_offset()), TimeZone::fixed(offset));
242+
eval_timestamp_minus(a, b, output, ctx, |input| input.timestamp(), |result| {
243+
timestamp_tz::new(result, a.seconds_offset())
244+
}, TimeZone::fixed(offset));
239245
},
240246
),
241247
);

src/query/pipeline/transforms/src/processors/transforms/sorts/core/row_convert/variable.rs

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,12 +17,14 @@ use std::ops::Range;
1717
use databend_common_column::bitmap::Bitmap;
1818
use databend_common_column::buffer::Buffer;
1919
use databend_common_column::types::months_days_micros;
20+
use databend_common_column::types::timestamp_tz;
2021
use databend_common_exception::ErrorCode;
2122
use databend_common_exception::Result;
2223
use databend_common_expression::types::binary::BinaryColumn;
2324
use databend_common_expression::types::binary::BinaryColumnBuilder;
2425
use databend_common_expression::types::i256;
2526
use databend_common_expression::types::nullable::NullableColumn;
27+
use databend_common_expression::types::timestamp_tz::TimestampTzType;
2628
use databend_common_expression::types::AccessType;
2729
use databend_common_expression::types::BinaryType;
2830
use databend_common_expression::types::BooleanType;
@@ -128,6 +130,7 @@ impl RowConverter<VariableRows> for VariableRowConverter {
128130
| DataType::Number(_)
129131
| DataType::Decimal(_)
130132
| DataType::Timestamp
133+
| DataType::TimestampTz
131134
| DataType::Interval
132135
| DataType::Date
133136
| DataType::Binary
@@ -337,6 +340,11 @@ impl LengthCalculatorVisitor<'_> {
337340
*length += i64::ENCODED_LEN
338341
}
339342
}
343+
DataType::TimestampTz => {
344+
for length in self.lengths.iter_mut() {
345+
*length += timestamp_tz::ENCODED_LEN
346+
}
347+
}
340348
DataType::Date => {
341349
for length in self.lengths.iter_mut() {
342350
*length += i32::ENCODED_LEN
@@ -570,6 +578,21 @@ impl EncodeVisitor<'_> {
570578
self.field.nulls_first,
571579
);
572580
}
581+
DataType::TimestampTz => {
582+
let scalar_value = if is_null {
583+
timestamp_tz::default()
584+
} else {
585+
*scalar.as_timestamp_tz().unwrap()
586+
};
587+
fixed_encode_const::<TimestampTzType>(
588+
&mut self.out.data,
589+
&mut self.out.offsets,
590+
is_null,
591+
scalar_value,
592+
self.field.asc,
593+
self.field.nulls_first,
594+
);
595+
}
573596
DataType::Date => {
574597
let scalar_value = if is_null {
575598
0i32

tests/sqllogictests/suites/base/11_data_type/11_0001_data_type_date_time.test

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -336,6 +336,20 @@ insert into t values(1, '2022-02-03T03:02:00+0800')
336336
statement ok
337337
drop table t
338338

339+
query TT
340+
WITH t AS (
341+
SELECT to_timestamp_tz('2021-12-20 09:00:01 +00:00') AS v, 'A_09:00@UTC' AS tag
342+
UNION ALL
343+
SELECT to_timestamp_tz('2021-12-20 17:00:00 +08:00'), 'B_17:00@+8'
344+
UNION ALL
345+
SELECT to_timestamp_tz('2021-12-20 09:30:00 +00:00'), 'C_09:30@UTC'
346+
)
347+
SELECT tag, v FROM t ORDER BY v;
348+
----
349+
B_17:00@+8 2021-12-20 17:00:00.000000 +0800
350+
A_09:00@UTC 2021-12-20 09:00:01.000000 +0000
351+
C_09:30@UTC 2021-12-20 09:30:00.000000 +0000
352+
339353
statement ok
340354
set timezone='UTC'
341355

0 commit comments

Comments
 (0)