Copilot commented on code in PR #60169:
URL: https://github.com/apache/doris/pull/60169#discussion_r2719570518


##########
fe/fe-core/src/main/java/org/apache/doris/datasource/iceberg/IcebergMetadataOps.java:
##########
@@ -716,15 +723,36 @@ public void renameColumn(ExternalTable dorisTable, String 
oldName, String newNam
     public void modifyColumn(ExternalTable dorisTable, Column column, 
ColumnPosition position, long updateTime)
             throws UserException {
         Table icebergTable = IcebergUtils.getIcebergTable(dorisTable);
-        validateForModifyColumn(column, icebergTable);
-        Type icebergType = 
IcebergUtils.dorisTypeToIcebergType(column.getType());
+        NestedField currentCol = 
icebergTable.schema().findField(column.getName());
+        if (currentCol == null) {
+            throw new UserException("Column " + column.getName() + " does not 
exist");
+        }
+
+        validateCommonColumnInfo(column);
         UpdateSchema updateSchema = icebergTable.updateSchema();
-        updateSchema.updateColumn(column.getName(), 
icebergType.asPrimitiveType(), column.getComment());
-        if (column.isAllowNull()) {
-            // we can change a required column to optional, but not the other 
way around
-            // because we don't know whether there is existing data with null 
values.
-            updateSchema.makeColumnOptional(column.getName());
+
+        if (column.getType().isComplexType()) {
+            // Complex type processing branch
+            validateForModifyComplexColumn(column, currentCol);
+            applyComplexTypeChange(updateSchema, column.getName(), 
currentCol.type(), column.getType());
+            if (column.isAllowNull()) {
+                updateSchema.makeColumnOptional(column.getName());
+            }
+            if (!Objects.equals(currentCol.doc(), column.getComment())) {
+                updateSchema.updateColumnDoc(column.getName(), 
column.getComment());
+            }
+        } else {

Review Comment:
   The new complex-type schema evolution logic in `modifyColumn` 
(STRUCT/ARRAY/MAP handling, nested promotions, nullability checks, etc.) isn’t 
covered by any tests in `IcebergMetadataOpTest` or other Iceberg tests, so 
regressions here would be hard to catch. Given that the rest of the Iceberg 
integration has unit tests, please consider adding tests that exercise 
successful and failing `ALTER TABLE ... MODIFY COLUMN` cases for complex types 
(e.g., struct field append, nested array element promotions, map value 
promotions, illegal field renames).



##########
fe/fe-core/src/main/java/org/apache/doris/datasource/iceberg/IcebergMetadataOps.java:
##########
@@ -737,23 +765,162 @@ public void modifyColumn(ExternalTable dorisTable, 
Column column, ColumnPosition
         refreshTable(dorisTable, updateTime);
     }
 
-    private void validateForModifyColumn(Column column, Table icebergTable) 
throws UserException {
-        validateCommonColumnInfo(column);
+    private void validateForModifyColumn(Column column, NestedField 
currentCol) throws UserException {
         // check complex type
         if (column.getType().isComplexType()) {
             throw new UserException("Modify column type to non-primitive type 
is not supported: " + column.getType());
         }
-        // check exist
-        NestedField currentCol = 
icebergTable.schema().findField(column.getName());
-        if (currentCol == null) {
-            throw new UserException("Column " + column.getName() + " does not 
exist");
-        }
         // check nullable
         if (currentCol.isOptional() && !column.isAllowNull()) {
             throw new UserException("Can not change nullable column " + 
column.getName() + " to not null");
         }
     }
 
+    private void validateForModifyComplexColumn(Column column, NestedField 
currentCol) throws UserException {
+        if (!column.getType().isComplexType()) {
+            throw new UserException("Modify column type to non-complex type is 
not supported: " + column.getType());
+        }
+        org.apache.doris.catalog.Type oldDorisType = 
IcebergUtils.icebergTypeToDorisType(
+                currentCol.type(), false, false);
+        org.apache.doris.catalog.Type newDorisType = column.getType();
+        try {
+            ColumnType.checkSupportSchemaChangeForComplexType(oldDorisType, 
newDorisType, false);
+        } catch (DdlException e) {
+            throw new UserException(e.getMessage(), e);
+        }
+        if (currentCol.isOptional() && !column.isAllowNull()) {
+            throw new UserException("Cannot change nullable column " + 
column.getName() + " to not null");
+        }
+        if (column.getDefaultValue() != null || 
column.getDefaultValueExprDef() != null) {
+            throw new UserException("Complex type default value only supports 
NULL");
+        }
+    }
+
+    private void applyComplexTypeChange(UpdateSchema updateSchema, String path,
+            org.apache.iceberg.types.Type oldIcebergType,
+            org.apache.doris.catalog.Type newDorisType) throws UserException {
+        switch (oldIcebergType.typeId()) {
+            case STRUCT:
+                applyStructChange(updateSchema, path, 
oldIcebergType.asStructType(), (StructType) newDorisType);
+                break;
+            case LIST:
+                applyListChange(updateSchema, path, (Types.ListType) 
oldIcebergType, (ArrayType) newDorisType);
+                break;
+            case MAP:

Review Comment:
   `applyComplexTypeChange` decides how to cast `newDorisType` based on 
`oldIcebergType.typeId()`, but there is no validation that the new type has the 
same complex kind (STRUCT/ARRAY/MAP) as the existing column. For example, 
altering a STRUCT column to ARRAY (or a primitive column to a complex type) 
will pass `validateForModifyComplexColumn` but then hit a `ClassCastException` 
when casting `newDorisType` to `StructType`/`ArrayType`/`MapType`, resulting in 
an internal error instead of a user-facing validation error. It would be safer 
to explicitly check in `validateForModifyComplexColumn` (or here) that the old 
and new Doris types are both complex and of the same kind, and throw a 
`UserException` when they differ, before calling this method.
   ```suggestion
               org.apache.doris.catalog.Type newDorisType) throws UserException 
{
           // Ensure the new Doris type is complex before performing any casts 
based on the old Iceberg type.
           if (!newDorisType.isComplexType()) {
               throw new UserException("Modify column type to non-complex type 
is not supported: " + newDorisType);
           }
   
           switch (oldIcebergType.typeId()) {
               case STRUCT:
                   if (!(newDorisType instanceof StructType)) {
                       throw new UserException("Cannot modify STRUCT column to 
type: " + newDorisType);
                   }
                   applyStructChange(updateSchema, path, 
oldIcebergType.asStructType(), (StructType) newDorisType);
                   break;
               case LIST:
                   if (!(newDorisType instanceof ArrayType)) {
                       throw new UserException("Cannot modify LIST/ARRAY column 
to type: " + newDorisType);
                   }
                   applyListChange(updateSchema, path, (Types.ListType) 
oldIcebergType, (ArrayType) newDorisType);
                   break;
               case MAP:
                   if (!(newDorisType instanceof MapType)) {
                       throw new UserException("Cannot modify MAP column to 
type: " + newDorisType);
                   }
   ```



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to