|
|
|
@ -1,15 +1,20 @@
|
|
|
|
|
#include <base/sort.h>
|
|
|
|
|
|
|
|
|
|
#include <Core/ColumnWithTypeAndName.h>
|
|
|
|
|
#include <Columns/ColumnArray.h>
|
|
|
|
|
#include <Columns/ColumnMap.h>
|
|
|
|
|
#include <Columns/ColumnTuple.h>
|
|
|
|
|
#include <Columns/ColumnVector.h>
|
|
|
|
|
#include <DataTypes/IDataType.h>
|
|
|
|
|
#include <DataTypes/DataTypeArray.h>
|
|
|
|
|
#include <DataTypes/DataTypeTuple.h>
|
|
|
|
|
#include <DataTypes/DataTypesNumber.h>
|
|
|
|
|
#include <DataTypes/DataTypeMap.h>
|
|
|
|
|
#include <Functions/FunctionFactory.h>
|
|
|
|
|
#include <Functions/FunctionHelpers.h>
|
|
|
|
|
#include <Functions/IFunction.h>
|
|
|
|
|
#include "Core/ColumnWithTypeAndName.h"
|
|
|
|
|
#include "DataTypes/DataTypeMap.h"
|
|
|
|
|
#include "DataTypes/IDataType.h"
|
|
|
|
|
#include <Interpreters/castColumn.h>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
namespace DB
|
|
|
|
|
{
|
|
|
|
@ -19,6 +24,8 @@ namespace ErrorCodes
|
|
|
|
|
extern const int ILLEGAL_TYPE_OF_ARGUMENT;
|
|
|
|
|
extern const int NUMBER_OF_ARGUMENTS_DOESNT_MATCH;
|
|
|
|
|
extern const int TOO_LARGE_ARRAY_SIZE;
|
|
|
|
|
extern const int BAD_ARGUMENTS;
|
|
|
|
|
extern const int LOGICAL_ERROR;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
class FunctionMapPopulateSeries : public IFunction
|
|
|
|
@ -35,415 +42,458 @@ private:
|
|
|
|
|
bool useDefaultImplementationForConstants() const override { return true; }
|
|
|
|
|
bool isSuitableForShortCircuitArgumentsExecution(const DataTypesWithConstInfo & /*arguments*/) const override { return true; }
|
|
|
|
|
|
|
|
|
|
void checkTypes(const DataTypePtr & key_type, const DataTypePtr max_key_type) const
|
|
|
|
|
void checkTypes(const DataTypePtr & key_type, const DataTypePtr & value_type, const DataTypePtr & max_key_type) const
|
|
|
|
|
{
|
|
|
|
|
WhichDataType which_key(key_type);
|
|
|
|
|
if (!(which_key.isInt() || which_key.isUInt()))
|
|
|
|
|
WhichDataType key_data_type(key_type);
|
|
|
|
|
WhichDataType value_data_type(value_type);
|
|
|
|
|
|
|
|
|
|
if (!(key_data_type.isInt() || key_data_type.isUInt()))
|
|
|
|
|
{
|
|
|
|
|
throw Exception(
|
|
|
|
|
ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT, "Keys for {} function should be of integer type (signed or unsigned)", getName());
|
|
|
|
|
ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT,
|
|
|
|
|
"Function {} key argument should be of signed or unsigned integer type. Actual type {}",
|
|
|
|
|
getName(),
|
|
|
|
|
key_type->getName());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (max_key_type)
|
|
|
|
|
if (!(value_data_type.isInt() || value_data_type.isUInt()))
|
|
|
|
|
{
|
|
|
|
|
WhichDataType which_max_key(max_key_type);
|
|
|
|
|
|
|
|
|
|
if (which_max_key.isNullable())
|
|
|
|
|
throw Exception(
|
|
|
|
|
ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT,
|
|
|
|
|
"Max key argument in arguments of function " + getName() + " can not be Nullable");
|
|
|
|
|
|
|
|
|
|
if (key_type->getTypeId() != max_key_type->getTypeId())
|
|
|
|
|
throw Exception(ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT, "Max key type in {} should be same as keys type", getName());
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
DataTypePtr getReturnTypeForTuple(const DataTypes & arguments) const
|
|
|
|
|
{
|
|
|
|
|
if (arguments.size() < 2)
|
|
|
|
|
throw Exception(
|
|
|
|
|
ErrorCodes::NUMBER_OF_ARGUMENTS_DOESNT_MATCH, "Function {} accepts at least two arrays for key and value", getName());
|
|
|
|
|
ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT,
|
|
|
|
|
"Function {} key argument should be of signed or unsigned integer type. Actual type {}",
|
|
|
|
|
getName(),
|
|
|
|
|
key_type->getName());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (arguments.size() > 3)
|
|
|
|
|
throw Exception(ErrorCodes::NUMBER_OF_ARGUMENTS_DOESNT_MATCH, "Too many arguments in {} call", getName());
|
|
|
|
|
if (!max_key_type)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
const DataTypeArray * key_array_type = checkAndGetDataType<DataTypeArray>(arguments[0].get());
|
|
|
|
|
const DataTypeArray * val_array_type = checkAndGetDataType<DataTypeArray>(arguments[1].get());
|
|
|
|
|
WhichDataType max_key_data_type(max_key_type);
|
|
|
|
|
|
|
|
|
|
if (!key_array_type || !val_array_type)
|
|
|
|
|
throw Exception(ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT, "Function {} accepts two arrays for key and value", getName());
|
|
|
|
|
if (max_key_data_type.isNullable())
|
|
|
|
|
throw Exception(
|
|
|
|
|
ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT,
|
|
|
|
|
"Function {} max key argument can not be Nullable. Actual type {}",
|
|
|
|
|
getName(),
|
|
|
|
|
max_key_type->getName());
|
|
|
|
|
|
|
|
|
|
const auto & key_type = key_array_type->getNestedType();
|
|
|
|
|
|
|
|
|
|
if (arguments.size() == 3)
|
|
|
|
|
this->checkTypes(key_type, arguments[2]);
|
|
|
|
|
else
|
|
|
|
|
this->checkTypes(key_type, nullptr);
|
|
|
|
|
|
|
|
|
|
return std::make_shared<DataTypeTuple>(DataTypes{arguments[0], arguments[1]});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
DataTypePtr getReturnTypeForMap(const DataTypes & arguments) const
|
|
|
|
|
{
|
|
|
|
|
const auto * map = assert_cast<const DataTypeMap *>(arguments[0].get());
|
|
|
|
|
if (arguments.size() == 1)
|
|
|
|
|
this->checkTypes(map->getKeyType(), nullptr);
|
|
|
|
|
else if (arguments.size() == 2)
|
|
|
|
|
this->checkTypes(map->getKeyType(), arguments[1]);
|
|
|
|
|
else
|
|
|
|
|
throw Exception(ErrorCodes::NUMBER_OF_ARGUMENTS_DOESNT_MATCH, "Too many arguments in {} call", getName());
|
|
|
|
|
|
|
|
|
|
return std::make_shared<DataTypeMap>(map->getKeyType(), map->getValueType());
|
|
|
|
|
if (!(max_key_data_type.isInt() || max_key_data_type.isUInt()))
|
|
|
|
|
throw Exception(
|
|
|
|
|
ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT,
|
|
|
|
|
"Function {} max key should be of signed or unsigned integer type. Actual type {}.",
|
|
|
|
|
getName(),
|
|
|
|
|
key_type->getName(),
|
|
|
|
|
max_key_type->getName());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
DataTypePtr getReturnTypeImpl(const DataTypes & arguments) const override
|
|
|
|
|
{
|
|
|
|
|
if (arguments.empty())
|
|
|
|
|
throw Exception(ErrorCodes::NUMBER_OF_ARGUMENTS_DOESNT_MATCH, getName() + " accepts at least one map or two arrays");
|
|
|
|
|
if (arguments.empty() || arguments.size() > 3)
|
|
|
|
|
throw Exception(ErrorCodes::NUMBER_OF_ARGUMENTS_DOESNT_MATCH,
|
|
|
|
|
"Function {} accepts at least one map or two arrays arguments, and optional max key argument",
|
|
|
|
|
getName());
|
|
|
|
|
|
|
|
|
|
if (arguments[0]->getTypeId() == TypeIndex::Array)
|
|
|
|
|
return getReturnTypeForTuple(arguments);
|
|
|
|
|
else if (arguments[0]->getTypeId() == TypeIndex::Map)
|
|
|
|
|
return getReturnTypeForMap(arguments);
|
|
|
|
|
WhichDataType key_argument_data_type(arguments[0]);
|
|
|
|
|
|
|
|
|
|
DataTypePtr key_argument_series_type;
|
|
|
|
|
DataTypePtr value_argument_series_type;
|
|
|
|
|
|
|
|
|
|
size_t max_key_argument_index = 0;
|
|
|
|
|
|
|
|
|
|
if (key_argument_data_type.isArray())
|
|
|
|
|
{
|
|
|
|
|
DataTypePtr value_type;
|
|
|
|
|
if (1 < arguments.size())
|
|
|
|
|
value_type = arguments[1];
|
|
|
|
|
|
|
|
|
|
if (arguments.size() < 2 || (value_type && !isArray(value_type)))
|
|
|
|
|
throw Exception(ErrorCodes::BAD_ARGUMENTS,
|
|
|
|
|
"Function {} if array argument is passed as key, additional array argument as value must be passed",
|
|
|
|
|
getName());
|
|
|
|
|
|
|
|
|
|
const auto & key_array_type = assert_cast<const DataTypeArray &>(*arguments[0]);
|
|
|
|
|
const auto & value_array_type = assert_cast<const DataTypeArray &>(*value_type);
|
|
|
|
|
|
|
|
|
|
key_argument_series_type = key_array_type.getNestedType();
|
|
|
|
|
value_argument_series_type = value_array_type.getNestedType();
|
|
|
|
|
|
|
|
|
|
max_key_argument_index = 2;
|
|
|
|
|
}
|
|
|
|
|
else if (key_argument_data_type.isMap())
|
|
|
|
|
{
|
|
|
|
|
const auto & map_data_type = assert_cast<const DataTypeMap &>(*arguments[0]);
|
|
|
|
|
|
|
|
|
|
key_argument_series_type = map_data_type.getKeyType();
|
|
|
|
|
value_argument_series_type = map_data_type.getValueType();
|
|
|
|
|
|
|
|
|
|
max_key_argument_index = 1;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
throw Exception(
|
|
|
|
|
ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT,
|
|
|
|
|
"Function {} only accepts one map or arrays, but got {}",
|
|
|
|
|
getName(),
|
|
|
|
|
arguments[0]->getName());
|
|
|
|
|
|
|
|
|
|
DataTypePtr max_key_argument_type;
|
|
|
|
|
if (max_key_argument_index < arguments.size())
|
|
|
|
|
max_key_argument_type = arguments[max_key_argument_index];
|
|
|
|
|
|
|
|
|
|
checkTypes(key_argument_series_type, value_argument_series_type, max_key_argument_type);
|
|
|
|
|
|
|
|
|
|
if (key_argument_data_type.isArray())
|
|
|
|
|
return std::make_shared<DataTypeTuple>(DataTypes{arguments[0], arguments[1]});
|
|
|
|
|
else
|
|
|
|
|
return arguments[0];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Struct holds input and output columns references,
|
|
|
|
|
// Both arrays and maps have similar columns to work with but extracted differently
|
|
|
|
|
template <typename KeyType, typename ValType>
|
|
|
|
|
struct ColumnsInOut
|
|
|
|
|
template <typename KeyType, typename ValueType>
|
|
|
|
|
void executeImplTyped(
|
|
|
|
|
const ColumnPtr & key_column,
|
|
|
|
|
const ColumnPtr & value_column,
|
|
|
|
|
const ColumnPtr & offsets_column,
|
|
|
|
|
const ColumnPtr & max_key_column,
|
|
|
|
|
MutableColumnPtr result_key_column,
|
|
|
|
|
MutableColumnPtr result_value_column,
|
|
|
|
|
MutableColumnPtr result_offset_column) const
|
|
|
|
|
{
|
|
|
|
|
// inputs
|
|
|
|
|
const PaddedPODArray<KeyType> & in_keys_data;
|
|
|
|
|
const PaddedPODArray<ValType> & in_vals_data;
|
|
|
|
|
const IColumn::Offsets & in_key_offsets;
|
|
|
|
|
const IColumn::Offsets & in_val_offsets;
|
|
|
|
|
size_t row_count;
|
|
|
|
|
bool key_is_const;
|
|
|
|
|
bool val_is_const;
|
|
|
|
|
const auto & key_column_typed = assert_cast<const ColumnVector<KeyType> &>(*key_column);
|
|
|
|
|
const auto & key_column_data = key_column_typed.getData();
|
|
|
|
|
|
|
|
|
|
// outputs
|
|
|
|
|
PaddedPODArray<KeyType> & out_keys_data;
|
|
|
|
|
PaddedPODArray<ValType> & out_vals_data;
|
|
|
|
|
const auto & offsets_column_typed = assert_cast<const ColumnVector<ColumnArray::Offset> &>(*offsets_column);
|
|
|
|
|
const auto & offsets = offsets_column_typed.getData();
|
|
|
|
|
|
|
|
|
|
IColumn::Offsets & out_keys_offsets;
|
|
|
|
|
// with map argument this field will not be used
|
|
|
|
|
IColumn::Offsets * out_vals_offsets;
|
|
|
|
|
};
|
|
|
|
|
const auto & value_column_typed = assert_cast<const ColumnVector<ValueType> &>(*value_column);
|
|
|
|
|
const auto & value_column_data = value_column_typed.getData();
|
|
|
|
|
|
|
|
|
|
template <typename KeyType, typename ValType>
|
|
|
|
|
ColumnsInOut<KeyType, ValType> getInOutDataFromArrays(MutableColumnPtr & res_column, ColumnPtr * arg_columns) const
|
|
|
|
|
{
|
|
|
|
|
auto * out_tuple = assert_cast<ColumnTuple *>(res_column.get());
|
|
|
|
|
auto & out_keys_array = assert_cast<ColumnArray &>(out_tuple->getColumn(0));
|
|
|
|
|
auto & out_vals_array = assert_cast<ColumnArray &>(out_tuple->getColumn(1));
|
|
|
|
|
auto & result_key_column_typed = assert_cast<ColumnVector<KeyType> &>(*result_key_column);
|
|
|
|
|
auto & result_key_data = result_key_column_typed.getData();
|
|
|
|
|
|
|
|
|
|
const auto * key_column = arg_columns[0].get();
|
|
|
|
|
const auto * in_keys_array = checkAndGetColumn<ColumnArray>(key_column);
|
|
|
|
|
auto & result_value_column_typed = assert_cast<ColumnVector<ValueType> &>(*result_value_column);
|
|
|
|
|
auto & result_value_data = result_value_column_typed.getData();
|
|
|
|
|
|
|
|
|
|
bool key_is_const = false, val_is_const = false;
|
|
|
|
|
auto & result_offsets_column_typed = assert_cast<ColumnVector<ColumnArray::Offset> &>(*result_offset_column);
|
|
|
|
|
auto & result_offsets_data = result_offsets_column_typed.getData();
|
|
|
|
|
|
|
|
|
|
if (!in_keys_array)
|
|
|
|
|
const PaddedPODArray<KeyType> * max_key_data = max_key_column ? &assert_cast<const ColumnVector<KeyType> &>(*max_key_column).getData() : nullptr;
|
|
|
|
|
|
|
|
|
|
PaddedPODArray<std::pair<KeyType, ValueType>> sorted_keys_values;
|
|
|
|
|
|
|
|
|
|
size_t key_offsets_size = offsets.size();
|
|
|
|
|
result_key_data.reserve(key_offsets_size);
|
|
|
|
|
result_value_data.reserve(key_offsets_size);
|
|
|
|
|
|
|
|
|
|
for (size_t offset_index = 0; offset_index < key_offsets_size; ++offset_index)
|
|
|
|
|
{
|
|
|
|
|
const ColumnConst * const_array = checkAndGetColumnConst<ColumnArray>(key_column);
|
|
|
|
|
if (!const_array)
|
|
|
|
|
throw Exception(
|
|
|
|
|
ErrorCodes::ILLEGAL_COLUMN, "Expected array column in function {}, found {}", getName(), key_column->getName());
|
|
|
|
|
size_t start_offset = offsets[offset_index - 1];
|
|
|
|
|
size_t end_offset = offsets[offset_index];
|
|
|
|
|
|
|
|
|
|
in_keys_array = checkAndGetColumn<ColumnArray>(const_array->getDataColumnPtr().get());
|
|
|
|
|
key_is_const = true;
|
|
|
|
|
}
|
|
|
|
|
sorted_keys_values.clear();
|
|
|
|
|
|
|
|
|
|
const auto * val_column = arg_columns[1].get();
|
|
|
|
|
const auto * in_values_array = checkAndGetColumn<ColumnArray>(val_column);
|
|
|
|
|
if (!in_values_array)
|
|
|
|
|
{
|
|
|
|
|
const ColumnConst * const_array = checkAndGetColumnConst<ColumnArray>(val_column);
|
|
|
|
|
if (!const_array)
|
|
|
|
|
throw Exception(
|
|
|
|
|
ErrorCodes::ILLEGAL_COLUMN, "Expected array column in function {}, found {}", getName(), val_column->getName());
|
|
|
|
|
for (; start_offset < end_offset; ++start_offset)
|
|
|
|
|
sorted_keys_values.emplace_back(key_column_data[start_offset], value_column_data[start_offset]);
|
|
|
|
|
|
|
|
|
|
in_values_array = checkAndGetColumn<ColumnArray>(const_array->getDataColumnPtr().get());
|
|
|
|
|
val_is_const = true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!in_keys_array || !in_values_array)
|
|
|
|
|
/* something went wrong */
|
|
|
|
|
throw Exception(ErrorCodes::ILLEGAL_COLUMN, "Illegal columns in arguments of function " + getName());
|
|
|
|
|
|
|
|
|
|
const auto & in_keys_data = assert_cast<const ColumnVector<KeyType> &>(in_keys_array->getData()).getData();
|
|
|
|
|
const auto & in_values_data = assert_cast<const ColumnVector<ValType> &>(in_values_array->getData()).getData();
|
|
|
|
|
const auto & in_keys_offsets = in_keys_array->getOffsets();
|
|
|
|
|
const auto & in_vals_offsets = in_values_array->getOffsets();
|
|
|
|
|
|
|
|
|
|
auto & out_keys_data = assert_cast<ColumnVector<KeyType> &>(out_keys_array.getData()).getData();
|
|
|
|
|
auto & out_vals_data = assert_cast<ColumnVector<ValType> &>(out_vals_array.getData()).getData();
|
|
|
|
|
auto & out_keys_offsets = out_keys_array.getOffsets();
|
|
|
|
|
|
|
|
|
|
size_t row_count = key_is_const ? in_values_array->size() : in_keys_array->size();
|
|
|
|
|
IColumn::Offsets * out_vals_offsets = &out_vals_array.getOffsets();
|
|
|
|
|
|
|
|
|
|
return {
|
|
|
|
|
in_keys_data,
|
|
|
|
|
in_values_data,
|
|
|
|
|
in_keys_offsets,
|
|
|
|
|
in_vals_offsets,
|
|
|
|
|
row_count,
|
|
|
|
|
key_is_const,
|
|
|
|
|
val_is_const,
|
|
|
|
|
out_keys_data,
|
|
|
|
|
out_vals_data,
|
|
|
|
|
out_keys_offsets,
|
|
|
|
|
out_vals_offsets};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
template <typename KeyType, typename ValType>
|
|
|
|
|
ColumnsInOut<KeyType, ValType> getInOutDataFromMap(MutableColumnPtr & res_column, ColumnPtr * arg_columns) const
|
|
|
|
|
{
|
|
|
|
|
const auto * in_map = assert_cast<const ColumnMap *>(arg_columns[0].get());
|
|
|
|
|
const auto & in_nested_array = in_map->getNestedColumn();
|
|
|
|
|
const auto & in_nested_tuple = in_map->getNestedData();
|
|
|
|
|
const auto & in_keys_data = assert_cast<const ColumnVector<KeyType> &>(in_nested_tuple.getColumn(0)).getData();
|
|
|
|
|
const auto & in_vals_data = assert_cast<const ColumnVector<ValType> &>(in_nested_tuple.getColumn(1)).getData();
|
|
|
|
|
const auto & in_keys_offsets = in_nested_array.getOffsets();
|
|
|
|
|
|
|
|
|
|
auto * out_map = assert_cast<ColumnMap *>(res_column.get());
|
|
|
|
|
auto & out_nested_array = out_map->getNestedColumn();
|
|
|
|
|
auto & out_nested_tuple = out_map->getNestedData();
|
|
|
|
|
auto & out_keys_data = assert_cast<ColumnVector<KeyType> &>(out_nested_tuple.getColumn(0)).getData();
|
|
|
|
|
auto & out_vals_data = assert_cast<ColumnVector<ValType> &>(out_nested_tuple.getColumn(1)).getData();
|
|
|
|
|
auto & out_keys_offsets = out_nested_array.getOffsets();
|
|
|
|
|
|
|
|
|
|
return {
|
|
|
|
|
in_keys_data,
|
|
|
|
|
in_vals_data,
|
|
|
|
|
in_keys_offsets,
|
|
|
|
|
in_keys_offsets,
|
|
|
|
|
in_nested_array.size(),
|
|
|
|
|
false,
|
|
|
|
|
false,
|
|
|
|
|
out_keys_data,
|
|
|
|
|
out_vals_data,
|
|
|
|
|
out_keys_offsets,
|
|
|
|
|
nullptr};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
template <typename KeyType, typename ValType>
|
|
|
|
|
ColumnPtr execute2(ColumnPtr * arg_columns, ColumnPtr max_key_column, const DataTypePtr & res_type) const
|
|
|
|
|
{
|
|
|
|
|
MutableColumnPtr res_column = res_type->createColumn();
|
|
|
|
|
bool max_key_is_const = false;
|
|
|
|
|
auto columns = res_column->getDataType() == TypeIndex::Tuple ? getInOutDataFromArrays<KeyType, ValType>(res_column, arg_columns)
|
|
|
|
|
: getInOutDataFromMap<KeyType, ValType>(res_column, arg_columns);
|
|
|
|
|
|
|
|
|
|
KeyType max_key_const{0};
|
|
|
|
|
|
|
|
|
|
if (max_key_column && isColumnConst(*max_key_column))
|
|
|
|
|
{
|
|
|
|
|
const auto * column_const = static_cast<const ColumnConst *>(&*max_key_column);
|
|
|
|
|
max_key_const = column_const->template getValue<KeyType>();
|
|
|
|
|
max_key_is_const = true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
IColumn::Offset offset{0};
|
|
|
|
|
std::map<KeyType, ValType> res_map;
|
|
|
|
|
|
|
|
|
|
//Iterate through two arrays and fill result values.
|
|
|
|
|
for (size_t row = 0; row < columns.row_count; ++row)
|
|
|
|
|
{
|
|
|
|
|
size_t key_offset = 0, val_offset = 0, items_count = columns.in_key_offsets[0], val_array_size = columns.in_val_offsets[0];
|
|
|
|
|
|
|
|
|
|
res_map.clear();
|
|
|
|
|
|
|
|
|
|
if (!columns.key_is_const)
|
|
|
|
|
if unlikely(sorted_keys_values.empty())
|
|
|
|
|
{
|
|
|
|
|
key_offset = row > 0 ? columns.in_key_offsets[row - 1] : 0;
|
|
|
|
|
items_count = columns.in_key_offsets[row] - key_offset;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!columns.val_is_const)
|
|
|
|
|
{
|
|
|
|
|
val_offset = row > 0 ? columns.in_val_offsets[row - 1] : 0;
|
|
|
|
|
val_array_size = columns.in_val_offsets[row] - val_offset;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (items_count != val_array_size)
|
|
|
|
|
throw Exception(
|
|
|
|
|
ErrorCodes::NUMBER_OF_ARGUMENTS_DOESNT_MATCH,
|
|
|
|
|
"Key and value array should have same amount of elements in function {}",
|
|
|
|
|
getName());
|
|
|
|
|
|
|
|
|
|
if (items_count == 0)
|
|
|
|
|
{
|
|
|
|
|
columns.out_keys_offsets.push_back(offset);
|
|
|
|
|
result_offsets_data.emplace_back(result_value_data.size());
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for (size_t i = 0; i < items_count; ++i)
|
|
|
|
|
::sort(sorted_keys_values.begin(), sorted_keys_values.end());
|
|
|
|
|
|
|
|
|
|
KeyType min_key = sorted_keys_values.front().first;
|
|
|
|
|
KeyType max_key = sorted_keys_values.back().first;
|
|
|
|
|
|
|
|
|
|
if (max_key_data)
|
|
|
|
|
{
|
|
|
|
|
res_map.insert({columns.in_keys_data[key_offset + i], columns.in_vals_data[val_offset + i]});
|
|
|
|
|
}
|
|
|
|
|
max_key = (*max_key_data)[offset_index];
|
|
|
|
|
|
|
|
|
|
auto min_key = res_map.begin()->first;
|
|
|
|
|
auto max_key = res_map.rbegin()->first;
|
|
|
|
|
|
|
|
|
|
if (max_key_column)
|
|
|
|
|
{
|
|
|
|
|
/* update the current max key if it's not constant */
|
|
|
|
|
if (max_key_is_const)
|
|
|
|
|
if (unlikely(max_key < min_key))
|
|
|
|
|
{
|
|
|
|
|
max_key = max_key_const;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
max_key = (static_cast<const ColumnVector<KeyType> *>(max_key_column.get()))->getData()[row];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* no need to add anything, max key is less that first key */
|
|
|
|
|
if (max_key < min_key)
|
|
|
|
|
{
|
|
|
|
|
columns.out_keys_offsets.push_back(offset);
|
|
|
|
|
result_offsets_data.emplace_back(result_value_data.size());
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static constexpr size_t MAX_ARRAY_SIZE = 1ULL << 30;
|
|
|
|
|
if (static_cast<size_t>(max_key) - static_cast<size_t>(min_key) > MAX_ARRAY_SIZE)
|
|
|
|
|
throw Exception(ErrorCodes::TOO_LARGE_ARRAY_SIZE, "Too large array size in the result of function {}", getName());
|
|
|
|
|
using KeyTypeUnsigned = ::make_unsigned_t<KeyType>;
|
|
|
|
|
KeyTypeUnsigned max_min_key_difference = 0;
|
|
|
|
|
|
|
|
|
|
/* fill the result arrays */
|
|
|
|
|
KeyType key;
|
|
|
|
|
for (key = min_key;; ++key)
|
|
|
|
|
if constexpr (::is_unsigned_v<KeyType>)
|
|
|
|
|
{
|
|
|
|
|
columns.out_keys_data.push_back(key);
|
|
|
|
|
max_min_key_difference = max_key - min_key;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
bool is_max_key_positive = max_key >= 0;
|
|
|
|
|
bool is_min_key_positive = min_key >= 0;
|
|
|
|
|
|
|
|
|
|
auto it = res_map.find(key);
|
|
|
|
|
if (it != res_map.end())
|
|
|
|
|
if (is_max_key_positive && is_min_key_positive)
|
|
|
|
|
{
|
|
|
|
|
columns.out_vals_data.push_back(it->second);
|
|
|
|
|
max_min_key_difference = static_cast<KeyTypeUnsigned>(max_key - min_key);
|
|
|
|
|
}
|
|
|
|
|
else if (is_max_key_positive && !is_min_key_positive)
|
|
|
|
|
{
|
|
|
|
|
KeyTypeUnsigned min_key_unsigned = -static_cast<KeyTypeUnsigned>(min_key);
|
|
|
|
|
max_min_key_difference = static_cast<KeyTypeUnsigned>(max_key) + min_key_unsigned;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
columns.out_vals_data.push_back(0);
|
|
|
|
|
/// Both max and min key are negative
|
|
|
|
|
KeyTypeUnsigned min_key_unsigned = -static_cast<KeyTypeUnsigned>(min_key);
|
|
|
|
|
KeyTypeUnsigned max_key_unsigned = -static_cast<KeyTypeUnsigned>(max_key);
|
|
|
|
|
max_min_key_difference = min_key_unsigned - max_key_unsigned;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static constexpr size_t MAX_ARRAY_SIZE = 1ULL << 30;
|
|
|
|
|
if (max_min_key_difference > MAX_ARRAY_SIZE)
|
|
|
|
|
throw Exception(ErrorCodes::TOO_LARGE_ARRAY_SIZE,
|
|
|
|
|
"Function {} too large array size in the result",
|
|
|
|
|
getName());
|
|
|
|
|
|
|
|
|
|
size_t length = static_cast<size_t>(max_min_key_difference);
|
|
|
|
|
size_t result_key_data_size = result_key_data.size();
|
|
|
|
|
size_t result_value_data_size = result_value_data.size();
|
|
|
|
|
size_t sorted_keys_values_size = sorted_keys_values.size();
|
|
|
|
|
|
|
|
|
|
result_key_data.resize_fill(result_key_data_size + length + 1);
|
|
|
|
|
result_value_data.resize_fill(result_value_data_size + length + 1);
|
|
|
|
|
|
|
|
|
|
size_t sorted_values_index = 0;
|
|
|
|
|
|
|
|
|
|
for (KeyType current_key = min_key; current_key <= max_key; ++current_key)
|
|
|
|
|
{
|
|
|
|
|
size_t key_offset_index = current_key - min_key;
|
|
|
|
|
size_t insert_index = result_value_data_size + key_offset_index;
|
|
|
|
|
|
|
|
|
|
result_key_data[insert_index] = current_key;
|
|
|
|
|
|
|
|
|
|
if (sorted_values_index < sorted_keys_values_size &&
|
|
|
|
|
sorted_keys_values[sorted_values_index].first == current_key)
|
|
|
|
|
{
|
|
|
|
|
auto & sorted_key_value = sorted_keys_values[sorted_values_index];
|
|
|
|
|
if (current_key == sorted_key_value.first)
|
|
|
|
|
{
|
|
|
|
|
result_value_data[insert_index] = sorted_key_value.second;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
++sorted_values_index;
|
|
|
|
|
while (sorted_values_index < sorted_keys_values_size &&
|
|
|
|
|
current_key == sorted_keys_values[sorted_values_index].first)
|
|
|
|
|
{
|
|
|
|
|
++sorted_values_index;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
++offset;
|
|
|
|
|
if (key == max_key)
|
|
|
|
|
if (current_key == max_key)
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
columns.out_keys_offsets.push_back(offset);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (columns.out_vals_offsets)
|
|
|
|
|
columns.out_vals_offsets->insert(columns.out_keys_offsets.begin(), columns.out_keys_offsets.end());
|
|
|
|
|
|
|
|
|
|
return res_column;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
template <typename KeyType>
|
|
|
|
|
ColumnPtr execute1(ColumnPtr * arg_columns, ColumnPtr max_key_column, const DataTypePtr & res_type, const DataTypePtr & val_type) const
|
|
|
|
|
{
|
|
|
|
|
switch (val_type->getTypeId())
|
|
|
|
|
{
|
|
|
|
|
case TypeIndex::Int8:
|
|
|
|
|
return execute2<KeyType, Int8>(arg_columns, max_key_column, res_type);
|
|
|
|
|
case TypeIndex::Int16:
|
|
|
|
|
return execute2<KeyType, Int16>(arg_columns, max_key_column, res_type);
|
|
|
|
|
case TypeIndex::Int32:
|
|
|
|
|
return execute2<KeyType, Int32>(arg_columns, max_key_column, res_type);
|
|
|
|
|
case TypeIndex::Int64:
|
|
|
|
|
return execute2<KeyType, Int64>(arg_columns, max_key_column, res_type);
|
|
|
|
|
case TypeIndex::Int128:
|
|
|
|
|
return execute2<KeyType, Int128>(arg_columns, max_key_column, res_type);
|
|
|
|
|
case TypeIndex::Int256:
|
|
|
|
|
return execute2<KeyType, Int256>(arg_columns, max_key_column, res_type);
|
|
|
|
|
case TypeIndex::UInt8:
|
|
|
|
|
return execute2<KeyType, UInt8>(arg_columns, max_key_column, res_type);
|
|
|
|
|
case TypeIndex::UInt16:
|
|
|
|
|
return execute2<KeyType, UInt16>(arg_columns, max_key_column, res_type);
|
|
|
|
|
case TypeIndex::UInt32:
|
|
|
|
|
return execute2<KeyType, UInt32>(arg_columns, max_key_column, res_type);
|
|
|
|
|
case TypeIndex::UInt64:
|
|
|
|
|
return execute2<KeyType, UInt64>(arg_columns, max_key_column, res_type);
|
|
|
|
|
case TypeIndex::UInt128:
|
|
|
|
|
return execute2<KeyType, UInt128>(arg_columns, max_key_column, res_type);
|
|
|
|
|
case TypeIndex::UInt256:
|
|
|
|
|
return execute2<KeyType, UInt256>(arg_columns, max_key_column, res_type);
|
|
|
|
|
default:
|
|
|
|
|
throw Exception(ErrorCodes::ILLEGAL_COLUMN, "Illegal columns in arguments of function " + getName());
|
|
|
|
|
result_offsets_data.emplace_back(result_value_data.size());
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ColumnPtr executeImpl(const ColumnsWithTypeAndName & arguments, const DataTypePtr &, size_t) const override
|
|
|
|
|
struct KeyAndValueInput
|
|
|
|
|
{
|
|
|
|
|
DataTypePtr res_type, key_type, val_type;
|
|
|
|
|
ColumnPtr max_key_column = nullptr;
|
|
|
|
|
ColumnPtr arg_columns[] = {arguments[0].column, nullptr};
|
|
|
|
|
DataTypePtr key_series_type;
|
|
|
|
|
DataTypePtr value_series_type;
|
|
|
|
|
|
|
|
|
|
if (arguments[0].type->getTypeId() == TypeIndex::Array)
|
|
|
|
|
ColumnPtr key_column;
|
|
|
|
|
ColumnPtr value_column;
|
|
|
|
|
ColumnPtr offsets_column;
|
|
|
|
|
|
|
|
|
|
/// Optional max key column
|
|
|
|
|
ColumnPtr max_key_column;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
KeyAndValueInput extractKeyAndValueInput(const ColumnsWithTypeAndName & arguments) const
|
|
|
|
|
{
|
|
|
|
|
KeyAndValueInput input;
|
|
|
|
|
|
|
|
|
|
size_t max_key_argument_index = 0;
|
|
|
|
|
|
|
|
|
|
auto first_argument_column = arguments[0].column->convertToFullColumnIfConst();
|
|
|
|
|
ColumnPtr second_argument_array_column;
|
|
|
|
|
|
|
|
|
|
if (const auto * key_argument_array_column = typeid_cast<const ColumnArray *>(first_argument_column.get()))
|
|
|
|
|
{
|
|
|
|
|
key_type = assert_cast<const DataTypeArray *>(arguments[0].type.get())->getNestedType();
|
|
|
|
|
val_type = assert_cast<const DataTypeArray *>(arguments[1].type.get())->getNestedType();
|
|
|
|
|
res_type = getReturnTypeImpl(DataTypes{arguments[0].type, arguments[1].type});
|
|
|
|
|
const ColumnArray * value_argument_array_column = nullptr;
|
|
|
|
|
|
|
|
|
|
arg_columns[1] = arguments[1].column;
|
|
|
|
|
if (arguments.size() == 3)
|
|
|
|
|
if (1 < arguments.size())
|
|
|
|
|
{
|
|
|
|
|
/* max key provided */
|
|
|
|
|
max_key_column = arguments[2].column;
|
|
|
|
|
second_argument_array_column = arguments[1].column->convertToFullColumnIfConst();
|
|
|
|
|
value_argument_array_column = typeid_cast<const ColumnArray *>(second_argument_array_column.get());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!value_argument_array_column)
|
|
|
|
|
throw Exception(ErrorCodes::BAD_ARGUMENTS,
|
|
|
|
|
"Function {} if array argument is passed as key, additional array argument as value must be passed",
|
|
|
|
|
getName());
|
|
|
|
|
|
|
|
|
|
input.key_series_type = assert_cast<const DataTypeArray &>(*arguments[0].type).getNestedType();
|
|
|
|
|
input.key_column = key_argument_array_column->getDataPtr();
|
|
|
|
|
const auto & key_offsets = key_argument_array_column->getOffsets();
|
|
|
|
|
|
|
|
|
|
input.value_series_type = assert_cast<const DataTypeArray &>(*arguments[1].type).getNestedType();
|
|
|
|
|
input.value_column = value_argument_array_column->getDataPtr();
|
|
|
|
|
const auto & value_offsets = value_argument_array_column->getOffsets();
|
|
|
|
|
|
|
|
|
|
if (key_offsets != value_offsets)
|
|
|
|
|
throw Exception(
|
|
|
|
|
ErrorCodes::BAD_ARGUMENTS,
|
|
|
|
|
"Function {} key and value array should have same amount of elements",
|
|
|
|
|
getName());
|
|
|
|
|
|
|
|
|
|
input.offsets_column = key_argument_array_column->getOffsetsPtr();
|
|
|
|
|
max_key_argument_index = 2;
|
|
|
|
|
}
|
|
|
|
|
else if (const auto * key_argument_map_column = typeid_cast<const ColumnMap *>(first_argument_column.get()))
|
|
|
|
|
{
|
|
|
|
|
const auto & nested_array = key_argument_map_column->getNestedColumn();
|
|
|
|
|
const auto & nested_data_column = key_argument_map_column->getNestedData();
|
|
|
|
|
|
|
|
|
|
const auto & map_argument_type = assert_cast<const DataTypeMap &>(*arguments[0].type);
|
|
|
|
|
input.key_series_type = map_argument_type.getKeyType();
|
|
|
|
|
input.value_series_type = map_argument_type.getValueType();
|
|
|
|
|
|
|
|
|
|
input.key_column = nested_data_column.getColumnPtr(0);
|
|
|
|
|
input.value_column = nested_data_column.getColumnPtr(1);
|
|
|
|
|
input.offsets_column = nested_array.getOffsetsPtr();
|
|
|
|
|
|
|
|
|
|
max_key_argument_index = 1;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
throw Exception(
|
|
|
|
|
ErrorCodes::ILLEGAL_COLUMN,
|
|
|
|
|
"Function {} only accepts one map or arrays, but got {}",
|
|
|
|
|
getName(),
|
|
|
|
|
arguments[0].type->getName());
|
|
|
|
|
|
|
|
|
|
ColumnPtr max_key_column;
|
|
|
|
|
|
|
|
|
|
if (max_key_argument_index < arguments.size())
|
|
|
|
|
{
|
|
|
|
|
max_key_column = arguments[max_key_argument_index].column->convertToFullColumnIfConst();
|
|
|
|
|
auto max_key_column_type = arguments[max_key_argument_index].type;
|
|
|
|
|
|
|
|
|
|
if (!max_key_column_type->equals(*input.key_series_type))
|
|
|
|
|
{
|
|
|
|
|
ColumnWithTypeAndName column_to_cast = {max_key_column, max_key_column_type, ""};
|
|
|
|
|
auto casted_column = castColumnAccurate(std::move(column_to_cast), input.key_series_type);
|
|
|
|
|
max_key_column = std::move(casted_column);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
input.max_key_column = std::move(max_key_column);
|
|
|
|
|
|
|
|
|
|
return input;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
struct ResultColumns
|
|
|
|
|
{
|
|
|
|
|
MutableColumnPtr result_key_column;
|
|
|
|
|
MutableColumnPtr result_value_column;
|
|
|
|
|
MutableColumnPtr result_offset_column;
|
|
|
|
|
IColumn * result_offset_column_raw;
|
|
|
|
|
/// If we return tuple of two arrays, this offset need to be the same as result_offset_column
|
|
|
|
|
MutableColumnPtr result_array_additional_offset_column;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
ResultColumns extractResultColumns(MutableColumnPtr & result_column, const DataTypePtr & result_type) const
|
|
|
|
|
{
|
|
|
|
|
ResultColumns result;
|
|
|
|
|
|
|
|
|
|
auto * tuple_column = typeid_cast<ColumnTuple *>(result_column.get());
|
|
|
|
|
|
|
|
|
|
if (tuple_column && tuple_column->tupleSize() == 2)
|
|
|
|
|
{
|
|
|
|
|
auto key_array_column = tuple_column->getColumnPtr(0)->assumeMutable();
|
|
|
|
|
auto value_array_column = tuple_column->getColumnPtr(1)->assumeMutable();
|
|
|
|
|
|
|
|
|
|
auto * key_array_column_typed = typeid_cast<ColumnArray *>(key_array_column.get());
|
|
|
|
|
auto * value_array_column_typed = typeid_cast<ColumnArray *>(value_array_column.get());
|
|
|
|
|
|
|
|
|
|
if (!key_array_column_typed || !value_array_column_typed)
|
|
|
|
|
throw Exception(ErrorCodes::LOGICAL_ERROR,
|
|
|
|
|
"Function {} result type should be Tuple with two nested Array columns or Map. Actual {}",
|
|
|
|
|
getName(),
|
|
|
|
|
result_type->getName());
|
|
|
|
|
|
|
|
|
|
result.result_key_column = key_array_column_typed->getDataPtr()->assumeMutable();
|
|
|
|
|
result.result_value_column = value_array_column_typed->getDataPtr()->assumeMutable();
|
|
|
|
|
result.result_offset_column = key_array_column_typed->getOffsetsPtr()->assumeMutable();
|
|
|
|
|
result.result_offset_column_raw = result.result_offset_column.get();
|
|
|
|
|
result.result_array_additional_offset_column = value_array_column_typed->getOffsetsPtr()->assumeMutable();
|
|
|
|
|
}
|
|
|
|
|
else if (const auto * map_column = typeid_cast<ColumnMap *>(result_column.get()))
|
|
|
|
|
{
|
|
|
|
|
result.result_key_column = map_column->getNestedData().getColumnPtr(0)->assumeMutable();
|
|
|
|
|
result.result_value_column = map_column->getNestedData().getColumnPtr(1)->assumeMutable();
|
|
|
|
|
result.result_offset_column = map_column->getNestedColumn().getOffsetsPtr()->assumeMutable();
|
|
|
|
|
result.result_offset_column_raw = result.result_offset_column.get();
|
|
|
|
|
result.result_array_additional_offset_column = nullptr;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
assert(arguments[0].type->getTypeId() == TypeIndex::Map);
|
|
|
|
|
|
|
|
|
|
const auto * map_type = assert_cast<const DataTypeMap *>(arguments[0].type.get());
|
|
|
|
|
res_type = getReturnTypeImpl(DataTypes{arguments[0].type});
|
|
|
|
|
key_type = map_type->getKeyType();
|
|
|
|
|
val_type = map_type->getValueType();
|
|
|
|
|
|
|
|
|
|
if (arguments.size() == 2)
|
|
|
|
|
{
|
|
|
|
|
/* max key provided */
|
|
|
|
|
max_key_column = arguments[1].column;
|
|
|
|
|
}
|
|
|
|
|
throw Exception(ErrorCodes::LOGICAL_ERROR,
|
|
|
|
|
"Function {} result type should be Tuple with two nested Array columns or Map. Actual {}",
|
|
|
|
|
getName(),
|
|
|
|
|
result_type->getName());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
switch (key_type->getTypeId())
|
|
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ColumnPtr executeImpl(const ColumnsWithTypeAndName & arguments, const DataTypePtr & result_type, size_t) const override
|
|
|
|
|
{
|
|
|
|
|
auto input = extractKeyAndValueInput(arguments);
|
|
|
|
|
|
|
|
|
|
auto result_column = result_type->createColumn();
|
|
|
|
|
auto result_columns = extractResultColumns(result_column, result_type);
|
|
|
|
|
|
|
|
|
|
auto call = [&](const auto & types)
|
|
|
|
|
{
|
|
|
|
|
case TypeIndex::Int8:
|
|
|
|
|
return execute1<Int8>(arg_columns, max_key_column, res_type, val_type);
|
|
|
|
|
case TypeIndex::Int16:
|
|
|
|
|
return execute1<Int16>(arg_columns, max_key_column, res_type, val_type);
|
|
|
|
|
case TypeIndex::Int32:
|
|
|
|
|
return execute1<Int32>(arg_columns, max_key_column, res_type, val_type);
|
|
|
|
|
case TypeIndex::Int64:
|
|
|
|
|
return execute1<Int64>(arg_columns, max_key_column, res_type, val_type);
|
|
|
|
|
case TypeIndex::Int128:
|
|
|
|
|
return execute1<Int128>(arg_columns, max_key_column, res_type, val_type);
|
|
|
|
|
case TypeIndex::Int256:
|
|
|
|
|
return execute1<Int256>(arg_columns, max_key_column, res_type, val_type);
|
|
|
|
|
case TypeIndex::UInt8:
|
|
|
|
|
return execute1<UInt8>(arg_columns, max_key_column, res_type, val_type);
|
|
|
|
|
case TypeIndex::UInt16:
|
|
|
|
|
return execute1<UInt16>(arg_columns, max_key_column, res_type, val_type);
|
|
|
|
|
case TypeIndex::UInt32:
|
|
|
|
|
return execute1<UInt32>(arg_columns, max_key_column, res_type, val_type);
|
|
|
|
|
case TypeIndex::UInt64:
|
|
|
|
|
return execute1<UInt64>(arg_columns, max_key_column, res_type, val_type);
|
|
|
|
|
case TypeIndex::UInt128:
|
|
|
|
|
return execute1<UInt128>(arg_columns, max_key_column, res_type, val_type);
|
|
|
|
|
case TypeIndex::UInt256:
|
|
|
|
|
return execute1<UInt256>(arg_columns, max_key_column, res_type, val_type);
|
|
|
|
|
default:
|
|
|
|
|
throw Exception(ErrorCodes::ILLEGAL_COLUMN, "Illegal columns in arguments of function " + getName());
|
|
|
|
|
using Types = std::decay_t<decltype(types)>;
|
|
|
|
|
using KeyType = typename Types::LeftType;
|
|
|
|
|
using ValueType = typename Types::RightType;
|
|
|
|
|
|
|
|
|
|
static constexpr bool key_and_value_are_numbers = IsDataTypeNumber<KeyType> && IsDataTypeNumber<ValueType>;
|
|
|
|
|
static constexpr bool key_is_float = std::is_same_v<KeyType, DataTypeFloat32> || std::is_same_v<KeyType, DataTypeFloat64>;
|
|
|
|
|
|
|
|
|
|
if constexpr (key_and_value_are_numbers && !key_is_float)
|
|
|
|
|
{
|
|
|
|
|
using KeyFieldType = typename KeyType::FieldType;
|
|
|
|
|
using ValueFieldType = typename ValueType::FieldType;
|
|
|
|
|
|
|
|
|
|
executeImplTyped<KeyFieldType, ValueFieldType>(
|
|
|
|
|
input.key_column,
|
|
|
|
|
input.value_column,
|
|
|
|
|
input.offsets_column,
|
|
|
|
|
input.max_key_column,
|
|
|
|
|
std::move(result_columns.result_key_column),
|
|
|
|
|
std::move(result_columns.result_value_column),
|
|
|
|
|
std::move(result_columns.result_offset_column));
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return false;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
if (!callOnTwoTypeIndexes(input.key_series_type->getTypeId(), input.value_series_type->getTypeId(), call))
|
|
|
|
|
throw Exception(ErrorCodes::ILLEGAL_COLUMN,
|
|
|
|
|
"Function {} illegal columns passed as arguments",
|
|
|
|
|
getName());
|
|
|
|
|
|
|
|
|
|
if (result_columns.result_array_additional_offset_column)
|
|
|
|
|
{
|
|
|
|
|
result_columns.result_array_additional_offset_column->insertRangeFrom(
|
|
|
|
|
*result_columns.result_offset_column_raw,
|
|
|
|
|
0,
|
|
|
|
|
result_columns.result_offset_column_raw->size());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return result_column;
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
@ -451,4 +501,5 @@ void registerFunctionMapPopulateSeries(FunctionFactory & factory)
|
|
|
|
|
{
|
|
|
|
|
factory.registerFunction<FunctionMapPopulateSeries>();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|