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:
parent
c22ac38ea2
commit
d608d523c5
@ -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;
|
||||
|
@ -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
|
||||
|
@ -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();
|
||||
|
@ -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*
|
||||
*/
|
||||
|
@ -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 )* ;
|
||||
|
||||
/**
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -51,6 +51,7 @@ class ExpressionEvaluator : public TreeVisitor<TypedValue> {
|
||||
BLOCK_VISIT(RemoveLabels);
|
||||
BLOCK_VISIT(Merge);
|
||||
BLOCK_VISIT(Unwind);
|
||||
BLOCK_VISIT(CreateIndex);
|
||||
|
||||
#undef BLOCK_VISIT
|
||||
|
||||
|
@ -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.
|
||||
|
@ -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"));
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user