github-actions[bot] commented on code in PR #66538:
URL: https://github.com/apache/doris/pull/66538#discussion_r3878723996


##########
fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/commands/insert/InsertUtils.java:
##########
@@ -514,6 +612,62 @@ private static DataType targetTypeForInlineValue(
         return targetType;
     }
 
+    private static NamedExpression resolveIcebergDefaultReferences(
+            NamedExpression value,
+            Optional<IcebergWriteSchemaContext> writeSchemaContext,
+            List<String> targetNameParts) {
+        if (!writeSchemaContext.isPresent()) {
+            return value;
+        }
+        Expression resolved = value.rewriteDownShortCircuit(candidate -> {
+            if (!(candidate instanceof Default)) {
+                return candidate;
+            }
+            Expression reference = candidate.child(0);
+            if (!(reference instanceof UnboundSlot)) {
+                throw new AnalysisException("DEFAULT requires a column 
reference");
+            }
+            List<String> nameParts = ((UnboundSlot) reference).getNameParts();
+            if (nameParts.size() > 1) {
+                ConnectContext context = Preconditions.checkNotNull(
+                        ConnectContext.get(),
+                        "Qualified DEFAULT requires a ConnectContext");
+                UpdateCommand.checkAssignmentColumn(
+                        context, nameParts, targetNameParts, null);
+            }
+            return writeSchemaContext.get().resolveWriteDefault(
+                    nameParts.get(nameParts.size() - 1));
+        });
+        Preconditions.checkState(resolved instanceof NamedExpression,
+                "Inline table value must remain a named expression after 
DEFAULT resolution");
+        return (NamedExpression) resolved;
+    }
+
+    private static Plan resolveIcebergSelectDefaultReferences(
+            Plan query,
+            Optional<IcebergWriteSchemaContext> writeSchemaContext,
+            List<String> targetNameParts) {
+        return query.rewriteUp(plan -> {

Review Comment:
   [P1] Resolve DEFAULTs in every INSERT query representation
   
   This traversal handles only Project/one-row expressions. Grouped outputs 
live on `LogicalAggregate`/`LogicalRepeat`; WITH producers are extra plans 
attached later; scalar subqueries own a query plan inside a leaf expression; 
and VALUES under set operations remain an `UnboundInlineTable` leaf. Each shape 
bypasses the pinned-target rewrite, so `DEFAULT(name)` can use a staging 
default or fail binding depending on representation. Rewrite/rebuild all four 
output carriers with the pinned context, and cover INTO/OVERWRITE plus affected 
EXPLAIN forms.



##########
be/src/format_v2/table_reader.cpp:
##########
@@ -753,32 +879,219 @@ Status TableReader::annotate_projected_column(const 
TFileScanSlotInfo& slot_info
     return Status::OK();
 }
 
-std::optional<ColumnDefinition> 
TableReader::_find_current_table_column_by_field_id(
-        int32_t field_id, DataTypePtr type) const {
+std::optional<ColumnDefinition> TableReader::_find_table_column_by_field_id(
+        int32_t field_id, DataTypePtr type, bool include_historical_schemas) 
const {
     if (_scan_params == nullptr || !_scan_params->__isset.history_schema_info 
||
         _scan_params->history_schema_info.empty()) {
         return std::nullopt;
     }
-    const auto* schema = &_scan_params->history_schema_info.front();
+    const auto find_field = [field_id](const schema::external::TSchema& 
schema) {
+        return schema.__isset.root_field ? 
find_external_field_by_id(&schema.root_field, field_id)
+                                         : nullptr;
+    };
+
+    const auto* current_schema = &_scan_params->history_schema_info.front();
     if (_scan_params->__isset.current_schema_id) {
         for (const auto& candidate_schema : _scan_params->history_schema_info) 
{
             if (candidate_schema.__isset.schema_id &&
                 candidate_schema.schema_id == _scan_params->current_schema_id) 
{
-                schema = &candidate_schema;
+                current_schema = &candidate_schema;
                 break;
             }
         }
     }
-    if (!schema->__isset.root_field || !schema->root_field.__isset.fields) {
+    if (const auto* field = find_field(*current_schema); field != nullptr) {
+        return build_schema_column_from_external_field(
+                *field, std::move(type), 
supports_iceberg_scan_semantics_v1(_scan_params));
+    }
+    if (const auto* split_schema = _split_schema(); split_schema != nullptr) {
+        if (const auto* field = find_field(*split_schema); field != nullptr) {
+            return build_schema_column_from_external_field(
+                    *field, std::move(type), 
supports_iceberg_scan_semantics_v1(_scan_params));
+        }
+    }
+    if (!include_historical_schemas) {
         return std::nullopt;
     }
-    for (const auto& field_ptr : schema->root_field.fields) {
-        const auto* field = get_field_ptr(field_ptr);
-        if (field != nullptr && field->__isset.id && field->id == field_id) {
-            return build_schema_column_from_external_field(*field, 
std::move(type));
+
+    const schema::external::TSchema* latest_schema = nullptr;
+    const schema::external::TField* latest_field = nullptr;
+    for (const auto& candidate_schema : _scan_params->history_schema_info) {
+        if (&candidate_schema == current_schema) {
+            continue;
+        }
+        const auto* candidate_field = find_field(candidate_schema);
+        if (candidate_field == nullptr) {
+            continue;
+        }
+        if (latest_schema == nullptr || (candidate_schema.__isset.schema_id &&
+                                         (!latest_schema->__isset.schema_id ||
+                                          candidate_schema.schema_id > 
latest_schema->schema_id))) {
+            latest_schema = &candidate_schema;
+            latest_field = candidate_field;
         }
     }
-    return std::nullopt;
+    if (latest_field == nullptr) {
+        return std::nullopt;
+    }
+    return build_schema_column_from_external_field(
+            *latest_field, std::move(type), 
supports_iceberg_scan_semantics_v1(_scan_params));
+}
+
+std::optional<std::vector<ColumnDefinition>> 
TableReader::_find_table_column_path_by_field_id(
+        int32_t field_id, DataTypePtr leaf_type, bool 
include_historical_schemas) const {
+    if (_scan_params == nullptr || !_scan_params->__isset.history_schema_info 
||
+        _scan_params->history_schema_info.empty()) {
+        return std::nullopt;
+    }
+    const auto build_path = [&](const schema::external::TSchema& schema)
+            -> std::optional<std::vector<ColumnDefinition>> {
+        auto external_path = find_external_struct_field_path_by_id(schema, 
field_id);
+        if (!external_path.has_value()) {
+            return std::nullopt;
+        }
+
+        std::vector<DataTypePtr> path_types(external_path->size());
+        path_types.back() = leaf_type;
+        for (size_t index = external_path->size(); index > 1; --index) {
+            const auto* parent = (*external_path)[index - 2];
+            const auto* child = (*external_path)[index - 1];
+            DORIS_CHECK(parent != nullptr);
+            DORIS_CHECK(child != nullptr);
+            DORIS_CHECK(child->__isset.name);
+            if (!parent->__isset.nestedField || 
!parent->nestedField.__isset.struct_field) {
+                return std::nullopt;
+            }
+            DataTypePtr path_type = std::make_shared<DataTypeStruct>(
+                    DataTypes {path_types[index - 1]}, Strings {child->name});
+            if (parent->__isset.is_optional && parent->is_optional) {
+                path_type = make_nullable(path_type);
+            }
+            path_types[index - 2] = std::move(path_type);
+        }
+
+        std::vector<ColumnDefinition> result;
+        result.reserve(external_path->size());
+        for (size_t index = 0; index < external_path->size(); ++index) {
+            result.push_back(build_schema_column_metadata_from_external_field(

Review Comment:
   [P1] Keep synthetic ancestor types consistent after promotion
   
   `path_types` builds the ancestors from the delete-file leaf type before this 
metadata restore. With an old INT delete key and current BIGINT `payload.k`, 
that yields a `STRUCT<INT>` ancestor but a BIGINT leaf definition. If an older 
data file lacks the whole defaulted struct, V2 creates the default as 
`STRUCT<INT>`, then `NestedStructFieldExpr` allocates BIGINT and inserts the 
INT child before the later cast back to the delete key, causing an invalid 
column-type operation. Restore the current leaf type first and build ancestors 
bottom-up from it (or keep the whole path consistently historical), with 
Parquet/ORC missing-struct plus INT-to-BIGINT coverage.



-- 
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