mirror of
https://github.com/ClickHouse/ClickHouse.git
synced 2024-09-20 00:30:49 +00:00
Merge pull request #42195 from evillique/tuple-of-intervals
Better INTERVAL parsing and execution
This commit is contained in:
commit
09aca50c3b
@ -1331,7 +1331,7 @@ public:
|
||||
}
|
||||
|
||||
template <typename DateOrTime>
|
||||
inline auto addQuarters(DateOrTime d, Int64 delta) const
|
||||
inline auto NO_SANITIZE_UNDEFINED addQuarters(DateOrTime d, Int64 delta) const
|
||||
{
|
||||
return addMonths(d, delta * 3);
|
||||
}
|
||||
|
@ -411,6 +411,7 @@ inline bool isDecimal(const DataTypePtr & data_type) { return WhichDataType(data
|
||||
inline bool isTuple(const DataTypePtr & data_type) { return WhichDataType(data_type).isTuple(); }
|
||||
inline bool isArray(const DataTypePtr & data_type) { return WhichDataType(data_type).isArray(); }
|
||||
inline bool isMap(const DataTypePtr & data_type) {return WhichDataType(data_type).isMap(); }
|
||||
inline bool isInterval(const DataTypePtr & data_type) {return WhichDataType(data_type).isInterval(); }
|
||||
inline bool isNothing(const DataTypePtr & data_type) { return WhichDataType(data_type).isNothing(); }
|
||||
inline bool isUUID(const DataTypePtr & data_type) { return WhichDataType(data_type).isUUID(); }
|
||||
|
||||
|
@ -22,6 +22,7 @@
|
||||
#include <DataTypes/DataTypeFactory.h>
|
||||
#include <DataTypes/DataTypeFixedString.h>
|
||||
#include <DataTypes/DataTypeInterval.h>
|
||||
#include <DataTypes/DataTypeTuple.h>
|
||||
#include <DataTypes/DataTypeString.h>
|
||||
#include <DataTypes/DataTypesDecimal.h>
|
||||
#include <DataTypes/DataTypesNumber.h>
|
||||
@ -642,7 +643,8 @@ class FunctionBinaryArithmetic : public IFunction
|
||||
DataTypeInt8, DataTypeInt16, DataTypeInt32, DataTypeInt64, DataTypeInt128, DataTypeInt256,
|
||||
DataTypeDecimal32, DataTypeDecimal64, DataTypeDecimal128, DataTypeDecimal256,
|
||||
DataTypeDate, DataTypeDateTime,
|
||||
DataTypeFixedString, DataTypeString>;
|
||||
DataTypeFixedString, DataTypeString,
|
||||
DataTypeInterval>;
|
||||
|
||||
using Floats = TypeList<DataTypeFloat32, DataTypeFloat64>;
|
||||
|
||||
@ -717,6 +719,82 @@ class FunctionBinaryArithmetic : public IFunction
|
||||
return FunctionFactory::instance().get(function_name, context);
|
||||
}
|
||||
|
||||
static FunctionOverloadResolverPtr
|
||||
getFunctionForDateTupleOfIntervalsArithmetic(const DataTypePtr & type0, const DataTypePtr & type1, ContextPtr context)
|
||||
{
|
||||
bool first_is_date_or_datetime = isDateOrDate32(type0) || isDateTime(type0) || isDateTime64(type0);
|
||||
bool second_is_date_or_datetime = isDateOrDate32(type1) || isDateTime(type1) || isDateTime64(type1);
|
||||
|
||||
/// Exactly one argument must be Date or DateTime
|
||||
if (first_is_date_or_datetime == second_is_date_or_datetime)
|
||||
return {};
|
||||
|
||||
if (!isTuple(type0) && !isTuple(type1))
|
||||
return {};
|
||||
|
||||
/// Special case when the function is plus or minus, one of arguments is Date/DateTime and another is Tuple.
|
||||
/// We construct another function and call it.
|
||||
if constexpr (!is_plus && !is_minus)
|
||||
return {};
|
||||
|
||||
if (isTuple(type0) && second_is_date_or_datetime && is_minus)
|
||||
throw Exception("Wrong order of arguments for function " + String(name) + ": argument of Tuple type cannot be first",
|
||||
ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT);
|
||||
|
||||
std::string function_name;
|
||||
if (is_plus)
|
||||
{
|
||||
function_name = "addTupleOfIntervals";
|
||||
}
|
||||
else
|
||||
{
|
||||
function_name = "subtractTupleOfIntervals";
|
||||
}
|
||||
|
||||
return FunctionFactory::instance().get(function_name, context);
|
||||
}
|
||||
|
||||
static FunctionOverloadResolverPtr
|
||||
getFunctionForMergeIntervalsArithmetic(const DataTypePtr & type0, const DataTypePtr & type1, ContextPtr context)
|
||||
{
|
||||
/// Special case when the function is plus or minus, first argument is Interval or Tuple of Intervals
|
||||
/// and the second argument is the Interval of a different kind.
|
||||
/// We construct another function (example: addIntervals) and call it
|
||||
|
||||
if constexpr (!is_plus && !is_minus)
|
||||
return {};
|
||||
|
||||
const auto * tuple_data_type_0 = checkAndGetDataType<DataTypeTuple>(type0.get());
|
||||
const auto * interval_data_type_0 = checkAndGetDataType<DataTypeInterval>(type0.get());
|
||||
const auto * interval_data_type_1 = checkAndGetDataType<DataTypeInterval>(type1.get());
|
||||
|
||||
if ((!tuple_data_type_0 && !interval_data_type_0) || !interval_data_type_1)
|
||||
return {};
|
||||
|
||||
if (interval_data_type_0 && interval_data_type_0->equals(*interval_data_type_1))
|
||||
return {};
|
||||
|
||||
if (tuple_data_type_0)
|
||||
{
|
||||
auto & tuple_types = tuple_data_type_0->getElements();
|
||||
for (auto & type : tuple_types)
|
||||
if (!isInterval(type))
|
||||
return {};
|
||||
}
|
||||
|
||||
std::string function_name;
|
||||
if (is_plus)
|
||||
{
|
||||
function_name = "addInterval";
|
||||
}
|
||||
else
|
||||
{
|
||||
function_name = "subtractInterval";
|
||||
}
|
||||
|
||||
return FunctionFactory::instance().get(function_name, context);
|
||||
}
|
||||
|
||||
static FunctionOverloadResolverPtr
|
||||
getFunctionForTupleArithmetic(const DataTypePtr & type0, const DataTypePtr & type1, ContextPtr context)
|
||||
{
|
||||
@ -915,6 +993,30 @@ class FunctionBinaryArithmetic : public IFunction
|
||||
return function->execute(new_arguments, result_type, input_rows_count);
|
||||
}
|
||||
|
||||
ColumnPtr executeDateTimeTupleOfIntervalsPlusMinus(const ColumnsWithTypeAndName & arguments, const DataTypePtr & result_type,
|
||||
size_t input_rows_count, const FunctionOverloadResolverPtr & function_builder) const
|
||||
{
|
||||
ColumnsWithTypeAndName new_arguments = arguments;
|
||||
|
||||
/// Tuple argument must be second.
|
||||
if (isTuple(arguments[0].type))
|
||||
std::swap(new_arguments[0], new_arguments[1]);
|
||||
|
||||
auto function = function_builder->build(new_arguments);
|
||||
|
||||
return function->execute(new_arguments, result_type, input_rows_count);
|
||||
}
|
||||
|
||||
ColumnPtr executeIntervalTupleOfIntervalsPlusMinus(const ColumnsWithTypeAndName & arguments, const DataTypePtr & result_type,
|
||||
size_t input_rows_count, const FunctionOverloadResolverPtr & function_builder) const
|
||||
{
|
||||
ColumnsWithTypeAndName new_arguments = arguments;
|
||||
|
||||
auto function = function_builder->build(new_arguments);
|
||||
|
||||
return function->execute(new_arguments, result_type, input_rows_count);
|
||||
}
|
||||
|
||||
ColumnPtr executeTupleNumberOperator(const ColumnsWithTypeAndName & arguments, const DataTypePtr & result_type,
|
||||
size_t input_rows_count, const FunctionOverloadResolverPtr & function_builder) const
|
||||
{
|
||||
@ -1134,6 +1236,34 @@ public:
|
||||
return function->getResultType();
|
||||
}
|
||||
|
||||
/// Special case when the function is plus or minus, one of arguments is Date/DateTime and another is Tuple.
|
||||
if (auto function_builder = getFunctionForDateTupleOfIntervalsArithmetic(arguments[0], arguments[1], context))
|
||||
{
|
||||
ColumnsWithTypeAndName new_arguments(2);
|
||||
|
||||
for (size_t i = 0; i < 2; ++i)
|
||||
new_arguments[i].type = arguments[i];
|
||||
|
||||
/// Tuple argument must be second.
|
||||
if (isTuple(new_arguments[0].type))
|
||||
std::swap(new_arguments[0], new_arguments[1]);
|
||||
|
||||
auto function = function_builder->build(new_arguments);
|
||||
return function->getResultType();
|
||||
}
|
||||
|
||||
/// Special case when the function is plus or minus, one of arguments is Interval/Tuple of Intervals and another is Interval.
|
||||
if (auto function_builder = getFunctionForMergeIntervalsArithmetic(arguments[0], arguments[1], context))
|
||||
{
|
||||
ColumnsWithTypeAndName new_arguments(2);
|
||||
|
||||
for (size_t i = 0; i < 2; ++i)
|
||||
new_arguments[i].type = arguments[i];
|
||||
|
||||
auto function = function_builder->build(new_arguments);
|
||||
return function->getResultType();
|
||||
}
|
||||
|
||||
/// Special case when the function is multiply or divide, one of arguments is Tuple and another is Number.
|
||||
if (auto function_builder = getFunctionForTupleAndNumberArithmetic(arguments[0], arguments[1], context))
|
||||
{
|
||||
@ -1185,6 +1315,21 @@ public:
|
||||
type_res = std::make_shared<DataTypeString>();
|
||||
return true;
|
||||
}
|
||||
else if constexpr (std::is_same_v<LeftDataType, DataTypeInterval> || std::is_same_v<RightDataType, DataTypeInterval>)
|
||||
{
|
||||
if constexpr (std::is_same_v<LeftDataType, DataTypeInterval> &&
|
||||
std::is_same_v<RightDataType, DataTypeInterval>)
|
||||
{
|
||||
if constexpr (is_plus || is_minus)
|
||||
{
|
||||
if (left.getKind() == right.getKind())
|
||||
{
|
||||
type_res = std::make_shared<LeftDataType>(left.getKind());
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
using ResultDataType = typename BinaryOperationTraits<Op, LeftDataType, RightDataType>::ResultDataType;
|
||||
@ -1566,6 +1711,18 @@ public:
|
||||
return executeDateTimeIntervalPlusMinus(arguments, result_type, input_rows_count, function_builder);
|
||||
}
|
||||
|
||||
/// Special case when the function is plus or minus, one of arguments is Date/DateTime and another is Tuple.
|
||||
if (auto function_builder = getFunctionForDateTupleOfIntervalsArithmetic(arguments[0].type, arguments[1].type, context))
|
||||
{
|
||||
return executeDateTimeTupleOfIntervalsPlusMinus(arguments, result_type, input_rows_count, function_builder);
|
||||
}
|
||||
|
||||
/// Special case when the function is plus or minus, one of arguments is Interval/Tuple of Intervals and another is Interval.
|
||||
if (auto function_builder = getFunctionForMergeIntervalsArithmetic(arguments[0].type, arguments[1].type, context))
|
||||
{
|
||||
return executeIntervalTupleOfIntervalsPlusMinus(arguments, result_type, input_rows_count, function_builder);
|
||||
}
|
||||
|
||||
/// Special case when the function is plus, minus or multiply, both arguments are tuples.
|
||||
if (auto function_builder = getFunctionForTupleArithmetic(arguments[0].type, arguments[1].type, context))
|
||||
{
|
||||
|
@ -3,6 +3,7 @@
|
||||
#include <DataTypes/DataTypesNumber.h>
|
||||
#include <DataTypes/DataTypesDecimal.h>
|
||||
#include <DataTypes/DataTypeFixedString.h>
|
||||
#include <DataTypes/DataTypeInterval.h>
|
||||
#include <DataTypes/Native.h>
|
||||
#include <Columns/ColumnVector.h>
|
||||
#include <Columns/ColumnDecimal.h>
|
||||
@ -145,7 +146,8 @@ class FunctionUnaryArithmetic : public IFunction
|
||||
DataTypeDecimal<Decimal64>,
|
||||
DataTypeDecimal<Decimal128>,
|
||||
DataTypeDecimal<Decimal256>,
|
||||
DataTypeFixedString
|
||||
DataTypeFixedString,
|
||||
DataTypeInterval
|
||||
>(type, std::forward<F>(f));
|
||||
}
|
||||
|
||||
@ -211,6 +213,12 @@ public:
|
||||
return false;
|
||||
result = std::make_shared<DataType>(type.getN());
|
||||
}
|
||||
else if constexpr (std::is_same_v<DataTypeInterval, DataType>)
|
||||
{
|
||||
if constexpr (!IsUnaryOperation<Op>::negate)
|
||||
return false;
|
||||
result = std::make_shared<DataTypeInterval>(type.getKind());
|
||||
}
|
||||
else
|
||||
{
|
||||
using T0 = typename DataType::FieldType;
|
||||
|
@ -1,5 +1,6 @@
|
||||
#include <Columns/ColumnTuple.h>
|
||||
#include <DataTypes/DataTypeArray.h>
|
||||
#include <DataTypes/DataTypeInterval.h>
|
||||
#include <DataTypes/DataTypeTuple.h>
|
||||
#include <DataTypes/DataTypesNumber.h>
|
||||
#include <DataTypes/DataTypeNothing.h>
|
||||
@ -415,6 +416,274 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
template <typename Impl>
|
||||
class FunctionDateOrDateTimeOperationTupleOfIntervals : public ITupleFunction
|
||||
{
|
||||
public:
|
||||
static constexpr auto name = Impl::name;
|
||||
|
||||
explicit FunctionDateOrDateTimeOperationTupleOfIntervals(ContextPtr context_) : ITupleFunction(context_) {}
|
||||
static FunctionPtr create(ContextPtr context_)
|
||||
{
|
||||
return std::make_shared<FunctionDateOrDateTimeOperationTupleOfIntervals>(context_);
|
||||
}
|
||||
|
||||
String getName() const override { return name; }
|
||||
|
||||
size_t getNumberOfArguments() const override { return 2; }
|
||||
|
||||
DataTypePtr getReturnTypeImpl(const ColumnsWithTypeAndName & arguments) const override
|
||||
{
|
||||
if (!isDateOrDate32(arguments[0].type) && !isDateTime(arguments[0].type) && !isDateTime64(arguments[0].type))
|
||||
throw Exception{ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT,
|
||||
"Illegal type {} of first argument of function {}. Should be a date or a date with time",
|
||||
arguments[0].type->getName(), getName()};
|
||||
|
||||
const auto * cur_tuple = checkAndGetDataType<DataTypeTuple>(arguments[1].type.get());
|
||||
|
||||
if (!cur_tuple)
|
||||
throw Exception{ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT,
|
||||
"Illegal type {} of second argument of function {}. Should be a tuple",
|
||||
arguments[0].type->getName(), getName()};
|
||||
|
||||
const auto & cur_types = cur_tuple->getElements();
|
||||
|
||||
Columns cur_elements;
|
||||
if (arguments[1].column)
|
||||
cur_elements = getTupleElements(*arguments[1].column);
|
||||
|
||||
size_t tuple_size = cur_types.size();
|
||||
if (tuple_size == 0)
|
||||
return arguments[0].type;
|
||||
|
||||
auto plus = FunctionFactory::instance().get(Impl::func_name, context);
|
||||
DataTypePtr res_type = arguments[0].type;
|
||||
for (size_t i = 0; i < tuple_size; ++i)
|
||||
{
|
||||
try
|
||||
{
|
||||
ColumnWithTypeAndName left{res_type, {}};
|
||||
ColumnWithTypeAndName right{cur_elements.empty() ? nullptr : cur_elements[i], cur_types[i], {}};
|
||||
auto plus_elem = plus->build({left, right});
|
||||
res_type = plus_elem->getResultType();
|
||||
}
|
||||
catch (DB::Exception & e)
|
||||
{
|
||||
e.addMessage("While executing function {} for tuple element {}", getName(), i);
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
return res_type;
|
||||
}
|
||||
|
||||
ColumnPtr executeImpl(const ColumnsWithTypeAndName & arguments, const DataTypePtr &, size_t input_rows_count) const override
|
||||
{
|
||||
const auto * cur_tuple = checkAndGetDataType<DataTypeTuple>(arguments[1].type.get());
|
||||
const auto & cur_types = cur_tuple->getElements();
|
||||
auto cur_elements = getTupleElements(*arguments[1].column);
|
||||
|
||||
size_t tuple_size = cur_elements.size();
|
||||
if (tuple_size == 0)
|
||||
return arguments[0].column;
|
||||
|
||||
auto plus = FunctionFactory::instance().get(Impl::func_name, context);
|
||||
ColumnWithTypeAndName res;
|
||||
for (size_t i = 0; i < tuple_size; ++i)
|
||||
{
|
||||
ColumnWithTypeAndName column{cur_elements[i], cur_types[i], {}};
|
||||
auto elem_plus = plus->build(ColumnsWithTypeAndName{i == 0 ? arguments[0] : res, column});
|
||||
auto res_type = elem_plus->getResultType();
|
||||
res.column = elem_plus->execute({i == 0 ? arguments[0] : res, column}, res_type, input_rows_count);
|
||||
res.type = res_type;
|
||||
}
|
||||
|
||||
return res.column;
|
||||
}
|
||||
};
|
||||
|
||||
struct AddTupleOfIntervalsImpl
|
||||
{
|
||||
static constexpr auto name = "addTupleOfIntervals";
|
||||
static constexpr auto func_name = "plus";
|
||||
};
|
||||
|
||||
struct SubtractTupleOfIntervalsImpl
|
||||
{
|
||||
static constexpr auto name = "subtractTupleOfIntervals";
|
||||
static constexpr auto func_name = "minus";
|
||||
};
|
||||
|
||||
using FunctionAddTupleOfIntervals = FunctionDateOrDateTimeOperationTupleOfIntervals<AddTupleOfIntervalsImpl>;
|
||||
|
||||
using FunctionSubtractTupleOfIntervals = FunctionDateOrDateTimeOperationTupleOfIntervals<SubtractTupleOfIntervalsImpl>;
|
||||
|
||||
template <bool is_minus>
|
||||
struct FunctionTupleOperationInterval : public ITupleFunction
|
||||
{
|
||||
public:
|
||||
static constexpr auto name = is_minus ? "subtractInterval" : "addInterval";
|
||||
|
||||
explicit FunctionTupleOperationInterval(ContextPtr context_) : ITupleFunction(context_) {}
|
||||
|
||||
static FunctionPtr create(ContextPtr context_)
|
||||
{
|
||||
return std::make_shared<FunctionTupleOperationInterval>(context_);
|
||||
}
|
||||
|
||||
String getName() const override { return name; }
|
||||
|
||||
size_t getNumberOfArguments() const override { return 2; }
|
||||
|
||||
DataTypePtr getReturnTypeImpl(const DataTypes & arguments) const override
|
||||
{
|
||||
if (!isTuple(arguments[0]) && !isInterval(arguments[0]))
|
||||
throw Exception(ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT,
|
||||
"Illegal type {} of first argument of function {}, must be Tuple or Interval",
|
||||
arguments[0]->getName(), getName());
|
||||
|
||||
if (!isInterval(arguments[1]))
|
||||
throw Exception(ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT,
|
||||
"Illegal type {} of second argument of function {}, must be Interval",
|
||||
arguments[1]->getName(), getName());
|
||||
|
||||
DataTypes types;
|
||||
|
||||
const auto * tuple = checkAndGetDataType<DataTypeTuple>(arguments[0].get());
|
||||
|
||||
if (tuple)
|
||||
{
|
||||
const auto & cur_types = tuple->getElements();
|
||||
|
||||
for (const auto & type : cur_types)
|
||||
if (!isInterval(type))
|
||||
throw Exception(ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT,
|
||||
"Illegal type {} of Tuple element of first argument of function {}, must be Interval",
|
||||
type->getName(), getName());
|
||||
|
||||
types = cur_types;
|
||||
}
|
||||
else
|
||||
{
|
||||
types = {arguments[0]};
|
||||
}
|
||||
|
||||
const auto * interval_last = checkAndGetDataType<DataTypeInterval>(types.back().get());
|
||||
const auto * interval_new = checkAndGetDataType<DataTypeInterval>(arguments[1].get());
|
||||
|
||||
if (!interval_last->equals(*interval_new))
|
||||
types.push_back(arguments[1]);
|
||||
|
||||
return std::make_shared<DataTypeTuple>(types);
|
||||
}
|
||||
|
||||
ColumnPtr executeImpl(const ColumnsWithTypeAndName & arguments, const DataTypePtr &, size_t input_rows_count) const override
|
||||
{
|
||||
if (!isInterval(arguments[1].type))
|
||||
throw Exception(ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT,
|
||||
"Illegal type {} of second argument of function {}, must be Interval",
|
||||
arguments[1].type->getName(), getName());
|
||||
|
||||
Columns tuple_columns;
|
||||
|
||||
const auto * first_tuple = checkAndGetDataType<DataTypeTuple>(arguments[0].type.get());
|
||||
const auto * first_interval = checkAndGetDataType<DataTypeInterval>(arguments[0].type.get());
|
||||
const auto * second_interval = checkAndGetDataType<DataTypeInterval>(arguments[1].type.get());
|
||||
|
||||
bool can_be_merged;
|
||||
|
||||
if (first_interval)
|
||||
{
|
||||
can_be_merged = first_interval->equals(*second_interval);
|
||||
|
||||
if (can_be_merged)
|
||||
tuple_columns.resize(1);
|
||||
else
|
||||
tuple_columns.resize(2);
|
||||
|
||||
tuple_columns[0] = arguments[0].column->convertToFullColumnIfConst();
|
||||
}
|
||||
else if (first_tuple)
|
||||
{
|
||||
const auto & cur_types = first_tuple->getElements();
|
||||
|
||||
for (const auto & type : cur_types)
|
||||
if (!isInterval(type))
|
||||
throw Exception(ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT,
|
||||
"Illegal type {} of Tuple element of first argument of function {}, must be Interval",
|
||||
type->getName(), getName());
|
||||
|
||||
auto cur_elements = getTupleElements(*arguments[0].column);
|
||||
size_t tuple_size = cur_elements.size();
|
||||
|
||||
if (tuple_size == 0)
|
||||
{
|
||||
can_be_merged = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
const auto * tuple_last_interval = checkAndGetDataType<DataTypeInterval>(cur_types.back().get());
|
||||
can_be_merged = tuple_last_interval->equals(*second_interval);
|
||||
}
|
||||
|
||||
if (can_be_merged)
|
||||
tuple_columns.resize(tuple_size);
|
||||
else
|
||||
tuple_columns.resize(tuple_size + 1);
|
||||
|
||||
for (size_t i = 0; i < tuple_size; ++i)
|
||||
tuple_columns[i] = cur_elements[i];
|
||||
}
|
||||
else
|
||||
throw Exception(ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT,
|
||||
"Illegal type {} of first argument of function {}, must be Tuple or Interval",
|
||||
arguments[0].type->getName(), getName());
|
||||
|
||||
|
||||
ColumnPtr & last_column = tuple_columns.back();
|
||||
|
||||
if (can_be_merged)
|
||||
{
|
||||
ColumnWithTypeAndName left{last_column, arguments[1].type, {}};
|
||||
|
||||
if constexpr (is_minus)
|
||||
{
|
||||
auto minus = FunctionFactory::instance().get("minus", context);
|
||||
auto elem_minus = minus->build({left, arguments[1]});
|
||||
last_column = elem_minus->execute({left, arguments[1]}, arguments[1].type, input_rows_count)
|
||||
->convertToFullColumnIfConst();
|
||||
}
|
||||
else
|
||||
{
|
||||
auto plus = FunctionFactory::instance().get("plus", context);
|
||||
auto elem_plus = plus->build({left, arguments[1]});
|
||||
last_column = elem_plus->execute({left, arguments[1]}, arguments[1].type, input_rows_count)
|
||||
->convertToFullColumnIfConst();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if constexpr (is_minus)
|
||||
{
|
||||
auto negate = FunctionFactory::instance().get("negate", context);
|
||||
auto elem_negate = negate->build({arguments[1]});
|
||||
last_column = elem_negate->execute({arguments[1]}, arguments[1].type, input_rows_count);
|
||||
}
|
||||
else
|
||||
{
|
||||
last_column = arguments[1].column;
|
||||
}
|
||||
}
|
||||
|
||||
return ColumnTuple::create(tuple_columns);
|
||||
}
|
||||
};
|
||||
|
||||
using FunctionTupleAddInterval = FunctionTupleOperationInterval<false>;
|
||||
|
||||
using FunctionTupleSubtractInterval = FunctionTupleOperationInterval<true>;
|
||||
|
||||
|
||||
/// this is for convenient usage in LNormalize
|
||||
template <class FuncLabel>
|
||||
class FunctionLNorm : public ITupleFunction {};
|
||||
@ -1282,6 +1551,65 @@ REGISTER_FUNCTION(VectorFunctions)
|
||||
factory.registerFunction<FunctionTupleDivide>();
|
||||
factory.registerFunction<FunctionTupleNegate>();
|
||||
|
||||
factory.registerFunction<FunctionAddTupleOfIntervals>(
|
||||
{
|
||||
R"(
|
||||
Consecutively adds a tuple of intervals to a Date or a DateTime.
|
||||
[example:tuple]
|
||||
)",
|
||||
Documentation::Examples{
|
||||
{"tuple", "WITH toDate('2018-01-01') AS date SELECT addTupleOfIntervals(date, (INTERVAL 1 DAY, INTERVAL 1 YEAR))"},
|
||||
},
|
||||
Documentation::Categories{"Tuple", "Interval", "Date", "DateTime"}
|
||||
});
|
||||
|
||||
factory.registerFunction<FunctionSubtractTupleOfIntervals>(
|
||||
{
|
||||
R"(
|
||||
Consecutively subtracts a tuple of intervals from a Date or a DateTime.
|
||||
[example:tuple]
|
||||
)",
|
||||
Documentation::Examples{
|
||||
{"tuple", "WITH toDate('2018-01-01') AS date SELECT subtractTupleOfIntervals(date, (INTERVAL 1 DAY, INTERVAL 1 YEAR))"},
|
||||
},
|
||||
Documentation::Categories{"Tuple", "Interval", "Date", "DateTime"}
|
||||
});
|
||||
|
||||
factory.registerFunction<FunctionTupleAddInterval>(
|
||||
{
|
||||
R"(
|
||||
Adds an interval to another interval or tuple of intervals. The returned value is tuple of intervals.
|
||||
[example:tuple]
|
||||
[example:interval1]
|
||||
|
||||
If the types of the first interval (or the interval in the tuple) and the second interval are the same they will be merged into one interval.
|
||||
[example:interval2]
|
||||
)",
|
||||
Documentation::Examples{
|
||||
{"tuple", "SELECT addInterval((INTERVAL 1 DAY, INTERVAL 1 YEAR), INTERVAL 1 MONTH)"},
|
||||
{"interval1", "SELECT addInterval(INTERVAL 1 DAY, INTERVAL 1 MONTH)"},
|
||||
{"interval2", "SELECT addInterval(INTERVAL 1 DAY, INTERVAL 1 DAY)"},
|
||||
},
|
||||
Documentation::Categories{"Tuple", "Interval"}
|
||||
});
|
||||
factory.registerFunction<FunctionTupleSubtractInterval>(
|
||||
{
|
||||
R"(
|
||||
Adds an negated interval to another interval or tuple of intervals. The returned value is tuple of intervals.
|
||||
[example:tuple]
|
||||
[example:interval1]
|
||||
|
||||
If the types of the first interval (or the interval in the tuple) and the second interval are the same they will be merged into one interval.
|
||||
[example:interval2]
|
||||
)",
|
||||
Documentation::Examples{
|
||||
{"tuple", "SELECT subtractInterval((INTERVAL 1 DAY, INTERVAL 1 YEAR), INTERVAL 1 MONTH)"},
|
||||
{"interval1", "SELECT subtractInterval(INTERVAL 1 DAY, INTERVAL 1 MONTH)"},
|
||||
{"interval2", "SELECT subtractInterval(INTERVAL 2 DAY, INTERVAL 1 DAY)"},
|
||||
},
|
||||
Documentation::Categories{"Tuple", "Interval"}
|
||||
});
|
||||
|
||||
factory.registerFunction<FunctionTupleMultiplyByNumber>();
|
||||
factory.registerFunction<FunctionTupleDivideByNumber>();
|
||||
|
||||
|
@ -1734,49 +1734,54 @@ public:
|
||||
|
||||
if (state == 0)
|
||||
{
|
||||
state = 1;
|
||||
|
||||
auto begin = pos;
|
||||
auto init_expected = expected;
|
||||
ASTPtr string_literal;
|
||||
String literal;
|
||||
|
||||
//// A String literal followed INTERVAL keyword,
|
||||
/// the literal can be a part of an expression or
|
||||
/// include Number and INTERVAL TYPE at the same time
|
||||
if (ParserStringLiteral{}.parse(pos, string_literal, expected))
|
||||
if (ParserStringLiteral{}.parse(pos, string_literal, expected)
|
||||
&& string_literal->as<ASTLiteral &>().value.tryGet(literal))
|
||||
{
|
||||
String literal;
|
||||
if (string_literal->as<ASTLiteral &>().value.tryGet(literal))
|
||||
Tokens tokens(literal.data(), literal.data() + literal.size());
|
||||
IParser::Pos token_pos(tokens, 0);
|
||||
Expected token_expected;
|
||||
ASTPtr expr;
|
||||
|
||||
if (!ParserNumber{}.parse(token_pos, expr, token_expected))
|
||||
return false;
|
||||
|
||||
/// case: INTERVAL '1' HOUR
|
||||
/// back to begin
|
||||
if (!token_pos.isValid())
|
||||
{
|
||||
Tokens tokens(literal.data(), literal.data() + literal.size());
|
||||
IParser::Pos token_pos(tokens, 0);
|
||||
Expected token_expected;
|
||||
ASTPtr expr;
|
||||
|
||||
if (!ParserNumber{}.parse(token_pos, expr, token_expected))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
/// case: INTERVAL '1' HOUR
|
||||
/// back to begin
|
||||
if (!token_pos.isValid())
|
||||
{
|
||||
pos = begin;
|
||||
expected = init_expected;
|
||||
}
|
||||
else
|
||||
{
|
||||
/// case: INTERVAL '1 HOUR'
|
||||
if (!parseIntervalKind(token_pos, token_expected, interval_kind))
|
||||
return false;
|
||||
|
||||
elements = {makeASTFunction(interval_kind.toNameOfFunctionToIntervalDataType(), expr)};
|
||||
finished = true;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
pos = begin;
|
||||
expected = init_expected;
|
||||
return true;
|
||||
}
|
||||
|
||||
/// case: INTERVAL '1 HOUR'
|
||||
if (!parseIntervalKind(token_pos, token_expected, interval_kind))
|
||||
return false;
|
||||
|
||||
pushResult(makeASTFunction(interval_kind.toNameOfFunctionToIntervalDataType(), expr));
|
||||
|
||||
/// case: INTERVAL '1 HOUR 1 SECOND ...'
|
||||
while (token_pos.isValid())
|
||||
{
|
||||
if (!ParserNumber{}.parse(token_pos, expr, token_expected) ||
|
||||
!parseIntervalKind(token_pos, token_expected, interval_kind))
|
||||
return false;
|
||||
|
||||
pushResult(makeASTFunction(interval_kind.toNameOfFunctionToIntervalDataType(), expr));
|
||||
}
|
||||
|
||||
finished = true;
|
||||
}
|
||||
state = 1;
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -1795,6 +1800,17 @@ public:
|
||||
return true;
|
||||
}
|
||||
|
||||
protected:
|
||||
bool getResultImpl(ASTPtr & node) override
|
||||
{
|
||||
if (elements.size() == 1)
|
||||
node = elements[0];
|
||||
else
|
||||
node = makeASTFunction("tuple", std::move(elements));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private:
|
||||
IntervalKind interval_kind;
|
||||
};
|
||||
|
32
tests/queries/0_stateless/02457_tuple_of_intervals.reference
Normal file
32
tests/queries/0_stateless/02457_tuple_of_intervals.reference
Normal file
@ -0,0 +1,32 @@
|
||||
SELECT (toIntervalSecond(-1), toIntervalMinute(2), toIntervalMonth(-3), toIntervalYear(1))
|
||||
---
|
||||
-1
|
||||
2022-10-12
|
||||
2022-10-10
|
||||
(2)
|
||||
(0)
|
||||
2022-11-12
|
||||
2022-09-10
|
||||
(1,2)
|
||||
(1,0)
|
||||
---
|
||||
2022-10-12
|
||||
2022-10-10
|
||||
2022-10-12
|
||||
(2) Tuple(IntervalSecond)
|
||||
(0) Tuple(IntervalSecond)
|
||||
---
|
||||
3 IntervalSecond
|
||||
(1,2) Tuple(IntervalHour, IntervalSecond)
|
||||
(1,1,1) Tuple(IntervalSecond, IntervalHour, IntervalSecond)
|
||||
(2,1) Tuple(IntervalSecond, IntervalHour)
|
||||
-3 IntervalSecond
|
||||
(-1,-2) Tuple(IntervalHour, IntervalSecond)
|
||||
(-1,-1,-1) Tuple(IntervalSecond, IntervalHour, IntervalSecond)
|
||||
(-2,-1) Tuple(IntervalSecond, IntervalHour)
|
||||
---
|
||||
1 2022-03-01
|
||||
1 2022-02-28
|
||||
1 2023-07-11 00:01:59
|
||||
1 2021-07-31 23:00:00
|
||||
1 2021-06-10 23:59:59.000
|
71
tests/queries/0_stateless/02457_tuple_of_intervals.sql
Normal file
71
tests/queries/0_stateless/02457_tuple_of_intervals.sql
Normal file
@ -0,0 +1,71 @@
|
||||
EXPLAIN SYNTAX SELECT INTERVAL '-1 SECOND 2 MINUTE -3 MONTH 1 YEAR';
|
||||
|
||||
SELECT '---';
|
||||
|
||||
SELECT negate(INTERVAL 1 SECOND);
|
||||
SELECT addTupleOfIntervals('2022-10-11'::Date, tuple(INTERVAL 1 DAY));
|
||||
SELECT subtractTupleOfIntervals('2022-10-11'::Date, tuple(INTERVAL 1 DAY));
|
||||
SELECT addInterval(tuple(INTERVAL 1 SECOND), INTERVAL 1 SECOND);
|
||||
SELECT subtractInterval(tuple(INTERVAL 1 SECOND), INTERVAL 1 SECOND);
|
||||
|
||||
SELECT addTupleOfIntervals('2022-10-11'::Date, (INTERVAL 1 DAY, INTERVAL 1 MONTH));
|
||||
SELECT subtractTupleOfIntervals('2022-10-11'::Date, (INTERVAL 1 DAY, INTERVAL 1 MONTH));
|
||||
SELECT addInterval((INTERVAL 1 DAY, INTERVAL 1 SECOND), INTERVAL 1 SECOND);
|
||||
SELECT subtractInterval(tuple(INTERVAL 1 DAY, INTERVAL 1 SECOND), INTERVAL 1 SECOND);
|
||||
|
||||
SELECT '---';
|
||||
|
||||
SELECT '2022-10-11'::Date + tuple(INTERVAL 1 DAY);
|
||||
SELECT '2022-10-11'::Date - tuple(INTERVAL 1 DAY);
|
||||
SELECT tuple(INTERVAL 1 DAY) + '2022-10-11'::Date;
|
||||
SELECT tuple(INTERVAL 1 DAY) - '2022-10-11'::Date; -- { serverError 43 }
|
||||
|
||||
WITH tuple(INTERVAL 1 SECOND) + INTERVAL 1 SECOND as expr SELECT expr, toTypeName(expr);
|
||||
WITH tuple(INTERVAL 1 SECOND) - INTERVAL 1 SECOND as expr SELECT expr, toTypeName(expr);
|
||||
WITH INTERVAL 1 SECOND + tuple(INTERVAL 1 SECOND) as expr SELECT expr, toTypeName(expr); -- { serverError 43 }
|
||||
WITH INTERVAL 1 SECOND - tuple(INTERVAL 1 SECOND) as expr SELECT expr, toTypeName(expr); -- { serverError 43 }
|
||||
|
||||
SELECT '---';
|
||||
|
||||
WITH INTERVAL 1 SECOND + INTERVAL 1 SECOND + INTERVAL 1 SECOND as expr SELECT expr, toTypeName(expr);
|
||||
WITH INTERVAL 1 HOUR + INTERVAL 1 SECOND + INTERVAL 1 SECOND as expr SELECT expr, toTypeName(expr);
|
||||
WITH INTERVAL 1 SECOND + INTERVAL 1 HOUR + INTERVAL 1 SECOND as expr SELECT expr, toTypeName(expr);
|
||||
WITH INTERVAL 1 SECOND + INTERVAL 1 SECOND + INTERVAL 1 HOUR as expr SELECT expr, toTypeName(expr);
|
||||
|
||||
WITH - INTERVAL 1 SECOND - INTERVAL 1 SECOND - INTERVAL 1 SECOND as expr SELECT expr, toTypeName(expr);
|
||||
WITH - INTERVAL 1 HOUR - INTERVAL 1 SECOND - INTERVAL 1 SECOND as expr SELECT expr, toTypeName(expr);
|
||||
WITH - INTERVAL 1 SECOND - INTERVAL 1 HOUR - INTERVAL 1 SECOND as expr SELECT expr, toTypeName(expr);
|
||||
WITH - INTERVAL 1 SECOND - INTERVAL 1 SECOND - INTERVAL 1 HOUR as expr SELECT expr, toTypeName(expr);
|
||||
|
||||
SELECT '---';
|
||||
|
||||
WITH '2022-01-30'::Date + INTERVAL 1 MONTH + INTERVAL 1 DAY AS e1,
|
||||
'2022-01-30'::Date + (INTERVAL 1 MONTH + INTERVAL 1 DAY) AS e2,
|
||||
'2022-01-30'::Date + (INTERVAL 1 MONTH, INTERVAL 1 DAY) AS e3,
|
||||
'2022-01-30'::Date + INTERVAL '1 MONTH 1 DAY' AS e4
|
||||
SELECT e1 == e2 AND e2 == e3 AND e3 == e4, e1;
|
||||
|
||||
WITH '2022-01-30'::Date + INTERVAL 1 DAY + INTERVAL 1 MONTH AS e1,
|
||||
'2022-01-30'::Date + (INTERVAL 1 DAY + INTERVAL 1 MONTH) AS e2,
|
||||
'2022-01-30'::Date + (INTERVAL 1 DAY, INTERVAL 1 MONTH) AS e3,
|
||||
'2022-01-30'::Date + INTERVAL '1 DAY 1 MONTH' AS e4
|
||||
SELECT e1 == e2 AND e2 == e3 AND e3 == e4, e1;
|
||||
|
||||
WITH '2022-10-11'::Date + INTERVAL -1 SECOND + INTERVAL 2 MINUTE + INTERVAL -3 MONTH + INTERVAL 1 YEAR AS e1,
|
||||
'2022-10-11'::Date + (INTERVAL -1 SECOND + INTERVAL 2 MINUTE + INTERVAL -3 MONTH + INTERVAL 1 YEAR) AS e2,
|
||||
'2022-10-11'::Date + (INTERVAL -1 SECOND, INTERVAL 2 MINUTE, INTERVAL -3 MONTH, INTERVAL 1 YEAR) AS e3,
|
||||
'2022-10-11'::Date + INTERVAL '-1 SECOND 2 MINUTE -3 MONTH 1 YEAR' AS e4
|
||||
SELECT e1 == e2 AND e2 == e3 AND e3 == e4, e1;
|
||||
|
||||
WITH '2022-10-11'::DateTime - INTERVAL 1 QUARTER - INTERVAL -3 WEEK - INTERVAL 1 YEAR - INTERVAL 1 HOUR AS e1,
|
||||
'2022-10-11'::DateTime + (- INTERVAL 1 QUARTER - INTERVAL -3 WEEK - INTERVAL 1 YEAR - INTERVAL 1 HOUR) AS e2,
|
||||
'2022-10-11'::DateTime - (INTERVAL 1 QUARTER, INTERVAL -3 WEEK, INTERVAL 1 YEAR, INTERVAL 1 HOUR) AS e3,
|
||||
'2022-10-11'::DateTime - INTERVAL '1 QUARTER -3 WEEK 1 YEAR 1 HOUR' AS e4
|
||||
SELECT e1 == e2 AND e2 == e3 AND e3 == e4, e1;
|
||||
|
||||
|
||||
WITH '2022-10-11'::DateTime64 - INTERVAL 1 YEAR - INTERVAL 4 MONTH - INTERVAL 1 SECOND AS e1,
|
||||
'2022-10-11'::DateTime64 + (- INTERVAL 1 YEAR - INTERVAL 4 MONTH - INTERVAL 1 SECOND) AS e2,
|
||||
'2022-10-11'::DateTime64 - (INTERVAL 1 YEAR, INTERVAL 4 MONTH, INTERVAL 1 SECOND) AS e3,
|
||||
'2022-10-11'::DateTime64 - INTERVAL '1 YEAR 4 MONTH 1 SECOND' AS e4
|
||||
SELECT e1 == e2 AND e2 == e3 AND e3 == e4, e1;
|
Loading…
Reference in New Issue
Block a user