Add syntax for node key constraints
Reviewers: msantl, teon.banek, llugovic, vkasljevic Reviewed By: msantl Subscribers: pullbot Differential Revision: https://phabricator.memgraph.io/D1953
This commit is contained in:
parent
c7684c7e93
commit
c269e2f468
@ -2590,7 +2590,7 @@ cpp<#
|
||||
}")
|
||||
:clone (clone-name-ix-vector "Property")))
|
||||
(:public
|
||||
(lcp:define-enum type (exists unique)
|
||||
(lcp:define-enum type (exists unique node-key)
|
||||
(:serialize (:lcp) (:capnp))))
|
||||
(:serialize (:slk :load-args '((storage "query::AstStorage *")))
|
||||
(:capnp :load-args '((storage "AstStorage *"))))
|
||||
|
@ -87,11 +87,13 @@ antlrcpp::Any CypherMainVisitor::visitConstraintQuery(
|
||||
antlrcpp::Any CypherMainVisitor::visitConstraint(
|
||||
MemgraphCypher::ConstraintContext *ctx) {
|
||||
Constraint constraint;
|
||||
CHECK(ctx->EXISTS() || ctx->UNIQUE());
|
||||
CHECK(ctx->EXISTS() || ctx->UNIQUE() || (ctx->NODE() && ctx->KEY()));
|
||||
if (ctx->EXISTS()) {
|
||||
constraint.type = Constraint::Type::EXISTS;
|
||||
} else if (ctx->UNIQUE()) {
|
||||
constraint.type = Constraint::Type::UNIQUE;
|
||||
} else if (ctx->NODE() && ctx->KEY()) {
|
||||
constraint.type = Constraint::Type::NODE_KEY;
|
||||
}
|
||||
constraint.label = AddLabel(ctx->labelName()->accept(this));
|
||||
std::string node_name = ctx->nodeName->symbolicName()->accept(this);
|
||||
|
@ -31,10 +31,11 @@ query : cypherQuery
|
||||
| constraintQuery
|
||||
;
|
||||
|
||||
constraintQuery : ( CREATE | DROP ) constraint ;
|
||||
constraintQuery : ( CREATE | DROP ) CONSTRAINT ON constraint ;
|
||||
|
||||
constraint : CONSTRAINT ON '(' nodeName=variable ':' labelName ')' ASSERT EXISTS '(' constraintPropertyList ')'
|
||||
| CONSTRAINT ON '(' nodeName=variable ':' labelName ')' ASSERT constraintPropertyList IS UNIQUE
|
||||
constraint : '(' nodeName=variable ':' labelName ')' ASSERT EXISTS '(' constraintPropertyList ')'
|
||||
| '(' nodeName=variable ':' labelName ')' ASSERT constraintPropertyList IS UNIQUE
|
||||
| '(' nodeName=variable ':' labelName ')' ASSERT '(' constraintPropertyList ')' IS NODE KEY
|
||||
;
|
||||
|
||||
constraintPropertyList : variable propertyLookup ( ',' variable propertyLookup )* ;
|
||||
@ -336,10 +337,12 @@ cypherKeyword : ALL
|
||||
| INDEX
|
||||
| INFO
|
||||
| IS
|
||||
| KEY
|
||||
| LIMIT
|
||||
| L_SKIP
|
||||
| MATCH
|
||||
| MERGE
|
||||
| NODE
|
||||
| NONE
|
||||
| NOT
|
||||
| ON
|
||||
|
@ -102,10 +102,12 @@ IN : I N ;
|
||||
INDEX : I N D E X ;
|
||||
INFO : I N F O ;
|
||||
IS : I S ;
|
||||
KEY : K E Y ;
|
||||
LIMIT : L I M I T ;
|
||||
L_SKIP : S K I P ;
|
||||
MATCH : M A T C H ;
|
||||
MERGE : M E R G E ;
|
||||
NODE : N O D E ;
|
||||
NONE : N O N E ;
|
||||
NOT : N O T ;
|
||||
ON : O N ;
|
||||
|
@ -91,7 +91,8 @@ const trie::Trie kKeywords = {
|
||||
"stream", "streams", "load", "data", "kafka", "transform",
|
||||
"batch", "interval", "show", "start", "stats", "stop",
|
||||
"size", "topic", "test", "unique", "explain", "profile",
|
||||
"storage", "index", "info", "exists" "assert", "constraint"};
|
||||
"storage", "index", "info", "exists" "assert", "constraint",
|
||||
"node", "key"};
|
||||
|
||||
// Unicode codepoints that are allowed at the start of the unescaped name.
|
||||
const std::bitset<kBitsetSize> kUnescapedNameAllowedStarts(std::string(
|
||||
|
@ -665,6 +665,9 @@ Callback HandleConstraintQuery(ConstraintQuery *constraint_query,
|
||||
case Constraint::Type::UNIQUE:
|
||||
type = "unique";
|
||||
break;
|
||||
case Constraint::Type::NODE_KEY:
|
||||
type = "node key";
|
||||
break;
|
||||
}
|
||||
switch (constraint_query->action_type_) {
|
||||
case ConstraintQuery::ActionType::CREATE:
|
||||
|
@ -2700,6 +2700,7 @@ TEST_P(CypherMainVisitorTest, CreateConstraintSyntaxError) {
|
||||
EXPECT_THROW(ast_generator.ParseQuery("CREATE CONSTRAINT ON (n:label) ASSERT "
|
||||
"EXISTS (m.prop1, m.prop2)"),
|
||||
SemanticException);
|
||||
|
||||
EXPECT_THROW(ast_generator.ParseQuery(
|
||||
"CREATE CONSTRAINT ON (:label) ASSERT IS UNIQUE"),
|
||||
SyntaxException);
|
||||
@ -2718,6 +2719,31 @@ TEST_P(CypherMainVisitorTest, CreateConstraintSyntaxError) {
|
||||
EXPECT_THROW(ast_generator.ParseQuery("CREATE CONSTRAINT ON (n:label) ASSERT "
|
||||
"m.prop1, m.prop2 IS UNIQUE"),
|
||||
SemanticException);
|
||||
|
||||
EXPECT_THROW(ast_generator.ParseQuery(
|
||||
"CREATE CONSTRAINT ON (:label) ASSERT IS NODE KEY"),
|
||||
SyntaxException);
|
||||
EXPECT_THROW(
|
||||
ast_generator.ParseQuery("CREATE CONSTRAINT () ASSERT IS NODE KEY"),
|
||||
SyntaxException);
|
||||
EXPECT_THROW(ast_generator.ParseQuery(
|
||||
"CREATE CONSTRAINT ON () ASSERT (prop1) IS NODE KEY"),
|
||||
SyntaxException);
|
||||
EXPECT_THROW(ast_generator.ParseQuery(
|
||||
"CREATE CONSTRAINT ON () ASSERT (prop1, prop2) IS NODE KEY"),
|
||||
SyntaxException);
|
||||
EXPECT_THROW(ast_generator.ParseQuery("CREATE CONSTRAINT ON (n:label) ASSERT "
|
||||
"(n.prop1, missing.prop2) IS NODE KEY"),
|
||||
SemanticException);
|
||||
EXPECT_THROW(ast_generator.ParseQuery("CREATE CONSTRAINT ON (n:label) ASSERT "
|
||||
"(m.prop1, m.prop2) IS NODE KEY"),
|
||||
SemanticException);
|
||||
EXPECT_THROW(ast_generator.ParseQuery("CREATE CONSTRAINT ON (n:label) ASSERT "
|
||||
"n.prop1, n.prop2 IS NODE KEY"),
|
||||
SyntaxException);
|
||||
EXPECT_THROW(ast_generator.ParseQuery("CREATE CONSTRAINT ON (n:label) ASSERT "
|
||||
"exists(n.prop1, n.prop2) IS NODE KEY"),
|
||||
SyntaxException);
|
||||
}
|
||||
|
||||
TEST_P(CypherMainVisitorTest, CreateConstraint) {
|
||||
@ -2767,6 +2793,30 @@ TEST_P(CypherMainVisitorTest, CreateConstraint) {
|
||||
UnorderedElementsAre(ast_generator.Prop("prop1"),
|
||||
ast_generator.Prop("prop2")));
|
||||
}
|
||||
{
|
||||
auto &ast_generator = *GetParam();
|
||||
auto *query = dynamic_cast<ConstraintQuery *>(ast_generator.ParseQuery(
|
||||
"CREATE CONSTRAINT ON (n:label) ASSERT (n.prop1) IS NODE KEY"));
|
||||
ASSERT_TRUE(query);
|
||||
EXPECT_EQ(query->action_type_, ConstraintQuery::ActionType::CREATE);
|
||||
EXPECT_EQ(query->constraint_.type, Constraint::Type::NODE_KEY);
|
||||
EXPECT_EQ(query->constraint_.label, ast_generator.Label("label"));
|
||||
EXPECT_THAT(query->constraint_.properties,
|
||||
UnorderedElementsAre(ast_generator.Prop("prop1")));
|
||||
}
|
||||
{
|
||||
auto &ast_generator = *GetParam();
|
||||
auto *query = dynamic_cast<ConstraintQuery *>(
|
||||
ast_generator.ParseQuery("CREATE CONSTRAINT ON (n:label) ASSERT "
|
||||
"(n.prop1, n.prop2) IS NODE KEY"));
|
||||
ASSERT_TRUE(query);
|
||||
EXPECT_EQ(query->action_type_, ConstraintQuery::ActionType::CREATE);
|
||||
EXPECT_EQ(query->constraint_.type, Constraint::Type::NODE_KEY);
|
||||
EXPECT_EQ(query->constraint_.label, ast_generator.Label("label"));
|
||||
EXPECT_THAT(query->constraint_.properties,
|
||||
UnorderedElementsAre(ast_generator.Prop("prop1"),
|
||||
ast_generator.Prop("prop2")));
|
||||
}
|
||||
}
|
||||
|
||||
TEST_P(CypherMainVisitorTest, DropConstraint) {
|
||||
@ -2816,6 +2866,30 @@ TEST_P(CypherMainVisitorTest, DropConstraint) {
|
||||
UnorderedElementsAre(ast_generator.Prop("prop1"),
|
||||
ast_generator.Prop("prop2")));
|
||||
}
|
||||
{
|
||||
auto &ast_generator = *GetParam();
|
||||
auto *query = dynamic_cast<ConstraintQuery *>(ast_generator.ParseQuery(
|
||||
"DROP CONSTRAINT ON (n:label) ASSERT (n.prop1) IS NODE KEY"));
|
||||
ASSERT_TRUE(query);
|
||||
EXPECT_EQ(query->action_type_, ConstraintQuery::ActionType::DROP);
|
||||
EXPECT_EQ(query->constraint_.type, Constraint::Type::NODE_KEY);
|
||||
EXPECT_EQ(query->constraint_.label, ast_generator.Label("label"));
|
||||
EXPECT_THAT(query->constraint_.properties,
|
||||
UnorderedElementsAre(ast_generator.Prop("prop1")));
|
||||
}
|
||||
{
|
||||
auto &ast_generator = *GetParam();
|
||||
auto *query = dynamic_cast<ConstraintQuery *>(
|
||||
ast_generator.ParseQuery("DROP CONSTRAINT ON (n:label) ASSERT "
|
||||
"(n.prop1, n.prop2) IS NODE KEY"));
|
||||
ASSERT_TRUE(query);
|
||||
EXPECT_EQ(query->action_type_, ConstraintQuery::ActionType::DROP);
|
||||
EXPECT_EQ(query->constraint_.type, Constraint::Type::NODE_KEY);
|
||||
EXPECT_EQ(query->constraint_.label, ast_generator.Label("label"));
|
||||
EXPECT_THAT(query->constraint_.properties,
|
||||
UnorderedElementsAre(ast_generator.Prop("prop1"),
|
||||
ast_generator.Prop("prop2")));
|
||||
}
|
||||
}
|
||||
|
||||
TEST_P(CypherMainVisitorTest, RegexMatch) {
|
||||
|
Loading…
Reference in New Issue
Block a user