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

difin 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 e14df633406 HIVE-29699: Iceberg: Add support for the Unknown data type 
(#6588)
e14df633406 is described below

commit e14df63340649c48beadd65498120bf948c5cf2a
Author: Dmitriy Fingerman <[email protected]>
AuthorDate: Fri Jul 10 18:41:43 2026 -0400

    HIVE-29699: Iceberg: Add support for the Unknown data type (#6588)
    
    * HIVE-29699: Iceberg: Add support for the Unknown data type
    
    ---------
    
    Co-authored-by: Dmitriy Fingerman <[email protected]>
    Co-authored-by: Dmitriy Fingerman <[email protected]>
---
 .../apache/iceberg/hive/HiveSchemaConverter.java   |   2 +
 .../org/apache/iceberg/hive/HiveSchemaUtil.java    |   2 +
 .../apache/iceberg/hive/TestHiveSchemaUtil.java    |  30 +++
 .../org/apache/iceberg/mr/hive/Deserializer.java   |  10 +
 .../objectinspector/IcebergObjectInspector.java    |   3 +
 .../iceberg/mr/hive/vector/HiveValueConverter.java |   2 +
 .../mr/hive/TestHiveIcebergUnknownType.java        |  89 +++++++++
 .../TestIcebergObjectInspector.java                |  12 +-
 .../src/test/queries/positive/unknown_type.q       |  54 ++++++
 .../src/test/results/positive/unknown_type.q.out   | 214 +++++++++++++++++++++
 jdbc/src/java/org/apache/hive/jdbc/JdbcColumn.java |   4 +
 .../org/apache/hadoop/hive/ql/parse/HiveParser.g   |   9 +-
 .../hive/ql/exec/vector/VectorAssignRow.java       |   6 +
 .../hive/ql/exec/vector/VectorDeserializeRow.java  |   3 +
 .../hive/ql/exec/vector/VectorExtractRow.java      |   2 +
 .../hive/ql/exec/vector/VectorSerializeRow.java    |  11 ++
 .../hive/ql/exec/vector/VectorizationContext.java  |   2 +
 .../hive/ql/exec/vector/VectorizedBatchUtil.java   |   2 +
 .../expressions/VectorExpressionWriterFactory.java |  25 +++
 .../ql/io/parquet/convert/HiveSchemaConverter.java |   4 +-
 .../org/apache/hadoop/hive/ql/metadata/Table.java  |   2 +
 .../calcite/translator/TypeConverter.java          |   6 +
 .../hadoop/hive/ql/parse/BaseSemanticAnalyzer.java |   1 +
 .../hadoop/hive/ql/parse/SemanticAnalyzer.java     |   7 +-
 .../hadoop/hive/ql/stats/TestStatsUtils.java       |   1 +
 .../unknown_type_non_iceberg_table.q               |   1 +
 .../unknown_type_non_iceberg_table.q.out           |   5 +
 serde/if/serde.thrift                              |   1 +
 .../apache/hadoop/hive/serde/serdeConstants.java   |   1 +
 .../hadoop/hive/serde2/lazy/LazyFactory.java       |   5 +
 .../hive/serde2/lazybinary/LazyBinaryUtils.java    |   6 +
 .../serde2/objectinspector/ObjectInspector.java    |   2 +-
 .../objectinspector/ObjectInspectorFactory.java    |   4 +
 .../objectinspector/UnknownObjectInspector.java    |  46 +++++
 .../org/apache/hadoop/hive/serde2/thrift/Type.java |   8 +-
 .../hive/serde2/typeinfo/TypeInfoFactory.java      |   4 +
 .../hadoop/hive/serde2/typeinfo/TypeInfoUtils.java |  44 +++++
 .../hive/serde2/typeinfo/UnknownTypeInfo.java      |  69 +++++++
 .../hive/serde2/typeinfo/TestUnknownTypeInfo.java  |  79 ++++++++
 service-rpc/if/TCLIService.thrift                  |   7 +-
 .../service/rpc/thrift/TCLIServiceConstants.java   |   2 +
 .../apache/hive/service/rpc/thrift/TTypeId.java    |   5 +-
 service-rpc/src/gen/thrift/gen-php/TTypeId.php     |   3 +
 .../org/apache/hive/service/cli/ColumnValue.java   |   2 +
 .../apache/hadoop/hive/metastore/ColumnType.java   |   2 +
 45 files changed, 789 insertions(+), 10 deletions(-)

diff --git 
a/iceberg/iceberg-catalog/src/main/java/org/apache/iceberg/hive/HiveSchemaConverter.java
 
b/iceberg/iceberg-catalog/src/main/java/org/apache/iceberg/hive/HiveSchemaConverter.java
index 07631516cd4..e7a0ad91571 100644
--- 
a/iceberg/iceberg-catalog/src/main/java/org/apache/iceberg/hive/HiveSchemaConverter.java
+++ 
b/iceberg/iceberg-catalog/src/main/java/org/apache/iceberg/hive/HiveSchemaConverter.java
@@ -186,6 +186,8 @@ Type convertType(TypeInfo typeInfo, String defaultValue, 
boolean shouldAddInitia
         return Types.ListType.ofOptional(listId, listType);
       case VARIANT:
         return Types.VariantType.get();
+      case UNKNOWN:
+        return Types.UnknownType.get();
       default:
         throw new IllegalArgumentException("Unknown type " + 
typeInfo.getCategory());
     }
diff --git 
a/iceberg/iceberg-catalog/src/main/java/org/apache/iceberg/hive/HiveSchemaUtil.java
 
b/iceberg/iceberg-catalog/src/main/java/org/apache/iceberg/hive/HiveSchemaUtil.java
index 3f18deab4ea..306fb343e22 100644
--- 
a/iceberg/iceberg-catalog/src/main/java/org/apache/iceberg/hive/HiveSchemaUtil.java
+++ 
b/iceberg/iceberg-catalog/src/main/java/org/apache/iceberg/hive/HiveSchemaUtil.java
@@ -411,6 +411,8 @@ public static String convertToTypeString(Type type) {
         return String.format("map<%s,%s>", convert(mapType.keyType()), 
convert(mapType.valueType()));
       case VARIANT:
         return "variant";
+      case UNKNOWN:
+        return "unknown";
       default:
         throw new UnsupportedOperationException(type + " is not supported");
     }
diff --git 
a/iceberg/iceberg-catalog/src/test/java/org/apache/iceberg/hive/TestHiveSchemaUtil.java
 
b/iceberg/iceberg-catalog/src/test/java/org/apache/iceberg/hive/TestHiveSchemaUtil.java
index 6234a6f8db5..838344ac62d 100644
--- 
a/iceberg/iceberg-catalog/src/test/java/org/apache/iceberg/hive/TestHiveSchemaUtil.java
+++ 
b/iceberg/iceberg-catalog/src/test/java/org/apache/iceberg/hive/TestHiveSchemaUtil.java
@@ -26,6 +26,7 @@
 import org.apache.hadoop.hive.metastore.api.FieldSchema;
 import org.apache.hadoop.hive.serde.serdeConstants;
 import org.apache.hadoop.hive.serde2.typeinfo.TypeInfo;
+import org.apache.hadoop.hive.serde2.typeinfo.TypeInfoFactory;
 import org.apache.hadoop.hive.serde2.typeinfo.TypeInfoUtils;
 import org.apache.iceberg.Schema;
 import org.apache.iceberg.relocated.com.google.common.collect.ImmutableList;
@@ -171,6 +172,35 @@ public void testConversionWithoutLastComment() {
     assertThat(schema.asStruct()).isEqualTo(expected.asStruct());
   }
 
+  @Test
+  public void testUnknownTypeConvertToIcebergSchema() {
+    List<FieldSchema> hiveSchema = ImmutableList.of(
+        new FieldSchema("id", serdeConstants.INT_TYPE_NAME, null),
+        new FieldSchema("placeholder", serdeConstants.UNKNOWN_TYPE_NAME, 
"placeholder comment"));
+    Schema expected = new Schema(
+        optional(1, "id", Types.IntegerType.get()),
+        optional(2, "placeholder", Types.UnknownType.get(), "placeholder 
comment"));
+
+    
assertThat(HiveSchemaUtil.convert(hiveSchema).asStruct()).isEqualTo(expected.asStruct());
+  }
+
+  @Test
+  public void testUnknownTypeConvertToHiveSchema() {
+    Schema icebergSchema = new Schema(
+        optional(1, "id", Types.IntegerType.get()),
+        optional(2, "placeholder", Types.UnknownType.get(), "placeholder 
comment"));
+    List<FieldSchema> expected = ImmutableList.of(
+        new FieldSchema("id", serdeConstants.INT_TYPE_NAME, null),
+        new FieldSchema("placeholder", serdeConstants.UNKNOWN_TYPE_NAME, 
"placeholder comment"));
+
+    assertThat(HiveSchemaUtil.convert(icebergSchema)).isEqualTo(expected);
+  }
+
+  @Test
+  public void testUnknownTypeAndTypeInfoConvert() {
+    checkConvert(TypeInfoFactory.getUnknownTypeInfo(), 
Types.UnknownType.get());
+  }
+
   protected List<FieldSchema> getSupportedFieldSchemas() {
     List<FieldSchema> fields = Lists.newArrayList();
     fields.add(new FieldSchema("c_float", serdeConstants.FLOAT_TYPE_NAME, 
"float comment"));
diff --git 
a/iceberg/iceberg-handler/src/main/java/org/apache/iceberg/mr/hive/Deserializer.java
 
b/iceberg/iceberg-handler/src/main/java/org/apache/iceberg/mr/hive/Deserializer.java
index dbafe176f6b..95bc7fd650f 100644
--- 
a/iceberg/iceberg-handler/src/main/java/org/apache/iceberg/mr/hive/Deserializer.java
+++ 
b/iceberg/iceberg-handler/src/main/java/org/apache/iceberg/mr/hive/Deserializer.java
@@ -37,6 +37,7 @@
 import org.apache.iceberg.relocated.com.google.common.collect.Lists;
 import org.apache.iceberg.relocated.com.google.common.collect.Maps;
 import org.apache.iceberg.schema.SchemaWithPartnerVisitor;
+import org.apache.iceberg.types.Type;
 import org.apache.iceberg.types.Type.PrimitiveType;
 import org.apache.iceberg.types.Types;
 import org.apache.iceberg.types.Types.ListType;
@@ -110,6 +111,15 @@ public FieldDeserializer field(NestedField field, 
ObjectInspectorPair pair, Fiel
 
     @Override
     public FieldDeserializer primitive(PrimitiveType type, ObjectInspectorPair 
pair) {
+      if (type.typeId() == Type.TypeID.UNKNOWN) {
+        return o -> {
+          if (o != null) {
+            throw new IllegalArgumentException("UNKNOWN type columns only 
accept NULL values");
+          }
+          return null;
+        };
+      }
+
       return o -> {
         if (o == null) {
           return null;
diff --git 
a/iceberg/iceberg-handler/src/main/java/org/apache/iceberg/mr/hive/serde/objectinspector/IcebergObjectInspector.java
 
b/iceberg/iceberg-handler/src/main/java/org/apache/iceberg/mr/hive/serde/objectinspector/IcebergObjectInspector.java
index 6f4b563fa5f..bf5158d62a5 100644
--- 
a/iceberg/iceberg-handler/src/main/java/org/apache/iceberg/mr/hive/serde/objectinspector/IcebergObjectInspector.java
+++ 
b/iceberg/iceberg-handler/src/main/java/org/apache/iceberg/mr/hive/serde/objectinspector/IcebergObjectInspector.java
@@ -23,6 +23,7 @@
 import javax.annotation.Nullable;
 import org.apache.hadoop.hive.serde2.objectinspector.ObjectInspector;
 import org.apache.hadoop.hive.serde2.objectinspector.ObjectInspectorFactory;
+import org.apache.hadoop.hive.serde2.objectinspector.UnknownObjectInspector;
 import 
org.apache.hadoop.hive.serde2.objectinspector.primitive.PrimitiveObjectInspectorFactory;
 import org.apache.hadoop.hive.serde2.typeinfo.PrimitiveTypeInfo;
 import org.apache.hadoop.hive.serde2.typeinfo.TypeInfoFactory;
@@ -106,6 +107,8 @@ public ObjectInspector primitive(Type.PrimitiveType 
primitiveType) {
             IcebergTimestampObjectInspectorHive3.get(9);
       case TIME:
         return IcebergTimeObjectInspector.get();
+      case UNKNOWN:
+        return UnknownObjectInspector.get();
       default:
         throw new IllegalArgumentException(primitiveType.typeId() + " type is 
not supported");
     }
diff --git 
a/iceberg/iceberg-handler/src/main/java/org/apache/iceberg/mr/hive/vector/HiveValueConverter.java
 
b/iceberg/iceberg-handler/src/main/java/org/apache/iceberg/mr/hive/vector/HiveValueConverter.java
index 0804a7abe9f..e5e61c46606 100644
--- 
a/iceberg/iceberg-handler/src/main/java/org/apache/iceberg/mr/hive/vector/HiveValueConverter.java
+++ 
b/iceberg/iceberg-handler/src/main/java/org/apache/iceberg/mr/hive/vector/HiveValueConverter.java
@@ -104,6 +104,8 @@ public static Object convert(Type type, Object object) {
       case DOUBLE:
       case STRING:
         return object;
+      case UNKNOWN:
+        return null;
       default:
         throw new UnsupportedOperationException("Not a supported type: " + 
type);
     }
diff --git 
a/iceberg/iceberg-handler/src/test/java/org/apache/iceberg/mr/hive/TestHiveIcebergUnknownType.java
 
b/iceberg/iceberg-handler/src/test/java/org/apache/iceberg/mr/hive/TestHiveIcebergUnknownType.java
new file mode 100644
index 00000000000..fd2647964c6
--- /dev/null
+++ 
b/iceberg/iceberg-handler/src/test/java/org/apache/iceberg/mr/hive/TestHiveIcebergUnknownType.java
@@ -0,0 +1,89 @@
+/*
+ * 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.iceberg.mr.hive;
+
+import java.util.Collection;
+import java.util.List;
+import org.apache.iceberg.FileFormat;
+import org.apache.iceberg.Schema;
+import org.apache.iceberg.catalog.TableIdentifier;
+import org.apache.iceberg.mr.hive.test.TestTables.TestTableType;
+import org.apache.iceberg.relocated.com.google.common.collect.ImmutableList;
+import org.apache.iceberg.relocated.com.google.common.collect.ImmutableMap;
+import org.apache.iceberg.types.Types;
+import org.junit.Assert;
+import org.junit.Test;
+import org.junit.runners.Parameterized.Parameters;
+
+import static org.apache.iceberg.types.Types.NestedField.optional;
+
+public class TestHiveIcebergUnknownType extends 
HiveIcebergStorageHandlerWithEngineBase {
+
+  @Parameters(name = "fileFormat={0}, catalog={1}, isVectorized={2}, 
formatVersion={3}")
+  public static Collection<Object[]> parameters() {
+    return HiveIcebergStorageHandlerWithEngineBase.getParameters(p ->
+        p.fileFormat() == FileFormat.PARQUET &&
+        p.testTableType() == TestTableType.HIVE_CATALOG &&
+        p.formatVersion() == 3);
+  }
+
+  @Test
+  public void testUnknownTypeReadWrite() {
+    Schema schema = new Schema(
+        optional(1, "id", Types.IntegerType.get()),
+        optional(2, "placeholder", Types.UnknownType.get()));
+
+    TableIdentifier table = TableIdentifier.of("default", 
"unknown_type_basic");
+
+    testTables.createTable(shell, table.name(),
+        schema, org.apache.iceberg.PartitionSpec.unpartitioned(), fileFormat, 
ImmutableList.of(), formatVersion,
+        ImmutableMap.of());
+
+    shell.executeStatement(
+        String.format("INSERT INTO %s VALUES (1, NULL), (2, NULL)", table));
+
+    List<Object[]> rows = shell.executeStatement(
+        String.format("SELECT id, placeholder FROM %s ORDER BY id", table));
+
+    Assert.assertEquals(2, rows.size());
+    Assert.assertEquals(1, rows.get(0)[0]);
+    Assert.assertNull(rows.get(0)[1]);
+    Assert.assertEquals(2, rows.get(1)[0]);
+    Assert.assertNull(rows.get(1)[1]);
+  }
+
+  @Test
+  public void testUnknownTypeDdl() {
+    TableIdentifier table = TableIdentifier.of("default", "unknown_type_ddl");
+
+    shell.executeStatement(String.format(
+        "CREATE EXTERNAL TABLE %s (id INT, placeholder UNKNOWN) STORED BY 
ICEBERG " +
+            "TBLPROPERTIES ('format-version'='3')", table));
+
+    shell.executeStatement(String.format("INSERT INTO %s VALUES (1, NULL)", 
table));
+
+    List<Object[]> rows = shell.executeStatement(
+        String.format("SELECT id, placeholder FROM %s", table));
+
+    Assert.assertEquals(1, rows.size());
+    Assert.assertEquals(1, rows.get(0)[0]);
+    Assert.assertNull(rows.get(0)[1]);
+  }
+}
diff --git 
a/iceberg/iceberg-handler/src/test/java/org/apache/iceberg/mr/hive/serde/objectinspector/TestIcebergObjectInspector.java
 
b/iceberg/iceberg-handler/src/test/java/org/apache/iceberg/mr/hive/serde/objectinspector/TestIcebergObjectInspector.java
index a4f2dcc10f5..7e9a3eb6f47 100644
--- 
a/iceberg/iceberg-handler/src/test/java/org/apache/iceberg/mr/hive/serde/objectinspector/TestIcebergObjectInspector.java
+++ 
b/iceberg/iceberg-handler/src/test/java/org/apache/iceberg/mr/hive/serde/objectinspector/TestIcebergObjectInspector.java
@@ -23,6 +23,7 @@
 import org.apache.hadoop.hive.serde2.objectinspector.ObjectInspectorFactory;
 import org.apache.hadoop.hive.serde2.objectinspector.StructField;
 import org.apache.hadoop.hive.serde2.objectinspector.StructObjectInspector;
+import org.apache.hadoop.hive.serde2.objectinspector.UnknownObjectInspector;
 import 
org.apache.hadoop.hive.serde2.objectinspector.primitive.PrimitiveObjectInspectorFactory;
 import org.apache.hadoop.hive.serde2.typeinfo.PrimitiveTypeInfo;
 import org.apache.hadoop.hive.serde2.typeinfo.TypeInfoFactory;
@@ -32,6 +33,7 @@
 import org.junit.jupiter.api.Assertions;
 import org.junit.jupiter.api.Test;
 
+import static org.apache.iceberg.types.Types.NestedField.optional;
 import static org.apache.iceberg.types.Types.NestedField.required;
 
 
@@ -60,7 +62,8 @@ public class TestIcebergObjectInspector {
                   Types.NestedField.required(20, "nested_field", 
Types.StringType.get(), "nested field comment")),
                   "struct comment"
           ),
-          required(21, "time_field", Types.TimeType.get(), "time comment")
+          required(21, "time_field", Types.TimeType.get(), "time comment"),
+          optional(22, "unknown_field", Types.UnknownType.get(), "unknown 
comment")
   );
 
   @Test
@@ -205,6 +208,13 @@ public void testIcebergObjectInspector() {
     Assertions.assertEquals("time_field", timeField.getFieldName());
     Assertions.assertEquals("time comment", timeField.getFieldComment());
     Assertions.assertEquals(IcebergTimeObjectInspector.get(), 
timeField.getFieldObjectInspector());
+
+    // unknown
+    StructField unknownField = soi.getStructFieldRef("unknown_field");
+    Assertions.assertEquals(22, unknownField.getFieldID());
+    Assertions.assertEquals("unknown_field", unknownField.getFieldName());
+    Assertions.assertEquals("unknown comment", unknownField.getFieldComment());
+    Assertions.assertEquals(UnknownObjectInspector.get(), 
unknownField.getFieldObjectInspector());
   }
 
   private static ObjectInspector getPrimitiveObjectInspector(Class<?> clazz) {
diff --git a/iceberg/iceberg-handler/src/test/queries/positive/unknown_type.q 
b/iceberg/iceberg-handler/src/test/queries/positive/unknown_type.q
new file mode 100644
index 00000000000..60c9edad983
--- /dev/null
+++ b/iceberg/iceberg-handler/src/test/queries/positive/unknown_type.q
@@ -0,0 +1,54 @@
+-- SORT_QUERY_RESULTS
+-- Mask neededVirtualColumns due to non-strict order
+--! qt:replace:/(\s+neededVirtualColumns:\s)(.*)/$1#Masked#/
+-- Mask random uuid
+--! qt:replace:/(\s+uuid\s+)\S+(\s*)/$1#Masked#$2/
+-- Mask a random snapshot id
+--! qt:replace:/(\s+current-snapshot-id\s+)\S+(\s*)/$1#Masked#/
+-- Mask added file size
+--! qt:replace:/(\S\"added-files-size\\\":\\\")(\d+)(\\\")/$1#Masked#$3/
+-- Mask total file size
+--! qt:replace:/(\S\"total-files-size\\\":\\\")(\d+)(\\\")/$1#Masked#$3/
+-- Mask current-snapshot-timestamp-ms
+--! qt:replace:/(\s+current-snapshot-timestamp-ms\s+)\S+(\s*)/$1#Masked#$2/
+-- Mask removed file size
+--! qt:replace:/(\S\"removed-files-size\\\":\\\")(\d+)(\\\")/$1#Masked#$3/
+-- Mask iceberg version
+--! 
qt:replace:/(\S\"iceberg-version\\\":\\\")(\w+\s\w+\s\d+\.\d+\.\d+\s\(\w+\s\w+\))(\\\")/$1#Masked#$3/
+
+-- Create test table with unknown placeholder column
+CREATE EXTERNAL TABLE unknown_test_basic (
+    id INT,
+    placeholder UNKNOWN
+) STORED BY ICEBERG tblproperties('format-version'='3');
+
+-- Unknown columns are not stored; only NULL values are accepted
+INSERT INTO unknown_test_basic VALUES
+(1, NULL),
+(2, NULL);
+
+SELECT id, placeholder FROM unknown_test_basic ORDER BY id;
+
+-- Add another unknown column to an existing table
+ALTER TABLE unknown_test_basic ADD COLUMNS (extra UNKNOWN);
+
+INSERT INTO unknown_test_basic VALUES
+(3, NULL, NULL);
+
+SELECT id, placeholder, extra FROM unknown_test_basic ORDER BY id;
+
+-- CTLT preserves unknown column types from the source table
+CREATE TABLE unknown_test_ctlt LIKE unknown_test_basic STORED BY ICEBERG 
tblproperties('format-version'='3');
+
+DESC FORMATTED unknown_test_ctlt;
+
+INSERT INTO unknown_test_ctlt SELECT * FROM unknown_test_basic;
+
+SELECT id, placeholder, extra FROM unknown_test_ctlt ORDER BY id;
+
+-- CTAS preserves unknown column types from the query schema
+CREATE TABLE unknown_test_ctas STORED BY ICEBERG 
tblproperties('format-version'='3') AS SELECT * FROM unknown_test_basic;
+
+DESC FORMATTED unknown_test_ctas;
+
+SELECT id, placeholder, extra FROM unknown_test_ctas ORDER BY id;
diff --git 
a/iceberg/iceberg-handler/src/test/results/positive/unknown_type.q.out 
b/iceberg/iceberg-handler/src/test/results/positive/unknown_type.q.out
new file mode 100644
index 00000000000..faa92ff4836
--- /dev/null
+++ b/iceberg/iceberg-handler/src/test/results/positive/unknown_type.q.out
@@ -0,0 +1,214 @@
+PREHOOK: query: CREATE EXTERNAL TABLE unknown_test_basic (
+    id INT,
+    placeholder UNKNOWN
+) STORED BY ICEBERG tblproperties('format-version'='3')
+PREHOOK: type: CREATETABLE
+PREHOOK: Output: database:default
+PREHOOK: Output: default@unknown_test_basic
+POSTHOOK: query: CREATE EXTERNAL TABLE unknown_test_basic (
+    id INT,
+    placeholder UNKNOWN
+) STORED BY ICEBERG tblproperties('format-version'='3')
+POSTHOOK: type: CREATETABLE
+POSTHOOK: Output: database:default
+POSTHOOK: Output: default@unknown_test_basic
+PREHOOK: query: INSERT INTO unknown_test_basic VALUES
+(1, NULL),
+(2, NULL)
+PREHOOK: type: QUERY
+PREHOOK: Input: _dummy_database@_dummy_table
+PREHOOK: Output: default@unknown_test_basic
+POSTHOOK: query: INSERT INTO unknown_test_basic VALUES
+(1, NULL),
+(2, NULL)
+POSTHOOK: type: QUERY
+POSTHOOK: Input: _dummy_database@_dummy_table
+POSTHOOK: Output: default@unknown_test_basic
+PREHOOK: query: SELECT id, placeholder FROM unknown_test_basic ORDER BY id
+PREHOOK: type: QUERY
+PREHOOK: Input: default@unknown_test_basic
+PREHOOK: Output: hdfs://### HDFS PATH ###
+POSTHOOK: query: SELECT id, placeholder FROM unknown_test_basic ORDER BY id
+POSTHOOK: type: QUERY
+POSTHOOK: Input: default@unknown_test_basic
+POSTHOOK: Output: hdfs://### HDFS PATH ###
+1      NULL
+2      NULL
+PREHOOK: query: ALTER TABLE unknown_test_basic ADD COLUMNS (extra UNKNOWN)
+PREHOOK: type: ALTERTABLE_ADDCOLS
+PREHOOK: Input: default@unknown_test_basic
+PREHOOK: Output: default@unknown_test_basic
+POSTHOOK: query: ALTER TABLE unknown_test_basic ADD COLUMNS (extra UNKNOWN)
+POSTHOOK: type: ALTERTABLE_ADDCOLS
+POSTHOOK: Input: default@unknown_test_basic
+POSTHOOK: Output: default@unknown_test_basic
+PREHOOK: query: INSERT INTO unknown_test_basic VALUES
+(3, NULL, NULL)
+PREHOOK: type: QUERY
+PREHOOK: Input: _dummy_database@_dummy_table
+PREHOOK: Output: default@unknown_test_basic
+POSTHOOK: query: INSERT INTO unknown_test_basic VALUES
+(3, NULL, NULL)
+POSTHOOK: type: QUERY
+POSTHOOK: Input: _dummy_database@_dummy_table
+POSTHOOK: Output: default@unknown_test_basic
+PREHOOK: query: SELECT id, placeholder, extra FROM unknown_test_basic ORDER BY 
id
+PREHOOK: type: QUERY
+PREHOOK: Input: default@unknown_test_basic
+PREHOOK: Output: hdfs://### HDFS PATH ###
+POSTHOOK: query: SELECT id, placeholder, extra FROM unknown_test_basic ORDER 
BY id
+POSTHOOK: type: QUERY
+POSTHOOK: Input: default@unknown_test_basic
+POSTHOOK: Output: hdfs://### HDFS PATH ###
+1      NULL    NULL
+2      NULL    NULL
+3      NULL    NULL
+PREHOOK: query: CREATE TABLE unknown_test_ctlt LIKE unknown_test_basic STORED 
BY ICEBERG tblproperties('format-version'='3')
+PREHOOK: type: CREATETABLE
+PREHOOK: Output: database:default
+PREHOOK: Output: default@unknown_test_ctlt
+POSTHOOK: query: CREATE TABLE unknown_test_ctlt LIKE unknown_test_basic STORED 
BY ICEBERG tblproperties('format-version'='3')
+POSTHOOK: type: CREATETABLE
+POSTHOOK: Output: database:default
+POSTHOOK: Output: default@unknown_test_ctlt
+PREHOOK: query: DESC FORMATTED unknown_test_ctlt
+PREHOOK: type: DESCTABLE
+PREHOOK: Input: default@unknown_test_ctlt
+POSTHOOK: query: DESC FORMATTED unknown_test_ctlt
+POSTHOOK: type: DESCTABLE
+POSTHOOK: Input: default@unknown_test_ctlt
+# col_name             data_type               comment             
+id                     int                                         
+placeholder            unknown                                     
+extra                  unknown                                     
+                
+# Detailed Table Information            
+Database:              default                  
+#### A masked pattern was here ####
+Retention:             0                        
+#### A masked pattern was here ####
+Table Type:            EXTERNAL_TABLE           
+Table Parameters:               
+       COLUMN_STATS_ACCURATE   
{\"BASIC_STATS\":\"true\",\"COLUMN_STATS\":{\"extra\":\"true\",\"id\":\"true\",\"placeholder\":\"true\"}}
+       EXTERNAL                TRUE                
+       TRANSLATED_TO_EXTERNAL  TRUE                
+       bucketing_version       2                   
+       created_with_ctlt       true                
+       current-schema          
{\"type\":\"struct\",\"schema-id\":0,\"fields\":[{\"id\":1,\"name\":\"id\",\"required\":false,\"type\":\"int\"},{\"id\":2,\"name\":\"placeholder\",\"required\":false,\"type\":\"unknown\"},{\"id\":3,\"name\":\"extra\",\"required\":false,\"type\":\"unknown\"}]}
+       format-version          3                   
+       metadata_location       hdfs://### HDFS PATH ###
+       numFiles                0                   
+       numRows                 0                   
+       parquet.compression     zstd                
+       rawDataSize             0                   
+       snapshot-count          0                   
+       storage_handler         
org.apache.iceberg.mr.hive.HiveIcebergStorageHandler
+       table_type              ICEBERG             
+       totalSize               #Masked#
+#### A masked pattern was here ####
+       uuid                    #Masked#
+       write.delete.mode       merge-on-read       
+       write.merge.mode        merge-on-read       
+       write.metadata.delete-after-commit.enabled      true                
+       write.update.mode       merge-on-read       
+                
+# Storage Information           
+SerDe Library:         org.apache.iceberg.mr.hive.HiveIcebergSerDe      
+InputFormat:           org.apache.iceberg.mr.hive.HiveIcebergInputFormat       
 
+OutputFormat:          org.apache.iceberg.mr.hive.HiveIcebergOutputFormat      
 
+Compressed:            No                       
+Sort Columns:          []                       
+PREHOOK: query: INSERT INTO unknown_test_ctlt SELECT * FROM unknown_test_basic
+PREHOOK: type: QUERY
+PREHOOK: Input: default@unknown_test_basic
+PREHOOK: Output: default@unknown_test_ctlt
+POSTHOOK: query: INSERT INTO unknown_test_ctlt SELECT * FROM unknown_test_basic
+POSTHOOK: type: QUERY
+POSTHOOK: Input: default@unknown_test_basic
+POSTHOOK: Output: default@unknown_test_ctlt
+PREHOOK: query: SELECT id, placeholder, extra FROM unknown_test_ctlt ORDER BY 
id
+PREHOOK: type: QUERY
+PREHOOK: Input: default@unknown_test_ctlt
+PREHOOK: Output: hdfs://### HDFS PATH ###
+POSTHOOK: query: SELECT id, placeholder, extra FROM unknown_test_ctlt ORDER BY 
id
+POSTHOOK: type: QUERY
+POSTHOOK: Input: default@unknown_test_ctlt
+POSTHOOK: Output: hdfs://### HDFS PATH ###
+1      NULL    NULL
+2      NULL    NULL
+3      NULL    NULL
+PREHOOK: query: CREATE TABLE unknown_test_ctas STORED BY ICEBERG 
tblproperties('format-version'='3') AS SELECT * FROM unknown_test_basic
+PREHOOK: type: CREATETABLE_AS_SELECT
+PREHOOK: Input: default@unknown_test_basic
+PREHOOK: Output: database:default
+PREHOOK: Output: default@unknown_test_ctas
+PREHOOK: Output: hdfs://### HDFS PATH ###
+POSTHOOK: query: CREATE TABLE unknown_test_ctas STORED BY ICEBERG 
tblproperties('format-version'='3') AS SELECT * FROM unknown_test_basic
+POSTHOOK: type: CREATETABLE_AS_SELECT
+POSTHOOK: Input: default@unknown_test_basic
+POSTHOOK: Output: database:default
+POSTHOOK: Output: default@unknown_test_ctas
+POSTHOOK: Output: hdfs://### HDFS PATH ###
+POSTHOOK: Lineage: unknown_test_ctas.extra SIMPLE 
[(unknown_test_basic)unknown_test_basic.FieldSchema(name:extra, type:unknown, 
comment:null), ]
+POSTHOOK: Lineage: unknown_test_ctas.id SIMPLE 
[(unknown_test_basic)unknown_test_basic.FieldSchema(name:id, type:int, 
comment:null), ]
+POSTHOOK: Lineage: unknown_test_ctas.placeholder SIMPLE 
[(unknown_test_basic)unknown_test_basic.FieldSchema(name:placeholder, 
type:unknown, comment:null), ]
+PREHOOK: query: DESC FORMATTED unknown_test_ctas
+PREHOOK: type: DESCTABLE
+PREHOOK: Input: default@unknown_test_ctas
+POSTHOOK: query: DESC FORMATTED unknown_test_ctas
+POSTHOOK: type: DESCTABLE
+POSTHOOK: Input: default@unknown_test_ctas
+# col_name             data_type               comment             
+id                     int                                         
+placeholder            unknown                                     
+extra                  unknown                                     
+                
+# Detailed Table Information            
+Database:              default                  
+#### A masked pattern was here ####
+Retention:             0                        
+#### A masked pattern was here ####
+Table Type:            EXTERNAL_TABLE           
+Table Parameters:               
+       COLUMN_STATS_ACCURATE   {\"BASIC_STATS\":\"true\"}
+       EXTERNAL                TRUE                
+       bucketing_version       2                   
+       current-schema          
{\"type\":\"struct\",\"schema-id\":0,\"fields\":[{\"id\":1,\"name\":\"id\",\"required\":false,\"type\":\"int\"},{\"id\":2,\"name\":\"placeholder\",\"required\":false,\"type\":\"unknown\"},{\"id\":3,\"name\":\"extra\",\"required\":false,\"type\":\"unknown\"}]}
+       current-snapshot-id     #Masked#
+       current-snapshot-summary        
{\"added-data-files\":\"1\",\"added-records\":\"3\",\"added-files-size\":\"#Masked#\",\"changed-partition-count\":\"1\",\"total-records\":\"3\",\"total-files-size\":\"#Masked#\",\"total-data-files\":\"1\",\"total-delete-files\":\"0\",\"total-position-deletes\":\"0\",\"total-equality-deletes\":\"0\",\"iceberg-version\":\"#Masked#\"}
+       current-snapshot-timestamp-ms   #Masked#       
+       format-version          3                   
+       metadata_location       hdfs://### HDFS PATH ###
+       numFiles                1                   
+       numRows                 3                   
+       parquet.compression     zstd                
+       previous_metadata_location      hdfs://### HDFS PATH ###
+       serialization.format    1                   
+       snapshot-count          1                   
+       storage_handler         
org.apache.iceberg.mr.hive.HiveIcebergStorageHandler
+       table_type              ICEBERG             
+       totalSize               #Masked#
+#### A masked pattern was here ####
+       uuid                    #Masked#
+       write.delete.mode       merge-on-read       
+       write.merge.mode        merge-on-read       
+       write.metadata.delete-after-commit.enabled      true                
+       write.update.mode       merge-on-read       
+                
+# Storage Information           
+SerDe Library:         org.apache.iceberg.mr.hive.HiveIcebergSerDe      
+InputFormat:           org.apache.iceberg.mr.hive.HiveIcebergInputFormat       
 
+OutputFormat:          org.apache.iceberg.mr.hive.HiveIcebergOutputFormat      
 
+Compressed:            No                       
+Sort Columns:          []                       
+PREHOOK: query: SELECT id, placeholder, extra FROM unknown_test_ctas ORDER BY 
id
+PREHOOK: type: QUERY
+PREHOOK: Input: default@unknown_test_ctas
+PREHOOK: Output: hdfs://### HDFS PATH ###
+POSTHOOK: query: SELECT id, placeholder, extra FROM unknown_test_ctas ORDER BY 
id
+POSTHOOK: type: QUERY
+POSTHOOK: Input: default@unknown_test_ctas
+POSTHOOK: Output: hdfs://### HDFS PATH ###
+1      NULL    NULL
+2      NULL    NULL
+3      NULL    NULL
diff --git a/jdbc/src/java/org/apache/hive/jdbc/JdbcColumn.java 
b/jdbc/src/java/org/apache/hive/jdbc/JdbcColumn.java
index d3b7098fa7f..2747ae751d4 100644
--- a/jdbc/src/java/org/apache/hive/jdbc/JdbcColumn.java
+++ b/jdbc/src/java/org/apache/hive/jdbc/JdbcColumn.java
@@ -168,6 +168,8 @@ static Type typeStringToHiveType(String type) throws 
SQLException {
       /* fall through */
     case "null":
       return Type.NULL_TYPE;
+    case serdeConstants.UNKNOWN_TYPE_NAME:
+      return Type.UNKNOWN_TYPE;
     default:
       throw new SQLException("Unrecognized column type: " + type);
     }
@@ -224,6 +226,8 @@ static String getColumnTypeName(String type) throws 
SQLException {
         /* fall through */
       case "null":
         return serdeConstants.VOID_TYPE_NAME;
+      case serdeConstants.UNKNOWN_TYPE_NAME:
+        return serdeConstants.UNKNOWN_TYPE_NAME;
       case "map":
         return serdeConstants.MAP_TYPE_NAME;
       case "array":
diff --git a/parser/src/java/org/apache/hadoop/hive/ql/parse/HiveParser.g 
b/parser/src/java/org/apache/hadoop/hive/ql/parse/HiveParser.g
index 0033aa3251c..fba3f297221 100644
--- a/parser/src/java/org/apache/hadoop/hive/ql/parse/HiveParser.g
+++ b/parser/src/java/org/apache/hadoop/hive/ql/parse/HiveParser.g
@@ -2420,7 +2420,8 @@ type
     | structType
     | mapType
     | unionType
-    | variantType;
+    | variantType
+    | unknownType;
 
 primitiveType
 @init { pushMsg("primitive type specification", state); }
@@ -2486,6 +2487,12 @@ variantType
     : KW_VARIANT -> TOK_VARIANT
     ;
 
+unknownType
+@init { pushMsg("unknown type", state); }
+@after { popMsg(state); }
+    : KW_UNKNOWN -> TOK_UNKNOWN
+    ;
+
 setOperator
 @init { pushMsg("set operator", state); }
 @after { popMsg(state); }
diff --git 
a/ql/src/java/org/apache/hadoop/hive/ql/exec/vector/VectorAssignRow.java 
b/ql/src/java/org/apache/hadoop/hive/ql/exec/vector/VectorAssignRow.java
index 5e3768660f6..9f73060cc0b 100644
--- a/ql/src/java/org/apache/hadoop/hive/ql/exec/vector/VectorAssignRow.java
+++ b/ql/src/java/org/apache/hadoop/hive/ql/exec/vector/VectorAssignRow.java
@@ -626,6 +626,9 @@ private void assignRowColumn(
         assignRowColumn(unionColumnVector.fields[tag], batchIndex, 
objectTypeInfos.get(tag), union.getObject());
       }
       break;
+    case UNKNOWN:
+      VectorizedBatchUtil.setNullColIsNullValue(columnVector, batchIndex);
+      return;
     default:
       throw new RuntimeException("Category " + 
targetTypeInfo.getCategory().name() + " not supported");
     }
@@ -986,6 +989,9 @@ private void assignConvertRowColumn(ColumnVector 
columnVector, int batchIndex,
               unionObjectInspector.getField(tag));
         }
         break;
+      case UNKNOWN:
+        VectorizedBatchUtil.setNullColIsNullValue(columnVector, batchIndex);
+        return;
       default:
         throw new RuntimeException("Category " + targetCategory.name() + " not 
supported");
       }
diff --git 
a/ql/src/java/org/apache/hadoop/hive/ql/exec/vector/VectorDeserializeRow.java 
b/ql/src/java/org/apache/hadoop/hive/ql/exec/vector/VectorDeserializeRow.java
index c1f5d8ca32c..6410ff80e93 100644
--- 
a/ql/src/java/org/apache/hadoop/hive/ql/exec/vector/VectorDeserializeRow.java
+++ 
b/ql/src/java/org/apache/hadoop/hive/ql/exec/vector/VectorDeserializeRow.java
@@ -355,6 +355,8 @@ private Field allocateComplexField(TypeInfo sourceTypeInfo) 
{
             new UnionComplexTypeHelper(fields);
         return new Field(category, unionHelper, sourceTypeInfo, new 
VectorUnionDeserializer());
       }
+    case UNKNOWN:
+      return new Field(category, null, sourceTypeInfo, new 
VectorVoidDeserializer());
     default:
       throw new RuntimeException("Category " + category + " not supported");
     }
@@ -368,6 +370,7 @@ private Field allocateField(TypeInfo sourceTypeInfo) {
     case MAP:
     case STRUCT:
     case UNION:
+    case UNKNOWN:
       return allocateComplexField(sourceTypeInfo);
     default:
       throw new RuntimeException("Category " + sourceTypeInfo.getCategory() + 
" not supported");
diff --git 
a/ql/src/java/org/apache/hadoop/hive/ql/exec/vector/VectorExtractRow.java 
b/ql/src/java/org/apache/hadoop/hive/ql/exec/vector/VectorExtractRow.java
index b3be81646c5..9945f957ef5 100644
--- a/ql/src/java/org/apache/hadoop/hive/ql/exec/vector/VectorExtractRow.java
+++ b/ql/src/java/org/apache/hadoop/hive/ql/exec/vector/VectorExtractRow.java
@@ -564,6 +564,8 @@ public Object extractRowColumn(
         standardUnion.setObject(object);
         return standardUnion;
       }
+    case UNKNOWN:
+      return null;
     default:
       throw new RuntimeException("Category " + category.name() + " not 
supported");
     }
diff --git 
a/ql/src/java/org/apache/hadoop/hive/ql/exec/vector/VectorSerializeRow.java 
b/ql/src/java/org/apache/hadoop/hive/ql/exec/vector/VectorSerializeRow.java
index fd4fffe292f..deca7290800 100644
--- a/ql/src/java/org/apache/hadoop/hive/ql/exec/vector/VectorSerializeRow.java
+++ b/ql/src/java/org/apache/hadoop/hive/ql/exec/vector/VectorSerializeRow.java
@@ -176,6 +176,10 @@ private Field createField(TypeInfo typeInfo) {
         field.children = createFields(objectTypeInfos.toArray(new 
TypeInfo[objectTypeInfos.size()]));
         field.writer = new VectorSerializeUnionWriter();
         break;
+      case UNKNOWN:
+        field.children = new Field[0];
+        field.writer = new VectorSerializeUnknownWriter();
+        break;
       default:
         throw new RuntimeException();
       }
@@ -579,6 +583,13 @@ void serialize(Object colInfo, Field field, int 
adjustedBatchIndex) throws IOExc
     }
   }
 
+  class VectorSerializeUnknownWriter extends VectorSerializeWriter {
+    @Override
+    void serialize(Object colInfo, Field field, int adjustedBatchIndex) throws 
IOException {
+      // Unknown type values are always null and handled before the writer is 
invoked.
+    }
+  }
+
   public boolean getHasAnyNulls() {
     return hasAnyNulls;
   }
diff --git 
a/ql/src/java/org/apache/hadoop/hive/ql/exec/vector/VectorizationContext.java 
b/ql/src/java/org/apache/hadoop/hive/ql/exec/vector/VectorizationContext.java
index 894231e991e..c6d2625eb77 100644
--- 
a/ql/src/java/org/apache/hadoop/hive/ql/exec/vector/VectorizationContext.java
+++ 
b/ql/src/java/org/apache/hadoop/hive/ql/exec/vector/VectorizationContext.java
@@ -4581,6 +4581,8 @@ public static ColumnVector.Type 
getColumnVectorTypeFromTypeInfo(TypeInfo typeInf
           throw new HiveException("Unexpected primitive type category " + 
primitiveCategory);
         }
       }
+      case UNKNOWN:
+        return ColumnVector.Type.VOID;
       default:
         throw new HiveException("Unexpected type category " +
             typeInfo.getCategory());
diff --git 
a/ql/src/java/org/apache/hadoop/hive/ql/exec/vector/VectorizedBatchUtil.java 
b/ql/src/java/org/apache/hadoop/hive/ql/exec/vector/VectorizedBatchUtil.java
index 4be9e1435f0..d4d4af31a94 100644
--- a/ql/src/java/org/apache/hadoop/hive/ql/exec/vector/VectorizedBatchUtil.java
+++ b/ql/src/java/org/apache/hadoop/hive/ql/exec/vector/VectorizedBatchUtil.java
@@ -208,6 +208,8 @@ public static ColumnVector createColumnVector(TypeInfo 
typeInfo,
             createColumnVector(mapTypeInfo.getMapKeyTypeInfo()),
             createColumnVector(mapTypeInfo.getMapValueTypeInfo()));
       }
+    case UNKNOWN:
+      return new VoidColumnVector(VectorizedRowBatch.DEFAULT_SIZE);
     default:
       throw new RuntimeException("Vectorization is not supported for datatype:"
           + typeInfo.getCategory());
diff --git 
a/ql/src/java/org/apache/hadoop/hive/ql/exec/vector/expressions/VectorExpressionWriterFactory.java
 
b/ql/src/java/org/apache/hadoop/hive/ql/exec/vector/expressions/VectorExpressionWriterFactory.java
index 163347febeb..4c70bac5426 100644
--- 
a/ql/src/java/org/apache/hadoop/hive/ql/exec/vector/expressions/VectorExpressionWriterFactory.java
+++ 
b/ql/src/java/org/apache/hadoop/hive/ql/exec/vector/expressions/VectorExpressionWriterFactory.java
@@ -738,6 +738,9 @@ public static VectorExpressionWriter 
genVectorExpressionWritable(
         return genVectorExpressionWritableUnion(
             (SettableUnionObjectInspector) fieldObjInspector);
 
+      case UNKNOWN:
+        return genVectorExpressionWritableUnknown(fieldObjInspector);
+
       default:
         throw new IllegalArgumentException("Unknown type " +
             fieldObjInspector.getCategory());
@@ -1719,6 +1722,28 @@ public Object setValue(Object union, ColumnVector 
column, int row)
     }.init(fieldObjInspector);
   }
 
+  private static VectorExpressionWriter genVectorExpressionWritableUnknown(
+      ObjectInspector fieldObjInspector) throws HiveException {
+    return new VectorExpressionWriterBase() {
+
+      @Override
+      public Object writeValue(ColumnVector column, int row) throws 
HiveException {
+        return null;
+      }
+
+      @Override
+      public Object setValue(Object row, ColumnVector column, int columnRow)
+          throws HiveException {
+        return null;
+      }
+
+      @Override
+      public Object initValue(Object ost) throws HiveException {
+        return null;
+      }
+    }.init(fieldObjInspector);
+  }
+
   // For complex types like STRUCT, MAP, etc we do not support, we need a 
writer that
   // does nothing.  We assume the Vectorizer class has not validated the query 
to actually
   // try and use the complex types.  They do show up in inputObjInspector[0] 
and need to be
diff --git 
a/ql/src/java/org/apache/hadoop/hive/ql/io/parquet/convert/HiveSchemaConverter.java
 
b/ql/src/java/org/apache/hadoop/hive/ql/io/parquet/convert/HiveSchemaConverter.java
index a59c15ee5d8..e1ca092a20a 100644
--- 
a/ql/src/java/org/apache/hadoop/hive/ql/io/parquet/convert/HiveSchemaConverter.java
+++ 
b/ql/src/java/org/apache/hadoop/hive/ql/io/parquet/convert/HiveSchemaConverter.java
@@ -129,7 +129,9 @@ private static Type convertType(final String name, final 
TypeInfo typeInfo, Conf
         return Types.primitive(PrimitiveTypeName.INT32, repetition)
             .as(LogicalTypeAnnotation.dateType()).named(name);
       } else if (typeInfo.equals(TypeInfoFactory.unknownTypeInfo)) {
-        throw new UnsupportedOperationException("Unknown type not 
implemented");
+        throw new UnsupportedOperationException("Legacy unknown primitive type 
is not supported for Parquet");
+      } else if (typeInfo.equals(TypeInfoFactory.getUnknownTypeInfo())) {
+        throw new UnsupportedOperationException("Unknown type is not stored in 
Parquet data files");
       } else {
         throw new IllegalArgumentException("Unknown type: " + typeInfo);
       }
diff --git a/ql/src/java/org/apache/hadoop/hive/ql/metadata/Table.java 
b/ql/src/java/org/apache/hadoop/hive/ql/metadata/Table.java
index ad16a2f22ce..001eff15f55 100644
--- a/ql/src/java/org/apache/hadoop/hive/ql/metadata/Table.java
+++ b/ql/src/java/org/apache/hadoop/hive/ql/metadata/Table.java
@@ -93,6 +93,7 @@
 import com.google.common.base.Preconditions;
 
 import static org.apache.commons.lang3.StringUtils.isBlank;
+import static org.apache.hadoop.hive.serde.serdeConstants.UNKNOWN_TYPE_NAME;
 import static org.apache.hadoop.hive.serde.serdeConstants.VARIANT_TYPE_NAME;
 
 /**
@@ -1479,6 +1480,7 @@ public List<VirtualColumn> getVirtualColumns() {
 
   private static boolean isUnsupportedInNonIceberg(String columnType) {
     return VARIANT_TYPE_NAME.equalsIgnoreCase(columnType) ||
+        UNKNOWN_TYPE_NAME.equalsIgnoreCase(columnType) ||
         
TypeInfoFactory.nanoTimestampTypeInfo.getQualifiedName().equalsIgnoreCase(columnType)
 ||
         
TypeInfoFactory.timestampNanoLocalTZTypeInfo.getQualifiedName().equalsIgnoreCase(columnType);
   }
diff --git 
a/ql/src/java/org/apache/hadoop/hive/ql/optimizer/calcite/translator/TypeConverter.java
 
b/ql/src/java/org/apache/hadoop/hive/ql/optimizer/calcite/translator/TypeConverter.java
index 1f7ccd1773c..9fa5ebf286d 100644
--- 
a/ql/src/java/org/apache/hadoop/hive/ql/optimizer/calcite/translator/TypeConverter.java
+++ 
b/ql/src/java/org/apache/hadoop/hive/ql/optimizer/calcite/translator/TypeConverter.java
@@ -164,6 +164,12 @@ public static RelDataType convert(TypeInfo type, boolean 
nullable, RelDataTypeFa
     case UNION:
       convertedType = convert((UnionTypeInfo) type, dtFactory);
       break;
+    case VARIANT:
+      convertedType = dtFactory.createSqlType(SqlTypeName.OTHER);
+      break;
+    case UNKNOWN:
+      convertedType = dtFactory.createSqlType(SqlTypeName.NULL);
+      break;
     }
     // hive does not have concept of not nullable types
     return dtFactory.createTypeWithNullability(convertedType, nullable);
diff --git 
a/ql/src/java/org/apache/hadoop/hive/ql/parse/BaseSemanticAnalyzer.java 
b/ql/src/java/org/apache/hadoop/hive/ql/parse/BaseSemanticAnalyzer.java
index 8f763b4857c..434e131b4d0 100644
--- a/ql/src/java/org/apache/hadoop/hive/ql/parse/BaseSemanticAnalyzer.java
+++ b/ql/src/java/org/apache/hadoop/hive/ql/parse/BaseSemanticAnalyzer.java
@@ -1048,6 +1048,7 @@ public static String getTypeStringFromAST(ASTNode 
typeNode)
     TOKEN_TO_TYPE.put(HiveParser.TOK_INTERVAL_DAY_TIME, 
serdeConstants.INTERVAL_DAY_TIME_TYPE_NAME);
     TOKEN_TO_TYPE.put(HiveParser.TOK_DECIMAL, 
serdeConstants.DECIMAL_TYPE_NAME);
     TOKEN_TO_TYPE.put(HiveParser.TOK_VARIANT, 
serdeConstants.VARIANT_TYPE_NAME);
+    TOKEN_TO_TYPE.put(HiveParser.TOK_UNKNOWN, 
serdeConstants.UNKNOWN_TYPE_NAME);
   }
 
   private static String getTypeName(ASTNode node) throws SemanticException {
diff --git a/ql/src/java/org/apache/hadoop/hive/ql/parse/SemanticAnalyzer.java 
b/ql/src/java/org/apache/hadoop/hive/ql/parse/SemanticAnalyzer.java
index ea981c98bb3..b754e677a47 100644
--- a/ql/src/java/org/apache/hadoop/hive/ql/parse/SemanticAnalyzer.java
+++ b/ql/src/java/org/apache/hadoop/hive/ql/parse/SemanticAnalyzer.java
@@ -9126,6 +9126,9 @@ private ExprNodeDesc handleConversion(StructField 
tableField, ColumnInfo rowFiel
     // JSON-format. However, we may add more operators.
     // Thus, we still keep the conversion.
     if (!tableFieldTypeInfo.equals(rowFieldTypeInfo)) {
+      if (TypeInfoUtils.isVoidCompatibleTarget(rowFieldTypeInfo, 
tableFieldTypeInfo)) {
+        return column;
+      }
       // need to do some conversions here
       conversion.set(true);
       if (tableFieldTypeInfo.getCategory() != Category.PRIMITIVE) {
@@ -9348,8 +9351,8 @@ private List<ExprNodeDesc> genConvertCol(String dest, QB 
qb, TableDesc tableDesc
           rowFields.get(posn).getInternalName(), 
rowFields.get(posn).getTabAlias(),
           rowFields.get(posn).getIsVirtualCol());
 
-      if (convert && !tableFieldTypeInfo.equals(rowFieldTypeInfo)) {
-        // need to do some conversions here
+      if (convert && !tableFieldTypeInfo.equals(rowFieldTypeInfo)
+          && !TypeInfoUtils.isVoidCompatibleTarget(rowFieldTypeInfo, 
tableFieldTypeInfo)) {
         if (tableFieldTypeInfo.getCategory() != Category.PRIMITIVE) {
           // cannot convert to complex types
           column = null;
diff --git a/ql/src/test/org/apache/hadoop/hive/ql/stats/TestStatsUtils.java 
b/ql/src/test/org/apache/hadoop/hive/ql/stats/TestStatsUtils.java
index 2faefafdee3..bb09151e788 100644
--- a/ql/src/test/org/apache/hadoop/hive/ql/stats/TestStatsUtils.java
+++ b/ql/src/test/org/apache/hadoop/hive/ql/stats/TestStatsUtils.java
@@ -108,6 +108,7 @@ void testPrimitiveSizeEstimations() throws Exception {
     exclusions.add(serdeConstants.STRUCT_TYPE_NAME);
     exclusions.add(serdeConstants.UNION_TYPE_NAME);
     exclusions.add(serdeConstants.VARIANT_TYPE_NAME);
+    exclusions.add(serdeConstants.UNKNOWN_TYPE_NAME);
     Field[] serdeFields = serdeConstants.class.getFields();
     for (Field field : serdeFields) {
       if (!Modifier.isStatic(field.getModifiers())) {
diff --git 
a/ql/src/test/queries/clientnegative/unknown_type_non_iceberg_table.q 
b/ql/src/test/queries/clientnegative/unknown_type_non_iceberg_table.q
new file mode 100644
index 00000000000..85084d25713
--- /dev/null
+++ b/ql/src/test/queries/clientnegative/unknown_type_non_iceberg_table.q
@@ -0,0 +1 @@
+CREATE EXTERNAL TABLE unknown_test_basic (id INT, placeholder UNKNOWN);
diff --git 
a/ql/src/test/results/clientnegative/unknown_type_non_iceberg_table.q.out 
b/ql/src/test/results/clientnegative/unknown_type_non_iceberg_table.q.out
new file mode 100644
index 00000000000..597c0f28acf
--- /dev/null
+++ b/ql/src/test/results/clientnegative/unknown_type_non_iceberg_table.q.out
@@ -0,0 +1,5 @@
+PREHOOK: query: CREATE EXTERNAL TABLE unknown_test_basic (id INT, placeholder 
UNKNOWN)
+PREHOOK: type: CREATETABLE
+PREHOOK: Output: database:default
+PREHOOK: Output: default@unknown_test_basic
+FAILED: Execution Error, return code 40000 from 
org.apache.hadoop.hive.ql.ddl.DDLTask. 
org.apache.hadoop.hive.ql.metadata.HiveException: Column name placeholder 
cannot be of type 'unknown' as it is not supported in non-Iceberg tables.
diff --git a/serde/if/serde.thrift b/serde/if/serde.thrift
index f8d0f0a06ac..057f227b75e 100644
--- a/serde/if/serde.thrift
+++ b/serde/if/serde.thrift
@@ -71,6 +71,7 @@ const string MAP_TYPE_NAME  = "map";
 const string STRUCT_TYPE_NAME  = "struct";
 const string UNION_TYPE_NAME  = "uniontype";
 const string VARIANT_TYPE_NAME  = "variant";
+const string UNKNOWN_TYPE_NAME  = "unknown";
 
 const string LIST_COLUMNS = "columns";
 const string LIST_COLUMN_TYPES = "columns.types";
diff --git 
a/serde/src/gen/thrift/gen-javabean/org/apache/hadoop/hive/serde/serdeConstants.java
 
b/serde/src/gen/thrift/gen-javabean/org/apache/hadoop/hive/serde/serdeConstants.java
index 20e607a67bd..d43bc3d8d96 100644
--- 
a/serde/src/gen/thrift/gen-javabean/org/apache/hadoop/hive/serde/serdeConstants.java
+++ 
b/serde/src/gen/thrift/gen-javabean/org/apache/hadoop/hive/serde/serdeConstants.java
@@ -94,6 +94,7 @@ public class serdeConstants {
   public static final java.lang.String UNION_TYPE_NAME = "uniontype";
 
   public static final java.lang.String VARIANT_TYPE_NAME = "variant";
+  public static final java.lang.String UNKNOWN_TYPE_NAME = "unknown";
 
   public static final java.lang.String LIST_COLUMNS = "columns";
 
diff --git a/serde/src/java/org/apache/hadoop/hive/serde2/lazy/LazyFactory.java 
b/serde/src/java/org/apache/hadoop/hive/serde2/lazy/LazyFactory.java
index 8b9c012d9b9..4a51e6c90dd 100644
--- a/serde/src/java/org/apache/hadoop/hive/serde2/lazy/LazyFactory.java
+++ b/serde/src/java/org/apache/hadoop/hive/serde2/lazy/LazyFactory.java
@@ -68,6 +68,7 @@
 import org.apache.hadoop.hive.serde2.typeinfo.PrimitiveTypeInfo;
 import org.apache.hadoop.hive.serde2.typeinfo.StructTypeInfo;
 import org.apache.hadoop.hive.serde2.typeinfo.TypeInfo;
+import org.apache.hadoop.hive.serde2.typeinfo.TypeInfoFactory;
 import org.apache.hadoop.hive.serde2.typeinfo.UnionTypeInfo;
 import org.apache.hadoop.io.Text;
 import org.apache.hadoop.io.Writable;
@@ -370,6 +371,10 @@ public static ObjectInspector 
createLazyObjectInspector(TypeInfo typeInfo,
       return LazyObjectInspectorFactory.getLazyUnionObjectInspector(lazyOIs,
           LazyUtils.getSeparator(lazyParams.getSeparators(), separatorIndex),
           lazyParams);
+    case UNKNOWN:
+      // Unknown columns are always null and are not stored in data files.
+      return LazyPrimitiveObjectInspectorFactory.getLazyObjectInspector(
+          TypeInfoFactory.voidTypeInfo, lazyParams);
    }
 
     throw new RuntimeException("Hive LazySerDe Internal error.");
diff --git 
a/serde/src/java/org/apache/hadoop/hive/serde2/lazybinary/LazyBinaryUtils.java 
b/serde/src/java/org/apache/hadoop/hive/serde2/lazybinary/LazyBinaryUtils.java
index ed521eed68b..f801a42b112 100644
--- 
a/serde/src/java/org/apache/hadoop/hive/serde2/lazybinary/LazyBinaryUtils.java
+++ 
b/serde/src/java/org/apache/hadoop/hive/serde2/lazybinary/LazyBinaryUtils.java
@@ -35,6 +35,7 @@
 import org.apache.hadoop.hive.serde2.typeinfo.PrimitiveTypeInfo;
 import org.apache.hadoop.hive.serde2.typeinfo.StructTypeInfo;
 import org.apache.hadoop.hive.serde2.typeinfo.TypeInfo;
+import org.apache.hadoop.hive.serde2.typeinfo.TypeInfoFactory;
 import org.apache.hadoop.hive.serde2.typeinfo.UnionTypeInfo;
 import org.apache.hadoop.io.WritableUtils;
 
@@ -513,6 +514,11 @@ public static ObjectInspector 
getLazyBinaryObjectInspectorFromTypeInfo(
             .getLazyBinaryUnionObjectInspector(fieldObjectInspectors);
         break;
       }
+      case UNKNOWN:
+        // Unknown columns are always null and are not stored in data files.
+        result = 
PrimitiveObjectInspectorFactory.getPrimitiveWritableObjectInspector(
+            TypeInfoFactory.voidTypeInfo);
+        break;
       default: {
         result = null;
       }
diff --git 
a/serde/src/java/org/apache/hadoop/hive/serde2/objectinspector/ObjectInspector.java
 
b/serde/src/java/org/apache/hadoop/hive/serde2/objectinspector/ObjectInspector.java
index 8e4183b615f..c342098c3e4 100644
--- 
a/serde/src/java/org/apache/hadoop/hive/serde2/objectinspector/ObjectInspector.java
+++ 
b/serde/src/java/org/apache/hadoop/hive/serde2/objectinspector/ObjectInspector.java
@@ -47,7 +47,7 @@ public interface ObjectInspector extends Cloneable {
    *
    */
   enum Category {
-    PRIMITIVE, LIST, MAP, STRUCT, UNION, VARIANT
+    PRIMITIVE, LIST, MAP, STRUCT, UNION, VARIANT, UNKNOWN
   };
 
   /**
diff --git 
a/serde/src/java/org/apache/hadoop/hive/serde2/objectinspector/ObjectInspectorFactory.java
 
b/serde/src/java/org/apache/hadoop/hive/serde2/objectinspector/ObjectInspectorFactory.java
index 8e251d19bb3..9f6247466b1 100644
--- 
a/serde/src/java/org/apache/hadoop/hive/serde2/objectinspector/ObjectInspectorFactory.java
+++ 
b/serde/src/java/org/apache/hadoop/hive/serde2/objectinspector/ObjectInspectorFactory.java
@@ -367,6 +367,10 @@ public static ObjectInspector getVariantObjectInspector() {
     return VariantObjectInspector.get();
   }
 
+  public static ObjectInspector getUnknownObjectInspector() {
+    return UnknownObjectInspector.get();
+  }
+
   public static ColumnarStructObjectInspector getColumnarStructObjectInspector(
       List<String> structFieldNames,
       List<ObjectInspector> structFieldObjectInspectors) {
diff --git 
a/serde/src/java/org/apache/hadoop/hive/serde2/objectinspector/UnknownObjectInspector.java
 
b/serde/src/java/org/apache/hadoop/hive/serde2/objectinspector/UnknownObjectInspector.java
new file mode 100644
index 00000000000..0facbd6c810
--- /dev/null
+++ 
b/serde/src/java/org/apache/hadoop/hive/serde2/objectinspector/UnknownObjectInspector.java
@@ -0,0 +1,46 @@
+/*
+ * 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.hadoop.hive.serde2.objectinspector;
+
+/**
+ * ObjectInspector for Iceberg V3 unknown type columns.
+ * <p>
+ * Unknown values are always null and are not stored in data files.
+ */
+public final class UnknownObjectInspector implements ObjectInspector {
+
+  private static final ObjectInspector INSTANCE = new UnknownObjectInspector();
+
+  private UnknownObjectInspector() {
+  }
+
+  public static ObjectInspector get() {
+    return INSTANCE;
+  }
+
+  @Override
+  public Category getCategory() {
+    return Category.UNKNOWN;
+  }
+
+  @Override
+  public String getTypeName() {
+    return "unknown";
+  }
+}
diff --git a/serde/src/java/org/apache/hadoop/hive/serde2/thrift/Type.java 
b/serde/src/java/org/apache/hadoop/hive/serde2/thrift/Type.java
index ca5abdbbf2c..6773b868700 100644
--- a/serde/src/java/org/apache/hadoop/hive/serde2/thrift/Type.java
+++ b/serde/src/java/org/apache/hadoop/hive/serde2/thrift/Type.java
@@ -109,7 +109,10 @@ public enum Type {
   USER_DEFINED_TYPE("USER_DEFINED",
       java.sql.Types.OTHER,
       TTypeId.USER_DEFINED_TYPE,
-      true, false);
+      true, false),
+  UNKNOWN_TYPE(serdeConstants.UNKNOWN_TYPE_NAME.toUpperCase(),
+      java.sql.Types.NULL,
+      TTypeId.UNKNOWN_TYPE);
 
   private final String name;
   private final TTypeId tType;
@@ -262,6 +265,9 @@ public static Type getType(TypeInfo typeInfo) {
     case UNION: {
       return Type.UNION_TYPE;
     }
+    case UNKNOWN: {
+      return Type.UNKNOWN_TYPE;
+    }
     default: {
       throw new RuntimeException("Unrecognized type: " + 
typeInfo.getCategory());
     }
diff --git 
a/serde/src/java/org/apache/hadoop/hive/serde2/typeinfo/TypeInfoFactory.java 
b/serde/src/java/org/apache/hadoop/hive/serde2/typeinfo/TypeInfoFactory.java
index f80c5192604..bee5ac82dc9 100644
--- a/serde/src/java/org/apache/hadoop/hive/serde2/typeinfo/TypeInfoFactory.java
+++ b/serde/src/java/org/apache/hadoop/hive/serde2/typeinfo/TypeInfoFactory.java
@@ -281,6 +281,10 @@ public static TypeInfo getVariantTypeInfo() {
     return VariantTypeInfo.get();
   }
 
+  public static TypeInfo getUnknownTypeInfo() {
+    return UnknownTypeInfo.get();
+  }
+
   static ConcurrentHashMap<TypeInfo, TypeInfo> cachedListTypeInfo = new 
ConcurrentHashMap<TypeInfo, TypeInfo>();
 
   public static TypeInfo getListTypeInfo(TypeInfo elementTypeInfo) {
diff --git 
a/serde/src/java/org/apache/hadoop/hive/serde2/typeinfo/TypeInfoUtils.java 
b/serde/src/java/org/apache/hadoop/hive/serde2/typeinfo/TypeInfoUtils.java
index b25d35868df..ef4bdd2ae7f 100644
--- a/serde/src/java/org/apache/hadoop/hive/serde2/typeinfo/TypeInfoUtils.java
+++ b/serde/src/java/org/apache/hadoop/hive/serde2/typeinfo/TypeInfoUtils.java
@@ -400,6 +400,7 @@ private Token expect(String item, String alternative) {
             && !serdeConstants.STRUCT_TYPE_NAME.equals(t.text)
             && !serdeConstants.UNION_TYPE_NAME.equals(t.text)
             && !serdeConstants.VARIANT_TYPE_NAME.equals(t.text)
+            && !serdeConstants.UNKNOWN_TYPE_NAME.equals(t.text)
             && null == PrimitiveObjectInspectorUtils
             .getTypeEntryFromTypeName(t.text)
             && !t.text.equals(alternative)) {
@@ -579,6 +580,11 @@ private TypeInfo parseType() {
         return TypeInfoFactory.getVariantTypeInfo();
       }
 
+      // Is this an unknown type?
+      if (serdeConstants.UNKNOWN_TYPE_NAME.equals(t.text)) {
+        return TypeInfoFactory.getUnknownTypeInfo();
+      }
+
       throw new RuntimeException("Internal error parsing position "
           + t.position + " of '" + typeInfoString + "'");
     }
@@ -683,6 +689,14 @@ public static ObjectInspector 
getStandardWritableObjectInspectorFromTypeInfo(
             fieldObjectInspectors);
         break;
       }
+      case VARIANT: {
+        result = ObjectInspectorFactory.getVariantObjectInspector();
+        break;
+      }
+      case UNKNOWN: {
+        result = ObjectInspectorFactory.getUnknownObjectInspector();
+        break;
+      }
 
       default: {
         result = null;
@@ -765,6 +779,14 @@ public static ObjectInspector 
getStandardJavaObjectInspectorFromTypeInfo(
             fieldObjectInspectors);
         break;
       }
+      case VARIANT: {
+        result = ObjectInspectorFactory.getVariantObjectInspector();
+        break;
+      }
+      case UNKNOWN: {
+        result = ObjectInspectorFactory.getUnknownObjectInspector();
+        break;
+      }
      default: {
         result = null;
       }
@@ -837,6 +859,14 @@ public static TypeInfo 
getTypeInfoFromObjectInspector(ObjectInspector oi) {
       result = TypeInfoFactory.getUnionTypeInfo(objectTypeInfos);
       break;
     }
+    case VARIANT: {
+      result = TypeInfoFactory.getVariantTypeInfo();
+      break;
+    }
+    case UNKNOWN: {
+      result = TypeInfoFactory.getUnknownTypeInfo();
+      break;
+    }
     default: {
       throw new RuntimeException("Unknown ObjectInspector category!");
     }
@@ -1013,6 +1043,16 @@ public static boolean 
implicitConvertible(PrimitiveCategory from, PrimitiveCateg
     return true;
   }
 
+  /**
+   * Returns whether a NULL literal (void) can be assigned to the target type.
+   * Iceberg unknown columns only accept NULL values.
+   */
+  public static boolean isVoidCompatibleTarget(TypeInfo from, TypeInfo to) {
+    return from.getCategory() == Category.PRIMITIVE
+        && ((PrimitiveTypeInfo) from).getPrimitiveCategory() == 
PrimitiveCategory.VOID
+        && to.getCategory() == Category.UNKNOWN;
+  }
+
   /**
    * Returns whether it is possible to implicitly convert an object of Class
    * from to Class to.
@@ -1022,6 +1062,10 @@ public static boolean implicitConvertible(TypeInfo from, 
TypeInfo to) {
       return true;
     }
 
+    if (isVoidCompatibleTarget(from, to)) {
+      return true;
+    }
+
     // Reimplemented to use PrimitiveCategory rather than TypeInfo, because
     // 2 TypeInfos from the same qualified type (varchar, decimal) should 
still be
     // seen as equivalent.
diff --git 
a/serde/src/java/org/apache/hadoop/hive/serde2/typeinfo/UnknownTypeInfo.java 
b/serde/src/java/org/apache/hadoop/hive/serde2/typeinfo/UnknownTypeInfo.java
new file mode 100644
index 00000000000..19c19524de4
--- /dev/null
+++ b/serde/src/java/org/apache/hadoop/hive/serde2/typeinfo/UnknownTypeInfo.java
@@ -0,0 +1,69 @@
+/*
+ * 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.hadoop.hive.serde2.typeinfo;
+
+import java.io.Serial;
+import java.io.Serializable;
+import java.util.Objects;
+import org.apache.hadoop.hive.common.classification.InterfaceAudience;
+import org.apache.hadoop.hive.common.classification.InterfaceStability;
+import org.apache.hadoop.hive.serde2.objectinspector.ObjectInspector.Category;
+
+/**
+ * UnknownTypeInfo represents the TypeInfo of an Iceberg V3 unknown type in 
Hive.
+ * <p>
+ * Unknown columns are schema placeholders for undetermined types. They must 
be optional,
+ * default to null, and are not stored in data files.
+ */
[email protected]
[email protected]
+public final class UnknownTypeInfo extends TypeInfo implements Serializable {
+
+  @Serial
+  private static final long serialVersionUID = 1L;
+
+  private static final UnknownTypeInfo INSTANCE = new UnknownTypeInfo();
+
+  public static UnknownTypeInfo get() {
+    return INSTANCE;
+  }
+
+  @Override
+  public Category getCategory() {
+    return Category.UNKNOWN;
+  }
+
+  @Override
+  public String getTypeName() {
+    return "unknown";
+  }
+
+  @Override
+  public boolean equals(Object o) {
+    if (this == o) {
+      return true;
+    }
+    return o instanceof UnknownTypeInfo;
+  }
+
+  @Override
+  public int hashCode() {
+    return Objects.hash(UnknownTypeInfo.class, getTypeName());
+  }
+}
diff --git 
a/serde/src/test/org/apache/hadoop/hive/serde2/typeinfo/TestUnknownTypeInfo.java
 
b/serde/src/test/org/apache/hadoop/hive/serde2/typeinfo/TestUnknownTypeInfo.java
new file mode 100644
index 00000000000..87a73c2031e
--- /dev/null
+++ 
b/serde/src/test/org/apache/hadoop/hive/serde2/typeinfo/TestUnknownTypeInfo.java
@@ -0,0 +1,79 @@
+/*
+ * 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.hadoop.hive.serde2.typeinfo;
+
+import java.util.List;
+import org.apache.hadoop.hive.serde.serdeConstants;
+import org.apache.hadoop.hive.serde2.SerDeException;
+import org.apache.hadoop.hive.serde2.lazy.LazyFactory;
+import org.apache.hadoop.hive.serde2.objectinspector.ObjectInspector;
+import org.apache.hadoop.hive.serde2.objectinspector.ObjectInspectorFactory;
+import org.apache.hadoop.hive.serde2.objectinspector.UnknownObjectInspector;
+import org.apache.hadoop.hive.serde2.thrift.Type;
+import org.apache.hadoop.io.Text;
+import org.junit.Assert;
+import org.junit.Test;
+
+public class TestUnknownTypeInfo {
+
+  @Test
+  public void testParseUnknownType() {
+    TypeInfo typeInfo = 
TypeInfoUtils.getTypeInfoFromTypeString(serdeConstants.UNKNOWN_TYPE_NAME);
+    Assert.assertEquals(UnknownTypeInfo.get(), typeInfo);
+    Assert.assertEquals(ObjectInspector.Category.UNKNOWN, 
typeInfo.getCategory());
+  }
+
+  @Test
+  public void testUnknownObjectInspector() {
+    ObjectInspector oi = ObjectInspectorFactory.getUnknownObjectInspector();
+    Assert.assertEquals(UnknownObjectInspector.get(), oi);
+    Assert.assertEquals(ObjectInspector.Category.UNKNOWN, oi.getCategory());
+    Assert.assertEquals(serdeConstants.UNKNOWN_TYPE_NAME, oi.getTypeName());
+  }
+
+  @Test
+  public void testVoidIsCompatibleWithUnknownType() {
+    Assert.assertTrue(TypeInfoUtils.isVoidCompatibleTarget(
+        TypeInfoFactory.voidTypeInfo, TypeInfoFactory.getUnknownTypeInfo()));
+    Assert.assertTrue(TypeInfoUtils.implicitConvertible(
+        TypeInfoFactory.voidTypeInfo, TypeInfoFactory.getUnknownTypeInfo()));
+  }
+
+  @Test
+  public void testLazyObjectInspectorForUnknownType() throws SerDeException {
+    ObjectInspector oi = LazyFactory.createLazyStructInspector(
+        List.of("placeholder"),
+        List.of(TypeInfoFactory.getUnknownTypeInfo()),
+        new byte[] {(byte) 1, (byte) 2, (byte) 3, (byte) 4, (byte) 5, (byte) 
6, (byte) 7, (byte) 8},
+        new Text("\\N"), false, false, (byte) 0);
+    Assert.assertNotNull(oi);
+  }
+
+  @Test
+  public void testStandardObjectInspectorFromTypeInfo() {
+    ObjectInspector oi = 
TypeInfoUtils.getStandardJavaObjectInspectorFromTypeInfo(UnknownTypeInfo.get());
+    Assert.assertEquals(UnknownObjectInspector.get(), oi);
+  }
+
+  @Test
+  public void testThriftTypeMapping() {
+    Assert.assertEquals(Type.UNKNOWN_TYPE, 
Type.getType(serdeConstants.UNKNOWN_TYPE_NAME));
+    Assert.assertEquals(Type.UNKNOWN_TYPE, 
Type.getType(UnknownTypeInfo.get()));
+  }
+}
diff --git a/service-rpc/if/TCLIService.thrift 
b/service-rpc/if/TCLIService.thrift
index a399e66445c..ee58cd9979d 100644
--- a/service-rpc/if/TCLIService.thrift
+++ b/service-rpc/if/TCLIService.thrift
@@ -94,7 +94,8 @@ enum TTypeId {
   CHAR_TYPE,
   INTERVAL_YEAR_MONTH_TYPE,
   INTERVAL_DAY_TIME_TYPE,
-  TIMESTAMPLOCALTZ_TYPE
+  TIMESTAMPLOCALTZ_TYPE,
+  UNKNOWN_TYPE
 }
 
 const set<TTypeId> PRIMITIVE_TYPES = [
@@ -115,7 +116,8 @@ const set<TTypeId> PRIMITIVE_TYPES = [
   TTypeId.CHAR_TYPE,
   TTypeId.INTERVAL_YEAR_MONTH_TYPE,
   TTypeId.INTERVAL_DAY_TIME_TYPE,
-  TTypeId.TIMESTAMPLOCALTZ_TYPE
+  TTypeId.TIMESTAMPLOCALTZ_TYPE,
+  TTypeId.UNKNOWN_TYPE
 ]
 
 const set<TTypeId> COMPLEX_TYPES = [
@@ -154,6 +156,7 @@ const map<TTypeId,string> TYPE_NAMES = {
   TTypeId.INTERVAL_YEAR_MONTH_TYPE: "INTERVAL_YEAR_MONTH"
   TTypeId.INTERVAL_DAY_TIME_TYPE: "INTERVAL_DAY_TIME"
   TTypeId.TIMESTAMPLOCALTZ_TYPE: "TIMESTAMP WITH LOCAL TIME ZONE"
+  TTypeId.UNKNOWN_TYPE: "UNKNOWN"
 }
 
 // Thrift does not support recursively defined types or forward declarations,
diff --git 
a/service-rpc/src/gen/thrift/gen-javabean/org/apache/hive/service/rpc/thrift/TCLIServiceConstants.java
 
b/service-rpc/src/gen/thrift/gen-javabean/org/apache/hive/service/rpc/thrift/TCLIServiceConstants.java
index 7b9c26b3d75..e525f2cdae3 100644
--- 
a/service-rpc/src/gen/thrift/gen-javabean/org/apache/hive/service/rpc/thrift/TCLIServiceConstants.java
+++ 
b/service-rpc/src/gen/thrift/gen-javabean/org/apache/hive/service/rpc/thrift/TCLIServiceConstants.java
@@ -29,6 +29,7 @@
     
PRIMITIVE_TYPES.add(org.apache.hive.service.rpc.thrift.TTypeId.INTERVAL_YEAR_MONTH_TYPE);
     
PRIMITIVE_TYPES.add(org.apache.hive.service.rpc.thrift.TTypeId.INTERVAL_DAY_TIME_TYPE);
     
PRIMITIVE_TYPES.add(org.apache.hive.service.rpc.thrift.TTypeId.TIMESTAMPLOCALTZ_TYPE);
+    
PRIMITIVE_TYPES.add(org.apache.hive.service.rpc.thrift.TTypeId.UNKNOWN_TYPE);
   }
 
   public static final java.util.Set<TTypeId> COMPLEX_TYPES = 
java.util.EnumSet.noneOf(TTypeId.class);
@@ -69,6 +70,7 @@
     TYPE_NAMES.put(org.apache.hive.service.rpc.thrift.TTypeId.TIMESTAMP_TYPE, 
"TIMESTAMP");
     TYPE_NAMES.put(org.apache.hive.service.rpc.thrift.TTypeId.TINYINT_TYPE, 
"TINYINT");
     TYPE_NAMES.put(org.apache.hive.service.rpc.thrift.TTypeId.UNION_TYPE, 
"UNIONTYPE");
+    TYPE_NAMES.put(org.apache.hive.service.rpc.thrift.TTypeId.UNKNOWN_TYPE, 
"UNKNOWN");
     TYPE_NAMES.put(org.apache.hive.service.rpc.thrift.TTypeId.VARCHAR_TYPE, 
"VARCHAR");
   }
 
diff --git 
a/service-rpc/src/gen/thrift/gen-javabean/org/apache/hive/service/rpc/thrift/TTypeId.java
 
b/service-rpc/src/gen/thrift/gen-javabean/org/apache/hive/service/rpc/thrift/TTypeId.java
index 5039ce50b2e..57108255c1c 100644
--- 
a/service-rpc/src/gen/thrift/gen-javabean/org/apache/hive/service/rpc/thrift/TTypeId.java
+++ 
b/service-rpc/src/gen/thrift/gen-javabean/org/apache/hive/service/rpc/thrift/TTypeId.java
@@ -31,7 +31,8 @@ public enum TTypeId implements org.apache.thrift.TEnum {
   CHAR_TYPE(19),
   INTERVAL_YEAR_MONTH_TYPE(20),
   INTERVAL_DAY_TIME_TYPE(21),
-  TIMESTAMPLOCALTZ_TYPE(22);
+  TIMESTAMPLOCALTZ_TYPE(22),
+  UNKNOWN_TYPE(23);
 
   private final int value;
 
@@ -99,6 +100,8 @@ public static TTypeId findByValue(int value) {
         return INTERVAL_DAY_TIME_TYPE;
       case 22:
         return TIMESTAMPLOCALTZ_TYPE;
+      case 23:
+        return UNKNOWN_TYPE;
       default:
         return null;
     }
diff --git a/service-rpc/src/gen/thrift/gen-php/TTypeId.php 
b/service-rpc/src/gen/thrift/gen-php/TTypeId.php
index 43f773d2e1e..ac799c4a173 100644
--- a/service-rpc/src/gen/thrift/gen-php/TTypeId.php
+++ b/service-rpc/src/gen/thrift/gen-php/TTypeId.php
@@ -62,6 +62,8 @@ final class TTypeId
 
     const TIMESTAMPLOCALTZ_TYPE = 22;
 
+    const UNKNOWN_TYPE = 23;
+
     static public $__names = array(
         0 => 'BOOLEAN_TYPE',
         1 => 'TINYINT_TYPE',
@@ -86,6 +88,7 @@ final class TTypeId
         20 => 'INTERVAL_YEAR_MONTH_TYPE',
         21 => 'INTERVAL_DAY_TIME_TYPE',
         22 => 'TIMESTAMPLOCALTZ_TYPE',
+        23 => 'UNKNOWN_TYPE',
     );
 }
 
diff --git a/service/src/java/org/apache/hive/service/cli/ColumnValue.java 
b/service/src/java/org/apache/hive/service/cli/ColumnValue.java
index 09ca12787e8..b994d6fc846 100644
--- a/service/src/java/org/apache/hive/service/cli/ColumnValue.java
+++ b/service/src/java/org/apache/hive/service/cli/ColumnValue.java
@@ -219,6 +219,8 @@ public static TColumnValue toTColumnValue(TypeDescriptor 
typeDescriptor, Object
       return stringValue((String)value);
     case NULL_TYPE:
       return stringValue((String)value);
+    case UNKNOWN_TYPE:
+      return stringValue((String)value);
     default:
       return null;
     }
diff --git 
a/standalone-metastore/metastore-common/src/main/java/org/apache/hadoop/hive/metastore/ColumnType.java
 
b/standalone-metastore/metastore-common/src/main/java/org/apache/hadoop/hive/metastore/ColumnType.java
index 9db91218242..511cfc050b7 100644
--- 
a/standalone-metastore/metastore-common/src/main/java/org/apache/hadoop/hive/metastore/ColumnType.java
+++ 
b/standalone-metastore/metastore-common/src/main/java/org/apache/hadoop/hive/metastore/ColumnType.java
@@ -83,6 +83,7 @@ public class ColumnType {
   public static final String UNION_TYPE_NAME = "uniontype";
 
   public static final String VARIANT_TYPE_NAME = "variant";
+  public static final String UNKNOWN_TYPE_NAME = "unknown";
 
   public static final String LIST_COLUMNS = "columns";
 
@@ -200,6 +201,7 @@ public class ColumnType {
     STRUCT_TYPE_NAME,
     UNION_TYPE_NAME,
     VARIANT_TYPE_NAME,
+    UNKNOWN_TYPE_NAME,
     LIST_COLUMNS,
     LIST_COLUMN_TYPES,
     COLUMN_NAME_DELIMITER

Reply via email to