Merge pull request #42195 from evillique/tuple-of-intervals

Better INTERVAL parsing and execution
This commit is contained in:
Nikolay Degterinsky 2022-11-08 16:21:39 +01:00 committed by GitHub
commit 09aca50c3b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 649 additions and 36 deletions

View File

@ -1331,7 +1331,7 @@ public:
} }
template <typename DateOrTime> 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); return addMonths(d, delta * 3);
} }

View File

@ -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 isTuple(const DataTypePtr & data_type) { return WhichDataType(data_type).isTuple(); }
inline bool isArray(const DataTypePtr & data_type) { return WhichDataType(data_type).isArray(); } 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 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 isNothing(const DataTypePtr & data_type) { return WhichDataType(data_type).isNothing(); }
inline bool isUUID(const DataTypePtr & data_type) { return WhichDataType(data_type).isUUID(); } inline bool isUUID(const DataTypePtr & data_type) { return WhichDataType(data_type).isUUID(); }

View File

@ -22,6 +22,7 @@
#include <DataTypes/DataTypeFactory.h> #include <DataTypes/DataTypeFactory.h>
#include <DataTypes/DataTypeFixedString.h> #include <DataTypes/DataTypeFixedString.h>
#include <DataTypes/DataTypeInterval.h> #include <DataTypes/DataTypeInterval.h>
#include <DataTypes/DataTypeTuple.h>
#include <DataTypes/DataTypeString.h> #include <DataTypes/DataTypeString.h>
#include <DataTypes/DataTypesDecimal.h> #include <DataTypes/DataTypesDecimal.h>
#include <DataTypes/DataTypesNumber.h> #include <DataTypes/DataTypesNumber.h>
@ -642,7 +643,8 @@ class FunctionBinaryArithmetic : public IFunction
DataTypeInt8, DataTypeInt16, DataTypeInt32, DataTypeInt64, DataTypeInt128, DataTypeInt256, DataTypeInt8, DataTypeInt16, DataTypeInt32, DataTypeInt64, DataTypeInt128, DataTypeInt256,
DataTypeDecimal32, DataTypeDecimal64, DataTypeDecimal128, DataTypeDecimal256, DataTypeDecimal32, DataTypeDecimal64, DataTypeDecimal128, DataTypeDecimal256,
DataTypeDate, DataTypeDateTime, DataTypeDate, DataTypeDateTime,
DataTypeFixedString, DataTypeString>; DataTypeFixedString, DataTypeString,
DataTypeInterval>;
using Floats = TypeList<DataTypeFloat32, DataTypeFloat64>; using Floats = TypeList<DataTypeFloat32, DataTypeFloat64>;
@ -717,6 +719,82 @@ class FunctionBinaryArithmetic : public IFunction
return FunctionFactory::instance().get(function_name, context); 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 static FunctionOverloadResolverPtr
getFunctionForTupleArithmetic(const DataTypePtr & type0, const DataTypePtr & type1, ContextPtr context) 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); 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, ColumnPtr executeTupleNumberOperator(const ColumnsWithTypeAndName & arguments, const DataTypePtr & result_type,
size_t input_rows_count, const FunctionOverloadResolverPtr & function_builder) const size_t input_rows_count, const FunctionOverloadResolverPtr & function_builder) const
{ {
@ -1134,6 +1236,34 @@ public:
return function->getResultType(); 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. /// 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)) if (auto function_builder = getFunctionForTupleAndNumberArithmetic(arguments[0], arguments[1], context))
{ {
@ -1185,6 +1315,21 @@ public:
type_res = std::make_shared<DataTypeString>(); type_res = std::make_shared<DataTypeString>();
return true; 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 else
{ {
using ResultDataType = typename BinaryOperationTraits<Op, LeftDataType, RightDataType>::ResultDataType; using ResultDataType = typename BinaryOperationTraits<Op, LeftDataType, RightDataType>::ResultDataType;
@ -1566,6 +1711,18 @@ public:
return executeDateTimeIntervalPlusMinus(arguments, result_type, input_rows_count, function_builder); 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. /// 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)) if (auto function_builder = getFunctionForTupleArithmetic(arguments[0].type, arguments[1].type, context))
{ {

View File

@ -3,6 +3,7 @@
#include <DataTypes/DataTypesNumber.h> #include <DataTypes/DataTypesNumber.h>
#include <DataTypes/DataTypesDecimal.h> #include <DataTypes/DataTypesDecimal.h>
#include <DataTypes/DataTypeFixedString.h> #include <DataTypes/DataTypeFixedString.h>
#include <DataTypes/DataTypeInterval.h>
#include <DataTypes/Native.h> #include <DataTypes/Native.h>
#include <Columns/ColumnVector.h> #include <Columns/ColumnVector.h>
#include <Columns/ColumnDecimal.h> #include <Columns/ColumnDecimal.h>
@ -145,7 +146,8 @@ class FunctionUnaryArithmetic : public IFunction
DataTypeDecimal<Decimal64>, DataTypeDecimal<Decimal64>,
DataTypeDecimal<Decimal128>, DataTypeDecimal<Decimal128>,
DataTypeDecimal<Decimal256>, DataTypeDecimal<Decimal256>,
DataTypeFixedString DataTypeFixedString,
DataTypeInterval
>(type, std::forward<F>(f)); >(type, std::forward<F>(f));
} }
@ -211,6 +213,12 @@ public:
return false; return false;
result = std::make_shared<DataType>(type.getN()); 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 else
{ {
using T0 = typename DataType::FieldType; using T0 = typename DataType::FieldType;

View File

@ -1,5 +1,6 @@
#include <Columns/ColumnTuple.h> #include <Columns/ColumnTuple.h>
#include <DataTypes/DataTypeArray.h> #include <DataTypes/DataTypeArray.h>
#include <DataTypes/DataTypeInterval.h>
#include <DataTypes/DataTypeTuple.h> #include <DataTypes/DataTypeTuple.h>
#include <DataTypes/DataTypesNumber.h> #include <DataTypes/DataTypesNumber.h>
#include <DataTypes/DataTypeNothing.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 /// this is for convenient usage in LNormalize
template <class FuncLabel> template <class FuncLabel>
class FunctionLNorm : public ITupleFunction {}; class FunctionLNorm : public ITupleFunction {};
@ -1282,6 +1551,65 @@ REGISTER_FUNCTION(VectorFunctions)
factory.registerFunction<FunctionTupleDivide>(); factory.registerFunction<FunctionTupleDivide>();
factory.registerFunction<FunctionTupleNegate>(); 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<FunctionTupleMultiplyByNumber>();
factory.registerFunction<FunctionTupleDivideByNumber>(); factory.registerFunction<FunctionTupleDivideByNumber>();

View File

@ -1734,16 +1734,18 @@ public:
if (state == 0) if (state == 0)
{ {
state = 1;
auto begin = pos; auto begin = pos;
auto init_expected = expected; auto init_expected = expected;
ASTPtr string_literal; ASTPtr string_literal;
String literal;
//// A String literal followed INTERVAL keyword, //// A String literal followed INTERVAL keyword,
/// the literal can be a part of an expression or /// the literal can be a part of an expression or
/// include Number and INTERVAL TYPE at the same time /// 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()); Tokens tokens(literal.data(), literal.data() + literal.size());
IParser::Pos token_pos(tokens, 0); IParser::Pos token_pos(tokens, 0);
@ -1751,32 +1753,35 @@ public:
ASTPtr expr; ASTPtr expr;
if (!ParserNumber{}.parse(token_pos, expr, token_expected)) if (!ParserNumber{}.parse(token_pos, expr, token_expected))
{
return false; return false;
}
else
{
/// case: INTERVAL '1' HOUR /// case: INTERVAL '1' HOUR
/// back to begin /// back to begin
if (!token_pos.isValid()) if (!token_pos.isValid())
{ {
pos = begin; pos = begin;
expected = init_expected; expected = init_expected;
return true;
} }
else
{
/// case: INTERVAL '1 HOUR' /// case: INTERVAL '1 HOUR'
if (!parseIntervalKind(token_pos, token_expected, interval_kind)) if (!parseIntervalKind(token_pos, token_expected, interval_kind))
return false; return false;
elements = {makeASTFunction(interval_kind.toNameOfFunctionToIntervalDataType(), expr)}; 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; finished = true;
return true;
} }
}
}
}
state = 1;
return true; return true;
} }
@ -1795,6 +1800,17 @@ public:
return true; 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: private:
IntervalKind interval_kind; IntervalKind interval_kind;
}; };

View 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

View 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;