Add temporal type arithmetic for queries (#214)

This commit is contained in:
Kostas Kyrimis 2021-09-03 13:43:27 +03:00 committed by Antonio Andelic
parent 086fc47769
commit 8d87e38c64
2 changed files with 104 additions and 9 deletions

View File

@ -843,11 +843,71 @@ inline void EnsureArithmeticallyOk(const TypedValue &a, const TypedValue &b, boo
// checked here because they are handled before this check is performed in
// arithmetic op implementations.
// TODO(antonio2368): Introduce typed value arithmetic
if (!is_legal(a) || !is_legal(b))
throw TypedValueException("Invalid {} operand types {}, {}", op_name, a.type(), b.type());
}
namespace {
std::optional<TypedValue> MaybeDoTemporalTypeAddition(const TypedValue &a, const TypedValue &b) {
// Duration
if (a.IsDuration() && b.IsDuration()) {
return TypedValue(a.ValueDuration() + b.ValueDuration());
}
// Date
if (a.IsDate() && b.IsDuration()) {
return TypedValue(a.ValueDate() + b.ValueDuration());
}
if (a.IsDuration() && b.IsDate()) {
return TypedValue(a.ValueDuration() + b.ValueDate());
}
// LocalTime
if (a.IsLocalTime() && b.IsDuration()) {
return TypedValue(a.ValueLocalTime() + b.ValueDuration());
}
if (a.IsDuration() && b.IsLocalTime()) {
return TypedValue(a.ValueDuration() + b.ValueLocalTime());
}
// LocalDateTime
if (a.IsLocalDateTime() && b.IsDuration()) {
return TypedValue(a.ValueLocalDateTime() + b.ValueDuration());
}
if (a.IsDuration() && b.IsLocalDateTime()) {
return TypedValue(a.ValueDuration() + b.ValueLocalDateTime());
}
return std::nullopt;
}
std::optional<TypedValue> MaybeDoTemporalTypeSubtraction(const TypedValue &a, const TypedValue &b) {
// Duration
if (a.IsDuration() && b.IsDuration()) {
return TypedValue(a.ValueDuration() - b.ValueDuration());
}
// Date
if (a.IsDate() && b.IsDuration()) {
return TypedValue(a.ValueDate() - b.ValueDuration());
}
if (a.IsDate() && b.IsDate()) {
return TypedValue(a.ValueDate() - b.ValueDate());
}
// LocalTime
if (a.IsLocalTime() && b.IsDuration()) {
return TypedValue(a.ValueLocalTime() - b.ValueDuration());
}
if (a.IsLocalTime() && b.IsLocalTime()) {
return TypedValue(a.ValueLocalTime() - b.ValueLocalTime());
}
// LocalDateTime
if (a.IsLocalDateTime() && b.IsDuration()) {
return TypedValue(a.ValueLocalDateTime() - b.ValueDuration());
}
if (a.IsLocalDateTime() && b.IsLocalDateTime()) {
return TypedValue(a.ValueLocalDateTime() - b.ValueLocalDateTime());
}
return std::nullopt;
}
} // namespace
TypedValue operator+(const TypedValue &a, const TypedValue &b) {
if (a.IsNull() || b.IsNull()) return TypedValue(a.GetMemoryResource());
@ -866,6 +926,10 @@ TypedValue operator+(const TypedValue &a, const TypedValue &b) {
return TypedValue(std::move(list), a.GetMemoryResource());
}
if (const auto maybe_add = MaybeDoTemporalTypeAddition(a, b); maybe_add) {
return *maybe_add;
}
EnsureArithmeticallyOk(a, b, true, "addition");
// no more Bool nor Null, summing works on anything from here onward
@ -874,23 +938,21 @@ TypedValue operator+(const TypedValue &a, const TypedValue &b) {
// at this point we only have int and double
if (a.IsDouble() || b.IsDouble()) {
return TypedValue(ToDouble(a) + ToDouble(b), a.GetMemoryResource());
} else {
return TypedValue(a.ValueInt() + b.ValueInt(), a.GetMemoryResource());
}
// TODO(antonio2368): Introduce typed value arithmetic
return TypedValue(a.ValueInt() + b.ValueInt(), a.GetMemoryResource());
}
TypedValue operator-(const TypedValue &a, const TypedValue &b) {
if (a.IsNull() || b.IsNull()) return TypedValue(a.GetMemoryResource());
EnsureArithmeticallyOk(a, b, false, "subtraction");
if (const auto maybe_sub = MaybeDoTemporalTypeSubtraction(a, b); maybe_sub) {
return *maybe_sub;
}
EnsureArithmeticallyOk(a, b, true, "subraction");
// at this point we only have int and double
if (a.IsDouble() || b.IsDouble()) {
return TypedValue(ToDouble(a) - ToDouble(b), a.GetMemoryResource());
} else {
return TypedValue(a.ValueInt() - b.ValueInt(), a.GetMemoryResource());
}
// TODO(antonio2368): Introduce typed value arithmetic
return TypedValue(a.ValueInt() - b.ValueInt(), a.GetMemoryResource());
}
TypedValue operator/(const TypedValue &a, const TypedValue &b) {

View File

@ -292,6 +292,22 @@ TEST_F(TypedValueArithmeticTest, Sum) {
EXPECT_PROP_EQ(TypedValue(2) + TypedValue(in), TypedValue(out1));
EXPECT_PROP_EQ(TypedValue(in) + TypedValue(2), TypedValue(out2));
EXPECT_PROP_EQ(TypedValue(in) + TypedValue(in), TypedValue(out3));
// Temporal Types
// Duration
EXPECT_NO_THROW(TypedValue(utils::Duration(1)) + TypedValue(utils::Duration(1)));
// Date
EXPECT_NO_THROW(TypedValue(utils::Date(1)) + TypedValue(utils::Duration(1)));
EXPECT_NO_THROW(TypedValue(utils::Duration(1)) + TypedValue(utils::Date(1)));
EXPECT_THROW(TypedValue(utils::Date(1)) + TypedValue(utils::Date(1)), TypedValueException);
// LocalTime
EXPECT_NO_THROW(TypedValue(utils::LocalTime(1)) + TypedValue(utils::Duration(1)));
EXPECT_NO_THROW(TypedValue(utils::Duration(1)) + TypedValue(utils::LocalTime(1)));
EXPECT_THROW(TypedValue(utils::LocalTime(1)) + TypedValue(utils::LocalTime(1)), TypedValueException);
// LocalDateTime
EXPECT_NO_THROW(TypedValue(utils::LocalDateTime(1)) + TypedValue(utils::Duration(1)));
EXPECT_NO_THROW(TypedValue(utils::Duration(1)) + TypedValue(utils::LocalDateTime(1)));
EXPECT_THROW(TypedValue(utils::LocalDateTime(1)) + TypedValue(utils::LocalDateTime(1)), TypedValueException);
}
TEST_F(TypedValueArithmeticTest, Difference) {
@ -304,8 +320,25 @@ TEST_F(TypedValueArithmeticTest, Difference) {
// implicit casting
EXPECT_FLOAT_EQ((TypedValue(2) - TypedValue(0.5)).ValueDouble(), 1.5);
EXPECT_FLOAT_EQ((TypedValue(2.5) - TypedValue(2)).ValueDouble(), 0.5);
// Temporal Types
// Duration
EXPECT_NO_THROW(TypedValue(utils::Duration(1)) - TypedValue(utils::Duration(1)));
// Date
EXPECT_NO_THROW(TypedValue(utils::Date(1)) - TypedValue(utils::Duration(1)));
EXPECT_NO_THROW(TypedValue(utils::Date(1)) - TypedValue(utils::Date(1)));
EXPECT_THROW(TypedValue(utils::Duration(1)) - TypedValue(utils::Date(1)), TypedValueException);
// LocalTime
EXPECT_NO_THROW(TypedValue(utils::LocalTime(1)) - TypedValue(utils::Duration(1)));
EXPECT_NO_THROW(TypedValue(utils::LocalTime(1)) - TypedValue(utils::LocalTime(1)));
EXPECT_THROW(TypedValue(utils::Duration(1)) - TypedValue(utils::LocalTime(1)), TypedValueException);
// LocalDateTime
EXPECT_NO_THROW(TypedValue(utils::LocalDateTime(1)) - TypedValue(utils::Duration(1)));
EXPECT_NO_THROW(TypedValue(utils::LocalDateTime(1)) - TypedValue(utils::LocalDateTime(1)));
EXPECT_THROW(TypedValue(utils::Duration(1)) - TypedValue(utils::LocalDateTime(1)), TypedValueException);
}
TEST_F(TypedValueArithmeticTest, Negate) { EXPECT_NO_THROW(-TypedValue(utils::Duration(1))); }
TEST_F(TypedValueArithmeticTest, Divison) {
ExpectArithmeticThrowsAndNull(false, [](const TypedValue &a, const TypedValue &b) { return a / b; });
EXPECT_THROW(TypedValue(1) / TypedValue(0), TypedValueException);