uros-b commented on code in PR #57681:
URL: https://github.com/apache/spark/pull/57681#discussion_r3700478253


##########
sql/core/src/main/scala/org/apache/spark/sql/execution/datasources/orc/types/ops/TimestampNanosOrcOps.scala:
##########
@@ -69,6 +79,19 @@ case class TimestampLTZNanosOrcOps(t: TimestampLTZNanosType) 
extends OrcTypeOps
       val instant = ts.toInstant
       set(ordinal, DateTimeUtils.instantToTimestampNanos(instant, t.precision))
     }
+
+  // The physical ORC category is a timestamp, so the search argument uses the 
TIMESTAMP leaf type.
+  override def predicateLeafType: Option[PredicateLeaf.Type] = 
Some(PredicateLeaf.Type.TIMESTAMP)
+
+  // The filter literal is an external java.time.Instant (see 
CatalystTypeConverters). ORC evaluates
+  // a TIMESTAMP_INSTANT predicate against the stored UTC value shifted into 
the JVM default zone,
+  // so the literal must be a java.sql.Timestamp whose local wall clock equals 
the instant's UTC
+  // wall clock; Timestamp.valueOf(LocalDateTime at UTC) produces exactly 
that. Any non-Instant
+  // value is passed through unchanged.
+  override def castFilterLiteral(value: Any): Any = value match {
+    case i: Instant => Timestamp.valueOf(LocalDateTime.ofInstant(i, 
ZoneOffset.UTC))

Review Comment:
   @stevomitric Please check: castFilterLiteral for TimestampLTZNanosType uses 
Timestamp.valueOf(LocalDateTime.ofInstant(i, ZoneOffset.UTC)), which produces a 
java.sql.Timestamp whose getTime() (epoch millis) equals the epoch of the UTC 
wall clock interpreted as the JVM local zone. Because 
java.sql.Timestamp.compareTo / getTime() are strictly epoch-millis-based (not 
"local wall clock"-based), and because OrcTimestamp for a TIMESTAMP_INSTANT 
column stores instant.toEpochMilli() (true UTC epoch), these two epoch-millis 
values diverge for any non-UTC JVM timezone. In UTC-ahead zones (e.g. IST, 
UTC+5:30) the filter literal is smaller than the stored epoch, so ORC's 
SearchArgument will skip stripes that contain valid matching rows; data loss. 
In UTC-behind zones stripes are over-included (conservative, not data-lossy). 
The correct construction is new java.sql.Timestamp(i.toEpochMilli) with 
setNanos(i.getNano()), matching how toJavaTimestampNoRebase and the regular 
TimestampType filter pat
 h work. The PR description's "shifted into the JVM default zone" reasoning is 
incorrect: Timestamp.compareTo operates on epoch millis regardless of any JVM 
zone shift.



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to