Skip to content

Commit

Permalink
Add plus and minus functions for Timestamp with IntervalDayTime (face…
Browse files Browse the repository at this point in the history
…bookincubator#7570)

Summary:
Pull Request resolved: facebookincubator#7570

Add plus(timestamp, interval), plus(interval, timestamp), and minus(timestamp, interval) functions.

This is the forth diff to fix facebookincubator#7490

Reviewed By: kgpai

Differential Revision: D51330143

fbshipit-source-id: 37418fba07f1c858a19df06e702ed55608867a36
  • Loading branch information
kagamiori authored and facebook-github-bot committed Nov 22, 2023
1 parent 3bb5f01 commit fab2181
Show file tree
Hide file tree
Showing 5 changed files with 181 additions and 10 deletions.
26 changes: 18 additions & 8 deletions velox/docs/functions/presto/datetime.rst
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,15 @@ Date and Time Operators
* - ``+``
- ``interval '1' second + interval '1' hour``
- ``0 01:00:01.000``
* - ``+``
- ``timestamp '1970-01-01 00:00:00.000' + interval '1' second``
- ``1970-01-01 00:00:01.000``
* - ``-``
- ``interval '1' hour - interval '1' second``
- ``0 00:59:59.000``
* - ``-``
- ``timestamp '1970-01-01 00:00:00.000' - interval '1' second``
- ``1969-12-31 23:59:59.000``
* - ``*``
- ``interval '1' second * 2``
- ``0 00:00:02.000``
Expand All @@ -36,17 +42,21 @@ Date and Time Operators

.. function:: plus(x, y) -> [same as x]

Returns the sum of ``x`` and ``y``. ``x`` and ``y`` are both intervals day
to second. Returns ``-106751991167 07:12:55.808`` when the addition
overflows in positive. Returns ``106751991167 07:12:55.807`` when the
addition overflows in negative.
Returns the sum of ``x`` and ``y``. Both ``x`` and ``y`` are intervals day
to second or one of them can be timestamp. For addition of two intervals day to
second, returns ``-106751991167 07:12:55.808`` when the addition overflows
in positive and returns ``106751991167 07:12:55.807`` when the addition
overflows in negative. When addition of a timestamp with an interval day to
second, overflowed results are wrapped around.

.. function:: minus(x, y) -> [same as x]

Returns the result of subtracting ``y`` from ``x``. ``x`` and ``y`` are
both intervals day to second. Returns ``-106751991167 07:12:55.808`` when
the subtraction overflows in positive. Returns ``106751991167 07:12:55.807``
when the subtraction overflows in negative.
Returns the result of subtracting ``y`` from ``x``. Both ``x`` and ``y``
are intervals day to second or ``x`` can be timestamp. For subtraction of
two intervals day to second, returns ``-106751991167 07:12:55.808`` when
the subtraction overflows in positive and returns ``106751991167 07:12:55.807``
when the subtraction overflows in negative. For subtraction of an interval
day to second from a timestamp, overflowed results are wrapped around.

.. function:: multiply(interval day to second, x) -> interval day to second

Expand Down
56 changes: 55 additions & 1 deletion velox/functions/prestosql/DateTimeFunctions.h
Original file line number Diff line number Diff line change
Expand Up @@ -401,7 +401,7 @@ struct DatePlusIntervalDayTime {
};

template <typename T>
struct TimestampMinusIntervalDayTime {
struct TimestampMinusFunction {
VELOX_DEFINE_FUNCTION_TYPES(T);

FOLLY_ALWAYS_INLINE void call(
Expand All @@ -412,6 +412,60 @@ struct TimestampMinusIntervalDayTime {
}
};

template <typename T>
struct TimestampPlusIntervalDayTime {
VELOX_DEFINE_FUNCTION_TYPES(T);

FOLLY_ALWAYS_INLINE void call(
out_type<Timestamp>& result,
const arg_type<Timestamp>& a,
const arg_type<IntervalDayTime>& b)
#if defined(__has_feature)
#if __has_feature(__address_sanitizer__)
__attribute__((__no_sanitize__("signed-integer-overflow")))
#endif
#endif
{
result = Timestamp::fromMillisNoError(a.toMillis() + b);
}
};

template <typename T>
struct IntervalDayTimePlusTimestamp {
VELOX_DEFINE_FUNCTION_TYPES(T);

FOLLY_ALWAYS_INLINE void call(
out_type<Timestamp>& result,
const arg_type<IntervalDayTime>& a,
const arg_type<Timestamp>& b)
#if defined(__has_feature)
#if __has_feature(__address_sanitizer__)
__attribute__((__no_sanitize__("signed-integer-overflow")))
#endif
#endif
{
result = Timestamp::fromMillisNoError(a + b.toMillis());
}
};

template <typename T>
struct TimestampMinusIntervalDayTime {
VELOX_DEFINE_FUNCTION_TYPES(T);

FOLLY_ALWAYS_INLINE void call(
out_type<Timestamp>& result,
const arg_type<Timestamp>& a,
const arg_type<IntervalDayTime>& b)
#if defined(__has_feature)
#if __has_feature(__address_sanitizer__)
__attribute__((__no_sanitize__("signed-integer-overflow")))
#endif
#endif
{
result = Timestamp::fromMillisNoError(a.toMillis() - b);
}
};

template <typename T>
struct DayOfWeekFunction : public InitSessionTimezone<T>,
public TimestampWithTimezoneSupport<T> {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,21 @@ void registerSimpleFunctions(const std::string& prefix) {
{prefix + "plus"});
registerFunction<
TimestampMinusIntervalDayTime,
Timestamp,
Timestamp,
IntervalDayTime>({prefix + "minus"});
registerFunction<
TimestampPlusIntervalDayTime,
Timestamp,
Timestamp,
IntervalDayTime>({prefix + "plus"});
registerFunction<
IntervalDayTimePlusTimestamp,
Timestamp,
IntervalDayTime,
Timestamp>({prefix + "plus"});
registerFunction<
TimestampMinusFunction,
IntervalDayTime,
Timestamp,
Timestamp>({prefix + "minus"});
Expand Down
79 changes: 78 additions & 1 deletion velox/functions/prestosql/tests/DateTimeFunctionsTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -837,7 +837,84 @@ TEST_F(DateTimeFunctionsTest, plusMinusDateIntervalDayTime) {
EXPECT_THROW(minus(baseDate, partDay), VeloxUserError);
}

TEST_F(DateTimeFunctionsTest, minusTimestampIntervalDayTime) {
TEST_F(DateTimeFunctionsTest, plusMinusTimestampIntervalDayTime) {
constexpr int64_t kLongMax = std::numeric_limits<int64_t>::max();
constexpr int64_t kLongMin = std::numeric_limits<int64_t>::min();

const auto minus = [&](std::optional<Timestamp> timestamp,
std::optional<int64_t> interval) {
return evaluateOnce<Timestamp>(
"c0 - c1",
makeRowVector({
makeNullableFlatVector<Timestamp>({timestamp}),
makeNullableFlatVector<int64_t>({interval}, INTERVAL_DAY_TIME()),
}));
};

EXPECT_EQ(std::nullopt, minus(std::nullopt, std::nullopt));
EXPECT_EQ(std::nullopt, minus(std::nullopt, 1));
EXPECT_EQ(std::nullopt, minus(Timestamp(0, 0), std::nullopt));
EXPECT_EQ(Timestamp(0, 0), minus(Timestamp(0, 0), 0));
EXPECT_EQ(Timestamp(0, 0), minus(Timestamp(10, 0), 10'000));
EXPECT_EQ(Timestamp(-10, 0), minus(Timestamp(10, 0), 20'000));
EXPECT_EQ(
Timestamp(-2, 50 * Timestamp::kNanosecondsInMillisecond),
minus(Timestamp(0, 50 * Timestamp::kNanosecondsInMillisecond), 2'000));
EXPECT_EQ(
Timestamp(-3, 995 * Timestamp::kNanosecondsInMillisecond),
minus(Timestamp(0, 0), 2'005));
EXPECT_EQ(
Timestamp(9223372036854774, 809000000),
minus(Timestamp(-1, 0), kLongMax));
EXPECT_EQ(
Timestamp(-9223372036854775, 192000000),
minus(Timestamp(1, 0), kLongMin));

const auto plusAndVerify = [&](std::optional<Timestamp> timestamp,
std::optional<int64_t> interval,
std::optional<Timestamp> expected) {
EXPECT_EQ(
expected,
evaluateOnce<Timestamp>(
"c0 + c1",
makeRowVector({
makeNullableFlatVector<Timestamp>({timestamp}),
makeNullableFlatVector<int64_t>(
{interval}, INTERVAL_DAY_TIME()),
})));
EXPECT_EQ(
expected,
evaluateOnce<Timestamp>(
"c1 + c0",
makeRowVector({
makeNullableFlatVector<Timestamp>({timestamp}),
makeNullableFlatVector<int64_t>(
{interval}, INTERVAL_DAY_TIME()),
})));
};

plusAndVerify(std::nullopt, std::nullopt, std::nullopt);
plusAndVerify(std::nullopt, 1, std::nullopt);
plusAndVerify(Timestamp(0, 0), std::nullopt, std::nullopt);
plusAndVerify(Timestamp(0, 0), 0, Timestamp(0, 0));
plusAndVerify(Timestamp(0, 0), 10'000, Timestamp(10, 0));
plusAndVerify(
Timestamp(0, 0),
20'005,
Timestamp(20, 5 * Timestamp::kNanosecondsInMillisecond));
plusAndVerify(
Timestamp(0, 0),
-30'005,
Timestamp(-31, 995 * Timestamp::kNanosecondsInMillisecond));
plusAndVerify(
Timestamp(1, 0), kLongMax, Timestamp(-9223372036854775, 191000000));
plusAndVerify(
Timestamp(0, 0), kLongMin, Timestamp(-9223372036854776, 192000000));
plusAndVerify(
Timestamp(-1, 0), kLongMin, Timestamp(9223372036854774, 808000000));
}

TEST_F(DateTimeFunctionsTest, minusTimestamp) {
const auto minus = [&](std::optional<int64_t> t1, std::optional<int64_t> t2) {
const auto timestamp1 = (t1.has_value()) ? Timestamp(t1.value(), 0)
: std::optional<Timestamp>();
Expand Down
15 changes: 15 additions & 0 deletions velox/type/Timestamp.h
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,21 @@ struct Timestamp {
return Timestamp(second, nano);
}

static Timestamp fromMillisNoError(int64_t millis)
#if defined(__has_feature)
#if __has_feature(__address_sanitizer__)
__attribute__((__no_sanitize__("signed-integer-overflow")))
#endif
#endif
{
if (millis >= 0 || millis % 1'000 == 0) {
return Timestamp(millis / 1'000, (millis % 1'000) * 1'000'000);
}
auto second = millis / 1'000 - 1;
auto nano = ((millis - second * 1'000) % 1'000) * 1'000'000;
return Timestamp(second, nano);
}

static Timestamp fromMicros(int64_t micros) {
if (micros >= 0 || micros % 1'000'000 == 0) {
return Timestamp(micros / 1'000'000, (micros % 1'000'000) * 1'000);
Expand Down

0 comments on commit fab2181

Please sign in to comment.