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


##########
fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/logical/LogicalFileScan.java:
##########
@@ -113,14 +156,41 @@ protected LogicalFileScan(RelationId id, ExternalTable 
table, List<String> quali
     }
 
     private static SelectedPartitions initialSelectedPartitions(
-            ExternalTable table, Optional<TableScanParams> scanParams) {
+            ExternalTable table, Optional<TableScanParams> scanParams,
+            Optional<MvccSnapshot> relationSnapshot) {
         if ((table instanceof PaimonExternalTable || table instanceof 
PaimonSysExternalTable)
                 && scanParams.isPresent() && scanParams.get().isOptions()) {
             // A relation-scoped historical snapshot cannot reuse partitions 
cached for the
             // statement-level latest snapshot; Paimon will prune its selected 
snapshot instead.
             return SelectedPartitions.NOT_PRUNED;
         }
-        return 
table.initSelectedPartitions(MvccUtil.getSnapshotFromContext(table));
+        return table.initSelectedPartitions(relationSnapshot);
+    }
+
+    private static Optional<List<Column>> captureRelationSchema(
+            ExternalTable table, Optional<TableScanParams> scanParams,
+            Optional<MvccSnapshot> relationSnapshot) {
+        if (scanParams.isPresent() && scanParams.get().isOptions()) {
+            if (table instanceof PaimonExternalTable) {
+                return Optional.of(ImmutableList.copyOf(
+                        ((PaimonExternalTable) 
table).getFullSchema(scanParams.get())));
+            }
+            if (table instanceof PaimonSysExternalTable) {
+                return Optional.of(ImmutableList.copyOf(
+                        ((PaimonSysExternalTable) 
table).getFullSchema(scanParams.get())));
+            }
+        }
+        return captureRelationSchema(table, relationSnapshot);
+    }
+

Review Comment:
   [P1] Do not pin ordinary Hive slots behind an empty snapshot
   
   This captures a relation schema for every `MvccTable`, but a 
non-Hudi/non-Iceberg `HMSExternalTable` returns only `EmptyMvccSnapshot`. After 
a metastore event or `REFRESH TABLE` between binding and physical 
initialization, logical slots remain on S0 while 
`HMSExternalTable.getFullSchema(Optional)` falls through to `ExternalTable`, 
ignores that placeholder, and reloads S1 for `FileQueryScanNode` column 
positions, defaults, and types. A rename then fails with `Column old_name not 
found`; type/default changes can mix generations. This is distinct from the 
live Iceberg empty-state comment because ordinary Hive has no executable 
snapshot at all. Please either give Hive a snapshot that reproduces its schema 
or exclude empty placeholders from this fence, with a binding-to-init 
refresh-barrier test.



##########
be/src/core/data_type_serde/data_type_array_serde.cpp:
##########
@@ -366,13 +366,14 @@ Status DataTypeArraySerDe::write_column_to_orc(const 
std::string& timezone, cons
     const IColumn& nested_column = array_col.get_data();
     const auto& offsets = array_col.get_offsets();
     for (size_t row_id = start; row_id < end; row_id++) {
-        size_t offset = offsets[row_id - 1];
         size_t next_offset = offsets[row_id];
-        RETURN_IF_ERROR(nested_serde->write_column_to_orc(timezone, 
nested_column, nullptr,
-                                                          
cur_batch->elements.get(), offset,
-                                                          next_offset, arena, 
options));
         cur_batch->offsets[row_id + 1] = next_offset;
     }
+    const size_t nested_start = start == 0 ? 0 : offsets[start - 1];
+    const size_t nested_end = end == 0 ? nested_start : offsets[end - 1];
+    RETURN_IF_ERROR(nested_serde->write_column_to_orc(timezone, nested_column, 
nullptr,
+                                                      
cur_batch->elements.get(), nested_start,
+                                                      nested_end, arena, 
options));

Review Comment:
   [P1] Compact collection payload hidden by the parent mask
   
   The struct writer now marks a child collection null from its parent mask, 
but this batched Array path ignores that mask and forwards the full 
offset-derived element span; Map does the same for keys and values. ORC 
suppresses the null row's collection length and then consumes the supplied 
child span as packed values, so when a null struct row retains nonempty 
physical collection payload, the following visible row reads that hidden 
payload and later elements shift. Nullable nested payload is not required to be 
defaulted. This is a write-side packing defect, distinct from the live 
TableReader parent-null issue. Please remove parent-masked entries or normalize 
offsets/payload equivalently, and round-trip a null parent with nonempty 
Array/Map payload followed by a present row.



##########
fe/fe-core/src/main/java/org/apache/doris/nereids/rules/expression/check/CheckCast.java:
##########
@@ -414,7 +414,9 @@ && check(((MapType) originalType).getValueType(), 
((MapType) targetType).getValu
                 return false;
             }
             for (int i = 0; i < targetFields.size(); i++) {
-                if (originalFields.get(i).isNullable() != 
targetFields.get(i).isNullable()) {
+                // A nullable target can safely accept a required source, but 
the inverse would
+                // allow a possible NULL into a required nested field.
+                if (originalFields.get(i).isNullable() && 
!targetFields.get(i).isNullable()) {
                     return false;
                 }

Review Comment:
   [P1] Preserve value nullability for `named_struct` too
   
   Separately from the live fallible-cast issue on this line, this check also 
rejects safe values because the parallel constructors disagree. `struct(1)` now 
reports a required child through `StructLiteral.computeDataType()`, but 
`CreateNamedStruct.customSignature()` still hard-codes every field nullable. 
Thus `CAST(named_struct('a', 1) AS STRUCT<a:BIGINT NOT NULL>)` and the 
equivalent INSERT are rejected even though the value is provably non-null. 
Please derive each named field's nullability from its value child as well, with 
cast/INSERT tests for both non-null and nullable values.



##########
fe/fe-core/src/main/java/org/apache/doris/nereids/rules/rewrite/PruneFileScanPartition.java:
##########
@@ -116,4 +116,10 @@ private SelectedPartitions 
pruneExternalPartitions(ExternalTable externalTable,
         return new SelectedPartitions(nameToPartitionItem.size(), 
selectedPartitionItems, true,
                 result.hasPartitionPredicate);
     }
+
+    static List<Column> getPartitionColumnsForScan(ExternalTable 
externalTable, LogicalFileScan scan) {
+        // The partition map is frozen on the logical relation, so its column 
list must come from
+        // the same snapshot rather than the mutable table-scoped 
compatibility entry.

Review Comment:
   [P1] Derive partition columns from the frozen generation
   
   Although this applies the existing thread's prescribed `relationSnapshot` 
argument, the Iceberg helper does not honor that generation. Its frozen 
snapshot value supplies the T0 partition map, but 
`getIcebergPartitionColumns()` enters a schema-ID-only cache whose miss path 
reloads the current table and derives columns from `table.spec()`. A 
metadata-only spec change from identity `p` to `q` keeps snapshot/schema IDs 
unchanged, so this rule can evaluate `q` against T0's encoded `p` ranges and 
prune the only matching partition. Please freeze partition columns with the 
partition map or derive them from the retained table, and add a spec-refresh 
barrier test.



##########
fe/fe-core/src/main/java/org/apache/doris/nereids/glue/translator/PhysicalPlanTranslator.java:
##########
@@ -585,7 +585,9 @@ public PlanFragment 
visitPhysicalIcebergTableSink(PhysicalIcebergTableSink<? ext
         List<Expr> outputExprs = Lists.newArrayList();
         icebergTableSink.getOutput().stream().map(Slot::getExprId)
                 .forEach(exprId -> 
outputExprs.add(context.findSlotRef(exprId)));
-        IcebergTableSink sink = new IcebergTableSink((IcebergExternalTable) 
icebergTableSink.getTargetTable());
+        IcebergTableSink sink = new IcebergTableSink(
+                (IcebergExternalTable) icebergTableSink.getTargetTable(),
+                icebergTableSink.getTargetIcebergTable());
         rootFragment.setSink(sink);
         sink.setOutputExprs(outputExprs);

Review Comment:
   [P1] Carry this pinned table into the Iceberg transaction
   
   The existing writer-generation thread stops at writer construction; this 
fixes that reload but leaves a distinct later one. 
`IcebergInsertExecutor.beforeExec()` calls `IcebergTransaction.beginInsert()`, 
which independently reloads the current `Table`. If a metadata-only spec change 
replaces identity partition `p` with `q` after planning, BE writes T0 files 
while static-overwrite commit logic uses T1; `buildPartitionFilter()` finds no 
T1 field for key `p`, returns `alwaysTrue`, and `overwriteByRowFilter` deletes 
the whole table. Please create or validate the transaction against this same 
target generation and make unmatched static keys fail, with a refresh-barrier 
static-overwrite test.



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