diff --git a/conf/nebula-graphd.conf.default b/conf/nebula-graphd.conf.default index 6f184327df6..c1be4f72580 100644 --- a/conf/nebula-graphd.conf.default +++ b/conf/nebula-graphd.conf.default @@ -97,3 +97,19 @@ ########## session ########## # Maximum number of sessions that can be created per IP and per user --max_sessions_per_ip_per_user=300 + +########## memory tracker ########## +# trackable memory ratio (trackable_memory / (total_memory - untracked_reserved_memory) ) +--memory_tracker_limit_ratio=0.8 +# untracked reserved memory in Mib +--memory_tracker_untracked_reserved_memory_mb=50 + +# enable log memory tracker stats periodically +--memory_tracker_detail_log=false +# log memory tacker stats interval in milliseconds +--memory_tracker_detail_log_interval_ms=60000 + +# enable memory background purge (if jemalloc is used) +--memory_purge_enabled=true +# memory background purge interval in seconds +--memory_purge_interval_seconds=10 diff --git a/conf/nebula-graphd.conf.production b/conf/nebula-graphd.conf.production index d5b07642394..3f1d828d533 100644 --- a/conf/nebula-graphd.conf.production +++ b/conf/nebula-graphd.conf.production @@ -96,3 +96,19 @@ ########## session ########## # Maximum number of sessions that can be created per IP and per user --max_sessions_per_ip_per_user=300 + +########## memory tracker ########## +# trackable memory ratio (trackable_memory / (total_memory - untracked_reserved_memory) ) +--memory_tracker_limit_ratio=0.8 +# untracked reserved memory in Mib +--memory_tracker_untracked_reserved_memory_mb=50 + +# enable log memory tracker stats periodically +--memory_tracker_detail_log=false +# log memory tacker stats interval in milliseconds +--memory_tracker_detail_log_interval_ms=60000 + +# enable memory background purge (if jemalloc is used) +--memory_purge_enabled=true +# memory background purge interval in seconds +--memory_purge_interval_seconds=10 diff --git a/conf/nebula-storaged.conf.default b/conf/nebula-storaged.conf.default index 34c345ac9ed..7746aef5057 100644 --- a/conf/nebula-storaged.conf.default +++ b/conf/nebula-storaged.conf.default @@ -122,3 +122,19 @@ --rebuild_index_part_rate_limit=4194304 # The amount of data sent in each batch when leader synchronizes rebuilding index --rebuild_index_batch_size=1048576 + +########## memory tracker ########## +# trackable memory ratio (trackable_memory / (total_memory - untracked_reserved_memory) ) +--memory_tracker_limit_ratio=0.8 +# untracked reserved memory in Mib +--memory_tracker_untracked_reserved_memory_mb=50 + +# enable log memory tracker stats periodically +--memory_tracker_detail_log=false +# log memory tacker stats interval in milliseconds +--memory_tracker_detail_log_interval_ms=60000 + +# enable memory background purge (if jemalloc is used) +--memory_purge_enabled=true +# memory background purge interval in seconds +--memory_purge_interval_seconds=10 diff --git a/conf/nebula-storaged.conf.production b/conf/nebula-storaged.conf.production index d0004f06fc5..a5f3f64b92a 100644 --- a/conf/nebula-storaged.conf.production +++ b/conf/nebula-storaged.conf.production @@ -123,3 +123,19 @@ --rebuild_index_part_rate_limit=4194304 # The amount of data sent in each batch when leader synchronizes rebuilding index --rebuild_index_batch_size=1048576 + +########## memory tracker ########## +# trackable memory ratio (trackable_memory / (total_memory - untracked_reserved_memory) ) +--memory_tracker_limit_ratio=0.8 +# untracked reserved memory in Mib +--memory_tracker_untracked_reserved_memory_mb=50 + +# enable log memory tracker stats periodically +--memory_tracker_detail_log=false +# log memory tacker stats interval in milliseconds +--memory_tracker_detail_log_interval_ms=60000 + +# enable memory background purge (if jemalloc is used) +--memory_purge_enabled=true +# memory background purge interval in seconds +--memory_purge_interval_seconds=10 diff --git a/src/clients/meta/MetaClient.cpp b/src/clients/meta/MetaClient.cpp index 7446ab95f12..e04cb91214e 100644 --- a/src/clients/meta/MetaClient.cpp +++ b/src/clients/meta/MetaClient.cpp @@ -828,6 +828,8 @@ Status MetaClient::handleResponse(const RESP& resp) { return Status::Error("No hosts!"); case nebula::cpp2::ErrorCode::E_EXISTED: return Status::Error("Existed!"); + case nebula::cpp2::ErrorCode::E_HISTORY_CONFLICT: + return Status::Error("Schema exisited before!"); case nebula::cpp2::ErrorCode::E_SPACE_NOT_FOUND: return Status::SpaceNotFound("Space not existed!"); case nebula::cpp2::ErrorCode::E_TAG_NOT_FOUND: diff --git a/src/common/datatypes/Value.cpp b/src/common/datatypes/Value.cpp index b0cd600effe..f17d9a4642b 100644 --- a/src/common/datatypes/Value.cpp +++ b/src/common/datatypes/Value.cpp @@ -292,6 +292,14 @@ Value::Value(Duration&& v) { setDU(std::make_unique(std::move(v))); } +Value::Value(const std::unordered_map& map) { + setM(std::make_unique(map)); +} + +Value::Value(std::unordered_map&& map) { + setM(std::make_unique(std::move(map))); +} + const std::string& Value::typeName() const { static const std::unordered_map typeNames = { {Type::__EMPTY__, "__EMPTY__"}, diff --git a/src/common/datatypes/Value.h b/src/common/datatypes/Value.h index 9749b529d3f..7047ccd0f53 100644 --- a/src/common/datatypes/Value.h +++ b/src/common/datatypes/Value.h @@ -91,49 +91,51 @@ struct Value { // matched ctor it will convert to bool type and the match the bool value // ctor, So we disable all pointer ctor except the char* template - Value(T*) = delete; // NOLINT - Value(const std::nullptr_t) = delete; // NOLINT - Value(const NullType& v); // NOLINT - Value(NullType&& v); // NOLINT - Value(const bool& v); // NOLINT - Value(bool&& v); // NOLINT - Value(const int8_t& v); // NOLINT - Value(int8_t&& v); // NOLINT - Value(const int16_t& v); // NOLINT - Value(int16_t&& v); // NOLINT - Value(const int32_t& v); // NOLINT - Value(int32_t&& v); // NOLINT - Value(const int64_t& v); // NOLINT - Value(int64_t&& v); // NOLINT - Value(const double& v); // NOLINT - Value(double&& v); // NOLINT - Value(const std::string& v); // NOLINT - Value(std::string&& v); // NOLINT - Value(const char* v); // NOLINT - Value(const Date& v); // NOLINT - Value(Date&& v); // NOLINT - Value(const Time& v); // NOLINT - Value(Time&& v); // NOLINT - Value(const DateTime& v); // NOLINT - Value(DateTime&& v); // NOLINT - Value(const Vertex& v); // NOLINT - Value(Vertex&& v); // NOLINT - Value(const Edge& v); // NOLINT - Value(Edge&& v); // NOLINT - Value(const Path& v); // NOLINT - Value(Path&& v); // NOLINT - Value(const List& v); // NOLINT - Value(List&& v); // NOLINT - Value(const Map& v); // NOLINT - Value(Map&& v); // NOLINT - Value(const Set& v); // NOLINT - Value(Set&& v); // NOLINT - Value(const DataSet& v); // NOLINT - Value(DataSet&& v); // NOLINT - Value(const Geography& v); // NOLINT - Value(Geography&& v); // NOLINT - Value(const Duration& v); // NOLINT - Value(Duration&& v); // NOLINT + Value(T*) = delete; // NOLINT + Value(const std::nullptr_t) = delete; // NOLINT + Value(const NullType& v); // NOLINT + Value(NullType&& v); // NOLINT + Value(const bool& v); // NOLINT + Value(bool&& v); // NOLINT + Value(const int8_t& v); // NOLINT + Value(int8_t&& v); // NOLINT + Value(const int16_t& v); // NOLINT + Value(int16_t&& v); // NOLINT + Value(const int32_t& v); // NOLINT + Value(int32_t&& v); // NOLINT + Value(const int64_t& v); // NOLINT + Value(int64_t&& v); // NOLINT + Value(const double& v); // NOLINT + Value(double&& v); // NOLINT + Value(const std::string& v); // NOLINT + Value(std::string&& v); // NOLINT + Value(const char* v); // NOLINT + Value(const Date& v); // NOLINT + Value(Date&& v); // NOLINT + Value(const Time& v); // NOLINT + Value(Time&& v); // NOLINT + Value(const DateTime& v); // NOLINT + Value(DateTime&& v); // NOLINT + Value(const Vertex& v); // NOLINT + Value(Vertex&& v); // NOLINT + Value(const Edge& v); // NOLINT + Value(Edge&& v); // NOLINT + Value(const Path& v); // NOLINT + Value(Path&& v); // NOLINT + Value(const List& v); // NOLINT + Value(List&& v); // NOLINT + Value(const Map& v); // NOLINT + Value(Map&& v); // NOLINT + Value(const Set& v); // NOLINT + Value(Set&& v); // NOLINT + Value(const DataSet& v); // NOLINT + Value(DataSet&& v); // NOLINT + Value(const Geography& v); // NOLINT + Value(Geography&& v); // NOLINT + Value(const Duration& v); // NOLINT + Value(Duration&& v); // NOLINT + Value(const std::unordered_map& map); // NOLINT + Value(std::unordered_map&& map); // NOLINT ~Value() { clear(); } diff --git a/src/common/expression/AttributeExpression.cpp b/src/common/expression/AttributeExpression.cpp index 026fce2bb0c..81b58315999 100644 --- a/src/common/expression/AttributeExpression.cpp +++ b/src/common/expression/AttributeExpression.cpp @@ -17,57 +17,30 @@ const Value &AttributeExpression::eval(ExpressionContext &ctx) { auto &lvalue = left()->eval(ctx); auto &rvalue = right()->eval(ctx); DCHECK(rvalue.isStr()); + auto la = [&rvalue](const Tag &t) { return t.name == rvalue.getStr(); }; // TODO(dutor) Take care of the builtin properties, _src, _vid, _type, etc. switch (lvalue.type()) { - case Value::Type::MAP: - return lvalue.getMap().at(rvalue.getStr()); + case Value::Type::MAP: { + auto &m = lvalue.getMap().kvs; + auto iter = m.find(rvalue.getStr()); + if (iter == m.end()) { + return Value::kNullValue; + } + return iter->second; + } case Value::Type::VERTEX: { - /* - * WARNING(Xuntao): Vertices shall not be evaluated as AttributeExpressions, - * since there shall always be a tag specified in the format of: - * var.tag.property. Due to design flaws, however, we have to keep - * this case segment. - */ if (rvalue.getStr() == kVid) { result_ = lvalue.getVertex().vid; return result_; } - /* - * WARNING(Xuntao): the following code snippet is dedicated to address the legacy - * problem of treating LabelTagProperty expressions as Attribute expressions. - * This snippet is necessary to allow users to write var.tag.prop in - * ListComprehensionExpression without making structural changes to the implementation. - */ - if (left()->kind() != Expression::Kind::kAttribute) { - if (right()->kind() == Expression::Kind::kConstant && - rvalue.type() == Value::Type::STRING) { - auto rStr = rvalue.getStr(); - for (auto &tag : lvalue.getVertex().tags) { - if (rStr.compare(tag.name) == 0) { - return lvalue; - } - } - LOG(ERROR) << "Tag not found for: " << rStr - << "Please check whether the related expression " - << "follows the format of vertex.tag.property."; - } - } else if (left()->kind() == Expression::Kind::kAttribute && - dynamic_cast(left())->right()->kind() == - Expression::Kind::kConstant) { - auto &tagName = dynamic_cast(left())->right()->eval(ctx).getStr(); - for (auto &tag : lvalue.getVertex().tags) { - if (tagName.compare(tag.name) != 0) { - continue; - } else { - auto iter = tag.props.find(rvalue.getStr()); - if (iter != tag.props.end()) { - return iter->second; - } - } - } + const auto &tags = lvalue.getVertex().tags; + auto iter = std::find_if(tags.begin(), tags.end(), la); + if (iter == tags.end()) { + return Value::kNullValue; } - return Value::kNullUnknownProp; + result_.setMap(Map(iter->props)); + return result_; } case Value::Type::EDGE: { DCHECK(!rvalue.getStr().empty()); @@ -98,11 +71,14 @@ const Value &AttributeExpression::eval(ExpressionContext &ctx) { case Value::Type::DATETIME: result_ = time::TimeUtils::getDateTimeAttr(lvalue.getDateTime(), rvalue.getStr()); return result_; - default: - if (lvalue.isNull() && lvalue.getNull() == NullType::UNKNOWN_PROP) { - // return UNKNOWN_PROP as plain null values, instead of bad type. - return Value::kNullValue; + case Value::Type::NULLVALUE: { + if (lvalue.isBadNull()) { + return Value::kNullBadType; } + return Value::kNullValue; + } + default: + // Unexpected data types return Value::kNullBadType; } } diff --git a/src/common/expression/MatchPathPatternExpression.h b/src/common/expression/MatchPathPatternExpression.h index 1438ddcc293..9a36458bc9b 100644 --- a/src/common/expression/MatchPathPatternExpression.h +++ b/src/common/expression/MatchPathPatternExpression.h @@ -53,6 +53,10 @@ class MatchPathPatternExpression final : public Expression { return *matchPath_; } + MatchPath* matchPathPtr() const { + return matchPath_.get(); + } + private: friend ObjectPool; explicit MatchPathPatternExpression(ObjectPool* pool, std::unique_ptr&& matchPath) diff --git a/src/common/expression/TextSearchExpression.h b/src/common/expression/TextSearchExpression.h index 078068111ae..b4239d91933 100644 --- a/src/common/expression/TextSearchExpression.h +++ b/src/common/expression/TextSearchExpression.h @@ -85,7 +85,7 @@ class TextSearchArgument final { std::string val_; std::string op_; int32_t fuzziness_{-2}; - int32_t limit_{-1}; + int32_t limit_{10000}; int32_t timeout_{-1}; }; diff --git a/src/common/expression/test/AttributeExpressionTest.cpp b/src/common/expression/test/AttributeExpressionTest.cpp index 615034cfe52..04fa594a150 100644 --- a/src/common/expression/test/AttributeExpressionTest.cpp +++ b/src/common/expression/test/AttributeExpressionTest.cpp @@ -2,10 +2,7 @@ * * This source code is licensed under Apache 2.0 License. */ -#include - #include "common/expression/test/TestBase.h" -#include "graph/util/ExpressionUtils.h" namespace nebula { @@ -96,50 +93,45 @@ TEST_F(AttributeExpressionTest, VertexAttribute) { {"Tug", "War"}, {"Venus", "RocksShow"}, }; - std::unordered_map aliasTypeMap = {{"v", graph::AliasType::kNode}}; - ExpressionContextMock expContext; - expContext.setVar("v", Value(vertex)); - { - auto *left = LabelExpression::make(&pool, "v"); - auto *right = ConstantExpression::make(&pool, "tag0"); - auto *labelAttribute = LabelAttributeExpression::make(&pool, left, right); - auto expr = - AttributeExpression::make(&pool, labelAttribute, ConstantExpression::make(&pool, "Mull")); - auto rewriteExpr = - graph::ExpressionUtils::rewriteAttr2LabelTagProp(expr->clone(), aliasTypeMap); - auto value = Expression::eval(rewriteExpr, expContext); + { + auto expr = AttributeExpression::make( + &pool, + AttributeExpression::make(&pool, + ConstantExpression::make(&pool, Value(vertex)), + ConstantExpression::make(&pool, "tag0")), + ConstantExpression::make(&pool, "Mull")); + + auto value = Expression::eval(expr, gExpCtxt); ASSERT_TRUE(value.isStr()); ASSERT_EQ("Kintyre", value.getStr()); } { - auto *left = LabelExpression::make(&pool, "v"); - auto *right = ConstantExpression::make(&pool, "tag1"); - auto *labelAttribute = LabelAttributeExpression::make(&pool, left, right); - auto expr = - AttributeExpression::make(&pool, labelAttribute, ConstantExpression::make(&pool, "Bip")); - auto rewriteExpr = - graph::ExpressionUtils::rewriteAttr2LabelTagProp(expr->clone(), aliasTypeMap); - auto value = Expression::eval(rewriteExpr, expContext); + auto expr = AttributeExpression::make( + &pool, + AttributeExpression::make(&pool, + ConstantExpression::make(&pool, Value(vertex)), + ConstantExpression::make(&pool, "tag1")), + ConstantExpression::make(&pool, "Bip")); + auto value = Expression::eval(expr, gExpCtxt); ASSERT_TRUE(value.isStr()); ASSERT_EQ("Bop", value.getStr()); } { - auto *left = LabelExpression::make(&pool, "v"); - auto *right = ConstantExpression::make(&pool, "tag0"); - auto *labelAttribute = LabelAttributeExpression::make(&pool, left, right); - auto expr = - AttributeExpression::make(&pool, labelAttribute, ConstantExpression::make(&pool, "Venus")); - auto rewriteExpr = - graph::ExpressionUtils::rewriteAttr2LabelTagProp(expr->clone(), aliasTypeMap); - auto value = Expression::eval(rewriteExpr, expContext); - ASSERT_TRUE(value.isStr()); - ASSERT_EQ("Mars", value.getStr()); + auto expr = AttributeExpression::make( + &pool, + AttributeExpression::make(&pool, + ConstantExpression::make(&pool, Value(vertex)), + ConstantExpression::make(&pool, "tag2")), + ConstantExpression::make(&pool, "Venus")); + + auto value = Expression::eval(expr, gExpCtxt); + ASSERT_TRUE(value.isNull()); } { auto *left = ConstantExpression::make(&pool, Value(vertex)); auto *right = LabelExpression::make(&pool, "_vid"); auto expr = AttributeExpression::make(&pool, left, right); - auto value = Expression::eval(expr, expContext); + auto value = Expression::eval(expr, gExpCtxt); ASSERT_TRUE(value.isStr()); ASSERT_EQ("vid", value.getStr()); } diff --git a/src/common/expression/test/ExpressionContextMock.h b/src/common/expression/test/ExpressionContextMock.h index c280de58ac4..fab7d557414 100644 --- a/src/common/expression/test/ExpressionContextMock.h +++ b/src/common/expression/test/ExpressionContextMock.h @@ -24,16 +24,7 @@ class ExpressionContextMock final : public ExpressionContext { } void setInnerVar(const std::string& var, Value val) override { - if (var == "xxx") { - if (vals_.empty()) { - vals_.emplace_back(val); - indices_[var] = vals_.size() - 1; - } else { - vals_[indices_[var]] = val; - } - } else { - exprValueMap_[var] = std::move(val); - } + exprValueMap_[var] = std::move(val); } const Value& getInnerVar(const std::string& var) const override { @@ -152,7 +143,7 @@ class ExpressionContextMock final : public ExpressionContext { void setVar(const std::string& var, Value val) override { // used by tests of list comprehesion, predicate or reduce - if (var == "n" || var == "p" || var == "totalNum" || var == "v") { + if (var == "n" || var == "p" || var == "totalNum") { vals_.emplace_back(val); indices_[var] = vals_.size() - 1; } diff --git a/src/common/expression/test/ListComprehensionExpressionTest.cpp b/src/common/expression/test/ListComprehensionExpressionTest.cpp index 6f762cc9a29..caffbc7869c 100644 --- a/src/common/expression/test/ListComprehensionExpressionTest.cpp +++ b/src/common/expression/test/ListComprehensionExpressionTest.cpp @@ -37,7 +37,7 @@ TEST_F(ListComprehensionExpressionTest, ListComprehensionEvaluate) { ASSERT_EQ(expected, value.getList()); } { - // [n IN nodes(p) | n.player.age + 5] + // [n IN nodes(p) | n.age + 5] auto v1 = Vertex("101", {Tag("player", {{"name", "joe"}, {"age", 18}})}); auto v2 = Vertex("102", {Tag("player", {{"name", "amber"}, {"age", 19}})}); auto v3 = Vertex("103", {Tag("player", {{"name", "shawdan"}, {"age", 20}})}); @@ -46,25 +46,22 @@ TEST_F(ListComprehensionExpressionTest, ListComprehensionEvaluate) { path.steps.emplace_back(Step(v2, 1, "like", 0, {})); path.steps.emplace_back(Step(v3, 1, "like", 0, {})); gExpCtxt.setVar("p", path); - std::unordered_map aliasTypeMap = { - {"xxx", graph::AliasType::kNode}}; + ArgumentList *argList = ArgumentList::make(&pool); argList->addArgument(VariableExpression::make(&pool, "p")); auto expr = ListComprehensionExpression::make( &pool, - "xxx", + "n", FunctionCallExpression::make(&pool, "nodes", argList), nullptr, ArithmeticExpression::makeAdd( &pool, - graph::ExpressionUtils::rewriteAttr2LabelTagProp( - AttributeExpression::make( - &pool, - LabelAttributeExpression::make(&pool, - LabelExpression::make(&pool, "xxx"), - ConstantExpression::make(&pool, "player")), - ConstantExpression::make(&pool, "age")), - aliasTypeMap), + AttributeExpression::make( + &pool, + AttributeExpression::make(&pool, + VariableExpression::makeInner(&pool, "n"), + ConstantExpression::make(&pool, "player")), + ConstantExpression::make(&pool, "age")), ConstantExpression::make(&pool, 5))); auto value = Expression::eval(expr, gExpCtxt); @@ -94,8 +91,6 @@ TEST_F(ListComprehensionExpressionTest, ListComprehensionExprToString) { { ArgumentList *argList = ArgumentList::make(&pool); argList->addArgument(LabelExpression::make(&pool, "p")); - std::unordered_map aliasTypeMap = { - {"n", graph::AliasType::kNode}}; auto expr = ListComprehensionExpression::make( &pool, "n", @@ -103,16 +98,10 @@ TEST_F(ListComprehensionExpressionTest, ListComprehensionExprToString) { nullptr, ArithmeticExpression::makeAdd( &pool, - graph::ExpressionUtils::rewriteAttr2LabelTagProp( - AttributeExpression::make( - &pool, - LabelAttributeExpression::make(&pool, - LabelExpression::make(&pool, "n"), - ConstantExpression::make(&pool, "player")), - ConstantExpression::make(&pool, "age")), - aliasTypeMap), + LabelAttributeExpression::make( + &pool, LabelExpression::make(&pool, "n"), ConstantExpression::make(&pool, "age")), ConstantExpression::make(&pool, 10))); - ASSERT_EQ("[n IN nodes(p) | (n.player.age+10)]", expr->toString()); + ASSERT_EQ("[n IN nodes(p) | (n.age+10)]", expr->toString()); } { auto listItems = ExpressionList::make(&pool); diff --git a/src/common/expression/test/PredicateExpressionTest.cpp b/src/common/expression/test/PredicateExpressionTest.cpp index 919533776c6..571281989de 100644 --- a/src/common/expression/test/PredicateExpressionTest.cpp +++ b/src/common/expression/test/PredicateExpressionTest.cpp @@ -31,7 +31,7 @@ TEST_F(PredicateExpressionTest, PredicateEvaluate) { ASSERT_EQ(false, value.getBool()); } { - // any(xxx IN nodes(p) WHERE xxx.player.age >= 19) + // any(n IN nodes(p) WHERE n.age >= 19) auto v1 = Vertex("101", {Tag("player", {{"name", "joe"}, {"age", 18}})}); auto v2 = Vertex("102", {Tag("player", {{"name", "amber"}, {"age", 19}})}); auto v3 = Vertex("103", {Tag("player", {{"name", "shawdan"}, {"age", 20}})}); @@ -40,25 +40,22 @@ TEST_F(PredicateExpressionTest, PredicateEvaluate) { path.steps.emplace_back(Step(v2, 1, "like", 0, {})); path.steps.emplace_back(Step(v3, 1, "like", 0, {})); gExpCtxt.setVar("p", path); - std::unordered_map aliasTypeMap = { - {"xxx", graph::AliasType::kNode}}; + ArgumentList *argList = ArgumentList::make(&pool); argList->addArgument(VariableExpression::make(&pool, "p")); auto expr = PredicateExpression::make( &pool, "any", - "xxx", + "n", FunctionCallExpression::make(&pool, "nodes", argList), RelationalExpression::makeGE( &pool, - graph::ExpressionUtils::rewriteAttr2LabelTagProp( - AttributeExpression::make( - &pool, - LabelAttributeExpression::make(&pool, - LabelExpression::make(&pool, "xxx"), - ConstantExpression::make(&pool, "player")), - ConstantExpression::make(&pool, "age")), - aliasTypeMap), + AttributeExpression::make( + &pool, + AttributeExpression::make(&pool, + VariableExpression::makeInner(&pool, "n"), + ConstantExpression::make(&pool, "player")), + ConstantExpression::make(&pool, "age")), ConstantExpression::make(&pool, 19))); auto value = Expression::eval(expr, gExpCtxt); @@ -96,25 +93,22 @@ TEST_F(PredicateExpressionTest, PredicateEvaluate) { path.steps.emplace_back(Step(v2, 1, "like", 0, {})); path.steps.emplace_back(Step(v3, 1, "like", 0, {})); gExpCtxt.setVar("p", path); - std::unordered_map aliasTypeMap = { - {"xxx", graph::AliasType::kNode}}; + ArgumentList *argList = ArgumentList::make(&pool); argList->addArgument(VariableExpression::make(&pool, "p")); auto expr = PredicateExpression::make( &pool, "none", - "xxx", + "n", FunctionCallExpression::make(&pool, "nodes", argList), RelationalExpression::makeGE( &pool, - graph::ExpressionUtils::rewriteAttr2LabelTagProp( - AttributeExpression::make( - &pool, - LabelAttributeExpression::make(&pool, - LabelExpression::make(&pool, "xxx"), - ConstantExpression::make(&pool, "player")), - ConstantExpression::make(&pool, "age")), - aliasTypeMap), + AttributeExpression::make( + &pool, + AttributeExpression::make(&pool, + VariableExpression::makeInner(&pool, "n"), + ConstantExpression::make(&pool, "player")), + ConstantExpression::make(&pool, "age")), ConstantExpression::make(&pool, 19))); auto value = Expression::eval(expr, gExpCtxt); diff --git a/src/common/graph/Response.h b/src/common/graph/Response.h index 7989548d565..8ce83a78bf6 100644 --- a/src/common/graph/Response.h +++ b/src/common/graph/Response.h @@ -77,6 +77,7 @@ X(E_WRONGCLUSTER, -2010) \ X(E_ZONE_NOT_ENOUGH, -2011) \ X(E_ZONE_IS_EMPTY, -2012) \ + X(E_HISTORY_CONFLICT, -2018) \ \ X(E_STORE_FAILURE, -2021) \ X(E_STORE_SEGMENT_ILLEGAL, -2022) \ diff --git a/src/common/memory/MemoryTracker.cpp b/src/common/memory/MemoryTracker.cpp index 548e4aacb38..a22a0aabf0d 100644 --- a/src/common/memory/MemoryTracker.cpp +++ b/src/common/memory/MemoryTracker.cpp @@ -15,7 +15,6 @@ ThreadMemoryStats::~ThreadMemoryStats() { // Return to global any reserved bytes on destruction if (reserved != 0) { MemoryStats::instance().freeGlobal(reserved); - DLOG(INFO) << std::this_thread::get_id() << " return reserved " << reserved; } } diff --git a/src/common/memory/MemoryUtils.cpp b/src/common/memory/MemoryUtils.cpp index 1d7170baffb..f8950fd3d21 100644 --- a/src/common/memory/MemoryUtils.cpp +++ b/src/common/memory/MemoryUtils.cpp @@ -44,7 +44,7 @@ DEFINE_string(cgroup_v2_memory_current_path, DEFINE_bool(memory_purge_enabled, true, "memory purge enabled, default true"); DEFINE_int32(memory_purge_interval_seconds, 10, "memory purge interval in seconds, default 10"); -DEFINE_bool(memory_tracker_detail_log, true, "print memory stats detail log"); +DEFINE_bool(memory_tracker_detail_log, false, "print memory stats detail log"); DEFINE_int32(memory_tracker_detail_log_interval_ms, 60000, "print memory stats detail log interval in ms"); diff --git a/src/graph/context/ast/CypherAstContext.h b/src/graph/context/ast/CypherAstContext.h index e0fb879b2c9..dfba403e101 100644 --- a/src/graph/context/ast/CypherAstContext.h +++ b/src/graph/context/ast/CypherAstContext.h @@ -46,7 +46,7 @@ struct NodeInfo { struct EdgeInfo { bool anonymous{false}; - MatchStepRange* range{nullptr}; + std::unique_ptr range{nullptr}; std::vector edgeTypes; MatchEdge::Direction direction{MatchEdge::Direction::OUT_EDGE}; std::vector types; diff --git a/src/graph/executor/algo/BFSShortestPathExecutor.cpp b/src/graph/executor/algo/BFSShortestPathExecutor.cpp index cf9faafa0d9..098ad840df5 100644 --- a/src/graph/executor/algo/BFSShortestPathExecutor.cpp +++ b/src/graph/executor/algo/BFSShortestPathExecutor.cpp @@ -167,15 +167,13 @@ folly::Future BFSShortestPathExecutor::conjunctPath() { } } - return folly::collect(futures) - .via(runner()) - .thenValue([this](auto&& resps) { - memory::MemoryCheckGuard guard; - for (auto& resp : resps) { - currentDs_.append(std::move(resp)); - } - return Status::OK(); - }); + return folly::collect(futures).via(runner()).thenValue([this](auto&& resps) { + memory::MemoryCheckGuard guard; + for (auto& resp : resps) { + currentDs_.append(std::move(resp)); + } + return Status::OK(); + }); } DataSet BFSShortestPathExecutor::doConjunct(const std::vector& meetVids, diff --git a/src/graph/executor/algo/ProduceAllPathsExecutor.cpp b/src/graph/executor/algo/ProduceAllPathsExecutor.cpp index f2b709a000f..0011caa5638 100644 --- a/src/graph/executor/algo/ProduceAllPathsExecutor.cpp +++ b/src/graph/executor/algo/ProduceAllPathsExecutor.cpp @@ -194,19 +194,17 @@ folly::Future ProduceAllPathsExecutor::conjunctPath() { } } - return folly::collect(futures) - .via(runner()) - .thenValue([this](auto&& resps) { - memory::MemoryCheckGuard guard; - for (auto& resp : resps) { - currentDs_.append(std::move(resp)); - } - preLeftPaths_.swap(leftPaths_); - preRightPaths_.swap(rightPaths_); - leftPaths_.clear(); - rightPaths_.clear(); - return Status::OK(); - }); + return folly::collect(futures).via(runner()).thenValue([this](auto&& resps) { + memory::MemoryCheckGuard guard; + for (auto& resp : resps) { + currentDs_.append(std::move(resp)); + } + preLeftPaths_.swap(leftPaths_); + preRightPaths_.swap(rightPaths_); + leftPaths_.clear(); + rightPaths_.clear(); + return Status::OK(); + }); } void ProduceAllPathsExecutor::setNextStepVid(Interims& paths, const string& var) { diff --git a/src/graph/executor/algo/ShortestPathBase.h b/src/graph/executor/algo/ShortestPathBase.h index 2a049246a74..d107f7fbbcd 100644 --- a/src/graph/executor/algo/ShortestPathBase.h +++ b/src/graph/executor/algo/ShortestPathBase.h @@ -23,7 +23,7 @@ class ShortestPathBase { std::unordered_map* stats) : pathNode_(node), qctx_(qctx), stats_(stats) { singleShortest_ = node->singleShortest(); - maxStep_ = node->stepRange()->max(); + maxStep_ = node->stepRange().max(); } virtual ~ShortestPathBase() {} diff --git a/src/graph/executor/maintain/EdgeExecutor.cpp b/src/graph/executor/maintain/EdgeExecutor.cpp index 1c8a3151f44..d790e085f1d 100644 --- a/src/graph/executor/maintain/EdgeExecutor.cpp +++ b/src/graph/executor/maintain/EdgeExecutor.cpp @@ -86,11 +86,8 @@ folly::Future ShowEdgesExecutor::execute() { SCOPED_TIMER(&execTime_); auto spaceId = qctx()->rctx()->session()->space().id; - return qctx() - ->getMetaClient() - ->listEdgeSchemas(spaceId) - .via(runner()) - .thenValue([this, spaceId](StatusOr> resp) { + return qctx()->getMetaClient()->listEdgeSchemas(spaceId).via(runner()).thenValue( + [this, spaceId](StatusOr> resp) { memory::MemoryCheckGuard guard; if (!resp.ok()) { LOG(WARNING) << "SpaceId: " << spaceId << ", Show edges failed: " << resp.status(); diff --git a/src/graph/executor/maintain/EdgeIndexExecutor.cpp b/src/graph/executor/maintain/EdgeIndexExecutor.cpp index 00f1f2e9d20..8fc1da1114a 100644 --- a/src/graph/executor/maintain/EdgeIndexExecutor.cpp +++ b/src/graph/executor/maintain/EdgeIndexExecutor.cpp @@ -118,11 +118,8 @@ folly::Future ShowEdgeIndexesExecutor::execute() { auto *iNode = asNode(node()); const auto &bySchema = iNode->name(); auto spaceId = qctx()->rctx()->session()->space().id; - return qctx() - ->getMetaClient() - ->listEdgeIndexes(spaceId) - .via(runner()) - .thenValue([this, spaceId, bySchema](StatusOr> resp) { + return qctx()->getMetaClient()->listEdgeIndexes(spaceId).via(runner()).thenValue( + [this, spaceId, bySchema](StatusOr> resp) { memory::MemoryCheckGuard guard; if (!resp.ok()) { LOG(WARNING) << "SpaceId: " << spaceId << ", Show edge indexes failed" << resp.status(); @@ -174,11 +171,8 @@ folly::Future ShowEdgeIndexStatusExecutor::execute() { SCOPED_TIMER(&execTime_); auto spaceId = qctx()->rctx()->session()->space().id; - return qctx() - ->getMetaClient() - ->listEdgeIndexStatus(spaceId) - .via(runner()) - .thenValue([this, spaceId](StatusOr> resp) { + return qctx()->getMetaClient()->listEdgeIndexStatus(spaceId).via(runner()).thenValue( + [this, spaceId](StatusOr> resp) { memory::MemoryCheckGuard guard; if (!resp.ok()) { LOG(WARNING) << "SpaceId: " << spaceId << ", Show edge index status failed" diff --git a/src/graph/executor/query/TraverseExecutor.cpp b/src/graph/executor/query/TraverseExecutor.cpp index f4b85fd6a8b..850820bf1fa 100644 --- a/src/graph/executor/query/TraverseExecutor.cpp +++ b/src/graph/executor/query/TraverseExecutor.cpp @@ -112,7 +112,7 @@ folly::Future TraverseExecutor::getNeighbors() { Expression* TraverseExecutor::selectFilter() { Expression* filter = nullptr; - if (!(currentStep_ == 1 && traverse_->zeroStep())) { + if (!(currentStep_ == 1 && range_.min() == 0)) { filter = const_cast(traverse_->filter()); } if (currentStep_ == 1) { @@ -166,18 +166,14 @@ folly::Future TraverseExecutor::handleResponse(RpcResponse&& resps) { initVertices_.emplace_back(vertex); } } - if (range_ && range_->min() == 0) { + if (range_.min() == 0) { result_.rows = buildZeroStepPath(); } } expand(iter.get()); if (!isFinalStep()) { if (vids_.empty()) { - if (range_ != nullptr) { - return buildResult(); - } else { - return folly::makeFuture(Status::OK()); - } + return buildResult(); } else { return getNeighbors(); } @@ -267,12 +263,8 @@ std::vector TraverseExecutor::buildZeroStepPath() { } folly::Future TraverseExecutor::buildResult() { - size_t minStep = 1; - size_t maxStep = 1; - if (range_ != nullptr) { - minStep = range_->min(); - maxStep = range_->max(); - } + size_t minStep = range_.min(); + size_t maxStep = range_.max(); result_.colNames = traverse_->colNames(); if (maxStep == 0) { diff --git a/src/graph/executor/query/TraverseExecutor.h b/src/graph/executor/query/TraverseExecutor.h index ed4ea9bf800..ecaa7e67fa2 100644 --- a/src/graph/executor/query/TraverseExecutor.h +++ b/src/graph/executor/query/TraverseExecutor.h @@ -67,8 +67,7 @@ class TraverseExecutor final : public StorageAccessExecutor { folly::Future buildPathMultiJobs(size_t minStep, size_t maxStep); bool isFinalStep() const { - return (range_ == nullptr && currentStep_ == 1) || - (range_ != nullptr && (currentStep_ == range_->max() || range_->max() == 0)); + return currentStep_ == range_.max() || range_.max() == 0; } bool filterSameEdge(const Row& lhs, @@ -133,7 +132,7 @@ class TraverseExecutor final : public StorageAccessExecutor { std::unordered_map, VertexHash, VertexEqual> adjList_; std::unordered_map, VertexHash, VertexEqual> dst2PathsMap_; const Traverse* traverse_{nullptr}; - MatchStepRange* range_{nullptr}; + MatchStepRange range_; size_t currentStep_{0}; }; diff --git a/src/graph/optimizer/rule/GetEdgesTransformAppendVerticesLimitRule.cpp b/src/graph/optimizer/rule/GetEdgesTransformAppendVerticesLimitRule.cpp index cb7db5d8175..c6971129237 100644 --- a/src/graph/optimizer/rule/GetEdgesTransformAppendVerticesLimitRule.cpp +++ b/src/graph/optimizer/rule/GetEdgesTransformAppendVerticesLimitRule.cpp @@ -58,7 +58,8 @@ bool GetEdgesTransformAppendVerticesLimitRule::match(OptContext *ctx, if (colNames[colSize - 2][0] != '_') { // src return false; } - if (traverse->stepRange() != nullptr) { + const auto &stepRange = traverse->stepRange(); + if (stepRange.min() != 1 || stepRange.max() != 1) { return false; } // Can't apply vertex filter in GetEdges @@ -120,7 +121,8 @@ StatusOr GetEdgesTransformAppendVerticesLimitRule::tra newLimitGroupNode->dependsOn(newAppendVerticesGroup); - auto *newScanEdges = GetEdgesTransformUtils::traverseToScanEdges(traverse, limit->count(qctx)); + auto *newScanEdges = + GetEdgesTransformUtils::traverseToScanEdges(traverse, limit->offset() + limit->count(qctx)); if (newScanEdges == nullptr) { return TransformResult::noTransform(); } diff --git a/src/graph/optimizer/rule/GetEdgesTransformRule.cpp b/src/graph/optimizer/rule/GetEdgesTransformRule.cpp index 17764389c2b..e72e4c213eb 100644 --- a/src/graph/optimizer/rule/GetEdgesTransformRule.cpp +++ b/src/graph/optimizer/rule/GetEdgesTransformRule.cpp @@ -54,7 +54,8 @@ bool GetEdgesTransformRule::match(OptContext *ctx, const MatchedResult &matched) if (colNames[colSize - 2][0] != '_') { // src return false; } - if (traverse->stepRange() != nullptr) { + const auto &stepRange = traverse->stepRange(); + if (stepRange.min() != 1 || stepRange.max() != 1) { return false; } // Can't apply vertex filter in GetEdges @@ -99,7 +100,8 @@ StatusOr GetEdgesTransformRule::transform( newProjectGroupNode->dependsOn(newLimitGroup); newProject->setInputVar(newLimit->outputVar()); - auto *newScanEdges = GetEdgesTransformUtils::traverseToScanEdges(traverse, limit->count(qctx)); + auto *newScanEdges = + GetEdgesTransformUtils::traverseToScanEdges(traverse, limit->offset() + limit->count(qctx)); if (newScanEdges == nullptr) { return TransformResult::noTransform(); } diff --git a/src/graph/planner/match/MatchPathPlanner.cpp b/src/graph/planner/match/MatchPathPlanner.cpp index 129334967d9..741c18f785b 100644 --- a/src/graph/planner/match/MatchPathPlanner.cpp +++ b/src/graph/planner/match/MatchPathPlanner.cpp @@ -202,6 +202,10 @@ Status MatchPathPlanner::leftExpandFromNode(size_t startIndex, SubPlan& subplan) addNodeAlias(node); bool expandInto = isExpandInto(dst.alias); auto& edge = edgeInfos[i - 1]; + MatchStepRange stepRange(1, 1); + if (edge.range != nullptr) { + stepRange = *edge.range; + } auto traverse = Traverse::make(qctx, subplan.root, spaceId); traverse->setSrc(nextTraverseStart); auto vertexProps = SchemaUtil::getAllVertexProp(qctx, spaceId, true); @@ -212,7 +216,7 @@ Status MatchPathPlanner::leftExpandFromNode(size_t startIndex, SubPlan& subplan) traverse->setTagFilter(genVertexFilter(node)); traverse->setEdgeFilter(genEdgeFilter(edge)); traverse->setEdgeDirection(edge.direction); - traverse->setStepRange(edge.range); + traverse->setStepRange(stepRange); traverse->setDedup(); // If start from end of the path pattern, the first traverse would not // track the previous path, otherwise, it should. @@ -269,6 +273,10 @@ Status MatchPathPlanner::rightExpandFromNode(size_t startIndex, SubPlan& subplan bool expandInto = isExpandInto(dst.alias); auto& edge = edgeInfos[i]; + MatchStepRange stepRange(1, 1); + if (edge.range != nullptr) { + stepRange = *edge.range; + } auto traverse = Traverse::make(qctx, subplan.root, spaceId); traverse->setSrc(nextTraverseStart); auto vertexProps = SchemaUtil::getAllVertexProp(qctx, spaceId, true); @@ -279,7 +287,7 @@ Status MatchPathPlanner::rightExpandFromNode(size_t startIndex, SubPlan& subplan traverse->setTagFilter(genVertexFilter(node)); traverse->setEdgeFilter(genEdgeFilter(edge)); traverse->setEdgeDirection(edge.direction); - traverse->setStepRange(edge.range); + traverse->setStepRange(stepRange); traverse->setDedup(); traverse->setTrackPrevPath(i != startIndex); traverse->setColNames( diff --git a/src/graph/planner/match/ShortestPathPlanner.cpp b/src/graph/planner/match/ShortestPathPlanner.cpp index 41bfe4a9db5..0250e79c281 100644 --- a/src/graph/planner/match/ShortestPathPlanner.cpp +++ b/src/graph/planner/match/ShortestPathPlanner.cpp @@ -97,6 +97,11 @@ StatusOr ShortestPathPlanner::transform(WhereClauseContext* bindWhereCl auto cp = CrossJoin::make(qctx, leftPlan.root, rightPlan.root); + MatchStepRange stepRange(1, 1); + if (edge.range != nullptr) { + stepRange = *edge.range; + } + auto shortestPath = ShortestPath::make(qctx, cp, spaceId, singleShortest); auto vertexProp = SchemaUtil::getAllVertexProp(qctx, spaceId, true); NG_RETURN_IF_ERROR(vertexProp); @@ -104,7 +109,7 @@ StatusOr ShortestPathPlanner::transform(WhereClauseContext* bindWhereCl shortestPath->setEdgeProps(SchemaUtil::getEdgeProps(edge, false, qctx, spaceId)); shortestPath->setReverseEdgeProps(SchemaUtil::getEdgeProps(edge, true, qctx, spaceId)); shortestPath->setEdgeDirection(edge.direction); - shortestPath->setStepRange(edge.range); + shortestPath->setStepRange(stepRange); shortestPath->setColNames(std::move(colNames)); subplan.root = shortestPath; diff --git a/src/graph/planner/plan/Algo.cpp b/src/graph/planner/plan/Algo.cpp index 8416bddd65d..f2ef34a7180 100644 --- a/src/graph/planner/plan/Algo.cpp +++ b/src/graph/planner/plan/Algo.cpp @@ -39,7 +39,7 @@ std::unique_ptr ProduceAllPaths::explain() const { std::unique_ptr ShortestPath::explain() const { auto desc = SingleInputNode::explain(); addDescription("singleShortest", folly::toJson(util::toJson(singleShortest_)), desc.get()); - addDescription("steps", range_ != nullptr ? range_->toString() : "", desc.get()); + addDescription("steps", range_.toString(), desc.get()); addDescription("edgeDirection", apache::thrift::util::enumNameSafe(edgeDirection_), desc.get()); addDescription( "vertexProps", vertexProps_ ? folly::toJson(util::toJson(*vertexProps_)) : "", desc.get()); diff --git a/src/graph/planner/plan/Algo.h b/src/graph/planner/plan/Algo.h index ff50714817e..a58ae6f555a 100644 --- a/src/graph/planner/plan/Algo.h +++ b/src/graph/planner/plan/Algo.h @@ -174,7 +174,7 @@ class ShortestPath final : public SingleInputNode { std::unique_ptr explain() const override; - MatchStepRange* stepRange() const { + MatchStepRange stepRange() const { return range_; } @@ -202,7 +202,7 @@ class ShortestPath final : public SingleInputNode { return singleShortest_; } - void setStepRange(MatchStepRange* range) { + void setStepRange(const MatchStepRange& range) { range_ = range; } @@ -234,7 +234,7 @@ class ShortestPath final : public SingleInputNode { private: GraphSpaceID space_; bool singleShortest_{false}; - MatchStepRange* range_{nullptr}; + MatchStepRange range_; std::unique_ptr> edgeProps_; std::unique_ptr> reverseEdgeProps_; std::unique_ptr> vertexProps_; diff --git a/src/graph/planner/plan/Query.cpp b/src/graph/planner/plan/Query.cpp index ad855bb112d..98383f09b37 100644 --- a/src/graph/planner/plan/Query.cpp +++ b/src/graph/planner/plan/Query.cpp @@ -790,7 +790,7 @@ void Traverse::cloneMembers(const Traverse& g) { std::unique_ptr Traverse::explain() const { auto desc = GetNeighbors::explain(); - addDescription("steps", range_ != nullptr ? range_->toString() : "", desc.get()); + addDescription("steps", range_.toString(), desc.get()); addDescription("vertex filter", vFilter_ != nullptr ? vFilter_->toString() : "", desc.get()); addDescription("edge filter", eFilter_ != nullptr ? eFilter_->toString() : "", desc.get()); addDescription("if_track_previous_path", folly::toJson(util::toJson(trackPrevPath_)), desc.get()); diff --git a/src/graph/planner/plan/Query.h b/src/graph/planner/plan/Query.h index ded9e30e2c4..f485795f0c5 100644 --- a/src/graph/planner/plan/Query.h +++ b/src/graph/planner/plan/Query.h @@ -1581,17 +1581,17 @@ class Traverse final : public GetNeighbors { Traverse* clone() const override; - MatchStepRange* stepRange() const { + MatchStepRange stepRange() const { return range_; } bool isOneStep() const { - return !range_; + return range_.min() == 1 && range_.max() == 1; } // Contains zero step bool zeroStep() const { - return range_ != nullptr && range_->min() == 0; + return range_.min() == 0; } Expression* vFilter() const { @@ -1617,7 +1617,7 @@ class Traverse final : public GetNeighbors { return this->colNames().back(); } - void setStepRange(MatchStepRange* range) { + void setStepRange(const MatchStepRange& range) { range_ = range; } @@ -1659,7 +1659,7 @@ class Traverse final : public GetNeighbors { private: void cloneMembers(const Traverse& g); - MatchStepRange* range_{nullptr}; + MatchStepRange range_; Expression* vFilter_{nullptr}; Expression* eFilter_{nullptr}; bool trackPrevPath_{true}; diff --git a/src/graph/service/GraphFlags.cpp b/src/graph/service/GraphFlags.cpp index 7ffce40a526..5b754b1bd89 100644 --- a/src/graph/service/GraphFlags.cpp +++ b/src/graph/service/GraphFlags.cpp @@ -12,7 +12,7 @@ DEFINE_int32(client_idle_timeout_secs, 28800, "The number of seconds NebulaGraph service waits before closing the idle connections"); DEFINE_int32(session_idle_timeout_secs, 28800, "The number of seconds before idle sessions expire"); -DEFINE_int32(session_reclaim_interval_secs, 10, "Period we try to reclaim expired sessions"); +DEFINE_int32(session_reclaim_interval_secs, 60, "Period we try to reclaim expired sessions"); DEFINE_int32(num_netio_threads, 0, "The number of networking threads, 0 for number of physical CPU cores"); diff --git a/src/graph/session/GraphSessionManager.cpp b/src/graph/session/GraphSessionManager.cpp index fce4206b088..e11fbc57d03 100644 --- a/src/graph/session/GraphSessionManager.cpp +++ b/src/graph/session/GraphSessionManager.cpp @@ -5,6 +5,7 @@ #include "graph/session/GraphSessionManager.h" #include "common/base/Base.h" +#include "common/base/Status.h" #include "common/stats/StatsManager.h" #include "common/time/WallClock.h" #include "graph/service/GraphFlags.h" @@ -258,8 +259,9 @@ void GraphSessionManager::updateSessionsToMeta() { auto handleKilledQueries = [this](auto&& resp) { if (!resp.ok()) { LOG(ERROR) << "Update sessions failed: " << resp.status(); - return Status::Error("Update sessions failed: %s", resp.status().toString().c_str()); + return; } + auto& killedQueriesForEachSession = *resp.value().killed_queries_ref(); for (auto& killedQueries : killedQueriesForEachSession) { auto sessionId = killedQueries.first; @@ -276,19 +278,24 @@ void GraphSessionManager::updateSessionsToMeta() { VLOG(1) << "Kill query, session: " << sessionId << " plan: " << epId; } } - return Status::OK(); }; // The response from meta contains sessions that are marked as killed, so we need to clean the // local cache and update statistics auto handleKilledSessions = [this](auto&& resp) { + if (!resp.ok()) { + LOG(ERROR) << "Update sessions failed: " << resp.status(); + return; + } + auto killSessions = resp.value().get_killed_sessions(); removeSessionFromLocalCache(killSessions); }; auto result = metaClient_->updateSessions(sessions).get(); if (!result.ok()) { - LOG(ERROR) << "Update sessions failed: " << result; + LOG(ERROR) << "Update sessions failed: " << result.status(); + return; } handleKilledQueries(result); handleKilledSessions(result); diff --git a/src/graph/util/test/FTindexUtilsTest.cpp b/src/graph/util/test/FTindexUtilsTest.cpp index f131d054993..41dff6a0dd4 100644 --- a/src/graph/util/test/FTindexUtilsTest.cpp +++ b/src/graph/util/test/FTindexUtilsTest.cpp @@ -70,7 +70,7 @@ TEST_F(FTIndexUtilsTest, rewriteTSFilter) { esResult.items = items; { MockESAdapter mockESAdapter; - EXPECT_CALL(mockESAdapter, prefix(indexName, "prefix_pattern", -1, -1)) + EXPECT_CALL(mockESAdapter, prefix(indexName, "prefix_pattern", 10000, -1)) .WillOnce(Return(esResult)); auto argument = TextSearchArgument::make(&pool, tagName, propName, "prefix_pattern"); auto expr = TextSearchExpression::makePrefix(&pool, argument); @@ -84,7 +84,7 @@ TEST_F(FTIndexUtilsTest, rewriteTSFilter) { { plugin::ESQueryResult emptyEsResult; MockESAdapter mockESAdapter; - EXPECT_CALL(mockESAdapter, prefix(indexName, "prefix_pattern", -1, -1)) + EXPECT_CALL(mockESAdapter, prefix(indexName, "prefix_pattern", 10000, -1)) .WillOnce(Return(emptyEsResult)); auto argument = TextSearchArgument::make(&pool, tagName, propName, "prefix_pattern"); auto expr = TextSearchExpression::makePrefix(&pool, argument); @@ -95,7 +95,7 @@ TEST_F(FTIndexUtilsTest, rewriteTSFilter) { { Status status = Status::Error("mock error"); MockESAdapter mockESAdapter; - EXPECT_CALL(mockESAdapter, prefix(indexName, "prefix_pattern", -1, -1)) + EXPECT_CALL(mockESAdapter, prefix(indexName, "prefix_pattern", 10000, -1)) .Times(FLAGS_ft_request_retry_times) .WillRepeatedly(Return(status)); auto argument = TextSearchArgument::make(&pool, tagName, propName, "prefix_pattern"); @@ -106,7 +106,7 @@ TEST_F(FTIndexUtilsTest, rewriteTSFilter) { } { MockESAdapter mockESAdapter; - EXPECT_CALL(mockESAdapter, wildcard(indexName, "wildcard_pattern", -1, -1)) + EXPECT_CALL(mockESAdapter, wildcard(indexName, "wildcard_pattern", 10000, -1)) .WillOnce(Return(esResult)); auto argument = TextSearchArgument::make(&pool, edgeName, propName, "wildcard_pattern"); auto expr = TextSearchExpression::makeWildcard(&pool, argument); @@ -121,7 +121,7 @@ TEST_F(FTIndexUtilsTest, rewriteTSFilter) { plugin::ESQueryResult singleEsResult; singleEsResult.items = {Item("a", "b", 1, "edge text")}; MockESAdapter mockESAdapter; - EXPECT_CALL(mockESAdapter, wildcard(indexName, "wildcard_pattern", -1, -1)) + EXPECT_CALL(mockESAdapter, wildcard(indexName, "wildcard_pattern", 10000, -1)) .WillOnce(Return(singleEsResult)); auto argument = TextSearchArgument::make(&pool, edgeName, propName, "wildcard_pattern"); auto expr = TextSearchExpression::makeWildcard(&pool, argument); @@ -132,7 +132,7 @@ TEST_F(FTIndexUtilsTest, rewriteTSFilter) { } { MockESAdapter mockESAdapter; - EXPECT_CALL(mockESAdapter, regexp(indexName, "regexp_pattern", -1, -1)) + EXPECT_CALL(mockESAdapter, regexp(indexName, "regexp_pattern", 10000, -1)) .WillOnce(Return(esResult)); auto argument = TextSearchArgument::make(&pool, edgeName, propName, "regexp_pattern"); auto expr = TextSearchExpression::makeRegexp(&pool, argument); @@ -145,7 +145,7 @@ TEST_F(FTIndexUtilsTest, rewriteTSFilter) { } { MockESAdapter mockESAdapter; - EXPECT_CALL(mockESAdapter, fuzzy(indexName, "fuzzy_pattern", "1", -1, -1)) + EXPECT_CALL(mockESAdapter, fuzzy(indexName, "fuzzy_pattern", "1", 10000, -1)) .WillOnce(Return(esResult)); auto argument = TextSearchArgument::make(&pool, tagName, propName, "fuzzy_pattern"); argument->setFuzziness(1); diff --git a/src/graph/validator/GoValidator.cpp b/src/graph/validator/GoValidator.cpp index cb2ebf62cd1..065db1ab2f8 100644 --- a/src/graph/validator/GoValidator.cpp +++ b/src/graph/validator/GoValidator.cpp @@ -188,8 +188,8 @@ Status GoValidator::extractTagIds() { return Status::OK(); } -void GoValidator::extractPropExprs(const Expression* expr, - std::unordered_set& uniqueExpr) { +Status GoValidator::extractPropExprs(const Expression* expr, + std::unordered_set& uniqueExpr) { ExtractPropExprVisitor visitor(vctx_, goCtx_->srcEdgePropsExpr, goCtx_->dstPropsExpr, @@ -197,6 +197,7 @@ void GoValidator::extractPropExprs(const Expression* expr, propExprColMap_, uniqueExpr); const_cast(expr)->accept(&visitor); + return std::move(visitor).status(); } Expression* GoValidator::rewriteVertexEdge2EdgeProp(const Expression* expr) { @@ -277,13 +278,13 @@ Status GoValidator::buildColumns() { std::unordered_set uniqueEdgeVertexExpr; auto filter = goCtx_->filter; if (filter != nullptr) { - extractPropExprs(filter, uniqueEdgeVertexExpr); + NG_RETURN_IF_ERROR(extractPropExprs(filter, uniqueEdgeVertexExpr)); goCtx_->filter = rewrite2VarProp(filter); } auto* newYieldExpr = pool->makeAndAdd(); for (auto* col : goCtx_->yieldExpr->columns()) { - extractPropExprs(col->expr(), uniqueEdgeVertexExpr); + NG_RETURN_IF_ERROR(extractPropExprs(col->expr(), uniqueEdgeVertexExpr)); newYieldExpr->addColumn(new YieldColumn(rewrite2VarProp(col->expr()), col->alias())); } diff --git a/src/graph/validator/GoValidator.h b/src/graph/validator/GoValidator.h index 8299c74b569..0f5293f7021 100644 --- a/src/graph/validator/GoValidator.h +++ b/src/graph/validator/GoValidator.h @@ -36,7 +36,7 @@ class GoValidator final : public Validator { Status extractTagIds(); - void extractPropExprs(const Expression* expr, std::unordered_set& uniqueCols); + Status extractPropExprs(const Expression* expr, std::unordered_set& uniqueCols); Expression* rewrite2VarProp(const Expression* expr); diff --git a/src/graph/validator/MatchValidator.cpp b/src/graph/validator/MatchValidator.cpp index e47de251cf7..492a36d76b5 100644 --- a/src/graph/validator/MatchValidator.cpp +++ b/src/graph/validator/MatchValidator.cpp @@ -296,14 +296,14 @@ Status MatchValidator::buildEdgeInfo(const MatchPath *path, } } AliasType aliasType = AliasType::kEdge; - auto *stepRange = edge->range(); + auto stepRange = const_cast(edge)->range(); if (stepRange != nullptr) { NG_RETURN_IF_ERROR(validateStepRange(stepRange)); - edgeInfos[i].range = stepRange; // Type of [e*1..2], [e*2] should be inference to EdgeList if (stepRange->max() > stepRange->min() || stepRange->min() > 1) { aliasType = AliasType::kEdgeList; } + edgeInfos[i].range.reset(new MatchStepRange(*stepRange)); } if (alias.empty()) { anonymous = true; @@ -509,14 +509,19 @@ Status MatchValidator::validateReturn(MatchReturn *ret, Status MatchValidator::validateAliases( const std::vector &exprs, const std::unordered_map &aliasesAvailable) const { - static const std::unordered_set kinds = {Expression::Kind::kLabel, - Expression::Kind::kLabelAttribute, - Expression::Kind::kLabelTagProperty, - // primitive props - Expression::Kind::kEdgeSrc, - Expression::Kind::kEdgeDst, - Expression::Kind::kEdgeRank, - Expression::Kind::kEdgeType}; + static const std::unordered_set kinds = { + Expression::Kind::kLabel, + Expression::Kind::kLabelAttribute, + Expression::Kind::kLabelTagProperty, + // primitive props + Expression::Kind::kEdgeSrc, + Expression::Kind::kEdgeDst, + Expression::Kind::kEdgeRank, + Expression::Kind::kEdgeType, + // invalid prop exprs + Expression::Kind::kSrcProperty, + Expression::Kind::kDstProperty, + }; for (auto *expr : exprs) { auto refExprs = ExpressionUtils::collectAll(expr, kinds); @@ -977,11 +982,6 @@ Status MatchValidator::checkAlias( auto name = static_cast(refExpr)->left()->name(); auto res = getAliasType(aliasesAvailable, name); NG_RETURN_IF_ERROR(res); - if (res.value() == AliasType::kNode) { - return Status::SemanticError( - "To get the property of the vertex in `%s', should use the format `var.tag.prop'", - refExpr->toString().c_str()); - } return Status::OK(); } case Expression::Kind::kEdgeSrc: { @@ -1058,6 +1058,11 @@ Status MatchValidator::checkAlias( name.c_str()); } } + case Expression::Kind::kSrcProperty: + case Expression::Kind::kDstProperty: { + return Status::SemanticError("Expression %s is not allowed to use in cypher", + refExpr->toString().c_str()); + } default: // refExpr must satisfy one of cases and should never hit this branch break; } @@ -1100,36 +1105,36 @@ Status MatchValidator::validateMatchPathExpr( auto *matchPathExprImpl = const_cast( static_cast(matchPathExpr)); // Check variables - NG_RETURN_IF_ERROR(checkMatchPathExpr(matchPathExprImpl->matchPath(), availableAliases)); + NG_RETURN_IF_ERROR(checkMatchPathExpr(matchPathExprImpl->matchPathPtr(), availableAliases)); // Build path alias - auto &matchPath = matchPathExprImpl->matchPath(); - auto pathAlias = matchPath.toString(); - matchPath.setAlias(new std::string(pathAlias)); + auto matchPathPtr = matchPathExprImpl->matchPathPtr(); + auto pathAlias = matchPathPtr->toString(); + matchPathPtr->setAlias(new std::string(pathAlias)); if (matchPathExprImpl->genList() == nullptr) { // Don't done in expression visitor Expression *genList = InputPropertyExpression::make(pool, pathAlias); matchPathExprImpl->setGenList(genList); } paths.emplace_back(); - NG_RETURN_IF_ERROR(validatePath(&matchPath, paths.back())); - NG_RETURN_IF_ERROR(buildRollUpPathInfo(&matchPath, paths.back())); + NG_RETURN_IF_ERROR(validatePath(matchPathPtr, paths.back())); + NG_RETURN_IF_ERROR(buildRollUpPathInfo(matchPathPtr, paths.back())); } return Status::OK(); } -bool extractSinglePathPredicate(Expression *expr, std::vector &pathPreds) { +bool extractSinglePathPredicate(Expression *expr, std::vector &pathPreds) { if (expr->kind() == Expression::Kind::kMatchPathPattern) { - auto pred = static_cast(expr)->matchPath().clone(); - pred.setPredicate(); - pathPreds.emplace_back(std::move(pred)); + auto pred = static_cast(expr)->matchPathPtr(); + pred->setPredicate(); + pathPreds.emplace_back(pred); // Absorb expression into path predicate return true; } else if (expr->kind() == Expression::Kind::kUnaryNot) { auto *operand = static_cast(expr)->operand(); if (operand->kind() == Expression::Kind::kMatchPathPattern) { - auto pred = static_cast(operand)->matchPath().clone(); - pred.setAntiPredicate(); - pathPreds.emplace_back(std::move(pred)); + auto pred = static_cast(operand)->matchPathPtr(); + pred->setAntiPredicate(); + pathPreds.emplace_back(pred); // Absorb expression into path predicate return true; } else if (operand->kind() == Expression::Kind::kFunctionCall) { @@ -1138,9 +1143,9 @@ bool extractSinglePathPredicate(Expression *expr, std::vector &pathPr auto args = funcExpr->args()->args(); DCHECK_EQ(args.size(), 1); if (args[0]->kind() == Expression::Kind::kMatchPathPattern) { - auto pred = static_cast(args[0])->matchPath().clone(); - pred.setAntiPredicate(); - pathPreds.emplace_back(std::move(pred)); + auto pred = static_cast(args[0])->matchPathPtr(); + pred->setAntiPredicate(); + pathPreds.emplace_back(pred); // Absorb expression into path predicate return true; } @@ -1151,7 +1156,7 @@ bool extractSinglePathPredicate(Expression *expr, std::vector &pathPr return false; } -bool extractMultiPathPredicate(Expression *expr, std::vector &pathPreds) { +bool extractMultiPathPredicate(Expression *expr, std::vector &pathPreds) { if (expr->kind() == Expression::Kind::kLogicalAnd) { auto &operands = static_cast(expr)->operands(); for (auto iter = operands.begin(); iter != operands.end();) { @@ -1177,7 +1182,7 @@ Status MatchValidator::validatePathInWhere( auto *pool = qctx_->objPool(); ValidatePatternExpressionVisitor visitor(pool, vctx_); expr->accept(&visitor); - std::vector pathPreds; + std::vector pathPreds; // FIXME(czp): Delete this function and add new expression visitor to cover all general cases if (extractMultiPathPredicate(expr, pathPreds)) { wctx.filter = nullptr; @@ -1190,11 +1195,11 @@ Status MatchValidator::validatePathInWhere( for (auto &pred : pathPreds) { NG_RETURN_IF_ERROR(checkMatchPathExpr(pred, availableAliases)); // Build path alias - auto pathAlias = pred.toString(); - pred.setAlias(new std::string(pathAlias)); + auto pathAlias = pred->toString(); + pred->setAlias(new std::string(pathAlias)); paths.emplace_back(); - NG_RETURN_IF_ERROR(validatePath(&pred, paths.back())); - NG_RETURN_IF_ERROR(buildRollUpPathInfo(&pred, paths.back())); + NG_RETURN_IF_ERROR(validatePath(pred, paths.back())); + NG_RETURN_IF_ERROR(buildRollUpPathInfo(pred, paths.back())); } // All inside pattern expressions are path predicate @@ -1211,7 +1216,7 @@ Status MatchValidator::validatePathInWhere( auto *matchPathExprImpl = const_cast( static_cast(matchPathExpr)); // Check variables - NG_RETURN_IF_ERROR(checkMatchPathExpr(matchPathExprImpl->matchPath(), availableAliases)); + NG_RETURN_IF_ERROR(checkMatchPathExpr(matchPathExprImpl->matchPathPtr(), availableAliases)); // Build path alias auto &matchPath = matchPathExprImpl->matchPath(); auto pathAlias = matchPath.toString(); @@ -1230,22 +1235,21 @@ Status MatchValidator::validatePathInWhere( } /*static*/ Status MatchValidator::checkMatchPathExpr( - const MatchPath &matchPath, - const std::unordered_map &availableAliases) { - if (matchPath.alias() != nullptr) { - const auto find = availableAliases.find(*matchPath.alias()); + MatchPath *matchPath, const std::unordered_map &availableAliases) { + if (matchPath->alias() != nullptr) { + const auto find = availableAliases.find(*matchPath->alias()); if (find == availableAliases.end()) { return Status::SemanticError( "PatternExpression are not allowed to introduce new variables: `%s'.", - matchPath.alias()->c_str()); + matchPath->alias()->c_str()); } if (find->second != AliasType::kPath) { return Status::SemanticError("`%s' is defined with type %s, but referenced with type Path", - matchPath.alias()->c_str(), + matchPath->alias()->c_str(), AliasTypeName::get(find->second).c_str()); } } - for (const auto &node : matchPath.nodes()) { + for (const auto &node : matchPath->nodes()) { if (node->variableDefinedSource() == MatchNode::VariableDefinedSource::kExpression) { // Checked in visitor continue; @@ -1264,7 +1268,7 @@ Status MatchValidator::validatePathInWhere( } } } - for (const auto &edge : matchPath.edges()) { + for (const auto &edge : matchPath->edges()) { if (!edge->alias().empty()) { const auto find = availableAliases.find(edge->alias()); if (find == availableAliases.end()) { diff --git a/src/graph/validator/MatchValidator.h b/src/graph/validator/MatchValidator.h index f1e9f5166d5..c78de9d3e7a 100644 --- a/src/graph/validator/MatchValidator.h +++ b/src/graph/validator/MatchValidator.h @@ -104,8 +104,7 @@ class MatchValidator final : public Validator { std::vector &paths); static Status checkMatchPathExpr( - const MatchPath &matchPath, - const std::unordered_map &availableAliases); + MatchPath *matchPath, const std::unordered_map &availableAliases); static Status buildRollUpPathInfo(const MatchPath *path, Path &pathInfo); diff --git a/src/graph/visitor/ExtractPropExprVisitor.cpp b/src/graph/visitor/ExtractPropExprVisitor.cpp index 61e79c11c61..fa2dd1db8b8 100644 --- a/src/graph/visitor/ExtractPropExprVisitor.cpp +++ b/src/graph/visitor/ExtractPropExprVisitor.cpp @@ -47,10 +47,6 @@ void ExtractPropExprVisitor::visit(VariableExpression* expr) { UNUSED(expr); } -void ExtractPropExprVisitor::visit(SubscriptExpression* expr) { - reportError(expr); -} - void ExtractPropExprVisitor::visit(LabelExpression* expr) { reportError(expr); } diff --git a/src/graph/visitor/ExtractPropExprVisitor.h b/src/graph/visitor/ExtractPropExprVisitor.h index 87095bd8db6..470d403d4d0 100644 --- a/src/graph/visitor/ExtractPropExprVisitor.h +++ b/src/graph/visitor/ExtractPropExprVisitor.h @@ -59,8 +59,6 @@ class ExtractPropExprVisitor final : public ExprVisitorImpl { // vertex/edge expression void visit(VertexExpression *) override; void visit(EdgeExpression *) override; - // binary expression - void visit(SubscriptExpression *) override; // column expression void visit(ColumnExpression *) override; diff --git a/src/interface/common.thrift b/src/interface/common.thrift index 0bdf5d44f94..7a87b1fb807 100644 --- a/src/interface/common.thrift +++ b/src/interface/common.thrift @@ -376,6 +376,7 @@ enum ErrorCode { E_RELATED_INDEX_EXISTS = -2015, // There are still indexes related to tag or edge, cannot drop it E_RELATED_SPACE_EXISTS = -2016, // There are still some space on the host, cannot drop it E_RELATED_FULLTEXT_INDEX_EXISTS = -2017, // There are still fulltext index on tag/edge + E_HISTORY_CONFLICT = -2018, // Existed before (e.g., schema) E_STORE_FAILURE = -2021, // Failed to store data E_STORE_SEGMENT_ILLEGAL = -2022, // Illegal storage segment diff --git a/src/kvstore/listener/elasticsearch/ESListener.cpp b/src/kvstore/listener/elasticsearch/ESListener.cpp index 66927c8c5da..92435e6ec22 100644 --- a/src/kvstore/listener/elasticsearch/ESListener.cpp +++ b/src/kvstore/listener/elasticsearch/ESListener.cpp @@ -73,6 +73,18 @@ void ESListener::pickTagAndEdgeData(BatchLogType type, const std::string& key, const std::string& value, const PickFunc& callback) { + bool isTag = nebula::NebulaKeyUtils::isTag(vIdLen_, key); + bool isEdge = nebula::NebulaKeyUtils::isEdge(vIdLen_, key); + if (!(isTag || isEdge)) { + return; + } + std::unordered_map ftIndexes; + nebula::RowReaderWrapper reader; + + std::string vid; + std::string src; + std::string dst; + int rank = 0; if (nebula::NebulaKeyUtils::isTag(vIdLen_, key)) { auto tagId = NebulaKeyUtils::getTagId(vIdLen_, key); auto ftIndexRes = schemaMan_->getFTIndex(spaceId_, tagId); @@ -80,34 +92,17 @@ void ESListener::pickTagAndEdgeData(BatchLogType type, LOG(ERROR) << ftIndexRes.status().message(); return; } - auto ftIndex = std::move(ftIndexRes).value(); - if (ftIndex.empty()) { - return; - } - auto reader = RowReaderWrapper::getTagPropReader(schemaMan_, spaceId_, tagId, value); - if (reader == nullptr) { - LOG(ERROR) << "get tag reader failed, tagID " << tagId; - return; - } - for (auto& index : ftIndex) { - if (index.second.get_fields().size() > 1) { - LOG(ERROR) << "Only one field will create fulltext index"; - } - auto field = index.second.get_fields().front(); - auto v = reader->getValueByName(field); - if (v.type() == Value::Type::NULLVALUE) { - continue; - } - if (v.type() != Value::Type::STRING) { - LOG(ERROR) << "Can't create fulltext index on type " << v.type(); + ftIndexes = std::move(ftIndexRes).value(); + if (type == BatchLogType::OP_BATCH_PUT) { + reader = RowReaderWrapper::getTagPropReader(schemaMan_, spaceId_, tagId, value); + if (reader == nullptr) { + LOG(ERROR) << "get tag reader failed, tagID " << tagId; + return; } - std::string indexName = index.first; - std::string vid = NebulaKeyUtils::getVertexId(vIdLen_, key).toString(); - vid = truncateVid(vid); - std::string text = std::move(v).getStr(); - callback(type, indexName, vid, "", "", 0, text); } - } else if (nebula::NebulaKeyUtils::isEdge(vIdLen_, key)) { + vid = NebulaKeyUtils::getVertexId(vIdLen_, key).toString(); + vid = truncateVid(vid); + } else { auto edgeType = NebulaKeyUtils::getEdgeType(vIdLen_, key); if (edgeType < 0) { return; @@ -116,33 +111,44 @@ void ESListener::pickTagAndEdgeData(BatchLogType type, if (!ftIndexRes.ok()) { return; } - auto ftIndex = std::move(ftIndexRes).value(); - auto reader = RowReaderWrapper::getEdgePropReader(schemaMan_, spaceId_, edgeType, value); - if (reader == nullptr) { - LOG(ERROR) << "get edge reader failed, schema ID " << edgeType; - return; - } - for (auto& index : ftIndex) { - if (index.second.get_fields().size() > 1) { - LOG(ERROR) << "Only one field will create fulltext index"; + ftIndexes = std::move(ftIndexRes).value(); + if (type == BatchLogType::OP_BATCH_PUT) { + reader = RowReaderWrapper::getEdgePropReader(schemaMan_, spaceId_, edgeType, value); + if (reader == nullptr) { + LOG(ERROR) << "get edge reader failed, schema ID " << edgeType; + return; } + } + src = NebulaKeyUtils::getSrcId(vIdLen_, key).toString(); + dst = NebulaKeyUtils::getDstId(vIdLen_, key).toString(); + rank = NebulaKeyUtils::getRank(vIdLen_, key); + + src = truncateVid(src); + dst = truncateVid(dst); + } + if (ftIndexes.empty()) { + return; + } + + for (auto& index : ftIndexes) { + if (index.second.get_fields().size() > 1) { + LOG(ERROR) << "Only one field will create fulltext index"; + } + std::string text; + std::string indexName = index.first; + if (type == BatchLogType::OP_BATCH_PUT) { auto field = index.second.get_fields().front(); auto v = reader->getValueByName(field); if (v.type() == Value::Type::NULLVALUE) { + callback(BatchLogType::OP_BATCH_REMOVE, indexName, vid, src, dst, 0, text); continue; } if (v.type() != Value::Type::STRING) { LOG(ERROR) << "Can't create fulltext index on type " << v.type(); } - std::string indexName = index.first; - std::string src = NebulaKeyUtils::getSrcId(vIdLen_, key).toString(); - std::string dst = NebulaKeyUtils::getDstId(vIdLen_, key).toString(); - int64_t rank = NebulaKeyUtils::getRank(vIdLen_, key); - std::string text = std::move(v).getStr(); - src = truncateVid(src); - dst = truncateVid(dst); - callback(type, indexName, "", src, dst, rank, text); + text = std::move(v).getStr(); } + callback(type, indexName, vid, src, dst, rank, text); } } diff --git a/src/meta/ActiveHostsMan.cpp b/src/meta/ActiveHostsMan.cpp index 1dd1bb811a5..aa5aa707e9b 100644 --- a/src/meta/ActiveHostsMan.cpp +++ b/src/meta/ActiveHostsMan.cpp @@ -13,7 +13,6 @@ DECLARE_int32(heartbeat_interval_secs); DEFINE_int32(agent_heartbeat_interval_secs, 60, "Agent heartbeat interval in seconds"); DECLARE_uint32(expired_time_factor); -DEFINE_bool(check_term_for_leader_info, false, "if check term when update leader info"); namespace nebula { namespace meta { @@ -46,16 +45,14 @@ nebula::cpp2::ErrorCode ActiveHostsMan::updateHostInfo(kvstore::KVStore* kv, TermID term = -1; nebula::cpp2::ErrorCode code; for (auto i = 0U; i != leaderKeys.size(); ++i) { - if (FLAGS_check_term_for_leader_info) { - if (statusVec[i].ok()) { - std::tie(std::ignore, term, code) = MetaKeyUtils::parseLeaderValV3(vals[i]); - if (code != nebula::cpp2::ErrorCode::SUCCEEDED) { - LOG(INFO) << apache::thrift::util::enumNameSafe(code); - continue; - } - if (terms[i] <= term) { - continue; - } + if (statusVec[i].ok()) { + std::tie(std::ignore, term, code) = MetaKeyUtils::parseLeaderValV3(vals[i]); + if (code != nebula::cpp2::ErrorCode::SUCCEEDED) { + LOG(INFO) << apache::thrift::util::enumNameSafe(code); + continue; + } + if (terms[i] <= term) { + continue; } } // write directly if not exist, or update if has greater term diff --git a/src/meta/MetaServiceUtils.cpp b/src/meta/MetaServiceUtils.cpp index 4a23439dc1e..6e864731a53 100644 --- a/src/meta/MetaServiceUtils.cpp +++ b/src/meta/MetaServiceUtils.cpp @@ -87,11 +87,20 @@ nebula::cpp2::ErrorCode MetaServiceUtils::alterColumnDefs( bool isEdge) { switch (op) { case cpp2::AlterSchemaOp::ADD: + // Check the current schema first. Then check all schemas. + for (auto it = cols.begin(); it != cols.end(); ++it) { + if (it->get_name() == col.get_name()) { + LOG(ERROR) << "Column existing: " << col.get_name(); + return nebula::cpp2::ErrorCode::E_EXISTED; + } + } + // There won't any two columns having the same name across all schemas. If there is a column + // having the same name with the intended change, it must be from history schemas. for (auto& versionedCols : allVersionedCols) { for (auto it = versionedCols.begin(); it != versionedCols.end(); ++it) { if (it->get_name() == col.get_name()) { - LOG(ERROR) << "Column currently or previously existing: " << col.get_name(); - return nebula::cpp2::ErrorCode::E_EXISTED; + LOG(ERROR) << "Column previously existing: " << col.get_name(); + return nebula::cpp2::ErrorCode::E_HISTORY_CONFLICT; } } } diff --git a/src/meta/processors/admin/CreateBackupProcessor.cpp b/src/meta/processors/admin/CreateBackupProcessor.cpp index 9d2e10b346a..5735e55ae66 100644 --- a/src/meta/processors/admin/CreateBackupProcessor.cpp +++ b/src/meta/processors/admin/CreateBackupProcessor.cpp @@ -85,9 +85,11 @@ void CreateBackupProcessor::process(const cpp2::CreateBackupReq& req) { // make sure there is no index job std::unordered_set jobTypes{cpp2::JobType::REBUILD_TAG_INDEX, cpp2::JobType::REBUILD_EDGE_INDEX, + cpp2::JobType::REBUILD_FULLTEXT_INDEX, cpp2::JobType::COMPACT, cpp2::JobType::INGEST, cpp2::JobType::DATA_BALANCE, + cpp2::JobType::ZONE_BALANCE, cpp2::JobType::LEADER_BALANCE}; auto result = jobMgr->checkTypeJobRunning(jobTypes); if (!nebula::ok(result)) { @@ -105,6 +107,7 @@ void CreateBackupProcessor::process(const cpp2::CreateBackupReq& req) { } folly::SharedMutex::WriteHolder holder(LockUtils::snapshotLock()); + LOG(INFO) << "Start to create checkpoints in all hosts."; // get active storage host list auto activeHostsRet = ActiveHostsMan::getActiveHosts(kvstore_); if (!nebula::ok(activeHostsRet)) { diff --git a/src/meta/processors/index/FTIndexProcessor.cpp b/src/meta/processors/index/FTIndexProcessor.cpp index d5df8d97268..278dee1e3ee 100644 --- a/src/meta/processors/index/FTIndexProcessor.cpp +++ b/src/meta/processors/index/FTIndexProcessor.cpp @@ -65,18 +65,6 @@ void CreateFTIndexProcessor::process(const cpp2::CreateFTIndexReq& req) { onFinished(); return; } - // if the data type is fixed_string, - // the data length must be less than MAX_INDEX_TYPE_LENGTH. - // else if the data type is string, - // will be truncated to MAX_INDEX_TYPE_LENGTH bytes when data insert. - if (targetCol->get_type().get_type() == nebula::cpp2::PropertyType::FIXED_STRING && - *targetCol->get_type().get_type_length() > MAX_INDEX_TYPE_LENGTH) { - LOG(INFO) << "Unsupported data length more than " << MAX_INDEX_TYPE_LENGTH - << " bytes : " << col << "(" << *targetCol->get_type().get_type_length() << ")"; - handleErrorCode(nebula::cpp2::ErrorCode::E_UNSUPPORTED); - onFinished(); - return; - } } // Check fulltext index exist. diff --git a/src/meta/processors/job/JobManager.cpp b/src/meta/processors/job/JobManager.cpp index c9d31c417fb..8b28bb0862a 100644 --- a/src/meta/processors/job/JobManager.cpp +++ b/src/meta/processors/job/JobManager.cpp @@ -567,8 +567,13 @@ nebula::cpp2::ErrorCode JobManager::addJob(JobDescription jobDesc) { if (rc == nebula::cpp2::ErrorCode::SUCCEEDED) { enqueue(spaceId, jobId, JbOp::ADD, jobDesc.getJobType()); inFlightJobs_.emplace(std::move(jobId), std::move(jobDesc)); + LOG(INFO) << folly::sformat("Add job successfully, job id={}, job type={}", + jobId, + apache::thrift::util::enumNameSafe(jobDesc.getJobType())); } else { - LOG(INFO) << "Add Job Failed"; + LOG(INFO) << folly::sformat("Add job failed, job id={}, job type={}", + jobId, + apache::thrift::util::enumNameSafe(jobDesc.getJobType())); if (rc != nebula::cpp2::ErrorCode::E_LEADER_CHANGED) { rc = nebula::cpp2::ErrorCode::E_ADD_JOB_FAILURE; } @@ -1002,6 +1007,7 @@ ErrorOr JobManager::checkTypeJobRunning( auto jobDesc = nebula::value(optJobRet); auto jType = jobDesc.getJobType(); if (jobTypes.find(jType) == jobTypes.end()) { + LOG(INFO) << "skip job type:" << apache::thrift::util::enumNameSafe(jType); continue; } diff --git a/src/meta/processors/job/RebuildJobExecutor.cpp b/src/meta/processors/job/RebuildJobExecutor.cpp index 0627416b820..a485144151c 100644 --- a/src/meta/processors/job/RebuildJobExecutor.cpp +++ b/src/meta/processors/job/RebuildJobExecutor.cpp @@ -64,12 +64,12 @@ nebula::cpp2::ErrorCode RebuildJobExecutor::stop() { auto tries = folly::collectAll(std::move(futures)).get(); if (std::any_of(tries.begin(), tries.end(), [](auto& t) { return t.hasException(); })) { LOG(INFO) << "RebuildJobExecutor::stop() RPC failure."; - return nebula::cpp2::ErrorCode::E_BALANCER_FAILURE; + return nebula::cpp2::ErrorCode::E_RPC_FAILURE; } for (const auto& t : tries) { if (!t.value().ok()) { LOG(INFO) << "Stop Build Index Failed"; - return nebula::cpp2::ErrorCode::E_BALANCER_FAILURE; + return nebula::cpp2::ErrorCode::E_RPC_FAILURE; } } return nebula::cpp2::ErrorCode::SUCCEEDED; diff --git a/src/meta/processors/job/StatsJobExecutor.cpp b/src/meta/processors/job/StatsJobExecutor.cpp index b381172d5b1..4d6aa26c413 100644 --- a/src/meta/processors/job/StatsJobExecutor.cpp +++ b/src/meta/processors/job/StatsJobExecutor.cpp @@ -198,13 +198,13 @@ nebula::cpp2::ErrorCode StatsJobExecutor::stop() { auto tries = folly::collectAll(std::move(futures)).get(); if (std::any_of(tries.begin(), tries.end(), [](auto& t) { return t.hasException(); })) { LOG(INFO) << "stats job stop() RPC failure."; - return nebula::cpp2::ErrorCode::E_BALANCER_FAILURE; + return nebula::cpp2::ErrorCode::E_RPC_FAILURE; } for (const auto& t : tries) { if (!t.value().ok()) { LOG(INFO) << "Stop stats job Failed"; - return nebula::cpp2::ErrorCode::E_BALANCER_FAILURE; + return nebula::cpp2::ErrorCode::E_RPC_FAILURE; } } return nebula::cpp2::ErrorCode::SUCCEEDED; diff --git a/src/meta/processors/job/StorageJobExecutor.cpp b/src/meta/processors/job/StorageJobExecutor.cpp index 1934c7aedec..1d179fa4e8b 100644 --- a/src/meta/processors/job/StorageJobExecutor.cpp +++ b/src/meta/processors/job/StorageJobExecutor.cpp @@ -231,7 +231,7 @@ folly::Future StorageJobExecutor::execute() { task.getErrorCode()); faildKV.emplace_back(std::move(taskKey), std::move(taskVal)); } - + baton.reset(); kvstore_->asyncMultiPut( kDefaultSpaceId, kDefaultPartId, std::move(faildKV), [&](nebula::cpp2::ErrorCode code) { if (code != nebula::cpp2::ErrorCode::SUCCEEDED) { diff --git a/src/meta/processors/session/SessionManagerProcessor.cpp b/src/meta/processors/session/SessionManagerProcessor.cpp index 75a30578add..e33a5341b60 100644 --- a/src/meta/processors/session/SessionManagerProcessor.cpp +++ b/src/meta/processors/session/SessionManagerProcessor.cpp @@ -57,11 +57,15 @@ void UpdateSessionsProcessor::process(const cpp2::UpdateSessionsReq& req) { if (!nebula::ok(ret)) { auto errCode = nebula::error(ret); LOG(INFO) << "Session id '" << sessionId << "' not found"; - // If the session requested to be updated can not be found in meta, the session has been - // killed + // If the session requested to be updated can not be found in meta, we consider the session + // has been killed if (errCode == nebula::cpp2::ErrorCode::E_KEY_NOT_FOUND) { killedSessions.emplace_back(sessionId); continue; + } else { + handleErrorCode(errCode); + onFinished(); + return; } } @@ -169,10 +173,18 @@ void RemoveSessionProcessor::process(const cpp2::RemoveSessionReq& req) { auto sessionKey = MetaKeyUtils::sessionKey(sessionId); auto ret = doGet(sessionKey); - // If the session is not found, we should continue to remove other sessions. if (!nebula::ok(ret)) { + auto errCode = nebula::error(ret); LOG(INFO) << "Session id `" << sessionId << "' not found"; - continue; + + // If the session is not found, we should continue to remove other sessions. + if (errCode == nebula::cpp2::ErrorCode::E_KEY_NOT_FOUND) { + continue; + } else { // for other error like leader change, we handle the error and return. + handleErrorCode(errCode); + onFinished(); + return; + } } // Remove session key from kvstore diff --git a/src/meta/test/CreateBackupProcessorTest.cpp b/src/meta/test/CreateBackupProcessorTest.cpp index 4291f19c834..d0bb559b371 100644 --- a/src/meta/test/CreateBackupProcessorTest.cpp +++ b/src/meta/test/CreateBackupProcessorTest.cpp @@ -272,9 +272,11 @@ TEST_F(CreateBackupProcessorTest, Basic) { TEST_F(CreateBackupProcessorTest, RunningJobs) { std::vector jobTypes{cpp2::JobType::REBUILD_TAG_INDEX, cpp2::JobType::REBUILD_EDGE_INDEX, + cpp2::JobType::REBUILD_FULLTEXT_INDEX, cpp2::JobType::COMPACT, cpp2::JobType::INGEST, cpp2::JobType::DATA_BALANCE, + cpp2::JobType::ZONE_BALANCE, cpp2::JobType::LEADER_BALANCE}; JobID jobId = 1; for (auto jobType : jobTypes) { diff --git a/src/parser/MatchPath.h b/src/parser/MatchPath.h index a0154f6f022..487ad0dba8b 100644 --- a/src/parser/MatchPath.h +++ b/src/parser/MatchPath.h @@ -111,7 +111,7 @@ class MatchEdge final { return props_; } - auto* range() const { + MatchStepRange* range() const { return range_.get(); } diff --git a/src/parser/parser.yy b/src/parser/parser.yy index 966e81c6f77..a969625f581 100644 --- a/src/parser/parser.yy +++ b/src/parser/parser.yy @@ -1682,7 +1682,15 @@ match_clause $$ = new MatchClause($2, $3, false/*optional*/); } | KW_OPTIONAL KW_MATCH match_path_list where_clause { - $$ = new MatchClause($3, $4, true); + if ($4 != nullptr) { + SCOPE_EXIT { + delete $3; + delete $4; + }; + throw nebula::GraphParser::syntax_error(@4, "Where clause in optional match is not supported."); + } else { + $$ = new MatchClause($3, nullptr, true); + } } ; diff --git a/tests/Makefile b/tests/Makefile index 8e33ae1efc8..caf4f48176c 100644 --- a/tests/Makefile +++ b/tests/Makefile @@ -2,7 +2,7 @@ # # This source code is licensed under Apache 2.0 License. -.PHONY: fmt check check-and-diff init init-all clean test tck fail up down test-all ldbc +.PHONY: fmt check check-and-diff init init-all clean test tck fail up down test-all ldbc ps kill PYPI_MIRROR = https://mirrors.aliyun.com/pypi/simple/ # PYPI_MIRROR = http://pypi.mirrors.ustc.edu.cn/simple --trusted-host pypi.mirrors.ustc.edu.cn diff --git a/tests/admin/test_permission.py b/tests/admin/test_permission.py index a1cf31fc54f..dda32fac188 100644 --- a/tests/admin/test_permission.py +++ b/tests/admin/test_permission.py @@ -891,6 +891,8 @@ def test_show_test(self): assert ret ret, self.guestClient = self.spawn_nebula_client_and_auth('guest', 'guest') assert ret + ret, self.nopClient = self.spawn_nebula_client_and_auth('nop', 'nop') + assert ret query = 'CREATE SPACE space4(partition_num=1, replica_factor=1, vid_type=FIXED_STRING(8))' resp = self.execute(query) @@ -930,6 +932,12 @@ def test_show_test(self): self.check_column_names(resp, expected_column_names) self.check_out_of_order_result(resp, expected_result) + resp = self.nopClient.execute(query) + self.check_resp_succeeded(resp) + expected_result = [] + self.check_column_names(resp, expected_column_names) + self.check_out_of_order_result(resp, expected_result) + query = 'SHOW ROLES IN space1' resp = self.guestClient.execute(query) self.check_resp_failed(resp, ttypes.ErrorCode.E_BAD_PERMISSION) @@ -987,6 +995,8 @@ def test_show_roles(self): assert ret ret, self.guestClient = self.spawn_nebula_client_and_auth('guest', 'guest') assert ret + ret, self.nopClient = self.spawn_nebula_client_and_auth('nop', 'nop') + assert ret query = 'SHOW ROLES IN space5' expected_result = [] @@ -1021,6 +1031,10 @@ def test_show_roles(self): self.check_resp_succeeded(resp) self.check_out_of_order_result(resp, expected_result) + expected_result = [] + resp = self.nopClient.execute(query) + self.check_resp_failed(resp, ttypes.ErrorCode.E_BAD_PERMISSION) + # clean up query = 'DROP SPACE IF EXISTS space5' resp = self.execute(query) diff --git a/tests/cert/test.2.crt b/tests/cert/test.2.crt new file mode 100644 index 00000000000..b3771fbb003 --- /dev/null +++ b/tests/cert/test.2.crt @@ -0,0 +1,22 @@ +-----BEGIN CERTIFICATE----- +MIIDszCCApsCFHDGB6747rJE8+MWP2IQdEfrONurMA0GCSqGSIb3DQEBCwUAMIGV +MQswCQYDVQQGEwJDTjERMA8GA1UECAwIWmhlamlhbmcxETAPBgNVBAcMCEhhbmd6 +aG91MRQwEgYDVQQKDAtWZXNvZnQgSW5jLjEMMAoGA1UECwwDRGV2MRMwEQYDVQQD +DApTaHlsb2NrIEhnMScwJQYJKoZIhvcNAQkBFhhzaHlsb2NrLmh1YW5nQHZlc29m +dC5jb20wHhcNMjMwMTEyMDgyMTA1WhcNMzMwMTA5MDgyMTA1WjCBlTELMAkGA1UE +BhMCQ04xETAPBgNVBAgMCFpoZWppYW5nMREwDwYDVQQHDAhIYW5nemhvdTEUMBIG +A1UECgwLVmVzb2Z0IEluYy4xDDAKBgNVBAsMA0RldjETMBEGA1UEAwwKU2h5bG9j +ayBIZzEnMCUGCSqGSIb3DQEJARYYc2h5bG9jay5odWFuZ0B2ZXNvZnQuY29tMIIB +IjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA1WFQaXCffwmjwyUJhogVlHdZ +P1PkT/cY6uzQMfiMZibjdsxUrIeNcZU6CGGbS1a+s+BTwuqnOaIWre7qCRtHdzlk +ZaMqFW424WUPCgQxYAekIpw1N8H0gFoj3GqmV+580Ar9Y4L7vUwYwdzMPaFh7AgN +rWDMGOUFXTUW64st2IEIR6M6y0CwFvZMfbIgBrV1oyxfxQnb1OXQQS/Aq0MImstI +fCB+hNhefeGarvJI83O6y0wGXIohTUPKZgkRq+etIw1NzWS3bmCfAt3cwFkvkcMV +GW85OvD1wKCR5vPbTKTs46Nwqoj58+avqwzWvnzYADDRBZBV9/+94ljZ5RbDLwID +AQABMA0GCSqGSIb3DQEBCwUAA4IBAQCxAe7T3POLX16IgQrx416uXunpMBZVEmC/ +0cr/tX/ZVr1D94pZBDbNBplvrIYM0oGUAlzWuOnGhGONzKMTtsyf1JwG0/I/dg7/ +ng6uhiwDjUuB+dJ+CpoBtFNKr9u+Imw6ApDjcbvaN+ycwHwjBy1XX1SIN5bo+xfk +2K+dTAxSI1Zlsqs3spGVIc3IqdfUVnbYkNM3Nx3UYxIjgVfVW1WLf7BgsjvStZKF +pqixpST485YibWVxET3GKOJ4/fZwSl3DUGAMIVBZw4l1juZgG2IL5rFB2PPkNybk +0Xkw4r+RS92NK+CWtfjzQTLGdqxJpLVLt8Ed+q9aA/ugZqT8J3BX +-----END CERTIFICATE----- diff --git a/tests/cert/test.2.csr b/tests/cert/test.2.csr new file mode 100644 index 00000000000..b6d520c4e76 --- /dev/null +++ b/tests/cert/test.2.csr @@ -0,0 +1,18 @@ +-----BEGIN CERTIFICATE REQUEST----- +MIIC9zCCAd8CAQAwgZUxCzAJBgNVBAYTAkNOMREwDwYDVQQIDAhaaGVqaWFuZzER +MA8GA1UEBwwISGFuZ3pob3UxFDASBgNVBAoMC1Zlc29mdCBJbmMuMQwwCgYDVQQL +DANEZXYxEzARBgNVBAMMClNoeWxvY2sgSGcxJzAlBgkqhkiG9w0BCQEWGHNoeWxv +Y2suaHVhbmdAdmVzb2Z0LmNvbTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoC +ggEBALgj6VJTBk7GlbaMW+lwyKjfW+ardnClLbNU+fh0gQGOAffRBz2RiTERzpva +3gpmG+JuxLOz4mUtcssGYkdX5p2Wpt3r2ne0h15UVth7sovyvmTQrS6opD+IZ9dm +7lbDxcZBnf+91gXmEB8f9pSc15eIwaUeqGH8s4YiPjVpM7/8VtRfhk5ps4RE2OiW +1k7vNhxEeBiW9bU5GmY/e8zN4YNJnSo3sXH47KlDekQeA4uZpJkM37GMEmvFImOP +11OvTIKEbPSuFDEvUKQMDB6xbTe83NUMV7tgMcTzoWbsPH/2GcSVTc32Kf1600tl +as6rl8gU2yrzjesEMOGxnDfFD1sCAwEAAaAcMBoGCSqGSIb3DQEJAjENDAtWZXNv +ZnQgSW5jLjANBgkqhkiG9w0BAQsFAAOCAQEASQzHAVdh3KqljVtxKtKvN2RyKhht +iMYS5YICC7ESLhcZhE4jp/XQ00jYs1tXRIczcmvstY/od9VmPX+ycL4OGUqzRaRF +Y4INCSy2/JIEz8aBoApuFzE5OZe/QS6ihwlZTSVW/Q/lTJeZ2N84Rj6ftlp4ZWLx +4NtyPuXWXIY/QOHp6rjbUYVcAvhFio2OIgfGFlV5E3rkmb/R2nU3vQ6BNK2sTpt7 +5ovXXbx7UrvS7/YVoP5+fg7iCSPOSA2t9yrD/N5htWSMiUJHp31EaTbNN8yxtVwb +ieAWLAuPrI0xoy6TLh2tDZX4HBw8ZwpBTDyi+tVdJdcsUzRM+w6xoTpjQg== +-----END CERTIFICATE REQUEST----- diff --git a/tests/cert/test.2.key b/tests/cert/test.2.key new file mode 100644 index 00000000000..d15589db759 --- /dev/null +++ b/tests/cert/test.2.key @@ -0,0 +1,30 @@ +-----BEGIN ENCRYPTED PRIVATE KEY----- +MIIFHDBOBgkqhkiG9w0BBQ0wQTApBgkqhkiG9w0BBQwwHAQINtWdG0nk//kCAggA +MAwGCCqGSIb3DQIJBQAwFAYIKoZIhvcNAwcECER/BYBcGn8fBIIEyMtKPrObru3d +AS+3X4f0dSRErHSNUMOaiRe82RmklGX6Jrhn6C7C5nUA7poM+FBrbJYX6j9txcY8 +6MhskOXPtDYq4o7T0VQ0w9GIxS13nPEFz0zdRvimDKJPH5+rUhAv6DTM7YqZAf1D +oQmVPCpWRoYWOtA4NKYZFqv4kIUQQybPclr2vv7Q0pbKdqTvkyK46DRqozgLREj3 +iNRIU2xa5nMGrL8M+AjOKTjMJP5fbJXFr1ZezrPRdNg3EWaMAk+9zMfsluYsx6zZ +60rEHdCStYj4Gisc/FIGDf/CMGiuENrdO6gw5wsZU0MaqbgjUAbCMTngtrAciQiA +Q4bFcu33NOAhcnmiMpBFE3QMHBm1e0aAIR3sSqlJoIdxeD64b6D2+HtHQCvm8dsm ++nDDJVWimbieXQrki0GH/rHZzeDXTkMgMKvjRvpSb/Ksfio9IlPrNu88EMLmN9CC +cxwEPVxJheISyguZwMxlvz4sSsuxz3Msfujc5IJ4t6K4N2LtLqu4LB4WrTMZVGuG +56rHO2AbrEWKDRywwsm9x6gXppM/IMMKOddUlk02nKxd+oqeQQx2ZwbC2VeydAok +eXbhq8ccus2zZtLE/l6zhM0BCd6eJRp1rrTcx27bSf+KeE/GoYCabi3+ZI4xICW2 +pDPSbM0Hg0Ma09FRHYs/Fevmy3VfAZ8VR0zCVvCOku8avfIN5fZBRDaNmu1Fa2CP +WPTkpH3WBlsKyWO73aHVk1h94XI63hPY/5PIQJsgSCg7479nG57nFxjJQCOJzcN1 +SjkiXWFmOhLtBrAFc5YAypE+NDDWHKs5EsvUZZ4Xo4uSKtdJDISOZIHqpmp8Ntdp +z2ospT9CGwiVjjefmS6kgwRs+Ky6k6U89hOrdF08LSRM13OCeKQPUu6FuWmqlcBe +U0Pzh4Yxg+VVslbudIfLnlembY6lS5/jp1LFo3tH3wAFG64vdPlpaU16Xqc+rRCw +Ntdjt2v4Ju+S5WImLZO7DOMN93lyg6CYt/yLflMoZdKE7zIAon24U/RjrKn/z0KF +G8I022tll7ahrN7c28M9olzSqEgJMg8+tX1Bn6nxJZuLoTCd2AP47Dzw4rVpYFqh +HAQisNFAzlUntW9TUdMAVHEQY58/dAwZxEWXUMvgizn/3T6eRN4H1zw8BK+ae1ni +nGjlSfhvQJ7CwEhcSwCfYFSN72vkGv42g13CN1bik2aeajqbvS8H4plyHkTzQF44 +rqoLdqQTapm0JUU+h1OBApYI/ryM/VQiZr+6onXhlFzc+dl8bmDMpdswM4tWnU+T +BGAyet5DjKanLoz8rWvrm2F+feLbZeepfMK8pI+5mcfZk6B43xjcDYMZcXlt6Crl +3DyIsVzFGVN7nhtUk1VoQgCOO2aLpa9CjGM6Gb6ugsVqHF7z91XLLHLEc+/qVPWu +UJBXvvnj7H3HBAeOc5Uz1jUdNS+kPPVlQiyQBP++ggpThRL9whsgakSemUy7xtW2 +nzYJJhNxkFLEX1fMYucS74+ArI7MFnbuBLbeG/lfjkCl6ErOYCnaIsjK4+Z74Ros +odYQxGYMobvHWzxT12WYUGpbpDslTFqIuULRsRBxeGzF4Ud9e0YI2vnjB4RA5daH +G2KzjFNOtKdl1ng/1UoKIQ== +-----END ENCRYPTED PRIVATE KEY----- diff --git a/tests/cert/test.2.password b/tests/cert/test.2.password new file mode 100644 index 00000000000..60b7570cd13 --- /dev/null +++ b/tests/cert/test.2.password @@ -0,0 +1 @@ +vesoft \ No newline at end of file diff --git a/tests/common/utils.py b/tests/common/utils.py index 760b638d146..77e7a34df07 100644 --- a/tests/common/utils.py +++ b/tests/common/utils.py @@ -442,7 +442,7 @@ def load_csv_data( def get_conn_pool(host: str, port: int, ssl_config: SSL_config): config = Config() - config.max_connection_pool_size = 20 + config.max_connection_pool_size = 30 config.timeout = 180000 # init connection pool pool = ConnectionPool() diff --git a/tests/job/test_session.py b/tests/job/test_session.py index 5c524c0b58b..2d36c8878bd 100644 --- a/tests/job/test_session.py +++ b/tests/job/test_session.py @@ -74,8 +74,8 @@ def cleanup(self): def test_sessions(self): # 1: test add session with right username try: - client_ok = self.client_pool.get_session('session_user', '123456') - assert client_ok is not None + user_session = self.client_pool.get_session('session_user', '123456') + assert user_session is not None assert True except Exception as e: assert False, e @@ -114,7 +114,7 @@ def test_sessions(self): assert session_id != 0 # 4: test get session info - resp = client_ok.execute('USE nba') + resp = user_session.execute('USE nba') self.check_resp_succeeded(resp) # wait for session sync. @@ -158,6 +158,16 @@ def test_sessions(self): self.check_resp_succeeded(resp) time.sleep(3) + # 6: test privilege + # show sessions with non-root user, only the root user can show all sessions + try: + non_root_session = self.client_pool.get_session('session_user', '123456') + assert non_root_session is not None + resp = non_root_session.execute('SHOW SESSIONS') + self.check_resp_failed(resp) + except Exception as e: + assert False, e + def test_the_same_id_to_different_graphd(self): conn1 = self.get_connection(self.addr_host1, self.addr_port1) @@ -431,15 +441,8 @@ def test_kill_session_multi_graph(self): self.check_resp_succeeded(ResultSet(resp, 0)) assert user1_session_num == len(ResultSet(resp, 0).rows()) + 1 - # execute query with the killed session - resp = conn2.execute( - session_id2, - 'SHOW HOSTS', - ) - # the session has not been synced to host2, so the query should succeed - self.check_resp_succeeded(ResultSet(resp, 0)) - # wait for the session to be synced (in test session_reclaim_interval_secs=2) + # and execute a query with the killed session time.sleep(4) resp = conn2.execute( session_id2, diff --git a/tests/tck/features/ddl/Ddl.feature b/tests/tck/features/ddl/Ddl.feature new file mode 100644 index 00000000000..70083f4c864 --- /dev/null +++ b/tests/tck/features/ddl/Ddl.feature @@ -0,0 +1,298 @@ +# Copyright (c) 2023 vesoft inc. All rights reserved. +# +# This source code is licensed under Apache 2.0 License. +Feature: DDL test + + Background: + Given an empty graph + And create a space with following options: + | partition_num | 1 | + | replica_factor | 1 | + | vid_type | FIXED_STRING(30) | + | charset | utf8 | + | collate | utf8_bin | + + Scenario: Tag DDL + When executing query: + """ + CREATE TAG A(); + """ + Then the execution should be successful + When executing query: + """ + CREATE TAG IF NOT EXISTS A(id int, name string); + """ + Then the execution should be successful + When executing query: + """ + CREATE TAG B( + id int NOT NULL DEFAULT 0+0 COMMENT "primary key", + name string NOT NULL, + createDate DATETIME, location geography(polygon), + isVisited bool COMMENT "kHop search flag", + nickName TIME DEFAULT time() + ) + TTL_DURATION = 100, TTL_COL = "id", COMMENT = "TAG B"; + """ + Then the execution should be successful + When executing query: + """ + DESC TAG A; + """ + Then the result should be, in any order: + | Field | Type | Null | Default | Comment | + When executing query: + """ + DESC TAG B; + """ + Then the result should be, in any order: + | Field | Type | Null | Default | Comment | + | "id" | "int64" | "NO" | 0 | "primary key" | + | "name" | "string" | "NO" | | | + | "createDate" | "datetime" | "YES" | | | + | "location" | "geography(polygon)" | "YES" | | | + | "isVisited" | "bool" | "YES" | | "kHop search flag" | + | "nickName" | "time" | "YES" | "time()" | | + When executing query: + """ + ALTER TAG B DROP (name) + """ + Then the execution should be successful + When executing query: + """ + CREATE TAG INDEX idx_A_1 on A(); + """ + Then the execution should be successful + When executing query: + """ + CREATE TAG INDEX idx_A_2 on A(id); + """ + Then a ExecutionError should be raised at runtime: Key not existed! + When executing query: + """ + CREATE TAG INDEX IF NOT EXISTS idx_A_3 on A(); + """ + Then the execution should be successful + When executing query: + """ + CREATE TAG INDEX idx_B_1 on B(isVisited, id, nickName, name(1), createDate); + """ + Then a ExecutionError should be raised at runtime: Key not existed! + When executing query: + """ + ALTER TAG B ADD (name string) + """ + # IMHO, this is really confusing. https://github.com/vesoft-inc/nebula/issues/2671 + Then a ExecutionError should be raised at runtime: Schema exisited before! + When executing query: + """ + ALTER TAG B ADD (namex string) + """ + Then the execution should be successful + When executing query: + """ + ALTER TAG B CHANGE (isVisited bool) + """ + Then the execution should be successful + When executing query: + """ + ALTER TAG B CHANGE (isVisited int) + """ + Then a ExecutionError should be raised at runtime: Unsupported! + When executing query: + """ + CREATE TAG INDEX idx_B_2 on B(id); + """ + Then the execution should be successful + When executing query: + """ + CREATE TAG INDEX idx_B_4 on B(namex); + """ + Then a ExecutionError should be raised at runtime: Invalid param! + When executing query: + """ + DROP TAG INDEX IF EXISTS idx_B_4; + """ + Then the execution should be successful + When executing query: + """ + CREATE TAG INDEX idx_B_5 on B(createDate); + """ + Then the execution should be successful + When executing query: + """ + CREATE TAG INDEX idx_B_6 on B(location); + """ + Then the execution should be successful + When executing query: + """ + CREATE TAG INDEX idx_B_7 on B(isVisited); + """ + Then the execution should be successful + When executing query: + """ + CREATE TAG INDEX idx_B_8 on B(nickName); + """ + Then the execution should be successful + When executing query: + """ + CREATE TAG INDEX idx_B_9 on B(id, nickName, namex(1), createDate); + """ + Then the execution should be successful + When executing query: + """ + CREATE TAG INDEX idx_B_10 on B(id, nickName, namex(1)); + """ + Then the execution should be successful + When executing query: + """ + CREATE TAG INDEX idx_E2_11 on E2(id, nickName, namex(1)); + """ + Then a ExecutionError should be raised at runtime: TagNotFound: Tag not existed! + When executing query: + """ + CREATE TAG INDEX idx_B_1 on B(isVisited, id, nickName, namex(1), createDate); + """ + Then the execution should be successful + + Scenario: Edge DDL + When executing query: + """ + CREATE EDGE E1(); + """ + Then the execution should be successful + When executing query: + """ + CREATE EDGE IF NOT EXISTS E1(id int, name string); + """ + Then the execution should be successful + When executing query: + """ + CREATE EDGE E2( + id int NOT NULL DEFAULT 0+0 COMMENT "primary key", + name string NOT NULL, + createDate DATETIME, location geography(polygon), + isVisited bool COMMENT "kHop search flag", + nickName TIME DEFAULT time() + ) + TTL_DURATION = 100, TTL_COL = "id", COMMENT = "EDGE E2"; + """ + Then the execution should be successful + When executing query: + """ + DESC EDGE E1; + """ + Then the result should be, in any order: + | Field | Type | Null | Default | Comment | + When executing query: + """ + DESC EDGE E2; + """ + Then the result should be, in any order: + | Field | Type | Null | Default | Comment | + | "id" | "int64" | "NO" | 0 | "primary key" | + | "name" | "string" | "NO" | | | + | "createDate" | "datetime" | "YES" | | | + | "location" | "geography(polygon)" | "YES" | | | + | "isVisited" | "bool" | "YES" | | "kHop search flag" | + | "nickName" | "time" | "YES" | "time()" | | + When executing query: + """ + ALTER EDGE E2 DROP (name) + """ + Then the execution should be successful + When executing query: + """ + CREATE EDGE INDEX idx_E1_1 on E1(); + """ + Then the execution should be successful + When executing query: + """ + CREATE EDGE INDEX idx_E1_2 on E1(id); + """ + Then a ExecutionError should be raised at runtime: Key not existed! + When executing query: + """ + CREATE EDGE INDEX IF NOT EXISTS idx_E1_3 on E1(); + """ + Then the execution should be successful + When executing query: + """ + CREATE EDGE INDEX idx_E2_1 on E2(isVisited, id, nickName, name(1), createDate); + """ + Then a ExecutionError should be raised at runtime: Key not existed! + When executing query: + """ + ALTER EDGE E2 ADD (name string) + """ + Then a ExecutionError should be raised at runtime: Schema exisited before! + When executing query: + """ + ALTER EDGE E2 ADD (namex string) + """ + Then the execution should be successful + When executing query: + """ + ALTER EDGE E2 CHANGE (isVisited bool) + """ + Then the execution should be successful + When executing query: + """ + ALTER EDGE E2 CHANGE (isVisited int) + """ + Then a ExecutionError should be raised at runtime: Unsupported! + When executing query: + """ + CREATE EDGE INDEX idx_E2_2 on E2(id); + """ + Then the execution should be successful + When executing query: + """ + CREATE EDGE INDEX idx_E2_4 on E2(namex); + """ + Then a ExecutionError should be raised at runtime: Invalid param! + When executing query: + """ + DROP EDGE INDEX IF EXISTS idx_E2_4; + """ + Then the execution should be successful + When executing query: + """ + CREATE EDGE INDEX idx_E2_5 on E2(createDate); + """ + Then the execution should be successful + When executing query: + """ + CREATE EDGE INDEX idx_E2_6 on E2(location); + """ + Then the execution should be successful + When executing query: + """ + CREATE EDGE INDEX idx_E2_7 on E2(isVisited); + """ + Then the execution should be successful + When executing query: + """ + CREATE EDGE INDEX idx_E2_8 on E2(nickName); + """ + Then the execution should be successful + When executing query: + """ + CREATE EDGE INDEX idx_E2_9 on E2(id, nickName, namex(1), createDate); + """ + Then the execution should be successful + When executing query: + """ + CREATE EDGE INDEX idx_E2_10 on E2(id, nickName, namex(1)); + """ + Then the execution should be successful + When executing query: + """ + CREATE TAG INDEX idx_E2_11 on E2(id, nickName, namex(1)); + """ + Then a ExecutionError should be raised at runtime: TagNotFound: Tag not existed! + When executing query: + """ + CREATE EDGE INDEX idx_E2_1 on E2(isVisited, id, nickName, namex(1), createDate); + """ + Then the execution should be successful diff --git a/tests/tck/features/delete/DeleteEdge.IntVid.feature b/tests/tck/features/delete/DeleteEdge.IntVid.feature index 87794b5ded5..f676ea0ef85 100644 --- a/tests/tck/features/delete/DeleteEdge.IntVid.feature +++ b/tests/tck/features/delete/DeleteEdge.IntVid.feature @@ -135,6 +135,78 @@ Feature: Delete int vid of edge | "Zhangsan" | 50 | "Jack" | Then drop the used space + Scenario: delete edges delete the edge with rank 0 by default + Given an empty graph + And create a space with following options: + | partition_num | 1 | + | replica_factor | 1 | + | vid_type | int | + And having executed: + """ + CREATE TAG IF NOT EXISTS person(name string, age int); + CREATE EDGE IF NOT EXISTS friend(intimacy int); + """ + And having executed: + """ + INSERT VERTEX + person(name, age) + VALUES + hash("Zhangsan"):("Zhangsan", 22), + hash("Lisi"):("Lisi", 23); + INSERT EDGE + friend(intimacy) + VALUES + hash("Zhangsan")->hash("Lisi"):(1), + hash("Zhangsan")->hash("Lisi")@15:(2), + hash("Zhangsan")->hash("Lisi")@25:(3), + hash("Zhangsan")->hash("Lisi")@35:(4); + """ + # before delete get result by go + When executing query: + """ + GO FROM hash("Zhangsan") OVER friend + YIELD $^.person.name, friend.intimacy, friend._rank, friend._dst + """ + Then the result should be, in any order: + | $^.person.name | friend.intimacy | friend._rank | friend._dst | + | "Zhangsan" | 1 | 0 | hash("Lisi") | + | "Zhangsan" | 2 | 15 | hash("Lisi") | + | "Zhangsan" | 3 | 25 | hash("Lisi") | + | "Zhangsan" | 4 | 35 | hash("Lisi") | + # delete edge friend, by default only the edge with rank of 0 will be deleted + When executing query: + """ + DELETE EDGE friend hash("Zhangsan")->hash("Lisi"); + """ + Then the execution should be successful + # check result + When executing query: + """ + GO FROM hash("Zhangsan") OVER friend + YIELD $^.person.name, friend.intimacy, friend._rank, friend._dst + """ + Then the result should be, in any order: + | $^.person.name | friend.intimacy | friend._rank | friend._dst | + | "Zhangsan" | 2 | 15 | hash("Lisi") | + | "Zhangsan" | 3 | 25 | hash("Lisi") | + | "Zhangsan" | 4 | 35 | hash("Lisi") | + # delete all edges with different ranks + When executing query: + """ + GO FROM hash("Zhangsan") OVER friend YIELD id($^) AS src, friend._rank AS rank, friend._dst AS dst + | DELETE EDGE friend $-.src -> $-.dst @ $-.rank; + """ + Then the execution should be successful + # check result + When executing query: + """ + GO FROM hash("Zhangsan") OVER friend + YIELD $^.person.name, friend.intimacy, friend._rank, friend._dst + """ + Then the result should be, in any order: + | $^.person.name | friend.intimacy | friend._rank | friend._dst | + Then drop the used space + Scenario: delete edges use pipe Given load "nba_int_vid" csv data to a new space # test delete with pipe wrong vid type diff --git a/tests/tck/features/delete/DeleteEdge.feature b/tests/tck/features/delete/DeleteEdge.feature index abf06a7a343..adb9a5c755c 100644 --- a/tests/tck/features/delete/DeleteEdge.feature +++ b/tests/tck/features/delete/DeleteEdge.feature @@ -135,6 +135,78 @@ Feature: Delete string vid of edge | "Zhangsan" | 50 | "Jack" | Then drop the used space + Scenario: delete edges delete the edge with rank 0 by default + Given an empty graph + And create a space with following options: + | partition_num | 1 | + | replica_factor | 1 | + | vid_type | FIXED_STRING(20) | + And having executed: + """ + CREATE TAG IF NOT EXISTS person(name string, age int); + CREATE EDGE IF NOT EXISTS friend(intimacy int); + """ + And having executed: + """ + INSERT VERTEX + person(name, age) + VALUES + "Zhangsan":("Zhangsan", 22), + "Lisi":("Lisi", 23); + INSERT EDGE + friend(intimacy) + VALUES + "Zhangsan"->"Lisi":(1), + "Zhangsan"->"Lisi"@15:(2), + "Zhangsan"->"Lisi"@25:(3), + "Zhangsan"->"Lisi"@35:(4); + """ + # before delete get result by go + When executing query: + """ + GO FROM "Zhangsan" OVER friend + YIELD $^.person.name, friend.intimacy, friend._rank, friend._dst + """ + Then the result should be, in any order: + | $^.person.name | friend.intimacy | friend._rank | friend._dst | + | "Zhangsan" | 1 | 0 | "Lisi" | + | "Zhangsan" | 2 | 15 | "Lisi" | + | "Zhangsan" | 3 | 25 | "Lisi" | + | "Zhangsan" | 4 | 35 | "Lisi" | + # delete edge friend, by default only the edge with rank of 0 will be deleted + When executing query: + """ + DELETE EDGE friend "Zhangsan"->"Lisi"; + """ + Then the execution should be successful + # check result + When executing query: + """ + GO FROM "Zhangsan" OVER friend + YIELD $^.person.name, friend.intimacy, friend._rank, friend._dst + """ + Then the result should be, in any order: + | $^.person.name | friend.intimacy | friend._rank | friend._dst | + | "Zhangsan" | 2 | 15 | "Lisi" | + | "Zhangsan" | 3 | 25 | "Lisi" | + | "Zhangsan" | 4 | 35 | "Lisi" | + # delete all edges with different ranks + When executing query: + """ + GO FROM "Zhangsan" OVER friend YIELD id($^) AS src, friend._rank AS rank, friend._dst AS dst + | DELETE EDGE friend $-.src -> $-.dst @ $-.rank; + """ + Then the execution should be successful + # check result + When executing query: + """ + GO FROM "Zhangsan" OVER friend + YIELD $^.person.name, friend.intimacy, friend._rank, friend._dst + """ + Then the result should be, in any order: + | $^.person.name | friend.intimacy | friend._rank | friend._dst | + Then drop the used space + Scenario: delete edges use pipe Given load "nba" csv data to a new space # test delete with pipe wrong vid type diff --git a/tests/tck/features/delete/DeleteTag.IntVid.feature b/tests/tck/features/delete/DeleteTag.IntVid.feature index 1bf23a2d956..11b106c7db9 100644 --- a/tests/tck/features/delete/DeleteTag.IntVid.feature +++ b/tests/tck/features/delete/DeleteTag.IntVid.feature @@ -200,6 +200,7 @@ Feature: Delete int vid of tag """ Then the execution should be successful # after delete tag + # the output has one row because the vertex has multiple tags When executing query: """ FETCH PROP ON player hash("Tim Duncan") YIELD player.name, player.age diff --git a/tests/tck/features/delete/DeleteTag.feature b/tests/tck/features/delete/DeleteTag.feature index 7a4fe136e94..3afecefa036 100644 --- a/tests/tck/features/delete/DeleteTag.feature +++ b/tests/tck/features/delete/DeleteTag.feature @@ -134,7 +134,7 @@ Feature: Delete string vid of tag Then the result should be, in any order: | id | | "Tim Duncan" | - # delete one tag + # delete all tag When executing query: """ DELETE TAG * FROM "Tim Duncan"; @@ -200,6 +200,7 @@ Feature: Delete string vid of tag """ Then the execution should be successful # after delete tag + # the output has one row because the vertex has multiple tags When executing query: """ FETCH PROP ON player "Tim Duncan" YIELD player.name, player.age diff --git a/tests/tck/features/delete/DeleteVertex.feature b/tests/tck/features/delete/DeleteVertex.feature index 58703d63a86..8cc9864d39d 100644 --- a/tests/tck/features/delete/DeleteVertex.feature +++ b/tests/tck/features/delete/DeleteVertex.feature @@ -108,7 +108,7 @@ Feature: Delete string vid of vertex | like._dst | | "Kobe Bryant" | | "Grant Hill" | - | -"Rudy Gay" | + | "Rudy Gay" | # before delete hash id vertex to check value by go When executing query: """ diff --git a/tests/tck/features/expression/Attribute.feature b/tests/tck/features/expression/Attribute.feature index c471c3554c4..7bf039d4994 100644 --- a/tests/tck/features/expression/Attribute.feature +++ b/tests/tck/features/expression/Attribute.feature @@ -61,8 +61,8 @@ Feature: Attribute RETURN {k1 : 1, k2: true}.K1 AS k """ Then the result should be, in any order: - | k | - | UNKNOWN_PROP | + | k | + | NULL | When executing query: """ MATCH (v) WHERE id(v) == 'Tim Duncan' RETURN v.player.name @@ -122,7 +122,7 @@ Feature: Attribute """ Then the result should be, in any order: | not_exists_attr | - | UNKNOWN_PROP | + | NULL | When executing query: """ MATCH (v) WHERE id(v) == 'Tim Duncan' RETURN v.player.not_exists_attr diff --git a/tests/tck/features/go/GO.feature b/tests/tck/features/go/GO.feature index 911d9f4b4cc..e39a5058b97 100644 --- a/tests/tck/features/go/GO.feature +++ b/tests/tck/features/go/GO.feature @@ -14,6 +14,25 @@ Feature: Go Sentence Then the result should be, in any order, with relax comparison: | serve._dst | | "Spurs" | + When executing query: + """ + GO FROM "Tim Duncan" OVER serve WHERE 1 > 0 YIELD serve._dst + """ + Then the result should be, in any order, with relax comparison: + | serve._dst | + | "Spurs" | + When executing query: + """ + GO FROM "Tim Duncan" OVER serve WHERE 1 < 0 YIELD serve._dst + """ + Then the result should be, in any order, with relax comparison: + | serve._dst | + When executing query: + """ + GO FROM "Tim Duncan" OVER serve WHERE 'Tim Duncan' > 0 YIELD serve._dst + """ + Then the result should be, in any order, with relax comparison: + | serve._dst | When executing query: """ GO FROM "Tim Duncan" OVER serve YIELD DISTINCT properties(edge) | YIELD COUNT(*) @@ -76,6 +95,17 @@ Feature: Go Sentence | "Boris Diaw" | 2008 | 2012 | "Hornets" | | "Boris Diaw" | 2012 | 2016 | "Spurs" | | "Boris Diaw" | 2016 | 2017 | "Jazz" | + When executing query: + """ + GO FROM "Boris Diaw" OVER serve YIELD $^.player.name, serve.start_year, serve.end_year, $$.team.name, 1, 1>2 + """ + Then the result should be, in any order, with relax comparison: + | $^.player.name | serve.start_year | serve.end_year | $$.team.name | 1 | (1>2) | + | "Boris Diaw" | 2003 | 2005 | "Hawks" | 1 | false | + | "Boris Diaw" | 2005 | 2008 | "Suns" | 1 | false | + | "Boris Diaw" | 2008 | 2012 | "Hornets" | 1 | false | + | "Boris Diaw" | 2012 | 2016 | "Spurs" | 1 | false | + | "Boris Diaw" | 2016 | 2017 | "Jazz" | 1 | false | When executing query: """ GO FROM "Rajon Rondo" OVER serve WHERE serve.start_year >= 2013 AND serve.end_year <= 2018 @@ -175,6 +205,12 @@ Feature: Go Sentence Then the result should be, in any order, with relax comparison: | $^.player.name | | "Tony Parker" | + When executing query: + """ + GO FROM 'Tony Parker' OVER like WHERE like._dst IN ['Tim Duncan', 'Danny Green'] AND 1 > 2 YIELD $^.player.name + """ + Then the result should be, in any order, with relax comparison: + | $^.player.name | Scenario: assignment simple When executing query: @@ -576,6 +612,37 @@ Feature: Go Sentence | "Chris Paul" | "Carmelo Anthony" | "Dwyane Wade" | | "Chris Paul" | "Dwyane Wade" | "LeBron James" | | "Chris Paul" | "Dwyane Wade" | "Carmelo Anthony" | + When executing query: + """ + GO 3 STEPS FROM "Tim Duncan" OVER like,serve + WHERE (serve.start_year>2000 OR like.likeness>90) AND size(labels($$)[0]) > 0 AND $$.player.age>40 + yield $$ as v + """ + Then the result should be, in any order, with relax comparison: + | v | + | ("Manu Ginobili" :player{age: 41, name: "Manu Ginobili"}) | + When executing query: + """ + GO 3 STEPS FROM "Tim Duncan" OVER like,serve + WHERE size(labels($$))>0 AND $$.player.age>40 + yield $$ as v + """ + Then the result should be, in any order, with relax comparison: + | v | + | ("Tim Duncan" :bachelor{name: "Tim Duncan", speciality: "psychology"} :player{age: 42, name: "Tim Duncan"}) | + | ("Tim Duncan" :bachelor{name: "Tim Duncan", speciality: "psychology"} :player{age: 42, name: "Tim Duncan"}) | + | ("Manu Ginobili" :player{age: 41, name: "Manu Ginobili"}) | + When executing query: + """ + GO 3 STEPS FROM "Tim Duncan" OVER like,serve + WHERE $$.player.age>40 AND size(labels($$)[0]) > 0 + yield $$ as v + """ + Then the result should be, in any order, with relax comparison: + | v | + | ("Tim Duncan" :player{age: 42, name: "Tim Duncan"} :bachelor{name: "Tim Duncan", speciality: "psychology"}) | + | ("Tim Duncan" :player{age: 42, name: "Tim Duncan"} :bachelor{name: "Tim Duncan", speciality: "psychology"}) | + | ("Manu Ginobili" :player{age: 41, name: "Manu Ginobili"}) | @skip Scenario: all prop(reason = $-.* over * $var.* not implement) @@ -1182,6 +1249,11 @@ Feature: Go Sentence GO FROM 'Tim Duncan' OVER like where like.likeness YIELD like._dst """ Then a SemanticError should be raised at runtime: `like.likeness', expected boolean, but was `INT' + When executing query: + """ + GO FROM 'Tim Duncan' OVER like where ($^)-[:like]->($$) YIELD like._dst + """ + Then a SyntaxError should be raised at runtime: syntax error near `)-[:like' Scenario: contain When executing query: @@ -1584,6 +1656,28 @@ Feature: Go Sentence | name | | EMPTY | + Scenario: negative step + When executing query: + """ + GO -1 STEPS FROM 'Tim Duncan' OVER serve BIDIRECT YIELD serve._dst + """ + Then a SyntaxError should be raised at runtime: syntax error near `-1 STEPS' + When executing query: + """ + GO -1 TO 2 STEPS FROM 'Tim Duncan' OVER serve BIDIRECT YIELD serve._dst + """ + Then a SyntaxError should be raised at runtime: syntax error near `-1 TO 2 ' + When executing query: + """ + GO -1 TO -2 STEPS FROM 'Tim Duncan' OVER serve BIDIRECT YIELD serve._dst + """ + Then a SyntaxError should be raised at runtime: syntax error near `-1 TO -2' + When executing query: + """ + GO 1 TO -2 STEPS FROM 'Tim Duncan' OVER serve BIDIRECT YIELD serve._dst + """ + Then a SyntaxError should be raised at runtime: syntax error near `-2 STEPS' + Scenario: zero step When executing query: """ diff --git a/tests/tck/features/insert/Insert.IntVid.feature b/tests/tck/features/insert/Insert.IntVid.feature index 34635dd3798..2ba6e2075db 100644 --- a/tests/tck/features/insert/Insert.IntVid.feature +++ b/tests/tck/features/insert/Insert.IntVid.feature @@ -236,6 +236,34 @@ Feature: Insert int vid of vertex and edge VALUES hash("Laura"):("Laura", 8, "three", 20190901008),hash("Amber"):("Amber", 9, "four", 20180901003) """ Then the execution should be successful + When executing query: + """ + FETCH PROP ON person hash("Laura") YIELD person.name, person.age + """ + Then the result should be, in any order: + | person.name | person.age | + | 'Laura' | 8 | + When executing query: + """ + FETCH PROP ON student hash("Laura") YIELD student.grade, student.number + """ + Then the result should be, in any order: + | student.grade | student.number | + | 'three' | 20190901008 | + When executing query: + """ + FETCH PROP ON person hash("Amber") YIELD person.name, person.age + """ + Then the result should be, in any order: + | person.name | person.age | + | 'Amber' | 9 | + When executing query: + """ + FETCH PROP ON student hash("Amber") YIELD student.grade, student.number + """ + Then the result should be, in any order: + | student.grade | student.number | + | 'four' | 20180901003 | # insert multi vertex one tag When executing query: """ @@ -586,3 +614,15 @@ Feature: Insert int vid of vertex and edge | src | dst | | 300 | 400 | Then drop the used space + + Scenario: insert vertex with non existent tag + Given an empty graph + And create a space with following options: + | partition_num | 1 | + | replica_factor | 1 | + | vid_type | FIXED_STRING(10) | + When try to execute query: + """ + INSERT VERTEX invalid_vertex VALUES "non_existed_tag":() + """ + Then a SemanticError should be raised at runtime: No schema found diff --git a/tests/tck/features/insert/Insert.feature b/tests/tck/features/insert/Insert.feature index 746922951d5..2a3785cec9f 100644 --- a/tests/tck/features/insert/Insert.feature +++ b/tests/tck/features/insert/Insert.feature @@ -95,7 +95,7 @@ Feature: Insert string vid of vertex and edge INSERT EDGE schoolmate(likeness, nickname) VALUES "Tom"->"Lucy":(85, "Lily") """ Then the execution should be successful - # insert vertex wrong type + # insert edge wrong type When executing query: """ INSERT EDGE schoolmate(likeness, nickname) VALUES "Laura"->"Amber":("87", ""); @@ -224,6 +224,34 @@ Feature: Insert string vid of vertex and edge "Amber":("Amber", 9, "four", 20180901003); """ Then the execution should be successful + When executing query: + """ + FETCH PROP ON person "Laura" YIELD person.name, person.age + """ + Then the result should be, in any order: + | person.name | person.age | + | 'Laura' | 8 | + When executing query: + """ + FETCH PROP ON student "Laura" YIELD student.grade, student.number + """ + Then the result should be, in any order: + | student.grade | student.number | + | 'three' | 20190901008 | + When executing query: + """ + FETCH PROP ON person "Amber" YIELD person.name, person.age + """ + Then the result should be, in any order: + | person.name | person.age | + | 'Amber' | 9 | + When executing query: + """ + FETCH PROP ON student "Amber" YIELD student.grade, student.number + """ + Then the result should be, in any order: + | student.grade | student.number | + | 'four' | 20180901003 | # insert multi vertex one tag When executing query: """ @@ -506,6 +534,22 @@ Feature: Insert string vid of vertex and edge Then the result should be, in any order, with relax comparison: | node | | ('English') | + # insert Chinese character + # if the Chinese character is out of the fixed_string's size, it will be truncated, + # and no invalid char should be inserted + When executing query: + """ + INSERT VERTEX course(name) VALUES "Chinese":("中文字符") + """ + Then the execution should be successful + # check result + When executing query: + """ + FETCH PROP ON course "Chinese" YIELD vertex as node + """ + Then the result should be, in any order, with relax comparison: + | node | + | ('Chinese') | # check result When executing query: """ @@ -523,6 +567,7 @@ Feature: Insert string vid of vertex and edge | course.name | course.introduce | | 'Engli' | NULL | | 'Math' | NULL | + | '中' | NULL | When executing query: """ LOOKUP ON student YIELD student.name, student.age @@ -630,3 +675,15 @@ Feature: Insert string vid of vertex and edge """ Then a ExecutionError should be raised at runtime: Storage Error: The VID must be a 64-bit integer or a string fitting space vertex id length limit. Then drop the used space + + Scenario: insert vertex with non existent tag + Given an empty graph + And create a space with following options: + | partition_num | 1 | + | replica_factor | 1 | + | vid_type | FIXED_STRING(10) | + When try to execute query: + """ + INSERT VERTEX invalid_vertex VALUES "non_existed_tag":() + """ + Then a SemanticError should be raised at runtime: No schema found diff --git a/tests/tck/features/insert/InsertEdgeOnDiffParts.feature b/tests/tck/features/insert/InsertEdgeOnDiffParts.feature index 9c33e3f058e..d1079a47700 100644 --- a/tests/tck/features/insert/InsertEdgeOnDiffParts.feature +++ b/tests/tck/features/insert/InsertEdgeOnDiffParts.feature @@ -1,7 +1,7 @@ # Copyright (c) 2021 vesoft inc. All rights reserved. # # This source code is licensed under Apache 2.0 License. -Feature: Insert vertex and edge with if not exists +Feature: Insert edge on different parts Scenario: insert edge with default value Given an empty graph diff --git a/tests/tck/features/match/Base.IntVid.feature b/tests/tck/features/match/Base.IntVid.feature index 098a3ff22c5..c045f105835 100644 --- a/tests/tck/features/match/Base.IntVid.feature +++ b/tests/tck/features/match/Base.IntVid.feature @@ -542,34 +542,126 @@ Feature: Basic match """ Then a ExecutionError should be raised at runtime: Scan vertices or edges need to specify a limit number, or limit number can not push down. - Scenario: can't get property of vertex + Scenario: Get property or tag from a vertex When executing query: """ - MATCH (v:player{name:"Tim Duncan"}) return v.name + MATCH (v:player{name:"Tim Duncan"}) RETURN v.name AS vname """ - Then a SemanticError should be raised at runtime: To get the property of the vertex in `v.name', should use the format `var.tag.prop' + Then the result should be, in any order: + | vname | + | NULL | + When executing query: + """ + MATCH (v:player{name:"Tim Duncan"}) RETURN v.player AS vtag + """ + Then the result should be, in any order: + | vtag | + | {name:"Tim Duncan"} | When executing query: """ - MATCH (v:player)-[]->(b) WHERE v.age > 30 return v.player.name + MATCH (v:player)-[]->(b) WHERE v.age > 30 RETURN v.player.name AS vname """ - Then a SemanticError should be raised at runtime: To get the property of the vertex in `v.age', should use the format `var.tag.prop' + Then the result should be, in any order: + | vname | When executing query: """ - MATCH (v:player)-[:like]->(b) WHERE v.player.age > 30 return v.player.name, b.age + MATCH (v:player)-[:like]->(b) WHERE v.player.age > 30 WITH v.player.name AS vname, b.age AS bage RETURN vname, bage """ - Then a SemanticError should be raised at runtime: To get the property of the vertex in `b.age', should use the format `var.tag.prop' + Then the result should be, in any order: + | vname | bage | + | "Rajon Rondo" | NULL | + | "Manu Ginobili" | NULL | + | "Tracy McGrady" | NULL | + | "Tracy McGrady" | NULL | + | "Tracy McGrady" | NULL | + | "Tim Duncan" | NULL | + | "Tim Duncan" | NULL | + | "Chris Paul" | NULL | + | "Chris Paul" | NULL | + | "Chris Paul" | NULL | + | "Rudy Gay" | NULL | + | "Paul Gasol" | NULL | + | "Paul Gasol" | NULL | + | "Boris Diaw" | NULL | + | "Boris Diaw" | NULL | + | "Aron Baynes" | NULL | + | "Carmelo Anthony" | NULL | + | "Carmelo Anthony" | NULL | + | "Carmelo Anthony" | NULL | + | "Danny Green" | NULL | + | "Danny Green" | NULL | + | "Danny Green" | NULL | + | "Vince Carter" | NULL | + | "Vince Carter" | NULL | + | "Jason Kidd" | NULL | + | "Jason Kidd" | NULL | + | "Jason Kidd" | NULL | + | "Marc Gasol" | NULL | + | "Grant Hill" | NULL | + | "Steve Nash" | NULL | + | "Steve Nash" | NULL | + | "Steve Nash" | NULL | + | "Steve Nash" | NULL | + | "Tony Parker" | NULL | + | "Tony Parker" | NULL | + | "Tony Parker" | NULL | + | "Marco Belinelli" | NULL | + | "Marco Belinelli" | NULL | + | "Marco Belinelli" | NULL | + | "Yao Ming" | NULL | + | "Yao Ming" | NULL | + | "Dirk Nowitzki" | NULL | + | "Dirk Nowitzki" | NULL | + | "Dirk Nowitzki" | NULL | + | "Shaquille O'Neal" | NULL | + | "Shaquille O'Neal" | NULL | + | "LaMarcus Aldridge" | NULL | + | "LaMarcus Aldridge" | NULL | + | "Tiago Splitter" | NULL | + | "Tiago Splitter" | NULL | + | "Ray Allen" | NULL | + | "LeBron James" | NULL | + | "Amar'e Stoudemire" | NULL | + | "Dwyane Wade" | NULL | + | "Dwyane Wade" | NULL | + | "Dwyane Wade" | NULL | + When executing query: + """ + MATCH (:player{name:"Tony Parker"})-[r]->(m) where exists(m.age) RETURN r + """ + Then the result should be, in any order: + | r | When executing query: """ - MATCH (:player{name:"Tony Parker"})-[r]->(m) where exists(m.age) return r + MATCH (:player{name:"Tony Parker"})-[r]->(m) where exists(m.player.age) RETURN r """ - Then a SemanticError should be raised at runtime: To get the property of the vertex in `m.age', should use the format `var.tag.prop' + Then the result should be, in any order, with relax comparison: + | r | + | [:teammate "Tony Parker"->"Kyle Anderson" @0 {end_year: 2016, start_year: 2014}] | + | [:teammate "Tony Parker"->"LaMarcus Aldridge" @0 {end_year: 2018, start_year: 2015}] | + | [:teammate "Tony Parker"->"Manu Ginobili" @0 {end_year: 2018, start_year: 2002}] | + | [:teammate "Tony Parker"->"Tim Duncan" @0 {end_year: 2016, start_year: 2001}] | + | [:like "Tony Parker"->"Tim Duncan" @0 {likeness: 95}] | + | [:like "Tony Parker"->"Manu Ginobili" @0 {likeness: 95}] | + | [:like "Tony Parker"->"LaMarcus Aldridge" @0 {likeness: 90}] | When executing query: """ MATCH (v :player{name:"Tim Duncan"})-[]-(v2)-[]-(v3) WITH v3.name as names - RETURN count(names) + RETURN count(names) AS c + """ + Then the result should be, in any order: + | c | + | 0 | + When executing query: + """ + MATCH (v:player{name:"Tim Duncan"})-[]-(v2)-[]-(v3) + WITH v3.player.name as names + RETURN count(distinct names) AS c """ - Then a SemanticError should be raised at runtime: To get the property of the vertex in `v3.name', should use the format `var.tag.prop' + Then the result should be, in any order: + | c | + | 25 | Scenario: filter is not a valid expression When executing query: diff --git a/tests/tck/features/match/Base.feature b/tests/tck/features/match/Base.feature index a1f6f29c096..3f93d4be2c6 100644 --- a/tests/tck/features/match/Base.feature +++ b/tests/tck/features/match/Base.feature @@ -1,7 +1,6 @@ # Copyright (c) 2020 vesoft inc. All rights reserved. # # This source code is licensed under Apache 2.0 License. -@jie Feature: Basic match Background: @@ -50,7 +49,7 @@ Feature: Basic match | 38 | When executing query: """ - MATCH (v:player {age: 29}) return v.player.name AS Name + MATCH (v:player {age: 29}) RETURN v.player.name AS Name """ Then the result should be, in any order: | Name | @@ -60,7 +59,7 @@ Feature: Basic match | 'Dejounte Murray' | When executing query: """ - MATCH (v:player {age: 29}) WHERE v.player.name STARTS WITH "J" return v.player.name AS Name + MATCH (v:player {age: 29}) WHERE v.player.name STARTS WITH "J" RETURN v.player.name AS Name """ Then the result should be, in any order: | Name | @@ -68,7 +67,7 @@ Feature: Basic match | 'Jonathon Simmons' | When executing query: """ - MATCH (v:player) WHERE v.player.age >= 38 AND v.player.age < 45 return v.player.name AS Name, v.player.age AS Age + MATCH (v:player) WHERE v.player.age >= 38 AND v.player.age < 45 RETURN v.player.name AS Name, v.player.age AS Age """ Then the result should be, in any order: | Name | Age | @@ -112,12 +111,12 @@ Feature: Basic match | ("Yao Ming" :player{age: 38, name: "Yao Ming"}) | 138 | When executing query: """ - MATCH (v:player) where v.player.age > 9223372036854775807+1 return v + MATCH (v:player) where v.player.age > 9223372036854775807+1 RETURN v """ Then a SemanticError should be raised at runtime: result of (9223372036854775807+1) cannot be represented as an integer When executing query: """ - MATCH (v:player) where v.player.age > -9223372036854775808-1 return v + MATCH (v:player) where v.player.age > -9223372036854775808-1 RETURN v """ Then a SemanticError should be raised at runtime: result of (-9223372036854775808-1) cannot be represented as an integer @@ -605,21 +604,21 @@ Feature: Basic match Scenario: Return path When executing query: """ - MATCH p = (n:player{name:"Tony Parker"}) return p,n + MATCH p = (n:player{name:"Tony Parker"}) RETURN p,n """ Then the result should be, in any order, with relax comparison: | p | n | | <("Tony Parker")> | ("Tony Parker") | When executing query: """ - MATCH p = (n:player{name:"LeBron James"})-[:like]->(m) return p, n.player.name, m.player.name + MATCH p = (n:player{name:"LeBron James"})-[:like]->(m) RETURN p, n.player.name, m.player.name """ Then the result should be, in any order, with relax comparison: | p | n.player.name | m.player.name | | <("LeBron James")-[:like@0]->("Ray Allen")> | "LeBron James" | "Ray Allen" | When executing query: """ - MATCH p = (n:player{name:"LeBron James"})<-[:like]-(m) return p, n.player.name, m.player.name + MATCH p = (n:player{name:"LeBron James"})<-[:like]-(m) RETURN p, n.player.name, m.player.name """ Then the result should be, in any order, with relax comparison: | p | n.player.name | m.player.name | @@ -631,7 +630,7 @@ Feature: Basic match | <("LeBron James")<-[:like@0]-("Kyrie Irving")> | "LeBron James" | "Kyrie Irving" | When executing query: """ - MATCH p = (n:player{name:"LeBron James"})-[:like]-(m) return p, n.player.name, m.player.name + MATCH p = (n:player{name:"LeBron James"})-[:like]-(m) RETURN p, n.player.name, m.player.name """ Then the result should be, in any order, with relax comparison: | p | n.player.name | m.player.name | @@ -644,7 +643,7 @@ Feature: Basic match | <("LeBron James")-[:like@0]->("Ray Allen")> | "LeBron James" | "Ray Allen" | When executing query: """ - MATCH p = (n:player{name:"LeBron James"})-[:like]->(m)-[:like]->(k) return p, n.player.name, m.player.name, k.player.name + MATCH p = (n:player{name:"LeBron James"})-[:like]->(m)-[:like]->(k) RETURN p, n.player.name, m.player.name, k.player.name """ Then the result should be, in any order, with relax comparison: | p | n.player.name | m.player.name | k.player.name | @@ -679,28 +678,28 @@ Feature: Basic match Then the result should be, in any order, with relax comparison: | p | - @skip - Scenario: Unsupported combination of some cypher clauses + Scenario: Combination of some cypher clauses When executing query: """ - MATCH (v:player) MATCH (t:team) RETURN v, t + UNWIND ["Tony Parker", "Tim Duncan", "Yao Ming"] AS a MATCH (v:player) WHERE v.player.name == a RETURN distinct a, v """ - Then a SemanticError should be raised at runtime: Match clause is not supported to be followed by other cypher clauses - When executing query: - """ - UNWIND ["Tony Parker", "Tim Duncan", "Yao Ming"] AS a MATCH (v:player) RETURN a, v - """ - Then a SemanticError should be raised at runtime: Match clause is not supported to be followed by other cypher clauses + Then the result should be, in any order, with relax comparison: + | a | v | + | "Yao Ming" | ("Yao Ming" :player{age: 38, name: "Yao Ming"}) | + | "Tim Duncan" | ("Tim Duncan" :player{age: 42, name: "Tim Duncan"} :bachelor{name: "Tim Duncan", speciality: "psychology"}) | + | "Tony Parker" | ("Tony Parker" :player{age: 36, name: "Tony Parker"}) | When executing query: """ - WITH "Tony Parker" AS a MATCH (v:player) RETURN a, v + WITH "Tony Parker" AS a MATCH (v:player) WHERE v.player.name == a RETURN a, v """ - Then a SemanticError should be raised at runtime: Match clause is not supported to be followed by other cypher clauses + Then the result should be, in any order, with relax comparison: + | a | v | + | "Tony Parker" | ("Tony Parker" :player{age: 36, name: "Tony Parker"}) | Scenario: exists When executing query: """ - match (:player{name:"Tony Parker"})-[r]->() where exists(r.likeness) return r, exists({a:12}.a) + match (:player{name:"Tony Parker"})-[r]->() where exists(r.likeness) RETURN r, exists({a:12}.a) """ Then the result should be, in any order, with relax comparison: | r | exists({a:12}.a) | @@ -709,13 +708,13 @@ Feature: Basic match | [:like "Tony Parker"->"Tim Duncan" @0 {likeness: 95}] | true | When executing query: """ - match (:player{name:"Tony Parker"})-[r]->(m) where exists(m.player.likeness) return r, exists({a:12}.a) + match (:player{name:"Tony Parker"})-[r]->(m) where exists(m.player.likeness) RETURN r, exists({a:12}.a) """ Then the result should be, in any order, with relax comparison: | r | exists({a:12}.a) | When executing query: """ - match (:player{name:"Tony Parker"})-[r]->(m) where exists({abc:123}.abc) return r + match (:player{name:"Tony Parker"})-[r]->(m) where exists({abc:123}.abc) RETURN r """ Then the result should be, in any order, with relax comparison: | r | @@ -730,13 +729,13 @@ Feature: Basic match | [:serve "Tony Parker"->"Spurs" @0 {end_year: 2018, start_year: 1999}] | When executing query: """ - match (:player{name:"Tony Parker"})-[r]->(m) where exists({abc:123}.ab) return r + match (:player{name:"Tony Parker"})-[r]->(m) where exists({abc:123}.ab) RETURN r """ Then the result should be, in any order, with relax comparison: | r | When executing query: """ - match (:player{name:"Tony Parker"})-[r]->(m) where exists(m.player.age) return r + match (:player{name:"Tony Parker"})-[r]->(m) where exists(m.player.age) RETURN r """ Then the result should be, in any order, with relax comparison: | r | @@ -751,28 +750,28 @@ Feature: Basic match Scenario: filter evaluable When executing query: """ - match (v:player{age: -1}) return v + match (v:player{age: -1}) RETURN v """ Then the result should be, in any order, with relax comparison: | v | | ("Null1" :player{age: -1, name: NULL}) | When executing query: """ - match (v:player{age: +20}) return v + match (v:player{age: +20}) RETURN v """ Then the result should be, in any order, with relax comparison: | v | | ("Luka Doncic" :player{age: 20, name: "Luka Doncic"}) | When executing query: """ - match (v:player{age: 1+19}) return v + match (v:player{age: 1+19}) RETURN v """ Then the result should be, in any order, with relax comparison: | v | | ("Luka Doncic" :player{age: 20, name: "Luka Doncic"}) | When executing query: """ - match (v:player)-[e:like{likeness:-1}]->() return e + match (v:player)-[e:like{likeness:-1}]->() RETURN e """ Then the result should be, in any order, with relax comparison: | e | @@ -780,7 +779,7 @@ Feature: Basic match | [:like "Rajon Rondo"->"Ray Allen" @0 {likeness: -1}] | When executing query: """ - match (v:player)-[e:like{likeness:40+50+5}]->() return e + match (v:player)-[e:like{likeness:40+50+5}]->() RETURN e """ Then the result should be, in any order, with relax comparison: | e | @@ -791,7 +790,7 @@ Feature: Basic match | [:like "Tony Parker"->"Tim Duncan" @0 {likeness: 95}] | When executing query: """ - match (v:player)-[e:like{likeness:4*20+5}]->() return e + match (v:player)-[e:like{likeness:4*20+5}]->() RETURN e """ Then the result should be, in any order, with relax comparison: | e | @@ -799,17 +798,17 @@ Feature: Basic match | [:like "Steve Nash"->"Jason Kidd"@0{likeness:85}] | When executing query: """ - match (v:player)-[e:like{likeness:"99"}]->() return e + match (v:player)-[e:like{likeness:"99"}]->() RETURN e """ Then the result should be, in any order, with relax comparison: | e | When executing query: """ - match (v:player{age:"24"-1}) return v + match (v:player{age:"24"-1}) RETURN v """ Then a SemanticError should be raised at runtime: Type error `("24"-1)' - Scenario: No return + Scenario: No RETURN When executing query: """ MATCH (v:player{name:"abc"}) @@ -817,14 +816,14 @@ Feature: Basic match Then a SyntaxError should be raised at runtime: syntax error near `)' When executing query: """ - MATCH (v:player) where v.player.name return v + MATCH (v:player) where v.player.name RETURN v """ Then a ExecutionError should be raised at runtime: Failed to evaluate condition: v.player.name. For boolean conditions, please write in their full forms like == or IS [NOT] NULL. Scenario: Unimplemented features When executing query: """ - MATCH (v) return v + MATCH (v) RETURN v """ Then a ExecutionError should be raised at runtime: Scan vertices or edges need to specify a limit number, or limit number can not push down. When executing query: @@ -845,12 +844,12 @@ Feature: Basic match Then a ExecutionError should be raised at runtime: Scan vertices or edges need to specify a limit number, or limit number can not push down. When executing query: """ - MATCH () -[]-> (v) return * + MATCH () -[]-> (v) RETURN * """ Then a ExecutionError should be raised at runtime: Scan vertices or edges need to specify a limit number, or limit number can not push down. When executing query: """ - MATCH () --> (v) --> () return * + MATCH () --> (v) --> () RETURN * """ Then a ExecutionError should be raised at runtime: Scan vertices or edges need to specify a limit number, or limit number can not push down. # The 0 step means node scan in fact, but p and t has no label or properties for index seek @@ -861,34 +860,152 @@ Feature: Basic match """ Then a ExecutionError should be raised at runtime: Scan vertices or edges need to specify a limit number, or limit number can not push down. - Scenario: can't get property of vertex + Scenario: Get property or tag from a vertex When executing query: """ - MATCH (v:player{name:"Tim Duncan"}) return v.name + MATCH (v:player{name:"Tim Duncan"}) RETURN v.name AS vname """ - Then a SemanticError should be raised at runtime: To get the property of the vertex in `v.name', should use the format `var.tag.prop' + Then the result should be, in any order: + | vname | + | NULL | When executing query: """ - MATCH (v:player)-[]->(b) WHERE v.age > 30 return v.player.name + MATCH (v:player{name:"Tim Duncan"}) RETURN v.player AS vtag """ - Then a SemanticError should be raised at runtime: To get the property of the vertex in `v.age', should use the format `var.tag.prop' + Then the result should be, in any order: + | vtag | + | {name:"Tim Duncan"} | When executing query: """ - MATCH (v:player)-[:like]->(b) WHERE v.player.age > 30 return v.player.name, b.age + MATCH (v:player)-[]->(b) WHERE v.age > 30 RETURN v.player.name AS vname """ - Then a SemanticError should be raised at runtime: To get the property of the vertex in `b.age', should use the format `var.tag.prop' + Then the result should be, in any order: + | vname | When executing query: """ - MATCH (:player{name:"Tony Parker"})-[r]->(m) where exists(m.age) return r + MATCH (v:player)-[:like]->(b) WHERE v.player.age > 30 WITH v.player.name AS vname, b.age AS bage RETURN vname, bage + """ + Then the result should be, in any order: + | vname | bage | + | "Rajon Rondo" | NULL | + | "Manu Ginobili" | NULL | + | "Tracy McGrady" | NULL | + | "Tracy McGrady" | NULL | + | "Tracy McGrady" | NULL | + | "Tim Duncan" | NULL | + | "Tim Duncan" | NULL | + | "Chris Paul" | NULL | + | "Chris Paul" | NULL | + | "Chris Paul" | NULL | + | "Rudy Gay" | NULL | + | "Paul Gasol" | NULL | + | "Paul Gasol" | NULL | + | "Boris Diaw" | NULL | + | "Boris Diaw" | NULL | + | "Aron Baynes" | NULL | + | "Carmelo Anthony" | NULL | + | "Carmelo Anthony" | NULL | + | "Carmelo Anthony" | NULL | + | "Danny Green" | NULL | + | "Danny Green" | NULL | + | "Danny Green" | NULL | + | "Vince Carter" | NULL | + | "Vince Carter" | NULL | + | "Jason Kidd" | NULL | + | "Jason Kidd" | NULL | + | "Jason Kidd" | NULL | + | "Marc Gasol" | NULL | + | "Grant Hill" | NULL | + | "Steve Nash" | NULL | + | "Steve Nash" | NULL | + | "Steve Nash" | NULL | + | "Steve Nash" | NULL | + | "Tony Parker" | NULL | + | "Tony Parker" | NULL | + | "Tony Parker" | NULL | + | "Marco Belinelli" | NULL | + | "Marco Belinelli" | NULL | + | "Marco Belinelli" | NULL | + | "Yao Ming" | NULL | + | "Yao Ming" | NULL | + | "Dirk Nowitzki" | NULL | + | "Dirk Nowitzki" | NULL | + | "Dirk Nowitzki" | NULL | + | "Shaquille O'Neal" | NULL | + | "Shaquille O'Neal" | NULL | + | "LaMarcus Aldridge" | NULL | + | "LaMarcus Aldridge" | NULL | + | "Tiago Splitter" | NULL | + | "Tiago Splitter" | NULL | + | "Ray Allen" | NULL | + | "LeBron James" | NULL | + | "Amar'e Stoudemire" | NULL | + | "Dwyane Wade" | NULL | + | "Dwyane Wade" | NULL | + | "Dwyane Wade" | NULL | + When executing query: + """ + MATCH (:player{name:"Tony Parker"})-[r]->(m) where exists(m.age) RETURN r """ - Then a SemanticError should be raised at runtime: To get the property of the vertex in `m.age', should use the format `var.tag.prop' + Then the result should be, in any order: + | r | + When executing query: + """ + MATCH (:player{name:"Tony Parker"})-[r]->(m) where exists(m.player.age) RETURN r + """ + Then the result should be, in any order, with relax comparison: + | r | + | [:teammate "Tony Parker"->"Kyle Anderson" @0 {end_year: 2016, start_year: 2014}] | + | [:teammate "Tony Parker"->"LaMarcus Aldridge" @0 {end_year: 2018, start_year: 2015}] | + | [:teammate "Tony Parker"->"Manu Ginobili" @0 {end_year: 2018, start_year: 2002}] | + | [:teammate "Tony Parker"->"Tim Duncan" @0 {end_year: 2016, start_year: 2001}] | + | [:like "Tony Parker"->"Tim Duncan" @0 {likeness: 95}] | + | [:like "Tony Parker"->"Manu Ginobili" @0 {likeness: 95}] | + | [:like "Tony Parker"->"LaMarcus Aldridge" @0 {likeness: 90}] | When executing query: """ MATCH (v :player{name:"Tim Duncan"})-[]-(v2)-[]-(v3) WITH v3.name as names - RETURN count(names) + RETURN count(names) AS c + """ + Then the result should be, in any order: + | c | + | 0 | + When executing query: """ - Then a SemanticError should be raised at runtime: To get the property of the vertex in `v3.name', should use the format `var.tag.prop' + MATCH (v:player{name:"Tim Duncan"})-[]-(v2)-[]-(v3) + WITH v3.player.name as names + RETURN count(distinct names) AS c + """ + Then the result should be, in any order: + | c | + | 25 | + When executing query: + """ + MATCH (v:player{name:"Tim Duncan"})-[]-(v2)-[]-(v3) + WITH {a : v, b: v3} AS m + RETURN distinct m.a.player.age AS vage, m.b.team AS bteam + """ + Then the result should be, in any order, with relax comparison: + | vage | bteam | + | 42 | NULL | + | 42 | {name: "Suns"} | + | 42 | {name: "Magic"} | + | 42 | {name: "Spurs"} | + | 42 | {name: "Lakers"} | + | 42 | {name: "Heat"} | + | 42 | {name: "Celtics"} | + | 42 | {name: "Cavaliers"} | + | 42 | {name: "Hornets"} | + | 42 | {name: "Warriors"} | + | 42 | {name: "Raptors"} | + | 42 | {name: "Kings"} | + | 42 | {name: "Hawks"} | + | 42 | {name: "Bulls"} | + | 42 | {name: "76ers"} | + | 42 | {name: "Jazz"} | + | 42 | {name: "Trail Blazers"} | + | 42 | {name: "Pistons"} | Scenario: filter is not a valid expression When executing query: @@ -945,7 +1062,7 @@ Feature: Basic match Then the execution should be successful When executing query: """ - MATCH (v) WHERE id(v) in ["v1", "v2", "v3", "v4"] return id(v) limit 10; + MATCH (v) WHERE id(v) in ["v1", "v2", "v3", "v4"] RETURN id(v) limit 10; """ Then the result should be, in any order, with relax comparison: | id(v) | @@ -961,7 +1078,7 @@ Feature: Basic match And wait 5 seconds When executing query: """ - MATCH (v) WHERE id(v) in ["v1", "v2", "v3", "v4"] return id(v) limit 10; + MATCH (v) WHERE id(v) in ["v1", "v2", "v3", "v4"] RETURN id(v) limit 10; """ Then the result should be, in any order, with relax comparison: | id(v) | @@ -972,7 +1089,7 @@ Feature: Basic match match (v)-[e:like]->() where id(v) == "Tim Duncan" and rank(e) == 0 - return * + RETURN * """ Then the result should be, in any order: | v | e | @@ -983,7 +1100,7 @@ Feature: Basic match match (v)-[e:like]->() where id(v) == "Tim Duncan" and rank(e) != 0 - return * + RETURN * """ Then the result should be, in any order: | v | e | @@ -994,7 +1111,7 @@ Feature: Basic match match (v)-[e:like]->() where id(v) == "Tim Duncan" and rank(e) == 0 - return * + RETURN * """ Then the result should be, in any order, with relax comparison: | v | e | @@ -1004,7 +1121,7 @@ Feature: Basic match """ match ()-[e]->() where rank(e) == 0 - return rank(e) + RETURN rank(e) limit 3 """ Then the result should be, in any order, with relax comparison: @@ -1016,7 +1133,7 @@ Feature: Basic match """ match ()-[e]->() where rank(e) != 0 - return rank(e) + RETURN rank(e) limit 1000 """ Then the result should be, in any order, with relax comparison: @@ -1031,7 +1148,7 @@ Feature: Basic match """ match ()-[e]->() where abs(rank(e)) != 0 and e.start_year > 2010 - return rank(e) + RETURN rank(e) limit 1000 """ Then the result should be, in any order, with relax comparison: @@ -1044,7 +1161,7 @@ Feature: Basic match """ match ()-[e]->() where abs(rank(e)) == 1 and e.start_year == 2016 - return e + RETURN e limit 1000 """ Then the result should be, in any order, with relax comparison: @@ -1054,7 +1171,7 @@ Feature: Basic match """ match ()-[e]->() where src(e) != "jack" - return rank(e) + RETURN rank(e) limit 10000 """ Then an ExecutionError should be raised at runtime: Scan vertices or edges need to specify a limit number, or limit number can not push down. @@ -1062,7 +1179,7 @@ Feature: Basic match """ match ()-[e]->() where src(e) != 0 or abs(rank(e)) != 0 - return rank(e) + RETURN rank(e) limit 10000 """ Then an ExecutionError should be raised at runtime: Scan vertices or edges need to specify a limit number, or limit number can not push down. @@ -1070,27 +1187,82 @@ Feature: Basic match Scenario: match_with_wrong_syntax When executing query: """ - MATCH (v{name: "Tim Duncan"}) return v + MATCH (v{name: "Tim Duncan"}) RETURN v """ Then a SemanticError should be raised at runtime: `name:"Tim Duncan"': No tag found for property. + When executing query: + """ + MATCH (v:player) RETURN $$.player.age AS a + """ + Then a SemanticError should be raised at runtime: Expression $$.player.age is not allowed to use in cypher + When executing query: + """ + MATCH (v:player) RETURN $^.player.age AS a + """ + Then a SemanticError should be raised at runtime: Expression $^.player.age is not allowed to use in cypher + When executing query: + """ + MATCH (v:player) RETURN $-.player.age AS a + """ + Then a SemanticError should be raised at runtime: `$-.player', not exist prop `player' + When executing query: + """ + MATCH (v:player) WHERE $var.player > 0 RETURN v + """ + Then a SemanticError should be raised at runtime: `$var.player', not exist variable `var' + When executing query: + """ + MATCH (v:player) WHERE $$.player.age > 0 RETURN v + """ + Then a SemanticError should be raised at runtime: Expression $$.player.age is not allowed to use in cypher + When executing query: + """ + MATCH (v:player) WHERE $^.player.age > 0 RETURN v + """ + Then a SemanticError should be raised at runtime: Expression $^.player.age is not allowed to use in cypher + When executing query: + """ + MATCH (v:player) WHERE $-.player.age > 0 RETURN v + """ + Then a SemanticError should be raised at runtime: `$-.player', not exist prop `player' + When executing query: + """ + MATCH (v:player) WHERE $var.player > 0 RETURN v + """ + Then a SemanticError should be raised at runtime: `$var.player', not exist variable `var' + When executing query: + """ + MATCH (v:player) WITH {k: $^} AS x RETUR x.k.player.name + """ + Then a SyntaxError should be raised at runtime: syntax error near `} AS x R' + When executing query: + """ + MATCH (v:player) WITH {k: $^.player.age} AS x RETURN x.k + """ + Then a SemanticError should be raised at runtime: Expression $^.player.age is not allowed to use in cypher + When executing query: + """ + MATCH (v:player) WITH {k: $var} AS x RETUR x.k.player.name + """ + Then a SyntaxError should be raised at runtime: Direct output of variable is prohibited near `{k: $var}' Scenario: match with tag filter When executing query: """ - MATCH (a:team)-[e*0..1]-(b) where id(a) == 'Tim Duncan' return b + MATCH (a:team)-[e*0..1]-(b) where id(a) == 'Tim Duncan' RETURN b """ Then the result should be, in any order, with relax comparison: | b | When executing query: """ - MATCH (a:team)-[e*0..0]-(b) where id(a) in ['Tim Duncan', 'Spurs'] return b + MATCH (a:team)-[e*0..0]-(b) where id(a) in ['Tim Duncan', 'Spurs'] RETURN b """ Then the result should be, in any order, with relax comparison: | b | | ('Spurs') | When executing query: """ - MATCH (a:team)-[e*0..1]-(b) where id(a) in ['Tim Duncan', 'Spurs'] return b + MATCH (a:team)-[e*0..1]-(b) where id(a) in ['Tim Duncan', 'Spurs'] RETURN b """ Then the result should be, in any order, with relax comparison: | b | diff --git a/tests/tck/features/match/MatchById.feature b/tests/tck/features/match/MatchById.feature index c679c7d6486..f53e3ed01b4 100644 --- a/tests/tck/features/match/MatchById.feature +++ b/tests/tck/features/match/MatchById.feature @@ -1031,7 +1031,7 @@ Feature: Match By Id """ OPTIONAL MATCH (n) OPTIONAL MATCH (n) WHERE id(n) == 'James Harden' RETURN n """ - Then a ExecutionError should be raised at runtime: Scan vertices or edges need to specify a limit number, or limit number can not push down. + Then a SyntaxError should be raised at runtime: Where clause in optional match is not supported. near `WHERE id(n) == 'James Harden'' When executing query: """ MATCH (v1)-[:like]->(v2:player)-[:serve]->(v3) diff --git a/tests/tck/features/match/MultiLineMultiQueryParts.feature b/tests/tck/features/match/MultiLineMultiQueryParts.feature index 7f1c72a40ca..1f0030c2831 100644 --- a/tests/tck/features/match/MultiLineMultiQueryParts.feature +++ b/tests/tck/features/match/MultiLineMultiQueryParts.feature @@ -197,103 +197,63 @@ Feature: Multi Line Multi Query Parts OPTIONAL MATCH (n)-[]->(l) WHERE id(n)=="Tony Parker" RETURN id(m) AS m, id(n) AS n, id(l) AS l; """ - Then the result should be, in any order: - | m | n | l | - | "Tim Duncan" | "Spurs" | NULL | - | "Tim Duncan" | "LaMarcus Aldridge" | NULL | - | "Tim Duncan" | "Tony Parker" | "Spurs" | - | "Tim Duncan" | "Tony Parker" | "LaMarcus Aldridge" | - | "Tim Duncan" | "Tony Parker" | "LaMarcus Aldridge" | - | "Tim Duncan" | "Tony Parker" | "Kyle Anderson" | - | "Tim Duncan" | "Tony Parker" | "Tim Duncan" | - | "Tim Duncan" | "Tony Parker" | "Tim Duncan" | - | "Tim Duncan" | "Tony Parker" | "Manu Ginobili" | - | "Tim Duncan" | "Tony Parker" | "Manu Ginobili" | - | "Tim Duncan" | "Tony Parker" | "Hornets" | - | "Tim Duncan" | "Tony Parker" | "Spurs" | - | "Tim Duncan" | "Tony Parker" | "LaMarcus Aldridge" | - | "Tim Duncan" | "Tony Parker" | "LaMarcus Aldridge" | - | "Tim Duncan" | "Tony Parker" | "Kyle Anderson" | - | "Tim Duncan" | "Tony Parker" | "Tim Duncan" | - | "Tim Duncan" | "Tony Parker" | "Tim Duncan" | - | "Tim Duncan" | "Tony Parker" | "Manu Ginobili" | - | "Tim Duncan" | "Tony Parker" | "Manu Ginobili" | - | "Tim Duncan" | "Tony Parker" | "Hornets" | - | "Tim Duncan" | "Danny Green" | NULL | - | "Tim Duncan" | "Manu Ginobili" | NULL | - | "Tim Duncan" | "Manu Ginobili" | NULL | + Then a SyntaxError should be raised at runtime: Where clause in optional match is not supported. near `WHERE id(n)=="Tony Parker"' When executing query: """ OPTIONAL match (v:player) WHERE v.player.age > 41 MATCH (v:player) WHERE v.player.age>40 RETURN count(*) AS count """ - Then the result should be, in order: - | count | - | 7 | + Then a SyntaxError should be raised at runtime: Where clause in optional match is not supported. near `WHERE v.player.age > 41' When executing query: """ - OPTIONAL match (v:player) WHERE v.player.age>43 + OPTIONAL match (v:player) WHERE v.player.age > 43 MATCH (n:player) WHERE n.player.age>40 RETURN count(*) AS count """ - Then the result should be, in order: - | count | - | 32 | + Then a SyntaxError should be raised at runtime: Where clause in optional match is not supported. near `WHERE v.player.age > 43' When executing query: """ - OPTIONAL MATCH (v:player) WHERE v.player.age > 40 and v.player.age<46 + OPTIONAL MATCH (v:player) WHERE v.player.age > 40 and v.player.age < 46 MATCH (v:player) WHERE v.player.age>43 RETURN count(*) AS count """ - Then the result should be, in any order: - | count | - | 2 | + Then a SyntaxError should be raised at runtime: Where clause in optional match is not supported. near `WHERE v.player.age > 40 and v.player.age < 46' When executing query: """ - MATCH (v:player) WHERE v.player.age > 40 and v.player.age<46 - OPTIONAL MATCH (v:player) WHERE v.player.age>43 + MATCH (v:player) WHERE v.player.age > 40 and v.player.age < 46 + OPTIONAL MATCH (v:player) WHERE v.player.age > 43 RETURN count(*) AS count """ - Then the result should be, in any order: - | count | - | 6 | + Then a SyntaxError should be raised at runtime: Where clause in optional match is not supported. near `WHERE v.player.age > 43' When executing query: """ OPTIONAL MATCH (v:player) WHERE v.player.age > 40 and v.player.age<46 OPTIONAL MATCH (v:player) WHERE v.player.age>43 RETURN count(*) AS count """ - Then the result should be, in any order: - | count | - | 6 | + Then a SyntaxError should be raised at runtime: Where clause in optional match is not supported. near `WHERE v.player.age > 40 and v.player.age<46' When executing query: """ - OPTIONAL MATCH (v:player) WHERE v.player.age>43 + OPTIONAL MATCH (v:player) WHERE v.player.age > 43 MATCH (v:player) WHERE v.player.age > 40 and v.player.age<46 RETURN count(*) AS count """ - Then the result should be, in any order: - | count | - | 2 | + Then a SyntaxError should be raised at runtime: Where clause in optional match is not supported. near `WHERE v.player.age > 43' When executing query: """ - MATCH (v:player) WHERE v.player.age>43 - OPTIONAL MATCH (v:player) WHERE v.player.age > 40 and v.player.age<46 + MATCH (v:player) WHERE v.player.age > 43 + OPTIONAL MATCH (v:player) WHERE v.player.age > 40 and v.player.age < 46 RETURN count(*) AS count """ - Then the result should be, in any order: - | count | - | 4 | + Then a SyntaxError should be raised at runtime: Where clause in optional match is not supported. near `WHERE v.player.age > 40 and v.player.age < 46' When executing query: """ - OPTIONAL MATCH (v:player) WHERE v.player.age>43 - OPTIONAL MATCH (v:player) WHERE v.player.age > 40 and v.player.age<46 + OPTIONAL MATCH (v:player) WHERE v.player.age > 43 + OPTIONAL MATCH (v:player) WHERE v.player.age > 40 and v.player.age < 46 RETURN count(*) AS count """ - Then the result should be, in any order: - | count | - | 4 | + Then a SyntaxError should be raised at runtime: Where clause in optional match is not supported. near `WHERE v.player.age > 43' # When the input of argument is NULL When executing query: """ @@ -370,22 +330,18 @@ Feature: Multi Line Multi Query Parts | 12 | When executing query: """ - OPTIONAL match (v:player) WHERE v.player.age>43 WITH v + OPTIONAL match (v:player) WHERE v.player.age > 43 WITH v MATCH (v:player) WHERE v.player.age>40 WITH v RETURN count(*) AS count """ - Then the result should be, in order: - | count | - | 4 | + Then a SyntaxError should be raised at runtime: Where clause in optional match is not supported. near `WHERE v.player.age > 43' When executing query: """ - OPTIONAL match (v:player) WHERE v.player.age>43 WITH v + OPTIONAL match (v:player) WHERE v.player.age > 43 WITH v MATCH (n:player) WHERE n.player.age>40 WITH v, n RETURN count(*) AS count """ - Then the result should be, in order: - | count | - | 32 | + Then a SyntaxError should be raised at runtime: Where clause in optional match is not supported. near `WHERE v.player.age > 43' When executing query: """ MATCH (a:player{age:42}) WITH a @@ -411,10 +367,10 @@ Feature: Multi Line Multi Query Parts OPTIONAL MATCH (n)-->(v) WHERE v.player.age < m.player.age RETURN n,v """ - Then a SemanticError should be raised at runtime: The where clause of optional match statement that reference variables defined by other statements is not supported yet. + Then a SyntaxError should be raised at runtime: Where clause in optional match is not supported. near `WHERE v.player.age < m.player.age' When executing query: """ MATCH (m)-[]-(n) WHERE id(m)=="Tim Duncan" OPTIONAL MATCH (n)-->(v) WHERE id(v) < id(m) RETURN count(*) AS count """ - Then a SemanticError should be raised at runtime: The where clause of optional match statement that reference variables defined by other statements is not supported yet. + Then a SyntaxError should be raised at runtime: Where clause in optional match is not supported. near `WHERE id(v) < id(m)' diff --git a/tests/tck/features/match/MultiQueryParts.feature b/tests/tck/features/match/MultiQueryParts.feature index e722235b3a8..4388244ba48 100644 --- a/tests/tck/features/match/MultiQueryParts.feature +++ b/tests/tck/features/match/MultiQueryParts.feature @@ -177,36 +177,40 @@ Feature: Multi Query Parts When profiling query: """ MATCH (v1:player)-[:like*2..2]->(v2)-[e3:like]->(v4) where id(v1) == "Tony Parker" - OPTIONAL MATCH (v3:player)-[:like]->(v1)<-[e5]-(v4) where id(v3) == "Tim Duncan" return * + OPTIONAL MATCH (v3:player)-[:like]->(v1)<-[e5]-(v4) + with v1, v2, e3, v4, e5, v3 where id(v3) == "Tim Duncan" or id(v3) is NULL + return * """ Then the result should be, in any order, with relax comparison: - | v1 | v2 | e3 | v4 | v3 | e5 | - | ("Tony Parker") | ("Tony Parker") | [:like "Tony Parker"->"LaMarcus Aldridge" @0] | ("LaMarcus Aldridge") | ("Tim Duncan") | [:like "LaMarcus Aldridge"->"Tony Parker" @0] | - | ("Tony Parker") | ("Tim Duncan") | [:like "Tim Duncan"->"Tony Parker" @0 ] | ("Tony Parker") | NULL | NULL | - | ("Tony Parker") | ("Tim Duncan") | [:like "Tim Duncan"->"Tony Parker" @0] | ("Tony Parker") | NULL | NULL | - | ("Tony Parker") | ("Tony Parker") | [:like "Tony Parker"->"Tim Duncan" @0 ] | ("Tim Duncan") | ("Tim Duncan") | [:teammate "Tim Duncan"->"Tony Parker" @0] | - | ("Tony Parker") | ("Manu Ginobili") | [:like "Manu Ginobili"->"Tim Duncan" @0 ] | ("Tim Duncan") | ("Tim Duncan") | [:teammate "Tim Duncan"->"Tony Parker" @0] | - | ("Tony Parker") | ("Tony Parker") | [:like "Tony Parker"->"Manu Ginobili" @0] | ("Manu Ginobili") | ("Tim Duncan") | [:teammate "Manu Ginobili"->"Tony Parker" @0] | - | ("Tony Parker") | ("Tony Parker") | [:like "Tony Parker"->"Manu Ginobili" @0 ] | ("Manu Ginobili") | ("Tim Duncan") | [:teammate "Manu Ginobili"->"Tony Parker" @0] | - | ("Tony Parker") | ("Tim Duncan") | [:like "Tim Duncan"->"Manu Ginobili" @0 ] | ("Manu Ginobili") | ("Tim Duncan") | [:teammate "Manu Ginobili"->"Tony Parker" @0 ] | - | ("Tony Parker") | ("Tim Duncan") | [:like "Tim Duncan"->"Manu Ginobili" @0] | ("Manu Ginobili") | ("Tim Duncan") | [:teammate "Manu Ginobili"->"Tony Parker" @0] | + | v1 | v2 | e3 | v4 | e5 | v3 | + | ("Tony Parker") | ("Tony Parker") | [:like "Tony Parker"->"LaMarcus Aldridge" @0] | ("LaMarcus Aldridge") | [:like "LaMarcus Aldridge"->"Tony Parker" @0] | ("Tim Duncan") | + | ("Tony Parker") | ("Tim Duncan") | [:like "Tim Duncan"->"Tony Parker" @0 ] | ("Tony Parker") | NULL | NULL | + | ("Tony Parker") | ("Tim Duncan") | [:like "Tim Duncan"->"Tony Parker" @0] | ("Tony Parker") | NULL | NULL | + | ("Tony Parker") | ("Tony Parker") | [:like "Tony Parker"->"Tim Duncan" @0 ] | ("Tim Duncan") | [:teammate "Tim Duncan"->"Tony Parker" @0] | ("Tim Duncan") | + | ("Tony Parker") | ("Manu Ginobili") | [:like "Manu Ginobili"->"Tim Duncan" @0 ] | ("Tim Duncan") | [:teammate "Tim Duncan"->"Tony Parker" @0] | ("Tim Duncan") | + | ("Tony Parker") | ("Tony Parker") | [:like "Tony Parker"->"Manu Ginobili" @0] | ("Manu Ginobili") | [:teammate "Manu Ginobili"->"Tony Parker" @0] | ("Tim Duncan") | + | ("Tony Parker") | ("Tony Parker") | [:like "Tony Parker"->"Manu Ginobili" @0 ] | ("Manu Ginobili") | [:teammate "Manu Ginobili"->"Tony Parker" @0] | ("Tim Duncan") | + | ("Tony Parker") | ("Tim Duncan") | [:like "Tim Duncan"->"Manu Ginobili" @0 ] | ("Manu Ginobili") | [:teammate "Manu Ginobili"->"Tony Parker" @0 ] | ("Tim Duncan") | + | ("Tony Parker") | ("Tim Duncan") | [:like "Tim Duncan"->"Manu Ginobili" @0] | ("Manu Ginobili") | [:teammate "Manu Ginobili"->"Tony Parker" @0] | ("Tim Duncan") | # The redudant Project after HashLeftJoin is removed now And the execution plan should be: | id | name | dependencies | profiling data | operator info | - | 19 | HashLeftJoin | 7,14 | | | - | 7 | Project | 6 | | | + | 22 | Project | 18 | | | + | 18 | Filter | 14 | | | + | 14 | HashLeftJoin | 7,13 | | | + | 7 | project | 6 | | | | 6 | AppendVertices | 5 | | | | 5 | Traverse | 20 | | | | 20 | Traverse | 2 | | | | 2 | Dedup | 1 | | | | 1 | PassThrough | 3 | | | | 3 | Start | | | | - | 14 | Project | 13 | | | - | 13 | Traverse | 21 | | | - | 21 | Traverse | 9 | | | - | 9 | Dedup | 8 | | | - | 8 | PassThrough | 10 | | | - | 10 | Start | | | | + | 13 | Project | 21 | | | + | 21 | AppendVertices | 11 | | | + | 11 | Traverse | 10 | | | + | 10 | AppendVertices | 9 | | | + | 9 | Traverse | 8 | | | + | 8 | Argument | | | | When executing query: """ MATCH (m)-[]-(n) WHERE id(m)=="Tim Duncan" diff --git a/tests/tck/features/match/Path.feature b/tests/tck/features/match/Path.feature index 9f31eca21e5..4e6230cb7e2 100644 --- a/tests/tck/features/match/Path.feature +++ b/tests/tck/features/match/Path.feature @@ -38,6 +38,8 @@ Feature: Matching paths | count(p) | count(p2) | | 966 | 966 | + # The correctness of the following test cases needs to be verified, mark it @skip for now + @skip Scenario: overlapping aliases variables When executing query: """ @@ -323,6 +325,8 @@ Feature: Matching paths | 1 | 1 | 1 | 96 | | 3 | 3 | 3 | 99 | + # The correctness of the following test cases needs to be verified, mark it @skip for now + @skip Scenario: symmetry paths When executing query: """ @@ -360,6 +364,8 @@ Feature: Matching paths | 2 | | 2 | + # The correctness of the following test cases needs to be verified, mark it @skip for now + @skip Scenario: src dst variables in paths When executing query: """ @@ -404,6 +410,8 @@ Feature: Matching paths | count(*) | | 190 | + # The correctness of the following test cases needs to be verified, mark it @skip for now + @skip Scenario: edgelist or single edge in paths When executing query: """ diff --git a/tests/tck/features/match/PathExpr.feature b/tests/tck/features/match/PathExpr.feature index d115285b67d..369db9b86b4 100644 --- a/tests/tck/features/match/PathExpr.feature +++ b/tests/tck/features/match/PathExpr.feature @@ -362,3 +362,88 @@ Feature: Basic match | p | | <("Tim Duncan" :bachelor{name: "Tim Duncan", speciality: "psychology"} :player{age: 42, name: "Tim Duncan"})-[:serve@0 {end_year: 2016, start_year: 1997}]->("Spurs" :team{name: "Spurs"})> | | <("Tim Duncan" :bachelor{name: "Tim Duncan", speciality: "psychology"} :player{age: 42, name: "Tim Duncan"})-[:serve@0 {end_year: 2016, start_year: 1997}]->("Spurs" :team{name: "Spurs"})> | + + Scenario: pattern in where + When executing query: + """ + MATCH (v:player)-[e]->(b) + WHERE id(v) IN ['Tim Duncan', 'Tony Parker'] AND (b)-[*1..2]->(v) + RETURN b + """ + Then the result should be, in any order: + | b | + | ("Danny Green" :player{age: 31, name: "Danny Green"}) | + | ("LaMarcus Aldridge" :player{age: 33, name: "LaMarcus Aldridge"}) | + | ("Manu Ginobili" :player{age: 41, name: "Manu Ginobili"}) | + | ("Tony Parker" :player{age: 36, name: "Tony Parker"}) | + | ("Manu Ginobili" :player{age: 41, name: "Manu Ginobili"}) | + | ("Tony Parker" :player{age: 36, name: "Tony Parker"}) | + | ("LaMarcus Aldridge" :player{age: 33, name: "LaMarcus Aldridge"}) | + | ("Manu Ginobili" :player{age: 41, name: "Manu Ginobili"}) | + | ("Tim Duncan" :player{age: 42, name: "Tim Duncan"} :bachelor{name: "Tim Duncan", speciality: "psychology"}) | + | ("LaMarcus Aldridge" :player{age: 33, name: "LaMarcus Aldridge"}) | + | ("Manu Ginobili" :player{age: 41, name: "Manu Ginobili"}) | + | ("Tim Duncan" :player{age: 42, name: "Tim Duncan"} :bachelor{name: "Tim Duncan", speciality: "psychology"}) | + When executing query: + """ + MATCH (v:player)-[e:like]->(b) + WHERE (b)-[:teammate]->(v) + RETURN e + """ + Then the result should be, in any order: + | e | + | [:like "Tim Duncan"->"Manu Ginobili" @0 {likeness: 95}] | + | [:like "Tim Duncan"->"Tony Parker" @0 {likeness: 95}] | + | [:like "Danny Green"->"Tim Duncan" @0 {likeness: 70}] | + | [:like "Manu Ginobili"->"Tim Duncan" @0 {likeness: 90}] | + | [:like "Tony Parker"->"Manu Ginobili" @0 {likeness: 95}] | + | [:like "Tony Parker"->"Tim Duncan" @0 {likeness: 95}] | + | [:like "LaMarcus Aldridge"->"Tim Duncan" @0 {likeness: 75}] | + | [:like "LaMarcus Aldridge"->"Tony Parker" @0 {likeness: 75}] | + When executing query: + """ + MATCH (v:player)-[e:like*2]->(b) + WHERE id(v) == 'Tony Parker' AND (b)-[*1..2]->(v) + RETURN distinct e + """ + Then the result should be, in any order: + | e | + | [[:like "Tony Parker"->"LaMarcus Aldridge" @0 {likeness: 90}], [:like "LaMarcus Aldridge"->"Tim Duncan" @0 {likeness: 75}]] | + | [[:like "Tony Parker"->"LaMarcus Aldridge" @0 {likeness: 90}], [:like "LaMarcus Aldridge"->"Tony Parker" @0 {likeness: 75}]] | + | [[:like "Tony Parker"->"Manu Ginobili" @0 {likeness: 95}], [:like "Manu Ginobili"->"Tim Duncan" @0 {likeness: 90}]] | + | [[:like "Tony Parker"->"Tim Duncan" @0 {likeness: 95}], [:like "Tim Duncan"->"Manu Ginobili" @0 {likeness: 95}]] | + | [[:like "Tony Parker"->"Tim Duncan" @0 {likeness: 95}], [:like "Tim Duncan"->"Tony Parker" @0 {likeness: 95}]] | + When executing query: + """ + MATCH (v:player{name: 'Tim Duncan'})-[e:like*3]->(n), (t:team {name: "Spurs"}) + WITH v, e, collect(distinct n) AS ns + UNWIND [n in ns | ()-[e*3]->(n:player)] AS p + RETURN p + """ + Then the result should be, in any order: + | p | + | [] | + | [] | + | [] | + | [] | + | [] | + When executing query: + """ + MATCH (v:player)-[e:like*3]->(n) + WHERE (n)-[e*3]->(:player) + RETURN v + """ + Then the result should be, in any order: + | v | + When executing query: + """ + MATCH (v:player)-[e:like*1..3]->(n) WHERE (n)-[e*1..4]->(:player) return v + """ + Then the result should be, in any order: + | v | + When executing query: + """ + MATCH (v:player)-[e:like*3]->(n) WHERE id(v)=="Tim Duncan" and (n)-[e*3]->(:player) return v + """ + Then the result should be, in any order: + | v | diff --git a/tests/tck/features/match/PathExprRefLocalVariable.feature b/tests/tck/features/match/PathExprRefLocalVariable.feature index 02355c3d134..5790242cdd7 100644 --- a/tests/tck/features/match/PathExprRefLocalVariable.feature +++ b/tests/tck/features/match/PathExprRefLocalVariable.feature @@ -123,21 +123,6 @@ Feature: Path expression reference local defined variables | "Rajon Rondo" | | "Ray Allen" | - @skip - Scenario: Fix after issue #2045 - When executing query: - """ - MATCH (v:player)-[e:like*1..3]->(n) WHERE (n)-[e*1..4]->(:player) return v - """ - Then the result should be, in any order: - | v | - When executing query: - """ - MATCH (v:player)-[e:like*3]->(n) WHERE id(v)=="Tim Duncan" and (n)-[e*3]->(:player) return v - """ - Then the result should be, in any order: - | v | - Scenario: In With When executing query: """ diff --git a/tests/tck/features/match/Scan.feature b/tests/tck/features/match/Scan.feature index fc84d2f70f5..b2acd24964d 100644 --- a/tests/tck/features/match/Scan.feature +++ b/tests/tck/features/match/Scan.feature @@ -78,6 +78,67 @@ Feature: Match seek by scan | Name | | "Mary" | + Scenario: query vertices by scan with skip limit + When executing query: + """ + MATCH (v) + RETURN v.person.name AS name + SKIP 10 LIMIT 4 + """ + Then the result should be, in any order: + | name | + | /[\w_]+/ | + | /[\w_]+/ | + | /[\w_]+/ | + | /[\w_]+/ | + When executing query: + """ + MATCH (v) + RETURN v.person.name AS name + SKIP 10 LIMIT 5 + """ + Then the result should be, in any order: + | name | + | /[\w_]+/ | + | /[\w_]+/ | + | /[\w_]+/ | + | /[\w_]+/ | + | /[\w_]+/ | + When executing query: + """ + MATCH (v) + RETURN v.person.name AS name + SKIP 10 LIMIT 7 + """ + Then the result should be, in any order: + | name | + | /[\w_]+/ | + | /[\w_]+/ | + | /[\w_]+/ | + | /[\w_]+/ | + | /[\w_]+/ | + | /[\w_]+/ | + | /[\w_]+/ | + When executing query: + """ + MATCH (v) + RETURN v.person.name AS name + SKIP 10 LIMIT 11 + """ + Then the result should be, in any order: + | name | + | /[\w_]+/ | + | /[\w_]+/ | + | /[\w_]+/ | + | /[\w_]+/ | + | /[\w_]+/ | + | /[\w_]+/ | + | /[\w_]+/ | + | /[\w_]+/ | + | /[\w_]+/ | + | /[\w_]+/ | + | /[\w_]+/ | + Scenario: query vertices by scan failed When executing query: """ @@ -168,3 +229,65 @@ Feature: Match seek by scan LIMIT 3 """ Then a ExecutionError should be raised at runtime: Scan vertices or edges need to specify a limit number, or limit number can not push down. + + # #5223 + Scenario: query edge by scan with skip limit + When executing query: + """ + MATCH ()-[e]->() + RETURN type(e) AS Type + SKIP 10 LIMIT 4 + """ + Then the result should be, in any order: + | Type | + | /[\w_]+/ | + | /[\w_]+/ | + | /[\w_]+/ | + | /[\w_]+/ | + When executing query: + """ + MATCH ()-[e]->() + RETURN type(e) AS Type + SKIP 10 LIMIT 5 + """ + Then the result should be, in any order: + | Type | + | /[\w_]+/ | + | /[\w_]+/ | + | /[\w_]+/ | + | /[\w_]+/ | + | /[\w_]+/ | + When executing query: + """ + MATCH ()-[e]->() + RETURN type(e) AS Type + SKIP 10 LIMIT 7 + """ + Then the result should be, in any order: + | Type | + | /[\w_]+/ | + | /[\w_]+/ | + | /[\w_]+/ | + | /[\w_]+/ | + | /[\w_]+/ | + | /[\w_]+/ | + | /[\w_]+/ | + When executing query: + """ + MATCH ()-[e]->() + RETURN type(e) AS Type + SKIP 10 LIMIT 11 + """ + Then the result should be, in any order: + | Type | + | /[\w_]+/ | + | /[\w_]+/ | + | /[\w_]+/ | + | /[\w_]+/ | + | /[\w_]+/ | + | /[\w_]+/ | + | /[\w_]+/ | + | /[\w_]+/ | + | /[\w_]+/ | + | /[\w_]+/ | + | /[\w_]+/ | diff --git a/tests/tck/features/match/VariableLengthPattern.feature b/tests/tck/features/match/VariableLengthPattern.feature index c3c19133787..5563b220f2c 100644 --- a/tests/tck/features/match/VariableLengthPattern.feature +++ b/tests/tck/features/match/VariableLengthPattern.feature @@ -375,3 +375,144 @@ Feature: Variable length Pattern match (m to n) """ Then the result should be, in any order: | v.player.name | + + Scenario: same src and dst for variable length pattern + When executing query: + """ + MATCH (v)-[e:like*0..0]-(v) + WHERE id(v) == 'Tim Duncan' + RETURN count(*) AS cnt + """ + Then the result should be, in any order: + | cnt | + | 1 | + When executing query: + """ + MATCH (v)-[e:like*0..2]-(v) + WHERE id(v) == 'Tim Duncan' + RETURN count(*) AS cnt + """ + Then the result should be, in any order: + | cnt | + | 5 | + When executing query: + """ + MATCH (v)-[e:like*2..3]-(v) + WHERE id(v) == 'Tim Duncan' + RETURN count(*) AS cnt + """ + Then the result should be, in any order: + | cnt | + | 48 | + When executing query: + """ + MATCH (v)-[e:like*2..3]->(v) + WHERE id(v) == 'Tim Duncan' + RETURN count(*) AS cnt + """ + Then the result should be, in any order: + | cnt | + | 4 | + When executing query: + """ + MATCH (v)-[e:like*0..]->(v) + WHERE id(v) == 'Tim Duncan' + RETURN count(*) AS cnt + """ + Then the result should be, in any order: + | cnt | + | 13 | + + Scenario: variable length pattern and list expression + When executing query: + """ + MATCH (v:player{name: 'Tim Duncan'})-[e:like*0..2]-(v2) + WHERE size([i in e WHERE i.likeness>90 | i])>1 + RETURN count(*) AS cnt + """ + Then the result should be, in any order: + | cnt | + | 18 | + + @skip + # https://github.com/vesoft-inc/nebula/issues/5221 + Scenario: variable scope test in path pattern + When executing query: + """ + MATCH (v:player{name: 'Tim Duncan'})-[e:like*0..2]-(v2) + WHERE size([i in e WHERE (v)-[i]-(v2) | i])>1 + RETURN count(*) AS cnt + """ + Then the result should be, in any order: + | cnt | + When executing query: + """ + MATCH (v:player{name: 'Tim Duncan'})-[e:like*0..2]-(v2)-[i]-(v3) + WHERE size([i in e WHERE (v)-[i]-(v2) | i])>1 + RETURN count(*) AS cnt + """ + Then the result should be, in any order: + | cnt | + When executing query: + """ + MATCH (v:player{name: 'Tim Duncan'})-[e:like*0..2]-(v2)-[i]-(v3) + WHERE size([i in e WHERE (v)-[i:like]-(v2) | i])>1 + RETURN count(*) AS cnt + """ + Then the result should be, in any order: + | cnt | + When executing query: + """ + MATCH (v:player)-[e*2]->(n) + WHERE size([n in e WHERE (v{name:'Tim Duncan'})-[n]-()])>3 + RETURN v + """ + Then the result should be, in any order: + | v | + When executing query: + """ + MATCH (v:player)-[e*2]->()-[n]-() + WHERE size([n in e WHERE (v)-[n]-()])>0 + RETURN count(*) AS cnt + """ + Then the result should be, in any order: + | cnt | + + @skip + Scenario: variable pattern in where clause + When executing query: + """ + MATCH (v:player{name: 'Tim Duncan'})-[e*0..2]-(v2) + WHERE NOT (v)-[:like*0..1]-(v2) + RETURN count(*) AS cnt + """ + Then the result should be, in any order: + | cnt | + | 76 | + When executing query: + """ + MATCH (v:player{name: 'Tim Duncan'})-[e*0..2]-(v2) + WHERE NOT (v)-[:like*1..2]-(v2) + RETURN count(*) AS cnt + """ + Then the result should be, in any order: + | cnt | + | 182 | + When executing query: + """ + MATCH (v:player{name: 'Tim Duncan'})-[e:like*0..2]-(v2) + WHERE NOT (v)-[:like*0..1]-(v2) + RETURN count(*) AS cnt + """ + Then the result should be, in any order: + | cnt | + | 56 | + When executing query: + """ + MATCH (v:player{name: 'Tim Duncan'})-[e:like*0..2]-(v2) + WHERE NOT (v)-[:like*1..2]-(v2) + RETURN count(*) AS cnt + """ + Then the result should be, in any order: + | cnt | + | 56 | diff --git a/tests/tck/features/match/With.feature b/tests/tck/features/match/With.feature index 5dd6278a82f..ad9dd034334 100644 --- a/tests/tck/features/match/With.feature +++ b/tests/tck/features/match/With.feature @@ -94,8 +94,8 @@ Feature: With clause RETURN x.c """ Then the result should be, in any order: - | x.c | - | UNKNOWN_PROP | + | x.c | + | NULL | Scenario: match with return When executing query: @@ -232,7 +232,10 @@ Feature: With clause WITH dst AS b RETURN b.age AS age, b """ - Then a SemanticError should be raised at runtime: To get the property of the vertex in `b.age', should use the format `var.tag.prop' + Then the result should be, in any order, with relax comparison: + | age | b | + | NULL | ("Manu Ginobili" :player{age: 41, name: "Manu Ginobili"}) | + | NULL | ("Tony Parker" :player{age: 36, name: "Tony Parker"}) | @skip Scenario: with match return diff --git a/tests/tck/features/optimizer/PrunePropertiesRule.feature b/tests/tck/features/optimizer/PrunePropertiesRule.feature index c2f32a43b98..2b8193a8ebe 100644 --- a/tests/tck/features/optimizer/PrunePropertiesRule.feature +++ b/tests/tck/features/optimizer/PrunePropertiesRule.feature @@ -539,7 +539,6 @@ Feature: Prune Properties rule WHERE id(v) == 'Tim Duncan' AND b.player.age > 20 WITH v, count(b) AS countB, t OPTIONAL MATCH (v)-[:like]-()<-[:like]-(oldB)-[:serve]->(t) - WHERE oldB.player.age > 10 WITH v, countB, t, count(oldB) AS cb RETURN t.team.name, sum(countB) """ @@ -549,25 +548,23 @@ Feature: Prune Properties rule | "Hornets" | 3 | And the execution plan should be: | id | name | dependencies | operator info | - | 21 | Aggregate | 20 | | - | 20 | Aggregate | 19 | | - | 19 | HashLeftJoin | 10, 25 | | - | 10 | Aggregate | 23 | | - | 23 | Project | 22 | | - | 22 | Filter | 29 | | - | 29 | AppendVertices | 28 | { "props": "[{ \"props\":[\"name\",\"age\",\"_tag\"]},{\"props\":[\"name\",\"speciality\",\"_tag\"] },{ \"props\":[\"name\",\"_tag\"]}]" } | - | 28 | Traverse | 27 | {"vertexProps": "[{\"props\":[\"age\"] }]", "edgeProps": "[{ \"props\": [\"_type\", \"_rank\", \"_dst\"]}]" } | - | 27 | Traverse | 26 | {"vertexProps": "", "edgeProps": "[{ \"props\": [\"_type\", \"_rank\", \"_dst\"]}]" } | - | 26 | Traverse | 2 | {"vertexProps": "[{ \"props\":[\"name\",\"age\",\"_tag\"]},{\"props\":[\"name\",\"speciality\",\"_tag\"] },{ \"props\":[\"name\",\"_tag\"]}]", "edgeProps": "[{ \"props\": [\"_type\", \"_rank\", \"_dst\"]}, { \"props\": [\"_type\", \"_rank\", \"_dst\"]}]" } | + | 19 | Aggregate | 18 | | + | 18 | Aggregate | 27 | | + | 27 | HashLeftJoin | 10,26 | | + | 10 | Aggregate | 21 | | + | 21 | Project | 20 | | + | 20 | Filter | 25 | | + | 25 | AppendVertices | 24 | { "props": "[{ \"props\":[\"name\",\"age\",\"_tag\"]},{\"props\":[\"name\",\"speciality\",\"_tag\"] },{ \"props\":[\"name\",\"_tag\"]}]" } | + | 24 | Traverse | 23 | {"vertexProps": "[{ \"props\":[\"age\"]}]", "edgeProps": "[{ \"props\": [\"_type\", \"_rank\", \"_dst\"]}]" } | + | 23 | Traverse | 22 | {"vertexProps": "", "edgeProps": "[{ \"props\": [\"_type\", \"_rank\", \"_dst\"]}]" } | + | 22 | Traverse | 2 | {"vertexProps": "[{ \"props\":[\"name\",\"age\",\"_tag\"]},{\"props\":[\"name\",\"speciality\",\"_tag\"] },{ \"props\":[\"name\",\"_tag\"]}]", "edgeProps": "[{ \"props\": [\"_type\", \"_rank\", \"_dst\"]}, { \"props\": [\"_type\", \"_rank\", \"_dst\"]}]" } | | 2 | Dedup | 1 | | | 1 | PassThrough | 3 | | | 3 | Start | | | - | 25 | Project | 24 | | - | 24 | Filter | 16 | | - | 16 | AppendVertices | 15 | { "props": "[{ \"props\":[\"name\",\"age\",\"_tag\"]},{\"props\":[\"name\",\"speciality\",\"_tag\"] },{ \"props\":[\"name\",\"_tag\"]}]"} | - | 15 | Traverse | 14 | {"vertexProps": "[{\"props\":[\"age\"] }]", "edgeProps": "[{ \"props\": [\"_type\", \"_rank\", \"_dst\"]}]" } | - | 14 | Traverse | 13 | {"vertexProps": "", "edgeProps": "[{ \"props\": [\"_type\", \"_rank\", \"_dst\"]}]" } | - | 13 | Traverse | 11 | {"vertexProps": "[{ \"props\":[\"name\",\"age\",\"_tag\"]},{\"props\":[\"name\",\"speciality\",\"_tag\"] },{ \"props\":[\"name\",\"_tag\"]}]", "edgeProps": "[{ \"props\": [\"_type\", \"_rank\", \"_dst\"]}, { \"props\": [\"_type\", \"_rank\", \"_dst\"]}]" } | + | 26 | Project | 14 | | + | 14 | Traverse | 13 | {"vertexProps": "", "edgeProps": "[{ \"props\": [\"_src\", \"_type\", \"_rank\", \"_dst\", \"start_year\", \"end_year\"]}]" } | + | 13 | Traverse | 12 | {"vertexProps": "", "edgeProps": "[{ \"props\": [\"_type\", \"_rank\", \"_dst\"]}]" } | + | 12 | Traverse | 11 | {"vertexProps": "[{ \"props\":[\"name\",\"age\",\"_tag\"]},{\"props\":[\"name\",\"speciality\",\"_tag\"] },{ \"props\":[\"name\",\"_tag\"]}]", "edgeProps": "[{ \"props\": [\"_type\", \"_rank\", \"_dst\"]}, { \"props\": [\"_type\", \"_rank\", \"_dst\"]}]" } | | 11 | Argument | | | @distonly @@ -756,9 +753,9 @@ Feature: Prune Properties rule """ Then the result should be, in order, with relax comparison: | properties(src_v).age | properties(e).degree | name | src_v.player.sex | e.start_year | dst_v.player.age | - | 41 | UNKNOWN_PROP | "Dejounte Murray" | "男" | 2022 | 29 | + | 41 | NULL | "Dejounte Murray" | "男" | 2022 | 29 | | 41 | 88 | "Spurs" | "男" | 2002 | NULL | - | 41 | UNKNOWN_PROP | "Tiago Splitter" | "男" | 2022 | 34 | + | 41 | NULL | "Tiago Splitter" | "男" | 2022 | 34 | When executing query: """ match (src_v:player{name:"Manu Ginobili"})-[e*2]-(dst_v) @@ -779,12 +776,12 @@ Feature: Prune Properties rule order by degree, name, age limit 5; """ Then the result should be, in order, with relax comparison: - | properties(src_v).sex | degree | name | age | e[1].start_year | dst_v.player.age | - | "男" | UNKNOWN_PROP | "Aron Baynes" | 41 | 2022 | 32 | - | "男" | UNKNOWN_PROP | "Blake Griffin" | 41 | 2022 | 30 | - | "男" | UNKNOWN_PROP | "Boris Diaw" | 41 | 2022 | 36 | - | "男" | UNKNOWN_PROP | "Carmelo Anthony" | 41 | 2022 | 34 | - | "男" | UNKNOWN_PROP | "Chris Paul" | 41 | 2022 | 33 | + | properties(src_v).sex | degree | name | age | e[1].start_year | dst_v.player.age | + | "男" | NULL | "Aron Baynes" | 41 | 2022 | 32 | + | "男" | NULL | "Blake Griffin" | 41 | 2022 | 30 | + | "男" | NULL | "Boris Diaw" | 41 | 2022 | 36 | + | "男" | NULL | "Carmelo Anthony" | 41 | 2022 | 34 | + | "男" | NULL | "Chris Paul" | 41 | 2022 | 33 | When executing query: """ match (v1)-->(v2)-->(v3) where id(v1)=="Manu Ginobili" @@ -860,11 +857,11 @@ Feature: Prune Properties rule """ Then the result should be, in order, with relax comparison: | properties(e).degree1 | properties(e).degree1 | e2.a | dst_v.p.name | dst_v.player.sex1 | properties(src_v).name2 | - | UNKNOWN_PROP | UNKNOWN_PROP | NULL | NULL | NULL | UNKNOWN_PROP | - | UNKNOWN_PROP | UNKNOWN_PROP | NULL | NULL | NULL | UNKNOWN_PROP | - | UNKNOWN_PROP | UNKNOWN_PROP | NULL | NULL | NULL | UNKNOWN_PROP | - | UNKNOWN_PROP | UNKNOWN_PROP | NULL | NULL | NULL | UNKNOWN_PROP | - | UNKNOWN_PROP | UNKNOWN_PROP | NULL | NULL | NULL | UNKNOWN_PROP | + | NULL | NULL | NULL | NULL | NULL | NULL | + | NULL | NULL | NULL | NULL | NULL | NULL | + | NULL | NULL | NULL | NULL | NULL | NULL | + | NULL | NULL | NULL | NULL | NULL | NULL | + | NULL | NULL | NULL | NULL | NULL | NULL | Then drop the used space Scenario: Project on not exist tag diff --git a/tests/tck/features/path/AllPath.feature b/tests/tck/features/path/AllPath.feature index 53efe544b1f..91afd8c9342 100644 --- a/tests/tck/features/path/AllPath.feature +++ b/tests/tck/features/path/AllPath.feature @@ -3,8 +3,30 @@ # This source code is licensed under Apache 2.0 License. Feature: All Path - Scenario: [1] ALL Path + Background: Given a graph with space named "nba" + + Scenario: ALL Path zero step + When executing query: + """ + FIND ALL PATH FROM "Tim Duncan" TO "Tim Duncan" OVER * UPTO 0 STEPS YIELD path as p + """ + Then the result should be, in any order, with relax comparison: + | p | + When executing query: + """ + FIND ALL PATH FROM "Tim Duncan" TO "Spurs", "Tony Parker" OVER * UPTO 0 STEPS YIELD path as p + """ + Then the result should be, in any order, with relax comparison: + | p | + When executing query: + """ + FIND ALL PATH FROM "Tim Duncan", "Tony Parker" TO "Tim Duncan" OVER * UPTO 0 STEPS YIELD path as p + """ + Then the result should be, in any order, with relax comparison: + | p | + + Scenario: ALL Path one TO one When executing query: """ FIND ALL PATH FROM "Tim Duncan" TO "Tim Duncan" OVER * UPTO 2 STEPS YIELD path as p @@ -30,6 +52,13 @@ Feature: All Path | <("Tim Duncan")-[:like]->("Tony Parker")> | | <("Tim Duncan")-[:like]->("Manu Ginobili")-[:like]->("Tim Duncan")-[:like]->("Tony Parker")> | | <("Tim Duncan")-[:like]->("Tony Parker")-[:like]->("LaMarcus Aldridge")-[:like]->("Tony Parker")> | + When executing query: + """ + FIND ALL PATH FROM "Tim Duncan" TO "Tony Parker" OVER like, noexist UPTO 3 STEPS YIELD path as p + """ + Then a SemanticError should be raised at runtime: noexist not found in space [nba]. + + Scenario: ALL Path one TO M When executing query: """ FIND ALL PATH FROM "Tim Duncan" TO "Tony Parker", "Manu Ginobili" OVER like UPTO 3 STEPS YIELD path as p @@ -69,8 +98,99 @@ Feature: All Path | <("Tim Duncan")-[:like]->("Tony Parker")-[:like]->("LaMarcus Aldridge")-[:serve]->("Spurs")> | | <("Tim Duncan")-[:like]->("Tony Parker")-[:like]->("Manu Ginobili")-[:serve]->("Spurs")> | + Scenario: ALL PATH M to one + When executing query: + """ + FIND ALL PATH FROM "Tony Parker","Spurs" TO "Tim Duncan" OVER like,serve UPTO 3 STEPS YIELD path as p + """ + Then the result should be, in any order, with relax comparison: + | p | + | <("Tony Parker")-[:like@0 {}]->("LaMarcus Aldridge")-[:like@0 {}]->("Tim Duncan")> | + | <("Tony Parker")-[:like@0 {}]->("Tim Duncan")> | + | <("Tony Parker")-[:like@0 {}]->("Manu Ginobili")-[:like@0 {}]->("Tim Duncan")> | + | <("Tony Parker")-[:like@0 {}]->("LaMarcus Aldridge")-[:like@0 {}]->("Tony Parker")-[:like@0 {}]->("Tim Duncan")> | + | <("Tony Parker")-[:like@0 {}]->("Tim Duncan")-[:like@0 {}]->("Manu Ginobili")-[:like@0 {}]->("Tim Duncan")> | + When executing query: + """ + FIND ALL PATH FROM "noexist", "Tony Parker","Spurs" TO "Tim Duncan" OVER like,serve UPTO 3 STEPS YIELD path as p + """ + Then the result should be, in any order, with relax comparison: + | p | + | <("Tony Parker")-[:like@0 {}]->("LaMarcus Aldridge")-[:like@0 {}]->("Tim Duncan")> | + | <("Tony Parker")-[:like@0 {}]->("Tim Duncan")> | + | <("Tony Parker")-[:like@0 {}]->("Manu Ginobili")-[:like@0 {}]->("Tim Duncan")> | + | <("Tony Parker")-[:like@0 {}]->("LaMarcus Aldridge")-[:like@0 {}]->("Tony Parker")-[:like@0 {}]->("Tim Duncan")> | + | <("Tony Parker")-[:like@0 {}]->("Tim Duncan")-[:like@0 {}]->("Manu Ginobili")-[:like@0 {}]->("Tim Duncan")> | + + Scenario: ALL PATH M to N + When executing query: + """ + FIND ALL PATH FROM "Yao Ming","Manu Ginobili" TO "Tim Duncan", "Larkers" OVER * UPTO 3 STEPS YIELD path as p + """ + Then the result should be, in any order, with relax comparison: + | p | + | <("Manu Ginobili")-[:like@0 {}]->("Tim Duncan")> | + | <("Manu Ginobili")-[:teammate@0 {}]->("Tim Duncan")> | + | <("Manu Ginobili")-[:teammate@0 {}]->("Tony Parker")-[:like@0 {}]->("Tim Duncan")> | + | <("Manu Ginobili")-[:teammate@0 {}]->("Tony Parker")-[:teammate@0 {}]->("Tim Duncan")> | + | <("Yao Ming")-[:like@0 {}]->("Shaquille O'Neal")-[:like@0 {}]->("Tim Duncan")> | + | <("Manu Ginobili")-[:like@0 {}]->("Tim Duncan")-[:teammate@0 {}]->("Danny Green")-[:like@0 {}]->("Tim Duncan")> | + | <("Manu Ginobili")-[:teammate@0 {}]->("Tim Duncan")-[:teammate@0 {}]->("Danny Green")-[:like@0 {}]->("Tim Duncan")> | + | <("Manu Ginobili")-[:teammate@0 {}]->("Tony Parker")-[:like@0 {}]->("LaMarcus Aldridge")-[:like@0 {}]->("Tim Duncan")> | + | <("Manu Ginobili")-[:teammate@0 {}]->("Tony Parker")-[:teammate@0 {}]->("LaMarcus Aldridge")-[:like@0 {}]->("Tim Duncan")> | + | <("Manu Ginobili")-[:like@0 {}]->("Tim Duncan")-[:teammate@0 {}]->("LaMarcus Aldridge")-[:like@0 {}]->("Tim Duncan")> | + | <("Manu Ginobili")-[:teammate@0 {}]->("Tim Duncan")-[:teammate@0 {}]->("LaMarcus Aldridge")-[:like@0 {}]->("Tim Duncan")> | + | <("Manu Ginobili")-[:like@0 {}]->("Tim Duncan")-[:like@0 {}]->("Tony Parker")-[:like@0 {}]->("Tim Duncan")> | + | <("Manu Ginobili")-[:like@0 {}]->("Tim Duncan")-[:like@0 {}]->("Tony Parker")-[:teammate@0 {}]->("Tim Duncan")> | + | <("Manu Ginobili")-[:teammate@0 {}]->("Tim Duncan")-[:like@0 {}]->("Tony Parker")-[:like@0 {}]->("Tim Duncan")> | + | <("Manu Ginobili")-[:teammate@0 {}]->("Tim Duncan")-[:like@0 {}]->("Tony Parker")-[:teammate@0 {}]->("Tim Duncan")> | + | <("Manu Ginobili")-[:like@0 {}]->("Tim Duncan")-[:teammate@0 {}]->("Tony Parker")-[:like@0 {}]->("Tim Duncan")> | + | <("Manu Ginobili")-[:like@0 {}]->("Tim Duncan")-[:teammate@0 {}]->("Tony Parker")-[:teammate@0 {}]->("Tim Duncan")> | + | <("Manu Ginobili")-[:teammate@0 {}]->("Tim Duncan")-[:teammate@0 {}]->("Tony Parker")-[:like@0 {}]->("Tim Duncan")> | + | <("Manu Ginobili")-[:teammate@0 {}]->("Tim Duncan")-[:teammate@0 {}]->("Tony Parker")-[:teammate@0 {}]->("Tim Duncan")> | + | <("Manu Ginobili")-[:teammate@0 {}]->("Tony Parker")-[:like@0 {}]->("Manu Ginobili")-[:like@0 {}]->("Tim Duncan")> | + | <("Manu Ginobili")-[:teammate@0 {}]->("Tony Parker")-[:like@0 {}]->("Manu Ginobili")-[:teammate@0 {}]->("Tim Duncan")> | + | <("Manu Ginobili")-[:teammate@0 {}]->("Tony Parker")-[:teammate@0 {}]->("Manu Ginobili")-[:like@0 {}]->("Tim Duncan")> | + | <("Manu Ginobili")-[:teammate@0 {}]->("Tony Parker")-[:teammate@0 {}]->("Manu Ginobili")-[:teammate@0 {}]->("Tim Duncan")> | + | <("Manu Ginobili")-[:like@0 {}]->("Tim Duncan")-[:like@0 {}]->("Manu Ginobili")-[:teammate@0 {}]->("Tim Duncan")> | + | <("Manu Ginobili")-[:teammate@0 {}]->("Tim Duncan")-[:like@0 {}]->("Manu Ginobili")-[:like@0 {}]->("Tim Duncan")> | + | <("Manu Ginobili")-[:like@0 {}]->("Tim Duncan")-[:teammate@0 {}]->("Manu Ginobili")-[:teammate@0 {}]->("Tim Duncan")> | + | <("Manu Ginobili")-[:teammate@0 {}]->("Tim Duncan")-[:teammate@0 {}]->("Manu Ginobili")-[:like@0 {}]->("Tim Duncan")> | + When executing query: + """ + FIND ALL PATH FROM "Yao Ming","Manu Ginobili" TO "Tim Duncan", "Larkers", "noexist" OVER * UPTO 3 STEPS YIELD path as p + """ + Then the result should be, in any order, with relax comparison: + | p | + | <("Manu Ginobili")-[:like@0 {}]->("Tim Duncan")> | + | <("Manu Ginobili")-[:teammate@0 {}]->("Tim Duncan")> | + | <("Manu Ginobili")-[:teammate@0 {}]->("Tony Parker")-[:like@0 {}]->("Tim Duncan")> | + | <("Manu Ginobili")-[:teammate@0 {}]->("Tony Parker")-[:teammate@0 {}]->("Tim Duncan")> | + | <("Yao Ming")-[:like@0 {}]->("Shaquille O'Neal")-[:like@0 {}]->("Tim Duncan")> | + | <("Manu Ginobili")-[:like@0 {}]->("Tim Duncan")-[:teammate@0 {}]->("Danny Green")-[:like@0 {}]->("Tim Duncan")> | + | <("Manu Ginobili")-[:teammate@0 {}]->("Tim Duncan")-[:teammate@0 {}]->("Danny Green")-[:like@0 {}]->("Tim Duncan")> | + | <("Manu Ginobili")-[:teammate@0 {}]->("Tony Parker")-[:like@0 {}]->("LaMarcus Aldridge")-[:like@0 {}]->("Tim Duncan")> | + | <("Manu Ginobili")-[:teammate@0 {}]->("Tony Parker")-[:teammate@0 {}]->("LaMarcus Aldridge")-[:like@0 {}]->("Tim Duncan")> | + | <("Manu Ginobili")-[:like@0 {}]->("Tim Duncan")-[:teammate@0 {}]->("LaMarcus Aldridge")-[:like@0 {}]->("Tim Duncan")> | + | <("Manu Ginobili")-[:teammate@0 {}]->("Tim Duncan")-[:teammate@0 {}]->("LaMarcus Aldridge")-[:like@0 {}]->("Tim Duncan")> | + | <("Manu Ginobili")-[:like@0 {}]->("Tim Duncan")-[:like@0 {}]->("Tony Parker")-[:like@0 {}]->("Tim Duncan")> | + | <("Manu Ginobili")-[:like@0 {}]->("Tim Duncan")-[:like@0 {}]->("Tony Parker")-[:teammate@0 {}]->("Tim Duncan")> | + | <("Manu Ginobili")-[:teammate@0 {}]->("Tim Duncan")-[:like@0 {}]->("Tony Parker")-[:like@0 {}]->("Tim Duncan")> | + | <("Manu Ginobili")-[:teammate@0 {}]->("Tim Duncan")-[:like@0 {}]->("Tony Parker")-[:teammate@0 {}]->("Tim Duncan")> | + | <("Manu Ginobili")-[:like@0 {}]->("Tim Duncan")-[:teammate@0 {}]->("Tony Parker")-[:like@0 {}]->("Tim Duncan")> | + | <("Manu Ginobili")-[:like@0 {}]->("Tim Duncan")-[:teammate@0 {}]->("Tony Parker")-[:teammate@0 {}]->("Tim Duncan")> | + | <("Manu Ginobili")-[:teammate@0 {}]->("Tim Duncan")-[:teammate@0 {}]->("Tony Parker")-[:like@0 {}]->("Tim Duncan")> | + | <("Manu Ginobili")-[:teammate@0 {}]->("Tim Duncan")-[:teammate@0 {}]->("Tony Parker")-[:teammate@0 {}]->("Tim Duncan")> | + | <("Manu Ginobili")-[:teammate@0 {}]->("Tony Parker")-[:like@0 {}]->("Manu Ginobili")-[:like@0 {}]->("Tim Duncan")> | + | <("Manu Ginobili")-[:teammate@0 {}]->("Tony Parker")-[:like@0 {}]->("Manu Ginobili")-[:teammate@0 {}]->("Tim Duncan")> | + | <("Manu Ginobili")-[:teammate@0 {}]->("Tony Parker")-[:teammate@0 {}]->("Manu Ginobili")-[:like@0 {}]->("Tim Duncan")> | + | <("Manu Ginobili")-[:teammate@0 {}]->("Tony Parker")-[:teammate@0 {}]->("Manu Ginobili")-[:teammate@0 {}]->("Tim Duncan")> | + | <("Manu Ginobili")-[:like@0 {}]->("Tim Duncan")-[:like@0 {}]->("Manu Ginobili")-[:teammate@0 {}]->("Tim Duncan")> | + | <("Manu Ginobili")-[:teammate@0 {}]->("Tim Duncan")-[:like@0 {}]->("Manu Ginobili")-[:like@0 {}]->("Tim Duncan")> | + | <("Manu Ginobili")-[:like@0 {}]->("Tim Duncan")-[:teammate@0 {}]->("Manu Ginobili")-[:teammate@0 {}]->("Tim Duncan")> | + | <("Manu Ginobili")-[:teammate@0 {}]->("Tim Duncan")-[:teammate@0 {}]->("Manu Ginobili")-[:like@0 {}]->("Tim Duncan")> | + Scenario: [1] ALL Path Run Time Input - Given a graph with space named "nba" When executing query: """ GO FROM "Tim Duncan" over * YIELD like._dst AS src, serve._src AS dst | @@ -101,7 +221,6 @@ Feature: All Path | <("Tony Parker")-[:like]->("Tim Duncan")-[:like]->("Manu Ginobili")-[:like]->("Tim Duncan")> | Scenario: [1] ALL Path With Limit - Given a graph with space named "nba" When executing query: """ FIND ALL PATH FROM "Tim Duncan" TO "Tony Parker","Spurs" OVER like,serve UPTO 3 STEPS YIELD path as p | @@ -127,7 +246,6 @@ Feature: All Path | <("Tony Parker")-[:like]->("Manu Ginobili")-[:like]->("Tim Duncan")> | Scenario: [1] ALL PATH REVERSELY - Given a graph with space named "nba" When executing query: """ FIND ALL PATH FROM "Tim Duncan" TO "Nobody","Spur" OVER like REVERSELY UPTO 3 STEPS YIELD path as p @@ -164,7 +282,6 @@ Feature: All Path | <("Tim Duncan")<-[:like]-("Tony Parker")<-[:like]-("LaMarcus Aldridge")<-[:like]-("Tony Parker")> | Scenario: [2] ALL PATH BIDIRECT - Given a graph with space named "nba" When executing query: """ FIND ALL PATH FROM "Tim Duncan" TO "Tony Parker" OVER like BIDIRECT UPTO 3 STEPS YIELD path as p @@ -210,8 +327,25 @@ Feature: All Path | <("Tim Duncan" :bachelor{name: "Tim Duncan", speciality: "psychology"} :player{age: 42, name: "Tim Duncan"})-[:like@0 {likeness: 95}]->("Manu Ginobili" :player{age: 41, name: "Manu Ginobili"})-[:like@0 {likeness: 90}]->("Tim Duncan" :bachelor{name: "Tim Duncan", speciality: "psychology"} :player{age: 42, name: "Tim Duncan"})-[:like@0 {likeness: 95}]->("Tony Parker" :player{age: 36, name: "Tony Parker"})> | | <("Tim Duncan" :bachelor{name: "Tim Duncan", speciality: "psychology"} :player{age: 42, name: "Tim Duncan"})-[:like@0 {likeness: 95}]->("Tony Parker" :player{age: 36, name: "Tony Parker"})-[:like@0 {likeness: 90}]->("LaMarcus Aldridge" :player{age: 33, name: "LaMarcus Aldridge"})-[:like@0 {likeness: 75}]->("Tony Parker" :player{age: 36, name: "Tony Parker"})> | - Scenario: ALL Path WITH FILTER - Given a graph with space named "nba" + Scenario: ALL Path constant filter + When executing query: + """ + FIND ALL PATH WITH PROP FROM "Tim Duncan" TO "Yao Ming" OVER * BIDIRECT + WHERE 1 > 2 UPTO 3 STEPS YIELD path as p + """ + Then the result should be, in any order, with relax comparison: + | p | + When executing query: + """ + FIND ALL PATH WITH PROP FROM "Tim Duncan" TO "Yao Ming" OVER * BIDIRECT + WHERE 1 < 2 UPTO 3 STEPS YIELD path as p + """ + Then the result should be, in any order, with relax comparison: + | p | + | <("Tim Duncan" :player{age: 42, name: "Tim Duncan"} :bachelor{name: "Tim Duncan", speciality: "psychology"})<-[:like@0 {likeness: 80}]-("Shaquille O'Neal" :player{age: 47, name: "Shaquille O'Neal"})<-[:like@0 {likeness: 90}]-("Yao Ming" :player{age: 38, name: "Yao Ming"})> | + | <("Tim Duncan" :player{age: 42, name: "Tim Duncan"} :bachelor{name: "Tim Duncan", speciality: "psychology"})-[:serve@0 {end_year: 2016, start_year: 1997}]->("Spurs" :team{name: "Spurs"})<-[:serve@0 {end_year: 2013, start_year: 2013}]-("Tracy McGrady" :player{age: 39, name: "Tracy McGrady"})<-[:like@0 {likeness: 90}]-("Yao Ming" :player{age: 38, name: "Yao Ming"})> | + + Scenario: ALL Path edge filter When executing query: """ FIND ALL PATH WITH PROP FROM "Tim Duncan" TO "Yao Ming" OVER * BIDIRECT @@ -242,6 +376,25 @@ Feature: All Path | <("Yao Ming" :player{age: 38, name: "Yao Ming"})-[:like@0 {likeness: 90}]->("Shaquille O'Neal" :player{age: 47, name: "Shaquille O'Neal"})-[:like@0 {likeness: 80}]->("Tim Duncan" :bachelor{name: "Tim Duncan", speciality: "psychology"} :player{age: 42, name: "Tim Duncan"})-[:teammate@0 {end_year: 2016, start_year: 2010}]->("Danny Green" :player{age: 31, name: "Danny Green"})> | | <("Yao Ming" :player{age: 38, name: "Yao Ming"})-[:like@0 {likeness: 90}]->("Tracy McGrady" :player{age: 39, name: "Tracy McGrady"})-[:serve@0 {end_year: 2000, start_year: 1997}]->("Raptors" :team{name: "Raptors"})<-[:serve@0 {end_year: 2019, start_year: 2018}]-("Danny Green" :player{age: 31, name: "Danny Green"})> | | <("Yao Ming" :player{age: 38, name: "Yao Ming"})-[:like@0 {likeness: 90}]->("Tracy McGrady" :player{age: 39, name: "Tracy McGrady"})-[:serve@0 {end_year: 2013, start_year: 2013}]->("Spurs" :team{name: "Spurs"})<-[:serve@0 {end_year: 2018, start_year: 2010}]-("Danny Green" :player{age: 31, name: "Danny Green"})> | + When executing query: + """ + FIND ALL PATH WITH PROP FROM "Yao Ming" TO "Danny Green" OVER * BIDIRECT + WHERE like.likeness is EMPTY OR like.likeness >= 80 AND 1 > 2 UPTO 3 STEPS YIELD path as p + """ + Then the result should be, in any order, with relax comparison: + | p | + When executing query: + """ + GO FROM "Tim Duncan" over * YIELD like._dst AS src, serve._src AS dst, like.likeness AS val | + FIND ALL PATH FROM $-.src TO $-.dst OVER like WHERE like.likeness > $-.val UPTO 3 STEPS YIELD path as p + """ + Then a SemanticError should be raised at runtime: Not support `(like.likeness>$-.val)' in where sentence. + When executing query: + """ + $v = GO FROM "Tim Duncan" over * YIELD like._dst AS src, serve._src AS dst, like.likeness AS val; + FIND ALL PATH FROM $v.src TO $v.dst OVER like WHERE like.likeness > $v.val UPTO 3 STEPS YIELD path as p + """ + Then a SemanticError should be raised at runtime: Not support `(like.likeness>$v.val)' in where sentence. Scenario: Dangling edge Given an empty graph @@ -279,7 +432,6 @@ Feature: All Path Then drop the used space Scenario: ALL PATH YIELD PATH - Given a graph with space named "nba" When executing query: """ FIND ALL PATH WITH PROP FROM "Yao Ming" TO "Danny Green" OVER * BIDIRECT diff --git a/tests/tck/features/path/ShortestPath.feature b/tests/tck/features/path/ShortestPath.feature index 3c950029381..e08cf5e51ee 100644 --- a/tests/tck/features/path/ShortestPath.feature +++ b/tests/tck/features/path/ShortestPath.feature @@ -6,6 +6,25 @@ Feature: Shortest Path Background: Given a graph with space named "nba" + Scenario: Shortest Path zero step + When executing query: + """ + FIND SHORTEST PATH FROM "Tim Duncan" , "Yao Ming" TO "Tony Parker" OVER like UPTO 0 STEPS YIELD path as p + """ + Then the result should be, in any order, with relax comparison: + | p | + When executing query: + """ + FIND SHORTEST PATH FROM "Tim Duncan", "Yao Ming" TO "Tony Parker", "Spurs" OVER * UPTO 0 STEPS YIELD path as p + """ + Then the result should be, in any order, with relax comparison: + | p | + When executing query: + """ + FIND SHORTEST PATH FROM "Tim Duncan", "Yao Ming" TO "Tony Parker", "Spurs" OVER * UPTO -1 STEPS YIELD path as p + """ + Then a SyntaxError should be raised at runtime: syntax error near `-1 STEPS' + Scenario: [1] SinglePair Shortest Path When executing query: """ @@ -14,6 +33,32 @@ Feature: Shortest Path Then the result should be, in any order, with relax comparison: | p | | <("Tim Duncan")-[:like]->("Tony Parker")> | + When executing query: + """ + FIND SHORTEST PATH FROM "Tim Duncan" TO "Tony Parker", "noexist" OVER like YIELD path as p + """ + Then the result should be, in any order, with relax comparison: + | p | + | <("Tim Duncan")-[:like]->("Tony Parker")> | + When executing query: + """ + FIND SHORTEST PATH FROM "Tim Duncan", "noexist" TO "Tony Parker" OVER like YIELD path as p + """ + Then the result should be, in any order, with relax comparison: + | p | + | <("Tim Duncan")-[:like]->("Tony Parker")> | + When executing query: + """ + FIND SHORTEST PATH FROM "Tim Duncan", "noexist" TO "Tony Parker", "noexist" OVER like YIELD path as p + """ + Then the result should be, in any order, with relax comparison: + | p | + | <("Tim Duncan")-[:like]->("Tony Parker")> | + When executing query: + """ + FIND SHORTEST PATH FROM "Tim Duncan", "noexist" TO "Tony Parker", "noexist" OVER noexistedge,like YIELD path as p + """ + Then a SemanticError should be raised at runtime: noexistedge not found in space [nba]. Scenario: [2] SinglePair Shortest Path When executing query: diff --git a/tests/tck/features/schema/Schema.feature b/tests/tck/features/schema/Schema.feature index 6ac6c6f35ac..fc386184eaf 100644 --- a/tests/tck/features/schema/Schema.feature +++ b/tests/tck/features/schema/Schema.feature @@ -956,7 +956,7 @@ Feature: Insert string vid of vertex and edge CREATE TAG person(name string, age int); """ Then the execution should be successful - And wait 3 seconds + And wait 10 seconds When executing query: """ INSERT VERTEX person values "1":("Tom", 23); @@ -974,7 +974,7 @@ Feature: Insert string vid of vertex and edge ALTER TAG person DROP (age); """ Then the execution should be successful - And wait 3 seconds + And wait 10 seconds When executing query: """ FETCH PROP ON person "1" yield properties(vertex) AS props; @@ -986,4 +986,4 @@ Feature: Insert string vid of vertex and edge """ ALTER TAG person ADD (age int); """ - Then a ExecutionError should be raised at runtime: Existed + Then a ExecutionError should be raised at runtime: Schema exisited before! diff --git a/tests/tck/features/subgraph/subgraph.feature b/tests/tck/features/subgraph/subgraph.feature index 9169159d30f..f06c8e2dcd1 100644 --- a/tests/tck/features/subgraph/subgraph.feature +++ b/tests/tck/features/subgraph/subgraph.feature @@ -57,6 +57,11 @@ Feature: subgraph $a = GO FROM "Tim Duncan" OVER like YIELD like._dst AS id, like._src AS id; GET SUBGRAPH WITH PROP FROM $a.id YIELD vertices as nodes """ Then a SemanticError should be raised at runtime: Duplicate Column Name : `id' + When executing query: + """ + GET SUBGRAPH FROM 'Tim Duncan' OUT like, noexist YIELD vertices as v + """ + Then a ExecutionError should be raised at runtime: EdgeNotFound: EdgeName `noexist` Scenario: zero step When executing query: diff --git a/tests/tck/features/yield/yield.IntVid.feature b/tests/tck/features/yield/yield.IntVid.feature index 0aef1b6b7e6..a6245d18a08 100644 --- a/tests/tck/features/yield/yield.IntVid.feature +++ b/tests/tck/features/yield/yield.IntVid.feature @@ -11,6 +11,141 @@ Feature: Yield Sentence Then the result should be, in any order: | 1 | | 1 | + When executing query: + """ + YIELD [1, 1.1, 1e2, 1.1e2, .3e4, 1.e4, 1234E-10, true] AS basic_value + """ + Then the result should be, in any order, with relax comparison: + | basic_value | + | [1, 1.1, 100.0, 110.0, 3000.0, 10000.0, 0.0000001234, true] | + When executing query: + """ + YIELD {p1: 1, p2: true, p3: "test"} AS r + """ + Then the result should be, in any order: + | r | + | {p1: 1, p2: true, p3: "test"} | + When executing query: + """ + YIELD true and false + """ + Then the result should be, in any order: + | (true AND false) | + | false | + When executing query: + """ + YIELD datetime('2012-03-04T22:11').year + """ + Then the result should be, in any order: + | datetime("2012-03-04T22:11").year | + | 2012 | + When executing query: + """ + YIELD datetime('2012-03-04T22:11').not_exists + """ + Then the result should be, in any order: + | datetime("2012-03-04T22:11").not_exists | + | UNKNOWN_PROP | + When executing query: + """ + YIELD CASE 1 WHEN 1 THEN 2 ELSE 3 END + """ + Then the result should be, in any order: + | CASE 1 WHEN 1 THEN 2 ELSE 3 END | + | 2 | + When executing query: + """ + YIELD abs(-1) + """ + Then the result should be, in any order: + | abs(-(1)) | + | 1 | + When executing query: + """ + YIELD v.l1.p1 + """ + Then a SemanticError should be raised at runtime: Invalid label identifiers: v + When executing query: + """ + YIELD l1 + """ + Then a SemanticError should be raised at runtime: Invalid label identifiers: l1 + When executing query: + """ + YIELD [i in [1, 2, 3] where i > 1 | i + 1] AS r + """ + Then the result should be, in any order: + | r | + | [3, 4] | + When executing query: + """ + YIELD all(n IN range(1, 5) WHERE n > 2) + """ + Then the result should be, in any order: + | all(n IN range(1,5) WHERE (n>2)) | + | false | + When executing query: + """ + YIELD like._dst + """ + Then a SemanticError should be raised at runtime: Only support input and variable in yield sentence. + When executing query: + """ + YIELD reduce(totalNum = 10, n IN range(1, 3) | totalNum + n) AS r + """ + Then the result should be, in any order: + | r | + | 16 | + When executing query: + """ + YIELD [1, 2, 3][2] + """ + Then the result should be, in any order: + | [1,2,3][2] | + | 3 | + When executing query: + """ + YIELD [1, 2, 3][0..1] + """ + Then the result should be, in any order: + | [1,2,3][0..1] | + | [1] | + When executing query: + """ + YIELD prefix(edge1.prop1,"高") + """ + Then a SyntaxError should be raised at runtime: syntax error near `(edge1.p' + When executing query: + """ + YIELD (INT)"1" + """ + Then the result should be, in any order: + | (INT)"1" | + | 1 | + When executing query: + """ + YIELD -(1) + """ + Then the result should be, in any order: + | -(1) | + | -1 | + When executing query: + """ + YIELD uuid() + """ + Then a SemanticError should be raised at runtime: Not supported expression `uuid()' for props deduction. + When executing query: + """ + YIELD $v + """ + Then a SyntaxError should be raised at runtime: Direct output of variable is prohibited near `$v' + When executing query: + """ + YIELD 1 AS num + """ + Then the result should be, in any order: + | num | + | 1 | When executing query: """ YIELD 1+1, (int)3.14, (string)(1+1), (string)true,"1+1" @@ -26,6 +161,20 @@ Feature: Yield Sentence | "Hello" | hash("Hello") | | "Hello" | 2275118702903107253 | + # TODO fix it + @skip + Scenario: Mistake + When executing query: + """ + YIELD count(*) + """ + Then a SemanticError should be raised at runtime: Don't support aggregate function without input. + When executing query: + """ + YIELD (v)-[:like]-() + """ + Then a SemanticError should be raised at runtime: Not support pattern expression. + Scenario: HashCall When executing query: """ diff --git a/tests/tck/features/yield/yield.feature b/tests/tck/features/yield/yield.feature index 47677b2cb3e..db9ad24b36c 100644 --- a/tests/tck/features/yield/yield.feature +++ b/tests/tck/features/yield/yield.feature @@ -4,6 +4,13 @@ Feature: Yield Sentence Given a graph with space named "nba" Scenario: Base + When executing query: + """ + YIELD 1 + """ + Then the result should be, in any order: + | 1 | + | 1 | When executing query: """ YIELD [1, 1.1, 1e2, 1.1e2, .3e4, 1.e4, 1234E-10, true] AS basic_value @@ -11,6 +18,134 @@ Feature: Yield Sentence Then the result should be, in any order, with relax comparison: | basic_value | | [1, 1.1, 100.0, 110.0, 3000.0, 10000.0, 0.0000001234, true] | + When executing query: + """ + YIELD {p1: 1, p2: true, p3: "test"} AS r + """ + Then the result should be, in any order: + | r | + | {p1: 1, p2: true, p3: "test"} | + When executing query: + """ + YIELD true and false + """ + Then the result should be, in any order: + | (true AND false) | + | false | + When executing query: + """ + YIELD datetime('2012-03-04T22:11').year + """ + Then the result should be, in any order: + | datetime("2012-03-04T22:11").year | + | 2012 | + When executing query: + """ + YIELD datetime('2012-03-04T22:11').not_exists + """ + Then the result should be, in any order: + | datetime("2012-03-04T22:11").not_exists | + | UNKNOWN_PROP | + When executing query: + """ + YIELD CASE 1 WHEN 1 THEN 2 ELSE 3 END + """ + Then the result should be, in any order: + | CASE 1 WHEN 1 THEN 2 ELSE 3 END | + | 2 | + When executing query: + """ + YIELD abs(-1) + """ + Then the result should be, in any order: + | abs(-(1)) | + | 1 | + When executing query: + """ + YIELD v.l1.p1 + """ + Then a SemanticError should be raised at runtime: Invalid label identifiers: v + When executing query: + """ + YIELD l1 + """ + Then a SemanticError should be raised at runtime: Invalid label identifiers: l1 + When executing query: + """ + YIELD [i in [1, 2, 3] where i > 1 | i + 1] AS r + """ + Then the result should be, in any order: + | r | + | [3, 4] | + When executing query: + """ + YIELD all(n IN range(1, 5) WHERE n > 2) + """ + Then the result should be, in any order: + | all(n IN range(1,5) WHERE (n>2)) | + | false | + When executing query: + """ + YIELD like._dst + """ + Then a SemanticError should be raised at runtime: Only support input and variable in yield sentence. + When executing query: + """ + YIELD reduce(totalNum = 10, n IN range(1, 3) | totalNum + n) AS r + """ + Then the result should be, in any order: + | r | + | 16 | + When executing query: + """ + YIELD [1, 2, 3][2] + """ + Then the result should be, in any order: + | [1,2,3][2] | + | 3 | + When executing query: + """ + YIELD [1, 2, 3][0..1] + """ + Then the result should be, in any order: + | [1,2,3][0..1] | + | [1] | + When executing query: + """ + YIELD prefix(edge1.prop1,"高") + """ + Then a SyntaxError should be raised at runtime: syntax error near `(edge1.p' + When executing query: + """ + YIELD (int)"1" + """ + Then the result should be, in any order: + | (INT)"1" | + | 1 | + When executing query: + """ + YIELD -(1) + """ + Then the result should be, in any order: + | -(1) | + | -1 | + When executing query: + """ + YIELD uuid() + """ + Then a SemanticError should be raised at runtime: Not supported expression `uuid()' for props deduction. + When executing query: + """ + YIELD $v + """ + Then a SyntaxError should be raised at runtime: Direct output of variable is prohibited near `$v' + When executing query: + """ + YIELD 1 AS num + """ + Then the result should be, in any order: + | num | + | 1 | When executing query: """ YIELD 1+1, (int)3.14, (string)(1+1), (string)true,"1+1" @@ -26,6 +161,20 @@ Feature: Yield Sentence | "Hello" | hash("Hello") | | "Hello" | 2275118702903107253 | + # TODO fix it + @skip + Scenario: Mistake + When executing query: + """ + YIELD count(*) + """ + Then a SemanticError should be raised at runtime: Don't support aggregate function without input. + When executing query: + """ + YIELD (v)-[:like]-() + """ + Then a SemanticError should be raised at runtime: Not support pattern expression. + Scenario: HashCall When executing query: """ diff --git a/tests/tck/ldbc/business_intelligence_workload/Read.feature b/tests/tck/ldbc/business_intelligence_workload/Read.feature index 523aa4d4788..176e00420f9 100644 --- a/tests/tck/ldbc/business_intelligence_workload/Read.feature +++ b/tests/tck/ldbc/business_intelligence_workload/Read.feature @@ -115,8 +115,7 @@ Feature: LDBC Business Intelligence Workload - Read tagName ASC LIMIT 100 """ - Then the result should be, in any order: - | tagName | countMonth1 | countMonth2 | diff | + Then a SyntaxError should be raised at runtime: Where clause in optional match is not supported. near `WHERE datetime('2012-06-01') <= message1.Message.creationDate AND message1.Messa' Scenario: 4. Popular topics in a country When executing query: @@ -530,8 +529,7 @@ Feature: LDBC Business Intelligence Workload - Read personCount DESC, messageCount DESC """ - Then the result should be, in order: - | messageCount | personCount | + Then a SyntaxError should be raised at runtime: Where clause in optional match is not supported. near `WHERE message.Message.content IS NOT NULL AND message.Message.length < 20 AND me' Scenario: 19. Stranger’s interaction # NOTICE: A big rewritten, have to test the correctness diff --git a/tests/tck/ldbc/interactive_workload/ComplexReads.feature b/tests/tck/ldbc/interactive_workload/ComplexReads.feature index dc7321b1be1..a0b470ebc59 100644 --- a/tests/tck/ldbc/interactive_workload/ComplexReads.feature +++ b/tests/tck/ldbc/interactive_workload/ComplexReads.feature @@ -137,8 +137,7 @@ Feature: LDBC Interactive Workload - Complex Reads sum(postsOnTag) AS postCount ORDER BY postCount DESC, tagName ASC """ - Then the result should be, in any order: - | tagName | postCount | + Then a SyntaxError should be raised at runtime: Where clause in optional match is not supported. near `WHERE oldPost.Post.creationDate < $startDate' Scenario: 5. New groups # {"personId":"6597069766734","minDate":"1288612800000"} diff --git a/tests/tck/utils/table.py b/tests/tck/utils/table.py index 7505c2a30ff..ca3f550542c 100644 --- a/tests/tck/utils/table.py +++ b/tests/tck/utils/table.py @@ -13,6 +13,8 @@ def _parse_value(cell: str, variables: dict) -> Value: + if not cell: + cell = "EMPTY" m = pattern.match(cell) if m: var = m.group(1)