diff --git a/docs/en/sql-reference/functions/tuple-map-functions.md b/docs/en/sql-reference/functions/tuple-map-functions.md index 1905e53af3e8..9885ac9d76ea 100644 --- a/docs/en/sql-reference/functions/tuple-map-functions.md +++ b/docs/en/sql-reference/functions/tuple-map-functions.md @@ -66,6 +66,46 @@ Result: - [Map(key, value)](../../sql-reference/data-types/map.md) data type +## mapFromArrays + +Merges an [Array](../../sql-reference/data-types/array.md) of keys and an [Array](../../sql-reference/data-types/array.md) of values into a [Map(key, value)](../../sql-reference/data-types/map.md). Notice that the second argument could also be a [Map](../../sql-reference/data-types/map.md), thus it is casted to an Array when executing. + +The function is a more convenient alternative to `CAST((key_array, value_array_or_map), 'Map(key_type, value_type)')`. For example, instead of writing `CAST((['aa', 'bb'], [4, 5]), 'Map(String, UInt32)')`, you can write `mapFromArrays(['aa', 'bb'], [4, 5])`. + +**Syntax** + +```sql +mapFromArrays(keys, values) +``` + +Alias: `MAP_FROM_ARRAYS(keys, values)` + +**Parameters** +- `keys` — Given key array to create a map from. The nested type of array must be: [String](../../sql-reference/data-types/string.md), [Integer](../../sql-reference/data-types/int-uint.md), [LowCardinality](../../sql-reference/data-types/lowcardinality.md), [FixedString](../../sql-reference/data-types/fixedstring.md), [UUID](../../sql-reference/data-types/uuid.md), [Date](../../sql-reference/data-types/date.md), [DateTime](../../sql-reference/data-types/datetime.md), [Date32](../../sql-reference/data-types/date32.md), [Enum](../../sql-reference/data-types/enum.md) +- `values` - Given value array or map to create a map from. + +**Returned value** + +- A map whose keys and values are constructed from the key array and value array/map. + +**Example** + +Query: + +```sql +select mapFromArrays(['a', 'b', 'c'], [1, 2, 3]) + +┌─mapFromArrays(['a', 'b', 'c'], [1, 2, 3])─┐ +│ {'a':1,'b':2,'c':3} │ +└───────────────────────────────────────────┘ + +SELECT mapFromArrays([1, 2, 3], map('a', 1, 'b', 2, 'c', 3)) + +┌─mapFromArrays([1, 2, 3], map('a', 1, 'b', 2, 'c', 3))─┐ +│ {1:('a',1),2:('b',2),3:('c',3)} │ +└───────────────────────────────────────────────────────┘ +``` + ## mapAdd Collect all the keys and sum corresponding values. @@ -429,6 +469,8 @@ Result: │ {} │ └────────────────────────────┘ ``` + + ## mapApply diff --git a/src/AggregateFunctions/AggregateFunctionArray.h b/src/AggregateFunctions/AggregateFunctionArray.h index d1494f46f4bc..21394e3ce055 100644 --- a/src/AggregateFunctions/AggregateFunctionArray.h +++ b/src/AggregateFunctions/AggregateFunctionArray.h @@ -13,7 +13,7 @@ struct Settings; namespace ErrorCodes { - extern const int SIZES_OF_ARRAYS_DOESNT_MATCH; + extern const int SIZES_OF_ARRAYS_DONT_MATCH; extern const int ILLEGAL_TYPE_OF_ARGUMENT; } @@ -129,7 +129,7 @@ class AggregateFunctionArray final : public IAggregateFunctionHelper(*elem.column); if (!first_array_column.hasEqualOffsets(another_array_column)) - throw Exception(ErrorCodes::SIZES_OF_ARRAYS_DOESNT_MATCH, + throw Exception(ErrorCodes::SIZES_OF_ARRAYS_DONT_MATCH, "Elements '{}' and '{}' " "of Nested data structure '{}' (Array columns) have different array sizes.", block.getByPosition(it->second).name, elem.name, split.first); diff --git a/src/Functions/FunctionHelpers.cpp b/src/Functions/FunctionHelpers.cpp index 791b2c1bbdb7..0721c52cdadc 100644 --- a/src/Functions/FunctionHelpers.cpp +++ b/src/Functions/FunctionHelpers.cpp @@ -16,7 +16,7 @@ namespace ErrorCodes { extern const int ILLEGAL_COLUMN; extern const int NUMBER_OF_ARGUMENTS_DOESNT_MATCH; - extern const int SIZES_OF_ARRAYS_DOESNT_MATCH; + extern const int SIZES_OF_ARRAYS_DONT_MATCH; extern const int ILLEGAL_TYPE_OF_ARGUMENT; } @@ -213,7 +213,7 @@ checkAndGetNestedArrayOffset(const IColumn ** columns, size_t num_arguments) if (i == 0) offsets = offsets_i; else if (*offsets_i != *offsets) - throw Exception(ErrorCodes::SIZES_OF_ARRAYS_DOESNT_MATCH, "Lengths of all arrays passed to aggregate function must be equal."); + throw Exception(ErrorCodes::SIZES_OF_ARRAYS_DONT_MATCH, "Lengths of all arrays passed to aggregate function must be equal."); } return {nested_columns, offsets->data()}; } diff --git a/src/Functions/array/FunctionArrayMapped.h b/src/Functions/array/FunctionArrayMapped.h index 89599edd9d10..ccfc7869036e 100644 --- a/src/Functions/array/FunctionArrayMapped.h +++ b/src/Functions/array/FunctionArrayMapped.h @@ -36,7 +36,7 @@ namespace ErrorCodes extern const int ILLEGAL_COLUMN; extern const int ILLEGAL_TYPE_OF_ARGUMENT; extern const int LOGICAL_ERROR; - extern const int SIZES_OF_ARRAYS_DOESNT_MATCH; + extern const int SIZES_OF_ARRAYS_DONT_MATCH; extern const int NUMBER_OF_ARGUMENTS_DOESNT_MATCH; } @@ -308,7 +308,7 @@ class FunctionArrayMapped : public IFunction if (getOffsetsPtr(*column_array) != offsets_column && getOffsets(*column_array) != typeid_cast(*offsets_column).getData()) throw Exception( - ErrorCodes::SIZES_OF_ARRAYS_DOESNT_MATCH, + ErrorCodes::SIZES_OF_ARRAYS_DONT_MATCH, "{}s passed to {} must have equal size", argument_type_name, getName()); diff --git a/src/Functions/array/arrayDistance.cpp b/src/Functions/array/arrayDistance.cpp index c1137848cc50..c68c89ee0d59 100644 --- a/src/Functions/array/arrayDistance.cpp +++ b/src/Functions/array/arrayDistance.cpp @@ -16,7 +16,7 @@ namespace ErrorCodes extern const int ILLEGAL_COLUMN; extern const int ILLEGAL_TYPE_OF_ARGUMENT; extern const int LOGICAL_ERROR; - extern const int SIZES_OF_ARRAYS_DOESNT_MATCH; + extern const int SIZES_OF_ARRAYS_DONT_MATCH; extern const int ARGUMENT_OUT_OF_BOUND; } @@ -356,7 +356,7 @@ class FunctionArrayDistance : public IFunction { ColumnArray::Offset prev_offset = row > 0 ? offsets_x[row] : 0; throw Exception( - ErrorCodes::SIZES_OF_ARRAYS_DOESNT_MATCH, + ErrorCodes::SIZES_OF_ARRAYS_DONT_MATCH, "Arguments of function {} have different array sizes: {} and {}", getName(), offsets_x[row] - prev_offset, @@ -423,7 +423,7 @@ class FunctionArrayDistance : public IFunction if (unlikely(offsets_x[0] != offsets_y[row] - prev_offset)) { throw Exception( - ErrorCodes::SIZES_OF_ARRAYS_DOESNT_MATCH, + ErrorCodes::SIZES_OF_ARRAYS_DONT_MATCH, "Arguments of function {} have different array sizes: {} and {}", getName(), offsets_x[0], diff --git a/src/Functions/array/arrayEnumerateExtended.h b/src/Functions/array/arrayEnumerateExtended.h index c3d69bb6972a..3f145c05b54c 100644 --- a/src/Functions/array/arrayEnumerateExtended.h +++ b/src/Functions/array/arrayEnumerateExtended.h @@ -20,7 +20,7 @@ namespace ErrorCodes extern const int NUMBER_OF_ARGUMENTS_DOESNT_MATCH; extern const int ILLEGAL_COLUMN; extern const int ILLEGAL_TYPE_OF_ARGUMENT; - extern const int SIZES_OF_ARRAYS_DOESNT_MATCH; + extern const int SIZES_OF_ARRAYS_DONT_MATCH; } class FunctionArrayEnumerateUniq; @@ -153,7 +153,7 @@ ColumnPtr FunctionArrayEnumerateExtended::executeImpl(const ColumnsWith offsets_column = array->getOffsetsPtr(); } else if (offsets_i != *offsets) - throw Exception(ErrorCodes::SIZES_OF_ARRAYS_DOESNT_MATCH, "Lengths of all arrays passed to {} must be equal.", + throw Exception(ErrorCodes::SIZES_OF_ARRAYS_DONT_MATCH, "Lengths of all arrays passed to {} must be equal.", getName()); const auto * array_data = &array->getData(); diff --git a/src/Functions/array/arrayEnumerateRanked.h b/src/Functions/array/arrayEnumerateRanked.h index 73feb3e46ea0..8a348c07421e 100644 --- a/src/Functions/array/arrayEnumerateRanked.h +++ b/src/Functions/array/arrayEnumerateRanked.h @@ -60,7 +60,7 @@ namespace DB namespace ErrorCodes { extern const int NUMBER_OF_ARGUMENTS_DOESNT_MATCH; - extern const int SIZES_OF_ARRAYS_DOESNT_MATCH; + extern const int SIZES_OF_ARRAYS_DONT_MATCH; } class FunctionArrayEnumerateUniqRanked; @@ -194,7 +194,7 @@ ColumnPtr FunctionArrayEnumerateRankedExtended::executeImpl( { if (*offsets_by_depth[0] != array->getOffsets()) { - throw Exception(ErrorCodes::SIZES_OF_ARRAYS_DOESNT_MATCH, + throw Exception(ErrorCodes::SIZES_OF_ARRAYS_DONT_MATCH, "Lengths and effective depths of all arrays passed to {} must be equal.", getName()); } } @@ -217,7 +217,7 @@ ColumnPtr FunctionArrayEnumerateRankedExtended::executeImpl( { if (*offsets_by_depth[col_depth] != array->getOffsets()) { - throw Exception(ErrorCodes::SIZES_OF_ARRAYS_DOESNT_MATCH, + throw Exception(ErrorCodes::SIZES_OF_ARRAYS_DONT_MATCH, "Lengths and effective depths of all arrays passed to {} must be equal.", getName()); } } @@ -225,7 +225,7 @@ ColumnPtr FunctionArrayEnumerateRankedExtended::executeImpl( if (col_depth < arrays_depths.depths[array_num]) { - throw Exception(ErrorCodes::SIZES_OF_ARRAYS_DOESNT_MATCH, + throw Exception(ErrorCodes::SIZES_OF_ARRAYS_DONT_MATCH, "{}: Passed array number {} depth ({}) is more than the actual array depth ({}).", getName(), array_num, std::to_string(arrays_depths.depths[array_num]), col_depth); } diff --git a/src/Functions/array/arrayReduce.cpp b/src/Functions/array/arrayReduce.cpp index d4896595941c..a4b2cc037ab1 100644 --- a/src/Functions/array/arrayReduce.cpp +++ b/src/Functions/array/arrayReduce.cpp @@ -19,7 +19,7 @@ namespace DB namespace ErrorCodes { - extern const int SIZES_OF_ARRAYS_DOESNT_MATCH; + extern const int SIZES_OF_ARRAYS_DONT_MATCH; extern const int NUMBER_OF_ARGUMENTS_DOESNT_MATCH; extern const int ILLEGAL_COLUMN; extern const int ILLEGAL_TYPE_OF_ARGUMENT; @@ -144,7 +144,7 @@ ColumnPtr FunctionArrayReduce::executeImpl(const ColumnsWithTypeAndName & argume if (i == 0) offsets = offsets_i; else if (*offsets_i != *offsets) - throw Exception(ErrorCodes::SIZES_OF_ARRAYS_DOESNT_MATCH, "Lengths of all arrays passed to {} must be equal.", + throw Exception(ErrorCodes::SIZES_OF_ARRAYS_DONT_MATCH, "Lengths of all arrays passed to {} must be equal.", getName()); } const IColumn ** aggregate_arguments = aggregate_arguments_vec.data(); diff --git a/src/Functions/array/arrayReduceInRanges.cpp b/src/Functions/array/arrayReduceInRanges.cpp index 07391c963a6a..790bc3ef8798 100644 --- a/src/Functions/array/arrayReduceInRanges.cpp +++ b/src/Functions/array/arrayReduceInRanges.cpp @@ -21,7 +21,7 @@ namespace DB namespace ErrorCodes { - extern const int SIZES_OF_ARRAYS_DOESNT_MATCH; + extern const int SIZES_OF_ARRAYS_DONT_MATCH; extern const int NUMBER_OF_ARGUMENTS_DOESNT_MATCH; extern const int ILLEGAL_COLUMN; extern const int ILLEGAL_TYPE_OF_ARGUMENT; @@ -190,7 +190,7 @@ ColumnPtr FunctionArrayReduceInRanges::executeImpl( if (i == 0) offsets = offsets_i; else if (*offsets_i != *offsets) - throw Exception(ErrorCodes::SIZES_OF_ARRAYS_DOESNT_MATCH, "Lengths of all arrays passed to {} must be equal.", + throw Exception(ErrorCodes::SIZES_OF_ARRAYS_DONT_MATCH, "Lengths of all arrays passed to {} must be equal.", getName()); } const IColumn ** aggregate_arguments = aggregate_arguments_vec.data(); diff --git a/src/Functions/array/arrayUniq.cpp b/src/Functions/array/arrayUniq.cpp index 1d1cf4e6392f..81ba5b620943 100644 --- a/src/Functions/array/arrayUniq.cpp +++ b/src/Functions/array/arrayUniq.cpp @@ -18,7 +18,7 @@ namespace DB namespace ErrorCodes { - extern const int SIZES_OF_ARRAYS_DOESNT_MATCH; + extern const int SIZES_OF_ARRAYS_DONT_MATCH; extern const int NUMBER_OF_ARGUMENTS_DOESNT_MATCH; extern const int ILLEGAL_COLUMN; extern const int ILLEGAL_TYPE_OF_ARGUMENT; @@ -151,7 +151,7 @@ ColumnPtr FunctionArrayUniq::executeImpl(const ColumnsWithTypeAndName & argument if (i == 0) offsets = &offsets_i; else if (offsets_i != *offsets) - throw Exception(ErrorCodes::SIZES_OF_ARRAYS_DOESNT_MATCH, "Lengths of all arrays passed to {} must be equal.", + throw Exception(ErrorCodes::SIZES_OF_ARRAYS_DONT_MATCH, "Lengths of all arrays passed to {} must be equal.", getName()); const auto * array_data = &array->getData(); diff --git a/src/Functions/array/arrayZip.cpp b/src/Functions/array/arrayZip.cpp index 3a50491fd4b2..44c323e3fe31 100644 --- a/src/Functions/array/arrayZip.cpp +++ b/src/Functions/array/arrayZip.cpp @@ -13,7 +13,7 @@ namespace DB namespace ErrorCodes { extern const int ILLEGAL_TYPE_OF_ARGUMENT; - extern const int SIZES_OF_ARRAYS_DOESNT_MATCH; + extern const int SIZES_OF_ARRAYS_DONT_MATCH; extern const int NUMBER_OF_ARGUMENTS_DOESNT_MATCH; extern const int ILLEGAL_COLUMN; } @@ -81,7 +81,7 @@ class FunctionArrayZip : public IFunction } else if (!column_array->hasEqualOffsets(static_cast(*first_array_column))) { - throw Exception(ErrorCodes::SIZES_OF_ARRAYS_DOESNT_MATCH, + throw Exception(ErrorCodes::SIZES_OF_ARRAYS_DONT_MATCH, "The argument 1 and argument {} of function {} have different array sizes", i + 1, getName()); } diff --git a/src/Functions/map.cpp b/src/Functions/map.cpp index 3160c5ddb437..4217550d5b03 100644 --- a/src/Functions/map.cpp +++ b/src/Functions/map.cpp @@ -26,6 +26,8 @@ namespace ErrorCodes { extern const int NUMBER_OF_ARGUMENTS_DOESNT_MATCH; extern const int ILLEGAL_TYPE_OF_ARGUMENT; + extern const int SIZES_OF_ARRAYS_DONT_MATCH; + extern const int ILLEGAL_COLUMN; } namespace @@ -147,6 +149,94 @@ class FunctionMap : public IFunction } }; +/// mapFromArrays(keys, values) is a function that allows you to make key-value pair from a pair of arrays +class FunctionMapFromArrays : public IFunction +{ +public: + static constexpr auto name = "mapFromArrays"; + + static FunctionPtr create(ContextPtr) { return std::make_shared(); } + String getName() const override { return name; } + + size_t getNumberOfArguments() const override { return 2; } + + bool isSuitableForShortCircuitArgumentsExecution(const DataTypesWithConstInfo & /*arguments*/) const override { return false; } + bool useDefaultImplementationForNulls() const override { return true; } + bool useDefaultImplementationForConstants() const override { return true; } + + DataTypePtr getReturnTypeImpl(const DataTypes & arguments) const override + { + if (arguments.size() != 2) + throw Exception( + ErrorCodes::NUMBER_OF_ARGUMENTS_DOESNT_MATCH, + "Function {} requires 2 arguments, but {} given", + getName(), + arguments.size()); + + /// The first argument should always be Array. + /// Because key type can not be nested type of Map, which is Tuple + DataTypePtr key_type; + if (const auto * keys_type = checkAndGetDataType(arguments[0].get())) + key_type = keys_type->getNestedType(); + else + throw Exception(ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT, "First argument for function {} must be an Array", getName()); + + DataTypePtr value_type; + if (const auto * value_array_type = checkAndGetDataType(arguments[1].get())) + value_type = value_array_type->getNestedType(); + else if (const auto * value_map_type = checkAndGetDataType(arguments[1].get())) + value_type = std::make_shared(value_map_type->getKeyValueTypes()); + else + throw Exception(ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT, "Second argument for function {} must be Array or Map", getName()); + + DataTypes key_value_types{key_type, value_type}; + return std::make_shared(key_value_types); + } + + ColumnPtr executeImpl( + const ColumnsWithTypeAndName & arguments, const DataTypePtr & /* result_type */, size_t /* input_rows_count */) const override + { + bool is_keys_const = isColumnConst(*arguments[0].column); + ColumnPtr holder_keys; + const ColumnArray * col_keys; + if (is_keys_const) + { + holder_keys = arguments[0].column->convertToFullColumnIfConst(); + col_keys = checkAndGetColumn(holder_keys.get()); + } + else + { + col_keys = checkAndGetColumn(arguments[0].column.get()); + } + + if (!col_keys) + throw Exception(ErrorCodes::ILLEGAL_COLUMN, "The first argument of function {} must be Array", getName()); + + bool is_values_const = isColumnConst(*arguments[1].column); + ColumnPtr holder_values; + if (is_values_const) + holder_values = arguments[1].column->convertToFullColumnIfConst(); + else + holder_values = arguments[1].column; + + const ColumnArray * col_values; + if (const auto * col_values_array = checkAndGetColumn(holder_values.get())) + col_values = col_values_array; + else if (const auto * col_values_map = checkAndGetColumn(holder_values.get())) + col_values = &col_values_map->getNestedColumn(); + else + throw Exception(ErrorCodes::ILLEGAL_COLUMN, "The second arguments of function {} must be Array or Map", getName()); + + if (!col_keys->hasEqualOffsets(*col_values)) + throw Exception(ErrorCodes::SIZES_OF_ARRAYS_DONT_MATCH, "Two arguments for function {} must have equal sizes", getName()); + + const auto & data_keys = col_keys->getDataPtr(); + const auto & data_values = col_values->getDataPtr(); + const auto & offsets = col_keys->getOffsetsPtr(); + auto nested_column = ColumnArray::create(ColumnTuple::create(Columns{data_keys, data_values}), offsets); + return ColumnMap::create(nested_column); + } +}; struct NameMapContains { static constexpr auto name = "mapContains"; }; @@ -649,6 +739,9 @@ REGISTER_FUNCTION(Map) factory.registerFunction(); factory.registerFunction(); factory.registerFunction(); + factory.registerFunction(); + factory.registerAlias("MAP_FROM_ARRAYS", "mapFromArrays"); + } } diff --git a/src/Functions/tupleElement.cpp b/src/Functions/tupleElement.cpp index 829262de30a7..048159c3b2f6 100644 --- a/src/Functions/tupleElement.cpp +++ b/src/Functions/tupleElement.cpp @@ -21,7 +21,7 @@ namespace ErrorCodes extern const int NUMBER_OF_ARGUMENTS_DOESNT_MATCH; extern const int NOT_FOUND_COLUMN_IN_BLOCK; extern const int NUMBER_OF_DIMENSIONS_MISMATCHED; - extern const int SIZES_OF_ARRAYS_DOESNT_MATCH; + extern const int SIZES_OF_ARRAYS_DONT_MATCH; } namespace @@ -201,7 +201,7 @@ class FunctionTupleElement : public IFunction const auto & array_y = *assert_cast(col_y.get()); if (!array_x.hasEqualOffsets(array_y)) { - throw Exception(ErrorCodes::SIZES_OF_ARRAYS_DOESNT_MATCH, + throw Exception(ErrorCodes::SIZES_OF_ARRAYS_DONT_MATCH, "The argument 1 and argument 3 of function {} have different array sizes", getName()); } } @@ -223,7 +223,7 @@ class FunctionTupleElement : public IFunction { if (unlikely(offsets_x[0] != offsets_y[row] - prev_offset)) { - throw Exception(ErrorCodes::SIZES_OF_ARRAYS_DOESNT_MATCH, + throw Exception(ErrorCodes::SIZES_OF_ARRAYS_DONT_MATCH, "The argument 1 and argument 3 of function {} have different array sizes", getName()); } prev_offset = offsets_y[row]; diff --git a/src/Functions/validateNestedArraySizes.cpp b/src/Functions/validateNestedArraySizes.cpp index 7e1dbc798d80..c422637ba7f2 100644 --- a/src/Functions/validateNestedArraySizes.cpp +++ b/src/Functions/validateNestedArraySizes.cpp @@ -12,7 +12,7 @@ namespace ErrorCodes { extern const int ILLEGAL_TYPE_OF_ARGUMENT; extern const int NUMBER_OF_ARGUMENTS_DOESNT_MATCH; - extern const int SIZES_OF_ARRAYS_DOESNT_MATCH; + extern const int SIZES_OF_ARRAYS_DONT_MATCH; } /** Function validateNestedArraySizes is used to check the consistency of Nested DataType subcolumns's offsets when Update @@ -106,7 +106,7 @@ ColumnPtr FunctionValidateNestedArraySizes::executeImpl( else if (first_length != length) { throw Exception( - ErrorCodes::SIZES_OF_ARRAYS_DOESNT_MATCH, + ErrorCodes::SIZES_OF_ARRAYS_DONT_MATCH, "Elements '{}' and '{}' of Nested data structure (Array columns) " "have different array sizes ({} and {} respectively) on row {}", arguments[1].name, arguments[args_idx].name, first_length, length, i); diff --git a/src/Interpreters/ArrayJoinAction.cpp b/src/Interpreters/ArrayJoinAction.cpp index 3650b888f9eb..4f42122e98f3 100644 --- a/src/Interpreters/ArrayJoinAction.cpp +++ b/src/Interpreters/ArrayJoinAction.cpp @@ -14,7 +14,7 @@ namespace DB namespace ErrorCodes { extern const int LOGICAL_ERROR; - extern const int SIZES_OF_ARRAYS_DOESNT_MATCH; + extern const int SIZES_OF_ARRAYS_DONT_MATCH; extern const int TYPE_MISMATCH; } @@ -186,7 +186,7 @@ void ArrayJoinAction::execute(Block & block) const ColumnArray & array = typeid_cast(*array_ptr); if (!is_unaligned && !array.hasEqualOffsets(*any_array)) - throw Exception(ErrorCodes::SIZES_OF_ARRAYS_DOESNT_MATCH, "Sizes of ARRAY-JOIN-ed arrays do not match"); + throw Exception(ErrorCodes::SIZES_OF_ARRAYS_DONT_MATCH, "Sizes of ARRAY-JOIN-ed arrays do not match"); current.column = typeid_cast(*array_ptr).getDataPtr(); current.type = type->getNestedType(); diff --git a/tests/queries/0_stateless/01651_map_functions.reference b/tests/queries/0_stateless/01651_map_functions.reference index 06adaf48cfd9..60f1b6e0d0c4 100644 --- a/tests/queries/0_stateless/01651_map_functions.reference +++ b/tests/queries/0_stateless/01651_map_functions.reference @@ -8,6 +8,8 @@ 0 ['name','age'] ['name','gender'] +{'name':'zhangsan','age':'10'} +{'name':'lisi','gender':'female'} 1 0 0 1 0 1 1 0 0 @@ -17,7 +19,20 @@ [1000] [1001] [1002] +{'1000':'2000','1000':'3000','1000':'4000'} +{'1001':'2002','1001':'3003','1001':'4004'} +{'1002':'2004','1002':'3006','1002':'4008'} {'aa':4,'bb':5} ['aa','bb'] [4,5] {'aa':4,'bb':5} 1 0 {0:0} 1 {0:0} 0 +{'aa':4,'bb':5} +{'aa':4,'bb':5} +{'aa':4,'bb':5} +{'aa':4,'bb':5} +{'aa':4,'bb':5} +{'aa':4,'bb':5} +{'aa':4,'bb':5} +{'aa':('a',4),'bb':('b',5)} +{'aa':('a',4),'bb':('b',5)} +{'aa':('a',4),'bb':('b',5)} diff --git a/tests/queries/0_stateless/01651_map_functions.sql b/tests/queries/0_stateless/01651_map_functions.sql index bbaaf9bee84b..5942bf8b2c22 100644 --- a/tests/queries/0_stateless/01651_map_functions.sql +++ b/tests/queries/0_stateless/01651_map_functions.sql @@ -2,23 +2,25 @@ set allow_experimental_map_type = 1; -- String type drop table if exists table_map; -create table table_map (a Map(String, String), b String) engine = Memory; -insert into table_map values ({'name':'zhangsan', 'age':'10'}, 'name'), ({'name':'lisi', 'gender':'female'},'age'); +create table table_map (a Map(String, String), b String, c Array(String), d Array(String)) engine = Memory; +insert into table_map values ({'name':'zhangsan', 'age':'10'}, 'name', ['name', 'age'], ['zhangsan', '10']), ({'name':'lisi', 'gender':'female'},'age',['name', 'gender'], ['lisi', 'female']); select mapContains(a, 'name') from table_map; select mapContains(a, 'gender') from table_map; select mapContains(a, 'abc') from table_map; select mapContains(a, b) from table_map; -select mapContains(a, 10) from table_map; -- { serverError 386 } +select mapContains(a, 10) from table_map; -- { serverError NO_COMMON_TYPE } select mapKeys(a) from table_map; +select mapFromArrays(c, d) from table_map; drop table if exists table_map; -CREATE TABLE table_map (a Map(UInt8, Int), b UInt8, c UInt32) engine = MergeTree order by tuple(); -insert into table_map select map(number, number), number, number from numbers(1000, 3); +CREATE TABLE table_map (a Map(UInt8, Int), b UInt8, c UInt32, d Array(String), e Array(String)) engine = MergeTree order by tuple(); +insert into table_map select map(number, number), number, number, [number, number, number], [number*2, number*3, number*4] from numbers(1000, 3); select mapContains(a, b), mapContains(a, c), mapContains(a, 233) from table_map; -select mapContains(a, 'aaa') from table_map; -- { serverError 386 } -select mapContains(b, 'aaa') from table_map; -- { serverError 43 } +select mapContains(a, 'aaa') from table_map; -- { serverError NO_COMMON_TYPE } +select mapContains(b, 'aaa') from table_map; -- { serverError ILLEGAL_TYPE_OF_ARGUMENT } select mapKeys(a) from table_map; select mapValues(a) from table_map; +select mapFromArrays(d, e) from table_map; drop table if exists table_map; @@ -27,3 +29,18 @@ select map( 'aa', 4, 'bb' , 5) as m, mapKeys(m), mapValues(m); select map( 'aa', 4, 'bb' , 5) as m, mapContains(m, 'aa'), mapContains(m, 'k'); select map(0, 0) as m, mapContains(m, number % 2) from numbers(2); + +select mapFromArrays(['aa', 'bb'], [4, 5]); +select mapFromArrays(['aa', 'bb'], materialize([4, 5])) from numbers(2); +select mapFromArrays(materialize(['aa', 'bb']), [4, 5]) from numbers(2); +select mapFromArrays(materialize(['aa', 'bb']), materialize([4, 5])) from numbers(2); +select mapFromArrays('aa', [4, 5]); -- { serverError ILLEGAL_TYPE_OF_ARGUMENT } +select mapFromArrays(['aa', 'bb'], 5); -- { serverError ILLEGAL_TYPE_OF_ARGUMENT } +select mapFromArrays(['aa', 'bb'], [4, 5], [6, 7]); -- { serverError NUMBER_OF_ARGUMENTS_DOESNT_MATCH } +select mapFromArrays(['aa', 'bb'], [4, 5, 6]); -- { serverError SIZES_OF_ARRAYS_DONT_MATCH } +select mapFromArrays([[1,2], [3,4]], [4, 5, 6]); -- { serverError BAD_ARGUMENTS } + +select mapFromArrays(['aa', 'bb'], map('a', 4, 'b', 5)); +select mapFromArrays(['aa', 'bb'], materialize(map('a', 4, 'b', 5))) from numbers(2); +select mapFromArrays(map('a', 4, 'b', 4), ['aa', 'bb']) from numbers(2); -- { serverError ILLEGAL_TYPE_OF_ARGUMENT } +select mapFromArrays(['aa', 'bb'], map('a', 4)); -- { serverError SIZES_OF_ARRAYS_DONT_MATCH } diff --git a/tests/queries/0_stateless/02415_all_new_functions_must_be_documented.reference b/tests/queries/0_stateless/02415_all_new_functions_must_be_documented.reference index d225cf5f332d..1a8d03bc320f 100644 --- a/tests/queries/0_stateless/02415_all_new_functions_must_be_documented.reference +++ b/tests/queries/0_stateless/02415_all_new_functions_must_be_documented.reference @@ -421,6 +421,7 @@ mapContains mapContainsKeyLike mapExtractKeyLike mapFilter +mapFromArrays mapKeys mapPopulateSeries mapSubtract diff --git a/utils/local-engine/Parser/RelParser.h b/utils/local-engine/Parser/RelParser.h index 658450970d9a..381777571df2 100644 --- a/utils/local-engine/Parser/RelParser.h +++ b/utils/local-engine/Parser/RelParser.h @@ -42,7 +42,7 @@ class RelParser static DB::Names parseFunctionArgumentNames(const Block & header, const google::protobuf::RepeatedPtrField & func_args); const DB::ActionsDAG::Node * parseArgument(ActionsDAGPtr action_dag, const substrait::Expression & rel) { - return plan_parser->parseArgument(action_dag, rel); + return plan_parser->parseExpression(action_dag, rel); } std::pair parseLiteral(const substrait::Expression_Literal & literal) { diff --git a/utils/local-engine/Parser/SerializedPlanParser.cpp b/utils/local-engine/Parser/SerializedPlanParser.cpp index 4882e97e0794..01d4b6e31f42 100644 --- a/utils/local-engine/Parser/SerializedPlanParser.cpp +++ b/utils/local-engine/Parser/SerializedPlanParser.cpp @@ -167,13 +167,14 @@ std::shared_ptr SerializedPlanParser::expressionsToActionsDAG( std::vector useless; if (function_name == "arrayJoin") { - actions_dag = parseArrayJoin(header, expr, result_names, useless, actions_dag, true); + /// Whether the function from spark is explode or posexplode + bool position = startsWith(function_signature, "posexplode"); + actions_dag = parseArrayJoin(header, expr, result_names, useless, actions_dag, true, position); } else { result_names.resize(1); actions_dag = parseFunction(header, expr, result_names[0], useless, actions_dag, true); - } for (const auto & result_name : result_names) @@ -196,7 +197,7 @@ std::shared_ptr SerializedPlanParser::expressionsToActionsDAG( } else if (expr.has_cast() || expr.has_if_then() || expr.has_literal()) { - const auto * node = parseArgument(actions_dag, expr); + const auto * node = parseExpression(actions_dag, expr); actions_dag->addOrReplaceInOutputs(*node); if (distinct_columns.contains(node->result_name)) { @@ -440,7 +441,7 @@ PrewhereInfoPtr SerializedPlanParser::parsePreWhereInfo(const substrait::Express // for in function if (rel.has_singular_or_list()) { - const auto *in_node = parseArgument(prewhere_info->prewhere_actions, rel); + const auto *in_node = parseExpression(prewhere_info->prewhere_actions, rel); prewhere_info->prewhere_actions->addOrReplaceInOutputs(*in_node); filter_name = in_node->result_name; } @@ -748,7 +749,7 @@ QueryPlanPtr SerializedPlanParser::parseOp(const substrait::Rel & rel, std::list else { actions_dag = std::make_shared(blockToNameAndTypeList(query_plan->getCurrentDataStream().header)); - const auto * node = parseArgument(actions_dag, filter.condition()); + const auto * node = parseExpression(actions_dag, filter.condition()); filter_name = node->result_name; } @@ -831,7 +832,6 @@ QueryPlanPtr SerializedPlanParser::parseOp(const substrait::Rel & rel, std::list } auto source = query_plan->getCurrentDataStream().header.getColumnsWithTypeAndName(); auto target = source; - // std::cout << "aggregate header:" << query_plan->getCurrentDataStream().header.dumpStructure() << std::endl; bool need_convert = false; for (size_t i = 0; i < measure_positions.size(); i++) @@ -842,8 +842,6 @@ QueryPlanPtr SerializedPlanParser::parseOp(const substrait::Rel & rel, std::list target[measure_positions[i]].type = target_type; target[measure_positions[i]].column = target_type->createColumn(); need_convert = true; - // std::cout << "source type:" << source[measure_positions[i]].type->getName() << std::endl; - // std::cout << "target type:" << target_type->getName() << std::endl; } } @@ -978,7 +976,7 @@ void SerializedPlanParser::addPreProjectStepIfNeeded( } else if (arg.has_literal()) { - const auto * node = parseArgument(expression, arg); + const auto * node = parseExpression(expression, arg); expression->addOrReplaceInOutputs(*node); measure_name = node->result_name; measure_names.emplace_back(measure_name); @@ -1284,38 +1282,27 @@ ActionsDAG::NodeRawConstPtrs SerializedPlanParser::parseArrayJoinWithDAG( std::vector & result_names, std::vector & required_columns, DB::ActionsDAGPtr actions_dag, - bool keep_result) + bool keep_result, bool position) { if (!rel.has_scalar_function()) - throw Exception(ErrorCodes::BAD_ARGUMENTS, "the root of expression should be a scalar function:\n {}", rel.DebugString()); + throw Exception(ErrorCodes::BAD_ARGUMENTS, "The root of expression should be a scalar function:\n {}", rel.DebugString()); const auto & scalar_function = rel.scalar_function(); + auto function_signature = function_mapping.at(std::to_string(rel.scalar_function().function_reference())); auto function_name = getFunctionName(function_signature, scalar_function); if (function_name != "arrayJoin") throw Exception( ErrorCodes::LOGICAL_ERROR, - "parseArrayJoinWithDAG should only process arrayJoin function, but input is {}", + "Function parseArrayJoinWithDAG should only process arrayJoin function, but input is {}", rel.ShortDebugString()); - ActionsDAG::NodeRawConstPtrs args; - for (const auto & arg : scalar_function.arguments()) - { - if (arg.value().has_scalar_function()) - { - std::string arg_name; - bool keep_arg = FUNCTION_NEED_KEEP_ARGUMENTS.contains(function_name); - parseFunctionWithDAG(arg.value(), arg_name, required_columns, actions_dag, keep_arg); - args.emplace_back(&actions_dag->getNodes().back()); - } - else - { - args.emplace_back(parseArgument(actions_dag, arg.value())); - } - } + /// The argument number of arrayJoin(converted from Spark explode/posexplode) should be 1 + if (scalar_function.arguments_size() != 1) + throw Exception(ErrorCodes::BAD_ARGUMENTS, "Argument number of arrayJoin should be 1 but is {}", scalar_function.arguments_size()); - if (args.size() != 1) - throw Exception(ErrorCodes::BAD_ARGUMENTS, "argument number of arrayJoin should be 1 but is {}", args.size()); + ActionsDAG::NodeRawConstPtrs args; + parseFunctionArguments(actions_dag, args, required_columns, function_name, scalar_function); /// arrayJoin(args[0]) auto array_join_name = "arrayJoin(" + args[0]->result_name + ")"; @@ -1323,44 +1310,113 @@ ActionsDAG::NodeRawConstPtrs SerializedPlanParser::parseArrayJoinWithDAG( auto arg_type = DB::removeNullable(args[0]->result_type); WhichDataType which(arg_type.get()); - if (which.isMap()) - { - /// In Spark: explode(map(k, v)) output 2 columns with default names "key" and "value" - /// In CH: arrayJoin(map(k, v)) output 1 column with Tuple Type. - /// So we must wrap arrayJoin with tupleElement function for compatiability. - auto tuple_element_builder = FunctionFactory::instance().get("tupleElement", context); - auto index_type = std::make_shared(); - - /// arrayJoin(args[0]).1 - ColumnWithTypeAndName key_index_col(index_type->createColumnConst(1, 1), index_type, getUniqueName("1")); - const auto * key_index_node = &actions_dag->addColumn(std::move(key_index_col)); - auto key_name = "tupleElement(" + array_join_name + ",1)"; - const auto * key_node = &actions_dag->addFunction(tuple_element_builder, {array_join_node, key_index_node}, key_name); - - /// arrayJoin(args[0]).2 - ColumnWithTypeAndName val_index_col(index_type->createColumnConst(1, 2), index_type, getUniqueName("2")); - const auto * val_index_node = &actions_dag->addColumn(std::move(val_index_col)); - auto val_name = "tupleElement(" + array_join_name + ",1)"; - const auto * val_node = &actions_dag->addFunction(tuple_element_builder, {array_join_node, val_index_node}, val_name); - - result_names.push_back(key_name); - result_names.push_back(val_name); - if (keep_result) + auto tuple_element_builder = FunctionFactory::instance().get("tupleElement", context); + auto tuple_index_type = std::make_shared(); + + auto add_tuple_element = [&](const ActionsDAG::Node * tuple_node, size_t i) -> const ActionsDAG::Node * + { + ColumnWithTypeAndName index_col(tuple_index_type->createColumnConst(1, i), tuple_index_type, getUniqueName(std::to_string(i))); + const auto * index_node = &actions_dag->addColumn(std::move(index_col)); + auto result_name = "tupleElement(" + tuple_node->result_name + ", " + index_node->result_name + ")"; + return &actions_dag->addFunction(tuple_element_builder, {tuple_node, index_node}, result_name); + }; + + /// Special process to keep compatiable with Spark + if (!position) + { + /// Spark: explode(array_or_map) -> CH: arrayJoin(array_or_map) + if (which.isMap()) + { + /// In Spark: explode(map(k, v)) output 2 columns with default names "key" and "value" + /// In CH: arrayJoin(map(k, v)) output 1 column with Tuple Type. + /// So we must wrap arrayJoin with tupleElement function for compatiability. + + /// arrayJoin(args[0]).1 + const auto * key_node = add_tuple_element(array_join_node, 1); + + /// arrayJoin(args[0]).2 + const auto * val_node = add_tuple_element(array_join_node, 2); + + result_names.push_back(key_node->result_name); + result_names.push_back(val_node->result_name); + if (keep_result) + { + actions_dag->addOrReplaceInOutputs(*key_node); + actions_dag->addOrReplaceInOutputs(*val_node); + } + return {key_node, val_node}; + } + else if (which.isArray()) { - actions_dag->addOrReplaceInOutputs(*key_node); - actions_dag->addOrReplaceInOutputs(*val_node); + result_names.push_back(array_join_name); + if (keep_result) + actions_dag->addOrReplaceInOutputs(*array_join_node); + return {array_join_node}; } - return {key_node, val_node}; + else + throw Exception(ErrorCodes::BAD_ARGUMENTS, "Argument type of arrayJoin converted from explode should be Array or Map but is {}", arg_type->getName()); } - else if (which.isArray()) + else { - result_names.push_back(array_join_name); - if (keep_result) - actions_dag->addOrReplaceInOutputs(*array_join_node); - return {array_join_node}; + /// Spark: posexplode(array_or_map) -> CH: arrayJoin(map), in which map = mapFromArrays(range(length(array_or_map)), array_or_map) + if (which.isMap()) + { + /// In Spark: posexplode(array_of_map) output 2 or 3 columns: (pos, col) or (pos, key, value) + /// In CH: arrayJoin(map(k, v)) output 1 column with Tuple Type. + /// So we must wrap arrayJoin with tupleElement function for compatiability. + + /// pos = arrayJoin(args[0]).1 + const auto * pos_node = add_tuple_element(array_join_node, 1); + + /// col = arrayJoin(args[0]).2 or (key, value) = arrayJoin(args[0]).2 + const auto * item_node = add_tuple_element(array_join_node, 2); + + /// Get type of y from node: cast(mapFromArrays(x, y), 'Map(K, V)') + auto raw_child_type = DB::removeNullable(args[0]->children[0]->children[1]->result_type); + if (isMap(raw_child_type)) + { + /// key = arrayJoin(args[0]).2.1 + const auto * item_key_node = add_tuple_element(item_node, 1); + + /// value = arrayJoin(args[0]).2.2 + const auto * item_value_node = add_tuple_element(item_node, 2); + + result_names.push_back(pos_node->result_name); + result_names.push_back(item_key_node->result_name); + result_names.push_back(item_value_node->result_name); + if (keep_result) + { + actions_dag->addOrReplaceInOutputs(*pos_node); + actions_dag->addOrReplaceInOutputs(*item_key_node); + actions_dag->addOrReplaceInOutputs(*item_value_node); + } + + return {pos_node, item_key_node, item_value_node}; + } + else if (isArray(raw_child_type)) + { + /// col = arrayJoin(args[0]).2 + result_names.push_back(pos_node->result_name); + result_names.push_back(item_node->result_name); + if (keep_result) + { + actions_dag->addOrReplaceInOutputs(*pos_node); + actions_dag->addOrReplaceInOutputs(*item_node); + } + return {pos_node, item_node}; + } + else + throw Exception( + ErrorCodes::BAD_ARGUMENTS, + "The raw input of arrayJoin converted from posexplode should be Array or Map type but is {}", + raw_child_type->getName()); + } + else + throw Exception( + ErrorCodes::BAD_ARGUMENTS, + "Argument type of arrayJoin converted from posexplode should be Map but is {}", + arg_type->getName()); } - else - throw Exception(ErrorCodes::BAD_ARGUMENTS, "argument type of arrayJoin should be Array or Map but is {}", arg_type->getName()); } const ActionsDAG::Node * SerializedPlanParser::parseFunctionWithDAG( @@ -1462,6 +1518,7 @@ const ActionsDAG::Node * SerializedPlanParser::parseFunctionWithDAG( result_name = function_name + "(" + args_name + ")"; const auto * function_node = &actions_dag->addFunction(function_builder, args, result_name); result_node = function_node; + if (!isTypeMatched(rel.scalar_function().output_type(), function_node->result_type)) { result_node = ActionsDAGUtil::convertNodeType( @@ -1491,7 +1548,9 @@ void SerializedPlanParser::parseFunctionArguments( { return &actions_dag->addColumn(ColumnWithTypeAndName(type->createColumnConst(1, field), type, getUniqueName(toString(field)))); }; + const auto & args = scalar_function.arguments(); + // Some functions need to be handled specially. if (function_name == "JSONExtract") { @@ -1595,7 +1654,7 @@ void SerializedPlanParser::parseFunctionArguments( const DB::ActionsDAG::Node * arg_node = nullptr; if (args[0].value().has_cast()) { - arg_node = parseArgument(actions_dag, args[0].value().cast().input()); + arg_node = parseExpression(actions_dag, args[0].value().cast().input()); const auto * res_type = arg_node->result_type.get(); if (res_type->isNullable()) { @@ -1680,7 +1739,7 @@ const DB::ActionsDAG::Node * SerializedPlanParser::parseFunctionArgument( } else { - res = parseArgument(actions_dag, arg.value()); + res = parseExpression(actions_dag, arg.value()); } return res; } @@ -1731,12 +1790,12 @@ ActionsDAGPtr SerializedPlanParser::parseArrayJoin( std::vector & result_names, std::vector & required_columns, ActionsDAGPtr actions_dag, - bool keep_result) + bool keep_result, bool position) { if (!actions_dag) actions_dag = std::make_shared(blockToNameAndTypeList(input)); - parseArrayJoinWithDAG(rel, result_names, required_columns, actions_dag, keep_result); + parseArrayJoinWithDAG(rel, result_names, required_columns, actions_dag, keep_result, position); return actions_dag; } @@ -1881,7 +1940,7 @@ std::pair SerializedPlanParser::parseLiteral(const substrait return std::make_pair(std::move(type), std::move(field)); } -const ActionsDAG::Node * SerializedPlanParser::parseArgument(ActionsDAGPtr action_dag, const substrait::Expression & rel) +const ActionsDAG::Node * SerializedPlanParser::parseExpression(ActionsDAGPtr action_dag, const substrait::Expression & rel) { auto add_column = [&](const DataTypePtr & type, const Field & field) -> auto { @@ -1912,26 +1971,7 @@ const ActionsDAG::Node * SerializedPlanParser::parseArgument(ActionsDAGPtr actio std::string ch_function_name = getCastFunction(rel.cast().type()); DB::ActionsDAG::NodeRawConstPtrs args; const auto & cast_input = rel.cast().input(); - args.emplace_back(parseArgument(action_dag, cast_input)); - /* - if (cast_input.has_selection() || cast_input.has_literal()) - { - args.emplace_back(parseArgument(action_dag, rel.cast().input())); - } - else if (cast_input.has_if_then()) - { - args.emplace_back(parseArgument(action_dag, rel.cast().input())); - } - else if (cast_input.has_scalar_function()) - { - std::string result; - std::vector useless; - const auto * node = parseFunctionWithDAG(cast_input, result, useless, action_dag, false); - args.emplace_back(node); - } - else - throw Exception(ErrorCodes::BAD_ARGUMENTS, "unsupported cast input {}", rel.cast().input().DebugString()); - */ + args.emplace_back(parseExpression(action_dag, cast_input)); if (ch_function_name.starts_with("toDecimal")) { @@ -1960,14 +2000,14 @@ const ActionsDAG::Node * SerializedPlanParser::parseArgument(ActionsDAGPtr actio for (int i = 0; i < condition_nums; ++i) { const auto & ifs = if_then.ifs(i); - const auto * if_node = parseArgument(action_dag, ifs.if_()); + const auto * if_node = parseExpression(action_dag, ifs.if_()); args.emplace_back(if_node); - const auto * then_node = parseArgument(action_dag, ifs.then()); + const auto * then_node = parseExpression(action_dag, ifs.then()); args.emplace_back(then_node); } - const auto * else_node = parseArgument(action_dag, if_then.else_()); + const auto * else_node = parseExpression(action_dag, if_then.else_()); args.emplace_back(else_node); std::string args_name; join(args, ',', args_name); @@ -1993,7 +2033,7 @@ const ActionsDAG::Node * SerializedPlanParser::parseArgument(ActionsDAGPtr actio throw Exception(ErrorCodes::LOGICAL_ERROR, "Options of SingularOrList must have literal type"); DB::ActionsDAG::NodeRawConstPtrs args; - args.emplace_back(parseArgument(action_dag, rel.singular_or_list().value())); + args.emplace_back(parseExpression(action_dag, rel.singular_or_list().value())); bool nullable = false; size_t options_len = options.size(); diff --git a/utils/local-engine/Parser/SerializedPlanParser.h b/utils/local-engine/Parser/SerializedPlanParser.h index 9f86c7c96236..d6fd8d90fe56 100644 --- a/utils/local-engine/Parser/SerializedPlanParser.h +++ b/utils/local-engine/Parser/SerializedPlanParser.h @@ -170,12 +170,14 @@ static const std::map SCALAR_FUNCTIONS = { {"get_array_item", "arrayElement"}, {"element_at", "arrayElement"}, {"array_contains", "has"}, + {"range", "range"}, /// dummy mapping // map functions {"map", "map"}, {"get_map_value", "arrayElement"}, {"map_keys", "mapKeys"}, {"map_values", "mapValues"}, + {"map_from_arrays", "mapFromArrays"}, // tuple functions {"get_struct_field", "tupleElement"}, @@ -183,6 +185,7 @@ static const std::map SCALAR_FUNCTIONS = { // table-valued generator function {"explode", "arrayJoin"}, + {"posexplode", "arrayJoin"}, // json functions {"get_json_object", "JSON_VALUE"}, @@ -266,7 +269,8 @@ class SerializedPlanParser std::vector & result_names, std::vector & required_columns, DB::ActionsDAGPtr actions_dag = nullptr, - bool keep_result = false); + bool keep_result = false, + bool position = false); const ActionsDAG::Node * parseFunctionWithDAG( const substrait::Expression & rel, std::string & result_name, @@ -278,7 +282,8 @@ class SerializedPlanParser std::vector & result_name, std::vector & required_columns, DB::ActionsDAGPtr actions_dag = nullptr, - bool keep_result = false); + bool keep_result = false, + bool position = false); void parseFunctionArguments( DB::ActionsDAGPtr & actions_dag, ActionsDAG::NodeRawConstPtrs & parsed_args, @@ -302,7 +307,7 @@ class SerializedPlanParser std::vector & measure_names, std::map & nullable_measure_names); DB::QueryPlanStepPtr parseAggregate(DB::QueryPlan & plan, const substrait::AggregateRel & rel, bool & is_final); - const DB::ActionsDAG::Node * parseArgument(DB::ActionsDAGPtr action_dag, const substrait::Expression & rel); + const DB::ActionsDAG::Node * parseExpression(DB::ActionsDAGPtr action_dag, const substrait::Expression & rel); const ActionsDAG::Node * toFunctionNode(ActionsDAGPtr action_dag, const String & function, const DB::ActionsDAG::NodeRawConstPtrs & args); // remove nullable after isNotNull