diff --git a/codecov.yml b/codecov.yml new file mode 100644 index 00000000000..dd59a0ba054 --- /dev/null +++ b/codecov.yml @@ -0,0 +1,14 @@ +# Copyright (c) 2021 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. + +# For more configuration details: +# https://docs.codecov.io/docs/codecov-yaml + +# validate the configuration: +# curl -X POST --data-binary @codecov.yml https://codecov.io/validate + +codecov: + allow_pseudo_compare: True + allow_coverage_offsets: True diff --git a/conf/nebula-graphd.conf.default b/conf/nebula-graphd.conf.default index 9951cf37896..fa406f8008a 100644 --- a/conf/nebula-graphd.conf.default +++ b/conf/nebula-graphd.conf.default @@ -81,3 +81,7 @@ ########## memory ########## # System memory high watermark ratio --system_memory_high_watermark_ratio=0.8 + +########## experimental feature ########## +# if use experimental features +--enable_experimental_feature=false diff --git a/conf/nebula-graphd.conf.production b/conf/nebula-graphd.conf.production index b3eebaf8dbf..26bc28827cf 100644 --- a/conf/nebula-graphd.conf.production +++ b/conf/nebula-graphd.conf.production @@ -79,3 +79,7 @@ ########## memory ########## # System memory high watermark ratio --system_memory_high_watermark_ratio=0.8 + +########## experimental feature ########## +# if use experimental features +--enable_experimental_feature=false diff --git a/src/common/datatypes/Map.cpp b/src/common/datatypes/Map.cpp index 7ab73d5c9b7..5ac176ae86a 100644 --- a/src/common/datatypes/Map.cpp +++ b/src/common/datatypes/Map.cpp @@ -46,3 +46,14 @@ folly::dynamic Map::getMetaData() const { } } // namespace nebula + +namespace std { +std::size_t hash::operator()(const nebula::Map& m) const noexcept { + size_t seed = 0; + for (auto& v : m.kvs) { + seed ^= hash()(v.first) + 0x9e3779b9 + (seed << 6) + (seed >> 2); + } + return seed; +} + +} // namespace std diff --git a/src/common/datatypes/Map.h b/src/common/datatypes/Map.h index 333e5d0cb0d..9e7db20c1b3 100644 --- a/src/common/datatypes/Map.h +++ b/src/common/datatypes/Map.h @@ -70,4 +70,12 @@ struct Map { inline std::ostream& operator<<(std::ostream& os, const Map& m) { return os << m.toString(); } } // namespace nebula + +namespace std { +template <> +struct hash { + std::size_t operator()(const nebula::Map& m) const noexcept; +}; + +} // namespace std #endif // COMMON_DATATYPES_MAP_H_ diff --git a/src/common/datatypes/Set.cpp b/src/common/datatypes/Set.cpp index 2e0e90a2f5a..5a130ace3e5 100644 --- a/src/common/datatypes/Set.cpp +++ b/src/common/datatypes/Set.cpp @@ -43,3 +43,14 @@ folly::dynamic Set::getMetaData() const { } } // namespace nebula + +namespace std { +std::size_t hash::operator()(const nebula::Set& s) const noexcept { + size_t seed = 0; + for (auto& v : s.values) { + seed ^= hash()(v) + 0x9e3779b9 + (seed << 6) + (seed >> 2); + } + return seed; +} + +} // namespace std diff --git a/src/common/datatypes/Set.h b/src/common/datatypes/Set.h index dabc33e0d7e..425097c04f2 100644 --- a/src/common/datatypes/Set.h +++ b/src/common/datatypes/Set.h @@ -54,6 +54,13 @@ struct Set { }; inline std::ostream& operator<<(std::ostream& os, const Set& s) { return os << s.toString(); } - } // namespace nebula + +namespace std { +template <> +struct hash { + std::size_t operator()(const nebula::Set& s) const noexcept; +}; + +} // namespace std #endif // COMMON_DATATYPES_SET_H_ diff --git a/src/common/datatypes/Value.cpp b/src/common/datatypes/Value.cpp index e4294a551ab..e6f82a2d517 100644 --- a/src/common/datatypes/Value.cpp +++ b/src/common/datatypes/Value.cpp @@ -69,10 +69,10 @@ std::size_t hash::operator()(const nebula::Value& v) const noexce return hash()(v.getGeography()); } case nebula::Value::Type::MAP: { - LOG(FATAL) << "Hash for MAP has not been implemented"; + return hash()(v.getMap()); } case nebula::Value::Type::SET: { - LOG(FATAL) << "Hash for SET has not been implemented"; + return hash()(v.getSet()); } case nebula::Value::Type::DATASET: { LOG(FATAL) << "Hash for DATASET has not been implemented"; diff --git a/src/common/expression/test/CMakeLists.txt b/src/common/expression/test/CMakeLists.txt index 20ba9d99f2a..577ce3f2f79 100644 --- a/src/common/expression/test/CMakeLists.txt +++ b/src/common/expression/test/CMakeLists.txt @@ -3,6 +3,50 @@ # This source code is licensed under Apache 2.0 License, # attached with Common Clause Condition 1.0, found in the LICENSES directory. +set(expression_test_common_libs + $ + $ + $ + $ + $ + $ + $ + $ + $ + $ + $ + $ + $ + $ + $ + $ + $ + $ + $ + $ + $ + $ + $ + $ + $ + $ + $ + $ + $ + $ + $ + $ + $ + $ + $ + $ + $ + $ + $ + $ + $ +) + nebula_add_library( expr_ctx_mock_obj OBJECT ExpressionContextMock.cpp @@ -24,9 +68,11 @@ nebula_add_test( $ $ $ + ${expression_test_common_libs} LIBRARIES gtest ${THRIFT_LIBRARIES} + ${PROXYGEN_LIBRARIES} ) nebula_add_executable( @@ -45,10 +91,12 @@ nebula_add_executable( $ $ $ + ${expression_test_common_libs} LIBRARIES follybenchmark boost_regex ${THRIFT_LIBRARIES} + ${PROXYGEN_LIBRARIES} ) nebula_add_executable( @@ -110,9 +158,11 @@ nebula_add_test( $ $ $ + ${expression_test_common_libs} LIBRARIES gtest ${THRIFT_LIBRARIES} + ${PROXYGEN_LIBRARIES} ) nebula_add_test( @@ -129,9 +179,11 @@ nebula_add_test( $ $ $ + ${expression_test_common_libs} LIBRARIES gtest ${THRIFT_LIBRARIES} + ${PROXYGEN_LIBRARIES} ) nebula_add_test( @@ -148,9 +200,11 @@ nebula_add_test( $ $ $ + ${expression_test_common_libs} LIBRARIES gtest ${THRIFT_LIBRARIES} + ${PROXYGEN_LIBRARIES} ) nebula_add_test( @@ -167,9 +221,11 @@ nebula_add_test( $ $ $ + ${expression_test_common_libs} LIBRARIES gtest ${THRIFT_LIBRARIES} + ${PROXYGEN_LIBRARIES} ) nebula_add_test( @@ -186,9 +242,11 @@ nebula_add_test( $ $ $ + ${expression_test_common_libs} LIBRARIES gtest ${THRIFT_LIBRARIES} + ${PROXYGEN_LIBRARIES} ) nebula_add_test( @@ -205,9 +263,11 @@ nebula_add_test( $ $ $ + ${expression_test_common_libs} LIBRARIES gtest ${THRIFT_LIBRARIES} + ${PROXYGEN_LIBRARIES} ) nebula_add_test( @@ -224,9 +284,11 @@ nebula_add_test( $ $ $ + ${expression_test_common_libs} LIBRARIES gtest ${THRIFT_LIBRARIES} + ${PROXYGEN_LIBRARIES} ) nebula_add_test( @@ -243,9 +305,11 @@ nebula_add_test( $ $ $ + ${expression_test_common_libs} LIBRARIES gtest ${THRIFT_LIBRARIES} + ${PROXYGEN_LIBRARIES} ) nebula_add_test( @@ -262,9 +326,11 @@ nebula_add_test( $ $ $ + ${expression_test_common_libs} LIBRARIES gtest ${THRIFT_LIBRARIES} + ${PROXYGEN_LIBRARIES} ) nebula_add_test( @@ -281,9 +347,11 @@ nebula_add_test( $ $ $ + ${expression_test_common_libs} LIBRARIES gtest ${THRIFT_LIBRARIES} + ${PROXYGEN_LIBRARIES} ) nebula_add_test( @@ -300,9 +368,11 @@ nebula_add_test( $ $ $ + ${expression_test_common_libs} LIBRARIES gtest ${THRIFT_LIBRARIES} + ${PROXYGEN_LIBRARIES} ) nebula_add_test( @@ -319,9 +389,11 @@ nebula_add_test( $ $ $ + ${expression_test_common_libs} LIBRARIES gtest ${THRIFT_LIBRARIES} + ${PROXYGEN_LIBRARIES} ) nebula_add_test( @@ -338,9 +410,11 @@ nebula_add_test( $ $ $ + ${expression_test_common_libs} LIBRARIES gtest ${THRIFT_LIBRARIES} + ${PROXYGEN_LIBRARIES} ) nebula_add_test( @@ -357,9 +431,11 @@ nebula_add_test( $ $ $ + ${expression_test_common_libs} LIBRARIES gtest ${THRIFT_LIBRARIES} + ${PROXYGEN_LIBRARIES} ) nebula_add_test( @@ -376,9 +452,11 @@ nebula_add_test( $ $ $ + ${expression_test_common_libs} LIBRARIES gtest ${THRIFT_LIBRARIES} + ${PROXYGEN_LIBRARIES} ) nebula_add_test( @@ -395,9 +473,11 @@ nebula_add_test( $ $ $ + ${expression_test_common_libs} LIBRARIES gtest ${THRIFT_LIBRARIES} + ${PROXYGEN_LIBRARIES} ) nebula_add_test( @@ -414,9 +494,11 @@ nebula_add_test( $ $ $ + ${expression_test_common_libs} LIBRARIES gtest ${THRIFT_LIBRARIES} + ${PROXYGEN_LIBRARIES} ) nebula_add_test( @@ -433,9 +515,11 @@ nebula_add_test( $ $ $ + ${expression_test_common_libs} LIBRARIES gtest ${THRIFT_LIBRARIES} + ${PROXYGEN_LIBRARIES} ) nebula_add_test( @@ -452,9 +536,11 @@ nebula_add_test( $ $ $ + ${expression_test_common_libs} LIBRARIES gtest ${THRIFT_LIBRARIES} + ${PROXYGEN_LIBRARIES} ) nebula_add_test( @@ -471,7 +557,9 @@ nebula_add_test( $ $ $ + ${expression_test_common_libs} LIBRARIES gtest ${THRIFT_LIBRARIES} + ${PROXYGEN_LIBRARIES} ) diff --git a/src/common/expression/test/FunctionCallExpressionTest.cpp b/src/common/expression/test/FunctionCallExpressionTest.cpp index 2f10c8ed350..63f6216f445 100644 --- a/src/common/expression/test/FunctionCallExpressionTest.cpp +++ b/src/common/expression/test/FunctionCallExpressionTest.cpp @@ -60,7 +60,7 @@ TEST_F(FunctionCallExpressionTest, FunctionCallTest) { path.src.vid = "1"; STEP("2", "edge", 0, 1); STEP("1", "edge", 0, -1); - TEST_FUNCTION(hasSameEdgeInPath, {path}, true); + TEST_PATH_FUNCTION(hasSameEdgeInPath, {path}, true); } { // hasSameEdgeInPath @@ -70,7 +70,7 @@ TEST_F(FunctionCallExpressionTest, FunctionCallTest) { STEP("2", "edge", 0, 1); STEP("1", "edge", 0, -1); STEP("2", "edge", 0, 1); - TEST_FUNCTION(hasSameEdgeInPath, {path}, true); + TEST_PATH_FUNCTION(hasSameEdgeInPath, {path}, true); } { // hasSameEdgeInPath @@ -80,7 +80,7 @@ TEST_F(FunctionCallExpressionTest, FunctionCallTest) { STEP("2", "edge", 0, 1); STEP("1", "edge", 0, 1); STEP("2", "edge", 0, 1); - TEST_FUNCTION(hasSameEdgeInPath, {path}, false); + TEST_PATH_FUNCTION(hasSameEdgeInPath, {path}, false); } { // hasSameEdgeInPath @@ -90,7 +90,7 @@ TEST_F(FunctionCallExpressionTest, FunctionCallTest) { STEP("2", "edge", 0, 1); STEP("1", "edge", 0, -1); STEP("2", "edge", 1, 1); - TEST_FUNCTION(hasSameEdgeInPath, {path}, false); + TEST_PATH_FUNCTION(hasSameEdgeInPath, {path}, false); } // Check function { diff --git a/src/common/expression/test/LogicalExpressionTest.cpp b/src/common/expression/test/LogicalExpressionTest.cpp index dd900642e20..4d87f99cbc7 100644 --- a/src/common/expression/test/LogicalExpressionTest.cpp +++ b/src/common/expression/test/LogicalExpressionTest.cpp @@ -94,21 +94,63 @@ TEST_F(LogicalExpressionTest, LogicalCalculation) { TEST_EXPR(true AND 2 / 0, Value::kNullDivByZero); TEST_EXPR(false AND 2 / 0, false); TEST_EXPR(2 / 0 AND 2 / 0, Value::kNullDivByZero); - TEST_EXPR(empty AND null AND 2 / 0 AND empty, Value::kNullDivByZero); + { + // empty AND null AND 2 / 0 AND empty + auto *operand1 = ConstantExpression::make(&pool, Value()); + auto *operand2 = ConstantExpression::make(&pool, Value(NullType::__NULL__)); + auto *operand3 = LogicalExpression::makeAnd(&pool, operand1, operand2); + auto *operand4 = ConstantExpression::make(&pool, Value(2)); + auto *operand5 = ConstantExpression::make(&pool, Value(0)); + auto *operand6 = ArithmeticExpression::makeDivision(&pool, operand4, operand5); + auto *operand7 = LogicalExpression::makeAnd(&pool, operand3, operand6); + auto *operand8 = ConstantExpression::make(&pool, Value()); + auto *expr = LogicalExpression::makeAnd(&pool, operand7, operand8); + auto eval = Expression::eval(expr, gExpCtxt); + EXPECT_EQ(eval.type(), Value::kNullDivByZero.type()); + EXPECT_EQ(eval, Value::kNullDivByZero) << "check failed: " << expr->toString(); + } TEST_EXPR(2 / 0 OR true, Value::kNullDivByZero); TEST_EXPR(2 / 0 OR false, Value::kNullDivByZero); TEST_EXPR(true OR 2 / 0, true); TEST_EXPR(false OR 2 / 0, Value::kNullDivByZero); TEST_EXPR(2 / 0 OR 2 / 0, Value::kNullDivByZero); - TEST_EXPR(empty OR null OR 2 / 0 OR empty, Value::kNullDivByZero); + { + // empty OR null OR 2 / 0 OR empty + auto *operand1 = ConstantExpression::make(&pool, Value()); + auto *operand2 = ConstantExpression::make(&pool, Value(NullType::__NULL__)); + auto *operand3 = LogicalExpression::makeOr(&pool, operand1, operand2); + auto *operand4 = ConstantExpression::make(&pool, Value(2)); + auto *operand5 = ConstantExpression::make(&pool, Value(0)); + auto *operand6 = ArithmeticExpression::makeDivision(&pool, operand4, operand5); + auto *operand7 = LogicalExpression::makeOr(&pool, operand3, operand6); + auto *operand8 = ConstantExpression::make(&pool, Value()); + auto *expr = LogicalExpression::makeOr(&pool, operand7, operand8); + auto eval = Expression::eval(expr, gExpCtxt); + EXPECT_EQ(eval.type(), Value::kNullDivByZero.type()); + EXPECT_EQ(eval, Value::kNullDivByZero) << "check failed: " << expr->toString(); + } TEST_EXPR(2 / 0 XOR true, Value::kNullDivByZero); TEST_EXPR(2 / 0 XOR false, Value::kNullDivByZero); TEST_EXPR(true XOR 2 / 0, Value::kNullDivByZero); TEST_EXPR(false XOR 2 / 0, Value::kNullDivByZero); TEST_EXPR(2 / 0 XOR 2 / 0, Value::kNullDivByZero); - TEST_EXPR(empty XOR 2 / 0 XOR null XOR empty, Value::kNullDivByZero); + { + // empty XOR 2 / 0 XOR null XOR empty + auto *operand1 = ConstantExpression::make(&pool, Value()); + auto *operand2 = ConstantExpression::make(&pool, Value(2)); + auto *operand3 = ConstantExpression::make(&pool, Value(0)); + auto *operand4 = ArithmeticExpression::makeDivision(&pool, operand2, operand3); + auto *operand5 = LogicalExpression::makeXor(&pool, operand1, operand4); + auto *operand6 = ConstantExpression::make(&pool, Value(NullType::__NULL__)); + auto *operand7 = LogicalExpression::makeXor(&pool, operand5, operand6); + auto *operand8 = ConstantExpression::make(&pool, Value()); + auto *expr = LogicalExpression::makeXor(&pool, operand7, operand8); + auto eval = Expression::eval(expr, gExpCtxt); + EXPECT_EQ(eval.type(), Value::kNullDivByZero.type()); + EXPECT_EQ(eval, Value::kNullDivByZero) << "check failed: " << expr->toString(); + } // test normal null TEST_EXPR(null AND true, Value::kNullValue); @@ -116,62 +158,411 @@ TEST_F(LogicalExpressionTest, LogicalCalculation) { TEST_EXPR(true AND null, Value::kNullValue); TEST_EXPR(false AND null, false); TEST_EXPR(null AND null, Value::kNullValue); - TEST_EXPR(empty AND null AND empty, Value::kNullValue); + { + // empty AND null AND empty + auto *operand1 = ConstantExpression::make(&pool, Value()); + auto *operand2 = ConstantExpression::make(&pool, Value(NullType::__NULL__)); + auto *operand3 = LogicalExpression::makeAnd(&pool, operand1, operand2); + auto *operand4 = ConstantExpression::make(&pool, Value()); + auto *expr = LogicalExpression::makeAnd(&pool, operand3, operand4); + auto eval = Expression::eval(expr, gExpCtxt); + EXPECT_EQ(eval.type(), Value::kNullValue.type()); + EXPECT_EQ(eval, Value::kNullValue) << "check failed: " << expr->toString(); + } TEST_EXPR(null OR true, true); TEST_EXPR(null OR false, Value::kNullValue); TEST_EXPR(true OR null, true); TEST_EXPR(false OR null, Value::kNullValue); TEST_EXPR(null OR null, Value::kNullValue); - TEST_EXPR(empty OR null OR empty, Value::kNullValue); + { + // empty OR null OR empty + auto *operand1 = ConstantExpression::make(&pool, Value()); + auto *operand2 = ConstantExpression::make(&pool, Value(NullType::__NULL__)); + auto *operand3 = LogicalExpression::makeOr(&pool, operand1, operand2); + auto *operand4 = ConstantExpression::make(&pool, Value()); + auto *expr = LogicalExpression::makeOr(&pool, operand3, operand4); + auto eval = Expression::eval(expr, gExpCtxt); + EXPECT_EQ(eval.type(), Value::kNullValue.type()); + EXPECT_EQ(eval, Value::kNullValue) << "check failed: " << expr->toString(); + } TEST_EXPR(null XOR true, Value::kNullValue); TEST_EXPR(null XOR false, Value::kNullValue); TEST_EXPR(true XOR null, Value::kNullValue); TEST_EXPR(false XOR null, Value::kNullValue); TEST_EXPR(null XOR null, Value::kNullValue); - TEST_EXPR(empty XOR null XOR empty, Value::kNullValue); + { + // empty XOR null XOR empty + auto *operand1 = ConstantExpression::make(&pool, Value()); + auto *operand2 = ConstantExpression::make(&pool, Value(NullType::__NULL__)); + auto *operand3 = LogicalExpression::makeXor(&pool, operand1, operand2); + auto *operand4 = ConstantExpression::make(&pool, Value()); + auto *expr = LogicalExpression::makeXor(&pool, operand3, operand4); + auto eval = Expression::eval(expr, gExpCtxt); + EXPECT_EQ(eval.type(), Value::kNullValue.type()); + EXPECT_EQ(eval, Value::kNullValue) << "check failed: " << expr->toString(); + } // test empty - TEST_EXPR(empty, Value::kEmpty); - TEST_EXPR(empty AND true, Value::kEmpty); - TEST_EXPR(empty AND false, false); - TEST_EXPR(true AND empty, Value::kEmpty); - TEST_EXPR(false AND empty, false); - TEST_EXPR(empty AND empty, Value::kEmpty); - TEST_EXPR(empty AND null, Value::kNullValue); - TEST_EXPR(null AND empty, Value::kNullValue); - TEST_EXPR(empty AND true AND empty, Value::kEmpty); - - TEST_EXPR(empty OR true, true); - TEST_EXPR(empty OR false, Value::kEmpty); - TEST_EXPR(true OR empty, true); - TEST_EXPR(false OR empty, Value::kEmpty); - TEST_EXPR(empty OR empty, Value::kEmpty); - TEST_EXPR(empty OR null, Value::kNullValue); - TEST_EXPR(null OR empty, Value::kNullValue); - TEST_EXPR(empty OR false OR empty, Value::kEmpty); - - TEST_EXPR(empty XOR true, Value::kEmpty); - TEST_EXPR(empty XOR false, Value::kEmpty); - TEST_EXPR(true XOR empty, Value::kEmpty); - TEST_EXPR(false XOR empty, Value::kEmpty); - TEST_EXPR(empty XOR empty, Value::kEmpty); - TEST_EXPR(empty XOR null, Value::kNullValue); - TEST_EXPR(null XOR empty, Value::kNullValue); - TEST_EXPR(true XOR empty XOR false, Value::kEmpty); - - TEST_EXPR(empty OR false AND true AND null XOR empty, Value::kEmpty); - TEST_EXPR(empty OR false AND true XOR empty OR true, true); + { + // empty + auto *expr = ConstantExpression::make(&pool, Value()); + auto eval = Expression::eval(expr, gExpCtxt); + EXPECT_EQ(eval.type(), Value::kEmpty.type()); + EXPECT_EQ(eval, Value::kEmpty) << "check failed: " << expr->toString(); + } + { + // empty AND true + auto *operand1 = ConstantExpression::make(&pool, Value()); + auto *operand2 = ConstantExpression::make(&pool, Value(true)); + auto *expr = LogicalExpression::makeAnd(&pool, operand1, operand2); + auto eval = Expression::eval(expr, gExpCtxt); + EXPECT_EQ(eval.type(), Value::kEmpty.type()); + EXPECT_EQ(eval, Value::kEmpty) << "check failed: " << expr->toString(); + } + { + // empty AND false + auto *operand1 = ConstantExpression::make(&pool, Value()); + auto *operand2 = ConstantExpression::make(&pool, Value(false)); + auto *expr = LogicalExpression::makeAnd(&pool, operand1, operand2); + auto eval = Expression::eval(expr, gExpCtxt); + EXPECT_EQ(eval.type(), Value(false).type()); + EXPECT_EQ(eval, Value(false)) << "check failed: " << expr->toString(); + } + { + // true AND empty + auto *operand1 = ConstantExpression::make(&pool, Value(true)); + auto *operand2 = ConstantExpression::make(&pool, Value()); + auto *expr = LogicalExpression::makeAnd(&pool, operand1, operand2); + auto eval = Expression::eval(expr, gExpCtxt); + EXPECT_EQ(eval.type(), Value::kEmpty.type()); + EXPECT_EQ(eval, Value::kEmpty) << "check failed: " << expr->toString(); + } + { + // false AND empty + auto *operand1 = ConstantExpression::make(&pool, Value(false)); + auto *operand2 = ConstantExpression::make(&pool, Value()); + auto *expr = LogicalExpression::makeAnd(&pool, operand1, operand2); + auto eval = Expression::eval(expr, gExpCtxt); + EXPECT_EQ(eval.type(), Value(false).type()); + EXPECT_EQ(eval, Value(false)) << "check failed: " << expr->toString(); + } + { + // empty AND empty + auto *operand1 = ConstantExpression::make(&pool, Value()); + auto *operand2 = ConstantExpression::make(&pool, Value()); + auto *expr = LogicalExpression::makeAnd(&pool, operand1, operand2); + auto eval = Expression::eval(expr, gExpCtxt); + EXPECT_EQ(eval.type(), Value::kEmpty.type()); + EXPECT_EQ(eval, Value::kEmpty) << "check failed: " << expr->toString(); + } + { + // empty AND null + auto *operand1 = ConstantExpression::make(&pool, Value()); + auto *operand2 = ConstantExpression::make(&pool, Value(NullType::__NULL__)); + auto *expr = LogicalExpression::makeAnd(&pool, operand1, operand2); + auto eval = Expression::eval(expr, gExpCtxt); + EXPECT_EQ(eval.type(), Value::kNullValue.type()); + EXPECT_EQ(eval, Value::kNullValue) << "check failed: " << expr->toString(); + } + { + // null AND empty + auto *operand1 = ConstantExpression::make(&pool, Value(NullType::__NULL__)); + auto *operand2 = ConstantExpression::make(&pool, Value()); + auto *expr = LogicalExpression::makeAnd(&pool, operand1, operand2); + auto eval = Expression::eval(expr, gExpCtxt); + EXPECT_EQ(eval.type(), Value::kNullValue.type()); + EXPECT_EQ(eval, Value::kNullValue) << "check failed: " << expr->toString(); + } + { + // empty AND true AND empty + auto *operand1 = ConstantExpression::make(&pool, Value()); + auto *operand2 = ConstantExpression::make(&pool, Value(true)); + auto *operand3 = LogicalExpression::makeAnd(&pool, operand1, operand2); + auto *operand4 = ConstantExpression::make(&pool, Value()); + auto *expr = LogicalExpression::makeAnd(&pool, operand3, operand4); + auto eval = Expression::eval(expr, gExpCtxt); + EXPECT_EQ(eval.type(), Value::kEmpty.type()); + EXPECT_EQ(eval, Value::kEmpty) << "check failed: " << expr->toString(); + } + + { + // empty OR true + auto *operand1 = ConstantExpression::make(&pool, Value()); + auto *operand2 = ConstantExpression::make(&pool, Value(true)); + auto *expr = LogicalExpression::makeOr(&pool, operand1, operand2); + auto eval = Expression::eval(expr, gExpCtxt); + EXPECT_EQ(eval.type(), Value(true).type()); + EXPECT_EQ(eval, Value(true)) << "check failed: " << expr->toString(); + } + { + // empty OR false + auto *operand1 = ConstantExpression::make(&pool, Value()); + auto *operand2 = ConstantExpression::make(&pool, Value(false)); + auto *expr = LogicalExpression::makeOr(&pool, operand1, operand2); + auto eval = Expression::eval(expr, gExpCtxt); + EXPECT_EQ(eval.type(), Value::kEmpty.type()); + EXPECT_EQ(eval, Value::kEmpty) << "check failed: " << expr->toString(); + } + { + // true OR empty + auto *operand1 = ConstantExpression::make(&pool, Value(true)); + auto *operand2 = ConstantExpression::make(&pool, Value()); + auto *expr = LogicalExpression::makeOr(&pool, operand1, operand2); + auto eval = Expression::eval(expr, gExpCtxt); + EXPECT_EQ(eval.type(), Value(true).type()); + EXPECT_EQ(eval, Value(true)) << "check failed: " << expr->toString(); + } + { + // false OR empty + auto *operand1 = ConstantExpression::make(&pool, Value(false)); + auto *operand2 = ConstantExpression::make(&pool, Value()); + auto *expr = LogicalExpression::makeOr(&pool, operand1, operand2); + auto eval = Expression::eval(expr, gExpCtxt); + EXPECT_EQ(eval.type(), Value::kEmpty.type()); + EXPECT_EQ(eval, Value::kEmpty) << "check failed: " << expr->toString(); + } + { + // empty OR empty + auto *operand1 = ConstantExpression::make(&pool, Value()); + auto *operand2 = ConstantExpression::make(&pool, Value()); + auto *expr = LogicalExpression::makeOr(&pool, operand1, operand2); + auto eval = Expression::eval(expr, gExpCtxt); + EXPECT_EQ(eval.type(), Value::kEmpty.type()); + EXPECT_EQ(eval, Value::kEmpty) << "check failed: " << expr->toString(); + } + { + // empty OR null + auto *operand1 = ConstantExpression::make(&pool, Value()); + auto *operand2 = ConstantExpression::make(&pool, Value(NullType::__NULL__)); + auto *expr = LogicalExpression::makeOr(&pool, operand1, operand2); + auto eval = Expression::eval(expr, gExpCtxt); + EXPECT_EQ(eval.type(), Value::kNullValue.type()); + EXPECT_EQ(eval, Value::kNullValue) << "check failed: " << expr->toString(); + } + { + // null OR empty + auto *operand1 = ConstantExpression::make(&pool, Value(NullType::__NULL__)); + auto *operand2 = ConstantExpression::make(&pool, Value()); + auto *expr = LogicalExpression::makeOr(&pool, operand1, operand2); + auto eval = Expression::eval(expr, gExpCtxt); + EXPECT_EQ(eval.type(), Value::kNullValue.type()); + EXPECT_EQ(eval, Value::kNullValue) << "check failed: " << expr->toString(); + } + { + // empty OR false OR empty + auto *operand1 = ConstantExpression::make(&pool, Value()); + auto *operand2 = ConstantExpression::make(&pool, Value(false)); + auto *operand3 = LogicalExpression::makeOr(&pool, operand1, operand2); + auto *operand4 = ConstantExpression::make(&pool, Value()); + auto *expr = LogicalExpression::makeOr(&pool, operand3, operand4); + auto eval = Expression::eval(expr, gExpCtxt); + EXPECT_EQ(eval.type(), Value::kEmpty.type()); + EXPECT_EQ(eval, Value::kEmpty) << "check failed: " << expr->toString(); + } + + { + // empty XOR true + auto *operand1 = ConstantExpression::make(&pool, Value()); + auto *operand2 = ConstantExpression::make(&pool, Value(true)); + auto *expr = LogicalExpression::makeXor(&pool, operand1, operand2); + auto eval = Expression::eval(expr, gExpCtxt); + EXPECT_EQ(eval.type(), Value::kEmpty.type()); + EXPECT_EQ(eval, Value::kEmpty) << "check failed: " << expr->toString(); + } + { + // empty XOR false + auto *operand1 = ConstantExpression::make(&pool, Value()); + auto *operand2 = ConstantExpression::make(&pool, Value(false)); + auto *expr = LogicalExpression::makeXor(&pool, operand1, operand2); + auto eval = Expression::eval(expr, gExpCtxt); + EXPECT_EQ(eval.type(), Value::kEmpty.type()); + EXPECT_EQ(eval, Value::kEmpty) << "check failed: " << expr->toString(); + } + { + // true XOR empty + auto *operand1 = ConstantExpression::make(&pool, Value(true)); + auto *operand2 = ConstantExpression::make(&pool, Value()); + auto *expr = LogicalExpression::makeXor(&pool, operand1, operand2); + auto eval = Expression::eval(expr, gExpCtxt); + EXPECT_EQ(eval.type(), Value::kEmpty.type()); + EXPECT_EQ(eval, Value::kEmpty) << "check failed: " << expr->toString(); + } + { + // false XOR empty + auto *operand1 = ConstantExpression::make(&pool, Value(false)); + auto *operand2 = ConstantExpression::make(&pool, Value()); + auto *expr = LogicalExpression::makeXor(&pool, operand1, operand2); + auto eval = Expression::eval(expr, gExpCtxt); + EXPECT_EQ(eval.type(), Value::kEmpty.type()); + EXPECT_EQ(eval, Value::kEmpty) << "check failed: " << expr->toString(); + } + { + // empty XOR empty + auto *operand1 = ConstantExpression::make(&pool, Value()); + auto *operand2 = ConstantExpression::make(&pool, Value()); + auto *expr = LogicalExpression::makeXor(&pool, operand1, operand2); + auto eval = Expression::eval(expr, gExpCtxt); + EXPECT_EQ(eval.type(), Value::kEmpty.type()); + EXPECT_EQ(eval, Value::kEmpty) << "check failed: " << expr->toString(); + } + { + // empty XOR null + auto *operand1 = ConstantExpression::make(&pool, Value()); + auto *operand2 = ConstantExpression::make(&pool, Value(NullType::__NULL__)); + auto *expr = LogicalExpression::makeXor(&pool, operand1, operand2); + auto eval = Expression::eval(expr, gExpCtxt); + EXPECT_EQ(eval.type(), Value::kNullValue.type()); + EXPECT_EQ(eval, Value::kNullValue) << "check failed: " << expr->toString(); + } + { + // null XOR empty + auto *operand1 = ConstantExpression::make(&pool, Value(NullType::__NULL__)); + auto *operand2 = ConstantExpression::make(&pool, Value()); + auto *expr = LogicalExpression::makeXor(&pool, operand1, operand2); + auto eval = Expression::eval(expr, gExpCtxt); + EXPECT_EQ(eval.type(), Value::kNullValue.type()); + EXPECT_EQ(eval, Value::kNullValue) << "check failed: " << expr->toString(); + } + { + // true XOR empty XOR false + auto *operand1 = ConstantExpression::make(&pool, Value(true)); + auto *operand2 = ConstantExpression::make(&pool, Value()); + auto *operand3 = LogicalExpression::makeXor(&pool, operand1, operand2); + auto *operand4 = ConstantExpression::make(&pool, Value(false)); + auto *expr = LogicalExpression::makeXor(&pool, operand3, operand4); + auto eval = Expression::eval(expr, gExpCtxt); + EXPECT_EQ(eval.type(), Value::kEmpty.type()); + EXPECT_EQ(eval, Value::kEmpty) << "check failed: " << expr->toString(); + } + + { + // empty OR false AND true AND null XOR empty + auto *operand1 = ConstantExpression::make(&pool, Value()); + auto *operand2 = ConstantExpression::make(&pool, Value(false)); + auto *operand3 = ConstantExpression::make(&pool, Value(true)); + auto *operand4 = LogicalExpression::makeAnd(&pool, operand2, operand3); + auto *operand5 = ConstantExpression::make(&pool, Value(NullType::__NULL__)); + auto *operand6 = LogicalExpression::makeAnd(&pool, operand4, operand5); + auto *operand7 = LogicalExpression::makeOr(&pool, operand1, operand6); + auto *operand8 = ConstantExpression::make(&pool, Value()); + auto *expr = LogicalExpression::makeXor(&pool, operand7, operand8); + auto eval = Expression::eval(expr, gExpCtxt); + EXPECT_EQ(eval.type(), Value::kEmpty.type()); + EXPECT_EQ(eval, Value::kEmpty) << "check failed: " << expr->toString(); + } + { + // empty OR false AND true XOR empty OR true + auto *operand1 = ConstantExpression::make(&pool, Value()); + auto *operand2 = ConstantExpression::make(&pool, Value(false)); + auto *operand3 = ConstantExpression::make(&pool, Value(true)); + auto *operand4 = LogicalExpression::makeAnd(&pool, operand2, operand3); + auto *operand5 = LogicalExpression::makeOr(&pool, operand1, operand4); + auto *operand6 = ConstantExpression::make(&pool, Value()); + auto *operand7 = LogicalExpression::makeXor(&pool, operand5, operand6); + auto *operand8 = ConstantExpression::make(&pool, Value(true)); + auto *expr = LogicalExpression::makeOr(&pool, operand7, operand8); + auto eval = Expression::eval(expr, gExpCtxt); + EXPECT_EQ(eval.type(), Value(true).type()); + EXPECT_EQ(eval, Value(true)) << "check failed: " << expr->toString(); + } // clang-format off - TEST_EXPR((empty OR false) AND true XOR empty XOR null AND 2 / 0, Value::kNullValue); + { + // (empty OR false) AND true XOR empty XOR null AND 2 / 0 + auto *operand1 = ConstantExpression::make(&pool, Value()); + auto *operand2 = ConstantExpression::make(&pool, Value(false)); + auto *operand3 = LogicalExpression::makeOr(&pool, operand1, operand2); + auto *operand4 = ConstantExpression::make(&pool, Value(true)); + auto *operand5 = LogicalExpression::makeAnd(&pool, operand3, operand4); + auto *operand6 = ConstantExpression::make(&pool, Value()); + auto *operand7 = LogicalExpression::makeXor(&pool, operand5, operand6); + auto *operand8 = ConstantExpression::make(&pool, Value(NullType::__NULL__)); + auto *operand9 = ConstantExpression::make(&pool, Value(2)); + auto *operand10 = ConstantExpression::make(&pool, Value(0)); + auto *operand11 = ArithmeticExpression::makeDivision(&pool, operand9, operand10); + auto *operand12 = LogicalExpression::makeAnd(&pool, operand8, operand11); + auto *expr = LogicalExpression::makeXor(&pool, operand7, operand12); + auto eval = Expression::eval(expr, gExpCtxt); + EXPECT_EQ(eval.type(), Value::kNullValue.type()); + EXPECT_EQ(eval, Value::kNullValue) << "check failed: " << expr->toString(); + } + // clang-format on // empty OR false AND 2/0 - TEST_EXPR(empty OR false AND true XOR empty XOR null AND 2 / 0, Value::kEmpty); - TEST_EXPR(empty AND true XOR empty XOR null AND 2 / 0, Value::kNullValue); - TEST_EXPR(empty OR false AND true XOR empty OR null AND 2 / 0, Value::kNullDivByZero); - TEST_EXPR(empty OR false AND empty XOR empty OR null, Value::kNullValue); + { + // empty OR false AND true XOR empty XOR null AND 2 / 0 + auto *operand1 = ConstantExpression::make(&pool, Value()); + auto *operand2 = ConstantExpression::make(&pool, Value(false)); + auto *operand3 = ConstantExpression::make(&pool, Value(true)); + auto *operand4 = LogicalExpression::makeAnd(&pool, operand2, operand3); + auto *operand5 = LogicalExpression::makeOr(&pool, operand1, operand4); + auto *operand6 = ConstantExpression::make(&pool, Value()); + auto *operand7 = LogicalExpression::makeXor(&pool, operand5, operand6); + auto *operand8 = ConstantExpression::make(&pool, Value(NullType::__NULL__)); + auto *operand9 = ConstantExpression::make(&pool, Value(2)); + auto *operand10 = ConstantExpression::make(&pool, Value(0)); + auto *operand11 = ArithmeticExpression::makeDivision(&pool, operand9, operand10); + auto *operand12 = LogicalExpression::makeAnd(&pool, operand8, operand11); + auto *expr = LogicalExpression::makeXor(&pool, operand7, operand12); + auto eval = Expression::eval(expr, gExpCtxt); + // caution + EXPECT_EQ(eval.type(), Value::kNullDivByZero.type()); + EXPECT_EQ(eval, Value::kNullDivByZero) << "check failed: " << expr->toString(); + } + { + // empty AND true XOR empty XOR null AND 2 / 0 + auto *operand1 = ConstantExpression::make(&pool, Value()); + auto *operand2 = ConstantExpression::make(&pool, Value(true)); + auto *operand3 = LogicalExpression::makeAnd(&pool, operand1, operand2); + auto *operand4 = ConstantExpression::make(&pool, Value()); + auto *operand5 = LogicalExpression::makeXor(&pool, operand3, operand4); + auto *operand6 = ConstantExpression::make(&pool, Value(NullType::__NULL__)); + auto *operand7 = ConstantExpression::make(&pool, Value(2)); + auto *operand8 = ConstantExpression::make(&pool, Value(0)); + auto *operand9 = ArithmeticExpression::makeDivision(&pool, operand7, operand8); + auto *operand10 = LogicalExpression::makeAnd(&pool, operand6, operand9); + auto *expr = LogicalExpression::makeXor(&pool, operand5, operand10); + auto eval = Expression::eval(expr, gExpCtxt); + EXPECT_EQ(eval.type(), Value::kNullValue.type()); + EXPECT_EQ(eval, Value::kNullValue) << "check failed: " << expr->toString(); + } + { + // empty OR false AND true XOR empty OR null AND 2 / 0 + auto *operand1 = ConstantExpression::make(&pool, Value()); + auto *operand2 = ConstantExpression::make(&pool, Value(false)); + auto *operand3 = ConstantExpression::make(&pool, Value(true)); + auto *operand4 = LogicalExpression::makeAnd(&pool, operand2, operand3); + auto *operand5 = LogicalExpression::makeOr(&pool, operand1, operand4); + auto *operand6 = ConstantExpression::make(&pool, Value()); + auto *operand7 = LogicalExpression::makeXor(&pool, operand5, operand6); + auto *operand8 = ConstantExpression::make(&pool, Value(NullType::__NULL__)); + auto *operand9 = ConstantExpression::make(&pool, Value(2)); + auto *operand10 = ConstantExpression::make(&pool, Value(0)); + auto *operand11 = ArithmeticExpression::makeDivision(&pool, operand9, operand10); + auto *operand12 = LogicalExpression::makeAnd(&pool, operand8, operand11); + auto *expr = LogicalExpression::makeOr(&pool, operand7, operand12); + auto eval = Expression::eval(expr, gExpCtxt); + EXPECT_EQ(eval.type(), Value::kNullDivByZero.type()); + EXPECT_EQ(eval, Value::kNullDivByZero) << "check failed: " << expr->toString(); + } + { + // TEST_EXPR(empty OR false AND empty XOR empty OR null, Value::kNullValue); + // 7 OR null + auto *operand1 = ConstantExpression::make(&pool, Value()); + auto *operand2 = ConstantExpression::make(&pool, Value(false)); + auto *operand3 = ConstantExpression::make(&pool, Value()); + auto *operand4 = LogicalExpression::makeAnd(&pool, operand2, operand3); + auto *operand5 = LogicalExpression::makeOr(&pool, operand1, operand4); + auto *operand6 = ConstantExpression::make(&pool, Value()); + auto *operand7 = LogicalExpression::makeXor(&pool, operand5, operand6); + auto *operand8 = ConstantExpression::make(&pool, Value(NullType::__NULL__)); + auto *expr = LogicalExpression::makeOr(&pool, operand7, operand8); + auto eval = Expression::eval(expr, gExpCtxt); + EXPECT_EQ(eval.type(), Value::kNullValue.type()); + EXPECT_EQ(eval, Value::kNullValue) << "check failed: " << expr->toString(); + } } } } // namespace nebula diff --git a/src/common/expression/test/RelationalExpressionTest.cpp b/src/common/expression/test/RelationalExpressionTest.cpp index 1ae134bc8ae..11bc892c463 100644 --- a/src/common/expression/test/RelationalExpressionTest.cpp +++ b/src/common/expression/test/RelationalExpressionTest.cpp @@ -96,15 +96,78 @@ TEST_F(ExpressionTest, LiteralConstantsRelational) { TEST_EXPR(1 <= 1, true); } { - TEST_EXPR(empty == empty, true); - TEST_EXPR(empty == null, Value::kNullValue); - TEST_EXPR(empty != null, Value::kNullValue); - TEST_EXPR(empty != 1, true); - TEST_EXPR(empty != true, true); - TEST_EXPR(empty > "1", Value::kEmpty); - TEST_EXPR(empty < 1, Value::kEmpty); - TEST_EXPR(empty >= 1.11, Value::kEmpty); - + // empty == empty + auto *operand1 = ConstantExpression::make(&pool, Value()); + auto *operand2 = ConstantExpression::make(&pool, Value()); + auto *expr = RelationalExpression::makeEQ(&pool, operand1, operand2); + auto eval = Expression::eval(expr, gExpCtxt); + EXPECT_EQ(eval.type(), Value(true).type()) << "type check failed: " << expr->toString(); + EXPECT_EQ(eval, Value(true)) << "check failed: " << expr->toString(); + } + { + // empty == null + auto *operand1 = ConstantExpression::make(&pool, Value()); + auto *operand2 = ConstantExpression::make(&pool, Value(NullType::__NULL__)); + auto *expr = RelationalExpression::makeEQ(&pool, operand1, operand2); + auto eval = Expression::eval(expr, gExpCtxt); + EXPECT_EQ(eval.type(), Value::kNullValue.type()) << "type check failed: " << expr->toString(); + EXPECT_EQ(eval, Value::kNullValue) << "check failed: " << expr->toString(); + } + { + // empty != null + auto *operand1 = ConstantExpression::make(&pool, Value()); + auto *operand2 = ConstantExpression::make(&pool, Value(NullType::__NULL__)); + auto *expr = RelationalExpression::makeNE(&pool, operand1, operand2); + auto eval = Expression::eval(expr, gExpCtxt); + EXPECT_EQ(eval.type(), Value::kNullValue.type()) << "type check failed: " << expr->toString(); + EXPECT_EQ(eval, Value::kNullValue) << "check failed: " << expr->toString(); + } + { + // empty != 1 + auto *operand1 = ConstantExpression::make(&pool, Value()); + auto *operand2 = ConstantExpression::make(&pool, Value(1)); + auto *expr = RelationalExpression::makeNE(&pool, operand1, operand2); + auto eval = Expression::eval(expr, gExpCtxt); + EXPECT_EQ(eval.type(), Value(true).type()) << "type check failed: " << expr->toString(); + EXPECT_EQ(eval, Value(true)) << "check failed: " << expr->toString(); + } + { + // empty != true + auto *operand1 = ConstantExpression::make(&pool, Value()); + auto *operand2 = ConstantExpression::make(&pool, Value(true)); + auto *expr = RelationalExpression::makeNE(&pool, operand1, operand2); + auto eval = Expression::eval(expr, gExpCtxt); + EXPECT_EQ(eval.type(), Value(true).type()) << "type check failed: " << expr->toString(); + EXPECT_EQ(eval, Value(true)) << "check failed: " << expr->toString(); + } + { + // empty > "1" + auto *operand1 = ConstantExpression::make(&pool, Value()); + auto *operand2 = ConstantExpression::make(&pool, Value("1")); + auto *expr = RelationalExpression::makeGT(&pool, operand1, operand2); + auto eval = Expression::eval(expr, gExpCtxt); + EXPECT_EQ(eval.type(), Value::kEmpty.type()) << "type check failed: " << expr->toString(); + EXPECT_EQ(eval, Value::kEmpty) << "check failed: " << expr->toString(); + } + { + // empty < 1 + auto *operand1 = ConstantExpression::make(&pool, Value()); + auto *operand2 = ConstantExpression::make(&pool, Value(1)); + auto *expr = RelationalExpression::makeLT(&pool, operand1, operand2); + auto eval = Expression::eval(expr, gExpCtxt); + EXPECT_EQ(eval.type(), Value::kEmpty.type()) << "type check failed: " << expr->toString(); + EXPECT_EQ(eval, Value::kEmpty) << "check failed: " << expr->toString(); + } + { + // empty >= 1.11 + auto *operand1 = ConstantExpression::make(&pool, Value()); + auto *operand2 = ConstantExpression::make(&pool, Value(1.11)); + auto *expr = RelationalExpression::makeGE(&pool, operand1, operand2); + auto eval = Expression::eval(expr, gExpCtxt); + EXPECT_EQ(eval.type(), Value::kEmpty.type()) << "type check failed: " << expr->toString(); + EXPECT_EQ(eval, Value::kEmpty) << "check failed: " << expr->toString(); + } + { TEST_EXPR(null != 1, Value::kNullValue); TEST_EXPR(null != true, Value::kNullValue); TEST_EXPR(null > "1", Value::kNullValue); diff --git a/src/common/expression/test/TestBase.h b/src/common/expression/test/TestBase.h index 2ba5a6c629a..89fc2b3cb85 100644 --- a/src/common/expression/test/TestBase.h +++ b/src/common/expression/test/TestBase.h @@ -46,162 +46,86 @@ #include "common/expression/VariableExpression.h" #include "common/expression/VertexExpression.h" #include "common/expression/test/ExpressionContextMock.h" +#include "parser/GQLParser.h" nebula::ExpressionContextMock gExpCtxt; nebula::ObjectPool pool; namespace nebula { - -static void InsertSpace(std::string &str) { - for (unsigned int i = 0; i < str.size(); i++) { - if (str[i] == '(') { - str.insert(i + 1, 1, ' '); - } else if (str[i] == ')') { - str.insert(i, 1, ' '); - i += 1; - } else { - continue; - } - } -} - -static std::vector InfixToSuffix(const std::vector &expr) { - std::vector values; - std::stack operators; - std::unordered_map priority = {{"OR", 1}, - {"AND", 2}, - {"XOR", 3}, - {"==", 4}, - {"!=", 4}, - {">=", 5}, - {"<=", 5}, - {">", 5}, - {"<", 5}, - {"+", 6}, - {"-", 6}, - {"*", 7}, - {"/", 7}, - {"%", 7}, - {"!", 8}}; - - for (const auto &str : expr) { - if (priority.find(str) != priority.end() || str == "(") { - if (operators.empty() || str == "(") { - operators.push(str); - } else { - if (operators.top() == "(" || priority[str] > priority[operators.top()]) { - operators.push(str); - } else { - while (!operators.empty() && priority[str] <= priority[operators.top()]) { - values.push_back(operators.top()); - operators.pop(); - } - operators.push(str); - } - } - } else if (str == ")") { - while (!operators.empty() && operators.top() != "(") { - values.push_back(operators.top()); - operators.pop(); - } - operators.pop(); - } else { - values.push_back(str); - } - } - while (!operators.empty()) { - values.push_back(operators.top()); - operators.pop(); - } - return values; -} - class ExpressionTest : public ::testing::Test { public: void SetUp() override {} void TearDown() override {} protected: - static std::unordered_map boolen_; - // static std::unordered_map op_; - static std::unordered_map< - std::string, - std::function> - op_; - - protected: - Expression *ExpressionCalu(const std::vector &expr) { - std::vector relationOp = {">", ">=", "<", "<=", "==", "!="}; - std::vector logicalOp = {"AND", "OR", "XOR"}; - std::vector arithmeticOp = {"+", "-", "*", "/", "%"}; - - std::vector symbol = InfixToSuffix(expr); - if (symbol.size() == 1) { - // TEST_EXPR(true, true) - if (boolen_.find(symbol.front()) != boolen_.end()) { - return ConstantExpression::make(&pool, boolen_[symbol.front()]); - } else if (symbol.front().find('.') != std::string::npos) { - // TEST_EXPR(123.0, 123.0) - return ConstantExpression::make(&pool, ::atof(symbol.front().c_str())); - } - // TEST_EXPR(123, 123) - return ConstantExpression::make(&pool, ::atoi(symbol.front().c_str())); - } - - // calu suffix expression - std::stack value; - for (const auto &str : symbol) { - if (op_.find(str) == op_.end()) { - Expression *ep = nullptr; - if (boolen_.find(str) != boolen_.end()) { - ep = ConstantExpression::make(&pool, boolen_[str.c_str()]); - } else if (str.find('.') != std::string::npos) { - ep = ConstantExpression::make(&pool, ::atof(str.c_str())); - } else { - ep = ConstantExpression::make(&pool, ::atoi(str.c_str())); - } - value.push(ep); - } else { - Expression *result = nullptr; - Expression *rhs = value.top(); - value.pop(); - Expression *lhs = value.top(); - value.pop(); - if (std::find(arithmeticOp.begin(), arithmeticOp.end(), str) != arithmeticOp.end()) { - result = op_[str](&pool, lhs, rhs); - } else if (std::find(relationOp.begin(), relationOp.end(), str) != relationOp.end()) { - result = op_[str](&pool, lhs, rhs); - } else if (std::find(logicalOp.begin(), logicalOp.end(), str) != logicalOp.end()) { - result = op_[str](&pool, lhs, rhs); - } else { - return ConstantExpression::make(&pool, NullType::UNKNOWN_PROP); - } - value.push(result); - } - } - return value.top(); - } - void testExpr(const std::string &exprSymbol, Value expected) { - std::string expr(exprSymbol); - InsertSpace(expr); - std::vector splitString; - boost::split(splitString, expr, boost::is_any_of(" \t")); - Expression *ep = ExpressionCalu(splitString); + std::string query = "RETURN " + exprSymbol; + nebula::graph::QueryContext queryCtxt; + nebula::GQLParser gParser(&queryCtxt); + auto result = gParser.parse(query); + ASSERT_EQ(result.ok(), true); + auto *sequentialSentences = static_cast(result.value().get()); + ASSERT_NE(sequentialSentences, nullptr); + auto sentences = sequentialSentences->sentences(); + ASSERT_GT(sentences.size(), 0); + auto *yieldSentence = static_cast(sentences[0]); + ASSERT_NE(yieldSentence, nullptr); + ASSERT_NE(yieldSentence->yield(), nullptr); + ASSERT_NE(yieldSentence->yield()->yields(), nullptr); + ASSERT_NE(yieldSentence->yield()->yields()->back(), nullptr); + Expression *ep = yieldSentence->yield()->yields()->back()->expr(); auto eval = Expression::eval(ep, gExpCtxt); EXPECT_EQ(eval.type(), expected.type()) << "type check failed: " << ep->toString(); EXPECT_EQ(eval, expected) << "check failed: " << ep->toString(); } void testToString(const std::string &exprSymbol, const char *expected) { - std::string expr(exprSymbol); - InsertSpace(expr); - std::vector splitString; - boost::split(splitString, expr, boost::is_any_of(" \t")); - Expression *ep = ExpressionCalu(splitString); + std::string query = "RETURN " + exprSymbol; + nebula::graph::QueryContext queryCtxt; + nebula::GQLParser gParser(&queryCtxt); + auto result = gParser.parse(query); + ASSERT_EQ(result.ok(), true); + auto *sequentialSentences = static_cast(result.value().get()); + ASSERT_NE(sequentialSentences, nullptr); + auto sentences = sequentialSentences->sentences(); + ASSERT_GT(sentences.size(), 0); + auto *yieldSentence = static_cast(sentences[0]); + ASSERT_NE(yieldSentence, nullptr); + ASSERT_NE(yieldSentence->yield(), nullptr); + ASSERT_NE(yieldSentence->yield()->yields(), nullptr); + ASSERT_NE(yieldSentence->yield()->yields()->back(), nullptr); + Expression *ep = yieldSentence->yield()->yields()->back()->expr(); + ASSERT_NE(ep, nullptr); EXPECT_EQ(ep->toString(), expected); } void testFunction(const char *name, const std::vector &args, const Value &expected) { + std::string query = "RETURN " + std::string(name) + "("; + for (const auto &i : args) { + query += i.toString() + ","; + } + if (query.back() == ',') { + query.pop_back(); + } + query += ")"; + nebula::graph::QueryContext queryCtxt; + nebula::GQLParser gParser(&queryCtxt); + auto result = gParser.parse(query); + ASSERT_EQ(result.ok(), true); + auto *sequentialSentences = static_cast(result.value().get()); + ASSERT_NE(sequentialSentences, nullptr); + auto sentences = sequentialSentences->sentences(); + ASSERT_GT(sentences.size(), 0); + auto *yieldSentence = static_cast(sentences[0]); + ASSERT_NE(yieldSentence, nullptr); + ASSERT_NE(yieldSentence->yield(), nullptr); + ASSERT_NE(yieldSentence->yield()->yields(), nullptr); + ASSERT_NE(yieldSentence->yield()->yields()->back(), nullptr); + auto eval = Expression::eval(yieldSentence->yield()->yields()->back()->expr(), gExpCtxt); + // EXPECT_EQ(eval.type(), expected.type()); + EXPECT_EQ(eval, expected); + } + + void testPathFunction(const char *name, const std::vector &args, const Value &expected) { ArgumentList *argList = ArgumentList::make(&pool); for (const auto &i : args) { argList->addArgument(ConstantExpression::make(&pool, i)); @@ -212,6 +136,7 @@ class ExpressionTest : public ::testing::Test { EXPECT_EQ(eval, expected); } }; + // expr -- the expression can evaluate by nGQL parser may not evaluated by c++ // expected -- the expected value of expression must evaluated by c++ #define TEST_EXPR(expr, expected) \ @@ -224,6 +149,11 @@ class ExpressionTest : public ::testing::Test { testFunction(#expr, args, expected); \ } while (0) +#define TEST_PATH_FUNCTION(expr, args, expected) \ + do { \ + testPathFunction(#expr, args, expected); \ + } while (0) + #define TEST_TOSTRING(expr, expected) \ do { \ testToString(#expr, expected); \ @@ -239,64 +169,6 @@ class ExpressionTest : public ::testing::Test { path.steps.emplace_back(std::move(step)); \ } while (0) -// Functions used to construct corresponding expressions -using expressionGen = - std::function; - -expressionGen makeAddExpr{&ArithmeticExpression::makeAdd}; -expressionGen makeMinusExpr{&ArithmeticExpression::makeMinus}; -expressionGen makeMultiplyExpr{&ArithmeticExpression::makeMultiply}; -expressionGen makeDivisionExpr{&ArithmeticExpression::makeDivision}; -expressionGen makeModExpr{&ArithmeticExpression::makeMod}; -expressionGen makeOrExpr{ - static_cast( - &LogicalExpression::makeOr)}; -expressionGen makeAndExpr{ - static_cast( - &LogicalExpression::makeAnd)}; -expressionGen makeXorExpr{ - static_cast( - &LogicalExpression::makeXor)}; -expressionGen makeRelGT{ - static_cast( - &RelationalExpression::makeGT)}; -expressionGen makeRelLT{ - static_cast( - &RelationalExpression::makeLT)}; -expressionGen makeRelGE{ - static_cast( - &RelationalExpression::makeGE)}; -expressionGen makeRelLE{ - static_cast( - &RelationalExpression::makeLE)}; -expressionGen makeRelEQ{ - static_cast( - &RelationalExpression::makeEQ)}; -expressionGen makeRelNE{ - static_cast( - &RelationalExpression::makeNE)}; - -std::unordered_map ExpressionTest::op_ = {{"+", makeAddExpr}, - {"-", makeMinusExpr}, - {"*", makeMultiplyExpr}, - {"/", makeDivisionExpr}, - {"%", makeModExpr}, - {"OR", makeOrExpr}, - {"AND", makeAndExpr}, - {"XOR", makeXorExpr}, - {">", makeRelGT}, - {"<", makeRelLT}, - {">=", makeRelGE}, - {"<=", makeRelLE}, - {"==", makeRelEQ}, - {"!=", makeRelNE}}; - -std::unordered_map ExpressionTest::boolen_ = { - {"true", Value(true)}, - {"false", Value(false)}, - {"empty", Value()}, - {"null", Value(NullType::__NULL__)}}; - static std::unordered_map> args_ = { {"null", {}}, {"int", {4}}, @@ -314,6 +186,5 @@ static std::unordered_map> args_ = { {"neg_side", {"abcdefghijklmnopq", -2}}, {"pad", {"abcdefghijkl", 16, "123"}}, {"udf_is_in", {4, 1, 2, 8, 4, 3, 1, 0}}}; - } // namespace nebula #endif // COMMON_EXPRESSION_TEST_TESTBASE_H_ diff --git a/tests/conftest.py b/tests/conftest.py index c4f320ea462..fa7d1770d3b 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -14,9 +14,9 @@ from tests.common.utils import get_conn_pool from tests.common.constants import NB_TMP_PATH, SPACE_TMP_PATH -#from thrift.transport import TSocket -#from thrift.transport import TTransport -#from thrift.protocol import TBinaryProtocol +from nebula2.fbthrift.transport import TSocket +from nebula2.fbthrift.transport import TTransport +from nebula2.fbthrift.protocol import TBinaryProtocol from nebula2.gclient.net import Connection from nebula2.graph import GraphService @@ -193,12 +193,12 @@ def workarround_for_class(request, pytestconfig, conn_pool, request.cls.cleanup() request.cls.drop_data() -#@pytest.fixture(scope="class") -#def establish_a_rare_connection(pytestconfig): -# addr = pytestconfig.getoption("address") -# host_addr = addr.split(":") if addr else ["localhost", get_ports()[0]] -# socket = TSocket.TSocket(host_addr[0], host_addr[1]) -# transport = TTransport.TBufferedTransport(socket) -# protocol = TBinaryProtocol.TBinaryProtocol(transport) -# transport.open() -# return GraphService.Client(protocol) +@pytest.fixture(scope="class") +def establish_a_rare_connection(pytestconfig): + addr = pytestconfig.getoption("address") + host_addr = addr.split(":") if addr else ["localhost", get_ports()[0]] + socket = TSocket.TSocket(host_addr[0], host_addr[1]) + transport = TTransport.TBufferedTransport(socket) + protocol = TBinaryProtocol.TBinaryProtocol(transport) + transport.open() + return GraphService.Client(protocol) diff --git a/tests/tck/conftest.py b/tests/tck/conftest.py index fc2d8f67685..eff2f8fe17a 100644 --- a/tests/tck/conftest.py +++ b/tests/tck/conftest.py @@ -32,8 +32,8 @@ from tests.tck.utils.table import dataset, table from tests.tck.utils.nbv import murmurhash2 -#from nebula2.graph.ttypes import VerifyClientVersionReq -#from nebula2.graph.ttypes import VerifyClientVersionResp +from nebula2.graph.ttypes import VerifyClientVersionReq +from nebula2.graph.ttypes import VerifyClientVersionResp parse = functools.partial(parsers.parse) rparse = functools.partial(parsers.re) @@ -536,30 +536,30 @@ def executing_query_with_params(query, indices, keys, graph_spaces, session, req ngql = combine_query(query).format(*vals) exec_query(request, ngql, session, graph_spaces) -#@given(parse("nothing")) -#def nothing(): -# pass -# -#@when(parse("connecting the servers with a compatible client version")) -#def connecting_servers_with_a_compatible_client_version(establish_a_rare_connection, graph_spaces): -# conn = establish_a_rare_connection -# graph_spaces["resp"] = conn.verifyClientVersion(VerifyClientVersionReq()) -# conn._iprot.trans.close() -# -#@then(parse("the connection should be established")) -#def check_client_compatible(graph_spaces): -# resp = graph_spaces["resp"] -# assert resp.error_code == ErrorCode.SUCCEEDED, f'The client was rejected by server: {resp}' -# -#@when(parse("connecting the servers with a client version of {version}")) -#def connecting_servers_with_a_compatible_client_version(version, establish_a_rare_connection, graph_spaces): -# conn = establish_a_rare_connection -# req = VerifyClientVersionReq() -# req.version = version -# graph_spaces["resp"] = conn.verifyClientVersion(req) -# conn._iprot.trans.close() -# -#@then(parse("the connection should be rejected")) -#def check_client_compatible(graph_spaces): -# resp = graph_spaces["resp"] -# assert resp.error_code == ErrorCode.E_CLIENT_SERVER_INCOMPATIBLE, f'The client was not rejected by server: {resp}' +@given(parse("nothing")) +def nothing(): + pass + +@when(parse("connecting the servers with a compatible client version")) +def connecting_servers_with_a_compatible_client_version(establish_a_rare_connection, graph_spaces): + conn = establish_a_rare_connection + graph_spaces["resp"] = conn.verifyClientVersion(VerifyClientVersionReq()) + conn._iprot.trans.close() + +@then(parse("the connection should be established")) +def check_client_compatible(graph_spaces): + resp = graph_spaces["resp"] + assert resp.error_code == ErrorCode.SUCCEEDED, f'The client was rejected by server: {resp}' + +@when(parse("connecting the servers with a client version of {version}")) +def connecting_servers_with_a_compatible_client_version(version, establish_a_rare_connection, graph_spaces): + conn = establish_a_rare_connection + req = VerifyClientVersionReq() + req.version = version + graph_spaces["resp"] = conn.verifyClientVersion(req) + conn._iprot.trans.close() + +@then(parse("the connection should be rejected")) +def check_client_compatible(graph_spaces): + resp = graph_spaces["resp"] + assert resp.error_code == ErrorCode.E_CLIENT_SERVER_INCOMPATIBLE, f'The client was not rejected by server: {resp}' diff --git a/tests/tck/features/go/GoYieldVertexEdge.feature b/tests/tck/features/go/GoYieldVertexEdge.feature index b38ebc7f876..4075300fbbf 100644 --- a/tests/tck/features/go/GoYieldVertexEdge.feature +++ b/tests/tck/features/go/GoYieldVertexEdge.feature @@ -226,9 +226,7 @@ Feature: Go Yield Vertex And Edge Sentence """ Then a SyntaxError should be raised at runtime: syntax error near `OVER' - @skip - # reason we not support hash hash hash from now on. line 67 in Value.cpp - Scenario: distinct map + Scenario: distinct map and set When executing query: """ GO FROM "Boris Diaw" OVER like YIELD dst(edge) as id | @@ -236,7 +234,21 @@ Feature: Go Yield Vertex And Edge Sentence GO FROM $-.id OVER serve YIELD DISTINCT dst(edge) as dst, edge as e, properties(edge) as props """ Then the result should be, in any order, with relax comparison: - | dst | e | props | + | dst | e | props | + | "Spurs" | [:serve "Manu Ginobili"->"Spurs" @0 {end_year: 2018, start_year: 2002}] | {end_year: 2018, start_year: 2002} | + | "Spurs" | [:serve "Tim Duncan"->"Spurs" @0 {end_year: 2016, start_year: 1997}] | {end_year: 2016, start_year: 1997} | + | "Hornets" | [:serve "Tony Parker"->"Hornets" @0 {end_year: 2019, start_year: 2018}] | {end_year: 2019, start_year: 2018} | + | "Spurs" | [:serve "Tony Parker"->"Spurs" @0 {end_year: 2018, start_year: 1999}] | {end_year: 2018, start_year: 1999} | + | "Spurs" | [:serve "LaMarcus Aldridge"->"Spurs" @0 {end_year: 2019, start_year: 2015}] | {end_year: 2019, start_year: 2015} | + | "Trail Blazers" | [:serve "LaMarcus Aldridge"->"Trail Blazers" @0 {end_year: 2015, start_year: 2006}] | {end_year: 2015, start_year: 2006} | + When executing query: + """ + GO 2 STEPS FROM "Tim Duncan" OVER like YIELD dst(edge) as id | + YIELD DISTINCT collect($-.id) as a, collect_set($-.id) as b + """ + Then the result should be, in any order, with relax comparison: + | a | b | + | ["Tim Duncan", "LaMarcus Aldridge", "Manu Ginobili", "Tim Duncan"] | {"Manu Ginobili", "LaMarcus Aldridge", "Tim Duncan"} | Scenario: distinct When executing query: diff --git a/tests/tck/features/match/Base.feature b/tests/tck/features/match/Base.feature index 98705f6b212..8cd92190d1a 100644 --- a/tests/tck/features/match/Base.feature +++ b/tests/tck/features/match/Base.feature @@ -211,6 +211,21 @@ Feature: Basic match | ("Paul Gasol" :player{age: 38, name: "Paul Gasol"}) | [:like "Paul Gasol"->"Marc Gasol" @0 {likeness: 99}] | ("Marc Gasol" :player{age: 34, name: "Marc Gasol"}) | | ("Yao Ming" :player{age: 38, name: "Yao Ming"}) | [:like "Yao Ming"->"Shaquile O'Neal" @0 {likeness: 90}] | ("Shaquile O'Neal" :player{age: 47, name: "Shaquile O'Neal"}) | | ("Yao Ming" :player{age: 38, name: "Yao Ming"}) | [:like "Yao Ming"->"Tracy McGrady" @0 {likeness: 90}] | ("Tracy McGrady" :player{age: 39, name: "Tracy McGrady"}) | + When executing query: + """ + MATCH (v:player)-[e:like]->(v2) where id(v) == "Tim Duncan" RETURN DISTINCT properties(e) as props, e + """ + Then the result should be, in any order, with relax comparison: + | props | e | + | {likeness: 95} | [:like "Tim Duncan"->"Manu Ginobili" @0 {likeness: 95}] | + | {likeness: 95} | [:like "Tim Duncan"->"Tony Parker" @0 {likeness: 95}] | + When executing query: + """ + MATCH (v:player)-[e:like]->(v2) where id(v) == "Tim Duncan" RETURN DISTINCT properties(e) as props + """ + Then the result should be, in any order, with relax comparison: + | props | + | {likeness: 95} | Scenario: two steps When executing query: diff --git a/tests/tck/features/verify_client_version/VerifyClientVersion.feature b/tests/tck/features/verify_client_version/VerifyClientVersion.feature index 777c11b9efd..0eb84a15825 100644 --- a/tests/tck/features/verify_client_version/VerifyClientVersion.feature +++ b/tests/tck/features/verify_client_version/VerifyClientVersion.feature @@ -2,7 +2,6 @@ # # This source code is licensed under Apache 2.0 License, # attached with Common Clause Condition 1.0, found in the LICENSES directory. -@skip Feature: Verify client version Scenario: compatible version