diff --git a/docs/en/sql-reference/functions/splitting-merging-functions.md b/docs/en/sql-reference/functions/splitting-merging-functions.md index 93bf2746c3c..b8ec276c7f9 100644 --- a/docs/en/sql-reference/functions/splitting-merging-functions.md +++ b/docs/en/sql-reference/functions/splitting-merging-functions.md @@ -213,7 +213,7 @@ SELECT splitByNonAlpha(' 1! a, b. '); ## arrayStringConcat(arr\[, separator\]) {#arraystringconcatarr-separator} -Concatenates the strings (values of type String or Nullable(String)) listed in the array with the separator. ’separator’ is an optional parameter: a constant string, set to an empty string by default. +Concatenates string representations of values listed in the array with the separator. `separator` is an optional parameter: a constant string, set to an empty string by default. Returns the string. ## alphaTokens(s) {#alphatokenss} diff --git a/docs/ru/sql-reference/functions/splitting-merging-functions.md b/docs/ru/sql-reference/functions/splitting-merging-functions.md index 595ad22ac46..b3f91077dfa 100644 --- a/docs/ru/sql-reference/functions/splitting-merging-functions.md +++ b/docs/ru/sql-reference/functions/splitting-merging-functions.md @@ -212,8 +212,8 @@ SELECT splitByNonAlpha(' 1! a, b. '); ## arrayStringConcat(arr\[, separator\]) {#arraystringconcatarr-separator} -Склеивает строки, перечисленные в массиве, с разделителем separator. -separator - необязательный параметр, константная строка, по умолчанию равен пустой строке. +Склеивает строковые представления элементов массива с разделителем `separator`. +`separator` - необязательный параметр, константная строка, по умолчанию равен пустой строке. Возвращается строка. ## alphaTokens(s) {#alphatokenss} diff --git a/src/Functions/FunctionsStringArray.cpp b/src/Functions/FunctionsStringArray.cpp index 0c76cde701a..0e73d6a33f5 100644 --- a/src/Functions/FunctionsStringArray.cpp +++ b/src/Functions/FunctionsStringArray.cpp @@ -1,21 +1,6 @@ #include #include -namespace -{ -bool isNullableStringOrNullableNothing(DB::DataTypePtr type) -{ - if (type->isNullable()) - { - const auto & nested_type = assert_cast(*type).getNestedType(); - if (isString(nested_type) || isNothing(nested_type)) - return true; - } - return false; -} - -} - namespace DB { namespace ErrorCodes @@ -33,11 +18,8 @@ DataTypePtr FunctionArrayStringConcat::getReturnTypeImpl(const DataTypes & argum ErrorCodes::NUMBER_OF_ARGUMENTS_DOESNT_MATCH); const DataTypeArray * array_type = checkAndGetDataType(arguments[0].get()); - // An array consisting of only Null-s has type Array(Nullable(Nothing)) - if (!array_type || !(isString(array_type->getNestedType()) || isNullableStringOrNullableNothing(array_type->getNestedType()))) - throw Exception( - "First argument for function " + getName() + " must be an array of String-s or Nullable(String)-s.", - ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT); + if (!array_type) + throw Exception("First argument for function " + getName() + " must be an array.", ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT); if (arguments.size() == 2 && !isString(arguments[1])) throw Exception("Second argument for function " + getName() + " must be constant string.", ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT); diff --git a/src/Functions/FunctionsStringArray.h b/src/Functions/FunctionsStringArray.h index c15a8db8186..27907626971 100644 --- a/src/Functions/FunctionsStringArray.h +++ b/src/Functions/FunctionsStringArray.h @@ -9,6 +9,7 @@ #include #include #include +#include #include #include #include @@ -17,7 +18,6 @@ #include #include - namespace DB { @@ -648,7 +648,7 @@ public: }; -/// Joins an array of strings into one string via a separator. +/// Joins an array of type serializable to string into one string via a separator. class FunctionArrayStringConcat : public IFunction { private: @@ -734,6 +734,25 @@ private: null_map); } + static ColumnPtr serializeNestedColumn(const ColumnArray & col_arr, const DataTypePtr & nested_type) + { + if (isString(nested_type)) + { + return col_arr.getDataPtr(); + } + else if (const ColumnNullable * col_nullable = checkAndGetColumn(col_arr.getData()); + col_nullable && isString(col_nullable->getNestedColumn().getDataType())) + { + return col_nullable->getNestedColumnPtr(); + } + else + { + ColumnsWithTypeAndName cols; + cols.emplace_back(col_arr.getDataPtr(), nested_type, "tmp"); + return ConvertImplGenericToString::execute(cols, std::make_shared()); + } + } + public: static constexpr auto name = "arrayStringConcat"; static FunctionPtr create(ContextPtr) { return std::make_shared(); } @@ -761,7 +780,9 @@ public: delimiter = col_delim->getValue(); } - if (const ColumnConst * col_const_arr = checkAndGetColumnConst(arguments[0].column.get())) + const auto & nested_type = assert_cast(*arguments[0].type).getNestedType(); + if (const ColumnConst * col_const_arr = checkAndGetColumnConst(arguments[0].column.get()); + col_const_arr && isString(nested_type)) { Array src_arr = col_const_arr->getValue(); String dst_str; @@ -778,25 +799,19 @@ public: return result_type->createColumnConst(col_const_arr->size(), dst_str); } + + ColumnPtr src_column = arguments[0].column->convertToFullColumnIfConst(); + const ColumnArray & col_arr = assert_cast(*src_column.get()); + + ColumnPtr str_subcolumn = serializeNestedColumn(col_arr, nested_type); + const ColumnString & col_string = assert_cast(*str_subcolumn.get()); + + auto col_res = ColumnString::create(); + if (const ColumnNullable * col_nullable = checkAndGetColumn(col_arr.getData())) + executeInternal(col_string, col_arr, delimiter, *col_res, col_nullable->getNullMapData().data()); else - { - const ColumnArray & col_arr = assert_cast(*arguments[0].column); - auto col_res = ColumnString::create(); - if (WhichDataType(col_arr.getData().getDataType()).isString()) - { - const ColumnString & col_string = assert_cast(col_arr.getData()); - executeInternal(col_string, col_arr, delimiter, *col_res); - } - else - { - const ColumnNullable & col_nullable = assert_cast(col_arr.getData()); - if (const ColumnString * col_string = typeid_cast(col_nullable.getNestedColumnPtr().get())) - executeInternal(*col_string, col_arr, delimiter, *col_res, col_nullable.getNullMapData().data()); - else - col_res->insertManyDefaults(col_arr.size()); - } - return col_res; - } + executeInternal(col_string, col_arr, delimiter, *col_res); + return col_res; } }; diff --git a/tests/queries/0_stateless/00255_array_concat_string.reference b/tests/queries/0_stateless/00255_array_concat_string.reference index e9fafe93ed1..4ffac8e5de0 100644 --- a/tests/queries/0_stateless/00255_array_concat_string.reference +++ b/tests/queries/0_stateless/00255_array_concat_string.reference @@ -65,7 +65,17 @@ yandex google test 123 hello world goodbye xyz yandex google test 123 hello wo 0 hello;world;xyz;def - +1;23;456 +1;23;456 +127.0.0.1; 1.0.0.1 +127.0.0.1; 1.0.0.1 +2021-10-01; 2021-10-02 +2021-10-01; 2021-10-02 hello;world;xyz;def - +1;23;456 +1;23;456 +127.0.0.1; 1.0.0.1 +127.0.0.1; 1.0.0.1 +2021-10-01; 2021-10-02 +2021-10-01; 2021-10-02 diff --git a/tests/queries/0_stateless/00255_array_concat_string.sql b/tests/queries/0_stateless/00255_array_concat_string.sql index 3bdae0821cd..f4f95956a16 100644 --- a/tests/queries/0_stateless/00255_array_concat_string.sql +++ b/tests/queries/0_stateless/00255_array_concat_string.sql @@ -9,8 +9,18 @@ SELECT arrayStringConcat(arrayMap(x -> toString(x), range(number)), ',') FROM sy SELECT arrayStringConcat(arrayMap(x -> transform(x, [0, 1, 2, 3, 4, 5, 6, 7, 8], ['yandex', 'google', 'test', '123', '', 'hello', 'world', 'goodbye', 'xyz'], ''), arrayMap(x -> x % 9, range(number))), ' ') FROM system.numbers LIMIT 20; SELECT arrayStringConcat(arrayMap(x -> toString(x), range(number % 4))) FROM system.numbers LIMIT 10; SELECT arrayStringConcat([Null, 'hello', Null, 'world', Null, 'xyz', 'def', Null], ';'); -SELECT arrayStringConcat([Null, Null], ';'); SELECT arrayStringConcat([Null::Nullable(String), Null::Nullable(String)], ';'); +SELECT arrayStringConcat(arr, ';') FROM (SELECT [1, 23, 456] AS arr); +SELECT arrayStringConcat(arr, ';') FROM (SELECT [Null, 1, Null, 23, Null, 456, Null] AS arr); +SELECT arrayStringConcat(arr, '; ') FROM (SELECT [toIPv4('127.0.0.1'), toIPv4('1.0.0.1')] AS arr); +SELECT arrayStringConcat(arr, '; ') FROM (SELECT [toIPv4('127.0.0.1'), Null, toIPv4('1.0.0.1')] AS arr); +SELECT arrayStringConcat(arr, '; ') FROM (SELECT [toDate('2021-10-01'), toDate('2021-10-02')] AS arr); +SELECT arrayStringConcat(arr, '; ') FROM (SELECT [toDate('2021-10-01'), Null, toDate('2021-10-02')] AS arr); SELECT arrayStringConcat(materialize([Null, 'hello', Null, 'world', Null, 'xyz', 'def', Null]), ';'); -SELECT arrayStringConcat(materialize([Null, Null]), ';'); SELECT arrayStringConcat(materialize([Null::Nullable(String), Null::Nullable(String)]), ';'); +SELECT arrayStringConcat(arr, ';') FROM (SELECT materialize([1, 23, 456]) AS arr); +SELECT arrayStringConcat(arr, ';') FROM (SELECT materialize([Null, 1, Null, 23, Null, 456, Null]) AS arr); +SELECT arrayStringConcat(arr, '; ') FROM (SELECT materialize([toIPv4('127.0.0.1'), toIPv4('1.0.0.1')]) AS arr); +SELECT arrayStringConcat(arr, '; ') FROM (SELECT materialize([toIPv4('127.0.0.1'), Null, toIPv4('1.0.0.1')]) AS arr); +SELECT arrayStringConcat(arr, '; ') FROM (SELECT materialize([toDate('2021-10-01'), toDate('2021-10-02')]) AS arr); +SELECT arrayStringConcat(arr, '; ') FROM (SELECT materialize([toDate('2021-10-01'), Null, toDate('2021-10-02')]) AS arr);