Skip to content

Commit 1076df4

Browse files
Chong Gaores-life
authored andcommitted
Fall back to CPU for non-UTC ORC timestamp writes
cuDF's ORC writer always stamps writerTimezone="UTC" in the stripe footer and cannot record the JVM writer timezone (rapidsai/cudf#23422). Because ORC's timestamp type is timezone-agnostic, a GPU-written file in a non-UTC JVM is read back shifted by the zone offset by a CPU ORC reader. Restore the write-side guard so non-UTC timestamp ORC writes fall back to CPU, and update the test to assert the fallback instead of xfail. Signed-off-by: Chong Gao <chongg@nvidia.com>
1 parent 12296d7 commit 1076df4

2 files changed

Lines changed: 31 additions & 16 deletions

File tree

integration_tests/src/main/python/orc_test.py

Lines changed: 15 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1118,24 +1118,25 @@ def test_orc_not_support_timestamp_ltz(std_input_path):
11181118
conf={},
11191119
error_message="ParseException")
11201120

1121-
# test ORC reader and writer with the same timezone
1122-
# the `tz_sensitive_test` mark guarantees the write and read are in the same timezone
1123-
# The `spark.sql.session.timeZone` here does not impact reader and writer timezone, but any way, we test it.
1124-
# For the tests that reader and writer timezones are different, refer to `OrcTimezoneSuite`
1121+
# Timestamp writes: in UTC the GPU writes on the GPU; in a non-UTC JVM the GPU write must fall
1122+
# back to CPU. cuDF's ORC writer always stamps writerTimezone="UTC" in the stripe footer and
1123+
# cannot record the JVM writer timezone (https://github.com/rapidsai/cudf/issues/23422), so a
1124+
# GPU-written non-UTC file would be read back shifted by the zone offset by a CPU ORC reader.
1125+
# The `tz_sensitive_test` mark runs this in both UTC and non-UTC JVM timezones.
11251126
@tz_sensitive_test
11261127
@ignore_order(local=True)
1127-
@pytest.mark.xfail(
1128-
is_not_utc(),
1129-
reason="https://github.com/rapidsai/cudf/issues/23422")
11301128
def test_orc_gpu_write_cpu_read_timestamp_in_non_utc_timezone(spark_tmp_path):
11311129
data_path = spark_tmp_path + "/ORC_GPU_WRITE_TZ"
1132-
assert_gpu_and_cpu_writes_are_equal_collect(
1133-
lambda spark, path: (
1134-
spark.range(3)
1135-
.selectExpr("CAST(1593604800 + id AS TIMESTAMP) AS ts")
1136-
.write.orc(path)),
1137-
lambda spark, path: spark.read.orc(path),
1138-
data_path)
1130+
write_func = lambda spark, path: (
1131+
spark.range(3)
1132+
.selectExpr("CAST(1593604800 + id AS TIMESTAMP) AS ts")
1133+
.write.orc(path))
1134+
read_func = lambda spark, path: spark.read.orc(path)
1135+
if is_not_utc():
1136+
# Non-UTC: the timestamp write must fall back to CPU (DataWritingCommandExec).
1137+
assert_gpu_fallback_write(write_func, read_func, data_path, 'DataWritingCommandExec')
1138+
else:
1139+
assert_gpu_and_cpu_writes_are_equal_collect(write_func, read_func, data_path)
11391140

11401141

11411142
@pytest.mark.parametrize("reader_confs", reader_opt_confs, ids=idfn)

sql-plugin/src/main/scala/org/apache/spark/sql/rapids/GpuOrcFileFormat.scala

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@
1616

1717
package org.apache.spark.sql.rapids
1818

19+
import java.time.ZoneId
20+
1921
import ai.rapids.cudf._
2022
import com.nvidia.spark.rapids._
2123
import com.nvidia.spark.rapids.jni.fileio.RapidsFileIO
@@ -31,6 +33,7 @@ import org.apache.spark.sql.SparkSession
3133
import org.apache.spark.sql.catalyst.util.CaseInsensitiveMap
3234
import org.apache.spark.sql.execution.datasources.FileFormat
3335
import org.apache.spark.sql.execution.datasources.orc.{OrcFileFormat, OrcOptions, OrcUtils}
36+
import org.apache.spark.sql.internal.SQLConf
3437
import org.apache.spark.sql.rapids.execution.TrampolineUtil
3538
import org.apache.spark.sql.types._
3639

@@ -82,8 +85,19 @@ object GpuOrcFileFormat extends Logging {
8285
t.isInstanceOf[BooleanType])
8386
}
8487

85-
// ORC writing always uses UTC internally (cuDF writes writerTimezone="UTC").
86-
// The reader side handles timezone conversion. No write-side timezone restriction needed.
88+
// cuDF's ORC writer always stamps writerTimezone="UTC" in the stripe footer and cannot
89+
// record the actual JVM writer timezone (https://github.com/rapidsai/cudf/issues/23422).
90+
// Because ORC's `timestamp` type is timezone-agnostic, a file written on the GPU in a
91+
// non-UTC JVM is read back shifted by the zone offset by a CPU ORC reader. Fall back to CPU
92+
// for non-UTC timestamp writes so the output stays interoperable. Reads use the JVM default
93+
// (systemDefault) zone, so the write-side gate matches the reader on the same check.
94+
val types = schema.map(_.dataType).toSet
95+
if (types.exists(GpuOverrides.isOrContainsTimestamp) &&
96+
!GpuOverrides.isUTCTimezone(ZoneId.systemDefault())) {
97+
meta.willNotWorkOnGpu("Writing ORC timestamps is only supported in the UTC timezone " +
98+
s"(JVM: ${ZoneId.systemDefault()}, session: ${SQLConf.get.sessionLocalTimeZone}). " +
99+
"See https://github.com/rapidsai/cudf/issues/23422")
100+
}
87101

88102
if (hasBools && !meta.conf.isOrcBoolTypeEnabled) {
89103
meta.willNotWorkOnGpu("Nullable Booleans can not work in certain cases with ORC writer." +

0 commit comments

Comments
 (0)