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


##########
be/src/exprs/function/cast/cast_base.cpp:
##########
@@ -17,11 +17,70 @@
 
 #include "exprs/function/cast/cast_base.h"
 
+#include <algorithm>
 #include <cstdint>
+#include <utility>
 
 #include "util/jsonb_writer.h"
 namespace doris::CastWrapper {
 
+namespace {
+
+/// A nullable column keeps an all zero NULL map when it has no NULL row, and 
there is nothing to
+/// inherit from such a column.
+bool has_masked_row(const NullMap::value_type* null_map, size_t rows) {
+    return std::any_of(null_map, null_map + rows,
+                       [](const NullMap::value_type value) { return value != 
0; });
+}
+
+} // namespace
+
+ChildNullMask build_child_null_mask(const NullMap::value_type* parent_null_map,
+                                    const IColumn::Offsets64* offsets, const 
ColumnPtr& child) {
+    if (parent_null_map == nullptr) {
+        return {.column = child, .null_map = nullptr, .mask_holder = nullptr};
+    }
+    const size_t rows = offsets == nullptr ? child->size() : offsets->size();
+    if (!has_masked_row(parent_null_map, rows)) {
+        return {.column = child, .null_map = nullptr, .mask_holder = nullptr};
+    }
+
+    auto mask = ColumnUInt8::create(child->size(), 0);
+    auto& mask_data = mask->get_data();
+    if (offsets == nullptr) {
+        // The children of a STRUCT share the rows of their parent.
+        std::copy_n(parent_null_map, rows, mask_data.begin());
+    } else {
+        // ARRAY and MAP children are flattened, so the NULL of a row has to 
be expanded to every
+        // element/entry that belongs to it.
+        for (size_t row = 0; row < rows; ++row) {
+            if (parent_null_map[row] == 0) {
+                continue;
+            }
+            const size_t first_child = row == 0 ? 0 : (*offsets)[row - 1];
+            std::fill(mask_data.begin() + first_child, mask_data.begin() + 
(*offsets)[row], 1);
+        }
+    }
+
+    const auto* nullable_child = 
check_and_get_column<ColumnNullable>(child.get());
+    if (nullable_child == nullptr) {
+        return {.column = child, .null_map = mask_data.data(), .mask_holder = 
std::move(mask)};
+    }
+
+    // The NULL state of a child is the union of its own NULL map and the mask 
inherited from the
+    // rows of its parent.
+    auto merged_mask = ColumnUInt8::create(child->size(), 0);

Review Comment:
   [P2] Merge the child NULL map into the first buffer
   
   `mask` already has `child->size()` entries and contains all inherited parent 
bits, but this branch allocates and zeroes another equally large `merged_mask`, 
ORs every byte into it, and then immediately frees `mask`. ARRAY children 
always take this nullable branch, and their flattened element count can be much 
larger than the row count; MAP and recursive complex casts repeat the cost. 
Please OR `child_null_map` into `mask_data` in place and move `mask` into the 
returned `ColumnNullable`, avoiding the extra child-cardinality allocation and 
initialization.



##########
be/src/exprs/function/cast/cast_to_int.h:
##########
@@ -72,6 +72,11 @@ class CastToImpl<CastMode, FromDataType, ToDataType> : 
public CastToBase {
         CastParameters params;
         params.is_strict = (CastMode == CastModeType::StrictMode);
         for (size_t i = 0; i < input_rows_count; ++i) {
+            // The source value of a row marked as null by the input null map 
is a hidden
+            // payload and has no SQL semantics, so it must not be checked.
+            if (null_map && null_map[i]) {
+                continue;

Review Comment:
   [P1] Initialize the destination before skipping masked rows
   
   `ColumnVector(input_rows_count)` is backed by `PODArray(size)`, which 
allocates and advances its end pointer without filling elements, so this 
`continue` bypasses the only write to `vec_to[i]`. The outer NULL map does not 
guarantee that payload is never read: a chained `CAST(CAST(IF(p, x, NULL) AS 
TINYINT) AS STRING)` unwraps the nullable column and 
`CastToStringFunction::execute_impl` formats every nested row while ignoring 
its `null_map`, reading the indeterminate TINYINT slot before the final NULL 
map is restored. The same issue applies to the newly skipped decimal and strict 
date/time/TIMESTAMP_NS destinations. Please write each masked slot's valid 
default (or value-initialize the target column) before continuing, and cover a 
chained nullable consumer.



##########
be/src/exprs/function/cast/cast_to_struct.h:
##########
@@ -68,21 +68,25 @@ WrapperType create_struct_wrapper(FunctionContext* context, 
const DataTypePtr& f
         size_t elements_num = to_element_types.size();
         Columns converted_columns(elements_num);
         for (size_t i = 0; i < elements_num; ++i) {
-            ColumnWithTypeAndName from_element_column 
{from_col_struct->get_column_ptr(i),
-                                                       from_element_types[i], 
""};
+            /// A field of a row that is NULL is a hidden payload as well. 
Fields share the rows of
+            /// their parent, so the mask of the parent can be used as is.
+            auto child_mask =

Review Comment:
   [P2] Skip inherited-mask work for unchanged fields
   
   This builds a child mask before knowing whether the prepared child 
conversion can use it. For an exact-type field, `prepare_impl` ends at 
`create_identity_wrapper`, so a wide `STRUCT` with only one changed field still 
rescans the same parent NULL map for every unchanged field and, if any row is 
NULL, allocates a row-sized mask for each one. The same happens to an unchanged 
MAP key, for example `MAP<INT,INT>` to `MAP<INT,TINYINT>`. These identity 
children cannot validate or reject their hidden payload, and the enclosing NULL 
already hides it. Please retain whether each child wrapper is an identity and 
forward exact-type children directly; also compute the parent masked state once 
per MAP/STRUCT invocation.



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