diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 9bd8c5f70c649d373edee068071e6892cdd49c39..df9d2a9ac4d65b929c16fa6a3ba488290704c179 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -14,3 +14,4 @@ nebula_add_subdirectory(context) nebula_add_subdirectory(scheduler) nebula_add_subdirectory(visitor) nebula_add_subdirectory(mock) +nebula_add_subdirectory(optimizer) diff --git a/src/daemons/CMakeLists.txt b/src/daemons/CMakeLists.txt index e4acc4d64ebe3daa24bf5f49f544ee444ae39a31..b4a05c3487bd678935dc6359927fbc3938ebf0c4 100644 --- a/src/daemons/CMakeLists.txt +++ b/src/daemons/CMakeLists.txt @@ -22,6 +22,7 @@ nebula_add_executable( $<TARGET_OBJECTS:scheduler_obj> $<TARGET_OBJECTS:idgenerator_obj> $<TARGET_OBJECTS:context_obj> + $<TARGET_OBJECTS:optimizer_obj> $<TARGET_OBJECTS:graph_flags_obj> $<TARGET_OBJECTS:graph_auth_obj> $<TARGET_OBJECTS:common_time_function_obj> diff --git a/src/optimizer/CMakeLists.txt b/src/optimizer/CMakeLists.txt new file mode 100644 index 0000000000000000000000000000000000000000..a38351553cedf9f9ff698e876cf324d0bf9163e6 --- /dev/null +++ b/src/optimizer/CMakeLists.txt @@ -0,0 +1,11 @@ +# Copyright (c) 2020 vesoft inc. All rights reserved. +# +# This source code is licensed under Apache 2.0 License, +# attached with Common Clause Condition 1.0, found in the LICENSES directory. + +nebula_add_library( + optimizer_obj OBJECT + OptimizerUtils.cpp +) + +nebula_add_subdirectory(test) diff --git a/src/optimizer/OptimizerUtils.cpp b/src/optimizer/OptimizerUtils.cpp new file mode 100644 index 0000000000000000000000000000000000000000..9452af61793446af1294600b6008022720409de5 --- /dev/null +++ b/src/optimizer/OptimizerUtils.cpp @@ -0,0 +1,324 @@ +/* Copyright (c) 2020 vesoft inc. All rights reserved. + * + * This source code is licensed under Apache 2.0 License, + * attached with Common Clause Condition 1.0, found in the LICENSES directory. + */ + +#include "optimizer/OptimizerUtils.h" +namespace nebula { +namespace graph { + +Value OptimizerUtils::boundValue(const meta::cpp2::ColumnDef& col, + BoundValueOperator op, + const Value& v) { + switch (op) { + case BoundValueOperator::GREATER_THAN : { + return boundValueWithGT(col, v); + } + case BoundValueOperator::LESS_THAN : { + return boundValueWithLT(col, v); + } + case BoundValueOperator::MAX : { + return boundValueWithMax(col, v); + } + case BoundValueOperator::MIN : { + return boundValueWithMin(col, v); + } + } + return Value(NullType::BAD_TYPE); +} + +Value OptimizerUtils::boundValueWithGT(const meta::cpp2::ColumnDef& col, const Value& v) { + auto type = SchemaUtil::propTypeToValueType(col.get_type()); + switch (type) { + case Value::Type::INT : { + if (v.getInt() == std::numeric_limits<int64_t>::max()) { + return v; + } else { + return v + 1; + } + } + case Value::Type::FLOAT : { + if (v.getFloat() > 0.0) { + if (v.getFloat() == std::numeric_limits<double_t>::max()) { + return v; + } + } else if (v.getFloat() == 0.0) { + return Value(std::numeric_limits<double_t>::min()); + } else { + if (v.getFloat() == -std::numeric_limits<double_t>::min()) { + return Value(0.0); + } + } + return v.getFloat() + 0.0000000000000001; + } + case Value::Type::BOOL: { + return v; + } + case Value::Type::STRING : { + if (!col.__isset.type_length || col.get_type_length() == nullptr) { + return Value(NullType::BAD_TYPE); + } + std::vector<unsigned char> bytes(v.getStr().begin(), v.getStr().end()); + bytes.resize(*col.get_type_length()); + for (size_t i = bytes.size();; i--) { + if (i > 0) { + if (bytes[i-1]++ != 255) break; + } else { + return Value(std::string(*col.get_type_length(), '\377')); + } + } + return Value(std::string(bytes.begin(), bytes.end())); + } + case Value::Type::DATE : { + if (Date(std::numeric_limits<int16_t>::max(), 12, 31) == v.getDate()) { + return v.getDate(); + } else if (Date() == v.getDate()) { + return Date(0, 1, 2); + } + auto d = v.getDate(); + if (d.day < 31) { + d.day += 1; + } else { + d.day = 1; + if (d.month < 12) { + d.month += 1; + } else { + d.month = 1; + if (d.year < std::numeric_limits<int16_t>::max()) { + d.year += 1; + } else { + return v.getDate(); + } + } + } + return Value(d); + } + case Value::Type::DATETIME : { + auto dt = v.getDateTime(); + // Ignore the time zone. + if (dt.microsec < std::numeric_limits<int32_t>::max()) { + dt.microsec = dt.microsec + 1; + } else { + dt.microsec = 1; + if (dt.sec < 60) { + dt.sec += 1; + } else { + dt.sec = 1; + if (dt.minute < 60) { + dt.minute += 1; + } else { + dt.minute = 1; + if (dt.hour < 24) { + dt.hour += 1; + } else { + dt.hour = 1; + if (dt.day < 31) { + dt.day += 1; + } else { + dt.day = 1; + if (dt.month < 12) { + dt.month += 1; + } else { + dt.month = 1; + if (dt.year < std::numeric_limits<int16_t>::max()) { + dt.year += 1; + } else { + return v.getDateTime(); + } + } + } + } + } + } + } + return Value(dt); + } + default : { + return Value(NullType::BAD_TYPE); + } + } +} + +Value OptimizerUtils::boundValueWithLT(const meta::cpp2::ColumnDef& col, const Value& v) { + auto type = SchemaUtil::propTypeToValueType(col.get_type()); + switch (type) { + case Value::Type::INT : { + if (v.getInt() == std::numeric_limits<int64_t>::min()) { + return v; + } else { + return v - 1; + } + } + case Value::Type::FLOAT : { + if (v.getFloat() < 0.0) { + if (v.getFloat() == -std::numeric_limits<double_t>::max()) { + return v; + } else if (v.getFloat() == -std::numeric_limits<double_t>::min()) { + return Value(0.0); + } + } else if (v.getFloat() == 0.0) { + return Value(-std::numeric_limits<double_t>::min()); + } + return v.getFloat() - 0.0000000000000001; + } + case Value::Type::BOOL: { + return v; + } + case Value::Type::STRING : { + if (!col.__isset.type_length || col.get_type_length() == nullptr) { + return Value(NullType::BAD_TYPE); + } + std::vector<unsigned char> bytes(v.getStr().begin(), v.getStr().end()); + bytes.resize(*col.get_type_length()); + for (size_t i = bytes.size();; i--) { + if (i > 0) { + if (bytes[i-1]-- != 0) break; + } else { + return Value(std::string(*col.get_type_length(), '\0')); + } + } + return Value(std::string(bytes.begin(), bytes.end())); + } + case Value::Type::DATE : { + if (Date() == v.getDate()) { + return v.getDate(); + } + auto d = v.getDate(); + if (d.day > 1) { + d.day -= 1; + } else { + d.day = 31; + if (d.month > 1) { + d.month -= 1; + } else { + d.month = 12; + if (d.year > 1) { + d.year -= 1; + } else { + return v.getDate(); + } + } + } + return Value(d); + } + case Value::Type::DATETIME : { + if (DateTime() == v.getDateTime()) { + return v.getDateTime(); + } + auto dt = v.getDateTime(); + if (dt.microsec > 1) { + dt.microsec -= 1; + } else { + dt.microsec = std::numeric_limits<int32_t>::max(); + if (dt.sec > 1) { + dt.sec -= 1; + } else { + dt.sec = 60; + if (dt.minute > 1) { + dt.minute -= 1; + } else { + dt.minute = 60; + if (dt.hour > 1) { + dt.hour -= 1; + } else { + dt.hour = 24; + if (dt.day > 1) { + dt.day -= 1; + } else { + dt.day = 31; + if (dt.month > 1) { + dt.month -= 1; + } else { + dt.month = 12; + if (dt.year > 1) { + dt.year -= 1; + } else { + return v.getDateTime(); + } + } + } + } + } + } + } + return Value(dt); + } + default : + return Value(NullType::BAD_TYPE); + } +} + +Value OptimizerUtils::boundValueWithMax(const meta::cpp2::ColumnDef& col, const Value& v) { + auto type = SchemaUtil::propTypeToValueType(col.get_type()); + switch (type) { + case Value::Type::INT : { + return Value(std::numeric_limits<int64_t>::max()); + } + case Value::Type::FLOAT : { + return Value(std::numeric_limits<double>::max()); + } + case Value::Type::BOOL: { + return v; + } + case Value::Type::STRING : { + if (!col.__isset.type_length || col.get_type_length() == nullptr) { + return Value(NullType::BAD_TYPE); + } + return Value(std::string(*col.get_type_length(), '\377')); + } + case Value::Type::DATE : { + Date d; + d.year = std::numeric_limits<int16_t>::max(); + d.month = 12; + d.day = 31; + return Value(d); + } + case Value::Type::DATETIME : { + DateTime dt; + dt.year = std::numeric_limits<int16_t>::max(); + dt.month = 12; + dt.day = 31; + dt.hour = 24; + dt.minute = 60; + dt.sec = 60; + dt.microsec = std::numeric_limits<int32_t>::max(); + dt.timezone = std::numeric_limits<int32_t>::max(); + return Value(dt); + } + default : + return Value(NullType::BAD_TYPE); + } +} + +Value OptimizerUtils::boundValueWithMin(const meta::cpp2::ColumnDef& col, const Value& v) { + auto type = SchemaUtil::propTypeToValueType(col.get_type()); + switch (type) { + case Value::Type::INT : { + return Value(std::numeric_limits<int64_t>::min()); + } + case Value::Type::FLOAT : { + return Value(-std::numeric_limits<double>::max()); + } + case Value::Type::BOOL: { + return v; + } + case Value::Type::STRING : { + if (!col.__isset.type_length || col.get_type_length() == nullptr) { + return Value(NullType::BAD_TYPE); + } + return Value(std::string(*col.get_type_length(), '\0')); + } + case Value::Type::DATE : { + return Value(Date()); + } + case Value::Type::DATETIME : { + return Value(DateTime()); + } + default : + return Value(NullType::BAD_TYPE); + } +} + +} // namespace graph +} // namespace nebula diff --git a/src/optimizer/OptimizerUtils.h b/src/optimizer/OptimizerUtils.h new file mode 100644 index 0000000000000000000000000000000000000000..60f526b51c69831431f2aa50f678fac0cfc7fa3c --- /dev/null +++ b/src/optimizer/OptimizerUtils.h @@ -0,0 +1,43 @@ +/* Copyright (c) 2020 vesoft inc. All rights reserved. + * + * This source code is licensed under Apache 2.0 License, + * attached with Common Clause Condition 1.0, found in the LICENSES directory. + */ +#ifndef NEBULA_GRAPH_OPTIMIZER_OPTIMIZERUTILS_H_ +#define NEBULA_GRAPH_OPTIMIZER_OPTIMIZERUTILS_H_ + +#include "common/base/Base.h" +#include "util/SchemaUtil.h" +#include <common/interface/gen-cpp2/meta_types.h> + +namespace nebula { +namespace graph { + +class OptimizerUtils { +public: + enum class BoundValueOperator { + GREATER_THAN = 0, + LESS_THAN, + MAX, + MIN, + }; + +public: + OptimizerUtils() = delete; + + static Value boundValue(const meta::cpp2::ColumnDef& col, + BoundValueOperator op, + const Value& v = Value()); + + static Value boundValueWithGT(const meta::cpp2::ColumnDef& col, const Value& v); + + static Value boundValueWithLT(const meta::cpp2::ColumnDef& col, const Value& v); + + static Value boundValueWithMax(const meta::cpp2::ColumnDef& col, const Value& v); + + static Value boundValueWithMin(const meta::cpp2::ColumnDef& col, const Value& v); +}; + +} // namespace graph +} // namespace nebula +#endif // NEBULA_GRAPH_OPTIMIZER_OPTIMIZERUTILS_H_ diff --git a/src/optimizer/test/CMakeLists.txt b/src/optimizer/test/CMakeLists.txt new file mode 100644 index 0000000000000000000000000000000000000000..771f9f97ef4cd98e320dccb247fadb8500835249 --- /dev/null +++ b/src/optimizer/test/CMakeLists.txt @@ -0,0 +1,48 @@ +nebula_add_test( + NAME + index_bound_value_test + SOURCES + IndexBoundValueTest.cpp + OBJECTS + $<TARGET_OBJECTS:common_base_obj> + $<TARGET_OBJECTS:common_concurrent_obj> + $<TARGET_OBJECTS:common_datatypes_obj> + $<TARGET_OBJECTS:common_expression_obj> + $<TARGET_OBJECTS:common_function_manager_obj> + $<TARGET_OBJECTS:common_time_obj> + $<TARGET_OBJECTS:common_time_function_obj> + $<TARGET_OBJECTS:common_meta_thrift_obj> + $<TARGET_OBJECTS:common_meta_client_obj> + $<TARGET_OBJECTS:common_meta_obj> + $<TARGET_OBJECTS:common_storage_thrift_obj> + $<TARGET_OBJECTS:common_graph_thrift_obj> + $<TARGET_OBJECTS:common_conf_obj> + $<TARGET_OBJECTS:common_fs_obj> + $<TARGET_OBJECTS:common_thrift_obj> + $<TARGET_OBJECTS:common_common_thrift_obj> + $<TARGET_OBJECTS:common_thread_obj> + $<TARGET_OBJECTS:common_file_based_cluster_id_man_obj> + $<TARGET_OBJECTS:common_charset_obj> + $<TARGET_OBJECTS:common_encryption_obj> + $<TARGET_OBJECTS:common_http_client_obj> + $<TARGET_OBJECTS:common_process_obj> + $<TARGET_OBJECTS:common_agg_function_obj> + $<TARGET_OBJECTS:idgenerator_obj> + $<TARGET_OBJECTS:expr_visitor_obj> + $<TARGET_OBJECTS:session_obj> + $<TARGET_OBJECTS:graph_auth_obj> + $<TARGET_OBJECTS:graph_flags_obj> + $<TARGET_OBJECTS:util_obj> + $<TARGET_OBJECTS:planner_obj> + $<TARGET_OBJECTS:parser_obj> + $<TARGET_OBJECTS:context_obj> + $<TARGET_OBJECTS:validator_obj> + $<TARGET_OBJECTS:optimizer_obj> + LIBRARIES + proxygenhttpserver + proxygenlib + ${THRIFT_LIBRARIES} + wangle + gtest + gtest_main +) diff --git a/src/optimizer/test/IndexBoundValueTest.cpp b/src/optimizer/test/IndexBoundValueTest.cpp new file mode 100644 index 0000000000000000000000000000000000000000..0ce8ce19b285d78c6a50c599d7afbfbe90f6ea60 --- /dev/null +++ b/src/optimizer/test/IndexBoundValueTest.cpp @@ -0,0 +1,278 @@ +/* Copyright (c) 2020 vesoft inc. All rights reserved. + * + * This source code is licensed under Apache 2.0 License, + * attached with Common Clause Condition 1.0, found in the LICENSES directory. + */ + +#include <gtest/gtest.h> +#include "optimizer/OptimizerUtils.h" + +namespace nebula { +namespace graph { + +using OP = OptimizerUtils::BoundValueOperator; + +TEST(IndexBoundValueTest, StringTest) { + meta::cpp2::ColumnDef col; + { + col.set_type(meta::cpp2::PropertyType::FIXED_STRING); + col.set_type_length(8); + } + std::vector<unsigned char> max = {255, 255, 255, 255, 255, 255, 255, 255}; + std::vector<unsigned char> min = {'\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0'}; + auto maxStr = std::string(max.begin(), max.end()); + auto minStr = std::string(min.begin(), min.end()); + EXPECT_EQ(maxStr, OptimizerUtils::boundValue(col, OP::MAX, Value("aa")).getStr()); + EXPECT_EQ(maxStr, OptimizerUtils::boundValue(col, OP::MAX, Value(maxStr)).getStr()); + EXPECT_EQ(minStr, OptimizerUtils::boundValue(col, OP::MIN, Value("aa")).getStr()); + EXPECT_EQ(minStr, OptimizerUtils::boundValue(col, OP::MIN, Value("")).getStr()); + + std::string retVal = OptimizerUtils::boundValue(col, OP::GREATER_THAN, Value("aa")).getStr(); + std::vector<unsigned char> expected = {'a', 'a', '\0', '\0', '\0', '\0', '\0', '\001'}; + EXPECT_EQ(std::string(expected.begin(), expected.end()), retVal); + + EXPECT_EQ(maxStr, OptimizerUtils::boundValue(col, OP::GREATER_THAN, Value(maxStr)).getStr()); + + retVal = OptimizerUtils::boundValue(col, OP::LESS_THAN, Value("aa")).getStr(); + expected = {'a', '`', 255, 255, 255, 255, 255, 255}; + EXPECT_EQ(std::string(expected.begin(), expected.end()), retVal); + + retVal = OptimizerUtils::boundValue(col, OP::LESS_THAN, Value("")).getStr(); + EXPECT_EQ(minStr, retVal); + + retVal = OptimizerUtils::boundValue(col, OP::LESS_THAN, Value(minStr)).getStr(); + EXPECT_EQ(minStr, retVal); + + { + auto actual = "ABCDEFGHIJKLMN"; + auto expectGT = "ABCDEFGI"; + auto expectLT = "ABCDEFGG"; + EXPECT_EQ(expectGT, + OptimizerUtils::boundValue(col, OP::GREATER_THAN, Value(actual)).getStr()); + EXPECT_EQ(expectLT, + OptimizerUtils::boundValue(col, OP::LESS_THAN, Value(actual)).getStr()); + } + { + std::vector<unsigned char> act = {255, 255, 255, 254, 255, 255, 255, 255}; + std::vector<unsigned char> exp = {255, 255, 255, 255, 0, 0, 0, 0}; + auto actStr = std::string(act.begin(), act.end()); + auto expStr = std::string(exp.begin(), exp.end()); + EXPECT_EQ(expStr, + OptimizerUtils::boundValue(col, OP::GREATER_THAN, Value(actStr)).getStr()); + } + { + std::vector<unsigned char> act = {255, 255, 255, 0, 0, 0, 0, 0}; + std::vector<unsigned char> exp = {255, 255, 254, 255, 255, 255, 255, 255}; + auto actStr = std::string(act.begin(), act.end()); + auto expStr = std::string(exp.begin(), exp.end()); + EXPECT_EQ(expStr, OptimizerUtils::boundValue(col, OP::LESS_THAN, Value(actStr)).getStr()); + } +} + +TEST(IndexBoundValueTest, BoolTest) { + meta::cpp2::ColumnDef col; + { + col.set_type(meta::cpp2::PropertyType::BOOL); + } + EXPECT_TRUE(OptimizerUtils::boundValue(col, OP::MIN, Value(true)).getBool()); + EXPECT_TRUE(OptimizerUtils::boundValue(col, OP::MAX, Value(true)).getBool()); + EXPECT_TRUE(OptimizerUtils::boundValue(col, OP::GREATER_THAN, Value(true)).getBool()); + EXPECT_TRUE(OptimizerUtils::boundValue(col, OP::LESS_THAN, Value(true)).getBool()); + + EXPECT_FALSE(OptimizerUtils::boundValue(col, OP::MIN, Value(false)).getBool()); + EXPECT_FALSE(OptimizerUtils::boundValue(col, OP::MAX, Value(false)).getBool()); + EXPECT_FALSE(OptimizerUtils::boundValue(col, OP::GREATER_THAN, Value(false)).getBool()); + EXPECT_FALSE(OptimizerUtils::boundValue(col, OP::LESS_THAN, Value(false)).getBool()); + + EXPECT_TRUE(OptimizerUtils::boundValue(col, OP::MIN, Value(NullType::__NULL__)).isNull()); + EXPECT_TRUE(OptimizerUtils::boundValue(col, OP::MAX, Value(NullType::__NULL__)).isNull()); + EXPECT_TRUE(OptimizerUtils::boundValue(col, OP::GREATER_THAN, + Value(NullType::__NULL__)).isNull()); + EXPECT_TRUE(OptimizerUtils::boundValue(col, OP::LESS_THAN, Value(NullType::__NULL__)).isNull()); +} + +TEST(IndexBoundValueTest, IntTest) { + meta::cpp2::ColumnDef col; + { + col.set_type(meta::cpp2::PropertyType::INT64); + } + auto maxInt = std::numeric_limits<int64_t>::max(); + auto minInt = std::numeric_limits<int64_t>::min(); + EXPECT_EQ(maxInt, OptimizerUtils::boundValue(col, OP::MAX, Value(maxInt)).getInt()); + EXPECT_EQ(maxInt, OptimizerUtils::boundValue(col, OP::MAX, Value(1L)).getInt()); + EXPECT_EQ(minInt, OptimizerUtils::boundValue(col, OP::MIN, Value(minInt)).getInt()); + EXPECT_EQ(minInt, OptimizerUtils::boundValue(col, OP::MIN, Value(1L)).getInt()); + + EXPECT_EQ(minInt, OptimizerUtils::boundValue(col, OP::LESS_THAN, Value(minInt)).getInt()); + EXPECT_EQ(maxInt, OptimizerUtils::boundValue(col, OP::GREATER_THAN, Value(maxInt)).getInt()); + EXPECT_EQ(minInt + 1, + OptimizerUtils::boundValue(col, OP::GREATER_THAN, Value(minInt)).getInt()); + EXPECT_EQ(maxInt - 1, + OptimizerUtils::boundValue(col, OP::LESS_THAN, Value(maxInt)).getInt()); + + EXPECT_EQ(1, OptimizerUtils::boundValue(col, OP::GREATER_THAN, Value(0L)).getInt()); + EXPECT_EQ(-1, OptimizerUtils::boundValue(col, OP::LESS_THAN, Value(0L)).getInt()); + + EXPECT_EQ(6, OptimizerUtils::boundValue(col, OP::GREATER_THAN, Value(5L)).getInt()); + EXPECT_EQ(4, OptimizerUtils::boundValue(col, OP::LESS_THAN, Value(5L)).getInt()); +} + +TEST(IndexBoundValueTest, DoubleTest) { + meta::cpp2::ColumnDef col; + { + col.set_type(meta::cpp2::PropertyType::DOUBLE); + } + auto maxDouble = std::numeric_limits<double_t>::max(); + auto minDouble = std::numeric_limits<double_t>::min(); + EXPECT_EQ(maxDouble, OptimizerUtils::boundValue(col, OP::MAX, Value(maxDouble)).getFloat()); + EXPECT_EQ(maxDouble, OptimizerUtils::boundValue(col, OP::MAX, Value(1.1)).getFloat()); + EXPECT_EQ(-maxDouble, OptimizerUtils::boundValue(col, OP::MIN, Value(minDouble)).getFloat()); + EXPECT_EQ(-maxDouble, OptimizerUtils::boundValue(col, OP::MIN, Value(1.1)).getFloat()); + + EXPECT_EQ(0.0, + OptimizerUtils::boundValue(col, OP::LESS_THAN, Value(-minDouble)).getFloat()); + EXPECT_EQ(maxDouble - 0.0000000000000001, + OptimizerUtils::boundValue(col, OP::LESS_THAN, Value(maxDouble)).getFloat()); + EXPECT_EQ(-minDouble, OptimizerUtils::boundValue(col, OP::LESS_THAN, Value(0.0)).getFloat()); + + EXPECT_EQ(5.1 - 0.0000000000000001, + OptimizerUtils::boundValue(col, OP::LESS_THAN, Value(5.1))); + + EXPECT_EQ(-(5.1 + 0.0000000000000001), + OptimizerUtils::boundValue(col, OP::LESS_THAN, Value(-5.1))); + + EXPECT_EQ(maxDouble, + OptimizerUtils::boundValue(col, OP::GREATER_THAN, Value(maxDouble)).getFloat()); + EXPECT_EQ(0.0, + OptimizerUtils::boundValue(col, OP::GREATER_THAN, Value(-minDouble)).getFloat()); + + EXPECT_EQ(minDouble, OptimizerUtils::boundValue(col, OP::GREATER_THAN, Value(0.0)).getFloat()); + + EXPECT_EQ(5.1 + 0.0000000000000001, + OptimizerUtils::boundValue(col, OP::GREATER_THAN, Value(5.1)).getFloat()); + + EXPECT_EQ(-(5.1 - 0.0000000000000001), + OptimizerUtils::boundValue(col, OP::GREATER_THAN, Value(-5.1)).getFloat()); +} + +TEST(IndexBoundValueTest, DateTest) { + meta::cpp2::ColumnDef col; + { + col.set_type(meta::cpp2::PropertyType::DATE); + } + auto maxYear = std::numeric_limits<int16_t>::max(); + EXPECT_EQ(Date(maxYear, 12, 31), + OptimizerUtils::boundValue(col, OP::MAX, Value(Date()))); + EXPECT_EQ(Date(), + OptimizerUtils::boundValue(col, OP::MIN, Value(Date(maxYear, 12, 31)))); + EXPECT_EQ(Date(2021, 1, 1), + OptimizerUtils::boundValue(col, OP::GREATER_THAN, Value(Date(2020, 12, 31)))); + EXPECT_EQ(Date(maxYear, 12, 31), + OptimizerUtils::boundValue(col, OP::GREATER_THAN, Value(Date(maxYear, 12, 31)))); + EXPECT_EQ(Date(2020, 1, 2), + OptimizerUtils::boundValue(col, OP::GREATER_THAN, Value(Date(2020, 1, 1)))); + EXPECT_EQ(Date(0, 1, 2), OptimizerUtils::boundValue(col, OP::GREATER_THAN, Value(Date()))); + EXPECT_EQ(Date(), OptimizerUtils::boundValue(col, OP::LESS_THAN, Value(Date()))); + EXPECT_EQ(Date(2019, 12, 30), + OptimizerUtils::boundValue(col, OP::LESS_THAN, Value(Date(2019, 12, 31)))); + EXPECT_EQ(Date(2018, 12, 31), + OptimizerUtils::boundValue(col, OP::LESS_THAN, Value(Date(2019, 1, 1)))); +} + +TEST(IndexBoundValueTest, DateTimeTest) { + meta::cpp2::ColumnDef col; + { + col.set_type(meta::cpp2::PropertyType::DATETIME); + } + DateTime maxDT; + { + maxDT.microsec = std::numeric_limits<int32_t>::max(); + maxDT.sec = 60; + maxDT.minute = 60; + maxDT.hour = 24; + maxDT.day = 31; + maxDT.month = 12; + maxDT.year = std::numeric_limits<int16_t>::max(); + maxDT.timezone = std::numeric_limits<int32_t>::max(); + } + + DateTime minDT = DateTime(); + + EXPECT_EQ(maxDT, OptimizerUtils::boundValue(col, OP::MAX, Value(maxDT)).getDateTime()); + EXPECT_EQ(minDT, OptimizerUtils::boundValue(col, OP::MIN, Value(maxDT)).getDateTime()); + EXPECT_EQ(maxDT, OptimizerUtils::boundValue(col, OP::GREATER_THAN, Value(maxDT)).getDateTime()); + + { + DateTime actual, expect; + actual.microsec = std::numeric_limits<int32_t>::max(); + actual.sec = 60; + actual.minute = 60; + actual.hour = 24; + actual.day = 31; + actual.month = 12; + actual.year = 2020; + actual.timezone = 1; + + expect.microsec = 1; + expect.sec = 1; + expect.minute = 1; + expect.hour = 1; + expect.day = 1; + expect.month = 1; + expect.year = 2021; + expect.timezone = 1; + EXPECT_EQ(expect, + OptimizerUtils::boundValue(col, OP::GREATER_THAN, Value(actual)).getDateTime()); + } + { + DateTime actual, expect; + actual.microsec = std::numeric_limits<int32_t>::max(); + actual.sec = 34; + actual.minute = 60; + actual.hour = 24; + actual.day = 31; + actual.month = 12; + actual.year = 2020; + actual.timezone = 1; + + expect.microsec = 1; + expect.sec = 35; + expect.minute = 60; + expect.hour = 24; + expect.day = 31; + expect.month = 12; + expect.year = 2020; + expect.timezone = 1; + EXPECT_EQ(expect, + OptimizerUtils::boundValue(col, OP::GREATER_THAN, Value(actual)).getDateTime()); + } + { + DateTime expect = DateTime(); + EXPECT_EQ(expect, OptimizerUtils::boundValue(col, OP::LESS_THAN, Value(expect))); + } + { + DateTime actual, expect; + actual.microsec = std::numeric_limits<int32_t>::max(); + actual.sec = 34; + actual.minute = 60; + actual.hour = 24; + actual.day = 31; + actual.month = 12; + actual.year = 2020; + actual.timezone = 1; + + expect.microsec = std::numeric_limits<int32_t>::max() - 1; + expect.sec = 34; + expect.minute = 60; + expect.hour = 24; + expect.day = 31; + expect.month = 12; + expect.year = 2020; + expect.timezone = 1; + EXPECT_EQ(expect, + OptimizerUtils::boundValue(col, OP::LESS_THAN, Value(actual)).getDateTime()); + } +} + +} // namespace graph +} // namespace nebula