@@ -304,6 +304,100 @@ 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
361+ << " null_pattern=" << null_pattern;
362+
363+ static_cast <void >(func->close (fn_ctx, FunctionContext::THREAD_LOCAL));
364+ static_cast <void >(func->close (fn_ctx, FunctionContext::FRAGMENT_LOCAL));
365+ };
366+
367+ // NULL input string → null result
368+ run_null_case (true , false );
369+ // NULL pattern → null result
370+ run_null_case (false , true );
371+
372+ // Invalid const pattern → open() should fail
373+ {
374+ auto col_str = ColumnString::create ();
375+ col_str->insert_data (" abc" , 3 );
376+ auto col_pattern = ColumnString::create ();
377+ col_pattern->insert_data (" (" , 1 );
378+ Block block;
379+ block.insert ({std::move (col_str), str_type, " str" });
380+ block.insert ({ColumnConst::create (std::move (col_pattern), 1 ), str_type, " pattern" });
381+ block.insert ({nullptr , return_type, " result" });
382+
383+ ColumnsWithTypeAndName arg_cols = {block.get_by_position (0 ), block.get_by_position (1 )};
384+ auto func =
385+ SimpleFunctionFactory::instance ().get_function (func_name, arg_cols, return_type);
386+ ASSERT_TRUE (func != nullptr );
387+
388+ std::vector<DataTypePtr> arg_types = {str_type, str_type};
389+ FunctionUtils fn_utils ({}, arg_types, false );
390+ auto * fn_ctx = fn_utils.get_fn_ctx ();
391+ fn_ctx->set_constant_cols (
392+ {nullptr , std::make_shared<ColumnPtrWrapper>(block.get_by_position (1 ).column )});
393+
394+ ASSERT_EQ (Status::OK (), func->open (fn_ctx, FunctionContext::FRAGMENT_LOCAL));
395+ // Invalid pattern should cause open() to fail for THREAD_LOCAL scope
396+ EXPECT_NE (Status::OK (), func->open (fn_ctx, FunctionContext::THREAD_LOCAL));
397+
398+ static_cast <void >(func->close (fn_ctx, FunctionContext::THREAD_LOCAL));
399+ static_cast <void >(func->close (fn_ctx, FunctionContext::FRAGMENT_LOCAL));
400+ }
307401}
308402
309403TEST (FunctionLikeTest, regexp_replace) {
0 commit comments