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


##########
fe/fe-core/src/main/java/org/apache/doris/datasource/iceberg/IcebergTransaction.java:
##########
@@ -289,14 +291,23 @@ public void beginDelete(ExternalTable dorisTable) throws 
UserException {
         }
     }
 
-    /**
-     * Begin merge operation for Iceberg UPDATE (single scan RowDelta).
-     */
-    public void beginMerge(ExternalTable dorisTable) throws UserException {
+    private Table createTransactionTable(ExternalTable dorisTable, Table 
retainedTable) {

Review Comment:
   [P1] Pin DELETE to the scanned table generation
   
   This new retained/writable transaction path still excludes `beginDelete()`: 
`IcebergDeleteCommand` first plans its scan from the relation-frozen S0 table, 
but `IcebergDeleteExecutor.beforeExec()` later reloads the live table and 
records its current snapshot as the RowDelta base. If a matching row is 
inserted as S1 in between, BE deletes only the S0 rows while 
`validateFromSnapshot(S1)` treats that insert as preceding the transaction and 
does not report it; DELETE can succeed with a row still satisfying its 
predicate. Please carry the retained target through the delete sink/executor 
too, and add a barrier test with a data commit between planning and 
`beginDelete()`.



##########
fe/fe-core/src/main/java/org/apache/doris/datasource/paimon/source/PaimonScanNode.java:
##########
@@ -177,6 +180,11 @@ public PaimonScanNode(PlanNodeId id,
 
     @Override
     protected void doInitialize() throws UserException {
+        Optional<MvccSnapshot> relationSnapshot = getRelationSnapshot();
+        if (desc.getTable() instanceof PaimonExternalTable) {
+            // Rebuild the source before applying query options so both layers 
use this relation's snapshot.
+            source = new PaimonSource(desc, relationSnapshot);

Review Comment:
   [P1] Preserve an empty Paimon snapshot at execution
   
   This relation-local rebuild still cannot fence an empty latest table. 
`PaimonLatestSnapshotProjectionLoader` stores snapshot ID `-1` but, when no 
snapshot exists, retains the live base `FileStoreTable` without 
`scan.snapshot-id`; `getProcessedTable()` then scans that handle normally. If 
the first commit lands after binding but before this initialization, the 
relation planned as empty reads the new rows. Please make the invalid-snapshot 
state executable as an empty scan (or otherwise pin it), and add an 
empty-bind/first-commit barrier test for normal Paimon scans.



##########
fe/fe-core/src/main/java/org/apache/doris/datasource/iceberg/IcebergExternalMetaCache.java:
##########
@@ -232,7 +232,8 @@ private IcebergSnapshotCacheValue 
loadSnapshotProjection(ExternalTable dorisTabl
                         latestIcebergSnapshot.getSnapshotId(), 
latestIcebergSnapshot.getSchemaId());
             }
             return new IcebergSnapshotCacheValue(
-                    icebergPartitionInfo, latestIcebergSnapshot, 
IcebergUtils.getNameMapping(icebergTable));
+                    icebergPartitionInfo, latestIcebergSnapshot, 
IcebergUtils.getNameMapping(icebergTable),
+                    icebergTable);

Review Comment:
   [P1] Freeze the table before deriving this projection
   
   The table is not frozen until the `IcebergSnapshotCacheValue` constructor, 
after the latest snapshot/schema, partitions, and name mapping have already 
been read from the shared `BaseTable`. A concurrent `ADD COLUMN ... DEFAULT` 
can land between those reads and this line, yielding S0 bound outputs but a 
frozen T1 target; `IcebergTableSink` then serializes T1's schema and 
`VIcebergTableWriter::open` rejects the S0/T1 column-count mismatch. Optimistic 
validation cannot help because the retained base already starts at T1. Please 
capture one `TableMetadata` first and derive every projection field plus the 
frozen table from it (also in `IcebergUtils.loadSnapshotCacheValue`), with a 
barrier test inside construction.



##########
be/src/format_v2/table_reader.h:
##########
@@ -1515,15 +1585,34 @@ class TableReader {
 
     Status _materialize_array_mapping_column(const ColumnMapping& mapping,
                                              const ColumnPtr& file_column, 
const size_t rows,
-                                             ColumnPtr* column) {
+                                             ColumnPtr* column,
+                                             const NullMap* 
nullable_parent_null_map = nullptr) {
         DORIS_CHECK(mapping.child_mappings.size() == 1);
         const auto full_file_column = 
file_column->convert_to_full_column_if_const();
         const NullMap* parent_null_map = nullptr;
         const auto* nested_file_column =
                 _nested_column_if_nullable(full_file_column, &parent_null_map);
+        if (parent_null_map != nullptr && !mapping.table_type->is_nullable()) {
+            DORIS_CHECK(parent_null_map->size() == rows);
+            if (nullable_parent_null_map != nullptr) {
+                DORIS_CHECK(nullable_parent_null_map->size() == rows);
+            }
+            for (size_t i = 0; i < rows; ++i) {
+                // ARRAY row masks cannot be forwarded to elements because 
they use different
+                // coordinates, so validate the container before dropping its 
nullable wrapper.
+                if ((*parent_null_map)[i] &&
+                    (nullable_parent_null_map == nullptr || 
!(*nullable_parent_null_map)[i])) {
+                    return Status::InternalError(
+                            "Source array contains NULL for non-nullable table 
column");
+                }
+            }
+        }
         const auto* file_array = assert_cast<const 
ColumnArray*>(nested_file_column);
         ColumnPtr nested_column = file_array->get_data_ptr();
-        const auto& element_mapping = mapping.child_mappings[0];
+        auto element_mapping = mapping.child_mappings[0];
+        // Keep the descriptor type for schema matching. ARRAY's nullable 
element wrapper is a
+        // storage invariant, so add it only at the materialization boundary.
+        element_mapping.table_type = make_nullable(element_mapping.table_type);
         RETURN_IF_ERROR(_materialize_present_child_mapping_column(

Review Comment:
   [P1] Mask hidden collection entries during descendant validation
   
   The container's row-level null map is still absent from this recursive 
element call; Map likewise validates every key/value without projecting its row 
mask through the offsets. The [Arrow 
format](https://arrow.apache.org/docs/format/Columnar.html#support-for-null-values)
 permits a null slot to occupy nonempty physical storage, and Doris's Arrow 
SerDes copy that full child span. A valid null array whose hidden `STRUCT<a INT 
NOT NULL>` element has a null `a` placeholder is therefore rejected by 
required-child alignment even though the container masks it. Please compact 
masked spans or propagate an entry-coordinate hidden mask, and add nullable 
Array/Map tests with nonempty hidden struct payload.



##########
be/src/core/data_type_serde/orc_serde_utils.h:
##########
@@ -0,0 +1,74 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+#pragma once
+
+#include <cstring>
+#include <orc/Vector.hh>
+
+#include "core/arena.h"
+
+namespace doris {
+
+inline void copy_orc_string_data_to_arena(orc::ColumnVectorBatch* batch, 
Arena& arena) {
+    if (auto* strings = dynamic_cast<orc::StringVectorBatch*>(batch)) {
+        size_t total_size = 0;
+        for (size_t i = 0; i < strings->numElements; ++i) {
+            const size_t length = static_cast<size_t>(strings->length[i]);
+            // Some serdes already allocate their payload in this Arena; 
copying it again would
+            // double the serialized string memory without extending its 
lifetime.
+            if (length > 0 && !arena.contains(strings->data[i], length)) {
+                total_size += length;
+            }
+        }
+        char* cursor = total_size == 0 ? nullptr : arena.alloc(total_size);
+        for (size_t i = 0; i < strings->numElements; ++i) {
+            const size_t length = static_cast<size_t>(strings->length[i]);
+            const char* source = strings->data[i];
+            if (length > 0 && !arena.contains(source, length)) {
+                std::memcpy(cursor, source, length);
+                strings->data[i] = cursor;
+                cursor += length;
+            } else if (length == 0) {

Review Comment:
   [P1] Preserve empty strings in ORC statistics
   
   This branch changes a visible zero-length string from a non-null pointer to 
`nullptr`. In the pinned ORC 1.9.0 writer, every non-null string is passed to 
`StringColumnStatisticsImpl::update`, but that routine updates min/max only 
when the pointer is non-null 
([writer](https://github.com/apache/orc/blob/rel/release-1.9.0/c%2B%2B/src/ColumnWriter.cc#L1097-L1134),
 
[statistics](https://github.com/apache/orc/blob/rel/release-1.9.0/c%2B%2B/src/Statistics.hh#L1181-L1205)).
 Thus a compacted collection leaf containing `""` and `"z"` records minimum 
`"z"`, which can make statistics pruning skip present data. Please preserve a 
non-null, lifetime-safe pointer for zero-length values (for example, an 
Arena-owned or static empty sentinel) and add a masked Array/Map case that 
verifies empty-string leaf statistics.



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