ClickHouse/src/Functions/array/range.cpp

445 lines
19 KiB
C++
Raw Normal View History

#include <Functions/IFunctionImpl.h>
2018-09-09 20:57:54 +00:00
#include <Functions/FunctionFactory.h>
#include <Functions/FunctionHelpers.h>
2019-10-29 03:03:47 +00:00
#include <DataTypes/DataTypesNumber.h>
2018-09-09 20:57:54 +00:00
#include <DataTypes/DataTypeArray.h>
2019-11-06 10:19:08 +00:00
#include <DataTypes/getLeastSupertype.h>
2018-09-09 20:57:54 +00:00
#include <Columns/ColumnArray.h>
#include <Columns/ColumnVector.h>
2019-11-06 10:19:08 +00:00
#include <Interpreters/castColumn.h>
2018-09-09 21:15:40 +00:00
#include <numeric>
2018-09-09 20:57:54 +00:00
namespace DB
{
namespace ErrorCodes
{
extern const int ARGUMENT_OUT_OF_BOUND;
extern const int ILLEGAL_COLUMN;
extern const int ILLEGAL_TYPE_OF_ARGUMENT;
2019-10-29 03:03:47 +00:00
extern const int NUMBER_OF_ARGUMENTS_DOESNT_MATCH;
2018-09-09 20:57:54 +00:00
}
class FunctionRange : public IFunction
{
public:
static constexpr auto name = "range";
2019-11-05 07:07:25 +00:00
static constexpr size_t max_elements = 100'000'000;
2020-04-14 21:05:45 +00:00
static FunctionPtr create(const Context &) { return std::make_shared<FunctionRange>(); }
2018-09-09 20:57:54 +00:00
private:
String getName() const override { return name; }
2019-10-29 03:03:47 +00:00
size_t getNumberOfArguments() const override { return 0; }
bool isVariadic() const override { return true; }
2018-09-09 20:57:54 +00:00
bool useDefaultImplementationForConstants() const override { return true; }
DataTypePtr getReturnTypeImpl(const DataTypes & arguments) const override
{
if (arguments.size() > 3 || arguments.empty())
2019-10-29 03:03:47 +00:00
{
2019-10-30 20:39:29 +00:00
throw Exception{"Function " + getName() + " needs 1..3 arguments; passed "
2019-10-29 03:03:47 +00:00
+ std::to_string(arguments.size()) + ".",
ErrorCodes::NUMBER_OF_ARGUMENTS_DOESNT_MATCH};
}
2018-09-09 20:57:54 +00:00
2019-10-29 03:03:47 +00:00
for (const auto & arg : arguments)
{
if (!isUnsignedInteger(arg))
throw Exception{"Illegal type " + arg->getName() + " of argument of function " + getName(),
ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT};
}
2019-11-06 10:19:08 +00:00
DataTypePtr common_type = getLeastSupertype(arguments);
return std::make_shared<DataTypeArray>(common_type);
2018-09-09 20:57:54 +00:00
}
2019-11-05 07:07:25 +00:00
template <typename T>
2020-10-14 14:04:50 +00:00
bool executeInternal(ColumnsWithTypeAndName & columns, const IColumn * arg, const size_t result) const
2019-11-05 07:07:25 +00:00
{
if (const auto in = checkAndGetColumn<ColumnVector<T>>(arg))
{
const auto & in_data = in->getData();
const auto total_values = std::accumulate(std::begin(in_data), std::end(in_data), size_t{},
[this] (const size_t lhs, const size_t rhs)
{
const auto sum = lhs + rhs;
if (sum < lhs)
throw Exception{"A call to function " + getName() + " overflows, investigate the values of arguments you are passing",
ErrorCodes::ARGUMENT_OUT_OF_BOUND};
return sum;
});
if (total_values > max_elements)
throw Exception{"A call to function " + getName() + " would produce " + std::to_string(total_values) +
" array elements, which is greater than the allowed maximum of " + std::to_string(max_elements),
ErrorCodes::ARGUMENT_OUT_OF_BOUND};
auto data_col = ColumnVector<T>::create(total_values);
auto offsets_col = ColumnArray::ColumnOffsets::create(in->size());
auto & out_data = data_col->getData();
auto & out_offsets = offsets_col->getData();
IColumn::Offset offset{};
for (size_t row_idx = 0, rows = in->size(); row_idx < rows; ++row_idx)
{
for (size_t elem_idx = 0, elems = in_data[row_idx]; elem_idx < elems; ++elem_idx)
out_data[offset + elem_idx] = elem_idx;
offset += in_data[row_idx];
out_offsets[row_idx] = offset;
}
2020-10-14 14:04:50 +00:00
columns[result].column = ColumnArray::create(std::move(data_col), std::move(offsets_col));
2019-11-05 07:07:25 +00:00
return true;
}
else
return false;
}
2019-11-06 10:19:08 +00:00
template <typename T>
2020-08-15 09:13:52 +00:00
bool executeConstStartStep(
2020-10-14 14:04:50 +00:00
ColumnsWithTypeAndName & columns, const IColumn * end_arg, const T start, const T step, const size_t input_rows_count, const size_t result) const
2019-11-06 10:19:08 +00:00
{
auto end_column = checkAndGetColumn<ColumnVector<T>>(end_arg);
if (!end_column)
{
return false;
}
const auto & end_data = end_column->getData();
size_t total_values = 0;
size_t pre_values = 0;
for (size_t row_idx = 0; row_idx < input_rows_count; ++row_idx)
{
if (start < end_data[row_idx] && step == 0)
throw Exception{"A call to function " + getName() + " overflows, the 3rd argument step can't be zero",
ErrorCodes::ARGUMENT_OUT_OF_BOUND};
pre_values += start >= end_data[row_idx] ? 0
: (end_data[row_idx] - start - 1) / step + 1;
if (pre_values < total_values)
throw Exception{"A call to function " + getName() + " overflows, investigate the values of arguments you are passing",
ErrorCodes::ARGUMENT_OUT_OF_BOUND};
total_values = pre_values;
if (total_values > max_elements)
throw Exception{"A call to function " + getName() + " would produce " + std::to_string(total_values) +
" array elements, which is greater than the allowed maximum of " + std::to_string(max_elements),
ErrorCodes::ARGUMENT_OUT_OF_BOUND};
}
auto data_col = ColumnVector<T>::create(total_values);
auto offsets_col = ColumnArray::ColumnOffsets::create(end_column->size());
auto & out_data = data_col->getData();
auto & out_offsets = offsets_col->getData();
IColumn::Offset offset{};
for (size_t row_idx = 0; row_idx < input_rows_count; ++row_idx)
{
for (size_t st = start, ed = end_data[row_idx]; st < ed; st += step)
2020-08-15 21:14:15 +00:00
{
2019-11-06 10:19:08 +00:00
out_data[offset++] = st;
2020-08-15 21:14:15 +00:00
if (st > st + step)
throw Exception{"A call to function " + getName() + " overflows, investigate the values of arguments you are passing",
ErrorCodes::ARGUMENT_OUT_OF_BOUND};
}
2019-11-06 10:19:08 +00:00
out_offsets[row_idx] = offset;
}
2020-10-14 14:04:50 +00:00
columns[result].column = ColumnArray::create(std::move(data_col), std::move(offsets_col));
2019-11-06 10:19:08 +00:00
return true;
}
template <typename T>
2020-08-15 09:13:52 +00:00
bool executeConstStep(
2020-10-14 14:04:50 +00:00
ColumnsWithTypeAndName & columns, const IColumn * start_arg, const IColumn * end_arg, const T step, const size_t input_rows_count, const size_t result) const
2018-09-09 20:57:54 +00:00
{
2019-11-06 10:19:08 +00:00
auto start_column = checkAndGetColumn<ColumnVector<T>>(start_arg);
auto end_column = checkAndGetColumn<ColumnVector<T>>(end_arg);
if (!end_column || !start_column)
{
return false;
}
const auto & start_data = start_column->getData();
const auto & end_data = end_column->getData();
size_t total_values = 0;
size_t pre_values = 0;
for (size_t row_idx = 0; row_idx < input_rows_count; ++row_idx)
{
if (start_data[row_idx] < end_data[row_idx] && step == 0)
throw Exception{"A call to function " + getName() + " overflows, the 3rd argument step can't be zero",
ErrorCodes::ARGUMENT_OUT_OF_BOUND};
pre_values += start_data[row_idx] >= end_data[row_idx] ? 0
: (end_data[row_idx] - start_data[row_idx] - 1) / step + 1;
if (pre_values < total_values)
throw Exception{"A call to function " + getName() + " overflows, investigate the values of arguments you are passing",
ErrorCodes::ARGUMENT_OUT_OF_BOUND};
total_values = pre_values;
if (total_values > max_elements)
throw Exception{"A call to function " + getName() + " would produce " + std::to_string(total_values) +
" array elements, which is greater than the allowed maximum of " + std::to_string(max_elements),
ErrorCodes::ARGUMENT_OUT_OF_BOUND};
}
auto data_col = ColumnVector<T>::create(total_values);
auto offsets_col = ColumnArray::ColumnOffsets::create(end_column->size());
auto & out_data = data_col->getData();
auto & out_offsets = offsets_col->getData();
IColumn::Offset offset{};
for (size_t row_idx = 0; row_idx < input_rows_count; ++row_idx)
{
for (size_t st = start_data[row_idx], ed = end_data[row_idx]; st < ed; st += step)
2020-08-15 21:14:15 +00:00
{
2019-11-06 10:19:08 +00:00
out_data[offset++] = st;
2020-08-15 21:14:15 +00:00
if (st > st + step)
throw Exception{"A call to function " + getName() + " overflows, investigate the values of arguments you are passing",
ErrorCodes::ARGUMENT_OUT_OF_BOUND};
}
2019-11-06 10:19:08 +00:00
out_offsets[row_idx] = offset;
}
2020-10-14 14:04:50 +00:00
columns[result].column = ColumnArray::create(std::move(data_col), std::move(offsets_col));
2019-11-06 10:19:08 +00:00
return true;
}
template <typename T>
2020-08-15 09:13:52 +00:00
bool executeConstStart(
2020-10-14 14:04:50 +00:00
ColumnsWithTypeAndName & columns, const IColumn * end_arg, const IColumn * step_arg, const T start, const size_t input_rows_count, const size_t result) const
2019-11-06 10:19:08 +00:00
{
auto end_column = checkAndGetColumn<ColumnVector<T>>(end_arg);
auto step_column = checkAndGetColumn<ColumnVector<T>>(step_arg);
if (!end_column || !step_column)
{
return false;
}
const auto & end_data = end_column->getData();
const auto & step_data = step_column->getData();
size_t total_values = 0;
size_t pre_values = 0;
for (size_t row_idx = 0; row_idx < input_rows_count; ++row_idx)
{
if (start < end_data[row_idx] && step_data[row_idx] == 0)
throw Exception{"A call to function " + getName() + " overflows, the 3rd argument step can't be zero",
ErrorCodes::ARGUMENT_OUT_OF_BOUND};
pre_values += start >= end_data[row_idx] ? 0
: (end_data[row_idx] - start - 1) / step_data[row_idx] + 1;
if (pre_values < total_values)
throw Exception{"A call to function " + getName() + " overflows, investigate the values of arguments you are passing",
ErrorCodes::ARGUMENT_OUT_OF_BOUND};
total_values = pre_values;
if (total_values > max_elements)
throw Exception{"A call to function " + getName() + " would produce " + std::to_string(total_values) +
" array elements, which is greater than the allowed maximum of " + std::to_string(max_elements),
ErrorCodes::ARGUMENT_OUT_OF_BOUND};
}
auto data_col = ColumnVector<T>::create(total_values);
auto offsets_col = ColumnArray::ColumnOffsets::create(end_column->size());
auto & out_data = data_col->getData();
auto & out_offsets = offsets_col->getData();
IColumn::Offset offset{};
for (size_t row_idx = 0; row_idx < input_rows_count; ++row_idx)
{
for (size_t st = start, ed = end_data[row_idx]; st < ed; st += step_data[row_idx])
2020-08-15 21:14:15 +00:00
{
2019-11-06 10:19:08 +00:00
out_data[offset++] = st;
2020-08-15 21:14:15 +00:00
if (st > st + step_data[row_idx])
throw Exception{"A call to function " + getName() + " overflows, investigate the values of arguments you are passing",
ErrorCodes::ARGUMENT_OUT_OF_BOUND};
}
2019-11-06 10:19:08 +00:00
out_offsets[row_idx] = offset;
}
2020-10-14 14:04:50 +00:00
columns[result].column = ColumnArray::create(std::move(data_col), std::move(offsets_col));
2019-11-06 10:19:08 +00:00
return true;
}
template <typename T>
2020-08-15 09:13:52 +00:00
bool executeGeneric(
2020-10-14 14:44:22 +00:00
ColumnsWithTypeAndName & block, const IColumn * start_col, const IColumn * end_col, const IColumn * step_col,
const size_t input_rows_count, const size_t result) const
2019-11-06 10:19:08 +00:00
{
auto start_column = checkAndGetColumn<ColumnVector<T>>(start_col);
auto end_column = checkAndGetColumn<ColumnVector<T>>(end_col);
auto step_column = checkAndGetColumn<ColumnVector<T>>(step_col);
if (!start_column || !end_column || !step_column)
2018-09-09 20:57:54 +00:00
{
2019-10-29 03:03:47 +00:00
return false;
}
2018-09-09 20:57:54 +00:00
const auto & start_data = start_column->getData();
const auto & end_start = end_column->getData();
const auto & step_data = step_column->getData();
2019-10-29 03:03:47 +00:00
size_t total_values = 0;
size_t pre_values = 0;
2019-10-29 03:03:47 +00:00
2019-10-31 04:59:50 +00:00
for (size_t row_idx = 0; row_idx < input_rows_count; ++row_idx)
2019-10-29 03:03:47 +00:00
{
2019-10-30 02:06:30 +00:00
if (start_data[row_idx] < end_start[row_idx] && step_data[row_idx] == 0)
2019-10-30 01:36:50 +00:00
throw Exception{"A call to function " + getName() + " overflows, the 3rd argument step can't be zero",
ErrorCodes::ARGUMENT_OUT_OF_BOUND};
pre_values += start_data[row_idx] >= end_start[row_idx] ? 0
: (end_start[row_idx] -start_data[row_idx] - 1) / (step_data[row_idx]) + 1;
if (pre_values < total_values)
throw Exception{"A call to function " + getName() + " overflows, investigate the values of arguments you are passing",
ErrorCodes::ARGUMENT_OUT_OF_BOUND};
total_values = pre_values;
2018-09-09 20:57:54 +00:00
if (total_values > max_elements)
throw Exception{"A call to function " + getName() + " would produce " + std::to_string(total_values) +
" array elements, which is greater than the allowed maximum of " + std::to_string(max_elements),
ErrorCodes::ARGUMENT_OUT_OF_BOUND};
2019-10-29 03:03:47 +00:00
}
2018-09-09 20:57:54 +00:00
2019-11-06 10:19:08 +00:00
auto data_col = ColumnVector<T>::create(total_values);
2019-10-29 03:03:47 +00:00
auto offsets_col = ColumnArray::ColumnOffsets::create(end_column->size());
2018-09-09 20:57:54 +00:00
2019-10-29 03:03:47 +00:00
auto & out_data = data_col->getData();
auto & out_offsets = offsets_col->getData();
2018-09-09 20:57:54 +00:00
2019-10-29 03:03:47 +00:00
IColumn::Offset offset{};
2019-10-31 04:59:50 +00:00
for (size_t row_idx = 0; row_idx < input_rows_count; ++row_idx)
2019-10-29 03:03:47 +00:00
{
for (size_t st = start_data[row_idx], ed = end_start[row_idx]; st < ed; st += step_data[row_idx])
2020-08-15 21:14:15 +00:00
{
2019-10-29 03:03:47 +00:00
out_data[offset++] = st;
2018-09-09 20:57:54 +00:00
2020-08-15 21:14:15 +00:00
if (st > st + step_data[row_idx])
throw Exception{"A call to function " + getName() + " overflows, investigate the values of arguments you are passing",
ErrorCodes::ARGUMENT_OUT_OF_BOUND};
}
2019-10-29 03:03:47 +00:00
out_offsets[row_idx] = offset;
2018-09-09 20:57:54 +00:00
}
2020-10-14 14:44:22 +00:00
block[result].column = ColumnArray::create(std::move(data_col), std::move(offsets_col));
2019-10-29 03:03:47 +00:00
return true;
}
2020-10-14 14:04:50 +00:00
void executeImpl(ColumnsWithTypeAndName & columns, const ColumnNumbers & arguments, size_t result, size_t input_rows_count) const override
{
2019-10-29 05:01:45 +00:00
if (arguments.size() == 1)
{
2020-10-14 14:04:50 +00:00
const auto * col = columns[arguments[0]].column.get();
if (!executeInternal<UInt8>(columns, col, result) &&
!executeInternal<UInt16>(columns, col, result) &&
!executeInternal<UInt32>(columns, col, result) &&
!executeInternal<UInt64>(columns, col, result))
2019-11-05 07:07:25 +00:00
{
throw Exception{"Illegal column " + col->getName() + " of argument of function " + getName(), ErrorCodes::ILLEGAL_COLUMN};
}
return;
2019-10-29 05:01:45 +00:00
}
2019-11-06 10:19:08 +00:00
Columns columns_holder(3);
2020-10-14 14:44:22 +00:00
ColumnRawPtrs column_ptrs(3);
2019-11-06 10:19:08 +00:00
2020-10-14 14:04:50 +00:00
const auto return_type = checkAndGetDataType<DataTypeArray>(columns[result].type.get())->getNestedType();
2019-11-06 10:19:08 +00:00
2019-10-29 05:01:45 +00:00
for (size_t i = 0; i < arguments.size(); ++i)
{
2019-11-06 10:19:08 +00:00
if (i == 1)
2020-10-14 14:04:50 +00:00
columns_holder[i] = castColumn(columns[arguments[i]], return_type)->convertToFullColumnIfConst();
2019-11-06 10:19:08 +00:00
else
2020-10-14 14:04:50 +00:00
columns_holder[i] = castColumn(columns[arguments[i]], return_type);
2019-11-06 10:19:08 +00:00
2020-10-14 14:44:22 +00:00
column_ptrs[i] = columns_holder[i].get();
2019-10-29 05:01:45 +00:00
}
// for step column, defaults to 1
2019-11-05 07:07:25 +00:00
if (arguments.size() == 2)
{
2019-11-06 10:19:08 +00:00
columns_holder[2] = return_type->createColumnConst(input_rows_count, 1);
2020-10-14 14:44:22 +00:00
column_ptrs[2] = columns_holder[2].get();
}
2020-10-14 14:44:22 +00:00
bool is_start_const = isColumnConst(*column_ptrs[0]);
bool is_step_const = isColumnConst(*column_ptrs[2]);
2019-11-06 10:19:08 +00:00
bool ok;
if (is_start_const && is_step_const)
{
2020-10-14 14:44:22 +00:00
UInt64 start = assert_cast<const ColumnConst &>(*column_ptrs[0]).getUInt(0);
UInt64 step = assert_cast<const ColumnConst &>(*column_ptrs[2]).getUInt(0);
2019-11-06 10:19:08 +00:00
2020-10-14 14:44:22 +00:00
ok = executeConstStartStep<UInt8>(columns, column_ptrs[1], start, step, input_rows_count, result) ||
executeConstStartStep<UInt16>(columns, column_ptrs[1], start, step, input_rows_count, result) ||
executeConstStartStep<UInt32>(columns, column_ptrs[1], start, step, input_rows_count, result) ||
executeConstStartStep<UInt64>(columns, column_ptrs[1], start, step, input_rows_count, result);
2019-11-06 10:19:08 +00:00
}
else if (is_start_const && !is_step_const)
{
2020-10-14 14:44:22 +00:00
UInt64 start = assert_cast<const ColumnConst &>(*column_ptrs[0]).getUInt(0);
2019-11-06 10:19:08 +00:00
2020-10-14 14:44:22 +00:00
ok = executeConstStart<UInt8>(columns, column_ptrs[1], column_ptrs[2], start, input_rows_count, result) ||
executeConstStart<UInt16>(columns, column_ptrs[1], column_ptrs[2], start, input_rows_count, result) ||
executeConstStart<UInt32>(columns, column_ptrs[1], column_ptrs[2], start, input_rows_count, result) ||
executeConstStart<UInt64>(columns, column_ptrs[1], column_ptrs[2], start, input_rows_count, result);
2019-11-06 10:19:08 +00:00
}
else if (!is_start_const && is_step_const)
{
2020-10-14 14:44:22 +00:00
UInt64 step = assert_cast<const ColumnConst &>(*column_ptrs[2]).getUInt(0);
2019-11-06 10:19:08 +00:00
2020-10-14 14:44:22 +00:00
ok = executeConstStep<UInt8>(columns, column_ptrs[0], column_ptrs[1], step, input_rows_count, result) ||
executeConstStep<UInt16>(columns, column_ptrs[0], column_ptrs[1], step, input_rows_count, result) ||
executeConstStep<UInt32>(columns, column_ptrs[0], column_ptrs[1], step, input_rows_count, result) ||
executeConstStep<UInt64>(columns, column_ptrs[0], column_ptrs[1], step, input_rows_count, result);
2019-11-06 10:19:08 +00:00
}
else
{
2020-10-14 14:44:22 +00:00
ok = executeGeneric<UInt8>(columns, column_ptrs[0], column_ptrs[1], column_ptrs[2], input_rows_count, result) ||
executeGeneric<UInt16>(columns, column_ptrs[0], column_ptrs[1], column_ptrs[2], input_rows_count, result) ||
executeGeneric<UInt32>(columns, column_ptrs[0], column_ptrs[1], column_ptrs[2], input_rows_count, result) ||
executeGeneric<UInt64>(columns, column_ptrs[0], column_ptrs[1], column_ptrs[2], input_rows_count, result);
2019-11-06 10:19:08 +00:00
}
if (!ok)
2018-09-09 20:57:54 +00:00
{
2020-10-14 14:44:22 +00:00
throw Exception{"Illegal columns " + column_ptrs[0]->getName() + " of argument of function " + getName(), ErrorCodes::ILLEGAL_COLUMN};
2018-09-09 20:57:54 +00:00
}
}
2019-10-29 03:03:47 +00:00
2018-09-09 20:57:54 +00:00
};
void registerFunctionRange(FunctionFactory & factory)
{
factory.registerFunction<FunctionRange>();
}
}