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


##########
be/src/format/arrow/arrow_block_convertor.cpp:
##########
@@ -118,6 +120,98 @@ std::shared_ptr<arrow::DataType> extension_storage_type(
     }
 }
 
+// `type` is the Doris logical type; `plain_arrow_type` is its ordinary SerDe 
mapping;
+// `target_type` is the Arrow representation requested by the consumer. For 
example,
+// DATETIMEV2(6) may bind timestamp(us) without a timezone, but TIMESTAMPTZ(6) 
must
+// retain a timezone to preserve instant semantics. Nested bindings obey the 
same rule.
+bool is_declared_plain_arrow_binding(const DataTypePtr& type,
+                                     const std::shared_ptr<arrow::DataType>& 
plain_arrow_type,
+                                     const std::shared_ptr<arrow::DataType>& 
target_type) {
+    if (plain_arrow_type->Equals(target_type)) {
+        return true;
+    }
+    if (plain_arrow_type->id() == arrow::Type::TIMESTAMP &&
+        target_type->id() == arrow::Type::TIMESTAMP) {
+        const auto& plain_timestamp = assert_cast<const 
arrow::TimestampType&>(*plain_arrow_type);
+        const auto& target_timestamp = assert_cast<const 
arrow::TimestampType&>(*target_type);
+        if (plain_timestamp.unit() != target_timestamp.unit()) {
+            return false;
+        }
+        const PrimitiveType primitive = 
remove_nullable(type)->get_primitive_type();
+        // A timezone-free Arrow timestamp is a wall-clock value and is 
therefore only compatible
+        // with DATETIMEV2; TIMESTAMPTZ must always retain its instant 
semantics.
+        if (target_timestamp.timezone().empty()) {
+            return primitive == TYPE_DATETIMEV2;
+        }

Review Comment:
   [P1] Compare timezone bindings semantically instead of by cctz name
   
   A valid `+00:00` session reaches BE as Java's canonical `Z`. The timezone 
cache resolves `Z` to a cctz object named `Zulu`, while Arrow schema 
construction rewrites the declared `Z` label to `UTC`. 
`write_plain_arrow_column` therefore builds the source timestamp with `Zulu`, 
this line resolves the target as `UTC`, and the new preflight rejects `UTC == 
Zulu` before writing even though both are the same zero-offset zone. This 
breaks timezone-aware timestamp batches on the common plain converter path 
(including Parquet/Hive and memory-scratch/Arrow-Flight consumers) for the 
supported `+00:00` session value. Please normalize both sides to one identity 
or compare actual zone equivalence, and add a full batch-conversion test using 
the cctz object resolved from `Z` rather than only checking the generated 
schema label.



##########
be/src/core/data_type_serde/data_type_array_serde.cpp:
##########
@@ -321,6 +321,77 @@ Status DataTypeArraySerDe::write_column_to_arrow(const 
IColumn& column, const Nu
     return Status::OK();
 }
 
+namespace {
+
+template <typename WriteNested>
+Status write_array_column_to_target(const IColumn& column, const NullMap* 
null_map,
+                                    arrow::ArrayBuilder* array_builder, 
int64_t start, int64_t end,
+                                    WriteNested&& write_nested) {
+    const auto& array_column = assert_cast<const ColumnArray&>(column);
+    const auto& offsets = array_column.get_offsets();
+    const auto& nested_data = array_column.get_data();
+    auto& builder = assert_cast<arrow::ListBuilder&>(*array_builder);
+    auto* nested_builder = builder.value_builder();
+    for (size_t array_idx = start; array_idx < end; ++array_idx) {
+        if (null_map != nullptr && (*null_map)[array_idx]) {
+            RETURN_IF_ERROR(checkArrowStatus(builder.AppendNull(), column, 
*array_builder));
+            continue;
+        }
+        RETURN_IF_ERROR(checkArrowStatus(builder.Append(), column, 
*array_builder));
+        RETURN_IF_ERROR(write_nested(nested_data, nested_builder, 
offsets[array_idx - 1],
+                                     offsets[array_idx]));
+    }
+    return Status::OK();
+}
+
+} // namespace
+
+Status DataTypeArraySerDe::write_column_to_paimon_arrow(
+        const std::shared_ptr<const IDataType>& type, const IColumn& column,
+        const NullMap* null_map, const std::shared_ptr<arrow::Field>& field,
+        arrow::ArrayBuilder* array_builder, int64_t start, int64_t end,
+        const cctz::time_zone& ctz) const {
+    // Reject an incompatible target before casting its nested schema or 
builder.
+    if (field->type()->id() != arrow::Type::LIST ||

Review Comment:
   [P1] Validate the nested binding before dispatching to the child SerDe
   
   This only checks that the outer target is a LIST. The Paimon/Iceberg 
converters bypass the recursive plain-binding validator, so a same-outer-kind 
mismatch reaches the child writer. For example, a Doris `array<decimal(10,3)>` 
value `1.234` has raw coefficient `1234`; if the pinned target is 
`list<decimal(10,2)>`, the Decimal128 writer accepts the same builder and 
persists that coefficient as `12.34`. `array<int>` versus `list<string>` 
instead reaches the wrong builder cast, and Map/Struct have the same gap. 
Please validate the full recursive binding (including decimal parameters, 
timestamp units, and struct field identity/order) before appending the parent 
builder.



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