@@ -304,6 +304,99 @@ TEST(FunctionLikeTest, regexp_extract_all_array) {
304304 run_case (" hitdecisiondlist" , " (i)(.*?)(e)" , " [\" i\" ]" );
305305 run_case (" no_match_here" , " x=([0-9]+)" , " []" );
306306 run_case (" abc" , " ([a-z]+)" , " [\" abc\" ]" );
307+
308+ // Helper for testing null input propagation
309+ auto nullable_str_type = make_nullable (str_type);
310+ auto run_null_case = [&](bool null_str, bool null_pattern) {
311+ ColumnPtr col_str;
312+ DataTypePtr str_col_type;
313+ if (null_str) {
314+ auto col = ColumnNullable::create (ColumnString::create (), ColumnUInt8::create ());
315+ col->insert_default ();
316+ col_str = std::move (col);
317+ str_col_type = nullable_str_type;
318+ } else {
319+ auto col = ColumnString::create ();
320+ col->insert_data (" abc" , 3 );
321+ col_str = std::move (col);
322+ str_col_type = str_type;
323+ }
324+
325+ ColumnPtr col_pattern;
326+ DataTypePtr pattern_col_type;
327+ if (null_pattern) {
328+ auto col = ColumnNullable::create (ColumnString::create (), ColumnUInt8::create ());
329+ col->insert_default ();
330+ col_pattern = ColumnConst::create (std::move (col), 1 );
331+ pattern_col_type = nullable_str_type;
332+ } else {
333+ auto col = ColumnString::create ();
334+ col->insert_data (" ([a-z]+)" , 8 );
335+ col_pattern = ColumnConst::create (std::move (col), 1 );
336+ pattern_col_type = str_type;
337+ }
338+
339+ Block block;
340+ block.insert ({col_str, str_col_type, " str" });
341+ block.insert ({col_pattern, pattern_col_type, " pattern" });
342+ block.insert ({nullptr , return_type, " result" });
343+
344+ ColumnsWithTypeAndName arg_cols = {block.get_by_position (0 ), block.get_by_position (1 )};
345+ auto func =
346+ SimpleFunctionFactory::instance ().get_function (func_name, arg_cols, return_type);
347+ ASSERT_TRUE (func != nullptr );
348+
349+ std::vector<DataTypePtr> arg_types = {str_col_type, pattern_col_type};
350+ FunctionUtils fn_utils ({}, arg_types, false );
351+ auto * fn_ctx = fn_utils.get_fn_ctx ();
352+ fn_ctx->set_constant_cols (
353+ {nullptr , std::make_shared<ColumnPtrWrapper>(block.get_by_position (1 ).column )});
354+
355+ ASSERT_EQ (Status::OK (), func->open (fn_ctx, FunctionContext::FRAGMENT_LOCAL));
356+ ASSERT_EQ (Status::OK (), func->open (fn_ctx, FunctionContext::THREAD_LOCAL));
357+ ASSERT_EQ (Status::OK (), func->execute (fn_ctx, block, {0 , 1 }, 2 , 1 ));
358+
359+ EXPECT_TRUE (block.get_by_position (2 ).column ->is_null_at (0 ))
360+ << " Expected null for null_str=" << null_str << " null_pattern=" << null_pattern;
361+
362+ static_cast <void >(func->close (fn_ctx, FunctionContext::THREAD_LOCAL));
363+ static_cast <void >(func->close (fn_ctx, FunctionContext::FRAGMENT_LOCAL));
364+ };
365+
366+ // NULL input string → null result
367+ run_null_case (true , false );
368+ // NULL pattern → null result
369+ run_null_case (false , true );
370+
371+ // Invalid const pattern → open() should fail
372+ {
373+ auto col_str = ColumnString::create ();
374+ col_str->insert_data (" abc" , 3 );
375+ auto col_pattern = ColumnString::create ();
376+ col_pattern->insert_data (" (" , 1 );
377+ Block block;
378+ block.insert ({std::move (col_str), str_type, " str" });
379+ block.insert ({ColumnConst::create (std::move (col_pattern), 1 ), str_type, " pattern" });
380+ block.insert ({nullptr , return_type, " result" });
381+
382+ ColumnsWithTypeAndName arg_cols = {block.get_by_position (0 ), block.get_by_position (1 )};
383+ auto func =
384+ SimpleFunctionFactory::instance ().get_function (func_name, arg_cols, return_type);
385+ ASSERT_TRUE (func != nullptr );
386+
387+ std::vector<DataTypePtr> arg_types = {str_type, str_type};
388+ FunctionUtils fn_utils ({}, arg_types, false );
389+ auto * fn_ctx = fn_utils.get_fn_ctx ();
390+ fn_ctx->set_constant_cols (
391+ {nullptr , std::make_shared<ColumnPtrWrapper>(block.get_by_position (1 ).column )});
392+
393+ ASSERT_EQ (Status::OK (), func->open (fn_ctx, FunctionContext::FRAGMENT_LOCAL));
394+ // Invalid pattern should cause open() to fail for THREAD_LOCAL scope
395+ EXPECT_NE (Status::OK (), func->open (fn_ctx, FunctionContext::THREAD_LOCAL));
396+
397+ static_cast <void >(func->close (fn_ctx, FunctionContext::THREAD_LOCAL));
398+ static_cast <void >(func->close (fn_ctx, FunctionContext::FRAGMENT_LOCAL));
399+ }
307400}
308401
309402TEST (FunctionLikeTest, regexp_replace) {
0 commit comments