summaryrefslogtreecommitdiffstats
path: root/contrib/clickhouse/src/Functions/array/FunctionsMapMiscellaneous.cpp
diff options
context:
space:
mode:
authorvitalyisaev <[email protected]>2023-11-14 09:58:56 +0300
committervitalyisaev <[email protected]>2023-11-14 10:20:20 +0300
commitc2b2dfd9827a400a8495e172a56343462e3ceb82 (patch)
treecd4e4f597d01bede4c82dffeb2d780d0a9046bd0 /contrib/clickhouse/src/Functions/array/FunctionsMapMiscellaneous.cpp
parentd4ae8f119e67808cb0cf776ba6e0cf95296f2df7 (diff)
YQ Connector: move tests from yql to ydb (OSS)
Перенос папки с тестами на Коннектор из папки yql в папку ydb (синхронизируется с github).
Diffstat (limited to 'contrib/clickhouse/src/Functions/array/FunctionsMapMiscellaneous.cpp')
-rw-r--r--contrib/clickhouse/src/Functions/array/FunctionsMapMiscellaneous.cpp464
1 files changed, 464 insertions, 0 deletions
diff --git a/contrib/clickhouse/src/Functions/array/FunctionsMapMiscellaneous.cpp b/contrib/clickhouse/src/Functions/array/FunctionsMapMiscellaneous.cpp
new file mode 100644
index 00000000000..157f2fa8a26
--- /dev/null
+++ b/contrib/clickhouse/src/Functions/array/FunctionsMapMiscellaneous.cpp
@@ -0,0 +1,464 @@
+#include <Columns/ColumnArray.h>
+#include <Columns/ColumnFunction.h>
+#include <Columns/ColumnMap.h>
+#include <Columns/ColumnTuple.h>
+#include <Columns/ColumnConst.h>
+
+#include <DataTypes/DataTypeArray.h>
+#include <DataTypes/DataTypeFunction.h>
+#include <DataTypes/DataTypeMap.h>
+#include <DataTypes/DataTypeTuple.h>
+
+#include <Functions/FunctionHelpers.h>
+#include <Functions/like.h>
+#include <Functions/array/arrayConcat.h>
+#include <Functions/array/arrayFilter.h>
+#include <Functions/array/arrayMap.h>
+#include <Functions/array/arraySort.h>
+#include <Functions/array/arrayIndex.h>
+#include <Functions/array/arrayExists.h>
+#include <Functions/array/arrayAll.h>
+#include <Functions/identity.h>
+#include <Functions/FunctionFactory.h>
+
+#include <base/map.h>
+
+namespace DB
+{
+
+namespace ErrorCodes
+{
+ extern const int NUMBER_OF_ARGUMENTS_DOESNT_MATCH;
+ extern const int ILLEGAL_TYPE_OF_ARGUMENT;
+}
+
+/** An adapter that allows to execute array* functions over Map types arguments.
+ * E.g. transform mapConcat to arrayConcat.
+ *
+ * Impl - the implementation of function that is applied
+ * to internal column of Map arguments (e.g. 'arrayConcat').
+ *
+ * Adapter - a struct that determines the way how to extract the internal array columns
+ * from Map arguments and possibly modify other columns.
+*/
+template <typename Impl, typename Adapter, typename Name>
+class FunctionMapToArrayAdapter : public IFunction
+{
+public:
+ static constexpr auto name = Name::name;
+ static FunctionPtr create(ContextPtr) { return std::make_shared<FunctionMapToArrayAdapter>(); }
+ String getName() const override { return name; }
+
+ bool isVariadic() const override { return impl.isVariadic(); }
+ size_t getNumberOfArguments() const override { return impl.getNumberOfArguments(); }
+ bool useDefaultImplementationForConstants() const override { return impl.useDefaultImplementationForConstants(); }
+ bool isSuitableForShortCircuitArgumentsExecution(const DataTypesWithConstInfo &) const override { return false; }
+
+ void getLambdaArgumentTypes(DataTypes & arguments) const override
+ {
+ Adapter::extractNestedTypes(arguments);
+ impl.getLambdaArgumentTypes(arguments);
+ }
+
+ DataTypePtr getReturnTypeImpl(const ColumnsWithTypeAndName & arguments) const override
+ {
+ if (arguments.empty())
+ throw Exception(
+ ErrorCodes::NUMBER_OF_ARGUMENTS_DOESNT_MATCH,
+ "Function {} requires at least one argument, passed {}", getName(), arguments.size());
+
+ auto nested_arguments = arguments;
+ Adapter::extractNestedTypesAndColumns(nested_arguments);
+
+ constexpr bool impl_has_get_return_type = requires
+ {
+ impl.getReturnTypeImpl(nested_arguments);
+ };
+
+ /// If method is not overloaded in the implementation call default implementation
+ /// from IFunction. Here inheritance cannot be used for template parameterized field.
+ if constexpr (impl_has_get_return_type)
+ return Adapter::wrapType(impl.getReturnTypeImpl(nested_arguments));
+ else
+ return Adapter::wrapType(dynamic_cast<const IFunction &>(impl).getReturnTypeImpl(nested_arguments));
+ }
+
+ ColumnPtr executeImpl(const ColumnsWithTypeAndName & arguments, const DataTypePtr & result_type, size_t input_rows_count) const override
+ {
+ auto nested_arguments = arguments;
+ Adapter::extractNestedTypesAndColumns(nested_arguments);
+ return Adapter::wrapColumn(impl.executeImpl(nested_arguments, Adapter::extractResultType(result_type), input_rows_count));
+ }
+
+private:
+ Impl impl;
+};
+
+
+template <typename Derived, typename Name>
+struct MapAdapterBase
+{
+ static void extractNestedTypes(DataTypes & types)
+ {
+ bool has_map_column = false;
+ for (auto & type : types)
+ {
+ if (const auto * type_map = typeid_cast<const DataTypeMap *>(type.get()))
+ {
+ has_map_column = true;
+ type = Derived::extractNestedType(*type_map);
+ }
+ }
+
+ if (!has_map_column)
+ throw Exception(ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT,
+ "Function {} requires at least one argument of type Map", Name::name);
+ }
+
+ static void extractNestedTypesAndColumns(ColumnsWithTypeAndName & arguments)
+ {
+ bool has_map_column = false;
+ for (auto & argument : arguments)
+ {
+ if (const auto * type_map = typeid_cast<const DataTypeMap *>(argument.type.get()))
+ {
+ has_map_column = true;
+ argument.type = Derived::extractNestedType(*type_map);
+
+ if (argument.column)
+ {
+ if (const auto * const_map = checkAndGetColumnConstData<ColumnMap>(argument.column.get()))
+ argument.column = ColumnConst::create(Derived::extractNestedColumn(*const_map), argument.column->size());
+ else
+ argument.column = Derived::extractNestedColumn(assert_cast<const ColumnMap &>(*argument.column));
+ }
+ }
+ }
+
+ if (!has_map_column)
+ throw Exception(ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT,
+ "Function {} requires at least one argument of type Map", Name::name);
+ }
+};
+
+/// Adapter that extracts nested Array(Tuple(key, value)) from Map columns.
+template <typename Name, bool returns_map = true>
+struct MapToNestedAdapter : public MapAdapterBase<MapToNestedAdapter<Name, returns_map>, Name>
+{
+ using MapAdapterBase<MapToNestedAdapter, Name>::extractNestedTypes;
+ using MapAdapterBase<MapToNestedAdapter, Name>::extractNestedTypesAndColumns;
+
+ static DataTypePtr extractNestedType(const DataTypeMap & type_map)
+ {
+ return type_map.getNestedTypeWithUnnamedTuple();
+ }
+
+ static ColumnPtr extractNestedColumn(const ColumnMap & column_map)
+ {
+ return column_map.getNestedColumnPtr();
+ }
+
+ static DataTypePtr extractResultType(const DataTypePtr & result_type)
+ {
+ if constexpr (returns_map)
+ return assert_cast<const DataTypeMap &>(*result_type).getNestedType();
+ return result_type;
+ }
+
+ static DataTypePtr wrapType(DataTypePtr type)
+ {
+ if constexpr (returns_map)
+ return std::make_shared<DataTypeMap>(std::move(type));
+ return type;
+ }
+
+ static ColumnPtr wrapColumn(ColumnPtr column)
+ {
+ if constexpr (returns_map)
+ return ColumnMap::create(std::move(column));
+ return column;
+ }
+};
+
+/// Adapter that extracts array with keys or values from Map columns.
+template <typename Name, size_t position>
+struct MapToSubcolumnAdapter : public MapAdapterBase<MapToSubcolumnAdapter<Name, position>, Name>
+{
+ static_assert(position <= 1);
+ using MapAdapterBase<MapToSubcolumnAdapter, Name>::extractNestedTypes;
+ using MapAdapterBase<MapToSubcolumnAdapter, Name>::extractNestedTypesAndColumns;
+
+ static DataTypePtr extractNestedType(const DataTypeMap & type_map)
+ {
+ const auto & array_type = assert_cast<const DataTypeArray &>(*type_map.getNestedType());
+ const auto & tuple_type = assert_cast<const DataTypeTuple &>(*array_type.getNestedType());
+ return std::make_shared<DataTypeArray>(tuple_type.getElement(position));
+ }
+
+ static ColumnPtr extractNestedColumn(const ColumnMap & column_map)
+ {
+ const auto & array_column = column_map.getNestedColumn();
+ const auto & tuple_column = column_map.getNestedData();
+ return ColumnArray::create(tuple_column.getColumnPtr(position), array_column.getOffsetsPtr());
+ }
+
+ static DataTypePtr extractResultType(const DataTypePtr & result_type) { return result_type; }
+ static DataTypePtr wrapType(DataTypePtr type) { return type; }
+ static ColumnPtr wrapColumn(ColumnPtr column) { return column; }
+};
+
+/// A special function that works like the following:
+/// mapKeyLike(pattern, key, value) <=> key LIKE pattern
+/// It is used to mimic lambda: (key, value) -> key LIKE pattern.
+class FunctionMapKeyLike : public IFunction
+{
+public:
+ String getName() const override { return "mapKeyLike"; }
+ size_t getNumberOfArguments() const override { return 3; }
+ bool isSuitableForShortCircuitArgumentsExecution(const DataTypesWithConstInfo & /*arguments*/) const override { return false; }
+ bool useDefaultImplementationForNulls() const override { return false; }
+
+ DataTypePtr getReturnTypeImpl(const DataTypes & arguments) const override
+ {
+ DataTypes new_arguments{arguments[1], arguments[0]};
+ return impl.getReturnTypeImpl(new_arguments);
+ }
+
+ ColumnPtr executeImpl(const ColumnsWithTypeAndName & arguments, const DataTypePtr & result_type, size_t input_rows_count) const override
+ {
+ ColumnsWithTypeAndName new_arguments{arguments[1], arguments[0]};
+ return impl.executeImpl(new_arguments, result_type, input_rows_count);
+ }
+
+private:
+ FunctionLike impl;
+};
+
+/// Adapter for map*KeyLike functions.
+/// It extracts nested Array(Tuple(key, value)) from Map columns
+/// and prepares ColumnFunction as first argument which works
+/// like lambda (k, v) -> k LIKE pattern to pass it to the nested
+/// function derived from FunctionArrayMapped.
+template <typename Name, bool returns_map>
+struct MapKeyLikeAdapter
+{
+ static void checkTypes(const DataTypes & types)
+ {
+ if (types.size() != 2)
+ throw Exception(ErrorCodes::NUMBER_OF_ARGUMENTS_DOESNT_MATCH,
+ "Number of arguments for function {} doesn't match: passed {}, should be 2",
+ Name::name, types.size());
+
+ const auto * map_type = checkAndGetDataType<DataTypeMap>(types[0].get());
+ if (!map_type)
+ throw Exception(ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT, "First argument for function {} must be a Map", Name::name);
+
+ if (!isStringOrFixedString(types[1]))
+ throw Exception(ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT, "Second argument for function {} must be String or FixedString", Name::name);
+
+ if (!isStringOrFixedString(map_type->getKeyType()))
+ throw Exception(ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT, "Key type of map for function {} must be String or FixedString", Name::name);
+ }
+
+ static void extractNestedTypes(DataTypes & types)
+ {
+ checkTypes(types);
+ const auto & map_type = assert_cast<const DataTypeMap &>(*types[0]);
+
+ DataTypes lambda_argument_types{types[1], map_type.getKeyType(), map_type.getValueType()};
+ auto result_type = FunctionMapKeyLike().getReturnTypeImpl(lambda_argument_types);
+
+ DataTypes argument_types{map_type.getKeyType(), map_type.getValueType()};
+ auto function_type = std::make_shared<DataTypeFunction>(argument_types, result_type);
+
+ types = {function_type, types[0]};
+ MapToNestedAdapter<Name, returns_map>::extractNestedTypes(types);
+ }
+
+ static void extractNestedTypesAndColumns(ColumnsWithTypeAndName & arguments)
+ {
+ checkTypes(collections::map<DataTypes>(arguments, [](const auto & elem) { return elem.type; }));
+
+ const auto & map_type = assert_cast<const DataTypeMap &>(*arguments[0].type);
+ const auto & pattern_arg = arguments[1];
+
+ ColumnPtr function_column;
+ auto function = std::make_shared<FunctionMapKeyLike>();
+
+ DataTypes lambda_argument_types{pattern_arg.type, map_type.getKeyType(), map_type.getValueType()};
+ auto result_type = function->getReturnTypeImpl(lambda_argument_types);
+
+ DataTypes argument_types{map_type.getKeyType(), map_type.getValueType()};
+ auto function_type = std::make_shared<DataTypeFunction>(argument_types, result_type);
+
+ if (pattern_arg.column)
+ {
+ /// Here we create ColumnFunction with already captured pattern column.
+ /// Nested function will append keys and values column and it will work as desired lambda.
+ auto function_base = std::make_shared<FunctionToFunctionBaseAdaptor>(function, lambda_argument_types, result_type);
+ function_column = ColumnFunction::create(pattern_arg.column->size(), std::move(function_base), ColumnsWithTypeAndName{pattern_arg});
+ }
+
+ ColumnWithTypeAndName function_arg{function_column, function_type, "__function_map_key_like"};
+ arguments = {function_arg, arguments[0]};
+ MapToNestedAdapter<Name, returns_map>::extractNestedTypesAndColumns(arguments);
+ }
+
+ static DataTypePtr extractResultType(const DataTypePtr & result_type)
+ {
+ return MapToNestedAdapter<Name, returns_map>::extractResultType(result_type);
+ }
+
+ static DataTypePtr wrapType(DataTypePtr type)
+ {
+ return MapToNestedAdapter<Name, returns_map>::wrapType(std::move(type));
+ }
+
+ static ColumnPtr wrapColumn(ColumnPtr column)
+ {
+ return MapToNestedAdapter<Name, returns_map>::wrapColumn(std::move(column));
+ }
+};
+
+struct NameMapConcat { static constexpr auto name = "mapConcat"; };
+using FunctionMapConcat = FunctionMapToArrayAdapter<FunctionArrayConcat, MapToNestedAdapter<NameMapConcat>, NameMapConcat>;
+
+struct NameMapKeys { static constexpr auto name = "mapKeys"; };
+using FunctionMapKeys = FunctionMapToArrayAdapter<FunctionIdentity, MapToSubcolumnAdapter<NameMapKeys, 0>, NameMapKeys>;
+
+struct NameMapValues { static constexpr auto name = "mapValues"; };
+using FunctionMapValues = FunctionMapToArrayAdapter<FunctionIdentity, MapToSubcolumnAdapter<NameMapValues, 1>, NameMapValues>;
+
+struct NameMapContains { static constexpr auto name = "mapContains"; };
+using FunctionMapContains = FunctionMapToArrayAdapter<FunctionArrayIndex<HasAction, NameMapContains>, MapToSubcolumnAdapter<NameMapKeys, 0>, NameMapContains>;
+
+struct NameMapFilter { static constexpr auto name = "mapFilter"; };
+using FunctionMapFilter = FunctionMapToArrayAdapter<FunctionArrayFilter, MapToNestedAdapter<NameMapFilter>, NameMapFilter>;
+
+struct NameMapApply { static constexpr auto name = "mapApply"; };
+using FunctionMapApply = FunctionMapToArrayAdapter<FunctionArrayMap, MapToNestedAdapter<NameMapApply>, NameMapApply>;
+
+struct NameMapExists { static constexpr auto name = "mapExists"; };
+using FunctionMapExists = FunctionMapToArrayAdapter<FunctionArrayExists, MapToNestedAdapter<NameMapExists, false>, NameMapExists>;
+
+struct NameMapAll { static constexpr auto name = "mapAll"; };
+using FunctionMapAll = FunctionMapToArrayAdapter<FunctionArrayAll, MapToNestedAdapter<NameMapAll, false>, NameMapAll>;
+
+struct NameMapContainsKeyLike { static constexpr auto name = "mapContainsKeyLike"; };
+using FunctionMapContainsKeyLike = FunctionMapToArrayAdapter<FunctionArrayExists, MapKeyLikeAdapter<NameMapContainsKeyLike, false>, NameMapContainsKeyLike>;
+
+struct NameMapExtractKeyLike { static constexpr auto name = "mapExtractKeyLike"; };
+using FunctionMapExtractKeyLike = FunctionMapToArrayAdapter<FunctionArrayFilter, MapKeyLikeAdapter<NameMapExtractKeyLike, true>, NameMapExtractKeyLike>;
+
+struct NameMapSort { static constexpr auto name = "mapSort"; };
+struct NameMapReverseSort { static constexpr auto name = "mapReverseSort"; };
+struct NameMapPartialSort { static constexpr auto name = "mapPartialSort"; };
+struct NameMapPartialReverseSort { static constexpr auto name = "mapPartialReverseSort"; };
+
+using FunctionMapSort = FunctionMapToArrayAdapter<FunctionArraySort, MapToNestedAdapter<NameMapSort>, NameMapSort>;
+using FunctionMapReverseSort = FunctionMapToArrayAdapter<FunctionArrayReverseSort, MapToNestedAdapter<NameMapReverseSort>, NameMapReverseSort>;
+using FunctionMapPartialSort = FunctionMapToArrayAdapter<FunctionArrayPartialSort, MapToNestedAdapter<NameMapPartialSort>, NameMapPartialSort>;
+using FunctionMapPartialReverseSort = FunctionMapToArrayAdapter<FunctionArrayPartialReverseSort, MapToNestedAdapter<NameMapPartialReverseSort>, NameMapPartialReverseSort>;
+
+REGISTER_FUNCTION(MapMiscellaneous)
+{
+ factory.registerFunction<FunctionMapConcat>(
+ FunctionDocumentation{
+ .description="The same as arrayConcat.",
+ .examples{{"mapConcat", "SELECT mapConcat(map('k1', 'v1'), map('k2', 'v2'))", ""}},
+ .categories{"Map"},
+ });
+
+ factory.registerFunction<FunctionMapKeys>(
+ FunctionDocumentation{
+ .description="Returns an array with the keys of map.",
+ .examples{{"mapKeys", "SELECT mapKeys(map('k1', 'v1', 'k2', 'v2'))", ""}},
+ .categories{"Map"},
+ });
+
+ factory.registerFunction<FunctionMapValues>(
+ FunctionDocumentation{
+ .description="Returns an array with the values of map.",
+ .examples{{"mapValues", "SELECT mapValues(map('k1', 'v1', 'k2', 'v2'))", ""}},
+ .categories{"Map"},
+ });
+
+ factory.registerFunction<FunctionMapContains>(
+ FunctionDocumentation{
+ .description="Checks whether the map has the specified key.",
+ .examples{{"mapContains", "SELECT mapContains(map('k1', 'v1', 'k2', 'v2'), 'k1')", ""}},
+ .categories{"Map"},
+ });
+
+ factory.registerFunction<FunctionMapFilter>(
+ FunctionDocumentation{
+ .description="The same as arrayFilter.",
+ .examples{{"mapFilter", "SELECT mapFilter((k, v) -> v > 1, map('k1', 1, 'k2', 2))", ""}},
+ .categories{"Map"},
+ });
+
+ factory.registerFunction<FunctionMapApply>(
+ FunctionDocumentation{
+ .description="The same as arrayMap.",
+ .examples{{"mapApply", "SELECT mapApply((k, v) -> (k, v * 2), map('k1', 1, 'k2', 2))", ""}},
+ .categories{"Map"},
+ });
+
+ factory.registerFunction<FunctionMapExists>(
+ FunctionDocumentation{
+ .description="The same as arrayExists.",
+ .examples{{"mapExists", "SELECT mapExists((k, v) -> v = 1, map('k1', 1, 'k2', 2))", ""}},
+ .categories{"Map"},
+ });
+
+ factory.registerFunction<FunctionMapAll>(
+ FunctionDocumentation{
+ .description="The same as arrayAll.",
+ .examples{{"mapAll", "SELECT mapAll((k, v) -> v = 1, map('k1', 1, 'k2', 2))", ""}},
+ .categories{"Map"},
+ });
+
+ factory.registerFunction<FunctionMapSort>(
+ FunctionDocumentation{
+ .description="The same as arraySort.",
+ .examples{{"mapSort", "SELECT mapSort((k, v) -> v, map('k1', 3, 'k2', 1, 'k3', 2))", ""}},
+ .categories{"Map"},
+ });
+
+ factory.registerFunction<FunctionMapReverseSort>(
+ FunctionDocumentation{
+ .description="The same as arrayReverseSort.",
+ .examples{{"mapReverseSort", "SELECT mapReverseSort((k, v) -> v, map('k1', 3, 'k2', 1, 'k3', 2))", ""}},
+ .categories{"Map"},
+ });
+
+ factory.registerFunction<FunctionMapPartialSort>(
+ FunctionDocumentation{
+ .description="The same as arrayReverseSort.",
+ .examples{{"mapPartialSort", "SELECT mapPartialSort((k, v) -> v, 2, map('k1', 3, 'k2', 1, 'k3', 2))", ""}},
+ .categories{"Map"},
+ });
+
+ factory.registerFunction<FunctionMapPartialReverseSort>(
+ FunctionDocumentation{
+ .description="The same as arrayPartialReverseSort.",
+ .examples{{"mapPartialReverseSort", "SELECT mapPartialReverseSort((k, v) -> v, 2, map('k1', 3, 'k2', 1, 'k3', 2))", ""}},
+ .categories{"Map"},
+ });
+
+ factory.registerFunction<FunctionMapContainsKeyLike>(
+ FunctionDocumentation{
+ .description="Checks whether map contains key LIKE specified pattern.",
+ .examples{{"mapContainsKeyLike", "SELECT mapContainsKeyLike(map('k1-1', 1, 'k2-1', 2), 'k1%')", ""}},
+ .categories{"Map"},
+ });
+
+ factory.registerFunction<FunctionMapExtractKeyLike>(
+ FunctionDocumentation{
+ .description="Returns a map with elements which key matches the specified pattern.",
+ .examples{{"mapExtractKeyLike", "SELECT mapExtractKeyLike(map('k1-1', 1, 'k2-1', 2), 'k1%')", ""}},
+ .categories{"Map"},
+ });
+}
+
+}