This is an automated email from the ASF dual-hosted git repository.
saihemanth-cloudera pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/hive.git
The following commit(s) were added to refs/heads/master by this push:
new 5a3af40bb15 HIVE-30020: Fix TIMESTAMP WITH LOCAL TIME ZONE data loss
and read failure on ORC tables (#6761)
5a3af40bb15 is described below
commit 5a3af40bb15d986fde8e886897c0e22bc7ebacce
Author: Vikram Ahuja <[email protected]>
AuthorDate: Thu Sep 24 23:08:22 2026 +0530
HIVE-30020: Fix TIMESTAMP WITH LOCAL TIME ZONE data loss and read failure
on ORC tables (#6761)
* HIVE-30020: Fix TIMESTAMP WITH LOCAL TIME ZONE data loss and read failure
on ORC tables
* Retrigger CI
* Retrigger CI again
---
.../apache/hadoop/hive/ql/io/orc/OrcStruct.java | 2 +
.../hadoop/hive/ql/io/orc/RecordReaderImpl.java | 44 ++++++++-
.../apache/hadoop/hive/ql/io/orc/WriterImpl.java | 15 +++
.../clientpositive/timestampz_with_file_formats.q | 19 ++++
.../llap/timestampz_with_file_formats.q.out | 106 +++++++++++++++++++++
5 files changed, 181 insertions(+), 5 deletions(-)
diff --git a/ql/src/java/org/apache/hadoop/hive/ql/io/orc/OrcStruct.java
b/ql/src/java/org/apache/hadoop/hive/ql/io/orc/OrcStruct.java
index 12291e7f494..700b1bcd97e 100644
--- a/ql/src/java/org/apache/hadoop/hive/ql/io/orc/OrcStruct.java
+++ b/ql/src/java/org/apache/hadoop/hive/ql/io/orc/OrcStruct.java
@@ -601,6 +601,8 @@ static ObjectInspector createObjectInspector(int columnId,
TypeInfoFactory.getVarcharTypeInfo(type.getMaximumLength()));
case TIMESTAMP:
return
PrimitiveObjectInspectorFactory.writableTimestampObjectInspector;
+ case TIMESTAMP_INSTANT:
+ return
PrimitiveObjectInspectorFactory.writableTimestampTZObjectInspector;
case DATE:
return PrimitiveObjectInspectorFactory.writableDateObjectInspector;
case DECIMAL:
diff --git a/ql/src/java/org/apache/hadoop/hive/ql/io/orc/RecordReaderImpl.java
b/ql/src/java/org/apache/hadoop/hive/ql/io/orc/RecordReaderImpl.java
index 190bec88218..6c0685dc5cc 100644
--- a/ql/src/java/org/apache/hadoop/hive/ql/io/orc/RecordReaderImpl.java
+++ b/ql/src/java/org/apache/hadoop/hive/ql/io/orc/RecordReaderImpl.java
@@ -19,12 +19,15 @@
package org.apache.hadoop.hive.ql.io.orc;
import java.io.IOException;
+import java.time.ZoneId;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import org.apache.hadoop.conf.Configuration;
+import org.apache.hadoop.hive.common.type.TimestampTZ;
+import org.apache.hadoop.hive.common.type.TimestampTZUtil;
import org.apache.hadoop.hive.conf.HiveConf;
import org.apache.hadoop.hive.ql.exec.vector.BytesColumnVector;
import org.apache.hadoop.hive.ql.exec.vector.ColumnVector;
@@ -46,6 +49,7 @@
import org.apache.hadoop.hive.serde2.io.HiveDecimalWritable;
import org.apache.hadoop.hive.serde2.io.HiveVarcharWritable;
import org.apache.hadoop.hive.serde2.io.ShortWritable;
+import org.apache.hadoop.hive.serde2.io.TimestampLocalTZWritable;
import org.apache.hadoop.hive.serde2.io.TimestampWritableV2;
import org.apache.hadoop.io.BooleanWritable;
import org.apache.hadoop.io.BytesWritable;
@@ -63,6 +67,10 @@ public class RecordReaderImpl extends
org.apache.orc.impl.RecordReaderImpl
private final VectorizedRowBatch batch;
private int rowInBatch;
private long baseRow;
+ // TIMESTAMP WITH LOCAL TIME ZONE is stored on disk as a UTC instant; this
is the session's
+ // display zone (hive.local.time.zone) to render that instant in, not the
JVM default zone,
+ // since SET TIME ZONE can change the former without touching the latter.
+ private final ZoneId localTimeZone;
protected RecordReaderImpl(ReaderImpl fileReader,
Reader.Options options, final Configuration conf) throws IOException {
@@ -75,6 +83,8 @@ protected RecordReaderImpl(ReaderImpl fileReader,
batch = this.schema.createRowBatch();
}
rowInBatch = 0;
+ localTimeZone = conf == null ? ZoneId.systemDefault()
+ : TimestampTZUtil.parseTimeZone(HiveConf.getVar(conf,
HiveConf.ConfVars.HIVE_LOCAL_TIME_ZONE));
}
/**
@@ -466,7 +476,29 @@ static TimestampWritableV2 nextTimestamp(ColumnVector
vector,
}
}
- static OrcStruct nextStruct(ColumnVector vector,
+ TimestampLocalTZWritable nextTimestampLocalTZ(ColumnVector vector,
+ int row,
+ Object previous) {
+ if (vector.isRepeating) {
+ row = 0;
+ }
+ if (vector.noNulls || !vector.isNull[row]) {
+ TimestampLocalTZWritable result;
+ if (previous == null || previous.getClass() !=
TimestampLocalTZWritable.class) {
+ result = new TimestampLocalTZWritable();
+ } else {
+ result = (TimestampLocalTZWritable) previous;
+ }
+ TimestampColumnVector tcv = (TimestampColumnVector) vector;
+ long epochSecond = Math.floorDiv(tcv.time[row], 1000L);
+ result.set(new TimestampTZ(epochSecond, tcv.nanos[row], localTimeZone));
+ return result;
+ } else {
+ return null;
+ }
+ }
+
+ OrcStruct nextStruct(ColumnVector vector,
int row,
TypeDescription schema,
Object previous) {
@@ -494,7 +526,7 @@ static OrcStruct nextStruct(ColumnVector vector,
}
}
- static OrcUnion nextUnion(ColumnVector vector,
+ OrcUnion nextUnion(ColumnVector vector,
int row,
TypeDescription schema,
Object previous) {
@@ -519,7 +551,7 @@ static OrcUnion nextUnion(ColumnVector vector,
}
}
- static ArrayList<Object> nextList(ColumnVector vector,
+ ArrayList<Object> nextList(ColumnVector vector,
int row,
TypeDescription schema,
Object previous) {
@@ -559,7 +591,7 @@ static ArrayList<Object> nextList(ColumnVector vector,
}
}
- static Map<Object,Object> nextMap(ColumnVector vector,
+ Map<Object,Object> nextMap(ColumnVector vector,
int row,
TypeDescription schema,
Object previous) {
@@ -591,7 +623,7 @@ static Map<Object,Object> nextMap(ColumnVector vector,
}
}
- static Object nextValue(ColumnVector vector,
+ Object nextValue(ColumnVector vector,
int row,
TypeDescription schema,
Object previous) {
@@ -624,6 +656,8 @@ static Object nextValue(ColumnVector vector,
return nextDate(vector, row, previous);
case TIMESTAMP:
return nextTimestamp(vector, row, previous);
+ case TIMESTAMP_INSTANT:
+ return nextTimestampLocalTZ(vector, row, previous);
case STRUCT:
return nextStruct(vector, row, schema, previous);
case UNION:
diff --git a/ql/src/java/org/apache/hadoop/hive/ql/io/orc/WriterImpl.java
b/ql/src/java/org/apache/hadoop/hive/ql/io/orc/WriterImpl.java
index 24083b3ab84..780a1efc370 100644
--- a/ql/src/java/org/apache/hadoop/hive/ql/io/orc/WriterImpl.java
+++ b/ql/src/java/org/apache/hadoop/hive/ql/io/orc/WriterImpl.java
@@ -26,6 +26,7 @@
import org.apache.hadoop.fs.FileSystem;
import org.apache.hadoop.fs.Path;
+import org.apache.hadoop.hive.common.type.TimestampTZ;
import org.apache.hadoop.hive.conf.HiveConf;
import org.apache.hadoop.hive.ql.exec.vector.BytesColumnVector;
import org.apache.hadoop.hive.ql.exec.vector.ColumnVector;
@@ -60,6 +61,7 @@
import
org.apache.hadoop.hive.serde2.objectinspector.primitive.LongObjectInspector;
import
org.apache.hadoop.hive.serde2.objectinspector.primitive.ShortObjectInspector;
import
org.apache.hadoop.hive.serde2.objectinspector.primitive.StringObjectInspector;
+import
org.apache.hadoop.hive.serde2.objectinspector.primitive.TimestampLocalTZObjectInspector;
import
org.apache.hadoop.hive.serde2.objectinspector.primitive.TimestampObjectInspector;
import org.apache.hadoop.io.BytesWritable;
import org.apache.hadoop.io.Text;
@@ -211,6 +213,16 @@ static void setColumn(int rowId, ColumnVector column,
.getPrimitiveJavaObject(obj).toSqlTimestamp());
break;
}
+ case TIMESTAMPLOCALTZ: {
+ TimestampColumnVector vector = (TimestampColumnVector) column;
+ vector.setIsUTC(true);
+ vector.setUsingProlepticCalendar(true);
+ TimestampTZ timestampTZ = ((TimestampLocalTZObjectInspector)
inspector)
+ .getPrimitiveJavaObject(obj);
+ vector.time[rowId] = timestampTZ.toEpochMilli();
+ vector.nanos[rowId] = timestampTZ.getNanos();
+ break;
+ }
case DATE: {
DateColumnVector vector = (DateColumnVector) column;
vector.setUsingProlepticCalendar(true);
@@ -230,6 +242,9 @@ static void setColumn(int rowId, ColumnVector column,
}
break;
}
+ default:
+ throw new IllegalArgumentException("Unknown primitive category "
+
+ ((PrimitiveObjectInspector)
inspector).getPrimitiveCategory());
}
break;
case STRUCT: {
diff --git a/ql/src/test/queries/clientpositive/timestampz_with_file_formats.q
b/ql/src/test/queries/clientpositive/timestampz_with_file_formats.q
new file mode 100644
index 00000000000..d42a3ff88a6
--- /dev/null
+++ b/ql/src/test/queries/clientpositive/timestampz_with_file_formats.q
@@ -0,0 +1,19 @@
+CREATE TABLE timestampltz_formats (
+ formatid string,
+ tsval timestamp with local time zone
+)
+ROW FORMAT SERDE 'org.apache.hadoop.hive.serde2.lazy.LazySimpleSerDe';
+
+LOAD DATA LOCAL INPATH '../../data/files/timestamps_mixed_formats.txt'
overwrite into table timestampltz_formats;
+
+SELECT * FROM timestampltz_formats;
+
+CREATE TABLE timestampltz_orc_format (
+ formatid string,
+ tsval timestamp with local time zone
+)
+stored as orc;
+
+insert into timestampltz_orc_format select * from timestampltz_formats;
+
+SELECT * FROM timestampltz_orc_format;
\ No newline at end of file
diff --git
a/ql/src/test/results/clientpositive/llap/timestampz_with_file_formats.q.out
b/ql/src/test/results/clientpositive/llap/timestampz_with_file_formats.q.out
new file mode 100644
index 00000000000..1f9410a0ae3
--- /dev/null
+++ b/ql/src/test/results/clientpositive/llap/timestampz_with_file_formats.q.out
@@ -0,0 +1,106 @@
+PREHOOK: query: CREATE TABLE timestampltz_formats (
+ formatid string,
+ tsval timestamp with local time zone
+)
+ROW FORMAT SERDE 'org.apache.hadoop.hive.serde2.lazy.LazySimpleSerDe'
+PREHOOK: type: CREATETABLE
+PREHOOK: Output: database:default
+PREHOOK: Output: default@timestampltz_formats
+POSTHOOK: query: CREATE TABLE timestampltz_formats (
+ formatid string,
+ tsval timestamp with local time zone
+)
+ROW FORMAT SERDE 'org.apache.hadoop.hive.serde2.lazy.LazySimpleSerDe'
+POSTHOOK: type: CREATETABLE
+POSTHOOK: Output: database:default
+POSTHOOK: Output: default@timestampltz_formats
+PREHOOK: query: LOAD DATA LOCAL INPATH
'../../data/files/timestamps_mixed_formats.txt' overwrite into table
timestampltz_formats
+PREHOOK: type: LOAD
+#### A masked pattern was here ####
+PREHOOK: Output: default@timestampltz_formats
+POSTHOOK: query: LOAD DATA LOCAL INPATH
'../../data/files/timestamps_mixed_formats.txt' overwrite into table
timestampltz_formats
+POSTHOOK: type: LOAD
+#### A masked pattern was here ####
+POSTHOOK: Output: default@timestampltz_formats
+PREHOOK: query: SELECT * FROM timestampltz_formats
+PREHOOK: type: QUERY
+PREHOOK: Input: default@timestampltz_formats
+#### A masked pattern was here ####
+POSTHOOK: query: SELECT * FROM timestampltz_formats
+POSTHOOK: type: QUERY
+POSTHOOK: Input: default@timestampltz_formats
+#### A masked pattern was here ####
+DEFAULT 2016-05-03 04:26:34.123456789 US/Pacific
+DEFAULT 2016-05-03 04:26:34.123456789 US/Pacific
+DEFAULT 2016-05-03 04:26:34.123456789 US/Pacific
+DEFAULT 2016-05-03 04:26:34.123456789 US/Pacific
+DEFAULT 2016-05-03 04:26:34.123456789 US/Pacific
+DEFAULT 2016-05-03 04:26:34.123456789 US/Pacific
+DEFAULT 2016-05-03 04:26:34.123456789 US/Pacific
+DEFAULT 2016-05-03 04:26:34.123456789 US/Pacific
+DEFAULT 2016-05-03 04:26:34.123456789 US/Pacific
+DEFAULT 2016-05-03 12:26:34.123456789 US/Pacific
+DEFAULT 2016-05-03 12:26:34.12345 US/Pacific
+DEFAULT 2016-05-03 12:26:34.12345 US/Pacific
+DEFAULT 2016-05-03 12:26:34.1 US/Pacific
+DEFAULT 2016-05-03 12:26:34.0 US/Pacific
+DEFAULT 2016-05-03 12:26:34.0 US/Pacific
+DEFAULT 2016-05-03 00:00:00.0 US/Pacific
+FORMAT1 NULL
+FORMAT1 NULL
+FORMAT2 NULL
+FORMAT2 NULL
+PREHOOK: query: CREATE TABLE timestampltz_orc_format (
+ formatid string,
+ tsval timestamp with local time zone
+)
+stored as orc
+PREHOOK: type: CREATETABLE
+PREHOOK: Output: database:default
+PREHOOK: Output: default@timestampltz_orc_format
+POSTHOOK: query: CREATE TABLE timestampltz_orc_format (
+ formatid string,
+ tsval timestamp with local time zone
+)
+stored as orc
+POSTHOOK: type: CREATETABLE
+POSTHOOK: Output: database:default
+POSTHOOK: Output: default@timestampltz_orc_format
+PREHOOK: query: insert into timestampltz_orc_format select * from
timestampltz_formats
+PREHOOK: type: QUERY
+PREHOOK: Input: default@timestampltz_formats
+PREHOOK: Output: default@timestampltz_orc_format
+POSTHOOK: query: insert into timestampltz_orc_format select * from
timestampltz_formats
+POSTHOOK: type: QUERY
+POSTHOOK: Input: default@timestampltz_formats
+POSTHOOK: Output: default@timestampltz_orc_format
+POSTHOOK: Lineage: timestampltz_orc_format.formatid SIMPLE
[(timestampltz_formats)timestampltz_formats.FieldSchema(name:formatid,
type:string, comment:null), ]
+POSTHOOK: Lineage: timestampltz_orc_format.tsval SIMPLE
[(timestampltz_formats)timestampltz_formats.FieldSchema(name:tsval,
type:timestamp with local time zone, comment:null), ]
+PREHOOK: query: SELECT * FROM timestampltz_orc_format
+PREHOOK: type: QUERY
+PREHOOK: Input: default@timestampltz_orc_format
+#### A masked pattern was here ####
+POSTHOOK: query: SELECT * FROM timestampltz_orc_format
+POSTHOOK: type: QUERY
+POSTHOOK: Input: default@timestampltz_orc_format
+#### A masked pattern was here ####
+DEFAULT 2016-05-03 04:26:34.123456789 US/Pacific
+DEFAULT 2016-05-03 04:26:34.123456789 US/Pacific
+DEFAULT 2016-05-03 04:26:34.123456789 US/Pacific
+DEFAULT 2016-05-03 04:26:34.123456789 US/Pacific
+DEFAULT 2016-05-03 04:26:34.123456789 US/Pacific
+DEFAULT 2016-05-03 04:26:34.123456789 US/Pacific
+DEFAULT 2016-05-03 04:26:34.123456789 US/Pacific
+DEFAULT 2016-05-03 04:26:34.123456789 US/Pacific
+DEFAULT 2016-05-03 04:26:34.123456789 US/Pacific
+DEFAULT 2016-05-03 12:26:34.123456789 US/Pacific
+DEFAULT 2016-05-03 12:26:34.12345 US/Pacific
+DEFAULT 2016-05-03 12:26:34.12345 US/Pacific
+DEFAULT 2016-05-03 12:26:34.1 US/Pacific
+DEFAULT 2016-05-03 12:26:34.0 US/Pacific
+DEFAULT 2016-05-03 12:26:34.0 US/Pacific
+DEFAULT 2016-05-03 00:00:00.0 US/Pacific
+FORMAT1 NULL
+FORMAT1 NULL
+FORMAT2 NULL
+FORMAT2 NULL