// Licensed to the Apache Software Foundation (ASF) under one // or more contributor license agreements. See the NOTICE file // distributed with this work for additional information // regarding copyright ownership. The ASF licenses this file // to you under the Apache License, Version 2.0 (the // "License"); you may not use this file except in compliance // with the License. You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, // software distributed under the License is distributed on an // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY // KIND, either express or implied. See the License for the // specific language governing permissions and limitations // under the License. #include #include #include #include #include #include #include #include #include #include #include #include "common/status.h" #include "core/block/block.h" #include "core/block/column_numbers.h" #include "core/block/column_with_type_and_name.h" #include "core/column/column.h" #include "core/column/column_array.h" #include "core/column/column_const.h" #include "core/column/column_execute_util.h" #include "core/column/column_nullable.h" #include "core/column/column_string.h" #include "core/column/column_vector.h" #include "core/data_type/data_type.h" #include "core/data_type/data_type_array.h" #include "core/data_type/data_type_nullable.h" #include "core/data_type/data_type_number.h" #include "core/data_type/data_type_string.h" #include "core/string_ref.h" #include "core/types.h" #include "exec/common/stringop_substring.h" #include "exprs/aggregate/aggregate_function.h" #include "exprs/function/function.h" #include "exprs/function/simple_function_factory.h" #include "exprs/function_context.h" #include "exprs/string_functions.h" namespace doris { // Helper structure to hold either RE2 or Boost.Regex struct RegexpExtractEngine { std::unique_ptr re2_regex; std::unique_ptr boost_regex; bool is_boost() const { return boost_regex != nullptr; } bool is_re2() const { return re2_regex != nullptr; } // Try to compile with RE2 first, fallback to Boost.Regex if RE2 fails static bool compile(const StringRef& pattern, std::string* error_str, RegexpExtractEngine& engine, bool enable_extended_regex) { re2::RE2::Options options; options.set_log_errors(false); // avoid RE2 printing to stderr; we handle errors ourselves options.set_dot_nl(true); // make '.' match '\n' by default, consistent with REGEXP/LIKE engine.re2_regex = std::make_unique(re2::StringPiece(pattern.data, pattern.size), options); if (engine.re2_regex->ok()) { return true; } else if (!enable_extended_regex) { *error_str = fmt::format( "Invalid regex pattern: {}. Error: {}. If you need advanced regex features, " "try setting enable_extended_regex=true", std::string(pattern.data, pattern.size), engine.re2_regex->error()); return false; } // RE2 failed, try Boost.Regex for advanced features like zero-width assertions engine.re2_regex.reset(); try { boost::regex::flag_type flags = boost::regex::normal; engine.boost_regex = std::make_unique(pattern.data, pattern.data + pattern.size, flags); return true; } catch (const boost::regex_error& e) { if (error_str) { *error_str = fmt::format("Invalid regex pattern: {}. Error: {}", std::string(pattern.data, pattern.size), e.what()); } return false; } } // Get number of capturing groups int number_of_capturing_groups() const { if (is_re2()) { return re2_regex->NumberOfCapturingGroups(); } else if (is_boost()) { return static_cast(boost_regex->mark_count()); } return 0; } // Match function for extraction bool match_and_extract(const char* data, size_t size, int index, std::string& result) const { if (is_re2()) { int max_matches = 1 + re2_regex->NumberOfCapturingGroups(); if (index >= max_matches) { return false; } std::vector matches(max_matches); bool success = re2_regex->Match(re2::StringPiece(data, size), 0, size, re2::RE2::UNANCHORED, matches.data(), max_matches); if (success && index < matches.size()) { const re2::StringPiece& match = matches[index]; result.assign(match.data(), match.size()); return true; } return false; } else if (is_boost()) { boost::cmatch matches; bool success = boost::regex_search(data, data + size, matches, *boost_regex); if (success && index < matches.size()) { result = matches[index].str(); return true; } return false; } return false; } // Match all occurrences and extract the first capturing group void match_all_and_extract(const char* data, size_t size, std::vector& results) const { if (is_re2()) { int max_matches = 1 + re2_regex->NumberOfCapturingGroups(); if (max_matches < 2) { return; // No capturing groups } size_t pos = 0; while (pos < size) { const char* str_pos = data + pos; size_t str_size = size - pos; std::vector matches(max_matches); bool success = re2_regex->Match(re2::StringPiece(str_pos, str_size), 0, str_size, re2::RE2::UNANCHORED, matches.data(), max_matches); if (!success) { break; } if (matches[0].empty()) { pos += 1; continue; } // Extract first capturing group if (matches.size() > 1 && !matches[1].empty()) { results.emplace_back(matches[1].data(), matches[1].size()); } // Move position forward auto offset = std::string(str_pos, str_size) .find(std::string(matches[0].data(), matches[0].size())); pos += offset + matches[0].size(); } } else if (is_boost()) { const char* search_start = data; const char* search_end = data + size; boost::match_results matches; while (boost::regex_search(search_start, search_end, matches, *boost_regex)) { if (matches.size() > 1 && matches[1].matched) { results.emplace_back(matches[1].str()); } if (matches[0].length() == 0) { if (search_start == search_end) { break; } search_start += 1; } else { search_start = matches[0].second; } } } } }; struct RegexpCountImpl { using StringColumnView = ColumnView; static void execute_impl(FunctionContext* context, ColumnPtr argument_columns[], size_t input_rows_count, ColumnInt32::Container& result_data) { auto str_col = StringColumnView::create(argument_columns[0]); auto pattern_col = StringColumnView::create(argument_columns[1]); for (size_t i = 0; i < input_rows_count; ++i) { DCHECK(!str_col.is_null_at(i)); DCHECK(!pattern_col.is_null_at(i)); result_data[i] = _execute_inner_loop(context, str_col, pattern_col, i); } } static int _execute_inner_loop(FunctionContext* context, const StringColumnView& str_col, const StringColumnView& pattern_col, const size_t index_now) { re2::RE2* re = reinterpret_cast( context->get_function_state(FunctionContext::THREAD_LOCAL)); std::unique_ptr scoped_re; if (re == nullptr) { std::string error_str; const auto pattern = pattern_col.value_at(index_now); bool st = StringFunctions::compile_regex(pattern, &error_str, StringRef(), StringRef(), scoped_re); if (!st) { context->add_warning(error_str.c_str()); throw Exception(Status::InvalidArgument(error_str)); return 0; } re = scoped_re.get(); } const auto str = str_col.value_at(index_now); int count = 0; size_t pos = 0; while (pos < str.size) { auto str_pos = str.data + pos; auto str_size = str.size - pos; re2::StringPiece str_sp_current = re2::StringPiece(str_pos, str_size); re2::StringPiece match; bool success = re->Match(str_sp_current, 0, str_size, re2::RE2::UNANCHORED, &match, 1); if (!success) { break; } if (match.empty()) { pos += 1; continue; } count++; size_t match_start = match.data() - str_sp_current.data(); pos += match_start + match.size(); } return count; } }; class FunctionRegexpCount : public IFunction { public: static constexpr auto name = "regexp_count"; static FunctionPtr create() { return std::make_shared(); } String get_name() const override { return name; } size_t get_number_of_arguments() const override { return 2; } DataTypePtr get_return_type_impl(const DataTypes& arguments) const override { return std::make_shared(); } Status open(FunctionContext* context, FunctionContext::FunctionStateScope scope) override { if (scope == FunctionContext::THREAD_LOCAL) { if (context->is_col_constant(1)) { DCHECK(!context->get_function_state(scope)); const auto pattern_col = context->get_constant_col(1)->column_ptr; const auto& pattern = pattern_col->get_data_at(0); if (pattern.size == 0) { return Status::OK(); } std::string error_str; std::unique_ptr scoped_re; bool st = StringFunctions::compile_regex(pattern, &error_str, StringRef(), StringRef(), scoped_re); if (!st) { context->set_error(error_str.c_str()); return Status::InvalidArgument(error_str); } std::shared_ptr re(scoped_re.release()); context->set_function_state(scope, re); } } return Status::OK(); } Status execute_impl(FunctionContext* context, Block& block, const ColumnNumbers& arguments, uint32_t result, size_t input_rows_count) const override { auto result_data_column = ColumnInt32::create(input_rows_count); auto& result_data = result_data_column->get_data(); ColumnPtr argument_columns[2]; argument_columns[0] = block.get_by_position(arguments[0]).column; argument_columns[1] = block.get_by_position(arguments[1]).column; RegexpCountImpl::execute_impl(context, argument_columns, input_rows_count, result_data); block.get_by_position(result).column = std::move(result_data_column); return Status::OK(); } }; struct ThreeParamTypes { static DataTypes get_variadic_argument_types() { return {std::make_shared(), std::make_shared(), std::make_shared()}; } }; struct FourParamTypes { static DataTypes get_variadic_argument_types() { return {std::make_shared(), std::make_shared(), std::make_shared(), std::make_shared()}; } }; // template FunctionRegexpFunctionality is used for regexp_replace/regexp_replace_one template class FunctionRegexpReplace : public IFunction { public: static constexpr auto name = Impl::name; static FunctionPtr create() { return std::make_shared(); } String get_name() const override { return name; } size_t get_number_of_arguments() const override { return get_variadic_argument_types_impl().size(); } bool is_variadic() const override { return true; } DataTypePtr get_return_type_impl(const DataTypes& arguments) const override { return make_nullable(std::make_shared()); } DataTypes get_variadic_argument_types_impl() const override { return ParamTypes::get_variadic_argument_types(); } Status open(FunctionContext* context, FunctionContext::FunctionStateScope scope) override { if (scope == FunctionContext::THREAD_LOCAL) { if (context->is_col_constant(1)) { DCHECK(!context->get_function_state(scope)); const auto pattern_col = context->get_constant_col(1)->column_ptr; const auto& pattern = pattern_col->get_data_at(0); if (pattern.size == 0) { return Status::OK(); } std::string error_str; std::unique_ptr scoped_re; StringRef options_value; if constexpr (std::is_same_v) { DCHECK_EQ(context->get_num_args(), 4); DCHECK(context->is_col_constant(3)); const auto options_col = context->get_constant_col(3)->column_ptr; options_value = options_col->get_data_at(0); } bool st = StringFunctions::compile_regex(pattern, &error_str, StringRef(), options_value, scoped_re); if (!st) { context->set_error(error_str.c_str()); return Status::InvalidArgument(error_str); } std::shared_ptr re(scoped_re.release()); context->set_function_state(scope, re); } } return Status::OK(); } Status execute_impl(FunctionContext* context, Block& block, const ColumnNumbers& arguments, uint32_t result, size_t input_rows_count) const override { size_t argument_size = arguments.size(); auto result_null_map = ColumnUInt8::create(input_rows_count, 0); auto result_data_column = ColumnString::create(); auto& result_data = result_data_column->get_chars(); auto& result_offset = result_data_column->get_offsets(); result_offset.resize(input_rows_count); bool col_const[3]; ColumnPtr argument_columns[3]; for (int i = 0; i < 3; ++i) { col_const[i] = is_column_const(*block.get_by_position(arguments[i]).column); } argument_columns[0] = col_const[0] ? static_cast( *block.get_by_position(arguments[0]).column) .convert_to_full_column() : block.get_by_position(arguments[0]).column; default_preprocess_parameter_columns(argument_columns, col_const, {1, 2}, block, arguments); StringRef options_value; if (col_const[1] && col_const[2]) { Impl::execute_impl_const_args(context, argument_columns, options_value, input_rows_count, result_data, result_offset, result_null_map->get_data()); } else { // the options have check in FE, so is always const, and get idx of 0 if (argument_size == 4) { options_value = block.get_by_position(arguments[3]).column->get_data_at(0); } Impl::execute_impl(context, argument_columns, options_value, input_rows_count, result_data, result_offset, result_null_map->get_data()); } block.get_by_position(result).column = ColumnNullable::create(std::move(result_data_column), std::move(result_null_map)); return Status::OK(); } }; template struct RegexpReplaceImpl { static constexpr auto name = ReplaceOne ? "regexp_replace_one" : "regexp_replace"; static void execute_impl(FunctionContext* context, ColumnPtr argument_columns[], const StringRef& options_value, size_t input_rows_count, ColumnString::Chars& result_data, ColumnString::Offsets& result_offset, NullMap& null_map) { const auto* str_col = check_and_get_column(argument_columns[0].get()); const auto* pattern_col = check_and_get_column(argument_columns[1].get()); const auto* replace_col = check_and_get_column(argument_columns[2].get()); for (size_t i = 0; i < input_rows_count; ++i) { _execute_inner_loop(context, str_col, pattern_col, replace_col, options_value, result_data, result_offset, null_map, i); } } static void execute_impl_const_args(FunctionContext* context, ColumnPtr argument_columns[], const StringRef& options_value, size_t input_rows_count, ColumnString::Chars& result_data, ColumnString::Offsets& result_offset, NullMap& null_map) { const auto* str_col = check_and_get_column(argument_columns[0].get()); const auto* pattern_col = check_and_get_column(argument_columns[1].get()); const auto* replace_col = check_and_get_column(argument_columns[2].get()); for (size_t i = 0; i < input_rows_count; ++i) { _execute_inner_loop(context, str_col, pattern_col, replace_col, options_value, result_data, result_offset, null_map, i); } } template static void _execute_inner_loop(FunctionContext* context, const ColumnString* str_col, const ColumnString* pattern_col, const ColumnString* replace_col, const StringRef& options_value, ColumnString::Chars& result_data, ColumnString::Offsets& result_offset, NullMap& null_map, const size_t index_now) { re2::RE2* re = reinterpret_cast( context->get_function_state(FunctionContext::THREAD_LOCAL)); std::unique_ptr scoped_re; // destroys re if state->re is nullptr if (re == nullptr) { std::string error_str; const auto& pattern = pattern_col->get_data_at(index_check_const(index_now, Const)); bool st = StringFunctions::compile_regex(pattern, &error_str, StringRef(), options_value, scoped_re); if (!st) { context->add_warning(error_str.c_str()); StringOP::push_null_string(index_now, result_data, result_offset, null_map); return; } re = scoped_re.get(); } re2::StringPiece replace_str = re2::StringPiece( replace_col->get_data_at(index_check_const(index_now, Const)).to_string_view()); std::string result_str(str_col->get_data_at(index_now).to_string()); if constexpr (ReplaceOne) { re2::RE2::Replace(&result_str, *re, replace_str); } else { re2::RE2::GlobalReplace(&result_str, *re, replace_str); } StringOP::push_value_string(result_str, index_now, result_data, result_offset); } }; template struct RegexpExtractImpl { static constexpr auto name = ReturnNull ? "regexp_extract_or_null" : "regexp_extract"; static constexpr size_t num_args = 3; static constexpr size_t PATTERN_ARG_IDX = 1; static DataTypePtr return_type() { return make_nullable(std::make_shared()); } static Status execute(FunctionContext* context, Block& block, const ColumnNumbers& arguments, uint32_t result, size_t input_rows_count) { bool col_const[3]; ColumnPtr argument_columns[3]; for (int i = 0; i < 3; ++i) { col_const[i] = is_column_const(*block.get_by_position(arguments[i]).column); } argument_columns[0] = col_const[0] ? static_cast( *block.get_by_position(arguments[0]).column) .convert_to_full_column() : block.get_by_position(arguments[0]).column; auto result_null_map = ColumnUInt8::create(input_rows_count, 0); auto result_data_column = ColumnString::create(); auto& result_data = result_data_column->get_chars(); auto& result_offset = result_data_column->get_offsets(); result_offset.resize(input_rows_count); auto& null_map = result_null_map->get_data(); default_preprocess_parameter_columns(argument_columns, col_const, {1, 2}, block, arguments); if (col_const[1] && col_const[2]) { _execute_loop(context, argument_columns, input_rows_count, result_data, result_offset, null_map); } else { _execute_loop(context, argument_columns, input_rows_count, result_data, result_offset, null_map); } block.get_by_position(result).column = ColumnNullable::create(std::move(result_data_column), std::move(result_null_map)); return Status::OK(); } private: template static void _execute_loop(FunctionContext* context, ColumnPtr argument_columns[], size_t input_rows_count, ColumnString::Chars& result_data, ColumnString::Offsets& result_offset, NullMap& null_map) { const auto* str_col = check_and_get_column(argument_columns[0].get()); const auto* pattern_col = check_and_get_column(argument_columns[1].get()); const auto* index_col = check_and_get_column(argument_columns[2].get()); if constexpr (Const) { const auto& index_data = index_col->get_int(0); if (index_data < 0) { for (size_t i = 0; i < input_rows_count; ++i) { ReturnNull ? StringOP::push_null_string(i, result_data, result_offset, null_map) : StringOP::push_empty_string(i, result_data, result_offset); } return; } for (size_t i = 0; i < input_rows_count; ++i) { _execute_inner_loop(context, str_col, pattern_col, index_data, result_data, result_offset, null_map, i); } } else { for (size_t i = 0; i < input_rows_count; ++i) { const auto& index_data = index_col->get_int(i); if (index_data < 0) { ReturnNull ? StringOP::push_null_string(i, result_data, result_offset, null_map) : StringOP::push_empty_string(i, result_data, result_offset); continue; } _execute_inner_loop(context, str_col, pattern_col, index_data, result_data, result_offset, null_map, i); } } } template static void _execute_inner_loop(FunctionContext* context, const ColumnString* str_col, const ColumnString* pattern_col, const Int64 index_data, ColumnString::Chars& result_data, ColumnString::Offsets& result_offset, NullMap& null_map, const size_t index_now) { auto* engine = reinterpret_cast( context->get_function_state(FunctionContext::THREAD_LOCAL)); std::unique_ptr scoped_engine; if (engine == nullptr) { std::string error_str; const auto& pattern = pattern_col->get_data_at(index_check_const(index_now, Const)); scoped_engine = std::make_unique(); bool st = RegexpExtractEngine::compile(pattern, &error_str, *scoped_engine, context->state()->enable_extended_regex()); if (!st) { context->add_warning(error_str.c_str()); StringOP::push_null_string(index_now, result_data, result_offset, null_map); return; } engine = scoped_engine.get(); } const auto& str = str_col->get_data_at(index_now); int max_matches = 1 + engine->number_of_capturing_groups(); if (index_data >= max_matches) { ReturnNull ? StringOP::push_null_string(index_now, result_data, result_offset, null_map) : StringOP::push_empty_string(index_now, result_data, result_offset); return; } std::string match_result; bool success = engine->match_and_extract(str.data, str.size, static_cast(index_data), match_result); if (!success) { ReturnNull ? StringOP::push_null_string(index_now, result_data, result_offset, null_map) : StringOP::push_empty_string(index_now, result_data, result_offset); return; } StringOP::push_value_string(std::string_view(match_result.data(), match_result.size()), index_now, result_data, result_offset); } }; // Output handler for existing string-formatted result: "['a','b']" struct RegexpExtractAllStringOutput { static constexpr const char* func_name = "regexp_extract_all"; static DataTypePtr return_type() { return make_nullable(std::make_shared()); } ColumnString::Chars& result_data; ColumnString::Offsets& result_offset; void push_empty(size_t index) { StringOP::push_empty_string(index, result_data, result_offset); } void push_null(size_t index, NullMap& null_map) { StringOP::push_null_string(index, result_data, result_offset, null_map); } void push_matches(size_t index, const std::vector& matches) { size_t total_size = 2; // '[' and ']' for (const auto& m : matches) { total_size += m.size() + 3; // "'xxx'," } size_t old_size = result_data.size(); result_data.resize(old_size + total_size); char* pos = reinterpret_cast(&result_data[old_size]); *pos++ = '['; for (size_t j = 0; j < matches.size(); ++j) { if (j > 0) { *pos++ = ','; } *pos++ = '\''; memcpy(pos, matches[j].data(), matches[j].size()); pos += matches[j].size(); *pos++ = '\''; } *pos++ = ']'; result_data.resize(old_size + static_cast(pos - reinterpret_cast( &result_data[old_size]))); result_offset[index] = static_cast(result_data.size()); } struct State { ColumnString::MutablePtr data_column; explicit State(size_t rows) : data_column(ColumnString::create()) { data_column->get_offsets().resize(rows); } RegexpExtractAllStringOutput create_handler() { return {.result_data = data_column->get_chars(), .result_offset = data_column->get_offsets()}; } ColumnPtr finalize(ColumnUInt8::MutablePtr null_map) { return ColumnNullable::create(std::move(data_column), std::move(null_map)); } }; }; // Output handler for proper Array> result struct RegexpExtractAllArrayOutput { static constexpr const char* func_name = "regexp_extract_all_array"; static DataTypePtr return_type() { return make_nullable( std::make_shared(make_nullable(std::make_shared()))); } ColumnString& nested_col; ColumnArray::Offsets64& array_offsets; NullMap& nested_null_map; UInt64 current_offset = 0; void push_empty(size_t index) { array_offsets.push_back(current_offset); } void push_null(size_t index, NullMap& null_map) { null_map[index] = 1; array_offsets.push_back(current_offset); } void push_matches(size_t index, const std::vector& matches) { for (const auto& m : matches) { nested_col.insert_data(m.data(), m.size()); nested_null_map.push_back(0); current_offset++; } array_offsets.push_back(current_offset); } struct State { ColumnArray::MutablePtr array_column; ColumnNullable* nested_nullable; explicit State(size_t /*rows*/) { auto nullable_str = make_nullable(std::make_shared()); array_column = ColumnArray::create(nullable_str->create_column(), ColumnArray::ColumnOffsets::create()); nested_nullable = assert_cast(&array_column->get_data()); } RegexpExtractAllArrayOutput create_handler() { return {.nested_col = assert_cast(nested_nullable->get_nested_column()), .array_offsets = array_column->get_offsets(), .nested_null_map = nested_nullable->get_null_map_data()}; } ColumnPtr finalize(ColumnUInt8::MutablePtr null_map) { return ColumnNullable::create(std::move(array_column), std::move(null_map)); } }; }; // Handler controls return type & column layout template struct RegexpExtractAllImpl { static constexpr auto name = Handler::func_name; static constexpr size_t num_args = 2; static constexpr size_t PATTERN_ARG_IDX = 1; static DataTypePtr return_type() { return Handler::return_type(); } static Status execute(FunctionContext* context, Block& block, const ColumnNumbers& arguments, uint32_t result, size_t input_rows_count) { bool col_const[2]; ColumnPtr argument_columns[2]; for (int i = 0; i < 2; ++i) { col_const[i] = is_column_const(*block.get_by_position(arguments[i]).column); } argument_columns[0] = col_const[0] ? static_cast( *block.get_by_position(arguments[0]).column) .convert_to_full_column() : block.get_by_position(arguments[0]).column; default_preprocess_parameter_columns(argument_columns, col_const, {1}, block, arguments); const auto* str_col = check_and_get_column(argument_columns[0].get()); const auto* pattern_col = check_and_get_column(argument_columns[1].get()); auto outer_null_map = ColumnUInt8::create(input_rows_count, 0); auto& null_map_data = outer_null_map->get_data(); typename Handler::State state(input_rows_count); auto handler = state.create_handler(); std::visit( [&](auto is_const) { for (size_t i = 0; i < input_rows_count; ++i) { if (null_map_data[i]) { handler.push_null(i, null_map_data); continue; } regexp_extract_all_inner_loop(context, str_col, pattern_col, handler, null_map_data, i); } }, make_bool_variant(col_const[1])); block.get_by_position(result).column = state.finalize(std::move(outer_null_map)); return Status::OK(); } private: template static void regexp_extract_all_inner_loop(FunctionContext* context, const ColumnString* str_col, const ColumnString* pattern_col, Handler& handler, NullMap& null_map, const size_t index_now) { auto* engine = reinterpret_cast( context->get_function_state(FunctionContext::THREAD_LOCAL)); std::unique_ptr scoped_engine; if (engine == nullptr) { std::string error_str; const auto& pattern = pattern_col->get_data_at(index_check_const(index_now, is_const)); scoped_engine = std::make_unique(); bool st = RegexpExtractEngine::compile(pattern, &error_str, *scoped_engine, context->state()->enable_extended_regex()); if (!st) { context->add_warning(error_str.c_str()); handler.push_null(index_now, null_map); return; } engine = scoped_engine.get(); } if (engine->number_of_capturing_groups() == 0) { handler.push_empty(index_now); return; } const auto& str = str_col->get_data_at(index_now); std::vector res_matches; engine->match_all_and_extract(str.data, str.size, res_matches); if (res_matches.empty()) { handler.push_empty(index_now); return; } handler.push_matches(index_now, res_matches); } }; // template FunctionRegexpFunctionality is used for regexp_xxxx series functions, not for regexp match. template class FunctionRegexpFunctionality : public IFunction { public: static constexpr auto name = Impl::name; static FunctionPtr create() { return std::make_shared(); } String get_name() const override { return name; } size_t get_number_of_arguments() const override { return Impl::num_args; } DataTypePtr get_return_type_impl(const DataTypes& arguments) const override { return Impl::return_type(); } Status open(FunctionContext* context, FunctionContext::FunctionStateScope scope) override { if (scope == FunctionContext::THREAD_LOCAL) { if (context->is_col_constant(Impl::PATTERN_ARG_IDX)) { DCHECK(!context->get_function_state(scope)); const auto pattern_col = context->get_constant_col(Impl::PATTERN_ARG_IDX)->column_ptr; const auto& pattern = pattern_col->get_data_at(0); if (pattern.size == 0) { return Status::OK(); } std::string error_str; auto engine = std::make_shared(); bool st = RegexpExtractEngine::compile(pattern, &error_str, *engine, context->state()->enable_extended_regex()); if (!st) { context->set_error(error_str.c_str()); return Status::InvalidArgument(error_str); } context->set_function_state(scope, engine); } } return Status::OK(); } Status execute_impl(FunctionContext* context, Block& block, const ColumnNumbers& arguments, uint32_t result, size_t input_rows_count) const override { return Impl::execute(context, block, arguments, result, input_rows_count); } }; void register_function_regexp_extract(SimpleFunctionFactory& factory) { factory.register_function, ThreeParamTypes>>(); factory.register_function, FourParamTypes>>(); factory.register_function, ThreeParamTypes>>(); factory.register_function, FourParamTypes>>(); factory.register_function>>(); factory.register_function>>(); factory.register_function< FunctionRegexpFunctionality>>(); factory.register_function< FunctionRegexpFunctionality>>(); factory.register_function(); } } // namespace doris