memgraph/src/query/frontend/ast/cypher_main_visitor.cpp
florijan 813d37e939 Migrate db::types to storage::
Reviewers: teon.banek, dgleich

Reviewed By: teon.banek, dgleich

Subscribers: pullbot

Differential Revision: https://phabricator.memgraph.io/D1110
2018-01-17 10:35:12 +01:00

1192 lines
42 KiB
C++

#include "query/frontend/ast/cypher_main_visitor.hpp"
#include <algorithm>
#include <climits>
#include <codecvt>
#include <cstring>
#include <limits>
#include <string>
#include <tuple>
#include <unordered_map>
#include <utility>
#include <vector>
#include "glog/logging.h"
#include "database/graph_db.hpp"
#include "query/common.hpp"
#include "query/exceptions.hpp"
#include "query/interpret/awesome_memgraph_functions.hpp"
#include "utils/exceptions.hpp"
#include "utils/string.hpp"
namespace query::frontend {
const std::string CypherMainVisitor::kAnonPrefix = "anon";
antlrcpp::Any CypherMainVisitor::visitRegularQuery(
CypherParser::RegularQueryContext *ctx) {
query_ = storage_.query();
DCHECK(ctx->singleQuery()) << "Expected single query.";
query_->single_query_ = ctx->singleQuery()->accept(this).as<SingleQuery *>();
// Check that union and union all dont mix
bool has_union = false;
bool has_union_all = false;
for (auto *child : ctx->cypherUnion()) {
if (child->ALL()) {
has_union_all = true;
} else {
has_union = true;
}
if (has_union && has_union_all) {
throw SemanticException("Invalid combination of UNION and UNION ALL.");
}
query_->cypher_unions_.push_back(child->accept(this).as<CypherUnion *>());
}
return query_;
}
antlrcpp::Any CypherMainVisitor::visitCypherUnion(
CypherParser::CypherUnionContext *ctx) {
bool distinct = !ctx->ALL();
auto *cypher_union = storage_.Create<CypherUnion>(distinct);
DCHECK(ctx->singleQuery()) << "Expected single query.";
cypher_union->single_query_ =
ctx->singleQuery()->accept(this).as<SingleQuery *>();
return cypher_union;
}
antlrcpp::Any CypherMainVisitor::visitSingleQuery(
CypherParser::SingleQueryContext *ctx) {
auto *single_query = storage_.Create<SingleQuery>();
for (auto *child : ctx->clause()) {
antlrcpp::Any got = child->accept(this);
if (got.is<Clause *>()) {
single_query->clauses_.push_back(got.as<Clause *>());
} else {
auto child_clauses = got.as<std::vector<Clause *>>();
single_query->clauses_.insert(single_query->clauses_.end(),
child_clauses.begin(), child_clauses.end());
}
}
// Check if ordering of clauses makes sense.
//
// TODO: should we forbid multiple consecutive set clauses? That case is
// little bit problematic because multiple barriers are needed. Multiple
// consecutive SET clauses are undefined behaviour in neo4j.
bool has_update = false;
bool has_return = false;
bool has_optional_match = false;
bool has_create_index = false;
for (Clause *clause : single_query->clauses_) {
if (dynamic_cast<Unwind *>(clause)) {
if (has_update || has_return) {
throw SemanticException(
"Unwind can't be after return or update clause");
}
} else if (auto *match = dynamic_cast<Match *>(clause)) {
if (has_update || has_return) {
throw SemanticException("Match can't be after return or update clause");
}
if (match->optional_) {
has_optional_match = true;
} else if (has_optional_match) {
throw SemanticException("Match can't be after optional match");
}
} else if (dynamic_cast<Create *>(clause) ||
dynamic_cast<Delete *>(clause) ||
dynamic_cast<SetProperty *>(clause) ||
dynamic_cast<SetProperties *>(clause) ||
dynamic_cast<SetLabels *>(clause) ||
dynamic_cast<RemoveProperty *>(clause) ||
dynamic_cast<RemoveLabels *>(clause) ||
dynamic_cast<Merge *>(clause)) {
if (has_return) {
throw SemanticException("Update clauses can't be after return");
}
has_update = true;
} else if (dynamic_cast<Return *>(clause)) {
if (has_return) {
throw SemanticException("There can be only one return in a clause");
}
has_return = true;
} else if (dynamic_cast<With *>(clause)) {
if (has_return) {
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 (single_query->clauses_.size() != 1U) {
throw SemanticException(
"CreateIndex must be only clause in the query.");
}
has_create_index = true;
} else {
DLOG(FATAL) << "Can't happen";
}
}
if (!has_update && !has_return && !has_create_index) {
throw SemanticException(
"Query should either update something, return results or create an "
"index");
}
// Construct unique names for anonymous identifiers;
int id = 1;
for (auto **identifier : anonymous_identifiers) {
while (true) {
std::string id_name = kAnonPrefix + std::to_string(id++);
if (users_identifiers.find(id_name) == users_identifiers.end()) {
*identifier = storage_.Create<Identifier>(id_name, false);
break;
}
}
}
return single_query;
}
antlrcpp::Any CypherMainVisitor::visitClause(CypherParser::ClauseContext *ctx) {
if (ctx->cypherReturn()) {
return static_cast<Clause *>(
ctx->cypherReturn()->accept(this).as<Return *>());
}
if (ctx->cypherMatch()) {
return static_cast<Clause *>(
ctx->cypherMatch()->accept(this).as<Match *>());
}
if (ctx->create()) {
return static_cast<Clause *>(ctx->create()->accept(this).as<Create *>());
}
if (ctx->cypherDelete()) {
return static_cast<Clause *>(
ctx->cypherDelete()->accept(this).as<Delete *>());
}
if (ctx->set()) {
// Different return type!!!
return ctx->set()->accept(this).as<std::vector<Clause *>>();
}
if (ctx->remove()) {
// Different return type!!!
return ctx->remove()->accept(this).as<std::vector<Clause *>>();
}
if (ctx->with()) {
return static_cast<Clause *>(ctx->with()->accept(this).as<With *>());
}
if (ctx->merge()) {
return static_cast<Clause *>(ctx->merge()->accept(this).as<Merge *>());
}
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("clause '{}'", ctx->getText());
return 0;
}
antlrcpp::Any CypherMainVisitor::visitCypherMatch(
CypherParser::CypherMatchContext *ctx) {
auto *match = storage_.Create<Match>();
match->optional_ = !!ctx->OPTIONAL();
if (ctx->where()) {
match->where_ = ctx->where()->accept(this);
}
match->patterns_ = ctx->pattern()->accept(this).as<std::vector<Pattern *>>();
return match;
}
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) {
std::pair<std::string, storage::Property> key =
ctx->propertyKeyName()->accept(this);
return storage_.Create<CreateIndex>(
ctx_.db_accessor_.Label(ctx->labelName()->accept(this)), key.second);
}
antlrcpp::Any CypherMainVisitor::visitCypherReturn(
CypherParser::CypherReturnContext *ctx) {
auto *return_clause = storage_.Create<Return>();
return_clause->body_ = ctx->returnBody()->accept(this);
if (ctx->DISTINCT()) {
return_clause->body_.distinct = true;
}
return return_clause;
}
antlrcpp::Any CypherMainVisitor::visitReturnBody(
CypherParser::ReturnBodyContext *ctx) {
ReturnBody body;
if (ctx->order()) {
body.order_by = ctx->order()
->accept(this)
.as<std::vector<std::pair<Ordering, Expression *>>>();
}
if (ctx->skip()) {
body.skip = static_cast<Expression *>(ctx->skip()->accept(this));
}
if (ctx->limit()) {
body.limit = static_cast<Expression *>(ctx->limit()->accept(this));
}
std::tie(body.all_identifiers, body.named_expressions) =
ctx->returnItems()
->accept(this)
.as<std::pair<bool, std::vector<NamedExpression *>>>();
return body;
}
antlrcpp::Any CypherMainVisitor::visitReturnItems(
CypherParser::ReturnItemsContext *ctx) {
std::vector<NamedExpression *> named_expressions;
for (auto *item : ctx->returnItem()) {
named_expressions.push_back(item->accept(this));
}
return std::pair<bool, std::vector<NamedExpression *>>(
ctx->getTokens(kReturnAllTokenId).size(), named_expressions);
}
antlrcpp::Any CypherMainVisitor::visitReturnItem(
CypherParser::ReturnItemContext *ctx) {
auto *named_expr = storage_.Create<NamedExpression>();
named_expr->expression_ = ctx->expression()->accept(this);
if (ctx->variable()) {
named_expr->name_ =
std::string(ctx->variable()->accept(this).as<std::string>());
} else {
if (in_with_ && !dynamic_cast<Identifier *>(named_expr->expression_)) {
throw SemanticException("Only variables can be non aliased in with");
}
named_expr->name_ = std::string(ctx->getText());
named_expr->token_position_ =
ctx->expression()->getStart()->getTokenIndex();
}
return named_expr;
}
antlrcpp::Any CypherMainVisitor::visitOrder(CypherParser::OrderContext *ctx) {
std::vector<std::pair<Ordering, Expression *>> order_by;
for (auto *sort_item : ctx->sortItem()) {
order_by.push_back(sort_item->accept(this));
}
return order_by;
}
antlrcpp::Any CypherMainVisitor::visitSortItem(
CypherParser::SortItemContext *ctx) {
return std::pair<Ordering, Expression *>(
ctx->DESC() || ctx->DESCENDING() ? Ordering::DESC : Ordering::ASC,
ctx->expression()->accept(this));
}
antlrcpp::Any CypherMainVisitor::visitNodePattern(
CypherParser::NodePatternContext *ctx) {
auto *node = storage_.Create<NodeAtom>();
if (ctx->variable()) {
std::string variable = ctx->variable()->accept(this);
node->identifier_ = storage_.Create<Identifier>(variable);
users_identifiers.insert(variable);
} else {
anonymous_identifiers.push_back(&node->identifier_);
}
if (ctx->nodeLabels()) {
node->labels_ =
ctx->nodeLabels()->accept(this).as<std::vector<storage::Label>>();
}
if (ctx->properties()) {
node->properties_ =
ctx->properties()
->accept(this)
.as<std::unordered_map<std::pair<std::string, storage::Property>,
Expression *>>();
}
return node;
}
antlrcpp::Any CypherMainVisitor::visitNodeLabels(
CypherParser::NodeLabelsContext *ctx) {
std::vector<storage::Label> labels;
for (auto *node_label : ctx->nodeLabel()) {
labels.push_back(ctx_.db_accessor_.Label(node_label->accept(this)));
}
return labels;
}
antlrcpp::Any CypherMainVisitor::visitProperties(
CypherParser::PropertiesContext *ctx) {
if (!ctx->mapLiteral()) {
// If child is not mapLiteral that means child is params. At the moment
// we don't support properties to be a param because we can generate
// better logical plan if we have an information about properties at
// compile time.
// TODO: implement other clauses.
throw utils::NotYetImplemented("property parameters");
}
return ctx->mapLiteral()->accept(this);
}
antlrcpp::Any CypherMainVisitor::visitMapLiteral(
CypherParser::MapLiteralContext *ctx) {
std::unordered_map<std::pair<std::string, storage::Property>, Expression *>
map;
for (int i = 0; i < static_cast<int>(ctx->propertyKeyName().size()); ++i) {
std::pair<std::string, storage::Property> key =
ctx->propertyKeyName()[i]->accept(this);
Expression *value = ctx->expression()[i]->accept(this);
if (!map.insert({key, value}).second) {
throw SemanticException("Same key can't appear twice in map literal");
}
}
return map;
}
antlrcpp::Any CypherMainVisitor::visitListLiteral(
CypherParser::ListLiteralContext *ctx) {
std::vector<Expression *> expressions;
for (auto expr_ctx_ptr : ctx->expression())
expressions.push_back(expr_ctx_ptr->accept(this));
return expressions;
}
antlrcpp::Any CypherMainVisitor::visitPropertyKeyName(
CypherParser::PropertyKeyNameContext *ctx) {
const std::string key_name = visitChildren(ctx);
return std::make_pair(key_name, ctx_.db_accessor_.Property(key_name));
}
antlrcpp::Any CypherMainVisitor::visitSymbolicName(
CypherParser::SymbolicNameContext *ctx) {
if (ctx->EscapedSymbolicName()) {
auto quoted_name = ctx->getText();
DCHECK(quoted_name.size() >= 2U && quoted_name[0] == '`' &&
quoted_name.back() == '`')
<< "Can't happen. Grammar ensures this";
// Remove enclosing backticks.
std::string escaped_name =
quoted_name.substr(1, static_cast<int>(quoted_name.size()) - 2);
// Unescape remaining backticks.
std::string name;
bool escaped = false;
for (auto c : escaped_name) {
if (escaped) {
if (c == '`') {
name.push_back('`');
escaped = false;
} else {
DLOG(FATAL) << "Can't happen. Grammar ensures that.";
}
} else if (c == '`') {
escaped = true;
} else {
name.push_back(c);
}
}
return name;
}
if (ctx->UnescapedSymbolicName() || ctx->HexLetter()) {
return std::string(ctx->getText());
}
// Symbolic names are case sensitive. Since StrippedQuery lowercases all
// keywords there is no way to differentiate between differently cased
// symbolic names if they are equal to a keyword.
throw SemanticException(
fmt::format("Symbolic name can't be keyword {}", ctx->getText()));
}
antlrcpp::Any CypherMainVisitor::visitPattern(
CypherParser::PatternContext *ctx) {
std::vector<Pattern *> patterns;
for (auto *pattern_part : ctx->patternPart()) {
patterns.push_back(pattern_part->accept(this));
}
return patterns;
}
antlrcpp::Any CypherMainVisitor::visitPatternPart(
CypherParser::PatternPartContext *ctx) {
Pattern *pattern = ctx->anonymousPatternPart()->accept(this);
if (ctx->variable()) {
std::string variable = ctx->variable()->accept(this);
pattern->identifier_ = storage_.Create<Identifier>(variable);
users_identifiers.insert(variable);
} else {
anonymous_identifiers.push_back(&pattern->identifier_);
}
return pattern;
}
antlrcpp::Any CypherMainVisitor::visitPatternElement(
CypherParser::PatternElementContext *ctx) {
if (ctx->patternElement()) {
return ctx->patternElement()->accept(this);
}
auto pattern = storage_.Create<Pattern>();
pattern->atoms_.push_back(ctx->nodePattern()->accept(this).as<NodeAtom *>());
for (auto *pattern_element_chain : ctx->patternElementChain()) {
std::pair<PatternAtom *, PatternAtom *> element =
pattern_element_chain->accept(this);
pattern->atoms_.push_back(element.first);
pattern->atoms_.push_back(element.second);
}
return pattern;
}
antlrcpp::Any CypherMainVisitor::visitPatternElementChain(
CypherParser::PatternElementChainContext *ctx) {
return std::pair<PatternAtom *, PatternAtom *>(
ctx->relationshipPattern()->accept(this).as<EdgeAtom *>(),
ctx->nodePattern()->accept(this).as<NodeAtom *>());
}
antlrcpp::Any CypherMainVisitor::visitRelationshipPattern(
CypherParser::RelationshipPatternContext *ctx) {
auto *edge = storage_.Create<EdgeAtom>();
auto relationshipDetail = ctx->relationshipDetail();
auto *variableExpansion =
relationshipDetail ? relationshipDetail->variableExpansion() : nullptr;
bool is_bfs = false;
if (variableExpansion)
std::tie(is_bfs, edge->lower_bound_, edge->upper_bound_) =
variableExpansion->accept(this)
.as<std::tuple<bool, Expression *, Expression *>>();
if (!variableExpansion)
edge->type_ = EdgeAtom::Type::SINGLE;
else if (is_bfs)
edge->type_ = EdgeAtom::Type::BREADTH_FIRST;
else
edge->type_ = EdgeAtom::Type::DEPTH_FIRST;
if (ctx->leftArrowHead() && !ctx->rightArrowHead()) {
edge->direction_ = EdgeAtom::Direction::IN;
} else if (!ctx->leftArrowHead() && ctx->rightArrowHead()) {
edge->direction_ = EdgeAtom::Direction::OUT;
} else {
// <-[]-> and -[]- is the same thing as far as we understand openCypher
// grammar.
edge->direction_ = EdgeAtom::Direction::BOTH;
}
if (!relationshipDetail) {
anonymous_identifiers.push_back(&edge->identifier_);
return edge;
}
if (relationshipDetail->variable()) {
std::string variable = relationshipDetail->variable()->accept(this);
edge->identifier_ = storage_.Create<Identifier>(variable);
users_identifiers.insert(variable);
} else {
anonymous_identifiers.push_back(&edge->identifier_);
}
if (relationshipDetail->relationshipTypes()) {
edge->edge_types_ = ctx->relationshipDetail()
->relationshipTypes()
->accept(this)
.as<std::vector<storage::EdgeType>>();
}
auto relationshipLambdas = relationshipDetail->relationshipLambda();
if (variableExpansion) {
switch (relationshipLambdas.size()) {
case 0:
// In variable length and BFS expansion inner variables are mandatory.
anonymous_identifiers.push_back(&edge->inner_edge_);
anonymous_identifiers.push_back(&edge->inner_node_);
break;
case 1: {
auto *lambda = relationshipLambdas[0];
std::string traversed_edge_variable =
lambda->traversed_edge->accept(this);
edge->inner_edge_ =
storage_.Create<Identifier>(traversed_edge_variable);
std::string traversed_node_variable =
lambda->traversed_node->accept(this);
edge->inner_node_ =
storage_.Create<Identifier>(traversed_node_variable);
edge->filter_expression_ = lambda->expression()->accept(this);
break;
};
default:
throw SemanticException("Only one relationship lambda allowed");
}
} else if (!relationshipLambdas.empty()) {
throw SemanticException(
"Relationship lambda only supported in variable length expansion");
}
auto properties = relationshipDetail->properties();
switch (properties.size()) {
case 0:
break;
case 1: {
edge->properties_ =
properties[0]
->accept(this)
.as<std::unordered_map<std::pair<std::string, storage::Property>,
Expression *>>();
break;
}
default:
throw SemanticException("Only one property map supported in edge");
}
return edge;
}
antlrcpp::Any CypherMainVisitor::visitRelationshipDetail(
CypherParser::RelationshipDetailContext *) {
DLOG(FATAL) << "Should never be called. See documentation in hpp.";
return 0;
}
antlrcpp::Any CypherMainVisitor::visitRelationshipLambda(
CypherParser::RelationshipLambdaContext *) {
DLOG(FATAL) << "Should never be called. See documentation in hpp.";
return 0;
}
antlrcpp::Any CypherMainVisitor::visitRelationshipTypes(
CypherParser::RelationshipTypesContext *ctx) {
std::vector<storage::EdgeType> types;
for (auto *edge_type : ctx->relTypeName()) {
types.push_back(ctx_.db_accessor_.EdgeType(edge_type->accept(this)));
}
return types;
}
antlrcpp::Any CypherMainVisitor::visitVariableExpansion(
CypherParser::VariableExpansionContext *ctx) {
DCHECK(ctx->expression().size() <= 2U)
<< "Expected 0, 1 or 2 bounds in range literal.";
bool is_bfs = !ctx->getTokens(CypherParser::BFS).empty();
Expression *lower = nullptr;
Expression *upper = nullptr;
if (ctx->expression().size() == 0U) {
// Case -[*]-
} else if (ctx->expression().size() == 1U) {
auto dots_tokens = ctx->getTokens(kDotsTokenId);
Expression *bound = ctx->expression()[0]->accept(this);
if (!dots_tokens.size()) {
// Case -[*bound]-
lower = bound;
upper = bound;
} else if (dots_tokens[0]->getSourceInterval().startsAfter(
ctx->expression()[0]->getSourceInterval())) {
// Case -[*bound..]-
lower = bound;
} else {
// Case -[*..bound]-
upper = bound;
}
} else {
// Case -[*lbound..rbound]-
lower = ctx->expression()[0]->accept(this);
upper = ctx->expression()[1]->accept(this);
}
return std::make_tuple(is_bfs, lower, upper);
}
antlrcpp::Any CypherMainVisitor::visitExpression(
CypherParser::ExpressionContext *ctx) {
return static_cast<Expression *>(ctx->expression12()->accept(this));
}
// OR.
antlrcpp::Any CypherMainVisitor::visitExpression12(
CypherParser::Expression12Context *ctx) {
return LeftAssociativeOperatorExpression(ctx->expression11(), ctx->children,
{CypherParser::OR});
}
// XOR.
antlrcpp::Any CypherMainVisitor::visitExpression11(
CypherParser::Expression11Context *ctx) {
return LeftAssociativeOperatorExpression(ctx->expression10(), ctx->children,
{CypherParser::XOR});
}
// AND.
antlrcpp::Any CypherMainVisitor::visitExpression10(
CypherParser::Expression10Context *ctx) {
return LeftAssociativeOperatorExpression(ctx->expression9(), ctx->children,
{CypherParser::AND});
}
// NOT.
antlrcpp::Any CypherMainVisitor::visitExpression9(
CypherParser::Expression9Context *ctx) {
return PrefixUnaryOperator(ctx->expression8(), ctx->children,
{CypherParser::NOT});
}
// Comparisons.
// Expresion 1 < 2 < 3 is converted to 1 < 2 && 2 < 3 and then binary operator
// ast node is constructed for each operator.
antlrcpp::Any CypherMainVisitor::visitExpression8(
CypherParser::Expression8Context *ctx) {
if (!ctx->partialComparisonExpression().size()) {
// There is no comparison operators. We generate expression7.
return ctx->expression7()->accept(this);
}
// There is at least one comparison. We need to generate code for each of
// them. We don't call visitPartialComparisonExpression but do everything in
// this function and call expression7-s directly. Since every expression7
// can be generated twice (because it can appear in two comparisons) code
// generated by whole subtree of expression7 must not have any sideeffects.
// We handle chained comparisons as defined by mathematics, neo4j handles
// them in a very interesting, illogical and incomprehensible way. For
// example in neo4j:
// 1 < 2 < 3 -> true,
// 1 < 2 < 3 < 4 -> false,
// 5 > 3 < 5 > 3 -> true,
// 4 <= 5 < 7 > 6 -> false
// All of those comparisons evaluate to true in memgraph.
std::vector<Expression *> children;
children.push_back(ctx->expression7()->accept(this));
std::vector<size_t> operators;
auto partial_comparison_expressions = ctx->partialComparisonExpression();
for (auto *child : partial_comparison_expressions) {
children.push_back(child->expression7()->accept(this));
}
// First production is comparison operator.
for (auto *child : partial_comparison_expressions) {
operators.push_back(
dynamic_cast<antlr4::tree::TerminalNode *>(child->children[0])
->getSymbol()
->getType());
}
// Make all comparisons.
Expression *first_operand = children[0];
std::vector<Expression *> comparisons;
for (int i = 0; i < (int)operators.size(); ++i) {
auto *expr = children[i + 1];
// TODO: first_operand should only do lookup if it is only calculated and
// not recalculated whole subexpression once again. SymbolGenerator should
// generate symbol for every expresion and then lookup would be possible.
comparisons.push_back(
CreateBinaryOperatorByToken(operators[i], first_operand, expr));
first_operand = expr;
}
first_operand = comparisons[0];
// Calculate logical and of results of comparisons.
for (int i = 1; i < (int)comparisons.size(); ++i) {
first_operand = storage_.Create<AndOperator>(first_operand, comparisons[i]);
}
return first_operand;
}
antlrcpp::Any CypherMainVisitor::visitPartialComparisonExpression(
CypherParser::PartialComparisonExpressionContext *) {
DLOG(FATAL) << "Should never be called. See documentation in hpp.";
return 0;
}
// Addition and subtraction.
antlrcpp::Any CypherMainVisitor::visitExpression7(
CypherParser::Expression7Context *ctx) {
return LeftAssociativeOperatorExpression(ctx->expression6(), ctx->children,
{kPlusTokenId, kMinusTokenId});
}
// Multiplication, division, modding.
antlrcpp::Any CypherMainVisitor::visitExpression6(
CypherParser::Expression6Context *ctx) {
return LeftAssociativeOperatorExpression(
ctx->expression5(), ctx->children,
{kMultTokenId, kDivTokenId, kModTokenId});
}
// Power.
antlrcpp::Any CypherMainVisitor::visitExpression5(
CypherParser::Expression5Context *ctx) {
if (ctx->expression4().size() > 1U) {
// TODO: implement power operator. In neo4j power is left associative and
// int^int -> float.
throw utils::NotYetImplemented("power (^) operator");
}
return visitChildren(ctx);
}
// Unary minus and plus.
antlrcpp::Any CypherMainVisitor::visitExpression4(
CypherParser::Expression4Context *ctx) {
return PrefixUnaryOperator(ctx->expression3a(), ctx->children,
{kUnaryPlusTokenId, kUnaryMinusTokenId});
}
// IS NULL, IS NOT NULL, STARTS WITH, ..
antlrcpp::Any CypherMainVisitor::visitExpression3a(
CypherParser::Expression3aContext *ctx) {
Expression *expression = ctx->expression3b()->accept(this);
for (auto *op : ctx->stringAndNullOperators()) {
if (op->IS() && op->NOT() && op->CYPHERNULL()) {
expression = static_cast<Expression *>(storage_.Create<NotOperator>(
storage_.Create<IsNullOperator>(expression)));
} else if (op->IS() && op->CYPHERNULL()) {
expression = static_cast<Expression *>(
storage_.Create<IsNullOperator>(expression));
} else if (op->IN()) {
expression = static_cast<Expression *>(storage_.Create<InListOperator>(
expression, op->expression3b()->accept(this)));
} else {
std::string function_name;
if (op->STARTS() && op->WITH()) {
function_name = kStartsWith;
} else if (op->ENDS() && op->WITH()) {
function_name = kEndsWith;
} else if (op->CONTAINS()) {
function_name = kContains;
} else {
throw utils::NotYetImplemented("function '{}'", op->getText());
}
auto expression2 = op->expression3b()->accept(this);
std::vector<Expression *> args = {expression, expression2};
expression = static_cast<Expression *>(
storage_.Create<Function>(function_name, args));
}
}
return expression;
}
antlrcpp::Any CypherMainVisitor::visitStringAndNullOperators(
CypherParser::StringAndNullOperatorsContext *) {
DLOG(FATAL) << "Should never be called. See documentation in hpp.";
return 0;
}
antlrcpp::Any CypherMainVisitor::visitExpression3b(
CypherParser::Expression3bContext *ctx) {
Expression *expression = ctx->expression2a()->accept(this);
for (auto *list_op : ctx->listIndexingOrSlicing()) {
if (list_op->getTokens(kDotsTokenId).size() == 0U) {
// If there is no '..' then we need to create list indexing operator.
expression = storage_.Create<ListMapIndexingOperator>(
expression, list_op->expression()[0]->accept(this));
} else if (!list_op->lower_bound && !list_op->upper_bound) {
throw SemanticException(
"List slicing operator requires at least one bound.");
} else {
Expression *lower_bound_ast =
list_op->lower_bound
? static_cast<Expression *>(list_op->lower_bound->accept(this))
: nullptr;
Expression *upper_bound_ast =
list_op->upper_bound
? static_cast<Expression *>(list_op->upper_bound->accept(this))
: nullptr;
expression = storage_.Create<ListSlicingOperator>(
expression, lower_bound_ast, upper_bound_ast);
}
}
return expression;
}
antlrcpp::Any CypherMainVisitor::visitListIndexingOrSlicing(
CypherParser::ListIndexingOrSlicingContext *) {
DLOG(FATAL) << "Should never be called. See documentation in hpp.";
return 0;
}
antlrcpp::Any CypherMainVisitor::visitExpression2a(
CypherParser::Expression2aContext *ctx) {
Expression *expression = ctx->expression2b()->accept(this);
if (ctx->nodeLabels()) {
auto labels =
ctx->nodeLabels()->accept(this).as<std::vector<storage::Label>>();
expression = storage_.Create<LabelsTest>(expression, labels);
}
return expression;
}
antlrcpp::Any CypherMainVisitor::visitExpression2b(
CypherParser::Expression2bContext *ctx) {
Expression *expression = ctx->atom()->accept(this);
for (auto *lookup : ctx->propertyLookup()) {
std::pair<std::string, storage::Property> key = lookup->accept(this);
auto property_lookup =
storage_.Create<PropertyLookup>(expression, key.first, key.second);
expression = property_lookup;
}
return expression;
}
antlrcpp::Any CypherMainVisitor::visitAtom(CypherParser::AtomContext *ctx) {
if (ctx->literal()) {
return ctx->literal()->accept(this);
} else if (ctx->parameter()) {
return static_cast<Expression *>(
ctx->parameter()->accept(this).as<ParameterLookup *>());
} else if (ctx->parenthesizedExpression()) {
return static_cast<Expression *>(
ctx->parenthesizedExpression()->accept(this));
} else if (ctx->variable()) {
std::string variable = ctx->variable()->accept(this);
users_identifiers.insert(variable);
return static_cast<Expression *>(storage_.Create<Identifier>(variable));
} else if (ctx->functionInvocation()) {
return static_cast<Expression *>(ctx->functionInvocation()->accept(this));
} else if (ctx->COUNT()) {
// Here we handle COUNT(*). COUNT(expression) is handled in
// visitFunctionInvocation with other aggregations. This is visible in
// functionInvocation and atom producions in opencypher grammar.
return static_cast<Expression *>(
storage_.Create<Aggregation>(nullptr, nullptr, Aggregation::Op::COUNT));
} else if (ctx->ALL()) {
auto *ident = storage_.Create<Identifier>(ctx->filterExpression()
->idInColl()
->variable()
->accept(this)
.as<std::string>());
Expression *list_expr =
ctx->filterExpression()->idInColl()->expression()->accept(this);
Where *where = ctx->filterExpression()->where()->accept(this);
return static_cast<Expression *>(
storage_.Create<All>(ident, list_expr, where));
} else if (ctx->caseExpression()) {
return static_cast<Expression *>(ctx->caseExpression()->accept(this));
}
// TODO: Implement this. We don't support comprehensions, filtering... at
// the moment.
throw utils::NotYetImplemented("atom expression '{}'", ctx->getText());
}
antlrcpp::Any CypherMainVisitor::visitParameter(
CypherParser::ParameterContext *ctx) {
return storage_.Create<ParameterLookup>(ctx->getStart()->getTokenIndex());
}
antlrcpp::Any CypherMainVisitor::visitLiteral(
CypherParser::LiteralContext *ctx) {
if (ctx->CYPHERNULL() || ctx->StringLiteral() || ctx->booleanLiteral() ||
ctx->numberLiteral()) {
int token_position = ctx->getStart()->getTokenIndex();
if (ctx->CYPHERNULL()) {
return static_cast<Expression *>(
storage_.Create<PrimitiveLiteral>(TypedValue::Null, token_position));
} else if (ctx_.is_query_cached_) {
// Instead of generating PrimitiveLiteral, we generate a ParameterLookup,
// so that the AST can be cached. This allows for varying literals, which
// are then looked up in the parameters table (even though they are not
// user provided). Note, that NULL always generates a PrimitiveLiteral.
return static_cast<Expression *>(
storage_.Create<ParameterLookup>(token_position));
} else if (ctx->StringLiteral()) {
return static_cast<Expression *>(storage_.Create<PrimitiveLiteral>(
visitStringLiteral(ctx->StringLiteral()->getText()).as<std::string>(),
token_position));
} else if (ctx->booleanLiteral()) {
return static_cast<Expression *>(storage_.Create<PrimitiveLiteral>(
ctx->booleanLiteral()->accept(this).as<bool>(), token_position));
} else if (ctx->numberLiteral()) {
return static_cast<Expression *>(storage_.Create<PrimitiveLiteral>(
ctx->numberLiteral()->accept(this).as<TypedValue>(), token_position));
}
LOG(FATAL) << "Expected to handle all cases above";
} else if (ctx->listLiteral()) {
return static_cast<Expression *>(storage_.Create<ListLiteral>(
ctx->listLiteral()->accept(this).as<std::vector<Expression *>>()));
} else {
return static_cast<Expression *>(storage_.Create<MapLiteral>(
ctx->mapLiteral()
->accept(this)
.as<std::unordered_map<std::pair<std::string, storage::Property>,
Expression *>>()));
}
return visitChildren(ctx);
}
antlrcpp::Any CypherMainVisitor::visitParenthesizedExpression(
CypherParser::ParenthesizedExpressionContext *ctx) {
return static_cast<Expression *>(ctx->expression()->accept(this));
}
antlrcpp::Any CypherMainVisitor::visitNumberLiteral(
CypherParser::NumberLiteralContext *ctx) {
if (ctx->integerLiteral()) {
return TypedValue(ctx->integerLiteral()->accept(this).as<int64_t>());
} else if (ctx->doubleLiteral()) {
return TypedValue(ctx->doubleLiteral()->accept(this).as<double>());
} else {
// This should never happen, except grammar changes and we don't notice
// change in this production.
DLOG(FATAL) << "can't happen";
throw std::exception();
}
}
antlrcpp::Any CypherMainVisitor::visitFunctionInvocation(
CypherParser::FunctionInvocationContext *ctx) {
if (ctx->DISTINCT()) {
throw utils::NotYetImplemented("DISTINCT function call");
}
std::string function_name = ctx->functionName()->accept(this);
std::vector<Expression *> expressions;
for (auto *expression : ctx->expression()) {
expressions.push_back(expression->accept(this));
}
if (expressions.size() == 1U) {
if (function_name == Aggregation::kCount) {
return static_cast<Expression *>(storage_.Create<Aggregation>(
expressions[0], nullptr, Aggregation::Op::COUNT));
}
if (function_name == Aggregation::kMin) {
return static_cast<Expression *>(storage_.Create<Aggregation>(
expressions[0], nullptr, Aggregation::Op::MIN));
}
if (function_name == Aggregation::kMax) {
return static_cast<Expression *>(storage_.Create<Aggregation>(
expressions[0], nullptr, Aggregation::Op::MAX));
}
if (function_name == Aggregation::kSum) {
return static_cast<Expression *>(storage_.Create<Aggregation>(
expressions[0], nullptr, Aggregation::Op::SUM));
}
if (function_name == Aggregation::kAvg) {
return static_cast<Expression *>(storage_.Create<Aggregation>(
expressions[0], nullptr, Aggregation::Op::AVG));
}
if (function_name == Aggregation::kCollect) {
return static_cast<Expression *>(storage_.Create<Aggregation>(
expressions[0], nullptr, Aggregation::Op::COLLECT_LIST));
}
}
if (expressions.size() == 2U && function_name == Aggregation::kCollect) {
return static_cast<Expression *>(storage_.Create<Aggregation>(
expressions[1], expressions[0], Aggregation::Op::COLLECT_MAP));
}
auto function = NameToFunction(function_name);
if (!function)
throw SemanticException("Function '{}' doesn't exist.", function_name);
return static_cast<Expression *>(
storage_.Create<Function>(function_name, expressions));
}
antlrcpp::Any CypherMainVisitor::visitFunctionName(
CypherParser::FunctionNameContext *ctx) {
return utils::ToUpperCase(ctx->getText());
}
antlrcpp::Any CypherMainVisitor::visitDoubleLiteral(
CypherParser::DoubleLiteralContext *ctx) {
return ParseDoubleLiteral(ctx->getText());
}
antlrcpp::Any CypherMainVisitor::visitIntegerLiteral(
CypherParser::IntegerLiteralContext *ctx) {
return ParseIntegerLiteral(ctx->getText());
}
antlrcpp::Any CypherMainVisitor::visitStringLiteral(
const std::string &escaped) {
return ParseStringLiteral(escaped);
}
antlrcpp::Any CypherMainVisitor::visitBooleanLiteral(
CypherParser::BooleanLiteralContext *ctx) {
if (ctx->getTokens(CypherParser::TRUE).size()) {
return true;
}
if (ctx->getTokens(CypherParser::FALSE).size()) {
return false;
}
DLOG(FATAL) << "Shouldn't happend";
throw std::exception();
}
antlrcpp::Any CypherMainVisitor::visitCypherDelete(
CypherParser::CypherDeleteContext *ctx) {
auto *del = storage_.Create<Delete>();
if (ctx->DETACH()) {
del->detach_ = true;
}
for (auto *expression : ctx->expression()) {
del->expressions_.push_back(expression->accept(this));
}
return del;
}
antlrcpp::Any CypherMainVisitor::visitWhere(CypherParser::WhereContext *ctx) {
auto *where = storage_.Create<Where>();
where->expression_ = ctx->expression()->accept(this);
return where;
}
antlrcpp::Any CypherMainVisitor::visitSet(CypherParser::SetContext *ctx) {
std::vector<Clause *> set_items;
for (auto *set_item : ctx->setItem()) {
set_items.push_back(set_item->accept(this));
}
return set_items;
}
antlrcpp::Any CypherMainVisitor::visitSetItem(
CypherParser::SetItemContext *ctx) {
// SetProperty
if (ctx->propertyExpression()) {
auto *set_property = storage_.Create<SetProperty>();
set_property->property_lookup_ = ctx->propertyExpression()->accept(this);
set_property->expression_ = ctx->expression()->accept(this);
return static_cast<Clause *>(set_property);
}
// SetProperties either assignment or update
if (ctx->getTokens(kPropertyAssignmentTokenId).size() ||
ctx->getTokens(kPropertyUpdateTokenId).size()) {
auto *set_properties = storage_.Create<SetProperties>();
set_properties->identifier_ = storage_.Create<Identifier>(
ctx->variable()->accept(this).as<std::string>());
set_properties->expression_ = ctx->expression()->accept(this);
if (ctx->getTokens(kPropertyUpdateTokenId).size()) {
set_properties->update_ = true;
}
return static_cast<Clause *>(set_properties);
}
// SetLabels
auto *set_labels = storage_.Create<SetLabels>();
set_labels->identifier_ = storage_.Create<Identifier>(
ctx->variable()->accept(this).as<std::string>());
set_labels->labels_ =
ctx->nodeLabels()->accept(this).as<std::vector<storage::Label>>();
return static_cast<Clause *>(set_labels);
}
antlrcpp::Any CypherMainVisitor::visitRemove(CypherParser::RemoveContext *ctx) {
std::vector<Clause *> remove_items;
for (auto *remove_item : ctx->removeItem()) {
remove_items.push_back(remove_item->accept(this));
}
return remove_items;
}
antlrcpp::Any CypherMainVisitor::visitRemoveItem(
CypherParser::RemoveItemContext *ctx) {
// RemoveProperty
if (ctx->propertyExpression()) {
auto *remove_property = storage_.Create<RemoveProperty>();
remove_property->property_lookup_ = ctx->propertyExpression()->accept(this);
return static_cast<Clause *>(remove_property);
}
// RemoveLabels
auto *remove_labels = storage_.Create<RemoveLabels>();
remove_labels->identifier_ = storage_.Create<Identifier>(
ctx->variable()->accept(this).as<std::string>());
remove_labels->labels_ =
ctx->nodeLabels()->accept(this).as<std::vector<storage::Label>>();
return static_cast<Clause *>(remove_labels);
}
antlrcpp::Any CypherMainVisitor::visitPropertyExpression(
CypherParser::PropertyExpressionContext *ctx) {
Expression *expression = ctx->atom()->accept(this);
for (auto *lookup : ctx->propertyLookup()) {
std::pair<std::string, storage::Property> key = lookup->accept(this);
auto property_lookup =
storage_.Create<PropertyLookup>(expression, key.first, key.second);
expression = property_lookup;
}
// It is guaranteed by grammar that there is at least one propertyLookup.
return dynamic_cast<PropertyLookup *>(expression);
}
antlrcpp::Any CypherMainVisitor::visitCaseExpression(
CypherParser::CaseExpressionContext *ctx) {
Expression *test_expression =
ctx->test ? ctx->test->accept(this).as<Expression *>() : nullptr;
auto alternatives = ctx->caseAlternatives();
// Reverse alternatives so that tree of IfOperators can be built bottom-up.
std::reverse(alternatives.begin(), alternatives.end());
Expression *else_expression =
ctx->else_expression
? ctx->else_expression->accept(this).as<Expression *>()
: storage_.Create<PrimitiveLiteral>(TypedValue::Null);
for (auto *alternative : alternatives) {
Expression *condition =
test_expression
? storage_.Create<EqualOperator>(
test_expression, alternative->when_expression->accept(this))
: alternative->when_expression->accept(this).as<Expression *>();
Expression *then_expression = alternative->then_expression->accept(this);
else_expression = storage_.Create<IfOperator>(condition, then_expression,
else_expression);
}
return else_expression;
}
antlrcpp::Any CypherMainVisitor::visitCaseAlternatives(
CypherParser::CaseAlternativesContext *) {
DLOG(FATAL) << "Should never be called. See documentation in hpp.";
return 0;
}
antlrcpp::Any CypherMainVisitor::visitWith(CypherParser::WithContext *ctx) {
auto *with = storage_.Create<With>();
in_with_ = true;
with->body_ = ctx->returnBody()->accept(this);
in_with_ = false;
if (ctx->DISTINCT()) {
with->body_.distinct = true;
}
if (ctx->where()) {
with->where_ = ctx->where()->accept(this);
}
return with;
}
antlrcpp::Any CypherMainVisitor::visitMerge(CypherParser::MergeContext *ctx) {
auto *merge = storage_.Create<Merge>();
merge->pattern_ = ctx->patternPart()->accept(this);
for (auto &merge_action : ctx->mergeAction()) {
auto set = merge_action->set()->accept(this).as<std::vector<Clause *>>();
if (merge_action->MATCH()) {
merge->on_match_.insert(merge->on_match_.end(), set.begin(), set.end());
} else {
DCHECK(merge_action->CREATE()) << "Expected ON MATCH or ON CREATE";
merge->on_create_.insert(merge->on_create_.end(), set.begin(), set.end());
}
}
return merge;
}
antlrcpp::Any CypherMainVisitor::visitUnwind(CypherParser::UnwindContext *ctx) {
auto *named_expr = storage_.Create<NamedExpression>();
named_expr->expression_ = ctx->expression()->accept(this);
named_expr->name_ =
std::string(ctx->variable()->accept(this).as<std::string>());
return storage_.Create<Unwind>(named_expr);
}
antlrcpp::Any CypherMainVisitor::visitFilterExpression(
CypherParser::FilterExpressionContext *) {
LOG(FATAL) << "Should never be called. See documentation in hpp.";
return 0;
}
} // namespace query::frontend