diff --git a/be/src/exprs/string_functions.cpp b/be/src/exprs/string_functions.cpp index 462d7b6146..fb6e0f5f98 100644 --- a/be/src/exprs/string_functions.cpp +++ b/be/src/exprs/string_functions.cpp @@ -682,6 +682,15 @@ StringVal StringFunctions::concat_ws(FunctionContext* context, const StringVal& return result; } +StringVal StringFunctions::elt(FunctionContext* context, const IntVal& pos, int num_children, + const StringVal* strs) { + if (pos.is_null || pos.val < 1 || num_children == 0 || pos.val > num_children) { + return StringVal::null(); + } + + return strs[pos.val - 1]; +} + IntVal StringFunctions::find_in_set(FunctionContext* context, const StringVal& str, const StringVal& str_set) { if (str.is_null || str_set.is_null) { diff --git a/be/src/exprs/string_functions.h b/be/src/exprs/string_functions.h index 3d47963c12..1e5ecf8d9e 100644 --- a/be/src/exprs/string_functions.h +++ b/be/src/exprs/string_functions.h @@ -115,6 +115,8 @@ public: static StringVal concat(doris_udf::FunctionContext*, int num_children, const StringVal* strs); static StringVal concat_ws(doris_udf::FunctionContext*, const doris_udf::StringVal& sep, int num_children, const doris_udf::StringVal* strs); + static StringVal elt(doris_udf::FunctionContext*, const doris_udf::IntVal& pos, + int num_children, const StringVal* strs); static IntVal find_in_set(doris_udf::FunctionContext*, const doris_udf::StringVal& str, const doris_udf::StringVal& str_set); diff --git a/be/src/vec/functions/function_string.cpp b/be/src/vec/functions/function_string.cpp index 1a327efd72..d5f451ef2d 100644 --- a/be/src/vec/functions/function_string.cpp +++ b/be/src/vec/functions/function_string.cpp @@ -629,6 +629,7 @@ void register_function_string(SimpleFunctionFactory& factory) { factory.register_function(); factory.register_function(); factory.register_function(); + factory.register_function(); factory.register_function(); factory.register_function(); factory.register_function(); diff --git a/be/src/vec/functions/function_string.h b/be/src/vec/functions/function_string.h index c0cb214fcd..69c61f4d6f 100644 --- a/be/src/vec/functions/function_string.h +++ b/be/src/vec/functions/function_string.h @@ -494,6 +494,51 @@ public: } }; +class FunctionStringElt : public IFunction { +public: + static constexpr auto name = "elt"; + static FunctionPtr create() { return std::make_shared(); } + String get_name() const override { return name; } + size_t get_number_of_arguments() const override { return 0; } + bool is_variadic() const override { return true; } + + DataTypePtr get_return_type_impl(const DataTypes& arguments) const override { + return std::make_shared(); + } + bool use_default_implementation_for_nulls() const override { return true; } + bool use_default_implementation_for_constants() const override { return true; } + + Status execute_impl(FunctionContext* context, Block& block, const ColumnNumbers& arguments, + size_t result, size_t input_rows_count) override { + int arguent_size = arguments.size(); + auto pos_col = + block.get_by_position(arguments[0]).column->convert_to_full_column_if_const(); + if (auto* nullable = check_and_get_column(*pos_col)) { + pos_col = nullable->get_nested_column_ptr(); + } + auto& pos_data = assert_cast(pos_col.get())->get_data(); + auto pos = pos_data[0]; + int num_children = arguent_size - 1; + if (pos < 1 || num_children == 0 || pos > num_children) { + auto null_map = ColumnUInt8::create(input_rows_count, 1); + auto res = ColumnString::create(); + auto& res_data = res->get_chars(); + auto& res_offset = res->get_offsets(); + res_offset.resize(input_rows_count); + for (size_t i = 0; i < input_rows_count; ++i) { + res_data.push_back('\0'); + res_offset[i] = res_data.size(); + } + block.get_by_position(result).column = + ColumnNullable::create(std::move(res), std::move(null_map)); + return Status::OK(); + } + + block.get_by_position(result).column = block.get_by_position(arguments[pos]).column; + return Status::OK(); + } +}; + // concat_ws (string,string....) or (string, Array) // TODO: avoid use fmtlib class FunctionStringConcatWs : public IFunction { diff --git a/be/test/exprs/string_functions_test.cpp b/be/test/exprs/string_functions_test.cpp index ef093da06f..fa6d912b4e 100644 --- a/be/test/exprs/string_functions_test.cpp +++ b/be/test/exprs/string_functions_test.cpp @@ -669,6 +669,14 @@ TEST_F(StringFunctionsTest, lower) { EXPECT_EQ(StringVal(""), StringFunctions::lower(ctx, StringVal(""))); } +TEST_F(StringFunctionsTest, elt) { + StringVal str[] = {"hello", "world"}; + EXPECT_EQ(StringVal("hello"), StringFunctions::elt(ctx, 1, 2, str)); + EXPECT_EQ(StringVal("world"), StringFunctions::elt(ctx, 2, 2, str)); + EXPECT_EQ(StringVal::null(), StringFunctions::elt(ctx, 0, 2, str)); + EXPECT_EQ(StringVal::null(), StringFunctions::elt(ctx, 3, 2, str)); +} + TEST_F(StringFunctionsTest, upper) { // function test EXPECT_EQ(StringVal("HELLO"), StringFunctions::upper(ctx, StringVal("HELLO"))); diff --git a/be/test/vec/function/function_string_test.cpp b/be/test/vec/function/function_string_test.cpp index 2d259014be..fe4b90fab8 100644 --- a/be/test/vec/function/function_string_test.cpp +++ b/be/test/vec/function/function_string_test.cpp @@ -343,6 +343,50 @@ TEST(function_string_test, function_concat_test) { }; } +TEST(function_string_test, function_elt_test) { + std::string func_name = "elt"; + + { + InputTypeSet input_types = {TypeIndex::Int32, TypeIndex::String, TypeIndex::String}; + + DataSet data_set = {{{1, std::string("hello"), std::string("world")}, std::string("hello")}, + {{1, std::string("你好"), std::string("百度")}, std::string("你好")}, + {{1, std::string("hello"), std::string("")}, std::string("hello")}}; + + check_function(func_name, input_types, data_set); + }; + + { + InputTypeSet input_types = {TypeIndex::Int32, TypeIndex::String, TypeIndex::String}; + + DataSet data_set = {{{2, std::string("hello"), std::string("world")}, std::string("world")}, + {{2, std::string("你好"), std::string("百度")}, std::string("百度")}, + {{2, std::string("hello"), std::string("")}, std::string("")}}; + + check_function(func_name, input_types, data_set); + }; + + { + InputTypeSet input_types = {TypeIndex::Int32, TypeIndex::String, TypeIndex::String}; + + DataSet data_set = {{{0, std::string("hello"), std::string("world")}, Null()}, + {{0, std::string("你好"), std::string("百度")}, Null()}, + {{0, std::string("hello"), std::string("")}, Null()}}; + + check_function(func_name, input_types, data_set); + }; + + { + InputTypeSet input_types = {TypeIndex::Int32, TypeIndex::String, TypeIndex::String}; + + DataSet data_set = {{{3, std::string("hello"), std::string("world")}, Null()}, + {{3, std::string("你好"), std::string("百度")}, Null()}, + {{3, std::string("hello"), std::string("")}, Null()}}; + + check_function(func_name, input_types, data_set); + }; +} + TEST(function_string_test, function_concat_ws_test) { std::string func_name = "concat_ws"; { diff --git a/docs/en/docs/sql-manual/sql-functions/string-functions/elt.md b/docs/en/docs/sql-manual/sql-functions/string-functions/elt.md new file mode 100644 index 0000000000..04667457a1 --- /dev/null +++ b/docs/en/docs/sql-manual/sql-functions/string-functions/elt.md @@ -0,0 +1,62 @@ +--- +{ + "title": "elt", + "language": "en" +} +--- + + + +## elt +### Description +#### Syntax + +`VARCHAR elt (INT,VARCHAR,...)` + +Returns the string at specified index. Returns NULL if there is no string at specified index. + +### example + +``` +mysql> select elt(1, 'aaa', 'bbb'); ++----------------------+ +| elt(1, 'aaa', 'bbb') | ++----------------------+ +| aaa | ++----------------------+ +mysql> select elt(2, 'aaa', 'bbb'); ++-----------------------+ +| elt(2, 'aaa', 'bbb') | ++-----------------------+ +| bbb | ++-----------------------+ +mysql> select elt(0, 'aaa', 'bbb'); ++----------------------+ +| elt(0, 'aaa', 'bbb') | ++----------------------+ +| NULL | ++----------------------+ +mysql> select elt(2, 'aaa', 'bbb'); ++-----------------------+ +| elt(3, 'aaa', 'bbb') | ++-----------------------+ +| NULL | ++-----------------------+ +``` +### keywords +ELT \ No newline at end of file diff --git a/docs/zh-CN/docs/sql-manual/sql-functions/string-functions/elt.md b/docs/zh-CN/docs/sql-manual/sql-functions/string-functions/elt.md new file mode 100644 index 0000000000..f61f1e1abe --- /dev/null +++ b/docs/zh-CN/docs/sql-manual/sql-functions/string-functions/elt.md @@ -0,0 +1,62 @@ +--- +{ + "title": "elt", + "language": "zh-CN" +} +--- + + + +## elt +### Description +#### Syntax + +`VARCHAR elt (INT,VARCHAR,...)` + +在指定的索引处返回一个字符串。如果指定的索引处没有字符串,则返回NULL。 + +### example + +``` +mysql> select elt(1, 'aaa', 'bbb'); ++----------------------+ +| elt(1, 'aaa', 'bbb') | ++----------------------+ +| aaa | ++----------------------+ +mysql> select elt(2, 'aaa', 'bbb'); ++-----------------------+ +| elt(2, 'aaa', 'bbb') | ++-----------------------+ +| bbb | ++-----------------------+ +mysql> select elt(0, 'aaa', 'bbb'); ++----------------------+ +| elt(0, 'aaa', 'bbb') | ++----------------------+ +| NULL | ++----------------------+ +mysql> select elt(2, 'aaa', 'bbb'); ++-----------------------+ +| elt(3, 'aaa', 'bbb') | ++-----------------------+ +| NULL | ++-----------------------+ +``` +### keywords +ELT \ No newline at end of file diff --git a/gensrc/script/doris_builtins_functions.py b/gensrc/script/doris_builtins_functions.py index a0660f87f0..9b9eafaba9 100755 --- a/gensrc/script/doris_builtins_functions.py +++ b/gensrc/script/doris_builtins_functions.py @@ -2027,6 +2027,12 @@ visible_functions = [ [['concat'], 'VARCHAR', ['VARCHAR', '...'], '_ZN5doris15StringFunctions6concatEPN9doris_udf15FunctionContextEiPKNS1_9StringValE', '', '', 'vec', ''], + [['elt'], 'VARCHAR', ['INT', 'VARCHAR', '...'], + '_ZN5doris15StringFunctions3eltEPN9doris_udf15FunctionContextERKNS1_6IntValEiPKNS1_9StringValE', + '', '', 'vec', ''], + [['elt'], 'STRING', ['INT', 'STRING', '...'], + '_ZN5doris15StringFunctions3eltEPN9doris_udf15FunctionContextERKNS1_6IntValEiPKNS1_9StringValE', + '', '', 'vec', ''], [['replace'], 'VARCHAR', ['VARCHAR', 'VARCHAR', 'VARCHAR'], '_ZN5doris15StringFunctions7replaceEPN9doris_udf15FunctionContextERKNS1_9StringValES6_S6_', '', '', 'vec', ''], diff --git a/regression-test/data/query_p0/sql_functions/string_functions/test_string_function.out b/regression-test/data/query_p0/sql_functions/string_functions/test_string_function.out index 61e0295054..6bdd10491d 100644 --- a/regression-test/data/query_p0/sql_functions/string_functions/test_string_function.out +++ b/regression-test/data/query_p0/sql_functions/string_functions/test_string_function.out @@ -1,4 +1,16 @@ -- This file is automatically generated. You should know what you did if you want to edit this +-- !sql -- +\N + +-- !sql -- +hello + +-- !sql -- +doris + +-- !sql -- +\N + -- !sql -- ac diff --git a/regression-test/suites/query_p0/sql_functions/string_functions/test_string_function.groovy b/regression-test/suites/query_p0/sql_functions/string_functions/test_string_function.groovy index 5f922276ed..8feee3e9e3 100644 --- a/regression-test/suites/query_p0/sql_functions/string_functions/test_string_function.groovy +++ b/regression-test/suites/query_p0/sql_functions/string_functions/test_string_function.groovy @@ -19,6 +19,11 @@ suite("test_string_function") { sql "set enable_vectorized_engine = true;" sql "set batch_size = 4096;" + qt_sql "select elt(0, \"hello\", \"doris\");" + qt_sql "select elt(1, \"hello\", \"doris\");" + qt_sql "select elt(2, \"hello\", \"doris\");" + qt_sql "select elt(3, \"hello\", \"doris\");" + qt_sql "select append_trailing_char_if_absent('a','c');" qt_sql "select append_trailing_char_if_absent('ac','c');"