HappenLee commented on code in PR #68359:
URL: https://github.com/apache/doris/pull/68359#discussion_r4236276438


##########
be/src/exprs/function/function_regexp.cpp:
##########
@@ -74,6 +76,60 @@ static bool advance_re2_search_position(const char* data, 
size_t size,
     return true;
 }
 
+// The regexp functions below handle NULL rows themselves 
(use_default_implementation_for_nulls()
+// returns false). The framework's default path runs a function over the 
nested column of a
+// Nullable argument, and the bytes stored under a NULL slot are whatever the 
producer left there;
+// compiling them as a pattern could fail and abort a query whose result for 
that row is simply
+// NULL.
+//
+// Strips Nullable from every argument into `nested_block` (a ColumnConst 
wrapper stays, so the
+// const/full handling of the functions applies unchanged) and ORs the 
argument null maps into
+// `null_map`, which the functions skip while executing and hand back as the 
result null map.
+// Returns false when an argument is a NULL constant: the whole result is NULL.
+bool unnest_regexp_arguments(const Block& block, const ColumnNumbers& 
arguments,
+                             Block& nested_block, ColumnNumbers& 
nested_arguments,
+                             NullMap& null_map) {
+    for (const auto argument : arguments) {
+        const auto& column = block.get_by_position(argument);
+        NullableColumnInfo info;
+        if (column.type->is_nullable()) {
+            info = column.get_nullable_column_info();
+            if (info.only_null) {
+                return false;
+            }
+            if (info.has_null) {
+                // A ColumnConst holding NULL is only_null, so this is a full 
column.
+                DCHECK(!info.is_const);
+                VectorizedUtils::update_null_map(null_map,
+                                                 
column.get_nullable_null_map_column()->get_data());
+            }
+        }
+        nested_arguments.push_back(nested_block.columns());
+        nested_block.insert(column.unnest_nullable(info, false));
+    }
+    return true;
+}
+
+// Shared execute() prologue of the regexp functions: a NULL constant argument 
makes the result
+// a NULL constant, otherwise `execute` runs over the Nullable-stripped 
arguments and returns the
+// result column already wrapped with `null_map`.
+template <typename Execute>
+Status execute_regexp_with_nulls(Block& block, const ColumnNumbers& arguments, 
uint32_t result,
+                                 size_t input_rows_count, Execute&& execute) {
+    auto& result_column = block.get_by_position(result);
+    auto null_map = ColumnUInt8::create(input_rows_count, 0);

Review Comment:
   The `regexp_count` fast path in `554e1207632` addresses my previous 
performance comment. Please apply the same principle through the shared regexp 
executor, so the nullable/non-nullable dispatch is implemented once rather than 
separately in each function.
   
   `FunctionRegexpReplace::execute_impl()` and 
`FunctionRegexpFunctionality::execute_impl()` still enter 
`execute_regexp_with_nulls()` unconditionally. With entirely non-nullable 
inputs, this builds `nested_block` and `nested_arguments` even though no 
Nullable wrapper needs stripping, and the extract/replace loops still 
read/check an all-zero input NULL map. These are metadata-wrapping and per-row 
checking costs; I have not benchmarked their impact.
   
   A focused approach would be:
   
   - Make the shared executor select the path once per block using 
`have_null_column(block, arguments)`.
   - For non-nullable inputs, pass the original block/arguments directly to an 
`execute<false>` specialization, with no input NULL map or temporary nested 
block. Compile out input NULL checks with `if constexpr (CheckNull)`.
   - For nullable inputs, reuse the current argument unnesting, NULL-map 
merging, all-NULL short-circuit, and `execute<true>` path that skips NULL rows 
before compiling patterns.
   - Route count, extract, extract-all, and replace through this dispatch while 
retaining their existing algorithms and result handlers.
   
   Please keep **input NULL checking separate from output nullability**. The 
extract/replace output NULL maps already existed before this PR; they are not 
all new overhead. In particular, `regexp_extract_or_null` can produce NULL for 
non-nullable inputs when there is no match, and a caller-declared nullable 
count result still needs its output wrapper. `CheckNull=false` should only 
remove input NULL processing, not these output semantics.
   
   This can stay within the existing regexp helper; changing the default NULL 
contract for all Doris functions is unnecessary for this fix. Please retain the 
hidden-invalid-payload and count fast-path tests, and cover non-nullable 
extract/replace inputs, including constant patterns and the no-match NULL 
result of `regexp_extract_or_null`.
   



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