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:
Marin Tomic 2019-04-09 11:42:34 +02:00
parent c7684c7e93
commit c269e2f468
7 changed files with 91 additions and 6 deletions

View File

@ -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 *"))))

View File

@ -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);

View File

@ -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

View File

@ -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 ;

View File

@ -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(

View File

@ -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:

View File

@ -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) {