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

ayushsaxena 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 ed6e001d926 HIVE-29252: Iceberg: [V3] Add support for Column Defaults 
with Alter commands (#6120)
ed6e001d926 is described below

commit ed6e001d9268ccba8ef2c7fee15ca336b5b5a78e
Author: Ayush Saxena <[email protected]>
AuthorDate: Sat Oct 18 19:34:42 2025 +0530

    HIVE-29252: Iceberg: [V3] Add support for Column Defaults with Alter 
commands (#6120)
---
 .../apache/iceberg/hive/HiveSchemaConverter.java   |  35 +----
 .../org/apache/iceberg/hive/HiveSchemaUtil.java    | 114 ++++++++++++++-
 .../apache/iceberg/hive/TestHiveSchemaUtil.java    |   2 +-
 .../iceberg/mr/hive/HiveIcebergMetaHook.java       |  91 ++++++++++--
 .../iceberg_replace_column_with_default_change.q   |  12 ++
 .../positive/iceberg_alter_default_column.q        |  49 +++++++
 ...ceberg_replace_column_with_default_change.q.out |  30 ++++
 .../positive/iceberg_alter_default_column.q.out    | 161 +++++++++++++++++++++
 .../org/apache/hadoop/hive/ql/parse/HiveParser.g   |  13 +-
 .../org/apache/hadoop/hive/ql/ddl/DDLUtils.java    |  13 ++
 .../column/add/AlterTableAddColumnsAnalyzer.java   |  10 +-
 .../change/AlterTableChangeColumnAnalyzer.java     |  21 ++-
 .../replace/AlterTableReplaceColumnsAnalyzer.java  |   9 +-
 .../ql/ddl/table/constraint/ConstraintsUtils.java  |   5 +-
 .../ql/ddl/table/create/CreateTableAnalyzer.java   |   2 +-
 .../hadoop/hive/ql/parse/BaseSemanticAnalyzer.java |   8 +-
 .../hadoop/hive/ql/session/SessionStateUtil.java   |   1 +
 17 files changed, 506 insertions(+), 70 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 d7891fe7601..330f67e32bb 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
@@ -22,7 +22,6 @@
 import java.util.Collections;
 import java.util.List;
 import java.util.Map;
-import org.apache.commons.lang3.StringUtils;
 import org.apache.hadoop.hive.serde2.typeinfo.DecimalTypeInfo;
 import org.apache.hadoop.hive.serde2.typeinfo.ListTypeInfo;
 import org.apache.hadoop.hive.serde2.typeinfo.MapTypeInfo;
@@ -31,11 +30,8 @@
 import org.apache.hadoop.hive.serde2.typeinfo.TypeInfo;
 import org.apache.iceberg.Schema;
 import org.apache.iceberg.expressions.Expressions;
-import org.apache.iceberg.expressions.Literal;
 import org.apache.iceberg.relocated.com.google.common.base.Preconditions;
-import org.apache.iceberg.relocated.com.google.common.base.Splitter;
 import org.apache.iceberg.relocated.com.google.common.collect.Lists;
-import org.apache.iceberg.types.Conversions;
 import org.apache.iceberg.types.Type;
 import org.apache.iceberg.types.Types;
 import org.slf4j.Logger;
@@ -63,9 +59,9 @@ static Schema convert(List<String> names, List<TypeInfo> 
typeInfos, List<String>
     return new Schema(converter.convertInternal(names, typeInfos, 
defaultValues, comments));
   }
 
-  static Type convert(TypeInfo typeInfo, boolean autoConvert) {
+  public static Type convert(TypeInfo typeInfo, boolean autoConvert, String 
defaultValue) {
     HiveSchemaConverter converter = new HiveSchemaConverter(autoConvert);
-    return converter.convertType(typeInfo, null);
+    return converter.convertType(typeInfo, defaultValue);
   }
 
   List<Types.NestedField> convertInternal(List<String> names, List<TypeInfo> 
typeInfos,
@@ -86,7 +82,7 @@ List<Types.NestedField> convertInternal(List<String> names, 
List<TypeInfo> typeI
 
       if (defaultValues.containsKey(columnName)) {
         if (type.isPrimitiveType()) {
-          Object icebergDefaultValue = 
getDefaultValue(defaultValues.get(columnName), type);
+          Object icebergDefaultValue = 
HiveSchemaUtil.getDefaultValue(defaultValues.get(columnName), type);
           fieldBuilder.withWriteDefault(Expressions.lit(icebergDefaultValue));
         } else if (!type.isStructType()) {
           throw new UnsupportedOperationException(
@@ -99,13 +95,6 @@ List<Types.NestedField> convertInternal(List<String> names, 
List<TypeInfo> typeI
     return result;
   }
 
-  private static Object getDefaultValue(String defaultValue, Type type) {
-    return switch (type.typeId()) {
-      case DATE, TIME, TIMESTAMP, TIMESTAMP_NANO -> 
Literal.of(stripQuotes(defaultValue)).to(type).value();
-      default -> Conversions.fromPartitionString(type, 
stripQuotes(defaultValue));
-    };
-  }
-
   Type convertType(TypeInfo typeInfo, String defaultValue) {
     switch (typeInfo.getCategory()) {
       case PRIMITIVE:
@@ -162,7 +151,7 @@ Type convertType(TypeInfo typeInfo, String defaultValue) {
         StructTypeInfo structTypeInfo = (StructTypeInfo) typeInfo;
         List<Types.NestedField> fields =
             convertInternal(structTypeInfo.getAllStructFieldNames(), 
structTypeInfo.getAllStructFieldTypeInfos(),
-                getDefaultValuesMap(defaultValue), Collections.emptyList());
+                HiveSchemaUtil.getDefaultValuesMap(defaultValue), 
Collections.emptyList());
         return Types.StructType.of(fields);
       case MAP:
         MapTypeInfo mapTypeInfo = (MapTypeInfo) typeInfo;
@@ -182,20 +171,4 @@ Type convertType(TypeInfo typeInfo, String defaultValue) {
         throw new IllegalArgumentException("Unknown type " + 
typeInfo.getCategory());
     }
   }
-
-  private static Map<String, String> getDefaultValuesMap(String defaultValue) {
-    if (StringUtils.isEmpty(defaultValue)) {
-      return Collections.emptyMap();
-    }
-    // For Struct, the default value is expected to be in key:value format
-    return 
Splitter.on(',').trimResults().withKeyValueSeparator(':').split(stripQuotes(defaultValue));
-  }
-
-  public static String stripQuotes(String val) {
-    if (val.charAt(0) == '\'' && val.charAt(val.length() - 1) == '\'' ||
-        val.charAt(0) == '"' && val.charAt(val.length() - 1) == '"') {
-      return val.substring(1, val.length() - 1);
-    }
-    return val;
-  }
 }
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 ce563b1e55d..362ea6a1006 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
@@ -27,6 +27,7 @@
 import java.util.Optional;
 import java.util.Set;
 import java.util.stream.Collectors;
+import org.apache.commons.lang3.StringUtils;
 import org.apache.hadoop.hive.metastore.api.FieldSchema;
 import org.apache.hadoop.hive.serde2.typeinfo.TypeInfo;
 import org.apache.hadoop.hive.serde2.typeinfo.TypeInfoUtils;
@@ -34,8 +35,11 @@
 import org.apache.iceberg.Schema;
 import org.apache.iceberg.data.GenericRecord;
 import org.apache.iceberg.data.Record;
+import org.apache.iceberg.expressions.Literal;
+import org.apache.iceberg.relocated.com.google.common.base.Splitter;
 import org.apache.iceberg.relocated.com.google.common.collect.Lists;
 import org.apache.iceberg.relocated.com.google.common.collect.Maps;
+import org.apache.iceberg.types.Conversions;
 import org.apache.iceberg.types.Type;
 import org.apache.iceberg.types.Types;
 import org.apache.iceberg.util.DateTimeUtil;
@@ -140,23 +144,29 @@ public static TypeInfo convert(Type type) {
 
   /**
    * Converts a Hive typeInfo object to an Iceberg type.
-   * @param typeInfo The Hive type
+   *
+   * @param typeInfo     The Hive type
+   * @param defaultValue the default value for the column, if any
    * @return The Iceberg type
    */
-  public static Type convert(TypeInfo typeInfo) {
-    return HiveSchemaConverter.convert(typeInfo, false);
+  public static Type convert(TypeInfo typeInfo, String defaultValue) {
+    return HiveSchemaConverter.convert(typeInfo, false, defaultValue);
   }
 
   /**
    * Returns a SchemaDifference containing those fields which are present in 
only one of the collections, as well as
    * those fields which are present in both (in terms of the name) but their 
type or comment has changed.
-   * @param minuendCollection Collection of fields to subtract from
+   *
+   * @param minuendCollection    Collection of fields to subtract from
    * @param subtrahendCollection Collection of fields to subtract
-   * @param bothDirections Whether or not to compute the missing fields from 
the minuendCollection as well
+   * @param schema               the iceberg table schema, if available. Used 
to compare default values
+   * @param defaultValues        the column default values
+   * @param bothDirections       Whether or not to compute the missing fields 
from the minuendCollection as well
    * @return the difference between the two schemas
    */
   public static SchemaDifference getSchemaDiff(Collection<FieldSchema> 
minuendCollection,
-                                               Collection<FieldSchema> 
subtrahendCollection, boolean bothDirections) {
+      Collection<FieldSchema> subtrahendCollection, Schema schema, Map<String, 
String> defaultValues,
+      boolean bothDirections) {
     SchemaDifference difference = new SchemaDifference();
 
     for (FieldSchema first : minuendCollection) {
@@ -178,13 +188,61 @@ public static SchemaDifference 
getSchemaDiff(Collection<FieldSchema> minuendColl
     }
 
     if (bothDirections) {
-      SchemaDifference otherWay = getSchemaDiff(subtrahendCollection, 
minuendCollection, false);
+      SchemaDifference otherWay = getSchemaDiff(subtrahendCollection, 
minuendCollection, null, defaultValues, false);
       otherWay.getMissingFromSecond().forEach(difference::addMissingFromFirst);
     }
 
+    if (schema != null) {
+      for (Types.NestedField field : schema.columns()) {
+        if (!isRemovedField(field, difference.getMissingFromFirst())) {
+          getDefaultValDiff(field, defaultValues, difference);
+        }
+      }
+    }
+
     return difference;
   }
 
+  private static boolean isRemovedField(Types.NestedField field, 
List<FieldSchema> missingFields) {
+    for (FieldSchema fieldSchema : missingFields) {
+      if (fieldSchema.getName().equalsIgnoreCase(field.name())) {
+        return true;
+      }
+    }
+    return false;
+  }
+
+  /**
+   * Computes whether the default value has changed for the given field.
+   * @param field         the field to check for default value change
+   * @param defaultValues the default values for the table schema, if 
available. Used to compare default values
+   * @param difference    the SchemaDifference object to update with the 
default value change if any
+   */
+  private static void getDefaultValDiff(Types.NestedField field, Map<String, 
String> defaultValues,
+      SchemaDifference difference) {
+
+    String defaultStr = defaultValues.get(field.name());
+
+    // Skip if no default at all
+    if (defaultStr == null && field.writeDefault() == null) {
+      return;
+    }
+
+    if (field.type().isPrimitiveType()) {
+      Object expectedDefault = HiveSchemaUtil.getDefaultValue(defaultStr, 
field.type());
+      if (!Objects.equals(expectedDefault, field.writeDefault())) {
+        difference.addDefaultChanged(field, expectedDefault);
+      }
+    } else if (field.type().isStructType()) {
+      Map<String, String> structDefaults = getDefaultValuesMap(defaultStr);
+
+      for (Types.NestedField nested : field.type().asStructType().fields()) {
+        getDefaultValDiff(nested, structDefaults, difference);
+      }
+    }
+  }
+
+
   /**
    * Compares two lists of columns to each other to find the (singular) column 
that was moved. This works ideally for
    * identifying the column that was moved by an ALTER TABLE ... CHANGE COLUMN 
command.
@@ -248,6 +306,7 @@ public static class SchemaDifference {
     private final List<FieldSchema> missingFromSecond = Lists.newArrayList();
     private final List<FieldSchema> typeChanged = Lists.newArrayList();
     private final List<FieldSchema> commentChanged = Lists.newArrayList();
+    private final Map<Types.NestedField, Object> defaultChanged = 
Maps.newHashMap();
 
     public List<FieldSchema> getMissingFromFirst() {
       return missingFromFirst;
@@ -265,9 +324,13 @@ public List<FieldSchema> getCommentChanged() {
       return commentChanged;
     }
 
+    public Map<Types.NestedField, Object> getDefaultChanged() {
+      return defaultChanged;
+    }
+
     public boolean isEmpty() {
       return missingFromFirst.isEmpty() && missingFromSecond.isEmpty() && 
typeChanged.isEmpty() &&
-          commentChanged.isEmpty();
+          commentChanged.isEmpty() && defaultChanged.isEmpty();
     }
 
     void addMissingFromFirst(FieldSchema field) {
@@ -285,6 +348,10 @@ void addTypeChanged(FieldSchema field) {
     void addCommentChanged(FieldSchema field) {
       commentChanged.add(field);
     }
+
+    void addDefaultChanged(Types.NestedField field, Object defaultValue) {
+      defaultChanged.put(field, defaultValue);
+    }
   }
 
 
@@ -408,4 +475,35 @@ public static Object convertToWriteType(Object value, Type 
type) {
 
     return value; // fallback
   }
+
+  public static Map<String, String> getDefaultValuesMap(String defaultValue) {
+    if (StringUtils.isEmpty(defaultValue)) {
+      return Collections.emptyMap();
+    }
+    // For Struct, the default value is expected to be in key:value format
+    return 
Splitter.on(',').trimResults().withKeyValueSeparator(':').split(stripQuotes(defaultValue));
+  }
+
+  public static String stripQuotes(String val) {
+    if (val.charAt(0) == '\'' && val.charAt(val.length() - 1) == '\'' ||
+        val.charAt(0) == '"' && val.charAt(val.length() - 1) == '"') {
+      return val.substring(1, val.length() - 1);
+    }
+    return val;
+  }
+
+  public static Object getDefaultValue(String defaultValue, Type type) {
+    if (defaultValue == null) {
+      return null;
+    }
+    return switch (type.typeId()) {
+      case DATE, TIME, TIMESTAMP, TIMESTAMP_NANO ->
+          Literal.of(stripQuotes(defaultValue)).to(type).value();
+      default -> Conversions.fromPartitionString(type, 
stripQuotes(defaultValue));
+    };
+  }
+
+  public static Type getStructType(TypeInfo typeInfo, String defaultValue) {
+    return HiveSchemaConverter.convert(typeInfo, false, defaultValue);
+  }
 }
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 1b2bae823ce..6daf3aeca5d 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
@@ -220,7 +220,7 @@ private void checkConvert(TypeInfo typeInfo, Type type) {
     // Convert to TypeInfo
     assertThat(HiveSchemaUtil.convert(type)).isEqualTo(typeInfo);
     // Convert to Type
-    assertEquals(type, HiveSchemaUtil.convert(typeInfo));
+    assertEquals(type, HiveSchemaUtil.convert(typeInfo, null));
   }
 
   /**
diff --git 
a/iceberg/iceberg-handler/src/main/java/org/apache/iceberg/mr/hive/HiveIcebergMetaHook.java
 
b/iceberg/iceberg-handler/src/main/java/org/apache/iceberg/mr/hive/HiveIcebergMetaHook.java
index 6c1a696f5e2..77d9915adde 100644
--- 
a/iceberg/iceberg-handler/src/main/java/org/apache/iceberg/mr/hive/HiveIcebergMetaHook.java
+++ 
b/iceberg/iceberg-handler/src/main/java/org/apache/iceberg/mr/hive/HiveIcebergMetaHook.java
@@ -31,6 +31,7 @@
 import java.util.Optional;
 import java.util.function.Function;
 import java.util.stream.Collectors;
+import java.util.stream.Stream;
 import org.apache.commons.collections.CollectionUtils;
 import org.apache.commons.lang3.ObjectUtils;
 import org.apache.hadoop.conf.Configuration;
@@ -45,6 +46,7 @@
 import org.apache.hadoop.hive.metastore.api.FieldSchema;
 import org.apache.hadoop.hive.metastore.api.MetaException;
 import org.apache.hadoop.hive.metastore.api.RequestPartsSpec;
+import org.apache.hadoop.hive.metastore.api.SQLDefaultConstraint;
 import org.apache.hadoop.hive.metastore.api.SerDeInfo;
 import org.apache.hadoop.hive.metastore.api.StorageDescriptor;
 import org.apache.hadoop.hive.metastore.api.hive_metastoreConstants;
@@ -102,6 +104,7 @@
 import org.apache.iceberg.exceptions.NoSuchTableException;
 import org.apache.iceberg.expressions.Expression;
 import org.apache.iceberg.expressions.Expressions;
+import org.apache.iceberg.expressions.Literal;
 import org.apache.iceberg.expressions.ResidualEvaluator;
 import org.apache.iceberg.expressions.UnboundPredicate;
 import org.apache.iceberg.expressions.UnboundTerm;
@@ -163,6 +166,7 @@ public class HiveIcebergMetaHook extends 
BaseHiveIcebergMetaHook {
   private Transaction transaction;
   private AlterTableType currentAlterTableOp;
   private HiveLock commitLock;
+  private List<SQLDefaultConstraint> sqlDefaultConstraints;
 
   public HiveIcebergMetaHook(Configuration conf) {
     super(conf);
@@ -502,6 +506,7 @@ public void 
commitAlterTable(org.apache.hadoop.hive.metastore.api.Table hmsTable
           if (transaction != null) {
             transaction.commitTransaction();
           }
+          setSqlDefaultConstraints();
           break;
         case ADDPROPS:
         case DROPPROPS:
@@ -518,6 +523,16 @@ public void 
commitAlterTable(org.apache.hadoop.hive.metastore.api.Table hmsTable
     }
   }
 
+  private void setSqlDefaultConstraints() {
+    try {
+      if (sqlDefaultConstraints != null && !sqlDefaultConstraints.isEmpty()) {
+        
SessionState.get().getHiveDb().addDefaultConstraint(sqlDefaultConstraints);
+      }
+    } catch (Exception e) {
+      throw new RuntimeException(e);
+    }
+  }
+
   @Override
   public void rollbackAlterTable(org.apache.hadoop.hive.metastore.api.Table 
hmsTable, EnvironmentContext context) {
     if (commitLock == null) {
@@ -654,15 +669,24 @@ private void setFileFormat(String format) {
 
   private void handleAddColumns(org.apache.hadoop.hive.metastore.api.Table 
hmsTable) {
     Collection<FieldSchema> addedCols =
-        HiveSchemaUtil.getSchemaDiff(hmsTable.getSd().getCols(), 
HiveSchemaUtil.convert(icebergTable.schema()), false)
+        HiveSchemaUtil.getSchemaDiff(hmsTable.getSd().getCols(), 
HiveSchemaUtil.convert(icebergTable.schema()), null,
+                null, false)
             .getMissingFromSecond();
     if (!addedCols.isEmpty()) {
       transaction = icebergTable.newTransaction();
       updateSchema = transaction.updateSchema();
     }
+    this.sqlDefaultConstraints =
+        (List<SQLDefaultConstraint>) SessionStateUtil.getResource(conf, 
SessionStateUtil.COLUMN_DEFAULTS).orElse(null);
+    Map<String, String> defaultValues = 
Stream.ofNullable(sqlDefaultConstraints).flatMap(Collection::stream)
+        .collect(Collectors.toMap(SQLDefaultConstraint::getColumn_name, 
SQLDefaultConstraint::getDefault_value));
     for (FieldSchema addedCol : addedCols) {
-      updateSchema.addColumn(addedCol.getName(),
-          
HiveSchemaUtil.convert(TypeInfoUtils.getTypeInfoFromTypeString(addedCol.getType())),
 addedCol.getComment());
+      String defaultValue = defaultValues.get(addedCol.getName());
+      Type type = 
HiveSchemaUtil.convert(TypeInfoUtils.getTypeInfoFromTypeString(addedCol.getType()),
 defaultValue);
+      Literal<Object> defaultVal = Optional.ofNullable(defaultValue).filter(v 
-> !type.isStructType())
+          .map(v -> Expressions.lit(HiveSchemaUtil.getDefaultValue(v, 
type))).orElse(null);
+
+      updateSchema.addColumn(addedCol.getName(), type, addedCol.getComment(), 
defaultVal);
     }
     updateSchema.commit();
   }
@@ -671,7 +695,8 @@ private void 
handleDropColumn(org.apache.hadoop.hive.metastore.api.Table hmsTabl
     List<FieldSchema> hmsCols = hmsTable.getSd().getCols();
     List<FieldSchema> icebergCols = 
HiveSchemaUtil.convert(icebergTable.schema());
 
-    List<FieldSchema> removedCols = HiveSchemaUtil.getSchemaDiff(icebergCols, 
hmsCols, false).getMissingFromSecond();
+    List<FieldSchema> removedCols =
+        HiveSchemaUtil.getSchemaDiff(icebergCols, hmsCols, null, null, 
false).getMissingFromSecond();
     if (removedCols.isEmpty()) {
       return;
     }
@@ -685,7 +710,14 @@ private void 
handleDropColumn(org.apache.hadoop.hive.metastore.api.Table hmsTabl
   private void handleReplaceColumns(org.apache.hadoop.hive.metastore.api.Table 
hmsTable) throws MetaException {
     List<FieldSchema> hmsCols = hmsTable.getSd().getCols();
     List<FieldSchema> icebergCols = 
HiveSchemaUtil.convert(icebergTable.schema());
-    HiveSchemaUtil.SchemaDifference schemaDifference = 
HiveSchemaUtil.getSchemaDiff(hmsCols, icebergCols, true);
+
+    List<SQLDefaultConstraint> defaultConstraints =
+        (List<SQLDefaultConstraint>) SessionStateUtil.getResource(conf, 
SessionStateUtil.COLUMN_DEFAULTS).orElse(null);
+    Map<String, String> defaultValues = 
Optional.ofNullable(defaultConstraints).orElse(Collections.emptyList()).stream()
+        .collect(Collectors.toMap(SQLDefaultConstraint::getColumn_name, 
SQLDefaultConstraint::getDefault_value));
+
+    HiveSchemaUtil.SchemaDifference schemaDifference =
+        HiveSchemaUtil.getSchemaDiff(hmsCols, icebergCols, 
icebergTable.schema(), defaultValues, true);
 
     // if there are columns dropped, let's remove them from the iceberg schema 
as well so we can compare the order
     if (!schemaDifference.getMissingFromFirst().isEmpty()) {
@@ -697,9 +729,11 @@ private void 
handleReplaceColumns(org.apache.hadoop.hive.metastore.api.Table hms
 
     // limit the scope of this operation to only dropping columns
     if (!schemaDifference.getMissingFromSecond().isEmpty() || 
!schemaDifference.getTypeChanged().isEmpty() ||
-        !schemaDifference.getCommentChanged().isEmpty() || outOfOrder != null) 
{
+        !schemaDifference.getCommentChanged().isEmpty() || outOfOrder != null 
||
+        !schemaDifference.getDefaultChanged().isEmpty()) {
       throw new MetaException("Unsupported operation to use REPLACE COLUMNS 
for adding a column, changing a " +
-          "column type, column comment or reordering columns. Only use REPLACE 
COLUMNS for dropping columns. " +
+          "column type, column comment, column default or reordering columns. 
" +
+          "Only use REPLACE COLUMNS for dropping columns. " +
           "For the other operations, consider using the ADD COLUMNS or CHANGE 
COLUMN commands.");
     }
 
@@ -722,7 +756,12 @@ private void 
handleChangeColumn(org.apache.hadoop.hive.metastore.api.Table hmsTa
     List<FieldSchema> hmsCols = hmsTable.getSd().getCols();
     List<FieldSchema> icebergCols = 
HiveSchemaUtil.convert(icebergTable.schema());
     // compute schema difference for renames, type/comment changes
-    HiveSchemaUtil.SchemaDifference schemaDifference = 
HiveSchemaUtil.getSchemaDiff(hmsCols, icebergCols, true);
+    List<SQLDefaultConstraint> defaultConstraints =
+        (List<SQLDefaultConstraint>) SessionStateUtil.getResource(conf, 
SessionStateUtil.COLUMN_DEFAULTS).orElse(null);
+    Map<String, String> defaultValues = 
Optional.ofNullable(defaultConstraints).orElse(Collections.emptyList()).stream()
+        .collect(Collectors.toMap(SQLDefaultConstraint::getColumn_name, 
SQLDefaultConstraint::getDefault_value));
+    HiveSchemaUtil.SchemaDifference schemaDifference =
+        HiveSchemaUtil.getSchemaDiff(hmsCols, icebergCols, null, null, true);
     // check column reorder (which could happen even in the absence of any 
rename, type or comment change)
     Map<String, String> renameMapping = ImmutableMap.of();
     if (!schemaDifference.getMissingFromSecond().isEmpty()) {
@@ -732,12 +771,12 @@ private void 
handleChangeColumn(org.apache.hadoop.hive.metastore.api.Table hmsTa
     }
     Pair<String, Optional<String>> outOfOrder = 
HiveSchemaUtil.getReorderedColumn(hmsCols, icebergCols, renameMapping);
 
-    if (!schemaDifference.isEmpty() || outOfOrder != null) {
+    if (!schemaDifference.isEmpty() || outOfOrder != null || 
!defaultValues.isEmpty()) {
       transaction = icebergTable.newTransaction();
       updateSchema = transaction.updateSchema();
     } else {
       // we should get here if the user didn't change anything about the column
-      // i.e. no changes to the name, type, comment or order
+      // i.e. no changes to the name, type, comment, default or order
       LOG.info("Found no difference between new and old schema for ALTER TABLE 
CHANGE COLUMN for" +
           " table: {}. There will be no Iceberg commit.", 
hmsTable.getTableName());
       return;
@@ -776,11 +815,41 @@ private void 
handleChangeColumn(org.apache.hadoop.hive.metastore.api.Table hmsTa
         updateSchema.moveFirst(outOfOrder.first());
       }
     }
+
+    // case 5: handle change of default values
+    handleDefaultValues(defaultValues, renameMapping, 
icebergTable.schema().columns(), "");
     updateSchema.commit();
 
     handlePartitionRename(schemaDifference);
   }
 
+  /**
+   * Updates the default values of the fields.
+   * @param defaultValues the map containing the default values.
+   * @param renameMapping the rename mapping of the columns
+   * @param columns the columns of the table
+   * @param prefix the prefix of the columns, empty unless a filed of struct.
+   */
+  private void handleDefaultValues(Map<String, String> defaultValues, 
Map<String, String> renameMapping,
+      List<Types.NestedField> columns, String prefix) {
+    if (!defaultValues.isEmpty()) {
+      for (Map.Entry<String, String> field : defaultValues.entrySet()) {
+        String simpleName =
+            renameMapping.containsKey(field.getKey()) ? 
renameMapping.get(field.getKey()) : field.getKey();
+        String qualifiedName = prefix + simpleName;
+        Type fieldType =
+            columns.stream().filter(col -> 
col.name().equalsIgnoreCase(simpleName)).findFirst().get().type();
+        if (fieldType.isStructType()) {
+          Map<String, String> structDefaults = 
HiveSchemaUtil.getDefaultValuesMap(field.getValue());
+          handleDefaultValues(structDefaults, renameMapping, 
fieldType.asStructType().fields(), qualifiedName + ".");
+        } else {
+          updateSchema.updateColumnDefault(qualifiedName,
+              Expressions.lit(HiveSchemaUtil.getDefaultValue(field.getValue(), 
fieldType)));
+        }
+      }
+    }
+  }
+
   private void handlePartitionRename(HiveSchemaUtil.SchemaDifference 
schemaDifference) {
     // in case a partition column has been renamed, spec needs to be adjusted 
too
     if (!schemaDifference.getMissingFromSecond().isEmpty()) {
@@ -795,7 +864,7 @@ private void 
handlePartitionRename(HiveSchemaUtil.SchemaDifference schemaDiffere
   }
 
   private Type.PrimitiveType getPrimitiveTypeOrThrow(FieldSchema field) throws 
MetaException {
-    Type newType = 
HiveSchemaUtil.convert(TypeInfoUtils.getTypeInfoFromTypeString(field.getType()));
+    Type newType = 
HiveSchemaUtil.convert(TypeInfoUtils.getTypeInfoFromTypeString(field.getType()),
 null);
     if (!(newType instanceof Type.PrimitiveType)) {
       throw new MetaException(String.format("Cannot promote type of column: 
'%s' to a non-primitive type: %s.",
           field.getName(), newType));
diff --git 
a/iceberg/iceberg-handler/src/test/queries/negative/iceberg_replace_column_with_default_change.q
 
b/iceberg/iceberg-handler/src/test/queries/negative/iceberg_replace_column_with_default_change.q
new file mode 100644
index 00000000000..3eefaf306b0
--- /dev/null
+++ 
b/iceberg/iceberg-handler/src/test/queries/negative/iceberg_replace_column_with_default_change.q
@@ -0,0 +1,12 @@
+CREATE TABLE ice_t (
+  id INT,
+  name STRING DEFAULT 'unknown',
+  age INT DEFAULT 25
+)
+STORED BY ICEBERG
+TBLPROPERTIES ('format-version'='3');
+
+ALTER TABLE ice_t REPLACE COLUMNS (
+  id INT,
+  name STRING DEFAULT 'unknown1',
+  age INT DEFAULT 25);
\ No newline at end of file
diff --git 
a/iceberg/iceberg-handler/src/test/queries/positive/iceberg_alter_default_column.q
 
b/iceberg/iceberg-handler/src/test/queries/positive/iceberg_alter_default_column.q
new file mode 100644
index 00000000000..4ec6abf4faf
--- /dev/null
+++ 
b/iceberg/iceberg-handler/src/test/queries/positive/iceberg_alter_default_column.q
@@ -0,0 +1,49 @@
+CREATE TABLE ice_t (
+  id INT)
+STORED BY ICEBERG
+TBLPROPERTIES ('format-version'='3');
+
+INSERT INTO ice_t (id) VALUES (1);
+
+ALTER TABLE ice_t ADD COLUMNS (point STRUCT<x:INT, y:INT> DEFAULT 'x:100,y:99',
+  name STRING DEFAULT 'unknown',
+  age INT DEFAULT 25,
+  salary DOUBLE DEFAULT 50000.0,
+  is_active BOOLEAN DEFAULT TRUE,
+  created_date DATE DEFAULT '2024-01-01',
+  created_ts TIMESTAMP DEFAULT '2024-01-01T10:00:00',
+  score DECIMAL(5,2) DEFAULT 100.00,
+  category STRING DEFAULT 'general');
+
+INSERT INTO ice_t (id) VALUES (2);
+
+SELECT * FROM ice_t ORDER BY id;
+
+ALTER TABLE ice_t REPLACE COLUMNS (id INT,
+  point STRUCT<x:INT, y:INT> DEFAULT 'x:100,y:99',
+  name STRING DEFAULT 'unknown',
+  age INT DEFAULT 25,
+  salary DOUBLE DEFAULT 50000.0,
+  is_active BOOLEAN DEFAULT TRUE,
+  created_date DATE DEFAULT '2024-01-01',
+  created_ts TIMESTAMP DEFAULT '2024-01-01T10:00:00',
+  category STRING DEFAULT 'general');
+
+SELECT * FROM ice_t ORDER BY id;
+
+-- change default of a field of Struct column
+ALTER TABLE ice_t CHANGE COLUMN point point STRUCT<x:INT, y:INT> DEFAULT 
'x:100,y:88';
+
+-- rename and change default value of age column
+ALTER TABLE ice_t CHANGE COLUMN age age_new int DEFAULT 21;
+
+INSERT INTO ice_t (id) VALUES (3);
+
+SELECT * FROM ice_t ORDER BY id;
+
+-- Rename the struct column with default changes
+ALTER TABLE ice_t CHANGE COLUMN point point_new STRUCT<x:INT, y:INT> DEFAULT 
'x:55,y:88';
+
+INSERT INTO ice_t (id) VALUES (4);
+
+SELECT * FROM ice_t ORDER BY id;
\ No newline at end of file
diff --git 
a/iceberg/iceberg-handler/src/test/results/negative/iceberg_replace_column_with_default_change.q.out
 
b/iceberg/iceberg-handler/src/test/results/negative/iceberg_replace_column_with_default_change.q.out
new file mode 100644
index 00000000000..0297b5f65ef
--- /dev/null
+++ 
b/iceberg/iceberg-handler/src/test/results/negative/iceberg_replace_column_with_default_change.q.out
@@ -0,0 +1,30 @@
+PREHOOK: query: CREATE TABLE ice_t (
+  id INT,
+  name STRING DEFAULT 'unknown',
+  age INT DEFAULT 25
+)
+STORED BY ICEBERG
+TBLPROPERTIES ('format-version'='3')
+PREHOOK: type: CREATETABLE
+PREHOOK: Output: database:default
+PREHOOK: Output: default@ice_t
+POSTHOOK: query: CREATE TABLE ice_t (
+  id INT,
+  name STRING DEFAULT 'unknown',
+  age INT DEFAULT 25
+)
+STORED BY ICEBERG
+TBLPROPERTIES ('format-version'='3')
+POSTHOOK: type: CREATETABLE
+POSTHOOK: Output: database:default
+POSTHOOK: Output: default@ice_t
+PREHOOK: query: ALTER TABLE ice_t REPLACE COLUMNS (
+  id INT,
+  name STRING DEFAULT 'unknown1',
+  age INT DEFAULT 25)
+PREHOOK: type: ALTERTABLE_REPLACECOLS
+PREHOOK: Input: default@ice_t
+PREHOOK: Output: default@ice_t
+FAILED: Execution Error, return code 40013 from 
org.apache.hadoop.hive.ql.ddl.DDLTask. Unable to alter table. 
MetaException(message:Unsupported operation to use REPLACE COLUMNS for adding a 
column, changing a column type, column comment, column default or reordering 
columns. Only use REPLACE COLUMNS for dropping columns. For the other 
operations, consider using the ADD COLUMNS or CHANGE COLUMN commands.)
+#### A masked pattern was here ####
+
diff --git 
a/iceberg/iceberg-handler/src/test/results/positive/iceberg_alter_default_column.q.out
 
b/iceberg/iceberg-handler/src/test/results/positive/iceberg_alter_default_column.q.out
new file mode 100644
index 00000000000..bf7cdcf4d96
--- /dev/null
+++ 
b/iceberg/iceberg-handler/src/test/results/positive/iceberg_alter_default_column.q.out
@@ -0,0 +1,161 @@
+PREHOOK: query: CREATE TABLE ice_t (
+  id INT)
+STORED BY ICEBERG
+TBLPROPERTIES ('format-version'='3')
+PREHOOK: type: CREATETABLE
+PREHOOK: Output: database:default
+PREHOOK: Output: default@ice_t
+POSTHOOK: query: CREATE TABLE ice_t (
+  id INT)
+STORED BY ICEBERG
+TBLPROPERTIES ('format-version'='3')
+POSTHOOK: type: CREATETABLE
+POSTHOOK: Output: database:default
+POSTHOOK: Output: default@ice_t
+PREHOOK: query: INSERT INTO ice_t (id) VALUES (1)
+PREHOOK: type: QUERY
+PREHOOK: Input: _dummy_database@_dummy_table
+PREHOOK: Output: default@ice_t
+POSTHOOK: query: INSERT INTO ice_t (id) VALUES (1)
+POSTHOOK: type: QUERY
+POSTHOOK: Input: _dummy_database@_dummy_table
+POSTHOOK: Output: default@ice_t
+PREHOOK: query: ALTER TABLE ice_t ADD COLUMNS (point STRUCT<x:INT, y:INT> 
DEFAULT 'x:100,y:99',
+  name STRING DEFAULT 'unknown',
+  age INT DEFAULT 25,
+  salary DOUBLE DEFAULT 50000.0,
+  is_active BOOLEAN DEFAULT TRUE,
+  created_date DATE DEFAULT '2024-01-01',
+  created_ts TIMESTAMP DEFAULT '2024-01-01T10:00:00',
+  score DECIMAL(5,2) DEFAULT 100.00,
+  category STRING DEFAULT 'general')
+PREHOOK: type: ALTERTABLE_ADDCOLS
+PREHOOK: Input: default@ice_t
+PREHOOK: Output: default@ice_t
+POSTHOOK: query: ALTER TABLE ice_t ADD COLUMNS (point STRUCT<x:INT, y:INT> 
DEFAULT 'x:100,y:99',
+  name STRING DEFAULT 'unknown',
+  age INT DEFAULT 25,
+  salary DOUBLE DEFAULT 50000.0,
+  is_active BOOLEAN DEFAULT TRUE,
+  created_date DATE DEFAULT '2024-01-01',
+  created_ts TIMESTAMP DEFAULT '2024-01-01T10:00:00',
+  score DECIMAL(5,2) DEFAULT 100.00,
+  category STRING DEFAULT 'general')
+POSTHOOK: type: ALTERTABLE_ADDCOLS
+POSTHOOK: Input: default@ice_t
+POSTHOOK: Output: default@ice_t
+PREHOOK: query: INSERT INTO ice_t (id) VALUES (2)
+PREHOOK: type: QUERY
+PREHOOK: Input: _dummy_database@_dummy_table
+PREHOOK: Output: default@ice_t
+POSTHOOK: query: INSERT INTO ice_t (id) VALUES (2)
+POSTHOOK: type: QUERY
+POSTHOOK: Input: _dummy_database@_dummy_table
+POSTHOOK: Output: default@ice_t
+PREHOOK: query: SELECT * FROM ice_t ORDER BY id
+PREHOOK: type: QUERY
+PREHOOK: Input: default@ice_t
+PREHOOK: Output: hdfs://### HDFS PATH ###
+POSTHOOK: query: SELECT * FROM ice_t ORDER BY id
+POSTHOOK: type: QUERY
+POSTHOOK: Input: default@ice_t
+POSTHOOK: Output: hdfs://### HDFS PATH ###
+1      NULL    NULL    NULL    NULL    NULL    NULL    NULL    NULL    NULL
+2      {"x":100,"y":99}        unknown 25      50000.0 true    2024-01-01      
2024-01-01 10:00:00     100.00  general
+PREHOOK: query: ALTER TABLE ice_t REPLACE COLUMNS (id INT,
+  point STRUCT<x:INT, y:INT> DEFAULT 'x:100,y:99',
+  name STRING DEFAULT 'unknown',
+  age INT DEFAULT 25,
+  salary DOUBLE DEFAULT 50000.0,
+  is_active BOOLEAN DEFAULT TRUE,
+  created_date DATE DEFAULT '2024-01-01',
+  created_ts TIMESTAMP DEFAULT '2024-01-01T10:00:00',
+  category STRING DEFAULT 'general')
+PREHOOK: type: ALTERTABLE_REPLACECOLS
+PREHOOK: Input: default@ice_t
+PREHOOK: Output: default@ice_t
+POSTHOOK: query: ALTER TABLE ice_t REPLACE COLUMNS (id INT,
+  point STRUCT<x:INT, y:INT> DEFAULT 'x:100,y:99',
+  name STRING DEFAULT 'unknown',
+  age INT DEFAULT 25,
+  salary DOUBLE DEFAULT 50000.0,
+  is_active BOOLEAN DEFAULT TRUE,
+  created_date DATE DEFAULT '2024-01-01',
+  created_ts TIMESTAMP DEFAULT '2024-01-01T10:00:00',
+  category STRING DEFAULT 'general')
+POSTHOOK: type: ALTERTABLE_REPLACECOLS
+POSTHOOK: Input: default@ice_t
+POSTHOOK: Output: default@ice_t
+PREHOOK: query: SELECT * FROM ice_t ORDER BY id
+PREHOOK: type: QUERY
+PREHOOK: Input: default@ice_t
+PREHOOK: Output: hdfs://### HDFS PATH ###
+POSTHOOK: query: SELECT * FROM ice_t ORDER BY id
+POSTHOOK: type: QUERY
+POSTHOOK: Input: default@ice_t
+POSTHOOK: Output: hdfs://### HDFS PATH ###
+1      NULL    NULL    NULL    NULL    NULL    NULL    NULL    NULL
+2      {"x":100,"y":99}        unknown 25      50000.0 true    2024-01-01      
2024-01-01 10:00:00     general
+PREHOOK: query: ALTER TABLE ice_t CHANGE COLUMN point point STRUCT<x:INT, 
y:INT> DEFAULT 'x:100,y:88'
+PREHOOK: type: ALTERTABLE_RENAMECOL
+PREHOOK: Input: default@ice_t
+PREHOOK: Output: default@ice_t
+POSTHOOK: query: ALTER TABLE ice_t CHANGE COLUMN point point STRUCT<x:INT, 
y:INT> DEFAULT 'x:100,y:88'
+POSTHOOK: type: ALTERTABLE_RENAMECOL
+POSTHOOK: Input: default@ice_t
+POSTHOOK: Output: default@ice_t
+PREHOOK: query: ALTER TABLE ice_t CHANGE COLUMN age age_new int DEFAULT 21
+PREHOOK: type: ALTERTABLE_RENAMECOL
+PREHOOK: Input: default@ice_t
+PREHOOK: Output: default@ice_t
+POSTHOOK: query: ALTER TABLE ice_t CHANGE COLUMN age age_new int DEFAULT 21
+POSTHOOK: type: ALTERTABLE_RENAMECOL
+POSTHOOK: Input: default@ice_t
+POSTHOOK: Output: default@ice_t
+PREHOOK: query: INSERT INTO ice_t (id) VALUES (3)
+PREHOOK: type: QUERY
+PREHOOK: Input: _dummy_database@_dummy_table
+PREHOOK: Output: default@ice_t
+POSTHOOK: query: INSERT INTO ice_t (id) VALUES (3)
+POSTHOOK: type: QUERY
+POSTHOOK: Input: _dummy_database@_dummy_table
+POSTHOOK: Output: default@ice_t
+PREHOOK: query: SELECT * FROM ice_t ORDER BY id
+PREHOOK: type: QUERY
+PREHOOK: Input: default@ice_t
+PREHOOK: Output: hdfs://### HDFS PATH ###
+POSTHOOK: query: SELECT * FROM ice_t ORDER BY id
+POSTHOOK: type: QUERY
+POSTHOOK: Input: default@ice_t
+POSTHOOK: Output: hdfs://### HDFS PATH ###
+1      NULL    NULL    NULL    NULL    NULL    NULL    NULL    NULL
+2      {"x":100,"y":99}        unknown 25      50000.0 true    2024-01-01      
2024-01-01 10:00:00     general
+3      {"x":100,"y":88}        unknown 21      50000.0 true    2024-01-01      
2024-01-01 10:00:00     general
+PREHOOK: query: ALTER TABLE ice_t CHANGE COLUMN point point_new STRUCT<x:INT, 
y:INT> DEFAULT 'x:55,y:88'
+PREHOOK: type: ALTERTABLE_RENAMECOL
+PREHOOK: Input: default@ice_t
+PREHOOK: Output: default@ice_t
+POSTHOOK: query: ALTER TABLE ice_t CHANGE COLUMN point point_new STRUCT<x:INT, 
y:INT> DEFAULT 'x:55,y:88'
+POSTHOOK: type: ALTERTABLE_RENAMECOL
+POSTHOOK: Input: default@ice_t
+POSTHOOK: Output: default@ice_t
+PREHOOK: query: INSERT INTO ice_t (id) VALUES (4)
+PREHOOK: type: QUERY
+PREHOOK: Input: _dummy_database@_dummy_table
+PREHOOK: Output: default@ice_t
+POSTHOOK: query: INSERT INTO ice_t (id) VALUES (4)
+POSTHOOK: type: QUERY
+POSTHOOK: Input: _dummy_database@_dummy_table
+POSTHOOK: Output: default@ice_t
+PREHOOK: query: SELECT * FROM ice_t ORDER BY id
+PREHOOK: type: QUERY
+PREHOOK: Input: default@ice_t
+PREHOOK: Output: hdfs://### HDFS PATH ###
+POSTHOOK: query: SELECT * FROM ice_t ORDER BY id
+POSTHOOK: type: QUERY
+POSTHOOK: Input: default@ice_t
+POSTHOOK: Output: hdfs://### HDFS PATH ###
+1      NULL    NULL    NULL    NULL    NULL    NULL    NULL    NULL
+2      {"x":100,"y":99}        unknown 25      50000.0 true    2024-01-01      
2024-01-01 10:00:00     general
+3      {"x":100,"y":88}        unknown 21      50000.0 true    2024-01-01      
2024-01-01 10:00:00     general
+4      {"x":55,"y":88} unknown 21      50000.0 true    2024-01-01      
2024-01-01 10:00:00     general
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 ddce6aa85af..8d4e943052e 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
@@ -2286,12 +2286,17 @@ columnRefOrder
 columnNameType
 @init { pushMsg("column specification", state); }
 @after { popMsg(state); }
-    : colName=identifier colType (KW_COMMENT comment=StringLiteral)?
-    -> {containExcludedCharForCreateTableColumnName($colName.text)}? 
{throwColumnNameException()}
-    -> {$comment == null}? ^(TOK_TABCOL $colName colType)
-    ->                     ^(TOK_TABCOL $colName colType $comment)
+    : colName=identifier colType
+      (KW_COMMENT comment=StringLiteral)?
+      defaultClause
+    -> ^(TOK_TABCOL $colName colType $comment? defaultClause?)
     ;
 
+defaultClause
+    : (KW_DEFAULT v=expression -> ^(TOK_DEFAULT_VALUE $v))?
+    ;
+
+
 columnNameTypeOrConstraint
 @init { pushMsg("column name or constraint", state); }
 @after { popMsg(state); }
diff --git a/ql/src/java/org/apache/hadoop/hive/ql/ddl/DDLUtils.java 
b/ql/src/java/org/apache/hadoop/hive/ql/ddl/DDLUtils.java
index 3ee8d74cea9..c9b74eca9d5 100644
--- a/ql/src/java/org/apache/hadoop/hive/ql/ddl/DDLUtils.java
+++ b/ql/src/java/org/apache/hadoop/hive/ql/ddl/DDLUtils.java
@@ -24,6 +24,7 @@
 import java.util.Optional;
 import java.util.Set;
 
+import org.apache.hadoop.conf.Configuration;
 import org.apache.hadoop.hive.common.TableName;
 import org.apache.hadoop.hive.conf.HiveConf;
 import org.apache.hadoop.hive.conf.HiveConf.ConfVars;
@@ -31,7 +32,9 @@
 import org.apache.hadoop.hive.metastore.TableType;
 import org.apache.hadoop.hive.metastore.api.Database;
 import org.apache.hadoop.hive.metastore.api.FieldSchema;
+import org.apache.hadoop.hive.metastore.api.SQLDefaultConstraint;
 import org.apache.hadoop.hive.metastore.api.hive_metastoreConstants;
+import org.apache.hadoop.hive.ql.ddl.table.constraint.ConstraintsUtils;
 import org.apache.hadoop.hive.ql.hooks.WriteEntity;
 import org.apache.hadoop.hive.ql.hooks.Entity.Type;
 import org.apache.hadoop.hive.ql.metadata.Hive;
@@ -248,4 +251,14 @@ public static boolean hasTransformsInPartitionSpec(Table 
table) {
       table.getStorageHandler().getPartitionTransformSpec(table).stream()
           .anyMatch(spec -> spec.getTransformType() != 
TransformSpec.TransformType.IDENTITY);
   }
+
+  public static void setDefaultColumnValues(Table table, TableName tableName,
+      List<ConstraintsUtils.ConstraintInfo> defaultConstraintsInfo, 
Configuration conf) throws SemanticException {
+    boolean isNativeColumnDefaultSupported = table.getStorageHandler() != null 
&& table.getStorageHandler()
+        .supportsDefaultColumnValues(table.getParameters());
+    List<SQLDefaultConstraint> defaultConstraints = new ArrayList<>();
+    ConstraintsUtils.constraintInfosToDefaultConstraints(tableName, 
defaultConstraintsInfo, defaultConstraints,
+        isNativeColumnDefaultSupported);
+    SessionStateUtil.addResourceOrThrow(conf, 
SessionStateUtil.COLUMN_DEFAULTS, defaultConstraints);
+  }
 }
diff --git 
a/ql/src/java/org/apache/hadoop/hive/ql/ddl/table/column/add/AlterTableAddColumnsAnalyzer.java
 
b/ql/src/java/org/apache/hadoop/hive/ql/ddl/table/column/add/AlterTableAddColumnsAnalyzer.java
index 4b490132165..c4d6a75876e 100644
--- 
a/ql/src/java/org/apache/hadoop/hive/ql/ddl/table/column/add/AlterTableAddColumnsAnalyzer.java
+++ 
b/ql/src/java/org/apache/hadoop/hive/ql/ddl/table/column/add/AlterTableAddColumnsAnalyzer.java
@@ -18,15 +18,18 @@
 
 package org.apache.hadoop.hive.ql.ddl.table.column.add;
 
+import java.util.ArrayList;
 import java.util.List;
 import java.util.Map;
 
 import org.apache.hadoop.hive.common.TableName;
 import org.apache.hadoop.hive.metastore.api.FieldSchema;
 import org.apache.hadoop.hive.ql.QueryState;
+import org.apache.hadoop.hive.ql.ddl.DDLUtils;
 import org.apache.hadoop.hive.ql.ddl.DDLWork;
 import org.apache.hadoop.hive.ql.ddl.DDLSemanticAnalyzerFactory.DDLType;
 import org.apache.hadoop.hive.ql.ddl.table.AbstractAlterTableAnalyzer;
+import org.apache.hadoop.hive.ql.ddl.table.constraint.ConstraintsUtils;
 import org.apache.hadoop.hive.ql.exec.TaskFactory;
 import org.apache.hadoop.hive.ql.io.AcidUtils;
 import org.apache.hadoop.hive.ql.metadata.Table;
@@ -46,7 +49,10 @@ public AlterTableAddColumnsAnalyzer(QueryState queryState) 
throws SemanticExcept
   @Override
   protected void analyzeCommand(TableName tableName, Map<String, String> 
partitionSpec, ASTNode command)
       throws SemanticException {
-    List<FieldSchema> newCols = getColumns((ASTNode) command.getChild(0));
+    List<ConstraintsUtils.ConstraintInfo> defaultConstraintsInfo = new 
ArrayList<>();
+    List<FieldSchema> newCols =
+        getColumns((ASTNode) command.getChild(0), true, 
ctx.getTokenRewriteStream(), new ArrayList<>(), new ArrayList<>(), new 
ArrayList<>(),
+            new ArrayList<>(), defaultConstraintsInfo, new ArrayList<>(), 
conf);
     boolean isCascade = false;
     if (null != command.getFirstChildWithType(HiveParser.TOK_CASCADE)) {
       isCascade = true;
@@ -59,6 +65,8 @@ protected void analyzeCommand(TableName tableName, 
Map<String, String> partition
     }
 
     addInputsOutputsAlterTable(tableName, partitionSpec, desc, desc.getType(), 
false);
+
+    DDLUtils.setDefaultColumnValues(table, tableName, defaultConstraintsInfo, 
conf);
     rootTasks.add(TaskFactory.get(new DDLWork(getInputs(), getOutputs(), 
desc)));
   }
 }
diff --git 
a/ql/src/java/org/apache/hadoop/hive/ql/ddl/table/column/change/AlterTableChangeColumnAnalyzer.java
 
b/ql/src/java/org/apache/hadoop/hive/ql/ddl/table/column/change/AlterTableChangeColumnAnalyzer.java
index cf017895f44..d3f34cc62c9 100644
--- 
a/ql/src/java/org/apache/hadoop/hive/ql/ddl/table/column/change/AlterTableChangeColumnAnalyzer.java
+++ 
b/ql/src/java/org/apache/hadoop/hive/ql/ddl/table/column/change/AlterTableChangeColumnAnalyzer.java
@@ -33,6 +33,7 @@
 import org.apache.hadoop.hive.metastore.api.SkewedInfo;
 import org.apache.hadoop.hive.ql.ErrorMsg;
 import org.apache.hadoop.hive.ql.QueryState;
+import org.apache.hadoop.hive.ql.ddl.DDLUtils;
 import org.apache.hadoop.hive.ql.ddl.DDLWork;
 import org.apache.hadoop.hive.ql.ddl.DDLSemanticAnalyzerFactory.DDLType;
 import org.apache.hadoop.hive.ql.ddl.table.AbstractAlterTableAnalyzer;
@@ -118,6 +119,7 @@ private Constraints getConstraints(TableName tableName, 
ASTNode command, String
     List<SQLUniqueConstraint> uniqueConstraints = null;
     List<SQLNotNullConstraint> notNullConstraints = null;
     List<SQLDefaultConstraint> defaultConstraints = null;
+    List<ConstraintsUtils.ConstraintInfo> defaultConstraintInfo = null;
     List<SQLCheckConstraint> checkConstraints = null;
     if (constraintChild != null) {
       // Process column constraint
@@ -128,10 +130,9 @@ private Constraints getConstraints(TableName tableName, 
ASTNode command, String
             checkConstraints, (ASTNode) command.getChild(2), 
this.ctx.getTokenRewriteStream());
         break;
       case HiveParser.TOK_DEFAULT_VALUE:
-        defaultConstraints = new ArrayList<>();
-        ConstraintsUtils.constraintInfosToDefaultConstraints(tableName,
+        defaultConstraintInfo =
             ConstraintsUtils.processDefaultConstraints(constraintChild, 
ImmutableList.of(newColumnName),
-                (ASTNode) command.getChild(2), 
this.ctx.getTokenRewriteStream()), defaultConstraints, false);
+                (ASTNode) command.getChild(2), 
this.ctx.getTokenRewriteStream());
         break;
       case HiveParser.TOK_NOT_NULL:
         notNullConstraints = new ArrayList<>();
@@ -162,8 +163,18 @@ private Constraints getConstraints(TableName tableName, 
ASTNode command, String
       ConstraintsUtils.validateCheckConstraint(table.getCols(), 
checkConstraints, ctx.getConf());
     }
 
-    if (table.getTableType() == TableType.EXTERNAL_TABLE &&
-        ConstraintsUtils.hasEnabledOrValidatedConstraints(notNullConstraints, 
defaultConstraints, checkConstraints)) {
+    boolean isNativeColumnDefaultSupported = table.getStorageHandler() != null 
&& table.getStorageHandler()
+        .supportsDefaultColumnValues(table.getParameters());
+
+    if (defaultConstraintInfo != null) {
+      defaultConstraints = new ArrayList<>();
+      ConstraintsUtils.constraintInfosToDefaultConstraints(tableName, 
defaultConstraintInfo, defaultConstraints,
+          isNativeColumnDefaultSupported);
+      DDLUtils.setDefaultColumnValues(table, tableName, defaultConstraintInfo, 
ctx.getConf());
+    }
+
+    if (table.getTableType() == TableType.EXTERNAL_TABLE && 
ConstraintsUtils.hasEnabledOrValidatedConstraints(
+        notNullConstraints, defaultConstraints, checkConstraints, 
isNativeColumnDefaultSupported)) {
       throw new SemanticException(ErrorMsg.INVALID_CSTR_SYNTAX.getMsg(
           "Constraints are disallowed with External tables. Only RELY is 
allowed."));
     }
diff --git 
a/ql/src/java/org/apache/hadoop/hive/ql/ddl/table/column/replace/AlterTableReplaceColumnsAnalyzer.java
 
b/ql/src/java/org/apache/hadoop/hive/ql/ddl/table/column/replace/AlterTableReplaceColumnsAnalyzer.java
index 933b80e5393..99a519c2073 100644
--- 
a/ql/src/java/org/apache/hadoop/hive/ql/ddl/table/column/replace/AlterTableReplaceColumnsAnalyzer.java
+++ 
b/ql/src/java/org/apache/hadoop/hive/ql/ddl/table/column/replace/AlterTableReplaceColumnsAnalyzer.java
@@ -18,15 +18,18 @@
 
 package org.apache.hadoop.hive.ql.ddl.table.column.replace;
 
+import java.util.ArrayList;
 import java.util.List;
 import java.util.Map;
 
 import org.apache.hadoop.hive.common.TableName;
 import org.apache.hadoop.hive.metastore.api.FieldSchema;
 import org.apache.hadoop.hive.ql.QueryState;
+import org.apache.hadoop.hive.ql.ddl.DDLUtils;
 import org.apache.hadoop.hive.ql.ddl.DDLWork;
 import org.apache.hadoop.hive.ql.ddl.DDLSemanticAnalyzerFactory.DDLType;
 import org.apache.hadoop.hive.ql.ddl.table.AbstractAlterTableAnalyzer;
+import org.apache.hadoop.hive.ql.ddl.table.constraint.ConstraintsUtils;
 import org.apache.hadoop.hive.ql.exec.TaskFactory;
 import org.apache.hadoop.hive.ql.io.AcidUtils;
 import org.apache.hadoop.hive.ql.metadata.Table;
@@ -46,7 +49,10 @@ public AlterTableReplaceColumnsAnalyzer(QueryState 
queryState) throws SemanticEx
   @Override
   protected void analyzeCommand(TableName tableName, Map<String, String> 
partitionSpec, ASTNode command)
       throws SemanticException {
-    List<FieldSchema> newCols = getColumns((ASTNode) command.getChild(0));
+    List<ConstraintsUtils.ConstraintInfo> defaultConstraintsInfo = new 
ArrayList<>();
+    List<FieldSchema> newCols =
+        getColumns((ASTNode) command.getChild(0), true, 
ctx.getTokenRewriteStream(), new ArrayList<>(),
+            new ArrayList<>(), new ArrayList<>(), new ArrayList<>(), 
defaultConstraintsInfo, new ArrayList<>(), conf);
     boolean isCascade = false;
     if (null != command.getFirstChildWithType(HiveParser.TOK_CASCADE)) {
       isCascade = true;
@@ -59,6 +65,7 @@ protected void analyzeCommand(TableName tableName, 
Map<String, String> partition
     }
 
     addInputsOutputsAlterTable(tableName, partitionSpec, desc, desc.getType(), 
false);
+    DDLUtils.setDefaultColumnValues(table, tableName, defaultConstraintsInfo, 
conf);
     rootTasks.add(TaskFactory.get(new DDLWork(getInputs(), getOutputs(), 
desc)));
   }
 }
diff --git 
a/ql/src/java/org/apache/hadoop/hive/ql/ddl/table/constraint/ConstraintsUtils.java
 
b/ql/src/java/org/apache/hadoop/hive/ql/ddl/table/constraint/ConstraintsUtils.java
index 0d6950b7f62..daac11c1858 100644
--- 
a/ql/src/java/org/apache/hadoop/hive/ql/ddl/table/constraint/ConstraintsUtils.java
+++ 
b/ql/src/java/org/apache/hadoop/hive/ql/ddl/table/constraint/ConstraintsUtils.java
@@ -525,7 +525,8 @@ private static void validateCheckExpr(ExprNodeDesc 
checkExpr) throws SemanticExc
   }
 
   public static boolean 
hasEnabledOrValidatedConstraints(List<SQLNotNullConstraint> notNullConstraints,
-      List<SQLDefaultConstraint> defaultConstraints, List<SQLCheckConstraint> 
checkConstraints) {
+      List<SQLDefaultConstraint> defaultConstraints, List<SQLCheckConstraint> 
checkConstraints,
+      boolean isNativeColumnDefaultSupported) {
     if (notNullConstraints != null) {
       for (SQLNotNullConstraint nnC : notNullConstraints) {
         if (nnC.isEnable_cstr() || nnC.isValidate_cstr()) {
@@ -534,7 +535,7 @@ public static boolean 
hasEnabledOrValidatedConstraints(List<SQLNotNullConstraint
       }
     }
 
-    if (defaultConstraints != null && !defaultConstraints.isEmpty()) {
+    if (defaultConstraints != null && !defaultConstraints.isEmpty() && 
!isNativeColumnDefaultSupported) {
       return true;
     }
 
diff --git 
a/ql/src/java/org/apache/hadoop/hive/ql/ddl/table/create/CreateTableAnalyzer.java
 
b/ql/src/java/org/apache/hadoop/hive/ql/ddl/table/create/CreateTableAnalyzer.java
index 37858b8af0c..28426406c8d 100644
--- 
a/ql/src/java/org/apache/hadoop/hive/ql/ddl/table/create/CreateTableAnalyzer.java
+++ 
b/ql/src/java/org/apache/hadoop/hive/ql/ddl/table/create/CreateTableAnalyzer.java
@@ -655,7 +655,7 @@ ASTNode analyzeCreateTable(ASTNode ast, QB qb, 
PlannerContext plannerCtx)
         SessionStateUtil.addResourceOrThrow(conf, META_TABLE_LOCATION, 
tblLocation);
 
         if (isExt && 
ConstraintsUtils.hasEnabledOrValidatedConstraints(notNullConstraints, 
crtTblDesc.getDefaultConstraints(),
-            checkConstraints)) {
+            checkConstraints, isNativeColumnDefaultSupported)) {
           throw new SemanticException(ErrorMsg.INVALID_CSTR_SYNTAX.getMsg(
               "Constraints are disallowed with External tables. " + "Only RELY 
is allowed."));
         }
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 f0850a27be6..f3415219d26 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
@@ -95,7 +95,6 @@
 import org.apache.hadoop.hive.ql.plan.ExprNodeDesc;
 import org.apache.hadoop.hive.ql.plan.FetchWork;
 import org.apache.hadoop.hive.ql.plan.FileSinkDesc;
-import org.apache.hadoop.hive.ql.plan.HiveOperation;
 import org.apache.hadoop.hive.ql.plan.ListBucketingCtx;
 import org.apache.hadoop.hive.ql.plan.PlanUtils;
 import org.apache.hadoop.hive.ql.plan.TableDesc;
@@ -918,8 +917,6 @@ public static List<FieldSchema> getColumns(
           ASTNode typeChild = (ASTNode) (child.getChild(1));
           col.setType(getTypeStringFromAST(typeChild));
 
-          // child 2 is the optional comment of the column
-          // child 3 is the optional constraint
           ASTNode constraintChild = null;
           if (child.getChildCount() == 4) {
             col.setComment(unescapeSQLString(child.getChild(2).getText()));
@@ -931,8 +928,9 @@ public static List<FieldSchema> getColumns(
             constraintChild = (ASTNode) child.getChild(2);
           }
           if (constraintChild != null) {
-            final TableName tName =
-                getQualifiedTableName((ASTNode) parent.getChild(0), 
MetaStoreUtils.getDefaultCatalog(conf));
+            final TableName tName = constraintChild.getToken().getType() != 
HiveParser.TOK_DEFAULT_VALUE ?
+                getQualifiedTableName((ASTNode) parent.getChild(0), 
MetaStoreUtils.getDefaultCatalog(conf)) :
+                null;
             // TODO CAT - for now always use the default catalog.  Eventually 
will want to see if
             // the user specified a catalog
             // Process column constraint
diff --git 
a/ql/src/java/org/apache/hadoop/hive/ql/session/SessionStateUtil.java 
b/ql/src/java/org/apache/hadoop/hive/ql/session/SessionStateUtil.java
index 32f38bc0126..d1367056855 100644
--- a/ql/src/java/org/apache/hadoop/hive/ql/session/SessionStateUtil.java
+++ b/ql/src/java/org/apache/hadoop/hive/ql/session/SessionStateUtil.java
@@ -39,6 +39,7 @@ public class SessionStateUtil {
   private static final String CONFLICT_DETECTION_FILTER = 
"conflictDetectionFilter.";
   public static final String DEFAULT_TABLE_LOCATION = "defaultLocation";
   public static final String MISSING_COLUMNS = "missingColumns";
+  public static final String COLUMN_DEFAULTS = "columnDefaults";
 
   private SessionStateUtil() {
   }

Reply via email to