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


##########
be/src/format_v2/table_reader.h:
##########
@@ -1339,32 +1401,107 @@ class TableReader {
             return Status::OK();
         }
         if (mapping.default_expr != nullptr) {
-            if (current_block->rows() == rows) {
-                ColumnWithTypeAndName result;
-                RETURN_IF_ERROR(_execute_default_expr_without_root_type_check(
-                        mapping.default_expr, current_block, &result));
-                ColumnPtr result_column = result.column;
-                RETURN_IF_ERROR(_align_column_nullability(&result_column, 
mapping.table_type));
-                *column = _detach_column(std::move(result_column));
-            } else {
-                DORIS_CHECK(mapping.constant_index.has_value());
-                Block eval_block;
-                
eval_block.insert({mapping.table_type->create_column_const_with_default_value(rows),
-                                   mapping.table_type, 
"__table_reader_const_rows"});
-                ColumnWithTypeAndName result;
-                RETURN_IF_ERROR(_execute_default_expr_without_root_type_check(
-                        mapping.default_expr, &eval_block, &result));
-                ColumnPtr result_column = result.column;
-                RETURN_IF_ERROR(_align_column_nullability(&result_column, 
mapping.table_type));
-                *column = _detach_column(std::move(result_column));
-            }
-            return Status::OK();
+            return _materialize_default_mapping_column(mapping, current_block, 
rows, column);
         }
         ColumnPtr result_column = 
mapping.table_type->create_column_const_with_default_value(rows);
         *column = _detach_column(std::move(result_column));
         return Status::OK();
     }
 
+    Status _materialize_default_mapping_column(const ColumnMapping& mapping, 
Block* current_block,
+                                               size_t rows, ColumnPtr* column) 
{
+        DORIS_CHECK(mapping.default_expr != nullptr);
+        DORIS_CHECK(mapping.table_type != nullptr);
+        DORIS_CHECK(current_block != nullptr);
+        DORIS_CHECK(column != nullptr);
+        if (current_block->rows() == rows) {
+            ColumnWithTypeAndName result;
+            
RETURN_IF_ERROR(_execute_default_expr_without_root_type_check(mapping.default_expr,
+                                                                          
current_block, &result));
+            ColumnPtr result_column = result.column;
+            RETURN_IF_ERROR(_align_column_nullability(&result_column, 
mapping.table_type));
+            *column = _detach_column(std::move(result_column));
+            return Status::OK();
+        }
+
+        DORIS_CHECK(mapping.constant_index.has_value());
+        Block eval_block;
+        
eval_block.insert({mapping.table_type->create_column_const_with_default_value(rows),
+                           mapping.table_type, "__table_reader_const_rows"});
+        ColumnWithTypeAndName result;
+        
RETURN_IF_ERROR(_execute_default_expr_without_root_type_check(mapping.default_expr,
+                                                                      
&eval_block, &result));
+        ColumnPtr result_column = result.column;
+        RETURN_IF_ERROR(_align_column_nullability(&result_column, 
mapping.table_type));
+        *column = _detach_column(std::move(result_column));
+        return Status::OK();
+    }
+
+    Status _materialize_variant_path_mapping_column(const ColumnMapping& 
mapping,
+                                                    Block* current_block, 
size_t rows,
+                                                    ColumnPtr* column,
+                                                    bool 
take_projection_result) {
+        DORIS_CHECK(!mapping.column_paths.empty());
+        DORIS_CHECK(mapping.resolved_variant_path != nullptr);
+        DORIS_CHECK(mapping.table_type != nullptr);
+        DORIS_CHECK(current_block != nullptr);
+        DORIS_CHECK(column != nullptr);
+
+        ColumnPtr root_column;
+        if (mapping.projection != nullptr) {
+            int result_id;
+            auto status = mapping.projection->execute(current_block, 
&result_id);
+            if (!status.ok()) {
+                return Status::InternalError(
+                        "Failed to read Variant root carrier for Path Slot 
'{}' "
+                        "(global_index={}, rows={}): {}, mapping={}",
+                        mapping.table_column_name, 
mapping.global_index.value(), rows,
+                        status.to_string(), mapping.debug_string());
+            }
+            root_column = take_projection_result
+                                  ? 
_take_and_detach_block_column(current_block, result_id)
+                                  : 
current_block->get_by_position(result_id).column;
+        } else if (mapping.default_expr != nullptr) {
+            // Schema-evolution defaults describe the missing root column. 
Apply the same path
+            // extraction after evaluating that root value instead of exposing 
the whole default.
+            RETURN_IF_ERROR(_materialize_default_mapping_column(mapping, 
current_block, rows,
+                                                                &root_column));
+        } else {
+            // A missing root without a default makes every requested subpath 
SQL NULL.
+            *column = _detach_column(
+                    
mapping.table_type->create_column_const_with_default_value(rows));
+            return Status::OK();
+        }
+
+        const ColumnPtr materialized = 
root_column->convert_to_full_column_if_const();
+        const IColumn* physical = materialized.get();
+        std::span<const uint8_t> outer_nulls;
+        if (const auto* nullable = 
check_and_get_column<ColumnNullable>(physical)) {
+            outer_nulls = nullable->get_null_map_data();
+            physical = &nullable->get_nested_column();
+        }
+        const auto* variant = check_and_get_column<ColumnVariantV2>(physical);
+        if (variant == nullptr) {
+            return Status::NotSupported(
+                    "Variant Path Slot '{}' requires a ColumnVariantV2 root 
carrier, got {}. "
+                    "Legacy ColumnVariant path extraction is not equivalent 
because it also needs "
+                    "sparse/document fallback semantics",
+                    mapping.table_column_name, materialized->get_name());
+        }
+        if (variant->size() != rows) {
+            return Status::InternalError(
+                    "Variant root carrier for Path Slot '{}' has {} rows, 
expected {}",
+                    mapping.table_column_name, variant->size(), rows);
+        }
+
+        ColumnPtr result;
+        RETURN_IF_ERROR(extract_variant_element_v2(*variant, 
*mapping.resolved_variant_path,

Review Comment:
   [P1] Size batches from the full fallback carrier
   
   Current production readers ignore `variant_paths`, so a Path-only request 
still materializes the complete Variant root here and then returns only the 
much smaller extracted leaf. `FileScannerV2` starts with a 32-row probe but 
updates `AdaptiveBlockSizePredictor` from the post-finalize table block; with a 
1 MiB root and a scalar path, that probe reads about 32 MiB yet reports only 
scalar bytes/row, allowing the next request to jump to the 4096-row cap and 
approach 4 GiB. Wide missing-root/default carriers have the same blind spot. 
This can OOM even though the requested Path Slot is narrow. Until readers 
advertise real physical path pruning, please cap these scans conservatively or 
feed the predictor the pre-extraction carrier bytes, and add scanner-level 
coverage for large-root/tiny-path reads and defaults.
   



##########
be/src/format_v2/column_mapper.cpp:
##########
@@ -58,6 +60,67 @@ namespace doris::format {
 
 namespace {
 
+std::string mapping_mode_to_string(TableColumnMappingMode mode);
+
+bool is_variant_v2_type(const DataTypePtr& type) {
+    return type != nullptr &&
+           dynamic_cast<const DataTypeVariantV2*>(remove_nullable(type).get()) 
!= nullptr;
+}
+
+Status resolve_variant_path(const std::vector<std::string>& column_paths,
+                            std::shared_ptr<const 
ResolvedVariantElementV2Path>* resolved_path) {
+    DORIS_CHECK(!column_paths.empty());
+    DORIS_CHECK(resolved_path != nullptr);
+    std::vector<VariantElementV2PathSegment> segments;
+    segments.reserve(column_paths.size());
+    for (const auto& key : column_paths) {
+        // TSlotDescriptor.column_paths is an already-tokenized list. Treat 
every entry as one
+        // object key; joining on '.' would turn a literal key such as "a.b" 
into two selectors.
+        // Array indexes need a typed transport and are intentionally outside 
this contract.
+        segments.push_back(
+                VariantElementV2PathSegment::object_key(StringRef(key.data(), 
key.size())));
+    }
+    std::unique_ptr<ResolvedVariantElementV2Path> candidate;
+    RETURN_IF_ERROR(resolve_variant_element_v2_path(segments, &candidate));
+    *resolved_path = std::move(candidate);
+    return Status::OK();
+}
+
+Status initialize_variant_path_mapping(const ColumnDefinition& table_column,
+                                       TableColumnMappingMode mode, 
ColumnMapping* mapping) {
+    DORIS_CHECK(mapping != nullptr);
+    mapping->column_paths = table_column.column_paths;
+    if (mapping->column_paths.empty()) {
+        return Status::OK();
+    }
+    if (mode != TableColumnMappingMode::BY_FIELD_ID) {

Review Comment:
   [P1] Preserve Path Slots on idless Iceberg files
   
   `IcebergTableReader::mapping_mode()` deliberately selects `BY_NAME` for 
legacy files without physical field IDs, but this returns `NotSupported` before 
`_find_file_field()` and before the missing-root initial/default/NULL branches. 
Once the future FE rewrite emits a Path Slot for a table that still contains an 
older idless data file, `v['metric']['x']` therefore fails on that split even 
if `v` is safely name-mappable, or did not exist yet and should produce its 
Iceberg initial default/SQL NULL. Please retain a safe root-name/name-mapping 
identity alongside the authoritative field ID and support the idless per-file 
mode, with Iceberg reader tests for both an existing root and a missing root 
with a default.
   



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