Add CreateIndex conversion to ast

Reviewers: florijan, teon.banek

Reviewed By: teon.banek

Subscribers: pullbot

Differential Revision: https://phabricator.memgraph.io/D522
This commit is contained in:
Mislav Bradac 2017-07-06 13:03:14 +02:00
parent c22ac38ea2
commit d608d523c5
10 changed files with 94 additions and 12 deletions

View File

@ -1389,6 +1389,26 @@ class Unwind : public Clause {
}
};
class CreateIndex : public Clause {
friend class AstTreeStorage;
public:
DEFVISITABLE(TreeVisitor<TypedValue>);
DEFVISITABLE(HierarchicalTreeVisitor);
CreateIndex *Clone(AstTreeStorage &storage) const override {
return storage.Create<CreateIndex>(label_, property_);
}
GraphDbTypes::Label label_;
GraphDbTypes::Property property_;
protected:
CreateIndex(int uid, GraphDbTypes::Label label,
GraphDbTypes::Property property)
: Clause(uid), label_(label), property_(property) {}
};
/// CachedAst is used for storing high level asts.
///
/// After query is stripped, parsed and converted to high level ast it can be
@ -1442,6 +1462,7 @@ class CachedAst {
}
bool Visit(Identifier &) override { return true; }
bool Visit(CreateIndex &) override { return true; }
bool PreVisit(Return &) override {
in_return_ = true;

View File

@ -53,6 +53,7 @@ class RemoveProperty;
class RemoveLabels;
class Merge;
class Unwind;
class CreateIndex;
using TreeCompositeVisitor = ::utils::CompositeVisitor<
Query, NamedExpression, OrOperator, XorOperator, AndOperator,
@ -66,7 +67,8 @@ using TreeCompositeVisitor = ::utils::CompositeVisitor<
Where, SetProperty, SetProperties, SetLabels, RemoveProperty, RemoveLabels,
Merge, Unwind>;
using TreeLeafVisitor = ::utils::LeafVisitor<Identifier, PrimitiveLiteral>;
using TreeLeafVisitor =
::utils::LeafVisitor<Identifier, PrimitiveLiteral, CreateIndex>;
class HierarchicalTreeVisitor : public TreeCompositeVisitor,
public TreeLeafVisitor {
@ -88,6 +90,6 @@ using TreeVisitor = ::utils::Visitor<
ListLiteral, PropertyLookup, LabelsTest, EdgeTypeTest, Aggregation,
Function, Create, Match, Return, With, Pattern, NodeAtom, EdgeAtom, Delete,
Where, SetProperty, SetProperties, SetLabels, RemoveProperty, RemoveLabels,
Merge, Unwind, Identifier, PrimitiveLiteral>;
Merge, Unwind, Identifier, PrimitiveLiteral, CreateIndex>;
} // namespace query

View File

@ -43,6 +43,7 @@ antlrcpp::Any CypherMainVisitor::visitSingleQuery(
bool has_update = false;
bool has_return = false;
bool has_optional_match = false;
bool has_create_index = false;
for (Clause *clause : query_->clauses_) {
if (dynamic_cast<Unwind *>(clause)) {
if (has_update || has_return) {
@ -80,13 +81,21 @@ antlrcpp::Any CypherMainVisitor::visitSingleQuery(
throw SemanticException("Return can't be before with");
}
has_update = has_return = has_optional_match = false;
} else if (dynamic_cast<CreateIndex *>(clause)) {
// If there is CreateIndex clause then there shouldn't be anything else.
if (query_->clauses_.size() != 1U) {
throw SemanticException(
"CreateIndex must be only clause in the query.");
}
has_create_index = true;
} else {
debug_assert(false, "Can't happen");
}
}
if (!has_update && !has_return) {
if (!has_update && !has_return && !has_create_index) {
throw SemanticException(
"Query should either update something or return results");
"Query should either update something, return results or create an "
"index");
}
// Construct unique names for anonymous identifiers;
@ -136,6 +145,10 @@ antlrcpp::Any CypherMainVisitor::visitClause(CypherParser::ClauseContext *ctx) {
if (ctx->unwind()) {
return static_cast<Clause *>(ctx->unwind()->accept(this).as<Unwind *>());
}
if (ctx->createIndex()) {
return static_cast<Clause *>(
ctx->createIndex()->accept(this).as<CreateIndex *>());
}
// TODO: implement other clauses.
throw utils::NotYetImplemented();
return 0;
@ -156,7 +169,16 @@ antlrcpp::Any CypherMainVisitor::visitCreate(CypherParser::CreateContext *ctx) {
auto *create = storage_.Create<Create>();
create->patterns_ = ctx->pattern()->accept(this).as<std::vector<Pattern *>>();
return create;
;
}
/**
* @return CreateIndex*
*/
antlrcpp::Any CypherMainVisitor::visitCreateIndex(
CypherParser::CreateIndexContext *ctx) {
return storage_.Create<CreateIndex>(
ctx_.db_accessor_.label(ctx->labelName()->accept(this)),
ctx->propertyKeyName()->accept(this));
}
antlrcpp::Any CypherMainVisitor::visitCypherReturn(
@ -598,7 +620,7 @@ antlrcpp::Any CypherMainVisitor::visitExpression6(
// Power.
antlrcpp::Any CypherMainVisitor::visitExpression5(
CypherParser::Expression5Context *ctx) {
if (ctx->expression4().size() > 1u) {
if (ctx->expression4().size() > 1U) {
// TODO: implement power operator. In neo4j power is left associative and
// int^int -> float.
throw utils::NotYetImplemented();

View File

@ -153,6 +153,12 @@ class CypherMainVisitor : public antlropencypher::CypherBaseVisitor {
*/
antlrcpp::Any visitCreate(CypherParser::CreateContext *ctx) override;
/**
* @return CreateIndex*
*/
antlrcpp::Any visitCreateIndex(
CypherParser::CreateIndexContext *ctx) override;
/**
* @return Return*
*/

View File

@ -39,6 +39,7 @@ clause : cypherMatch
| remove
| with
| cypherReturn
| createIndex
;
cypherMatch : ( OPTIONAL SP )? MATCH SP? pattern ( SP? where )? ;
@ -252,6 +253,8 @@ integerLiteral : HexInteger
| DecimalInteger
;
createIndex : CREATE SP INDEX SP ON SP? ':' SP? labelName SP? '(' SP? propertyKeyName SP? ')' ;
HexInteger : '0x' ( HexDigit )+ ;
DecimalInteger : ZeroDigit
@ -438,6 +441,8 @@ TRUE : ( 'T' | 't' ) ( 'R' | 'r' ) ( 'U' | 'u' ) ( 'E' | 'e' ) ;
FALSE : ( 'F' | 'f' ) ( 'A' | 'a' ) ( 'L' | 'l' ) ( 'S' | 's' ) ( 'E' | 'e' ) ;
INDEX : ( 'I' | 'i') ( 'N' | 'n' ) ( 'D' | 'd' ) ( 'E' | 'e' ) ( 'X' | 'x' ) ;
UnescapedSymbolicName : IdentifierStart ( IdentifierPart )* ;
/**

View File

@ -109,11 +109,11 @@ void SymbolGenerator::VisitReturnBody(ReturnBody &body, Where *where) {
// Clauses
bool SymbolGenerator::PreVisit(Create &create) {
bool SymbolGenerator::PreVisit(Create &) {
scope_.in_create = true;
return true;
}
bool SymbolGenerator::PostVisit(Create &create) {
bool SymbolGenerator::PostVisit(Create &) {
scope_.in_create = false;
return true;
}
@ -175,6 +175,8 @@ bool SymbolGenerator::PostVisit(Match &) {
return true;
}
bool SymbolGenerator::Visit(CreateIndex &) { return true; }
// Expressions
SymbolGenerator::ReturnType SymbolGenerator::Visit(Identifier &ident) {
@ -242,7 +244,7 @@ bool SymbolGenerator::PreVisit(Aggregation &aggr) {
return true;
}
bool SymbolGenerator::PostVisit(Aggregation &aggr) {
bool SymbolGenerator::PostVisit(Aggregation &) {
scope_.in_aggregation = false;
return true;
}
@ -259,7 +261,7 @@ bool SymbolGenerator::PreVisit(Pattern &pattern) {
return true;
}
bool SymbolGenerator::PostVisit(Pattern &pattern) {
bool SymbolGenerator::PostVisit(Pattern &) {
scope_.in_pattern = false;
scope_.in_create_node = false;
return true;
@ -285,7 +287,7 @@ bool SymbolGenerator::PreVisit(NodeAtom &node_atom) {
return false;
}
bool SymbolGenerator::PostVisit(NodeAtom &node_atom) {
bool SymbolGenerator::PostVisit(NodeAtom &) {
scope_.in_node_atom = false;
return true;
}
@ -315,7 +317,7 @@ bool SymbolGenerator::PreVisit(EdgeAtom &edge_atom) {
return false;
}
bool SymbolGenerator::PostVisit(EdgeAtom &edge_atom) {
bool SymbolGenerator::PostVisit(EdgeAtom &) {
scope_.in_edge_atom = false;
scope_.in_create_edge = false;
return true;

View File

@ -37,6 +37,7 @@ class SymbolGenerator : public HierarchicalTreeVisitor {
bool PostVisit(Unwind &) override;
bool PreVisit(Match &) override;
bool PostVisit(Match &) override;
bool Visit(CreateIndex &) override;
// Expressions
ReturnType Visit(Identifier &) override;

View File

@ -51,6 +51,7 @@ class ExpressionEvaluator : public TreeVisitor<TypedValue> {
BLOCK_VISIT(RemoveLabels);
BLOCK_VISIT(Merge);
BLOCK_VISIT(Unwind);
BLOCK_VISIT(CreateIndex);
#undef BLOCK_VISIT

View File

@ -140,6 +140,7 @@ class UsedSymbolsCollector : public HierarchicalTreeVisitor {
}
ReturnType Visit(PrimitiveLiteral &) override { return true; }
ReturnType Visit(query::CreateIndex &) override { return true; }
std::unordered_set<Symbol> symbols_;
const SymbolTable &symbol_table_;
@ -446,6 +447,8 @@ class ReturnBodyContext : public HierarchicalTreeVisitor {
return true;
}
bool Visit(query::CreateIndex &) override { return true; }
// Creates NamedExpression with an Identifier for each user declared symbol.
// This should be used when body.all_identifiers is true, to generate
// expressions for Produce operator.

View File

@ -1225,6 +1225,14 @@ TYPED_TEST(CypherMainVisitorTest, ClausesOrdering) {
SemanticException);
TypeParam("UNWIND [1,2,3] AS x CREATE (n) RETURN x");
TypeParam("CREATE (n) WITH n UNWIND [1,2,3] AS x RETURN x");
TypeParam("CREATE INDEX ON :a(b)");
ASSERT_THROW(TypeParam("CREATE INDEX ON :a(n) CREATE INDEX ON :b(c)"),
SemanticException);
ASSERT_THROW(TypeParam("CREATE (n) CREATE INDEX ON :a(n)"),
SemanticException);
ASSERT_THROW(TypeParam("CREATE INDEX ON :a(n) RETURN 2 + 2"),
SemanticException);
}
TYPED_TEST(CypherMainVisitorTest, Merge) {
@ -1261,4 +1269,15 @@ TYPED_TEST(CypherMainVisitorTest, Unwind) {
TYPED_TEST(CypherMainVisitorTest, UnwindWithoutAsError) {
EXPECT_THROW(TypeParam("UNWIND [1,2,3] RETURN 42"), SyntaxException);
}
TYPED_TEST(CypherMainVisitorTest, CreateIndex) {
TypeParam ast_generator("Create InDeX oN :mirko(slavko)");
auto *query = ast_generator.query_;
ASSERT_EQ(query->clauses_.size(), 1U);
auto *create_index = dynamic_cast<CreateIndex *>(query->clauses_[0]);
ASSERT_TRUE(create_index);
ASSERT_EQ(create_index->label_, ast_generator.db_accessor_->label("mirko"));
ASSERT_EQ(create_index->property_,
ast_generator.db_accessor_->property("slavko"));
}
}