Skip to content

Commit 2071259

Browse files
authored
Fixing issues with for timestamp literals (#8193)
Fixing issue for timestamp literals
1 parent 234217e commit 2071259

File tree

9 files changed

+103
-35
lines changed

9 files changed

+103
-35
lines changed

datafusion/common/src/scalar.rs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -983,6 +983,18 @@ impl ScalarValue {
983983
ScalarValue::Decimal256(Some(v), precision, scale) => Ok(
984984
ScalarValue::Decimal256(Some(v.neg_wrapping()), *precision, *scale),
985985
),
986+
ScalarValue::TimestampSecond(Some(v), tz) => {
987+
Ok(ScalarValue::TimestampSecond(Some(-v), tz.clone()))
988+
}
989+
ScalarValue::TimestampNanosecond(Some(v), tz) => {
990+
Ok(ScalarValue::TimestampNanosecond(Some(-v), tz.clone()))
991+
}
992+
ScalarValue::TimestampMicrosecond(Some(v), tz) => {
993+
Ok(ScalarValue::TimestampMicrosecond(Some(-v), tz.clone()))
994+
}
995+
ScalarValue::TimestampMillisecond(Some(v), tz) => {
996+
Ok(ScalarValue::TimestampMillisecond(Some(-v), tz.clone()))
997+
}
986998
value => _internal_err!(
987999
"Can not run arithmetic negative on scalar value {value:?}"
9881000
),

datafusion/core/tests/sql/timestamp.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -742,7 +742,7 @@ async fn test_arrow_typeof() -> Result<()> {
742742
"+-----------------------------------------------------------------------+",
743743
"| arrow_typeof(date_trunc(Utf8(\"microsecond\"),to_timestamp(Int64(61)))) |",
744744
"+-----------------------------------------------------------------------+",
745-
"| Timestamp(Second, None) |",
745+
"| Timestamp(Nanosecond, None) |",
746746
"+-----------------------------------------------------------------------+",
747747
];
748748
assert_batches_eq!(expected, &actual);

datafusion/expr/src/built_in_function.rs

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -779,13 +779,10 @@ impl BuiltinScalarFunction {
779779
BuiltinScalarFunction::SubstrIndex => {
780780
utf8_to_str_type(&input_expr_types[0], "substr_index")
781781
}
782-
BuiltinScalarFunction::ToTimestamp => Ok(match &input_expr_types[0] {
783-
Int64 => Timestamp(Second, None),
784-
_ => Timestamp(Nanosecond, None),
785-
}),
782+
BuiltinScalarFunction::ToTimestamp
783+
| BuiltinScalarFunction::ToTimestampNanos => Ok(Timestamp(Nanosecond, None)),
786784
BuiltinScalarFunction::ToTimestampMillis => Ok(Timestamp(Millisecond, None)),
787785
BuiltinScalarFunction::ToTimestampMicros => Ok(Timestamp(Microsecond, None)),
788-
BuiltinScalarFunction::ToTimestampNanos => Ok(Timestamp(Nanosecond, None)),
789786
BuiltinScalarFunction::ToTimestampSeconds => Ok(Timestamp(Second, None)),
790787
BuiltinScalarFunction::FromUnixtime => Ok(Timestamp(Second, None)),
791788
BuiltinScalarFunction::Now => {

datafusion/physical-expr/src/datetime_expressions.rs

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -966,9 +966,11 @@ pub fn to_timestamp_invoke(args: &[ColumnarValue]) -> Result<ColumnarValue> {
966966
}
967967

968968
match args[0].data_type() {
969-
DataType::Int64 => {
970-
cast_column(&args[0], &DataType::Timestamp(TimeUnit::Second, None), None)
971-
}
969+
DataType::Int64 => cast_column(
970+
&cast_column(&args[0], &DataType::Timestamp(TimeUnit::Second, None), None)?,
971+
&DataType::Timestamp(TimeUnit::Nanosecond, None),
972+
None,
973+
),
972974
DataType::Timestamp(_, None) => cast_column(
973975
&args[0],
974976
&DataType::Timestamp(TimeUnit::Nanosecond, None),

datafusion/physical-expr/src/expressions/negative.rs

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ use arrow::{
3333
use datafusion_common::{internal_err, DataFusionError, Result};
3434
use datafusion_expr::interval_arithmetic::Interval;
3535
use datafusion_expr::{
36-
type_coercion::{is_interval, is_null, is_signed_numeric},
36+
type_coercion::{is_interval, is_null, is_signed_numeric, is_timestamp},
3737
ColumnarValue,
3838
};
3939

@@ -160,7 +160,10 @@ pub fn negative(
160160
let data_type = arg.data_type(input_schema)?;
161161
if is_null(&data_type) {
162162
Ok(arg)
163-
} else if !is_signed_numeric(&data_type) && !is_interval(&data_type) {
163+
} else if !is_signed_numeric(&data_type)
164+
&& !is_interval(&data_type)
165+
&& !is_timestamp(&data_type)
166+
{
164167
internal_err!(
165168
"Can't create negative physical expr for (- '{arg:?}'), the type of child expr is {data_type}, not signed numeric"
166169
)

datafusion/sql/src/expr/mod.rs

Lines changed: 22 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ mod value;
2929

3030
use crate::planner::{ContextProvider, PlannerContext, SqlToRel};
3131
use arrow_schema::DataType;
32+
use arrow_schema::TimeUnit;
3233
use datafusion_common::{
3334
internal_err, not_impl_err, plan_err, Column, DFSchema, DataFusionError, Result,
3435
ScalarValue,
@@ -224,14 +225,27 @@ impl<'a, S: ContextProvider> SqlToRel<'a, S> {
224225

225226
SQLExpr::Cast {
226227
expr, data_type, ..
227-
} => Ok(Expr::Cast(Cast::new(
228-
Box::new(self.sql_expr_to_logical_expr(
229-
*expr,
230-
schema,
231-
planner_context,
232-
)?),
233-
self.convert_data_type(&data_type)?,
234-
))),
228+
} => {
229+
let dt = self.convert_data_type(&data_type)?;
230+
let expr =
231+
self.sql_expr_to_logical_expr(*expr, schema, planner_context)?;
232+
233+
// numeric constants are treated as seconds (rather as nanoseconds)
234+
// to align with postgres / duckdb semantics
235+
let expr = match &dt {
236+
DataType::Timestamp(TimeUnit::Nanosecond, tz)
237+
if expr.get_type(schema)? == DataType::Int64 =>
238+
{
239+
Expr::Cast(Cast::new(
240+
Box::new(expr),
241+
DataType::Timestamp(TimeUnit::Second, tz.clone()),
242+
))
243+
}
244+
_ => expr,
245+
};
246+
247+
Ok(Expr::Cast(Cast::new(Box::new(expr), dt)))
248+
}
235249

236250
SQLExpr::TryCast {
237251
expr, data_type, ..

datafusion/sql/tests/sql_integration.rs

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -606,11 +606,9 @@ fn select_compound_filter() {
606606
#[test]
607607
fn test_timestamp_filter() {
608608
let sql = "SELECT state FROM person WHERE birth_date < CAST (158412331400600000 as timestamp)";
609-
610609
let expected = "Projection: person.state\
611-
\n Filter: person.birth_date < CAST(Int64(158412331400600000) AS Timestamp(Nanosecond, None))\
610+
\n Filter: person.birth_date < CAST(CAST(Int64(158412331400600000) AS Timestamp(Second, None)) AS Timestamp(Nanosecond, None))\
612611
\n TableScan: person";
613-
614612
quick_test(sql, expected);
615613
}
616614

datafusion/sqllogictest/test_files/timestamps.slt

Lines changed: 47 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1788,8 +1788,50 @@ SELECT TIMESTAMPTZ '2020-01-01 00:00:00Z' = TIMESTAMP '2020-01-01'
17881788
----
17891789
true
17901790

1791-
# verify to_timestamp edge cases to be in sync with postgresql
1792-
query PPPPP
1793-
SELECT to_timestamp(null), to_timestamp(-62125747200), to_timestamp(0), to_timestamp(1926632005177), to_timestamp(1926632005)
1794-
----
1795-
NULL 0001-04-25T00:00:00 1970-01-01T00:00:00 +63022-07-16T12:59:37 2031-01-19T23:33:25
1791+
# verify timestamp cast with integer input
1792+
query PPPPPP
1793+
SELECT to_timestamp(null), to_timestamp(0), to_timestamp(1926632005), to_timestamp(1), to_timestamp(-1), to_timestamp(0-1)
1794+
----
1795+
NULL 1970-01-01T00:00:00 2031-01-19T23:33:25 1970-01-01T00:00:01 1969-12-31T23:59:59 1969-12-31T23:59:59
1796+
1797+
# verify timestamp syntax stlyes are consistent
1798+
query BBBBBBBBBBBBB
1799+
SELECT to_timestamp(null) is null as c1,
1800+
null::timestamp is null as c2,
1801+
cast(null as timestamp) is null as c3,
1802+
to_timestamp(0) = 0::timestamp as c4,
1803+
to_timestamp(1926632005) = 1926632005::timestamp as c5,
1804+
to_timestamp(1) = 1::timestamp as c6,
1805+
to_timestamp(-1) = -1::timestamp as c7,
1806+
to_timestamp(0-1) = (0-1)::timestamp as c8,
1807+
to_timestamp(0) = cast(0 as timestamp) as c9,
1808+
to_timestamp(1926632005) = cast(1926632005 as timestamp) as c10,
1809+
to_timestamp(1) = cast(1 as timestamp) as c11,
1810+
to_timestamp(-1) = cast(-1 as timestamp) as c12,
1811+
to_timestamp(0-1) = cast(0-1 as timestamp) as c13
1812+
----
1813+
true true true true true true true true true true true true true
1814+
1815+
# verify timestamp output types
1816+
query TTT
1817+
SELECT arrow_typeof(to_timestamp(1)), arrow_typeof(to_timestamp(null)), arrow_typeof(to_timestamp('2023-01-10 12:34:56.000'))
1818+
----
1819+
Timestamp(Nanosecond, None) Timestamp(Nanosecond, None) Timestamp(Nanosecond, None)
1820+
1821+
# verify timestamp output types using timestamp literal syntax
1822+
query BBBBBB
1823+
SELECT arrow_typeof(to_timestamp(1)) = arrow_typeof(1::timestamp) as c1,
1824+
arrow_typeof(to_timestamp(null)) = arrow_typeof(null::timestamp) as c2,
1825+
arrow_typeof(to_timestamp('2023-01-10 12:34:56.000')) = arrow_typeof('2023-01-10 12:34:56.000'::timestamp) as c3,
1826+
arrow_typeof(to_timestamp(1)) = arrow_typeof(cast(1 as timestamp)) as c4,
1827+
arrow_typeof(to_timestamp(null)) = arrow_typeof(cast(null as timestamp)) as c5,
1828+
arrow_typeof(to_timestamp('2023-01-10 12:34:56.000')) = arrow_typeof(cast('2023-01-10 12:34:56.000' as timestamp)) as c6
1829+
----
1830+
true true true true true true
1831+
1832+
# known issues. currently overflows (expects default precision to be microsecond instead of nanoseconds. Work pending)
1833+
#verify extreme values
1834+
#query PPPPPPPP
1835+
#SELECT to_timestamp(-62125747200), to_timestamp(1926632005177), -62125747200::timestamp, 1926632005177::timestamp, cast(-62125747200 as timestamp), cast(1926632005177 as timestamp)
1836+
#----
1837+
#0001-04-25T00:00:00 +63022-07-16T12:59:37 0001-04-25T00:00:00 +63022-07-16T12:59:37 0001-04-25T00:00:00 +63022-07-16T12:59:37

datafusion/sqllogictest/test_files/window.slt

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -895,14 +895,14 @@ SELECT
895895

896896
statement ok
897897
create table temp as values
898-
(1664264591000000000),
899-
(1664264592000000000),
900-
(1664264592000000000),
901-
(1664264593000000000),
902-
(1664264594000000000),
903-
(1664364594000000000),
904-
(1664464594000000000),
905-
(1664564594000000000);
898+
(1664264591),
899+
(1664264592),
900+
(1664264592),
901+
(1664264593),
902+
(1664264594),
903+
(1664364594),
904+
(1664464594),
905+
(1664564594);
906906

907907
statement ok
908908
create table t as select cast(column1 as timestamp) as ts from temp;

0 commit comments

Comments
 (0)