You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@datafusion.apache.org by gi...@apache.org on 2026/10/18 05:54:08 UTC

(datafusion) branch main updated: fix: preserve input strictness in `from_unixtime` (#26414)

This is an automated email from the ASF dual-hosted git repository.

github-merge-queue[bot] pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/datafusion.git


The following commit(s) were added to refs/heads/main by this push:
     new 5b5edc7f3c fix: preserve input strictness in `from_unixtime` (#26414)
5b5edc7f3c is described below

commit 5b5edc7f3cbc46106455e78d2a10551ed57e884d
Author: Huaijin <ha...@gmail.com>
AuthorDate: Sun Oct 18 05:29:56 2026 +0000

    fix: preserve input strictness in `from_unixtime` (#26414)
    
    ## Which issue does this PR close?
    
    Found while reviewing #26111; no separate issue. This fix is independent
    of that PR.
    
    ## Rationale for this change
    
    `from_unixtime(CAST(seconds AS BIGINT))` could incorrectly eliminate a
    multi-key sort. Ordered input rows `(seconds, id) = (1.1, 1), (1.9, 0)`
    produce the same timestamp, but the IDs must be reordered to `0, 1`.
    
    ## What changes are included in this PR?
    
    Propagate the first argument's strict ordering property instead of
    unconditionally returning `true`.
    
    ## What is the testing strategy for this PR?
    
    Three compact SQL cases in `datetime/timestamps.slt` check the result
    ordering with and without a timezone, including NULLs, and verify that
    direct integer inputs still avoid an extra sort. The two regression
    cases fail without the fix.
    
    Validated with the targeted SQL test file, all 10 `from_unixtime` unit
    tests, formatting, and `git diff --check`.
    
    ## Are there any user-facing changes?
    
    Yes. Queries with lossy nested inputs to `from_unixtime` retain required
    sorts and return correctly ordered rows.
---
 datafusion/functions/src/datetime/from_unixtime.rs |  8 ++---
 .../test_files/datetime/timestamps.slt             | 41 ++++++++++++++++++++++
 2 files changed, 44 insertions(+), 5 deletions(-)

diff --git a/datafusion/functions/src/datetime/from_unixtime.rs b/datafusion/functions/src/datetime/from_unixtime.rs
index 89a7e807e3..c1914d1f48 100644
--- a/datafusion/functions/src/datetime/from_unixtime.rs
+++ b/datafusion/functions/src/datetime/from_unixtime.rs
@@ -212,11 +212,9 @@ impl ScalarUDFImpl for FromUnixtimeFunc {
         Ok(true)
     }
 
-    fn strictly_order_preserving(&self, _inputs: &[ExprProperties]) -> Result<bool> {
-        // `from_unixtime` stores the input's exact `Int64` value as a
-        // `Timestamp(Second)`: the mapping is one-to-one, order-preserving,
-        // and maps nulls to nulls.
-        Ok(true)
+    fn strictly_order_preserving(&self, inputs: &[ExprProperties]) -> Result<bool> {
+        // A lossless cast cannot restore strictness lost by its input.
+        Ok(inputs[0].strictly_order_preserving)
     }
 
     fn documentation(&self) -> Option<&Documentation> {
diff --git a/datafusion/sqllogictest/test_files/datetime/timestamps.slt b/datafusion/sqllogictest/test_files/datetime/timestamps.slt
index 162846779a..a0f4af894a 100644
--- a/datafusion/sqllogictest/test_files/datetime/timestamps.slt
+++ b/datafusion/sqllogictest/test_files/datetime/timestamps.slt
@@ -460,6 +460,47 @@ SELECT from_unixtime(ts / 1000000000, 'UTC') FROM ts_data LIMIT 3;
 2020-09-08T12:42:29Z
 2020-09-08T11:42:29Z
 
+# LIMIT makes the inner sort necessary; the lossy cast creates tied seconds.
+statement ok
+CREATE VIEW fractional_epochs AS
+SELECT * FROM (VALUES (1.1::DOUBLE, 1), (1.9, 0), (NULL, 2), (2.1, 3)) AS v(seconds, id)
+ORDER BY seconds ASC NULLS FIRST, id LIMIT 3;
+
+query PI
+SELECT from_unixtime(CAST(seconds AS BIGINT)) AS t, id
+FROM fractional_epochs ORDER BY t ASC NULLS FIRST, id;
+----
+NULL 2
+1970-01-01T00:00:01 0
+1970-01-01T00:00:01 1
+
+query PI
+SELECT from_unixtime(CAST(seconds AS BIGINT), 'UTC') AS t, id
+FROM fractional_epochs ORDER BY t ASC NULLS FIRST, id;
+----
+NULL 2
+1970-01-01T00:00:01Z 0
+1970-01-01T00:00:01Z 1
+
+statement ok
+SET datafusion.explain.physical_plan_only = true;
+
+# A direct integer input still preserves the ordering of subsequent keys.
+query TT
+EXPLAIN SELECT from_unixtime(seconds, 'UTC') AS t, id
+FROM (
+  SELECT * FROM (VALUES (1::BIGINT, 1), (2, 0), (3, 2)) AS v(seconds, id)
+  ORDER BY seconds, id LIMIT 2
+) ORDER BY t, id;
+----
+physical_plan
+01)ProjectionExec: expr=[from_unixtime(column1@0, UTC) as t, column2@1 as id]
+02)--SortExec: TopK(fetch=2), expr=[column1@0 ASC NULLS LAST, column2@1 ASC NULLS LAST], preserve_partitioning=[false]
+03)----DataSourceExec: partitions=1, partition_sizes=[1]
+
+statement ok
+RESET datafusion.explain.physical_plan_only;
+
 # to_timestamp
 
 query I


---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@datafusion.apache.org
For additional commands, e-mail: commits-help@datafusion.apache.org