You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@paimon.apache.org by lz...@apache.org on 2023/05/26 08:32:30 UTC
[incubator-paimon] branch master updated: [core] paimon format avro support Timestamp LTZ (#1171)
This is an automated email from the ASF dual-hosted git repository.
lzljs3620320 pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/incubator-paimon.git
The following commit(s) were added to refs/heads/master by this push:
new 965620a5e [core] paimon format avro support Timestamp LTZ (#1171)
965620a5e is described below
commit 965620a5ec96772dac0f414d4510bfbf32e3cee0
Author: legendtkl <ta...@gmail.com>
AuthorDate: Fri May 26 16:32:24 2023 +0800
[core] paimon format avro support Timestamp LTZ (#1171)
---
.../paimon/format/avro/AvroSchemaConverter.java | 24 ++++++++-
.../format/avro/AvroToRowDataConverters.java | 49 ++++++++++++++++---
.../format/avro/RowDataToAvroConverters.java | 34 ++++++++++++-
.../paimon/format/avro/AvroBulkFormatTest.java | 49 +++++++++++++++----
.../format/avro/AvroBulkFormatTestUtils.java | 18 ++++++-
.../paimon/format/avro/AvroFileFormatTest.java | 17 ++-----
.../format/avro/AvroToRowDataConvertersTest.java | 47 ++++++++++++++++++
.../format/avro/RowDataToAvroConvertersTest.java | 57 ++++++++++++++++++++++
.../java/org/apache/paimon/hive/HiveTypeUtils.java | 1 +
.../org/apache/paimon/hive/HiveTypeUtilsTest.java | 11 ++---
10 files changed, 265 insertions(+), 42 deletions(-)
diff --git a/paimon-format/src/main/java/org/apache/paimon/format/avro/AvroSchemaConverter.java b/paimon-format/src/main/java/org/apache/paimon/format/avro/AvroSchemaConverter.java
index 86fc18110..bfca9006a 100644
--- a/paimon-format/src/main/java/org/apache/paimon/format/avro/AvroSchemaConverter.java
+++ b/paimon-format/src/main/java/org/apache/paimon/format/avro/AvroSchemaConverter.java
@@ -23,6 +23,7 @@ import org.apache.paimon.types.DataType;
import org.apache.paimon.types.DataTypeRoot;
import org.apache.paimon.types.DecimalType;
import org.apache.paimon.types.IntType;
+import org.apache.paimon.types.LocalZonedTimestampType;
import org.apache.paimon.types.MapType;
import org.apache.paimon.types.MultisetType;
import org.apache.paimon.types.RowType;
@@ -101,15 +102,36 @@ public class AvroSchemaConverter {
org.apache.avro.LogicalType avroLogicalType;
if (precision <= 3) {
avroLogicalType = LogicalTypes.timestampMillis();
+ } else if (precision <= 6) {
+ avroLogicalType = LogicalTypes.timestampMicros();
} else {
throw new IllegalArgumentException(
"Avro does not support TIMESTAMP type "
+ "with precision: "
+ precision
- + ", it only supports precision less than 3.");
+ + ", it only supports precision less than 6.");
}
Schema timestamp = avroLogicalType.addToSchema(SchemaBuilder.builder().longType());
return nullable ? nullableSchema(timestamp) : timestamp;
+ case TIMESTAMP_WITH_LOCAL_TIME_ZONE:
+ final LocalZonedTimestampType localTimestampType =
+ (LocalZonedTimestampType) dataType;
+ precision = localTimestampType.getPrecision();
+ org.apache.avro.LogicalType localTimestampLogicalType;
+ if (precision <= 3) {
+ localTimestampLogicalType = LogicalTypes.localTimestampMillis();
+ } else if (precision <= 6) {
+ localTimestampLogicalType = LogicalTypes.localTimestampMicros();
+ } else {
+ throw new IllegalArgumentException(
+ "Avro does not support TIMESTAMP type "
+ + "with precision: "
+ + precision
+ + ", it only supports precision less than 6.");
+ }
+ Schema localTimestampSchema =
+ localTimestampLogicalType.addToSchema(SchemaBuilder.builder().longType());
+ return nullable ? nullableSchema(localTimestampSchema) : localTimestampSchema;
case DATE:
// use int to represents Date
Schema date = LogicalTypes.date().addToSchema(SchemaBuilder.builder().intType());
diff --git a/paimon-format/src/main/java/org/apache/paimon/format/avro/AvroToRowDataConverters.java b/paimon-format/src/main/java/org/apache/paimon/format/avro/AvroToRowDataConverters.java
index cf15f4f41..3e2e11700 100644
--- a/paimon-format/src/main/java/org/apache/paimon/format/avro/AvroToRowDataConverters.java
+++ b/paimon-format/src/main/java/org/apache/paimon/format/avro/AvroToRowDataConverters.java
@@ -18,6 +18,7 @@
package org.apache.paimon.format.avro;
+import org.apache.paimon.annotation.VisibleForTesting;
import org.apache.paimon.data.BinaryString;
import org.apache.paimon.data.Decimal;
import org.apache.paimon.data.GenericArray;
@@ -30,7 +31,9 @@ import org.apache.paimon.types.DataField;
import org.apache.paimon.types.DataType;
import org.apache.paimon.types.DataTypes;
import org.apache.paimon.types.DecimalType;
+import org.apache.paimon.types.LocalZonedTimestampType;
import org.apache.paimon.types.RowType;
+import org.apache.paimon.types.TimestampType;
import org.apache.avro.generic.GenericFixed;
import org.apache.avro.generic.GenericRecord;
@@ -97,7 +100,8 @@ public class AvroToRowDataConverters {
}
/** Creates a runtime converter which assuming input object is not null. */
- private static AvroToRowDataConverter createConverter(DataType type) {
+ @VisibleForTesting
+ static AvroToRowDataConverter createConverter(DataType type) {
switch (type.getTypeRoot()) {
case TINYINT:
return avroObject -> ((Integer) avroObject).byteValue();
@@ -114,7 +118,23 @@ public class AvroToRowDataConverters {
case TIME_WITHOUT_TIME_ZONE:
return AvroToRowDataConverters::convertToTime;
case TIMESTAMP_WITHOUT_TIME_ZONE:
- return AvroToRowDataConverters::convertToTimestamp;
+ int precision = ((TimestampType) type).getPrecision();
+ if (precision <= 3) {
+ return AvroToRowDataConverters::convertToTimestampFromMillis;
+ } else if (precision <= 6) {
+ return AvroToRowDataConverters::convertToTimestampFromMicros;
+ } else {
+ throw new UnsupportedOperationException("Unsupported precision: " + precision);
+ }
+ case TIMESTAMP_WITH_LOCAL_TIME_ZONE:
+ precision = ((LocalZonedTimestampType) type).getPrecision();
+ if (precision <= 3) {
+ return AvroToRowDataConverters::convertToTimestampFromMillis;
+ } else if (precision <= 6) {
+ return AvroToRowDataConverters::convertToTimestampFromMicros;
+ } else {
+ throw new UnsupportedOperationException("Unsupported precision: " + precision);
+ }
case CHAR:
case VARCHAR:
return avroObject -> BinaryString.fromString(avroObject.toString());
@@ -186,22 +206,35 @@ public class AvroToRowDataConverters {
};
}
- private static Timestamp convertToTimestamp(Object object) {
- final long millis;
+ private static Timestamp convertToTimestampFromMillis(Object object) {
+ return convertToTimestamp(object, 3);
+ }
+
+ private static Timestamp convertToTimestampFromMicros(Object object) {
+ return convertToTimestamp(object, 6);
+ }
+
+ @VisibleForTesting
+ static Timestamp convertToTimestamp(Object object, int precision) {
if (object instanceof Long) {
- millis = (Long) object;
+ if (precision <= 3) {
+ return Timestamp.fromEpochMillis((Long) object);
+ } else if (precision <= 6) {
+ return Timestamp.fromMicros((Long) object);
+ } else {
+ throw new UnsupportedOperationException("Unsupported precision: " + precision);
+ }
} else if (object instanceof Instant) {
- millis = ((Instant) object).toEpochMilli();
+ return Timestamp.fromInstant((Instant) object);
} else {
JodaConverter jodaConverter = JodaConverter.getConverter();
if (jodaConverter != null) {
- millis = jodaConverter.convertTimestamp(object);
+ return Timestamp.fromEpochMillis(jodaConverter.convertTimestamp(object));
} else {
throw new IllegalArgumentException(
"Unexpected object type for TIMESTAMP logical type. Received: " + object);
}
}
- return Timestamp.fromEpochMillis(millis);
}
private static int convertToDate(Object object) {
diff --git a/paimon-format/src/main/java/org/apache/paimon/format/avro/RowDataToAvroConverters.java b/paimon-format/src/main/java/org/apache/paimon/format/avro/RowDataToAvroConverters.java
index a89e8d070..76bcb380c 100644
--- a/paimon-format/src/main/java/org/apache/paimon/format/avro/RowDataToAvroConverters.java
+++ b/paimon-format/src/main/java/org/apache/paimon/format/avro/RowDataToAvroConverters.java
@@ -28,6 +28,8 @@ import org.apache.paimon.types.DataField;
import org.apache.paimon.types.DataType;
import org.apache.paimon.types.RowType;
+import org.apache.avro.LogicalType;
+import org.apache.avro.LogicalTypes;
import org.apache.avro.Schema;
import org.apache.avro.generic.GenericData;
import org.apache.avro.generic.GenericRecord;
@@ -142,7 +144,37 @@ public class RowDataToAvroConverters {
@Override
public Object convert(Schema schema, Object object) {
- return ((Timestamp) object).toInstant().toEpochMilli();
+ LogicalType logicalType = schema.getLogicalType();
+
+ if (logicalType instanceof LogicalTypes.TimestampMillis) {
+ return ((Timestamp) object).toInstant().toEpochMilli();
+ } else if (logicalType instanceof LogicalTypes.TimestampMicros) {
+ return ((Timestamp) object).toMicros();
+ } else {
+ throw new UnsupportedOperationException(
+ "Unsupported timestamp type: " + logicalType);
+ }
+ }
+ };
+ break;
+ case TIMESTAMP_WITH_LOCAL_TIME_ZONE:
+ converter =
+ new RowDataToAvroConverter() {
+ private static final long serialVersionUID = 1L;
+
+ @Override
+ public Object convert(Schema schema, Object object) {
+ LogicalType logicalType = schema.getLogicalType();
+
+ if (logicalType instanceof LogicalTypes.LocalTimestampMillis) {
+ return ((Timestamp) object).toInstant().toEpochMilli();
+ } else if (logicalType
+ instanceof LogicalTypes.LocalTimestampMicros) {
+ return ((Timestamp) object).toMicros();
+ } else {
+ throw new UnsupportedOperationException(
+ "Unsupported timestamp type: " + logicalType);
+ }
}
};
break;
diff --git a/paimon-format/src/test/java/org/apache/paimon/format/avro/AvroBulkFormatTest.java b/paimon-format/src/test/java/org/apache/paimon/format/avro/AvroBulkFormatTest.java
index 054a3369b..b73cde5cc 100644
--- a/paimon-format/src/test/java/org/apache/paimon/format/avro/AvroBulkFormatTest.java
+++ b/paimon-format/src/test/java/org/apache/paimon/format/avro/AvroBulkFormatTest.java
@@ -21,6 +21,7 @@ package org.apache.paimon.format.avro;
import org.apache.paimon.data.BinaryString;
import org.apache.paimon.data.GenericRow;
import org.apache.paimon.data.InternalRow;
+import org.apache.paimon.data.Timestamp;
import org.apache.paimon.fs.Path;
import org.apache.paimon.fs.local.LocalFileIO;
import org.apache.paimon.reader.RecordReader;
@@ -49,35 +50,63 @@ import static org.assertj.core.api.Assertions.assertThat;
/** Tests for {@link AbstractAvroBulkFormat}. */
class AvroBulkFormatTest {
+ private static final long TIMESTAMP = System.currentTimeMillis();
+
private static final List<InternalRow> TEST_DATA =
Arrays.asList(
// -------- batch 0, block start 232 --------
GenericRow.of(
BinaryString.fromString("AvroBulk"),
- BinaryString.fromString("FormatTest")),
+ BinaryString.fromString("FormatTest"),
+ Timestamp.fromEpochMillis(TIMESTAMP),
+ Timestamp.fromMicros(TIMESTAMP * 1000 + 123),
+ Timestamp.fromEpochMillis(TIMESTAMP),
+ Timestamp.fromMicros(TIMESTAMP * 1000 + 123)),
GenericRow.of(
- BinaryString.fromString("Apache"), BinaryString.fromString("Paimon")),
+ BinaryString.fromString("Apache"),
+ BinaryString.fromString("Paimon"),
+ Timestamp.fromEpochMillis(TIMESTAMP),
+ Timestamp.fromMicros(TIMESTAMP * 1000 + 123),
+ Timestamp.fromEpochMillis(TIMESTAMP),
+ Timestamp.fromMicros(TIMESTAMP * 1000 + 123)),
GenericRow.of(
BinaryString.fromString(
"永和九年,岁在癸丑,暮春之初,会于会稽山阴之兰亭,修禊事也。群贤毕至,少"
+ "长咸集。此地有崇山峻岭,茂林修竹,又有清流激湍,映带左右。引"
+ "以为流觞曲水,列坐其次。虽无丝竹管弦之盛,一觞一咏,亦足以畅"
+ "叙幽情。"),
- BinaryString.fromString("")),
- // -------- batch 1, block start 593 --------
+ BinaryString.fromString(""),
+ Timestamp.fromEpochMillis(TIMESTAMP),
+ Timestamp.fromMicros(TIMESTAMP * 1000 + 123),
+ Timestamp.fromEpochMillis(TIMESTAMP),
+ Timestamp.fromMicros(TIMESTAMP * 1000 + 123)),
+ // -------- batch 1, block start 689 --------
GenericRow.of(
- BinaryString.fromString("File"), BinaryString.fromString("Format")),
+ BinaryString.fromString("File"),
+ BinaryString.fromString("Format"),
+ Timestamp.fromEpochMillis(TIMESTAMP),
+ Timestamp.fromMicros(TIMESTAMP * 1000 + 123),
+ Timestamp.fromEpochMillis(TIMESTAMP),
+ Timestamp.fromMicros(TIMESTAMP * 1000 + 123)),
GenericRow.of(
null,
BinaryString.fromString(
- "This is a string with English, 中文 and even 🍎🍌🍑🥝🍍🥭🍐")),
- // -------- batch 2, block start 705 --------
+ "This is a string with English, 中文 and even 🍎🍌🍑🥝🍍🥭🍐"),
+ Timestamp.fromEpochMillis(TIMESTAMP),
+ Timestamp.fromMicros(TIMESTAMP * 1000 + 123),
+ Timestamp.fromEpochMillis(TIMESTAMP),
+ Timestamp.fromMicros(TIMESTAMP * 1000 + 123)),
+ // -------- batch 2, block start 1147 --------
GenericRow.of(
BinaryString.fromString("block with"),
- BinaryString.fromString("only one record"))
- // -------- file length 752 --------
+ BinaryString.fromString("only one record"),
+ Timestamp.fromEpochMillis(TIMESTAMP),
+ Timestamp.fromMicros(TIMESTAMP * 1000 + 123),
+ Timestamp.fromEpochMillis(TIMESTAMP),
+ Timestamp.fromMicros(TIMESTAMP * 1000 + 123))
+ // -------- file length 1323 --------
);
- private static final List<Long> BLOCK_STARTS = Arrays.asList(233L, 595L, 707L);
+ private static final List<Long> BLOCK_STARTS = Arrays.asList(689L, 1147L, 1323L);
private File tmpFile;
diff --git a/paimon-format/src/test/java/org/apache/paimon/format/avro/AvroBulkFormatTestUtils.java b/paimon-format/src/test/java/org/apache/paimon/format/avro/AvroBulkFormatTestUtils.java
index a8901949c..b68a6379b 100644
--- a/paimon-format/src/test/java/org/apache/paimon/format/avro/AvroBulkFormatTestUtils.java
+++ b/paimon-format/src/test/java/org/apache/paimon/format/avro/AvroBulkFormatTestUtils.java
@@ -35,8 +35,22 @@ public class AvroBulkFormatTestUtils {
(RowType)
RowType.builder()
.fields(
- new DataType[] {DataTypes.STRING(), DataTypes.STRING()},
- new String[] {"a", "b"})
+ new DataType[] {
+ DataTypes.STRING(),
+ DataTypes.STRING(),
+ DataTypes.TIMESTAMP(3),
+ DataTypes.TIMESTAMP(6),
+ DataTypes.TIMESTAMP_WITH_LOCAL_TIME_ZONE(3),
+ DataTypes.TIMESTAMP_WITH_LOCAL_TIME_ZONE(6),
+ },
+ new String[] {
+ "a",
+ "b",
+ "timestamp_millis",
+ "timestamp_micros",
+ "local_timestamp_millis",
+ "local_timestamp_micros"
+ })
.build()
.notNull();
diff --git a/paimon-format/src/test/java/org/apache/paimon/format/avro/AvroFileFormatTest.java b/paimon-format/src/test/java/org/apache/paimon/format/avro/AvroFileFormatTest.java
index 525f2778b..0aa15f7f2 100644
--- a/paimon-format/src/test/java/org/apache/paimon/format/avro/AvroFileFormatTest.java
+++ b/paimon-format/src/test/java/org/apache/paimon/format/avro/AvroFileFormatTest.java
@@ -23,7 +23,6 @@ import org.apache.paimon.types.DataField;
import org.apache.paimon.types.DataTypes;
import org.apache.paimon.types.RowType;
-import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
@@ -57,6 +56,11 @@ public class AvroFileFormatTest {
dataFields.add(new DataField(index++, "timestamp_type", DataTypes.TIMESTAMP(3)));
dataFields.add(new DataField(index++, "date_type", DataTypes.DATE()));
dataFields.add(new DataField(index++, "decimal_type", DataTypes.DECIMAL(10, 3)));
+ dataFields.add(
+ new DataField(
+ index++,
+ "local_timestamp_type",
+ DataTypes.TIMESTAMP_WITH_LOCAL_TIME_ZONE(3)));
RowType rowType = new RowType(dataFields);
fileFormat.validateDataFields(rowType);
@@ -81,15 +85,4 @@ public class AvroFileFormatTest {
RowType rowType = new RowType(dataFields);
fileFormat.validateDataFields(rowType);
}
-
- @Test
- public void testUnsupportedDataTypes() {
- ArrayList<DataField> dataFields = new ArrayList<>();
- int index = 0;
- dataFields.add(new DataField(index++, "timestamp_type", DataTypes.TIMESTAMP(6)));
-
- RowType rowType = new RowType(dataFields);
- Assertions.assertThrows(
- IllegalArgumentException.class, () -> fileFormat.validateDataFields(rowType));
- }
}
diff --git a/paimon-format/src/test/java/org/apache/paimon/format/avro/AvroToRowDataConvertersTest.java b/paimon-format/src/test/java/org/apache/paimon/format/avro/AvroToRowDataConvertersTest.java
new file mode 100644
index 000000000..31cac15a3
--- /dev/null
+++ b/paimon-format/src/test/java/org/apache/paimon/format/avro/AvroToRowDataConvertersTest.java
@@ -0,0 +1,47 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.paimon.format.avro;
+
+import org.apache.paimon.data.Timestamp;
+
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+
+import java.time.Instant;
+
+/** Test for avro to row data converters. */
+public class AvroToRowDataConvertersTest {
+ private static final Timestamp NOW = Timestamp.now();
+ private static final long TS_MILLIS = NOW.getMillisecond();
+ private static final long TS_MICROS = NOW.toMicros() + 123L;
+ private static final Timestamp NOW_MICROS = Timestamp.fromMicros(TS_MICROS);
+ private static final Instant INSTANT = Instant.ofEpochMilli(TS_MILLIS);
+
+ @Test
+ public void testConvertToTimestamp() {
+ Assertions.assertEquals(NOW, AvroToRowDataConverters.convertToTimestamp(TS_MILLIS, 3));
+
+ Assertions.assertEquals(
+ NOW_MICROS, AvroToRowDataConverters.convertToTimestamp(TS_MICROS, 6));
+
+ Assertions.assertEquals(NOW, AvroToRowDataConverters.convertToTimestamp(INSTANT, 3));
+
+ Assertions.assertEquals(NOW, AvroToRowDataConverters.convertToTimestamp(INSTANT, 6));
+ }
+}
diff --git a/paimon-format/src/test/java/org/apache/paimon/format/avro/RowDataToAvroConvertersTest.java b/paimon-format/src/test/java/org/apache/paimon/format/avro/RowDataToAvroConvertersTest.java
new file mode 100644
index 000000000..9b38176aa
--- /dev/null
+++ b/paimon-format/src/test/java/org/apache/paimon/format/avro/RowDataToAvroConvertersTest.java
@@ -0,0 +1,57 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.paimon.format.avro;
+
+import org.apache.paimon.data.Timestamp;
+import org.apache.paimon.types.DataTypes;
+import org.apache.paimon.types.LocalZonedTimestampType;
+
+import org.apache.avro.Schema;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+
+import java.time.LocalDate;
+import java.time.LocalTime;
+import java.time.OffsetDateTime;
+import java.time.ZoneOffset;
+
+/** Test for row data to avro converters. */
+public class RowDataToAvroConvertersTest {
+
+ @Test
+ public void testTimestampWithTimeType() {
+ LocalZonedTimestampType zonedTimestampType = DataTypes.TIMESTAMP_WITH_LOCAL_TIME_ZONE(3);
+ RowDataToAvroConverters.RowDataToAvroConverter rowDataToAvroConverter =
+ RowDataToAvroConverters.createConverter(zonedTimestampType);
+
+ OffsetDateTime offsetDateTime =
+ OffsetDateTime.of(LocalDate.of(2023, 1, 1), LocalTime.of(0, 0, 0), ZoneOffset.UTC);
+
+ Schema schema = AvroSchemaConverter.convertToSchema(zonedTimestampType);
+ long timestamp = offsetDateTime.toInstant().toEpochMilli();
+ Timestamp millis = Timestamp.fromEpochMillis(timestamp);
+ Object converted = rowDataToAvroConverter.convert(schema, millis);
+
+ AvroToRowDataConverters.AvroToRowDataConverter avroToRowDataConverter =
+ AvroToRowDataConverters.createConverter(zonedTimestampType);
+
+ Object original = avroToRowDataConverter.convert(converted);
+ Assertions.assertEquals(millis, original);
+ }
+}
diff --git a/paimon-hive/paimon-hive-common/src/main/java/org/apache/paimon/hive/HiveTypeUtils.java b/paimon-hive/paimon-hive-common/src/main/java/org/apache/paimon/hive/HiveTypeUtils.java
index b2cdf6e75..da2bffc0d 100644
--- a/paimon-hive/paimon-hive-common/src/main/java/org/apache/paimon/hive/HiveTypeUtils.java
+++ b/paimon-hive/paimon-hive-common/src/main/java/org/apache/paimon/hive/HiveTypeUtils.java
@@ -89,6 +89,7 @@ public class HiveTypeUtils {
case DATE:
return TypeInfoFactory.dateTypeInfo;
case TIMESTAMP_WITHOUT_TIME_ZONE:
+ case TIMESTAMP_WITH_LOCAL_TIME_ZONE:
return TypeInfoFactory.timestampTypeInfo;
case ARRAY:
ArrayType arrayType = (ArrayType) logicalType;
diff --git a/paimon-hive/paimon-hive-common/src/test/java/org/apache/paimon/hive/HiveTypeUtilsTest.java b/paimon-hive/paimon-hive-common/src/test/java/org/apache/paimon/hive/HiveTypeUtilsTest.java
index 757b9d17a..ff5b1e9b3 100644
--- a/paimon-hive/paimon-hive-common/src/test/java/org/apache/paimon/hive/HiveTypeUtilsTest.java
+++ b/paimon-hive/paimon-hive-common/src/test/java/org/apache/paimon/hive/HiveTypeUtilsTest.java
@@ -27,7 +27,6 @@ import org.apache.hadoop.hive.serde2.typeinfo.TypeInfo;
import org.junit.jupiter.api.Test;
import static org.assertj.core.api.Assertions.assertThat;
-import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
/** Test for {@link HiveTypeUtils}. */
public class HiveTypeUtilsTest {
@@ -95,12 +94,8 @@ public class HiveTypeUtilsTest {
new DataField(1, "name", new VarCharType(Integer.MAX_VALUE))));
assertThat(rowTypeInfo.getTypeName()).isEqualTo("struct<id:int,name:string>");
- assertThatExceptionOfType(UnsupportedOperationException.class)
- .isThrownBy(
- () ->
- HiveTypeUtils.logicalTypeToTypeInfo(
- DataTypes.TIMESTAMP_WITH_LOCAL_TIME_ZONE())
- .getTypeName())
- .withMessage("Unsupported logical type TIMESTAMP(6) WITH LOCAL TIME ZONE");
+ TypeInfo timestampWithLocalZoneTypeInfo =
+ HiveTypeUtils.logicalTypeToTypeInfo(DataTypes.TIMESTAMP_WITH_LOCAL_TIME_ZONE());
+ assertThat(timestampWithLocalZoneTypeInfo.getTypeName()).isEqualTo("timestamp");
}
}