Skip to content

Commit 14cf166

Browse files
authored
refactor: Timestamp(i64, tz) -> Timestamp(DateTime<Tz>) (#715)
* ci: make output of ttc server with arrow consist with http handler. * chore: fix cargo deny * refactor: Timestamp(i64, tz) -> Timestamp(DateTime<Tz>)
1 parent 7442178 commit 14cf166

File tree

20 files changed

+603
-361
lines changed

20 files changed

+603
-361
lines changed

bindings/nodejs/Cargo.toml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@ doc = false
1414

1515
[dependencies]
1616
chrono = { workspace = true }
17-
chrono-tz = { workspace = true }
1817
databend-driver = { workspace = true, features = ["rustls", "flight-sql"] }
1918
env_logger = "0.11.8"
2019
tokio-stream = { workspace = true }

bindings/nodejs/src/lib.rs

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,6 @@
1616
extern crate napi_derive;
1717

1818
use chrono::{DateTime, NaiveDate, NaiveDateTime, NaiveTime};
19-
use chrono_tz::Tz;
2019
use databend_driver::LoadMethod;
2120
use napi::{bindgen_prelude::*, Env};
2221
use once_cell::sync::Lazy;
@@ -317,11 +316,7 @@ impl ToNapiValue for Value<'_> {
317316
databend_driver::Value::Number(n) => {
318317
NumberValue::to_napi_value(env, NumberValue(n.clone()))
319318
}
320-
databend_driver::Value::Timestamp(_, _tz) => {
321-
let inner = val.inner.clone();
322-
let v = DateTime::<Tz>::try_from(inner).map_err(format_napi_error)?;
323-
DateTime::to_napi_value(env, v)
324-
}
319+
databend_driver::Value::Timestamp(dt) => DateTime::to_napi_value(env, *dt),
325320
databend_driver::Value::TimestampTz(dt) => {
326321
let formatted = dt.format(TIMESTAMP_TIMEZONE_FORMAT);
327322
String::to_napi_value(env, formatted.to_string())

bindings/python/Cargo.toml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@ doc = false
1414

1515
[dependencies]
1616
chrono = { workspace = true }
17-
chrono-tz = { workspace = true }
1817
databend-client = { workspace = true }
1918
databend-driver = { workspace = true, features = ["rustls", "flight-sql"] }
2019
databend-driver-core = { workspace = true }

bindings/python/src/types.rs

Lines changed: 4 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,7 @@ use std::sync::Arc;
1818
use chrono::offset::Offset;
1919
#[cfg(feature = "cp38")]
2020
use chrono::FixedOffset;
21-
use chrono::{DateTime, Duration, NaiveDate};
22-
use chrono_tz::Tz;
21+
use chrono::{Duration, NaiveDate};
2322
use once_cell::sync::Lazy;
2423
use pyo3::exceptions::{PyAttributeError, PyException, PyStopAsyncIteration, PyStopIteration};
2524
use pyo3::sync::GILOnceCell;
@@ -78,19 +77,15 @@ impl<'py> IntoPyObject<'py> for Value {
7877
let v = NumberValue(n);
7978
v.into_bound_py_any(py)?
8079
}
81-
databend_driver::Value::Timestamp(_, _) => {
82-
let t = DateTime::<Tz>::try_from(self.0).map_err(|e| {
83-
PyException::new_err(format!("failed to convert timestamp: {e}"))
84-
})?;
80+
databend_driver::Value::Timestamp(dt) => {
8581
#[cfg(feature = "cp38")]
8682
{
8783
// chrono_tz -> PyDateTime isn't implemented for Python < 3.9 (no zoneinfo).
88-
let t: DateTime<FixedOffset> = t.with_timezone(&t.offset().fix());
89-
t.into_bound_py_any(py)?
84+
dt.with_timezone(&dt.offset().fix()).into_bound_py_any(py)?
9085
}
9186
#[cfg(not(feature = "cp38"))]
9287
{
93-
t.into_bound_py_any(py)?
88+
dt.into_bound_py_any(py)?
9489
}
9590
}
9691
databend_driver::Value::TimestampTz(t) => t.into_bound_py_any(py)?,

bindings/python/tests/blocking/steps/binding.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -176,7 +176,9 @@ def _(context):
176176
)
177177
exp = datetime(2024, 4, 16, 12, 34, 56, 789000, tzinfo=tz_expected)
178178
exp_bug = datetime(2024, 4, 16, 18, 34, 56, 789000, tzinfo=tz_expected)
179-
if DB_VERSION >= (1, 2, 840) and not os.getenv("BODY_FORMAT") == "arrow":
179+
if (DB_VERSION >= (1, 2, 840) and not os.getenv("BODY_FORMAT") == "arrow") or (
180+
DB_VERSION >= (1, 2, 844) and os.getenv("BODY_FORMAT") == "arrow"
181+
):
180182
assert row.values()[0] == exp, f"timestamp_tz: {row.values()[0]} {exp}"
181183
else:
182184
assert row.values()[0] == exp_bug, (

cli/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ ctrlc = { version = "3.4.6", features = ["termination"] }
2828
databend-common-ast = "0.2.3"
2929
dirs = "5.0"
3030
fern = { version = "0.7", features = ["colored"] }
31-
indicatif = "0.17"
31+
indicatif = "0.18.3"
3232
log = "0.4"
3333
mime_guess = "2.0"
3434
nom = "8.0.0"

cli/src/display.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -839,7 +839,7 @@ fn value_display_width(value: &Value, quote_string: bool) -> usize {
839839
Value::EmptyArray => EMPTY_WIDTH,
840840
Value::EmptyMap => EMPTY_WIDTH,
841841
Value::Date(_) => DATE_WIDTH,
842-
Value::Timestamp(_, _) => TIMESTAMP_WIDTH,
842+
Value::Timestamp(_) => TIMESTAMP_WIDTH,
843843
Value::String(_) => {
844844
let value_str = value.to_string();
845845
if quote_string {

driver/tests/driver/select_simple.rs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ use std::assert_eq;
1616
use std::collections::HashMap;
1717

1818
use chrono::{DateTime, NaiveDate, NaiveDateTime};
19+
use chrono_tz::Tz;
1920
use databend_driver::{params, Client, Connection, DecimalSize, NumberValue, Value};
2021

2122
use crate::common::DEFAULT_DSN;
@@ -195,8 +196,8 @@ async fn select_datetime() {
195196
assert!(row.is_some());
196197
let row = row.unwrap();
197198
{
198-
let (val,): (i64,) = row.clone().try_into().unwrap();
199-
assert_eq!(val, 1680006896789000);
199+
let (val,): (DateTime<Tz>,) = row.clone().try_into().unwrap();
200+
assert_eq!(val.timestamp_micros(), 1680006896789000);
200201
}
201202
{
202203
let (val,): (NaiveDateTime,) = row.try_into().unwrap();

sql/src/raw_rows.rs

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,14 +16,25 @@ use crate::error::Error;
1616
use crate::error::Result;
1717
use crate::rows::Row;
1818
use crate::rows::ServerStats;
19+
use crate::value::FormatOptions;
1920
use crate::value::Value;
2021
use chrono_tz::Tz;
2122
use databend_client::schema::SchemaRef;
23+
use lexical_core::WriteFloatOptionsBuilder;
2224
use std::pin::Pin;
2325
use std::task::Context;
2426
use std::task::Poll;
2527
use tokio_stream::{Stream, StreamExt};
2628

29+
pub static HTTP_HANDLER_OPTIONS: FormatOptions = FormatOptions {
30+
true_string: b"1",
31+
false_string: b"0",
32+
float_options: WriteFloatOptionsBuilder::new()
33+
.nan_string(Some(b"NaN"))
34+
.inf_string(Some(b"Infinity"))
35+
.build_unchecked(),
36+
};
37+
2738
#[derive(Clone, Debug)]
2839
pub enum RawRowWithStats {
2940
Row(RawRow),
@@ -76,7 +87,7 @@ impl From<Row> for RawRow {
7687
fn from(row: Row) -> Self {
7788
let mut raw_row: Vec<Option<String>> = Vec::with_capacity(row.values().len());
7889
for val in row.values() {
79-
raw_row.push(Some(val.to_string()));
90+
raw_row.push(Some(val.to_string_with_options(&HTTP_HANDLER_OPTIONS)));
8091
}
8192
RawRow::new(row, raw_row)
8293
}

sql/src/value/arrow_decoder.rs

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ use arrow_array::{
2222
StructArray, TimestampMicrosecondArray, UInt16Array, UInt32Array, UInt64Array, UInt8Array,
2323
};
2424
use arrow_schema::{DataType as ArrowDataType, Field as ArrowField, TimeUnit};
25-
use chrono::{FixedOffset, TimeZone};
25+
use chrono::{FixedOffset, LocalResult, TimeZone};
2626
use chrono_tz::Tz;
2727
use databend_client::schema::{
2828
DecimalSize, ARROW_EXT_TYPE_BITMAP, ARROW_EXT_TYPE_EMPTY_ARRAY, ARROW_EXT_TYPE_EMPTY_MAP,
@@ -327,7 +327,18 @@ impl TryFrom<(&ArrowField, &Arc<dyn ArrowArray>, usize, Tz)> for Value {
327327
}
328328
let ts = array.value(seq);
329329
match tz {
330-
None => Ok(Value::Timestamp(ts, ltz)),
330+
None => {
331+
let dt = match ltz.timestamp_micros(ts) {
332+
LocalResult::Single(dt) => dt,
333+
LocalResult::None => {
334+
return Err(Error::Parsing(format!(
335+
"time {ts} not exists in timezone {ltz}"
336+
)))
337+
}
338+
LocalResult::Ambiguous(dt1, _dt2) => dt1,
339+
};
340+
Ok(Value::Timestamp(dt))
341+
}
331342
Some(tz) => Err(ConvertError::new("timestamp", format!("{array:?}"))
332343
.with_message(format!("non-UTC timezone not supported: {tz:?}"))
333344
.into()),

0 commit comments

Comments
 (0)