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

Gabriel39 pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/doris.git


The following commit(s) were added to refs/heads/master by this push:
     new 58d7b815334 [fix](be) Open children of Iceberg nested struct 
expressions (#69832)
58d7b815334 is described below

commit 58d7b8153346aa4cf2c5f85971e8c3b75fb7dc5c
Author: daidai <[email protected]>
AuthorDate: Wed Nov 4 14:06:33 2026 +0800

    [fix](be) Open children of Iceberg nested struct expressions (#69832)
    
    ### What problem does this PR solve?
    
    Issue Number: #69782
    
    Related PR: #69711
    
    Problem Summary:
    `NestedStructFieldExpr::open()` reuses the prepare guard and skips
    opening its child. Use the normal open lifecycle, completing the nested
    traversal fix after #69711.
    
    ### Release note
    
    Fix initialization of nested struct expressions in FileScannerV2 Iceberg
    equality deletes.
    
    ### Check List (For Author)
    
    - Test
    - [x] Unit Test: added coverage for cast defaults, nullable ancestors,
    and cloned contexts.
    - Behavior changed:
        - [x] Yes. Open nested expression children before execution.
    - Does this need documentation?
        - [x] No.
    
    ### Check List (For Reviewer who merge this PR)
    
    - [ ] Confirm the release note
    - [ ] Confirm test cases
    - [ ] Confirm document
    - [ ] Add branch pick label
---
 be/src/format_v2/table/iceberg_reader.cpp       |  3 +-
 be/test/format_v2/table/iceberg_reader_test.cpp | 81 +++++++++++++++++++++++++
 2 files changed, 83 insertions(+), 1 deletion(-)

diff --git a/be/src/format_v2/table/iceberg_reader.cpp 
b/be/src/format_v2/table/iceberg_reader.cpp
index 210e06c560f..8d5e9852990 100644
--- a/be/src/format_v2/table/iceberg_reader.cpp
+++ b/be/src/format_v2/table/iceberg_reader.cpp
@@ -869,7 +869,8 @@ public:
 
     Status open(RuntimeState* state, VExprContext* context,
                 FunctionContext::FunctionStateScope scope) override {
-        RETURN_IF_ERROR_OR_PREPARED(VExpr::open(state, context, scope));
+        DCHECK(_prepare_finished);
+        RETURN_IF_ERROR(VExpr::open(state, context, scope));
         _open_finished = true;
         return Status::OK();
     }
diff --git a/be/test/format_v2/table/iceberg_reader_test.cpp 
b/be/test/format_v2/table/iceberg_reader_test.cpp
index d5dcea52cde..08edd25fe23 100644
--- a/be/test/format_v2/table/iceberg_reader_test.cpp
+++ b/be/test/format_v2/table/iceberg_reader_test.cpp
@@ -70,6 +70,7 @@
 #include "exprs/runtime_filter_expr.h"
 #include "exprs/vectorized_fn_call.h"
 #include "exprs/vexpr.h"
+#include "exprs/vexpr_context.h"
 #include "exprs/vliteral.h"
 #include "exprs/vslot_ref.h"
 #include "exprs/vtopn_pred.h"
@@ -4715,6 +4716,86 @@ TEST(IcebergV2ReaderTest, 
IcebergEqualityDeleteResolvesFieldIdThroughIdlessWrapp
     std::filesystem::remove_all(test_dir);
 }
 
+TEST(IcebergV2ReaderTest, MissingNestedEqualityKeyOpensStructTraversal) {
+    auto existing = make_file_column(3, "existing", 
std::make_shared<DataTypeInt32>());
+    auto inner = make_file_column(
+            2, "inner",
+            std::make_shared<DataTypeStruct>(DataTypes {existing.type}, 
Strings {"existing"}));
+    inner.children = {existing};
+    auto payload = make_file_column(
+            1, "payload",
+            std::make_shared<DataTypeStruct>(DataTypes {inner.type}, Strings 
{"inner"}));
+    payload.children = {inner};
+
+    auto table_payload = external_struct_schema_field(
+            "payload", 1,
+            {external_struct_schema_field(
+                    "inner", 2,
+                    {external_schema_field("existing", 3),
+                     external_schema_field("k", 4, {}, "7",
+                                           
external_primitive_type(TPrimitiveType::INT), false,
+                                           true)},
+                    true)},
+            true);
+    auto scan_params = make_local_scan_params(FileFormat::PARQUET);
+    
scan_params.__set_iceberg_scan_semantics_version(ICEBERG_SCAN_SEMANTICS_VERSION_2);
+    scan_params.__set_current_schema_id(100);
+    scan_params.__set_history_schema_info({external_schema(100, 
{table_payload})});
+
+    doris::format::iceberg::IcebergTableReader reader;
+    reader._scan_params = &scan_params;
+    
reader._iceberg_params.emplace().__set_original_file_path("nested-key.parquet");
+    const auto key_type = make_nullable(std::make_shared<DataTypeInt64>());
+    // Compare an INT default with a BIGINT tombstone below two nullable 
structs.
+    doris::format::iceberg::IcebergTableReader::EqualityDeleteFilter filter;
+    filter.field_ids = {4};
+    filter.field_names = {"k"};
+    filter.key_types = {make_nullable(std::make_shared<DataTypeInt32>())};
+    filter.delete_key_types = {key_type};
+    FileScanRequest request;
+    VExprSPtr expr;
+    DataTypePtr comparison_type;
+    ASSERT_TRUE(reader._build_missing_equality_delete_key_expr(
+                              filter, 0, {&payload, 
&payload.children.front()}, &request, &expr,
+                              &comparison_type)
+                        .ok());
+    ASSERT_EQ(expr->children().size(), 2);
+    ASSERT_EQ(expr->children()[0]->children().size(), 1);
+    RuntimeState state {TQueryOptions(), TQueryGlobals()};
+    auto context = VExprContext::create_shared(expr);
+    Defer close_context {[&] { context->close(); }};
+    ASSERT_TRUE(context->prepare(&state, RowDescriptor()).ok());
+    ASSERT_TRUE(context->open(&state).ok());
+    ASSERT_TRUE(expr->ready_status().ok());
+    ASSERT_TRUE(expr->children()[0]->ready_status().ok());
+    ASSERT_TRUE(expr->children()[0]->children()[0]->ready_status().ok());
+    ASSERT_TRUE(expr->children()[1]->ready_status().ok());
+
+    auto column = payload.type->create_column();
+    column->insert_many_defaults(3);
+    auto& nullable_payload = assert_cast<ColumnNullable&>(*column);
+    nullable_payload.get_null_map_data()[1] = 0;
+    nullable_payload.get_null_map_data()[2] = 0;
+    auto& nullable_inner = assert_cast<ColumnNullable&>(
+            
assert_cast<ColumnStruct&>(nullable_payload.get_nested_column()).get_column(0));
+    nullable_inner.get_null_map_data()[0] = 0;
+    nullable_inner.get_null_map_data()[2] = 0;
+    Block block;
+    block.insert({std::move(column), payload.type, "payload"});
+
+    VExprContextSPtr cloned_context;
+    ASSERT_TRUE(context->clone(&state, cloned_context).ok());
+    Defer close_clone {[&] { cloned_context->close(); }};
+    for (const auto& ctx : {context, cloned_context}) {
+        ColumnWithTypeAndName result;
+        ASSERT_TRUE(ctx->execute(&block, result).ok());
+        EXPECT_TRUE(result.type->equals(*key_type));
+        expect_nullable_int64_column_optional_values(
+                *result.column,
+                std::vector<std::optional<int64_t>> {std::nullopt, 
std::nullopt, 7});
+    }
+}
+
 // Keep the shared Parquet/ORC reader setup together so both V2 paths assert 
identical semantics.
 // 
NOLINTNEXTLINE(readability-function-cognitive-complexity,readability-function-size)
 TEST(IcebergV2ReaderTest, 
IcebergMissingNestedEqualityKeyPreservesNullableParent) {


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

Reply via email to