diff --git a/src/optimizer/OptimizerUtils.cpp b/src/optimizer/OptimizerUtils.cpp index c5592026219dad1b9f35cbca9ca248f304edd75a..42c6305d98983ecaadd0e23580ead60c06b075f2 100644 --- a/src/optimizer/OptimizerUtils.cpp +++ b/src/optimizer/OptimizerUtils.cpp @@ -20,10 +20,10 @@ Value OptimizerUtils::boundValue(const meta::cpp2::ColumnDef& col, return boundValueWithLT(col, v); } case BoundValueOperator::MAX : { - return boundValueWithMax(col, v); + return boundValueWithMax(col); } case BoundValueOperator::MIN : { - return boundValueWithMin(col, v); + return boundValueWithMin(col); } } return Value::kNullBadType; @@ -51,10 +51,7 @@ Value OptimizerUtils::boundValueWithGT(const meta::cpp2::ColumnDef& col, const V return Value(0.0); } } - return v.getFloat() + 0.0000000000000001; - } - case Value::Type::BOOL: { - return v; + return v.getFloat() + kEpsilon; } case Value::Type::STRING : { if (!col.type.__isset.type_length || @@ -162,6 +159,7 @@ Value OptimizerUtils::boundValueWithGT(const meta::cpp2::ColumnDef& col, const V return Value(dt); } case Value::Type::__EMPTY__: + case Value::Type::BOOL: case Value::Type::NULLVALUE: case Value::Type::VERTEX: case Value::Type::EDGE: @@ -199,10 +197,7 @@ Value OptimizerUtils::boundValueWithLT(const meta::cpp2::ColumnDef& col, const V } 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; + return v.getFloat() - kEpsilon; } case Value::Type::STRING : { if (!col.type.__isset.type_length || col.get_type().get_type_length() == nullptr) { @@ -311,6 +306,7 @@ Value OptimizerUtils::boundValueWithLT(const meta::cpp2::ColumnDef& col, const V return Value(dt); } case Value::Type::__EMPTY__: + case Value::Type::BOOL: case Value::Type::NULLVALUE: case Value::Type::VERTEX: case Value::Type::EDGE: @@ -328,7 +324,7 @@ Value OptimizerUtils::boundValueWithLT(const meta::cpp2::ColumnDef& col, const V return Value::kNullBadType; } -Value OptimizerUtils::boundValueWithMax(const meta::cpp2::ColumnDef& col, const Value& v) { +Value OptimizerUtils::boundValueWithMax(const meta::cpp2::ColumnDef& col) { auto type = SchemaUtil::propTypeToValueType(col.get_type().get_type()); switch (type) { case Value::Type::INT : { @@ -337,9 +333,6 @@ Value OptimizerUtils::boundValueWithMax(const meta::cpp2::ColumnDef& col, const case Value::Type::FLOAT : { return Value(std::numeric_limits<double>::max()); } - case Value::Type::BOOL: { - return v; - } case Value::Type::STRING : { if (!col.type.__isset.type_length || col.get_type().get_type_length() == nullptr) { @@ -374,6 +367,7 @@ Value OptimizerUtils::boundValueWithMax(const meta::cpp2::ColumnDef& col, const return Value(dt); } case Value::Type::__EMPTY__: + case Value::Type::BOOL: case Value::Type::NULLVALUE: case Value::Type::VERTEX: case Value::Type::EDGE: @@ -391,7 +385,7 @@ Value OptimizerUtils::boundValueWithMax(const meta::cpp2::ColumnDef& col, const return Value::kNullBadType; } -Value OptimizerUtils::boundValueWithMin(const meta::cpp2::ColumnDef& col, const Value& v) { +Value OptimizerUtils::boundValueWithMin(const meta::cpp2::ColumnDef& col) { auto type = SchemaUtil::propTypeToValueType(col.get_type().get_type()); switch (type) { case Value::Type::INT : { @@ -400,9 +394,6 @@ Value OptimizerUtils::boundValueWithMin(const meta::cpp2::ColumnDef& col, const case Value::Type::FLOAT : { return Value(-std::numeric_limits<double>::max()); } - case Value::Type::BOOL: { - return v; - } case Value::Type::STRING : { if (!col.type.__isset.type_length || col.get_type().get_type_length() == nullptr) { @@ -420,6 +411,7 @@ Value OptimizerUtils::boundValueWithMin(const meta::cpp2::ColumnDef& col, const return Value(DateTime()); } case Value::Type::__EMPTY__: + case Value::Type::BOOL: case Value::Type::NULLVALUE: case Value::Type::VERTEX: case Value::Type::EDGE: diff --git a/src/optimizer/OptimizerUtils.h b/src/optimizer/OptimizerUtils.h index 8870a436419c2d9e9bc4bb4ee01c08d2c1621d55..98e1200907450f53f8e7a4513fd372c5a42f7c4e 100644 --- a/src/optimizer/OptimizerUtils.h +++ b/src/optimizer/OptimizerUtils.h @@ -33,11 +33,13 @@ public: static Value boundValueWithLT(const meta::cpp2::ColumnDef& col, const Value& v); - static Value boundValueWithMax(const meta::cpp2::ColumnDef& col, const Value& v); + static Value boundValueWithMax(const meta::cpp2::ColumnDef& col); - static Value boundValueWithMin(const meta::cpp2::ColumnDef& col, const Value& v); + static Value boundValueWithMin(const meta::cpp2::ColumnDef& col); static Value normalizeValue(const meta::cpp2::ColumnDef& col, const Value& v); + + static constexpr double kEpsilon = 0.0000000000000001; }; } // namespace graph diff --git a/src/optimizer/rule/IndexScanRule.cpp b/src/optimizer/rule/IndexScanRule.cpp index 2c6e599a5896c2d8249e949da8cd5a7c979164f2..9aedb52f1f553434e8baa0376a00de33eb202f0e 100644 --- a/src/optimizer/rule/IndexScanRule.cpp +++ b/src/optimizer/rule/IndexScanRule.cpp @@ -173,6 +173,10 @@ Status IndexScanRule::appendColHint(std::vector<IndexColumnHint>& hints, begin = OptimizerUtils::normalizeValue(col, item.value_); break; } + // because only type for bool is true/false, which can not satisify [start, end) + if (col.get_type().get_type() == meta::cpp2::PropertyType::BOOL) { + return Status::SemanticError("Range scan for bool type is illegal"); + } NG_RETURN_IF_ERROR(boundValue(item, col, begin, end)); } diff --git a/src/optimizer/rule/IndexScanRule.h b/src/optimizer/rule/IndexScanRule.h index 4e0f0a5c24449e5a8406e0b65eee4e7f4d86f12d..1fcffd8285dfbdc2e652f372e4719f9cdf76a01f 100644 --- a/src/optimizer/rule/IndexScanRule.h +++ b/src/optimizer/rule/IndexScanRule.h @@ -29,6 +29,7 @@ using IndexQueryCtx = std::unique_ptr<std::vector<IndexQueryContext>>; class IndexScanRule final : public OptRule { FRIEND_TEST(IndexScanRuleTest, BoundValueTest); FRIEND_TEST(IndexScanRuleTest, IQCtxTest); + FRIEND_TEST(IndexScanRuleTest, BoundValueRangeTest); public: const Pattern& pattern() const override; diff --git a/src/optimizer/test/IndexBoundValueTest.cpp b/src/optimizer/test/IndexBoundValueTest.cpp index dad22ee3f24314cbd5906847ea5c175ad94b81c4..3e63fa62a227f302f59f1ef0d71775d562787f76 100644 --- a/src/optimizer/test/IndexBoundValueTest.cpp +++ b/src/optimizer/test/IndexBoundValueTest.cpp @@ -71,30 +71,6 @@ TEST(IndexBoundValueTest, StringTest) { } } -TEST(IndexBoundValueTest, BoolTest) { - meta::cpp2::ColumnDef col; - { - meta::cpp2::ColumnTypeDef typeDef; - typeDef.set_type(meta::cpp2::PropertyType::BOOL); - col.set_type(std::move(typeDef)); - } - 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; { @@ -345,3 +321,10 @@ TEST(IndexBoundValueTest, DateTimeTest) { } // namespace graph } // namespace nebula + +int main(int argc, char** argv) { + testing::InitGoogleTest(&argc, argv); + folly::init(&argc, &argv, true); + google::SetStderrLogging(google::INFO); + return RUN_ALL_TESTS(); +} diff --git a/src/optimizer/test/IndexScanRuleTest.cpp b/src/optimizer/test/IndexScanRuleTest.cpp index f215e76b3ab8b01f3a35f56b6d67c26da402623c..b0ced7f6d3df2603a52f342eb6bd7ce73c7ac569 100644 --- a/src/optimizer/test/IndexScanRuleTest.cpp +++ b/src/optimizer/test/IndexScanRuleTest.cpp @@ -242,5 +242,264 @@ TEST(IndexScanRuleTest, IQCtxTest) { } } +TEST(IndexScanRuleTest, BoundValueRangeTest) { + auto* inst = std::move(IndexScanRule::kInstance).get(); + auto* instance = static_cast<IndexScanRule*>(inst); + + { + meta::cpp2::ColumnDef col; + col.set_name("col_int"); + col.type.set_type(meta::cpp2::PropertyType::INT64); + { + std::vector<storage::cpp2::IndexColumnHint> hints; + // col_int < 2 + IndexScanRule::FilterItems items; + items.addItem("col_int", RelationalExpression::Kind::kRelLT, Value(2L)); + auto status = instance->appendColHint(hints, items, col); + EXPECT_TRUE(status.ok()); + EXPECT_EQ(1, hints.size()); + const auto& hint = hints[0]; + EXPECT_EQ("col_int", hint.column_name); + EXPECT_EQ(storage::cpp2::ScanType::RANGE, hint.scan_type); + EXPECT_EQ(std::numeric_limits<int64_t>::min(), hint.begin_value); + EXPECT_EQ(2, hint.end_value); + } + { + std::vector<storage::cpp2::IndexColumnHint> hints; + // col_int <= 2 + IndexScanRule::FilterItems items; + items.addItem("col_int", RelationalExpression::Kind::kRelLE, Value(2L)); + auto status = instance->appendColHint(hints, items, col); + EXPECT_TRUE(status.ok()); + EXPECT_EQ(1, hints.size()); + const auto& hint = hints[0]; + EXPECT_EQ("col_int", hint.column_name); + EXPECT_EQ(storage::cpp2::ScanType::RANGE, hint.scan_type); + EXPECT_EQ(std::numeric_limits<int64_t>::min(), hint.begin_value); + EXPECT_EQ(3, hint.end_value); + } + { + std::vector<storage::cpp2::IndexColumnHint> hints; + // col_int > 2 + IndexScanRule::FilterItems items; + items.addItem("col_int", RelationalExpression::Kind::kRelGT, Value(2L)); + auto status = instance->appendColHint(hints, items, col); + EXPECT_TRUE(status.ok()); + EXPECT_EQ(1, hints.size()); + const auto& hint = hints[0]; + EXPECT_EQ("col_int", hint.column_name); + EXPECT_EQ(storage::cpp2::ScanType::RANGE, hint.scan_type); + EXPECT_EQ(3, hint.begin_value); + EXPECT_EQ(std::numeric_limits<int64_t>::max(), hint.end_value); + } + { + std::vector<storage::cpp2::IndexColumnHint> hints; + // col_int >= 2 + IndexScanRule::FilterItems items; + items.addItem("col_int", RelationalExpression::Kind::kRelGE, Value(2L)); + auto status = instance->appendColHint(hints, items, col); + EXPECT_TRUE(status.ok()); + EXPECT_EQ(1, hints.size()); + const auto& hint = hints[0]; + EXPECT_EQ("col_int", hint.column_name); + EXPECT_EQ(storage::cpp2::ScanType::RANGE, hint.scan_type); + EXPECT_EQ(2, hint.begin_value); + EXPECT_EQ(std::numeric_limits<int64_t>::max(), hint.end_value); + } + { + std::vector<storage::cpp2::IndexColumnHint> hints; + // col_int > 2 and col_int < 5 + IndexScanRule::FilterItems items; + items.addItem("col_int", RelationalExpression::Kind::kRelGT, Value(2L)); + items.addItem("col_int", RelationalExpression::Kind::kRelLT, Value(5L)); + auto status = instance->appendColHint(hints, items, col); + EXPECT_TRUE(status.ok()); + EXPECT_EQ(1, hints.size()); + const auto& hint = hints[0]; + EXPECT_EQ("col_int", hint.column_name); + EXPECT_EQ(storage::cpp2::ScanType::RANGE, hint.scan_type); + EXPECT_EQ(3, hint.begin_value); + EXPECT_EQ(5, hint.end_value); + } + { + std::vector<storage::cpp2::IndexColumnHint> hints; + // col_int >= 2 and col_int <= 5 + IndexScanRule::FilterItems items; + items.addItem("col_int", RelationalExpression::Kind::kRelGE, Value(2L)); + items.addItem("col_int", RelationalExpression::Kind::kRelLE, Value(5L)); + auto status = instance->appendColHint(hints, items, col); + EXPECT_TRUE(status.ok()); + EXPECT_EQ(1, hints.size()); + const auto& hint = hints[0]; + EXPECT_EQ("col_int", hint.column_name); + EXPECT_EQ(storage::cpp2::ScanType::RANGE, hint.scan_type); + EXPECT_EQ(2, hint.begin_value); + EXPECT_EQ(6, hint.end_value); + } + } + { + meta::cpp2::ColumnDef col; + col.set_name("col_bool"); + col.type.set_type(meta::cpp2::PropertyType::BOOL); + { + std::vector<storage::cpp2::IndexColumnHint> hints; + // col_bool < true + IndexScanRule::FilterItems items; + items.addItem("col_bool", RelationalExpression::Kind::kRelLT, Value(true)); + auto status = instance->appendColHint(hints, items, col); + EXPECT_FALSE(status.ok()); + } + { + std::vector<storage::cpp2::IndexColumnHint> hints; + // col_bool >= false + IndexScanRule::FilterItems items; + items.addItem("col_bool", RelationalExpression::Kind::kRelGE, Value(false)); + auto status = instance->appendColHint(hints, items, col); + EXPECT_FALSE(status.ok()); + } + } + { + meta::cpp2::ColumnDef col; + col.set_name("col_double"); + col.type.set_type(meta::cpp2::PropertyType::DOUBLE); + { + std::vector<storage::cpp2::IndexColumnHint> hints; + // col_double < 1.0 + IndexScanRule::FilterItems items; + items.addItem("col_double", RelationalExpression::Kind::kRelLT, Value(1.0)); + auto status = instance->appendColHint(hints, items, col); + EXPECT_TRUE(status.ok()); + EXPECT_EQ(1, hints.size()); + const auto& hint = hints[0]; + EXPECT_EQ("col_double", hint.column_name); + EXPECT_EQ(storage::cpp2::ScanType::RANGE, hint.scan_type); + EXPECT_EQ(-std::numeric_limits<double>::max(), hint.begin_value); + EXPECT_EQ(1, hint.end_value); + } + { + std::vector<storage::cpp2::IndexColumnHint> hints; + // col_double >= 1.0 + IndexScanRule::FilterItems items; + items.addItem("col_double", RelationalExpression::Kind::kRelGE, Value(1.0)); + auto status = instance->appendColHint(hints, items, col); + EXPECT_TRUE(status.ok()); + EXPECT_EQ(1, hints.size()); + const auto& hint = hints[0]; + EXPECT_EQ("col_double", hint.column_name); + EXPECT_EQ(storage::cpp2::ScanType::RANGE, hint.scan_type); + EXPECT_EQ(1, hint.begin_value); + EXPECT_EQ(std::numeric_limits<double>::max(), hint.end_value); + } + { + std::vector<storage::cpp2::IndexColumnHint> hints; + // 1.0 < col_double <= 5.0 + IndexScanRule::FilterItems items; + items.addItem("col_double", RelationalExpression::Kind::kRelGT, Value(1.0)); + items.addItem("col_double", RelationalExpression::Kind::kRelLE, Value(5.0)); + auto status = instance->appendColHint(hints, items, col); + EXPECT_TRUE(status.ok()); + EXPECT_EQ(1, hints.size()); + const auto& hint = hints[0]; + EXPECT_EQ("col_double", hint.column_name); + EXPECT_EQ(storage::cpp2::ScanType::RANGE, hint.scan_type); + EXPECT_EQ(1 + OptimizerUtils::kEpsilon, hint.begin_value); + EXPECT_EQ(5 - OptimizerUtils::kEpsilon, hint.end_value); + } + } + { + meta::cpp2::ColumnDef col; + size_t len = 10; + col.set_name("col_str"); + col.type.set_type(meta::cpp2::PropertyType::STRING); + col.type.set_type_length(len); + { + std::vector<storage::cpp2::IndexColumnHint> hints; + // col_str < "ccc" + IndexScanRule::FilterItems items; + items.addItem("col_str", RelationalExpression::Kind::kRelLT, Value("ccc")); + auto status = instance->appendColHint(hints, items, col); + EXPECT_TRUE(status.ok()); + EXPECT_EQ(1, hints.size()); + const auto& hint = hints[0]; + EXPECT_EQ("col_str", hint.column_name); + EXPECT_EQ(storage::cpp2::ScanType::RANGE, hint.scan_type); + EXPECT_EQ(std::string(len, '\0'), hint.begin_value); + EXPECT_EQ("ccc", hint.end_value); + } + { + std::vector<storage::cpp2::IndexColumnHint> hints; + // col_str > "ccc" + IndexScanRule::FilterItems items; + items.addItem("col_str", RelationalExpression::Kind::kRelGT, Value("ccc")); + auto status = instance->appendColHint(hints, items, col); + EXPECT_TRUE(status.ok()); + EXPECT_EQ(1, hints.size()); + const auto& hint = hints[0]; + EXPECT_EQ("col_str", hint.column_name); + EXPECT_EQ(storage::cpp2::ScanType::RANGE, hint.scan_type); + std::string begin = std::string(3, 'c').append(6, 0x00).append(1, 0x01); + std::string end = std::string(len, static_cast<char>(0xFF)); + EXPECT_EQ(begin, hint.begin_value); + EXPECT_EQ(end, hint.end_value); + } + { + std::vector<storage::cpp2::IndexColumnHint> hints; + // "aaa" < col_str < "ccc" + IndexScanRule::FilterItems items; + items.addItem("col_str", RelationalExpression::Kind::kRelGT, Value("aaa")); + items.addItem("col_str", RelationalExpression::Kind::kRelLT, Value("ccc")); + auto status = instance->appendColHint(hints, items, col); + EXPECT_TRUE(status.ok()); + EXPECT_EQ(1, hints.size()); + const auto& hint = hints[0]; + EXPECT_EQ("col_str", hint.column_name); + EXPECT_EQ(storage::cpp2::ScanType::RANGE, hint.scan_type); + std::string begin = std::string(3, 'a').append(6, 0x00).append(1, 0x01); + std::string end = "ccc"; + EXPECT_EQ(begin, hint.begin_value); + EXPECT_EQ(end, hint.end_value); + } + { + std::vector<storage::cpp2::IndexColumnHint> hints; + // "aaa" <= col_str <= "ccc" + IndexScanRule::FilterItems items; + items.addItem("col_str", RelationalExpression::Kind::kRelGE, Value("aaa")); + items.addItem("col_str", RelationalExpression::Kind::kRelLE, Value("ccc")); + auto status = instance->appendColHint(hints, items, col); + EXPECT_TRUE(status.ok()); + EXPECT_EQ(1, hints.size()); + const auto& hint = hints[0]; + EXPECT_EQ("col_str", hint.column_name); + EXPECT_EQ(storage::cpp2::ScanType::RANGE, hint.scan_type); + std::string begin = "aaa"; + std::string end = std::string(3, 'c').append(6, 0x00).append(1, 0x01); + EXPECT_EQ(begin, hint.begin_value); + EXPECT_EQ(end, hint.end_value); + } + { + std::vector<storage::cpp2::IndexColumnHint> hints; + // "aaa" <= col_str < "ccc" + IndexScanRule::FilterItems items; + items.addItem("col_str", RelationalExpression::Kind::kRelGE, Value("aaa")); + items.addItem("col_str", RelationalExpression::Kind::kRelLT, Value("ccc")); + auto status = instance->appendColHint(hints, items, col); + EXPECT_TRUE(status.ok()); + EXPECT_EQ(1, hints.size()); + const auto& hint = hints[0]; + EXPECT_EQ("col_str", hint.column_name); + EXPECT_EQ(storage::cpp2::ScanType::RANGE, hint.scan_type); + EXPECT_EQ("aaa", hint.begin_value); + EXPECT_EQ("ccc", hint.end_value); + } + } +} + } // namespace opt } // namespace nebula + +int main(int argc, char** argv) { + testing::InitGoogleTest(&argc, argv); + folly::init(&argc, &argv, true); + google::SetStderrLogging(google::INFO); + return RUN_ALL_TESTS(); +}