mirror of
https://github.com/ClickHouse/ClickHouse.git
synced 2024-09-20 08:40:50 +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>
|
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);
|
||||||
}
|
}
|
||||||
|
@ -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(); }
|
||||||
|
|
||||||
|
@ -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))
|
||||||
{
|
{
|
||||||
|
@ -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;
|
||||||
|
@ -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>();
|
||||||
|
|
||||||
|
@ -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;
|
||||||
};
|
};
|
||||||
|
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