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

deniskuzZ 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 5ad73a6771e HIVE-29460: Iceberg: Support CLUSTERED BY for iceberg 
tables (#6358)
5ad73a6771e is described below

commit 5ad73a6771e6d3a6f6d2d026b3e6345ff562419b
Author: kokila-19 <[email protected]>
AuthorDate: Fri May 8 17:58:07 2026 +0530

    HIVE-29460: Iceberg: Support CLUSTERED BY for iceberg tables (#6358)
---
 .../apache/iceberg/hive/HiveOperationsBase.java    |  15 +
 .../apache/iceberg/hive/HiveTableOperations.java   |  16 +-
 .../mr/hive/TestHiveIcebergClusteredBy.java        | 185 ++++++
 .../test/queries/positive/iceberg_clustered_by.q   |  85 +++
 .../positive/llap/iceberg_clustered_by.q.out       | 685 +++++++++++++++++++++
 .../test/resources/testconfiguration.properties    |   2 +
 .../desc/formatter/TextDescTableFormatter.java     |   6 +-
 7 files changed, 991 insertions(+), 3 deletions(-)

diff --git 
a/iceberg/iceberg-catalog/src/main/java/org/apache/iceberg/hive/HiveOperationsBase.java
 
b/iceberg/iceberg-catalog/src/main/java/org/apache/iceberg/hive/HiveOperationsBase.java
index 937939b4816..3afff626bac 100644
--- 
a/iceberg/iceberg-catalog/src/main/java/org/apache/iceberg/hive/HiveOperationsBase.java
+++ 
b/iceberg/iceberg-catalog/src/main/java/org/apache/iceberg/hive/HiveOperationsBase.java
@@ -20,7 +20,9 @@
 package org.apache.iceberg.hive;
 
 import java.util.Collections;
+import java.util.List;
 import java.util.Map;
+import org.apache.commons.collections4.CollectionUtils;
 import org.apache.hadoop.hive.metastore.IMetaStoreClient;
 import org.apache.hadoop.hive.metastore.TableType;
 import org.apache.hadoop.hive.metastore.api.NoSuchObjectException;
@@ -161,6 +163,12 @@ default void persistTable(Table hmsTable, boolean 
updateHiveTable, String metada
 
   static StorageDescriptor storageDescriptor(
       Schema schema, String location, boolean hiveEngineEnabled) {
+    return storageDescriptor(schema, location, hiveEngineEnabled, null, -1);
+  }
+
+  static StorageDescriptor storageDescriptor(
+          Schema schema, String location, boolean hiveEngineEnabled,
+          List<String> bucketCols, int numBuckets) {
     final StorageDescriptor storageDescriptor = new StorageDescriptor();
     storageDescriptor.setCols(HiveSchemaUtil.convert(schema));
     storageDescriptor.setLocation(location);
@@ -178,6 +186,13 @@ static StorageDescriptor storageDescriptor(
     }
 
     storageDescriptor.setSerdeInfo(serDeInfo);
+
+    // Preserve Hive bucketing information if provided (for CLUSTERED BY 
support)
+    if (CollectionUtils.isNotEmpty(bucketCols) && numBuckets > 0) {
+      storageDescriptor.setBucketCols(bucketCols);
+      storageDescriptor.setNumBuckets(numBuckets);
+    }
+
     return storageDescriptor;
   }
 
diff --git 
a/iceberg/iceberg-catalog/src/main/java/org/apache/iceberg/hive/HiveTableOperations.java
 
b/iceberg/iceberg-catalog/src/main/java/org/apache/iceberg/hive/HiveTableOperations.java
index 0aeece27dba..104dd658408 100644
--- 
a/iceberg/iceberg-catalog/src/main/java/org/apache/iceberg/hive/HiveTableOperations.java
+++ 
b/iceberg/iceberg-catalog/src/main/java/org/apache/iceberg/hive/HiveTableOperations.java
@@ -20,6 +20,7 @@
 package org.apache.iceberg.hive;
 
 import java.util.Collections;
+import java.util.List;
 import java.util.Map;
 import java.util.Objects;
 import java.util.Set;
@@ -30,6 +31,7 @@
 import org.apache.hadoop.hive.metastore.TableType;
 import org.apache.hadoop.hive.metastore.api.InvalidObjectException;
 import org.apache.hadoop.hive.metastore.api.NoSuchObjectException;
+import org.apache.hadoop.hive.metastore.api.StorageDescriptor;
 import org.apache.hadoop.hive.metastore.api.Table;
 import org.apache.iceberg.BaseMetastoreOperations;
 import org.apache.iceberg.BaseMetastoreTableOperations;
@@ -178,11 +180,23 @@ protected void doCommit(TableMetadata base, TableMetadata 
metadata) {
         LOG.debug("Committing new table: {}", fullName);
       }
 
+      // Preserve Hive bucketing (CLUSTERED BY) if present in the existing 
table
+      StorageDescriptor sd = tbl.getSd();
+      List<String> bucketCols = null;
+      int numBuckets = -1;
+
+      if (sd != null) {
+        bucketCols = sd.getBucketCols();
+        numBuckets = sd.getNumBuckets();
+      }
+
       tbl.setSd(
           HiveOperationsBase.storageDescriptor(
               metadata.schema(),
               metadata.location(),
-              hiveEngineEnabled)); // set to pickup any schema changes
+              hiveEngineEnabled, // set to pickup any schema changes
+              bucketCols,
+              numBuckets));
 
       String metadataLocation = 
tbl.getParameters().get(METADATA_LOCATION_PROP);
       String baseMetadataLocation = base != null ? base.metadataFileLocation() 
: null;
diff --git 
a/iceberg/iceberg-handler/src/test/java/org/apache/iceberg/mr/hive/TestHiveIcebergClusteredBy.java
 
b/iceberg/iceberg-handler/src/test/java/org/apache/iceberg/mr/hive/TestHiveIcebergClusteredBy.java
new file mode 100644
index 00000000000..3bc3c712722
--- /dev/null
+++ 
b/iceberg/iceberg-handler/src/test/java/org/apache/iceberg/mr/hive/TestHiveIcebergClusteredBy.java
@@ -0,0 +1,185 @@
+/*
+ * 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.io.IOException;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import org.apache.hadoop.hive.metastore.api.Table;
+import org.apache.iceberg.FileFormat;
+import org.apache.iceberg.FileScanTask;
+import org.apache.iceberg.catalog.TableIdentifier;
+import org.apache.iceberg.io.CloseableIterable;
+import org.apache.iceberg.mr.hive.test.TestTables.TestTableType;
+import 
org.apache.iceberg.mr.hive.test.utils.HiveIcebergStorageHandlerTestUtils;
+import org.apache.thrift.TException;
+import org.junit.Assert;
+import org.junit.Test;
+import org.junit.runners.Parameterized.Parameters;
+
+/**
+ * Verifies that Iceberg tables honor the {@code CLUSTERED BY} clause by 
checking HMS metadata
+ * persistence, data readability and physical file distribution across buckets.
+ */
+public class TestHiveIcebergClusteredBy extends 
HiveIcebergStorageHandlerWithEngineBase {
+
+  private static final String TABLE_NAME = "ice_clus_data_files";
+  private static final TableIdentifier TABLE_ID = 
TableIdentifier.of("default", TABLE_NAME);
+  private static final int NUM_BUCKETS = 3;
+
+  private static final Map<Integer, Long> EXPECTED_BUCKET_RECORD_COUNTS = 
buildExpectedBuckets();
+
+  private static Map<Integer, Long> buildExpectedBuckets() {
+    Map<Integer, Long> bucketCounts = new LinkedHashMap<>();
+    bucketCounts.put(0, 4L);  // customer_id=2
+    bucketCounts.put(1, 3L);  // customer_id=3
+    bucketCounts.put(2, 2L);  // customer_id=1
+    return Collections.unmodifiableMap(bucketCounts);
+  }
+
+  @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.isVectorized());
+  }
+
+  private void setUpIcebergTable() {
+    shell.executeStatement(
+        "CREATE TABLE " + TABLE_NAME +
+        " (customer_id BIGINT, first_name STRING, last_name STRING) " +
+        "CLUSTERED BY (customer_id) INTO " + NUM_BUCKETS + " BUCKETS " +
+        "STORED BY ICEBERG STORED AS PARQUET " +
+        "TBLPROPERTIES ('format-version'='" + formatVersion + "')");
+
+    shell.executeStatement(testTables.getInsertQuery(
+        HiveIcebergStorageHandlerTestUtils.OTHER_CUSTOMER_RECORDS_2, TABLE_ID, 
false));
+  }
+
+  @Test
+  public void testHmsHasBucketMetadata() throws TException, 
InterruptedException {
+    setUpIcebergTable();
+    validateHmsBucketMetadata(TABLE_NAME);
+  }
+
+  @Test
+  public void testTotalRowCountAfterClusteredInsert() {
+    setUpIcebergTable();
+    List<Object[]> result = shell.executeStatement("SELECT COUNT(*) FROM " + 
TABLE_NAME);
+    long count = ((Number) result.getFirst()[0]).longValue();
+    
Assert.assertEquals(HiveIcebergStorageHandlerTestUtils.OTHER_CUSTOMER_RECORDS_2.size(),
 count);
+  }
+
+  @Test
+  public void testIcebergDataFilesAfterClusteredInsert() throws IOException {
+    setUpIcebergTable();
+    org.apache.iceberg.Table icebergTable = testTables.loadTable(TABLE_ID);
+    Map<Integer, Long> actualBucketRecordCounts = 
extractBucketRecordCounts(icebergTable);
+    Assert.assertEquals(EXPECTED_BUCKET_RECORD_COUNTS, 
actualBucketRecordCounts);
+  }
+
+  /**
+   * Tests that CLUSTERED BY metadata and bucketing behavior are preserved 
when migrating
+   * a native Hive table to Iceberg using ALTER TABLE CONVERT TO ICEBERG.
+   */
+  @Test
+  public void testNativeToIcebergMigrationWithClusteredBy() throws TException, 
InterruptedException, IOException {
+    String migrationTableName = "migration_clustered_table";
+    TableIdentifier migrationTableId = TableIdentifier.of("default", 
migrationTableName);
+
+    // Create a Native Hive table with CLUSTERED BY
+    shell.executeStatement(
+        "CREATE EXTERNAL TABLE " + migrationTableName +
+        " (customer_id BIGINT, first_name STRING, last_name STRING) " +
+        "CLUSTERED BY (customer_id) INTO " + NUM_BUCKETS + " BUCKETS " +
+        "STORED AS ORC");
+
+    // Insert initial records into the Native Hive table
+    List<org.apache.iceberg.data.Record> initialRecords =
+            
HiveIcebergStorageHandlerTestUtils.OTHER_CUSTOMER_RECORDS_2.subList(0, 4);
+    shell.executeStatement(testTables.getInsertQuery(initialRecords, 
migrationTableId, false));
+
+    validateHmsBucketMetadata(migrationTableName);
+
+    List<Object[]> initialCount = shell.executeStatement("SELECT COUNT(*) FROM 
" + migrationTableName);
+    Assert.assertEquals(4L, ((Number) initialCount.getFirst()[0]).longValue());
+
+    // Convert the Native Hive table to Iceberg table
+    shell.executeStatement("ALTER TABLE " + migrationTableName + " CONVERT TO 
ICEBERG");
+
+    validateHmsBucketMetadata(migrationTableName);
+    Table hmsTable = shell.metastore().getTable("default", migrationTableName);
+    Assert.assertEquals("org.apache.iceberg.mr.hive.HiveIcebergStorageHandler",
+        hmsTable.getParameters().get("storage_handler"));
+
+    // Insert remaining records into the Iceberg table
+    List<org.apache.iceberg.data.Record> remainingRecords =
+            
HiveIcebergStorageHandlerTestUtils.OTHER_CUSTOMER_RECORDS_2.subList(4, 9);
+    shell.executeStatement(testTables.getInsertQuery(remainingRecords, 
migrationTableId, false));
+
+    // Validate Iceberg table metadata and bucketing behavior
+    List<Object[]> finalCount = shell.executeStatement("SELECT COUNT(*) FROM " 
+ migrationTableName);
+    
Assert.assertEquals(HiveIcebergStorageHandlerTestUtils.OTHER_CUSTOMER_RECORDS_2.size(),
+        ((Number) finalCount.getFirst()[0]).longValue());
+
+    org.apache.iceberg.Table icebergTable = 
testTables.loadTable(migrationTableId);
+    Map<Integer, Long> bucketCounts = extractBucketRecordCounts(icebergTable);
+    Assert.assertEquals(EXPECTED_BUCKET_RECORD_COUNTS, bucketCounts);
+
+    shell.executeStatement("DROP TABLE IF EXISTS " + migrationTableName);
+  }
+
+  private void validateHmsBucketMetadata(String tableName) throws TException, 
InterruptedException {
+    Table hmsTable = shell.metastore().getTable("default", tableName);
+    Assert.assertEquals(NUM_BUCKETS, hmsTable.getSd().getNumBuckets());
+    Assert.assertEquals(Collections.singletonList("customer_id"), 
hmsTable.getSd().getBucketCols());
+  }
+
+  /**
+   * Extracts bucket ID from file names and counts records per bucket.
+   * Handles both Iceberg file names ({bucketId}-{attemptId}-...) and native 
Hive file names ({bucketId}_0).
+   */
+  private Map<Integer, Long> 
extractBucketRecordCounts(org.apache.iceberg.Table icebergTable) throws 
IOException {
+    Map<Integer, Long> bucketRecordCounts = new LinkedHashMap<>();
+    try (CloseableIterable<FileScanTask> tasks = 
icebergTable.newScan().planFiles()) {
+      for (FileScanTask task : tasks) {
+        String path = task.file().location();
+        String filename = path.substring(path.lastIndexOf('/') + 1);
+
+        int bucketId;
+        if (!filename.contains("-")) {
+          // Native Hive: 000000_0 → bucket 0
+          bucketId = Integer.parseInt(filename.split("_")[0]);
+        } else {
+          // Iceberg: 00000-0-... → bucket 0
+          bucketId = Integer.parseInt(filename.split("-")[0]);
+        }
+
+        // Sum records across multiple files in the same bucket (e.g., 
multiple inserts)
+        bucketRecordCounts.merge(bucketId, task.file().recordCount(), 
Long::sum);
+      }
+    }
+    return bucketRecordCounts;
+  }
+}
diff --git 
a/iceberg/iceberg-handler/src/test/queries/positive/iceberg_clustered_by.q 
b/iceberg/iceberg-handler/src/test/queries/positive/iceberg_clustered_by.q
new file mode 100644
index 00000000000..524ac931422
--- /dev/null
+++ b/iceberg/iceberg-handler/src/test/queries/positive/iceberg_clustered_by.q
@@ -0,0 +1,85 @@
+-- Test Iceberg tables with CLUSTERED BY clause
+-- Verify that Iceberg tables follow Hive bucketing behavior for query 
optimization
+
+-- SORT_QUERY_RESULTS
+-- 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 number of files
+--! qt:replace:/(\s+numFiles\s+)\S+(\s+)/$1#Masked#$2/
+-- Mask total data files
+--! qt:replace:/(\S\"total-data-files\\\":\\\")(\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/
+
+-- Simple CREATE with CLUSTERED BY
+CREATE TABLE ice_bucketed_simple (
+    id int,
+    name string,
+    age int
+)
+CLUSTERED BY (id) INTO 4 BUCKETS
+STORED BY ICEBERG;
+
+DESCRIBE FORMATTED ice_bucketed_simple;
+
+EXPLAIN INSERT INTO ice_bucketed_simple VALUES (1, 'Alice', 25), (2, 'Bob', 
30);
+
+INSERT INTO ice_bucketed_simple VALUES
+    (1,  'Alice',   25),
+    (2,  'Bob',     30),
+    (3,  'Carrie',  22),
+    (4,  'Danny',   28),
+    (5,  'Eve',     35),
+    (6,  'Frank',   40),
+    (7,  'Grace',   27),
+    (8,  'Henry',   33),
+    (9,  'Ivy',     29),
+    (10, 'Jack',    31);
+
+SELECT * FROM ice_bucketed_simple ORDER BY id;
+
+SELECT COUNT(*) from default.ice_bucketed_simple.files;
+
+-- CLUSTERED BY on multiple columns
+CREATE TABLE ice_bucketed_multi (
+    customer_id int,
+    order_id bigint,
+    product string
+)
+CLUSTERED BY (customer_id, order_id) INTO 8 BUCKETS
+STORED BY ICEBERG;
+
+DESCRIBE FORMATTED ice_bucketed_multi;
+
+EXPLAIN INSERT INTO ice_bucketed_multi VALUES (100, 1001, 'Widget'), (101, 
1002, 'Gadget');
+
+-- Convert EXTERNAL Hive table with CLUSTERED BY to Iceberg
+CREATE EXTERNAL TABLE hive_bucketed (
+    product_id int,
+    category string,
+    price decimal(10,2)
+)
+CLUSTERED BY (product_id) INTO 3 BUCKETS
+STORED AS ORC;
+
+INSERT INTO hive_bucketed VALUES (1, 'Electronics', 299.99), (2, 'Books', 
15.50);
+
+ALTER TABLE hive_bucketed CONVERT TO ICEBERG;
+
+DESCRIBE FORMATTED hive_bucketed;
+
+EXPLAIN INSERT INTO hive_bucketed VALUES (3, 'Furniture', 450.00);
+
+DROP TABLE IF EXISTS ice_bucketed_simple;
+DROP TABLE IF EXISTS ice_bucketed_multi;
+DROP TABLE IF EXISTS hive_bucketed;
diff --git 
a/iceberg/iceberg-handler/src/test/results/positive/llap/iceberg_clustered_by.q.out
 
b/iceberg/iceberg-handler/src/test/results/positive/llap/iceberg_clustered_by.q.out
new file mode 100644
index 00000000000..53b11da616c
--- /dev/null
+++ 
b/iceberg/iceberg-handler/src/test/results/positive/llap/iceberg_clustered_by.q.out
@@ -0,0 +1,685 @@
+PREHOOK: query: CREATE TABLE ice_bucketed_simple (
+    id int,
+    name string,
+    age int
+)
+CLUSTERED BY (id) INTO 4 BUCKETS
+STORED BY ICEBERG
+PREHOOK: type: CREATETABLE
+PREHOOK: Output: database:default
+PREHOOK: Output: default@ice_bucketed_simple
+POSTHOOK: query: CREATE TABLE ice_bucketed_simple (
+    id int,
+    name string,
+    age int
+)
+CLUSTERED BY (id) INTO 4 BUCKETS
+STORED BY ICEBERG
+POSTHOOK: type: CREATETABLE
+POSTHOOK: Output: database:default
+POSTHOOK: Output: default@ice_bucketed_simple
+PREHOOK: query: DESCRIBE FORMATTED ice_bucketed_simple
+PREHOOK: type: DESCTABLE
+PREHOOK: Input: default@ice_bucketed_simple
+POSTHOOK: query: DESCRIBE FORMATTED ice_bucketed_simple
+POSTHOOK: type: DESCTABLE
+POSTHOOK: Input: default@ice_bucketed_simple
+# col_name             data_type               comment             
+id                     int                                         
+name                   string                                      
+age                    int                                         
+                
+# 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\":{\"age\":\"true\",\"id\":\"true\",\"name\":\"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\":\"name\",\"required\":false,\"type\":\"string\"},{\"id\":3,\"name\":\"age\",\"required\":false,\"type\":\"int\"}]}
+       format-version          2                   
+       iceberg.orc.files.only  false               
+#### A masked pattern was here ####
+       numFiles                #Masked#                   
+       numRows                 0                   
+       parquet.compression     zstd                
+       rawDataSize             0                   
+       serialization.format    1                   
+       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                       
+Num Buckets:           4                        
+Bucket Columns:        [id]                     
+Sort Columns:          []                       
+PREHOOK: query: EXPLAIN INSERT INTO ice_bucketed_simple VALUES (1, 'Alice', 
25), (2, 'Bob', 30)
+PREHOOK: type: QUERY
+PREHOOK: Input: _dummy_database@_dummy_table
+PREHOOK: Output: default@ice_bucketed_simple
+POSTHOOK: query: EXPLAIN INSERT INTO ice_bucketed_simple VALUES (1, 'Alice', 
25), (2, 'Bob', 30)
+POSTHOOK: type: QUERY
+POSTHOOK: Input: _dummy_database@_dummy_table
+POSTHOOK: Output: default@ice_bucketed_simple
+STAGE DEPENDENCIES:
+  Stage-1 is a root stage
+  Stage-2 depends on stages: Stage-1
+  Stage-0 depends on stages: Stage-2
+  Stage-3 depends on stages: Stage-0
+
+STAGE PLANS:
+  Stage: Stage-1
+    Tez
+#### A masked pattern was here ####
+      Edges:
+        Reducer 2 <- Map 1 (SIMPLE_EDGE)
+        Reducer 3 <- Reducer 2 (CUSTOM_SIMPLE_EDGE)
+#### A masked pattern was here ####
+      Vertices:
+        Map 1 
+            Map Operator Tree:
+                TableScan
+                  alias: _dummy_table
+                  Row Limit Per Split: 1
+                  Statistics: Num rows: 1 Data size: 10 Basic stats: COMPLETE 
Column stats: COMPLETE
+                  Select Operator
+                    expressions: array(const struct(1,'Alice',25),const 
struct(2,'Bob',30)) (type: array<struct<col1:int,col2:string,col3:int>>)
+                    outputColumnNames: _col0
+                    Statistics: Num rows: 1 Data size: 56 Basic stats: 
COMPLETE Column stats: COMPLETE
+                    UDTF Operator
+                      Statistics: Num rows: 1 Data size: 56 Basic stats: 
COMPLETE Column stats: COMPLETE
+                      function name: inline
+                      Select Operator
+                        expressions: col1 (type: int), col2 (type: string), 
col3 (type: int)
+                        outputColumnNames: _col0, _col1, _col2
+                        Statistics: Num rows: 1 Data size: 8 Basic stats: 
COMPLETE Column stats: COMPLETE
+                        Reduce Output Operator
+                          key expressions: _col0 (type: int)
+                          null sort order: a
+                          sort order: +
+                          Map-reduce partition columns: _col0 (type: int)
+                          Statistics: Num rows: 1 Data size: 8 Basic stats: 
COMPLETE Column stats: COMPLETE
+                          value expressions: _col1 (type: string), _col2 
(type: int)
+            Execution mode: llap
+            LLAP IO: no inputs
+        Reducer 2 
+            Execution mode: vectorized, llap
+            Reduce Operator Tree:
+              Select Operator
+                expressions: KEY.reducesinkkey0 (type: int), VALUE._col0 
(type: string), VALUE._col1 (type: int)
+                outputColumnNames: _col0, _col1, _col2
+                Statistics: Num rows: 1 Data size: 8 Basic stats: COMPLETE 
Column stats: COMPLETE
+                File Output Operator
+                  compressed: false
+                  Statistics: Num rows: 1 Data size: 8 Basic stats: COMPLETE 
Column stats: COMPLETE
+                  table:
+                      input format: 
org.apache.iceberg.mr.hive.HiveIcebergInputFormat
+                      output format: 
org.apache.iceberg.mr.hive.HiveIcebergOutputFormat
+                      serde: org.apache.iceberg.mr.hive.HiveIcebergSerDe
+                      name: default.ice_bucketed_simple
+                Select Operator
+                  expressions: _col0 (type: int), _col1 (type: string), _col2 
(type: int)
+                  outputColumnNames: id, name, age
+                  Statistics: Num rows: 1 Data size: 8 Basic stats: COMPLETE 
Column stats: COMPLETE
+                  Group By Operator
+                    aggregations: min(id), max(id), count(1), count(id), 
compute_bit_vector_hll(id), max(length(name)), avg(COALESCE(length(name),0)), 
count(name), compute_bit_vector_hll(name), min(age), max(age), count(age), 
compute_bit_vector_hll(age)
+                    minReductionHashAggr: 0.4
+                    mode: hash
+                    outputColumnNames: _col0, _col1, _col2, _col3, _col4, 
_col5, _col6, _col7, _col8, _col9, _col10, _col11, _col12
+                    Statistics: Num rows: 1 Data size: 560 Basic stats: 
COMPLETE Column stats: COMPLETE
+                    Reduce Output Operator
+                      null sort order: 
+                      sort order: 
+                      Statistics: Num rows: 1 Data size: 560 Basic stats: 
COMPLETE Column stats: COMPLETE
+                      value expressions: _col0 (type: int), _col1 (type: int), 
_col2 (type: bigint), _col3 (type: bigint), _col4 (type: binary), _col5 (type: 
int), _col6 (type: struct<count:bigint,sum:double,input:int>), _col7 (type: 
bigint), _col8 (type: binary), _col9 (type: int), _col10 (type: int), _col11 
(type: bigint), _col12 (type: binary)
+        Reducer 3 
+            Execution mode: vectorized, llap
+            Reduce Operator Tree:
+              Group By Operator
+                aggregations: min(VALUE._col0), max(VALUE._col1), 
count(VALUE._col2), count(VALUE._col3), compute_bit_vector_hll(VALUE._col4), 
max(VALUE._col5), avg(VALUE._col6), count(VALUE._col7), 
compute_bit_vector_hll(VALUE._col8), min(VALUE._col9), max(VALUE._col10), 
count(VALUE._col11), compute_bit_vector_hll(VALUE._col12)
+                mode: mergepartial
+                outputColumnNames: _col0, _col1, _col2, _col3, _col4, _col5, 
_col6, _col7, _col8, _col9, _col10, _col11, _col12
+                Statistics: Num rows: 1 Data size: 492 Basic stats: COMPLETE 
Column stats: COMPLETE
+                Select Operator
+                  expressions: 'LONG' (type: string), UDFToLong(_col0) (type: 
bigint), UDFToLong(_col1) (type: bigint), (_col2 - _col3) (type: bigint), 
COALESCE(ndv_compute_bit_vector(_col4),0) (type: bigint), _col4 (type: binary), 
'STRING' (type: string), UDFToLong(COALESCE(_col5,0)) (type: bigint), 
COALESCE(_col6,0) (type: double), (_col2 - _col7) (type: bigint), 
COALESCE(ndv_compute_bit_vector(_col8),0) (type: bigint), _col8 (type: binary), 
'LONG' (type: string), UDFToLong(_col9) (typ [...]
+                  outputColumnNames: _col0, _col1, _col2, _col3, _col4, _col5, 
_col6, _col7, _col8, _col9, _col10, _col11, _col12, _col13, _col14, _col15, 
_col16, _col17
+                  Statistics: Num rows: 1 Data size: 794 Basic stats: COMPLETE 
Column stats: COMPLETE
+                  File Output Operator
+                    compressed: false
+                    Statistics: Num rows: 1 Data size: 794 Basic stats: 
COMPLETE Column stats: COMPLETE
+                    table:
+                        input format: 
org.apache.hadoop.mapred.SequenceFileInputFormat
+                        output format: 
org.apache.hadoop.hive.ql.io.HiveSequenceFileOutputFormat
+                        serde: 
org.apache.hadoop.hive.serde2.lazy.LazySimpleSerDe
+
+  Stage: Stage-2
+    Dependency Collection
+
+  Stage: Stage-0
+    Move Operator
+      tables:
+          replace: false
+          table:
+              input format: org.apache.iceberg.mr.hive.HiveIcebergInputFormat
+              output format: org.apache.iceberg.mr.hive.HiveIcebergOutputFormat
+              serde: org.apache.iceberg.mr.hive.HiveIcebergSerDe
+              name: default.ice_bucketed_simple
+
+  Stage: Stage-3
+    Stats Work
+      Basic Stats Work:
+      Column Stats Desc:
+          Columns: id, name, age
+          Column Types: int, string, int
+          Table: default.ice_bucketed_simple
+
+PREHOOK: query: INSERT INTO ice_bucketed_simple VALUES
+    (1,  'Alice',   25),
+    (2,  'Bob',     30),
+    (3,  'Carrie',  22),
+    (4,  'Danny',   28),
+    (5,  'Eve',     35),
+    (6,  'Frank',   40),
+    (7,  'Grace',   27),
+    (8,  'Henry',   33),
+    (9,  'Ivy',     29),
+    (10, 'Jack',    31)
+PREHOOK: type: QUERY
+PREHOOK: Input: _dummy_database@_dummy_table
+PREHOOK: Output: default@ice_bucketed_simple
+POSTHOOK: query: INSERT INTO ice_bucketed_simple VALUES
+    (1,  'Alice',   25),
+    (2,  'Bob',     30),
+    (3,  'Carrie',  22),
+    (4,  'Danny',   28),
+    (5,  'Eve',     35),
+    (6,  'Frank',   40),
+    (7,  'Grace',   27),
+    (8,  'Henry',   33),
+    (9,  'Ivy',     29),
+    (10, 'Jack',    31)
+POSTHOOK: type: QUERY
+POSTHOOK: Input: _dummy_database@_dummy_table
+POSTHOOK: Output: default@ice_bucketed_simple
+PREHOOK: query: SELECT * FROM ice_bucketed_simple ORDER BY id
+PREHOOK: type: QUERY
+PREHOOK: Input: default@ice_bucketed_simple
+#### A masked pattern was here ####
+POSTHOOK: query: SELECT * FROM ice_bucketed_simple ORDER BY id
+POSTHOOK: type: QUERY
+POSTHOOK: Input: default@ice_bucketed_simple
+#### A masked pattern was here ####
+1      Alice   25
+10     Jack    31
+2      Bob     30
+3      Carrie  22
+4      Danny   28
+5      Eve     35
+6      Frank   40
+7      Grace   27
+8      Henry   33
+9      Ivy     29
+PREHOOK: query: SELECT COUNT(*) from default.ice_bucketed_simple.files
+PREHOOK: type: QUERY
+PREHOOK: Input: default@ice_bucketed_simple
+#### A masked pattern was here ####
+POSTHOOK: query: SELECT COUNT(*) from default.ice_bucketed_simple.files
+POSTHOOK: type: QUERY
+POSTHOOK: Input: default@ice_bucketed_simple
+#### A masked pattern was here ####
+4
+PREHOOK: query: CREATE TABLE ice_bucketed_multi (
+    customer_id int,
+    order_id bigint,
+    product string
+)
+CLUSTERED BY (customer_id, order_id) INTO 8 BUCKETS
+STORED BY ICEBERG
+PREHOOK: type: CREATETABLE
+PREHOOK: Output: database:default
+PREHOOK: Output: default@ice_bucketed_multi
+POSTHOOK: query: CREATE TABLE ice_bucketed_multi (
+    customer_id int,
+    order_id bigint,
+    product string
+)
+CLUSTERED BY (customer_id, order_id) INTO 8 BUCKETS
+STORED BY ICEBERG
+POSTHOOK: type: CREATETABLE
+POSTHOOK: Output: database:default
+POSTHOOK: Output: default@ice_bucketed_multi
+PREHOOK: query: DESCRIBE FORMATTED ice_bucketed_multi
+PREHOOK: type: DESCTABLE
+PREHOOK: Input: default@ice_bucketed_multi
+POSTHOOK: query: DESCRIBE FORMATTED ice_bucketed_multi
+POSTHOOK: type: DESCTABLE
+POSTHOOK: Input: default@ice_bucketed_multi
+# col_name             data_type               comment             
+customer_id            int                                         
+order_id               bigint                                      
+product                string                                      
+                
+# 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\":{\"customer_id\":\"true\",\"order_id\":\"true\",\"product\":\"true\"}}
+       EXTERNAL                TRUE                
+       bucketing_version       2                   
+       current-schema          
{\"type\":\"struct\",\"schema-id\":0,\"fields\":[{\"id\":1,\"name\":\"customer_id\",\"required\":false,\"type\":\"int\"},{\"id\":2,\"name\":\"order_id\",\"required\":false,\"type\":\"long\"},{\"id\":3,\"name\":\"product\",\"required\":false,\"type\":\"string\"}]}
+       format-version          2                   
+       iceberg.orc.files.only  false               
+#### A masked pattern was here ####
+       numFiles                #Masked#                   
+       numRows                 0                   
+       parquet.compression     zstd                
+       rawDataSize             0                   
+       serialization.format    1                   
+       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                       
+Num Buckets:           8                        
+Bucket Columns:        [customer_id, order_id]  
+Sort Columns:          []                       
+PREHOOK: query: EXPLAIN INSERT INTO ice_bucketed_multi VALUES (100, 1001, 
'Widget'), (101, 1002, 'Gadget')
+PREHOOK: type: QUERY
+PREHOOK: Input: _dummy_database@_dummy_table
+PREHOOK: Output: default@ice_bucketed_multi
+POSTHOOK: query: EXPLAIN INSERT INTO ice_bucketed_multi VALUES (100, 1001, 
'Widget'), (101, 1002, 'Gadget')
+POSTHOOK: type: QUERY
+POSTHOOK: Input: _dummy_database@_dummy_table
+POSTHOOK: Output: default@ice_bucketed_multi
+STAGE DEPENDENCIES:
+  Stage-1 is a root stage
+  Stage-2 depends on stages: Stage-1
+  Stage-0 depends on stages: Stage-2
+  Stage-3 depends on stages: Stage-0
+
+STAGE PLANS:
+  Stage: Stage-1
+    Tez
+#### A masked pattern was here ####
+      Edges:
+        Reducer 2 <- Map 1 (SIMPLE_EDGE)
+        Reducer 3 <- Reducer 2 (CUSTOM_SIMPLE_EDGE)
+#### A masked pattern was here ####
+      Vertices:
+        Map 1 
+            Map Operator Tree:
+                TableScan
+                  alias: _dummy_table
+                  Row Limit Per Split: 1
+                  Statistics: Num rows: 1 Data size: 10 Basic stats: COMPLETE 
Column stats: COMPLETE
+                  Select Operator
+                    expressions: array(const struct(100,1001,'Widget'),const 
struct(101,1002,'Gadget')) (type: array<struct<col1:int,col2:int,col3:string>>)
+                    outputColumnNames: _col0
+                    Statistics: Num rows: 1 Data size: 56 Basic stats: 
COMPLETE Column stats: COMPLETE
+                    UDTF Operator
+                      Statistics: Num rows: 1 Data size: 56 Basic stats: 
COMPLETE Column stats: COMPLETE
+                      function name: inline
+                      Select Operator
+                        expressions: col1 (type: int), UDFToLong(col2) (type: 
bigint), col3 (type: string)
+                        outputColumnNames: _col0, _col1, _col2
+                        Statistics: Num rows: 1 Data size: 8 Basic stats: 
COMPLETE Column stats: COMPLETE
+                        Reduce Output Operator
+                          key expressions: _col0 (type: int), _col1 (type: 
bigint)
+                          null sort order: aa
+                          sort order: ++
+                          Map-reduce partition columns: _col0 (type: int), 
_col1 (type: bigint)
+                          Statistics: Num rows: 1 Data size: 8 Basic stats: 
COMPLETE Column stats: COMPLETE
+                          value expressions: _col2 (type: string)
+            Execution mode: llap
+            LLAP IO: no inputs
+        Reducer 2 
+            Execution mode: vectorized, llap
+            Reduce Operator Tree:
+              Select Operator
+                expressions: KEY.reducesinkkey0 (type: int), 
KEY.reducesinkkey1 (type: bigint), VALUE._col0 (type: string)
+                outputColumnNames: _col0, _col1, _col2
+                Statistics: Num rows: 1 Data size: 8 Basic stats: COMPLETE 
Column stats: COMPLETE
+                File Output Operator
+                  compressed: false
+                  Statistics: Num rows: 1 Data size: 8 Basic stats: COMPLETE 
Column stats: COMPLETE
+                  table:
+                      input format: 
org.apache.iceberg.mr.hive.HiveIcebergInputFormat
+                      output format: 
org.apache.iceberg.mr.hive.HiveIcebergOutputFormat
+                      serde: org.apache.iceberg.mr.hive.HiveIcebergSerDe
+                      name: default.ice_bucketed_multi
+                Select Operator
+                  expressions: _col0 (type: int), _col1 (type: bigint), _col2 
(type: string)
+                  outputColumnNames: customer_id, order_id, product
+                  Statistics: Num rows: 1 Data size: 8 Basic stats: COMPLETE 
Column stats: COMPLETE
+                  Group By Operator
+                    aggregations: min(customer_id), max(customer_id), 
count(1), count(customer_id), compute_bit_vector_hll(customer_id), 
min(order_id), max(order_id), count(order_id), 
compute_bit_vector_hll(order_id), max(length(product)), 
avg(COALESCE(length(product),0)), count(product), 
compute_bit_vector_hll(product)
+                    minReductionHashAggr: 0.4
+                    mode: hash
+                    outputColumnNames: _col0, _col1, _col2, _col3, _col4, 
_col5, _col6, _col7, _col8, _col9, _col10, _col11, _col12
+                    Statistics: Num rows: 1 Data size: 568 Basic stats: 
COMPLETE Column stats: COMPLETE
+                    Reduce Output Operator
+                      null sort order: 
+                      sort order: 
+                      Statistics: Num rows: 1 Data size: 568 Basic stats: 
COMPLETE Column stats: COMPLETE
+                      value expressions: _col0 (type: int), _col1 (type: int), 
_col2 (type: bigint), _col3 (type: bigint), _col4 (type: binary), _col5 (type: 
bigint), _col6 (type: bigint), _col7 (type: bigint), _col8 (type: binary), 
_col9 (type: int), _col10 (type: struct<count:bigint,sum:double,input:int>), 
_col11 (type: bigint), _col12 (type: binary)
+        Reducer 3 
+            Execution mode: vectorized, llap
+            Reduce Operator Tree:
+              Group By Operator
+                aggregations: min(VALUE._col0), max(VALUE._col1), 
count(VALUE._col2), count(VALUE._col3), compute_bit_vector_hll(VALUE._col4), 
min(VALUE._col5), max(VALUE._col6), count(VALUE._col7), 
compute_bit_vector_hll(VALUE._col8), max(VALUE._col9), avg(VALUE._col10), 
count(VALUE._col11), compute_bit_vector_hll(VALUE._col12)
+                mode: mergepartial
+                outputColumnNames: _col0, _col1, _col2, _col3, _col4, _col5, 
_col6, _col7, _col8, _col9, _col10, _col11, _col12
+                Statistics: Num rows: 1 Data size: 500 Basic stats: COMPLETE 
Column stats: COMPLETE
+                Select Operator
+                  expressions: 'LONG' (type: string), UDFToLong(_col0) (type: 
bigint), UDFToLong(_col1) (type: bigint), (_col2 - _col3) (type: bigint), 
COALESCE(ndv_compute_bit_vector(_col4),0) (type: bigint), _col4 (type: binary), 
'LONG' (type: string), _col5 (type: bigint), _col6 (type: bigint), (_col2 - 
_col7) (type: bigint), COALESCE(ndv_compute_bit_vector(_col8),0) (type: 
bigint), _col8 (type: binary), 'STRING' (type: string), 
UDFToLong(COALESCE(_col9,0)) (type: bigint), COALESCE(_c [...]
+                  outputColumnNames: _col0, _col1, _col2, _col3, _col4, _col5, 
_col6, _col7, _col8, _col9, _col10, _col11, _col12, _col13, _col14, _col15, 
_col16, _col17
+                  Statistics: Num rows: 1 Data size: 794 Basic stats: COMPLETE 
Column stats: COMPLETE
+                  File Output Operator
+                    compressed: false
+                    Statistics: Num rows: 1 Data size: 794 Basic stats: 
COMPLETE Column stats: COMPLETE
+                    table:
+                        input format: 
org.apache.hadoop.mapred.SequenceFileInputFormat
+                        output format: 
org.apache.hadoop.hive.ql.io.HiveSequenceFileOutputFormat
+                        serde: 
org.apache.hadoop.hive.serde2.lazy.LazySimpleSerDe
+
+  Stage: Stage-2
+    Dependency Collection
+
+  Stage: Stage-0
+    Move Operator
+      tables:
+          replace: false
+          table:
+              input format: org.apache.iceberg.mr.hive.HiveIcebergInputFormat
+              output format: org.apache.iceberg.mr.hive.HiveIcebergOutputFormat
+              serde: org.apache.iceberg.mr.hive.HiveIcebergSerDe
+              name: default.ice_bucketed_multi
+
+  Stage: Stage-3
+    Stats Work
+      Basic Stats Work:
+      Column Stats Desc:
+          Columns: customer_id, order_id, product
+          Column Types: int, bigint, string
+          Table: default.ice_bucketed_multi
+
+PREHOOK: query: CREATE EXTERNAL TABLE hive_bucketed (
+    product_id int,
+    category string,
+    price decimal(10,2)
+)
+CLUSTERED BY (product_id) INTO 3 BUCKETS
+STORED AS ORC
+PREHOOK: type: CREATETABLE
+PREHOOK: Output: database:default
+PREHOOK: Output: default@hive_bucketed
+POSTHOOK: query: CREATE EXTERNAL TABLE hive_bucketed (
+    product_id int,
+    category string,
+    price decimal(10,2)
+)
+CLUSTERED BY (product_id) INTO 3 BUCKETS
+STORED AS ORC
+POSTHOOK: type: CREATETABLE
+POSTHOOK: Output: database:default
+POSTHOOK: Output: default@hive_bucketed
+PREHOOK: query: INSERT INTO hive_bucketed VALUES (1, 'Electronics', 299.99), 
(2, 'Books', 15.50)
+PREHOOK: type: QUERY
+PREHOOK: Input: _dummy_database@_dummy_table
+PREHOOK: Output: default@hive_bucketed
+POSTHOOK: query: INSERT INTO hive_bucketed VALUES (1, 'Electronics', 299.99), 
(2, 'Books', 15.50)
+POSTHOOK: type: QUERY
+POSTHOOK: Input: _dummy_database@_dummy_table
+POSTHOOK: Output: default@hive_bucketed
+POSTHOOK: Lineage: hive_bucketed.category SCRIPT []
+POSTHOOK: Lineage: hive_bucketed.price SCRIPT []
+POSTHOOK: Lineage: hive_bucketed.product_id SCRIPT []
+PREHOOK: query: ALTER TABLE hive_bucketed CONVERT TO ICEBERG
+PREHOOK: type: ALTERTABLE_CONVERT
+PREHOOK: Input: default@hive_bucketed
+POSTHOOK: query: ALTER TABLE hive_bucketed CONVERT TO ICEBERG
+POSTHOOK: type: ALTERTABLE_CONVERT
+POSTHOOK: Input: default@hive_bucketed
+POSTHOOK: Output: default@hive_bucketed
+PREHOOK: query: DESCRIBE FORMATTED hive_bucketed
+PREHOOK: type: DESCTABLE
+PREHOOK: Input: default@hive_bucketed
+POSTHOOK: query: DESCRIBE FORMATTED hive_bucketed
+POSTHOOK: type: DESCTABLE
+POSTHOOK: Input: default@hive_bucketed
+# col_name             data_type               comment             
+product_id             int                                         
+category               string                                      
+price                  decimal(10,2)                               
+                
+# 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\":{\"category\":\"true\",\"price\":\"true\",\"product_id\":\"true\"}}
+       EXTERNAL                TRUE                
+       MIGRATED_TO_ICEBERG     true                
+       bucketing_version       2                   
+       current-schema          
{\"type\":\"struct\",\"schema-id\":0,\"fields\":[{\"id\":1,\"name\":\"product_id\",\"required\":false,\"type\":\"int\"},{\"id\":2,\"name\":\"category\",\"required\":false,\"type\":\"string\"},{\"id\":3,\"name\":\"price\",\"required\":false,\"type\":\"decimal(10,
 2)\"}]}
+       current-snapshot-id     #Masked#
+       current-snapshot-summary        
{\"added-data-files\":\"2\",\"added-records\":\"2\",\"added-files-size\":\"#Masked#\",\"changed-partition-count\":\"1\",\"total-records\":\"2\",\"total-files-size\":\"#Masked#\",\"total-data-files\":\"#Masked#\",\"total-delete-files\":\"0\",\"total-position-deletes\":\"0\",\"total-equality-deletes\":\"0\",\"iceberg-version\":\"#Masked#\"}
+       current-snapshot-timestamp-ms   #Masked#       
+       format-version          2                   
+       iceberg.orc.files.only  true                
+#### A masked pattern was here ####
+       numFiles                #Masked#                   
+       numRows                 2                   
+       parquet.compression     zstd                
+#### A masked pattern was here ####
+       rawDataSize             416                 
+       schema.name-mapping.default     [ {                 
+                                         \"field-id\" : 1, 
+                                         \"names\" : [ \"product_id\" ]
+                                       }, {                
+                                         \"field-id\" : 2, 
+                                         \"names\" : [ \"category\" ]
+                                       }, {                
+                                         \"field-id\" : 3, 
+                                         \"names\" : [ \"price\" ]
+                                       } ]                 
+       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.format.default    orc                 
+       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                       
+Num Buckets:           3                        
+Bucket Columns:        [product_id]             
+Sort Columns:          []                       
+PREHOOK: query: EXPLAIN INSERT INTO hive_bucketed VALUES (3, 'Furniture', 
450.00)
+PREHOOK: type: QUERY
+PREHOOK: Input: _dummy_database@_dummy_table
+PREHOOK: Output: default@hive_bucketed
+POSTHOOK: query: EXPLAIN INSERT INTO hive_bucketed VALUES (3, 'Furniture', 
450.00)
+POSTHOOK: type: QUERY
+POSTHOOK: Input: _dummy_database@_dummy_table
+POSTHOOK: Output: default@hive_bucketed
+STAGE DEPENDENCIES:
+  Stage-1 is a root stage
+  Stage-2 depends on stages: Stage-1
+  Stage-0 depends on stages: Stage-2
+  Stage-3 depends on stages: Stage-0
+
+STAGE PLANS:
+  Stage: Stage-1
+    Tez
+#### A masked pattern was here ####
+      Edges:
+        Reducer 2 <- Map 1 (SIMPLE_EDGE)
+        Reducer 3 <- Reducer 2 (CUSTOM_SIMPLE_EDGE)
+#### A masked pattern was here ####
+      Vertices:
+        Map 1 
+            Map Operator Tree:
+                TableScan
+                  alias: _dummy_table
+                  Row Limit Per Split: 1
+                  Statistics: Num rows: 1 Data size: 10 Basic stats: COMPLETE 
Column stats: COMPLETE
+                  Select Operator
+                    expressions: array(const struct(3,'Furniture',450)) (type: 
array<struct<col1:int,col2:string,col3:decimal(3,0)>>)
+                    outputColumnNames: _col0
+                    Statistics: Num rows: 1 Data size: 48 Basic stats: 
COMPLETE Column stats: COMPLETE
+                    UDTF Operator
+                      Statistics: Num rows: 1 Data size: 48 Basic stats: 
COMPLETE Column stats: COMPLETE
+                      function name: inline
+                      Select Operator
+                        expressions: col1 (type: int), col2 (type: string), 
CAST( col3 AS decimal(10,2)) (type: decimal(10,2))
+                        outputColumnNames: _col0, _col1, _col2
+                        Statistics: Num rows: 1 Data size: 112 Basic stats: 
COMPLETE Column stats: COMPLETE
+                        Reduce Output Operator
+                          key expressions: _col0 (type: int)
+                          null sort order: a
+                          sort order: +
+                          Map-reduce partition columns: _col0 (type: int)
+                          Statistics: Num rows: 1 Data size: 112 Basic stats: 
COMPLETE Column stats: COMPLETE
+                          value expressions: _col1 (type: string), _col2 
(type: decimal(10,2))
+            Execution mode: llap
+            LLAP IO: no inputs
+        Reducer 2 
+            Execution mode: vectorized, llap
+            Reduce Operator Tree:
+              Select Operator
+                expressions: KEY.reducesinkkey0 (type: int), VALUE._col0 
(type: string), VALUE._col1 (type: decimal(10,2))
+                outputColumnNames: _col0, _col1, _col2
+                Statistics: Num rows: 1 Data size: 112 Basic stats: COMPLETE 
Column stats: COMPLETE
+                File Output Operator
+                  compressed: false
+                  Statistics: Num rows: 1 Data size: 112 Basic stats: COMPLETE 
Column stats: COMPLETE
+                  table:
+                      input format: 
org.apache.iceberg.mr.hive.HiveIcebergInputFormat
+                      output format: 
org.apache.iceberg.mr.hive.HiveIcebergOutputFormat
+                      serde: org.apache.iceberg.mr.hive.HiveIcebergSerDe
+                      name: default.hive_bucketed
+                Select Operator
+                  expressions: _col0 (type: int), _col1 (type: string), _col2 
(type: decimal(10,2))
+                  outputColumnNames: product_id, category, price
+                  Statistics: Num rows: 1 Data size: 112 Basic stats: COMPLETE 
Column stats: COMPLETE
+                  Group By Operator
+                    aggregations: min(product_id), max(product_id), count(1), 
count(product_id), compute_bit_vector_hll(product_id), max(length(category)), 
avg(COALESCE(length(category),0)), count(category), 
compute_bit_vector_hll(category), min(price), max(price), count(price), 
compute_bit_vector_hll(price)
+                    minReductionHashAggr: 0.4
+                    mode: hash
+                    outputColumnNames: _col0, _col1, _col2, _col3, _col4, 
_col5, _col6, _col7, _col8, _col9, _col10, _col11, _col12
+                    Statistics: Num rows: 1 Data size: 776 Basic stats: 
COMPLETE Column stats: COMPLETE
+                    Reduce Output Operator
+                      null sort order: 
+                      sort order: 
+                      Statistics: Num rows: 1 Data size: 776 Basic stats: 
COMPLETE Column stats: COMPLETE
+                      value expressions: _col0 (type: int), _col1 (type: int), 
_col2 (type: bigint), _col3 (type: bigint), _col4 (type: binary), _col5 (type: 
int), _col6 (type: struct<count:bigint,sum:double,input:int>), _col7 (type: 
bigint), _col8 (type: binary), _col9 (type: decimal(10,2)), _col10 (type: 
decimal(10,2)), _col11 (type: bigint), _col12 (type: binary)
+        Reducer 3 
+            Execution mode: vectorized, llap
+            Reduce Operator Tree:
+              Group By Operator
+                aggregations: min(VALUE._col0), max(VALUE._col1), 
count(VALUE._col2), count(VALUE._col3), compute_bit_vector_hll(VALUE._col4), 
max(VALUE._col5), avg(VALUE._col6), count(VALUE._col7), 
compute_bit_vector_hll(VALUE._col8), min(VALUE._col9), max(VALUE._col10), 
count(VALUE._col11), compute_bit_vector_hll(VALUE._col12)
+                mode: mergepartial
+                outputColumnNames: _col0, _col1, _col2, _col3, _col4, _col5, 
_col6, _col7, _col8, _col9, _col10, _col11, _col12
+                Statistics: Num rows: 1 Data size: 708 Basic stats: COMPLETE 
Column stats: COMPLETE
+                Select Operator
+                  expressions: 'LONG' (type: string), UDFToLong(_col0) (type: 
bigint), UDFToLong(_col1) (type: bigint), (_col2 - _col3) (type: bigint), 
COALESCE(ndv_compute_bit_vector(_col4),0) (type: bigint), _col4 (type: binary), 
'STRING' (type: string), UDFToLong(COALESCE(_col5,0)) (type: bigint), 
COALESCE(_col6,0) (type: double), (_col2 - _col7) (type: bigint), 
COALESCE(ndv_compute_bit_vector(_col8),0) (type: bigint), _col8 (type: binary), 
'DECIMAL' (type: string), _col9 (type: decim [...]
+                  outputColumnNames: _col0, _col1, _col2, _col3, _col4, _col5, 
_col6, _col7, _col8, _col9, _col10, _col11, _col12, _col13, _col14, _col15, 
_col16, _col17
+                  Statistics: Num rows: 1 Data size: 1005 Basic stats: 
COMPLETE Column stats: COMPLETE
+                  File Output Operator
+                    compressed: false
+                    Statistics: Num rows: 1 Data size: 1005 Basic stats: 
COMPLETE Column stats: COMPLETE
+                    table:
+                        input format: 
org.apache.hadoop.mapred.SequenceFileInputFormat
+                        output format: 
org.apache.hadoop.hive.ql.io.HiveSequenceFileOutputFormat
+                        serde: 
org.apache.hadoop.hive.serde2.lazy.LazySimpleSerDe
+
+  Stage: Stage-2
+    Dependency Collection
+
+  Stage: Stage-0
+    Move Operator
+      tables:
+          replace: false
+          table:
+              input format: org.apache.iceberg.mr.hive.HiveIcebergInputFormat
+              output format: org.apache.iceberg.mr.hive.HiveIcebergOutputFormat
+              serde: org.apache.iceberg.mr.hive.HiveIcebergSerDe
+              name: default.hive_bucketed
+
+  Stage: Stage-3
+    Stats Work
+      Basic Stats Work:
+      Column Stats Desc:
+          Columns: product_id, category, price
+          Column Types: int, string, decimal(10,2)
+          Table: default.hive_bucketed
+
+PREHOOK: query: DROP TABLE IF EXISTS ice_bucketed_simple
+PREHOOK: type: DROPTABLE
+PREHOOK: Input: default@ice_bucketed_simple
+PREHOOK: Output: database:default
+PREHOOK: Output: default@ice_bucketed_simple
+POSTHOOK: query: DROP TABLE IF EXISTS ice_bucketed_simple
+POSTHOOK: type: DROPTABLE
+POSTHOOK: Input: default@ice_bucketed_simple
+POSTHOOK: Output: database:default
+POSTHOOK: Output: default@ice_bucketed_simple
+PREHOOK: query: DROP TABLE IF EXISTS ice_bucketed_multi
+PREHOOK: type: DROPTABLE
+PREHOOK: Input: default@ice_bucketed_multi
+PREHOOK: Output: database:default
+PREHOOK: Output: default@ice_bucketed_multi
+POSTHOOK: query: DROP TABLE IF EXISTS ice_bucketed_multi
+POSTHOOK: type: DROPTABLE
+POSTHOOK: Input: default@ice_bucketed_multi
+POSTHOOK: Output: database:default
+POSTHOOK: Output: default@ice_bucketed_multi
+PREHOOK: query: DROP TABLE IF EXISTS hive_bucketed
+PREHOOK: type: DROPTABLE
+PREHOOK: Input: default@hive_bucketed
+PREHOOK: Output: database:default
+PREHOOK: Output: default@hive_bucketed
+POSTHOOK: query: DROP TABLE IF EXISTS hive_bucketed
+POSTHOOK: type: DROPTABLE
+POSTHOOK: Input: default@hive_bucketed
+POSTHOOK: Output: database:default
+POSTHOOK: Output: default@hive_bucketed
diff --git a/itests/src/test/resources/testconfiguration.properties 
b/itests/src/test/resources/testconfiguration.properties
index 64a761acfdd..214afa40d6a 100644
--- a/itests/src/test/resources/testconfiguration.properties
+++ b/itests/src/test/resources/testconfiguration.properties
@@ -429,6 +429,7 @@ iceberg.llap.query.files=\
   iceberg_bucket_map_join_7.q,\
   iceberg_bucket_map_join_8.q,\
   iceberg_clustered.q,\
+  iceberg_clustered_by.q,\
   iceberg_create_locally_ordered_table.q,\
   iceberg_create_locally_zordered_table.q,\
   iceberg_merge_delete_files.q,\
@@ -480,6 +481,7 @@ iceberg.llap.only.query.files=\
   iceberg_bucket_map_join_7.q,\
   iceberg_bucket_map_join_8.q,\
   iceberg_clustered.q,\
+  iceberg_clustered_by.q,\
   iceberg_create_locally_ordered_table.q,\
   iceberg_create_locally_zordered_table.q,\
   iceberg_merge_delete_files.q,\
diff --git 
a/ql/src/java/org/apache/hadoop/hive/ql/ddl/table/info/desc/formatter/TextDescTableFormatter.java
 
b/ql/src/java/org/apache/hadoop/hive/ql/ddl/table/info/desc/formatter/TextDescTableFormatter.java
index b1dd9738572..75f39291cd0 100644
--- 
a/ql/src/java/org/apache/hadoop/hive/ql/ddl/table/info/desc/formatter/TextDescTableFormatter.java
+++ 
b/ql/src/java/org/apache/hadoop/hive/ql/ddl/table/info/desc/formatter/TextDescTableFormatter.java
@@ -311,9 +311,11 @@ private void getStorageDescriptorInfo(StringBuilder 
tableInfo, Table table, Stor
     formatOutput("InputFormat:", storageDesc.getInputFormat(), tableInfo);
     formatOutput("OutputFormat:", storageDesc.getOutputFormat(), tableInfo);
     formatOutput("Compressed:", storageDesc.isCompressed() ? "Yes" : "No", 
tableInfo);
+    // Show bucket columns for Native table or Iceberg table with CLUSTERED BY 
(Hive-style bucketing)
+    boolean hasClusteredBy = storageDesc.getNumBuckets() > 0
+            && CollectionUtils.isNotEmpty(storageDesc.getBucketCols());
     if (!table.isNonNative() || table.getStorageHandler() == null ||
-        !table.getStorageHandler().supportsPartitionTransform()) {
-      // The Iceberg partition transform already contains the bucketing 
information, and these are not relevant there
+        !table.getStorageHandler().supportsPartitionTransform() || 
hasClusteredBy) {
       formatOutput("Num Buckets:", 
String.valueOf(storageDesc.getNumBuckets()), tableInfo);
       formatOutput("Bucket Columns:", storageDesc.getBucketCols().toString(), 
tableInfo);
     }

Reply via email to