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]