it compilesga -A!

This commit is contained in:
Marko Budiselic 2017-03-12 00:04:10 +01:00
parent 026e0e6fbd
commit 1f75572d5e
11 changed files with 376 additions and 409 deletions

View File

@ -61,12 +61,12 @@ State state_executor_run(RecordStream<Socket> &output_stream, BoltDecoder &decod
{{"code", "Memgraph.SyntaxException"}, {"message", "Syntax error"}});
output_stream.send();
return ERROR;
} catch (const backend::cpp::GeneratorException &e) {
output_stream.write_failure(
{{"code", "Memgraph.GeneratorException"},
{"message", "Unsupported query"}});
output_stream.send();
return ERROR;
// } catch (const backend::cpp::GeneratorException &e) {
// output_stream.write_failure(
// {{"code", "Memgraph.GeneratorException"},
// {"message", "Unsupported query"}});
// output_stream.send();
// return ERROR;
} catch (const QueryEngineException &e) {
output_stream.write_failure(
{{"code", "Memgraph.QueryEngineException"},

View File

@ -1,38 +0,0 @@
#pragma once
#include "antlr4-runtime.h"
#include "query/frontend/ast/cypher_main_visitor.hpp"
#include <experimental/filesystem>
namespace fs = std::experimental::filesystem;
namespace backend {
namespace cpp {
using namespace antlr4;
class GeneratorException : public BasicException {
public:
using BasicException::BasicException;
GeneratorException() : BasicException("") {}
};
/**
* Traverse Antlr tree::ParseTree generated from Cypher grammar and generate
* C++.
*/
class Generator {
public:
/**
* Generates cpp code inside file on the path.
*/
Generator(tree::ParseTree *tree, const std::string &query,
const uint64_t stripped_hash, const fs::path &path) {
throw GeneratorException("unsupported query");
CypherMainVisitor visitor;
visitor.visit(tree);
}
};
}
}

View File

@ -3,9 +3,10 @@
namespace query {
void HighLevelAstConversion::Apply(Context &ctx,
std::shared_ptr<Query> HighLevelAstConversion::Apply(Context &ctx,
antlr4::tree::ParseTree *tree) {
query::frontend::CypherMainVisitor visitor(ctx);
visitor.visit(tree);
return visitor.query();
}
}

View File

@ -10,6 +10,7 @@ class TypedcheckedTree {};
class LogicalPlan {};
class Context;
class Query;
class LogicalPlanGenerator {
public:
@ -48,6 +49,6 @@ private:
class HighLevelAstConversion {
public:
void Apply(Context &ctx, antlr4::tree::ParseTree *tree);
std::shared_ptr<Query> Apply(Context &ctx, antlr4::tree::ParseTree *tree);
};
}

View File

@ -7,7 +7,6 @@ namespace fs = std::experimental::filesystem;
#include "data_structures/concurrent/concurrent_map.hpp"
#include "database/graph_db.hpp"
#include "logging/loggable.hpp"
#include "query/backend/cpp/generator.hpp"
#include "query/exception/query_engine.hpp"
#include "query/frontend/opencypher/parser.hpp"
#include "query/plan_compiler.hpp"
@ -137,8 +136,8 @@ private:
std::to_string(stripped.hash) + ".cpp");
frontend::opencypher::Parser parser(stripped.query);
backend::cpp::Generator(parser.tree(), stripped.query, stripped.hash,
generated_path);
// backend::cpp::Generator(parser.tree(), stripped.query, stripped.hash,
// generated_path);
return LoadCpp(generated_path, stripped.hash);
}

View File

@ -1,5 +1,8 @@
#pragma once
#include "query/frontend/interpret/interpret.hpp"
#include "query/frontend/logical/planner.hpp"
#include "query/context.hpp"
#include "database/graph_db_accessor.hpp"
#include "query/context.hpp"
#include "query/frontend/opencypher/parser.hpp"
@ -21,35 +24,32 @@ class Engine {
auto low_level_tree = parser.tree();
// AST -> high level tree
HighLevelAstConversion low2high_tree;
auto high_level_tree = low2high_tree.Apply(ctx, low_level_tree);
// symbol table fill
SymbolTable symbol_table;
TypeCheckVisitor typecheck_visitor;
high_level_tree.Accept(typecheck_visitor);
TypeCheckVisitor typecheck_visitor(symbol_table);
high_level_tree->Accept(typecheck_visitor);
// high level tree -> logical plan
auto logical_plan = query::Apply(high_level_tree);
auto logical_plan = Apply(*high_level_tree);
// generate frame based on symbol table max_position
Frame frame(symbol_table.max_position());
// interpret
auto cursor = logical_plan.MakeCursor(frame);
logical_plan.WriteHeader(stream);
auto symbols = logical_plan.OutputSymbols(symbol_table);
while (cursor.pull(frame, context)) {
auto cursor = logical_plan->MakeCursor(db_accessor);
logical_plan->WriteHeader(stream);
auto symbols = logical_plan->OutputSymbols(symbol_table);
while (cursor->pull(frame, symbol_table)) {
std::vector<TypedValue> values(symbols.size());
for (auto symbol : symbols) {
values.emplace_back(frame[symbol]);
values.emplace_back(frame[symbol.position_]);
}
stream.Result(values);
}
stream.Summary({"type" : "r"});
stream.Summary({{std::string("type"), TypedValue("r")}});
}
private:
Context ctx;
HighLevelAstConversion low2high_tree;
};
}

View File

@ -281,6 +281,9 @@ private:
antlrcpp::Any
visitIntegerLiteral(CypherParser::IntegerLiteralContext *ctx) override;
public:
std::shared_ptr<Query> query() { return query_; }
private:
Context &ctx_;
int next_ident_id_;

View File

@ -82,7 +82,7 @@ class ScanAll : public LogicalOperator {
for (auto label : node_part->labels_) {
if (!vertex.has_label(label)) return false;
}
frame[symbol_table[node_part->identifier_].position_] = vertex;
frame[symbol_table[*node_part->identifier_].position_] = vertex;
return true;
}
};

View File

@ -18,10 +18,10 @@ std::shared_ptr<LogicalOperator> GenMatch(
throw std::runtime_error("Not implemented");
}
auto& pattern = match.patterns_[0];
if (pattern->node_parts_.size() != 1) {
if (pattern->parts_.size() != 1) {
throw std::runtime_error("Not implemented");
}
auto& node_part = pattern->node_parts_[0];
auto node_part = std::dynamic_pointer_cast<NodePart>(pattern->parts_[0]);
return std::make_shared<ScanAll>(node_part);
}

View File

@ -5,6 +5,7 @@
#include "logging/streams/stdout.cpp"
#include "query/entry.hpp"
#include "query/backend/cpp/typed_value.hpp"
#include "query/frontend/logical/operator.hpp"
using std::cout;
using std::cin;
@ -20,8 +21,8 @@ int main(int argc, char* argv[]) {
// init db context
Dbms dbms;
ConsoleResultStream stream;
query::Engine<ConsoleResultStream> query_engine;
query::ConsoleResultStream stream;
query::Engine<query::ConsoleResultStream> query_engine;
// initialize the database
auto dba = dbms.active();

View File

@ -13,348 +13,348 @@
using namespace ::testing;
namespace {
using query::Context;
using namespace query::frontend;
class ParserTables {
template <typename T>
auto FilterAnies(std::unordered_map<std::string, antlrcpp::Any> map) {
std::unordered_map<std::string, T> filtered;
for (auto x : map) {
if (x.second.is<T>()) {
filtered[x.first] = x.second.as<T>();
}
}
return filtered;
}
public:
ParserTables(const std::string &query) {
frontend::opencypher::Parser parser(query);
auto *tree = parser.tree();
CypherMainVisitor visitor;
visitor.visit(tree);
identifiers_map_ = visitor.ids_map().back();
symbol_table_ = visitor.symbol_table();
pattern_parts_ = FilterAnies<PatternPart>(symbol_table_);
nodes_ = FilterAnies<Node>(symbol_table_);
relationships_ = FilterAnies<Relationship>(symbol_table_);
}
std::unordered_map<std::string, std::string> identifiers_map_;
std::unordered_map<std::string, antlrcpp::Any> symbol_table_;
std::unordered_map<std::string, PatternPart> pattern_parts_;
std::unordered_map<std::string, Node> nodes_;
std::unordered_map<std::string, Relationship> relationships_;
};
// TODO: Once expression evaluation is implemented, we should also test if
// property values are equal.
void CompareNodes(std::pair<std::string, Node> node_entry,
std::vector<std::string> labels,
std::vector<std::string> property_keys) {
auto node = node_entry.second;
ASSERT_EQ(node_entry.first, node.output_id);
ASSERT_THAT(node.labels,
UnorderedElementsAreArray(labels.begin(), labels.end()));
std::vector<std::string> node_property_keys;
for (auto x : node.properties) {
node_property_keys.push_back(x.first);
}
ASSERT_THAT(
node_property_keys,
UnorderedElementsAreArray(property_keys.begin(), property_keys.end()));
}
// If has_range is false, lower and upper bound values are ignored.
// TODO: Once expression evaluation is implemented, we should also test if
// property values are equal.
void CompareRelationships(
std::pair<std::string, Relationship> relationship_entry,
Relationship::Direction direction, std::vector<std::string> types,
std::vector<std::string> property_keys, bool has_range,
int64_t lower_bound = 1LL, int64_t upper_bound = LLONG_MAX) {
auto relationship = relationship_entry.second;
ASSERT_EQ(relationship_entry.first, relationship.output_id);
ASSERT_EQ(relationship.direction, direction);
ASSERT_THAT(relationship.types,
UnorderedElementsAreArray(types.begin(), types.end()));
std::vector<std::string> relationship_property_keys;
for (auto x : relationship.properties) {
relationship_property_keys.push_back(x.first);
}
ASSERT_THAT(
relationship_property_keys,
UnorderedElementsAreArray(property_keys.begin(), property_keys.end()));
ASSERT_EQ(relationship.has_range, has_range);
if (!has_range)
return;
ASSERT_EQ(relationship.lower_bound, lower_bound);
ASSERT_EQ(relationship.upper_bound, upper_bound);
}
// SyntaxException on incorrect syntax.
TEST(CompilerStructuresTest, SyntaxException) {
ASSERT_THROW(ParserTables("CREATE ()-[*1...2]-()"),
frontend::opencypher::SyntaxException);
}
// Empty node.
TEST(CompilerStructuresTest, NodePatternEmpty) {
ParserTables parser("CREATE ()");
ASSERT_EQ(parser.identifiers_map_.size(), 0U);
ASSERT_EQ(parser.nodes_.size(), 1U);
CompareNodes(*parser.nodes_.begin(), {}, {});
}
// Node with variable.
TEST(CompilerStructuresTest, NodePatternVariable) {
ParserTables parser("CREATE (var)");
ASSERT_EQ(parser.identifiers_map_.size(), 1U);
ASSERT_NE(parser.identifiers_map_.find("var"), parser.identifiers_map_.end());
ASSERT_EQ(parser.nodes_.size(), 1U);
auto output_identifier = parser.identifiers_map_["var"];
ASSERT_NE(parser.nodes_.find(output_identifier), parser.nodes_.end());
CompareNodes(*parser.nodes_.begin(), {}, {});
}
// Node with labels.
TEST(CompilerStructuresTest, NodePatternLabels) {
ParserTables parser("CREATE (:label1:label2:label3)");
ASSERT_EQ(parser.identifiers_map_.size(), 0U);
ASSERT_EQ(parser.nodes_.size(), 1U);
CompareNodes(*parser.nodes_.begin(), {"label1", "label2", "label3"}, {});
}
// Node with properties.
TEST(CompilerStructuresTest, NodePatternProperties) {
ParserTables parser("CREATE ({age: 5, name: \"John\", surname: \"Smith\"})");
ASSERT_EQ(parser.identifiers_map_.size(), 0U);
ASSERT_EQ(parser.nodes_.size(), 1U);
CompareNodes(*parser.nodes_.begin(), {}, {"age", "name", "surname"});
}
// Relationship without relationship details.
TEST(CompilerStructuresTest, RelationshipPatternNoDetails) {
ParserTables parser("CREATE ()--()");
ASSERT_EQ(parser.identifiers_map_.size(), 0U);
ASSERT_EQ(parser.relationships_.size(), 1U);
CompareRelationships(*parser.relationships_.begin(),
Relationship::Direction::BOTH, {}, {}, false);
}
// Relationship with empty relationship details.
TEST(CompilerStructuresTest, RelationshipPatternEmptyDetails) {
ParserTables parser("CREATE ()-[]-()");
ASSERT_EQ(parser.identifiers_map_.size(), 0U);
ASSERT_EQ(parser.relationships_.size(), 1U);
CompareRelationships(*parser.relationships_.begin(),
Relationship::Direction::BOTH, {}, {}, false);
}
// Relationship with left direction.
TEST(CompilerStructuresTest, RelationshipPatternLeftDirection) {
ParserTables parser("CREATE ()<--()");
ASSERT_EQ(parser.identifiers_map_.size(), 0U);
ASSERT_EQ(parser.relationships_.size(), 1U);
CompareRelationships(*parser.relationships_.begin(),
Relationship::Direction::LEFT, {}, {}, false);
}
// Relationship with right direction.
TEST(CompilerStructuresTest, RelationshipPatternRightDirection) {
ParserTables parser("CREATE ()-[]->()");
ASSERT_EQ(parser.identifiers_map_.size(), 0U);
ASSERT_EQ(parser.relationships_.size(), 1U);
CompareRelationships(*parser.relationships_.begin(),
Relationship::Direction::RIGHT, {}, {}, false);
}
// Relationship with both directions.
TEST(CompilerStructuresTest, RelationshipPatternBothDirection) {
ParserTables parser("CREATE ()<-[]->()");
ASSERT_EQ(parser.identifiers_map_.size(), 0U);
ASSERT_EQ(parser.relationships_.size(), 1U);
CompareRelationships(*parser.relationships_.begin(),
Relationship::Direction::BOTH, {}, {}, false);
}
// Relationship with unbounded variable range.
TEST(CompilerStructuresTest, RelationshipPatternUnbounded) {
ParserTables parser("CREATE ()-[*]-()");
ASSERT_EQ(parser.identifiers_map_.size(), 0U);
ASSERT_EQ(parser.relationships_.size(), 1U);
CompareRelationships(*parser.relationships_.begin(),
Relationship::Direction::BOTH, {}, {}, true, 1,
LLONG_MAX);
}
// Relationship with lower bounded variable range.
TEST(CompilerStructuresTest, RelationshipPatternLowerBounded) {
ParserTables parser("CREATE ()-[*5..]-()");
ASSERT_EQ(parser.identifiers_map_.size(), 0U);
ASSERT_EQ(parser.relationships_.size(), 1U);
CompareRelationships(*parser.relationships_.begin(),
Relationship::Direction::BOTH, {}, {}, true, 5,
LLONG_MAX);
}
// Relationship with upper bounded variable range.
TEST(CompilerStructuresTest, RelationshipPatternUpperBounded) {
ParserTables parser("CREATE ()-[*..10]-()");
ASSERT_EQ(parser.identifiers_map_.size(), 0U);
ASSERT_EQ(parser.relationships_.size(), 1U);
CompareRelationships(*parser.relationships_.begin(),
Relationship::Direction::BOTH, {}, {}, true, 1, 10);
}
// Relationship with lower and upper bounded variable range.
TEST(CompilerStructuresTest, RelationshipPatternLowerUpperBounded) {
ParserTables parser("CREATE ()-[*5..10]-()");
ASSERT_EQ(parser.identifiers_map_.size(), 0U);
ASSERT_EQ(parser.relationships_.size(), 1U);
CompareRelationships(*parser.relationships_.begin(),
Relationship::Direction::BOTH, {}, {}, true, 5, 10);
}
// Relationship with fixed number of edges.
TEST(CompilerStructuresTest, RelationshipPatternFixedRange) {
ParserTables parser("CREATE ()-[*10]-()");
ASSERT_EQ(parser.identifiers_map_.size(), 0U);
ASSERT_EQ(parser.relationships_.size(), 1U);
CompareRelationships(*parser.relationships_.begin(),
Relationship::Direction::BOTH, {}, {}, true, 10, 10);
}
// Relationship with invalid bound (larger than long long).
TEST(CompilerStructuresTest, RelationshipPatternInvalidBound) {
ASSERT_THROW(
ParserTables parser("CREATE ()-[*100000000000000000000000000]-()"),
SemanticException);
}
// Relationship with variable
TEST(CompilerStructuresTest, RelationshipPatternVariable) {
ParserTables parser("CREATE ()-[var]-()");
ASSERT_EQ(parser.identifiers_map_.size(), 1U);
ASSERT_NE(parser.identifiers_map_.find("var"), parser.identifiers_map_.end());
ASSERT_EQ(parser.relationships_.size(), 1U);
auto output_identifier = parser.identifiers_map_["var"];
ASSERT_NE(parser.relationships_.find(output_identifier),
parser.relationships_.end());
CompareRelationships(*parser.relationships_.begin(),
Relationship::Direction::BOTH, {}, {}, false);
}
// Relationship with labels.
TEST(CompilerStructuresTest, RelationshipPatternLabels) {
ParserTables parser("CREATE ()-[:label1|label2|:label3]-()");
ASSERT_EQ(parser.identifiers_map_.size(), 0U);
ASSERT_EQ(parser.relationships_.size(), 1U);
CompareRelationships(*parser.relationships_.begin(),
Relationship::Direction::BOTH,
{"label1", "label2", "label3"}, {}, false);
}
// Relationship with properties.
TEST(CompilerStructuresTest, RelationshipPatternProperties) {
ParserTables parser(
"CREATE ()-[{age: 5, name: \"John\", surname: \"Smith\"}]-()");
ASSERT_EQ(parser.identifiers_map_.size(), 0U);
ASSERT_EQ(parser.relationships_.size(), 1U);
CompareRelationships(*parser.relationships_.begin(),
Relationship::Direction::BOTH, {},
{"age", "name", "surname"}, false);
}
// PatternPart.
TEST(CompilerStructuresTest, PatternPart) {
ParserTables parser("CREATE ()--()");
ASSERT_EQ(parser.identifiers_map_.size(), 0U);
ASSERT_EQ(parser.pattern_parts_.size(), 1U);
ASSERT_EQ(parser.relationships_.size(), 1U);
ASSERT_EQ(parser.nodes_.size(), 2U);
ASSERT_EQ(parser.pattern_parts_.begin()->second.nodes.size(), 2U);
ASSERT_EQ(parser.pattern_parts_.begin()->second.relationships.size(), 1U);
}
// PatternPart in braces.
TEST(CompilerStructuresTest, PatternPartBraces) {
ParserTables parser("CREATE ((()--()))");
ASSERT_EQ(parser.identifiers_map_.size(), 0U);
ASSERT_EQ(parser.pattern_parts_.size(), 1U);
ASSERT_EQ(parser.relationships_.size(), 1U);
ASSERT_EQ(parser.nodes_.size(), 2U);
ASSERT_EQ(parser.pattern_parts_.begin()->second.nodes.size(), 2U);
ASSERT_EQ(parser.pattern_parts_.begin()->second.relationships.size(), 1U);
}
// PatternPart with variable.
TEST(CompilerStructuresTest, PatternPartVariable) {
ParserTables parser("CREATE var=()--()");
ASSERT_EQ(parser.identifiers_map_.size(), 1U);
ASSERT_EQ(parser.pattern_parts_.size(), 1U);
ASSERT_EQ(parser.relationships_.size(), 1U);
ASSERT_EQ(parser.nodes_.size(), 2U);
ASSERT_EQ(parser.pattern_parts_.begin()->second.nodes.size(), 2U);
ASSERT_EQ(parser.pattern_parts_.begin()->second.relationships.size(), 1U);
ASSERT_NE(parser.identifiers_map_.find("var"), parser.identifiers_map_.end());
auto output_identifier = parser.identifiers_map_["var"];
ASSERT_NE(parser.pattern_parts_.find(output_identifier),
parser.pattern_parts_.end());
}
// Multiple nodes with same variable and properties.
TEST(CompilerStructuresTest, MultipleNodesWithVariableAndProperties) {
ASSERT_THROW(ParserTables parser("CREATE (a {b: 5})-[]-(a {c: 5})"),
SemanticException);
}
// Multiple nodes with same variable name.
TEST(CompilerStructuresTest, MultipleNodesWithVariable) {
ParserTables parser("CREATE (a {b: 5, c: 5})-[]-(a)");
ASSERT_EQ(parser.identifiers_map_.size(), 1U);
ASSERT_EQ(parser.pattern_parts_.size(), 1U);
ASSERT_EQ(parser.relationships_.size(), 1U);
ASSERT_EQ(parser.nodes_.size(), 1U);
auto pattern_part = parser.pattern_parts_.begin()->second;
ASSERT_EQ(pattern_part.nodes.size(), 2U);
ASSERT_EQ(pattern_part.relationships.size(), 1U);
ASSERT_EQ(pattern_part.nodes[0], pattern_part.nodes[1]);
}
// Multiple relationships with same variable name and properties.
TEST(CompilerStructuresTest, MultipleRelationshipsWithVariableAndProperties) {
ASSERT_THROW(ParserTables parser("CREATE ()-[e {a: 5}]-()-[e {c: 5}]-()"),
SemanticException);
}
// Multiple relationships with same variable name.
TEST(CompilerStructuresTest, MultipleRelationshipsWithVariable) {
ParserTables parser("CREATE ()-[a {a: 5}]-()-[a]-()");
ASSERT_EQ(parser.identifiers_map_.size(), 1U);
ASSERT_EQ(parser.pattern_parts_.size(), 1U);
ASSERT_EQ(parser.relationships_.size(), 1U);
ASSERT_EQ(parser.nodes_.size(), 3U);
auto pattern_part = parser.pattern_parts_.begin()->second;
ASSERT_EQ(pattern_part.nodes.size(), 3U);
ASSERT_EQ(pattern_part.relationships.size(), 2U);
ASSERT_NE(pattern_part.nodes[0], pattern_part.nodes[1]);
ASSERT_NE(pattern_part.nodes[1], pattern_part.nodes[2]);
ASSERT_NE(pattern_part.nodes[0], pattern_part.nodes[2]);
ASSERT_EQ(pattern_part.relationships[0], pattern_part.relationships[1]);
}
// Different structures (nodes, realtionships, patterns) with same variable
// name.
TEST(CompilerStructuresTest, DifferentTypesWithVariable) {
ASSERT_THROW(ParserTables parser("CREATE a=(a)"), SemanticException);
ASSERT_THROW(ParserTables parser("CREATE (a)-[a]-()"), SemanticException);
ASSERT_THROW(ParserTables parser("CREATE a=()-[a]-()"), SemanticException);
}
}
// namespace {
//
// using query::Context;
// using namespace query::frontend;
//
// class ParserTables {
// template <typename T>
// auto FilterAnies(std::unordered_map<std::string, antlrcpp::Any> map) {
// std::unordered_map<std::string, T> filtered;
// for (auto x : map) {
// if (x.second.is<T>()) {
// filtered[x.first] = x.second.as<T>();
// }
// }
// return filtered;
// }
//
// public:
// ParserTables(const std::string &query) {
// frontend::opencypher::Parser parser(query);
// auto *tree = parser.tree();
// CypherMainVisitor visitor;
// visitor.visit(tree);
// identifiers_map_ = visitor.ids_map().back();
// symbol_table_ = visitor.symbol_table();
// pattern_parts_ = FilterAnies<PatternPart>(symbol_table_);
// nodes_ = FilterAnies<Node>(symbol_table_);
// relationships_ = FilterAnies<Relationship>(symbol_table_);
// }
//
// std::unordered_map<std::string, std::string> identifiers_map_;
// std::unordered_map<std::string, antlrcpp::Any> symbol_table_;
// std::unordered_map<std::string, PatternPart> pattern_parts_;
// std::unordered_map<std::string, Node> nodes_;
// std::unordered_map<std::string, Relationship> relationships_;
// };
//
// // TODO: Once expression evaluation is implemented, we should also test if
// // property values are equal.
// void CompareNodes(std::pair<std::string, Node> node_entry,
// std::vector<std::string> labels,
// std::vector<std::string> property_keys) {
// auto node = node_entry.second;
// ASSERT_EQ(node_entry.first, node.output_id);
// ASSERT_THAT(node.labels,
// UnorderedElementsAreArray(labels.begin(), labels.end()));
// std::vector<std::string> node_property_keys;
// for (auto x : node.properties) {
// node_property_keys.push_back(x.first);
// }
// ASSERT_THAT(
// node_property_keys,
// UnorderedElementsAreArray(property_keys.begin(), property_keys.end()));
// }
//
// // If has_range is false, lower and upper bound values are ignored.
// // TODO: Once expression evaluation is implemented, we should also test if
// // property values are equal.
// void CompareRelationships(
// std::pair<std::string, Relationship> relationship_entry,
// Relationship::Direction direction, std::vector<std::string> types,
// std::vector<std::string> property_keys, bool has_range,
// int64_t lower_bound = 1LL, int64_t upper_bound = LLONG_MAX) {
// auto relationship = relationship_entry.second;
// ASSERT_EQ(relationship_entry.first, relationship.output_id);
// ASSERT_EQ(relationship.direction, direction);
// ASSERT_THAT(relationship.types,
// UnorderedElementsAreArray(types.begin(), types.end()));
// std::vector<std::string> relationship_property_keys;
// for (auto x : relationship.properties) {
// relationship_property_keys.push_back(x.first);
// }
// ASSERT_THAT(
// relationship_property_keys,
// UnorderedElementsAreArray(property_keys.begin(), property_keys.end()));
// ASSERT_EQ(relationship.has_range, has_range);
// if (!has_range)
// return;
// ASSERT_EQ(relationship.lower_bound, lower_bound);
// ASSERT_EQ(relationship.upper_bound, upper_bound);
// }
//
// // SyntaxException on incorrect syntax.
// TEST(CompilerStructuresTest, SyntaxException) {
// ASSERT_THROW(ParserTables("CREATE ()-[*1...2]-()"),
// frontend::opencypher::SyntaxException);
// }
//
// // Empty node.
// TEST(CompilerStructuresTest, NodePatternEmpty) {
// ParserTables parser("CREATE ()");
// ASSERT_EQ(parser.identifiers_map_.size(), 0U);
// ASSERT_EQ(parser.nodes_.size(), 1U);
// CompareNodes(*parser.nodes_.begin(), {}, {});
// }
//
// // Node with variable.
// TEST(CompilerStructuresTest, NodePatternVariable) {
// ParserTables parser("CREATE (var)");
// ASSERT_EQ(parser.identifiers_map_.size(), 1U);
// ASSERT_NE(parser.identifiers_map_.find("var"), parser.identifiers_map_.end());
// ASSERT_EQ(parser.nodes_.size(), 1U);
// auto output_identifier = parser.identifiers_map_["var"];
// ASSERT_NE(parser.nodes_.find(output_identifier), parser.nodes_.end());
// CompareNodes(*parser.nodes_.begin(), {}, {});
// }
//
// // Node with labels.
// TEST(CompilerStructuresTest, NodePatternLabels) {
// ParserTables parser("CREATE (:label1:label2:label3)");
// ASSERT_EQ(parser.identifiers_map_.size(), 0U);
// ASSERT_EQ(parser.nodes_.size(), 1U);
// CompareNodes(*parser.nodes_.begin(), {"label1", "label2", "label3"}, {});
// }
//
// // Node with properties.
// TEST(CompilerStructuresTest, NodePatternProperties) {
// ParserTables parser("CREATE ({age: 5, name: \"John\", surname: \"Smith\"})");
// ASSERT_EQ(parser.identifiers_map_.size(), 0U);
// ASSERT_EQ(parser.nodes_.size(), 1U);
// CompareNodes(*parser.nodes_.begin(), {}, {"age", "name", "surname"});
// }
//
// // Relationship without relationship details.
// TEST(CompilerStructuresTest, RelationshipPatternNoDetails) {
// ParserTables parser("CREATE ()--()");
// ASSERT_EQ(parser.identifiers_map_.size(), 0U);
// ASSERT_EQ(parser.relationships_.size(), 1U);
// CompareRelationships(*parser.relationships_.begin(),
// Relationship::Direction::BOTH, {}, {}, false);
// }
//
// // Relationship with empty relationship details.
// TEST(CompilerStructuresTest, RelationshipPatternEmptyDetails) {
// ParserTables parser("CREATE ()-[]-()");
// ASSERT_EQ(parser.identifiers_map_.size(), 0U);
// ASSERT_EQ(parser.relationships_.size(), 1U);
// CompareRelationships(*parser.relationships_.begin(),
// Relationship::Direction::BOTH, {}, {}, false);
// }
//
// // Relationship with left direction.
// TEST(CompilerStructuresTest, RelationshipPatternLeftDirection) {
// ParserTables parser("CREATE ()<--()");
// ASSERT_EQ(parser.identifiers_map_.size(), 0U);
// ASSERT_EQ(parser.relationships_.size(), 1U);
// CompareRelationships(*parser.relationships_.begin(),
// Relationship::Direction::LEFT, {}, {}, false);
// }
//
// // Relationship with right direction.
// TEST(CompilerStructuresTest, RelationshipPatternRightDirection) {
// ParserTables parser("CREATE ()-[]->()");
// ASSERT_EQ(parser.identifiers_map_.size(), 0U);
// ASSERT_EQ(parser.relationships_.size(), 1U);
// CompareRelationships(*parser.relationships_.begin(),
// Relationship::Direction::RIGHT, {}, {}, false);
// }
//
// // Relationship with both directions.
// TEST(CompilerStructuresTest, RelationshipPatternBothDirection) {
// ParserTables parser("CREATE ()<-[]->()");
// ASSERT_EQ(parser.identifiers_map_.size(), 0U);
// ASSERT_EQ(parser.relationships_.size(), 1U);
// CompareRelationships(*parser.relationships_.begin(),
// Relationship::Direction::BOTH, {}, {}, false);
// }
//
// // Relationship with unbounded variable range.
// TEST(CompilerStructuresTest, RelationshipPatternUnbounded) {
// ParserTables parser("CREATE ()-[*]-()");
// ASSERT_EQ(parser.identifiers_map_.size(), 0U);
// ASSERT_EQ(parser.relationships_.size(), 1U);
// CompareRelationships(*parser.relationships_.begin(),
// Relationship::Direction::BOTH, {}, {}, true, 1,
// LLONG_MAX);
// }
//
// // Relationship with lower bounded variable range.
// TEST(CompilerStructuresTest, RelationshipPatternLowerBounded) {
// ParserTables parser("CREATE ()-[*5..]-()");
// ASSERT_EQ(parser.identifiers_map_.size(), 0U);
// ASSERT_EQ(parser.relationships_.size(), 1U);
// CompareRelationships(*parser.relationships_.begin(),
// Relationship::Direction::BOTH, {}, {}, true, 5,
// LLONG_MAX);
// }
//
// // Relationship with upper bounded variable range.
// TEST(CompilerStructuresTest, RelationshipPatternUpperBounded) {
// ParserTables parser("CREATE ()-[*..10]-()");
// ASSERT_EQ(parser.identifiers_map_.size(), 0U);
// ASSERT_EQ(parser.relationships_.size(), 1U);
// CompareRelationships(*parser.relationships_.begin(),
// Relationship::Direction::BOTH, {}, {}, true, 1, 10);
// }
//
// // Relationship with lower and upper bounded variable range.
// TEST(CompilerStructuresTest, RelationshipPatternLowerUpperBounded) {
// ParserTables parser("CREATE ()-[*5..10]-()");
// ASSERT_EQ(parser.identifiers_map_.size(), 0U);
// ASSERT_EQ(parser.relationships_.size(), 1U);
// CompareRelationships(*parser.relationships_.begin(),
// Relationship::Direction::BOTH, {}, {}, true, 5, 10);
// }
//
// // Relationship with fixed number of edges.
// TEST(CompilerStructuresTest, RelationshipPatternFixedRange) {
// ParserTables parser("CREATE ()-[*10]-()");
// ASSERT_EQ(parser.identifiers_map_.size(), 0U);
// ASSERT_EQ(parser.relationships_.size(), 1U);
// CompareRelationships(*parser.relationships_.begin(),
// Relationship::Direction::BOTH, {}, {}, true, 10, 10);
// }
//
// // Relationship with invalid bound (larger than long long).
// TEST(CompilerStructuresTest, RelationshipPatternInvalidBound) {
// ASSERT_THROW(
// ParserTables parser("CREATE ()-[*100000000000000000000000000]-()"),
// SemanticException);
// }
//
// // Relationship with variable
// TEST(CompilerStructuresTest, RelationshipPatternVariable) {
// ParserTables parser("CREATE ()-[var]-()");
// ASSERT_EQ(parser.identifiers_map_.size(), 1U);
// ASSERT_NE(parser.identifiers_map_.find("var"), parser.identifiers_map_.end());
// ASSERT_EQ(parser.relationships_.size(), 1U);
// auto output_identifier = parser.identifiers_map_["var"];
// ASSERT_NE(parser.relationships_.find(output_identifier),
// parser.relationships_.end());
// CompareRelationships(*parser.relationships_.begin(),
// Relationship::Direction::BOTH, {}, {}, false);
// }
//
// // Relationship with labels.
// TEST(CompilerStructuresTest, RelationshipPatternLabels) {
// ParserTables parser("CREATE ()-[:label1|label2|:label3]-()");
// ASSERT_EQ(parser.identifiers_map_.size(), 0U);
// ASSERT_EQ(parser.relationships_.size(), 1U);
// CompareRelationships(*parser.relationships_.begin(),
// Relationship::Direction::BOTH,
// {"label1", "label2", "label3"}, {}, false);
// }
//
// // Relationship with properties.
// TEST(CompilerStructuresTest, RelationshipPatternProperties) {
// ParserTables parser(
// "CREATE ()-[{age: 5, name: \"John\", surname: \"Smith\"}]-()");
// ASSERT_EQ(parser.identifiers_map_.size(), 0U);
// ASSERT_EQ(parser.relationships_.size(), 1U);
// CompareRelationships(*parser.relationships_.begin(),
// Relationship::Direction::BOTH, {},
// {"age", "name", "surname"}, false);
// }
//
// // PatternPart.
// TEST(CompilerStructuresTest, PatternPart) {
// ParserTables parser("CREATE ()--()");
// ASSERT_EQ(parser.identifiers_map_.size(), 0U);
// ASSERT_EQ(parser.pattern_parts_.size(), 1U);
// ASSERT_EQ(parser.relationships_.size(), 1U);
// ASSERT_EQ(parser.nodes_.size(), 2U);
// ASSERT_EQ(parser.pattern_parts_.begin()->second.nodes.size(), 2U);
// ASSERT_EQ(parser.pattern_parts_.begin()->second.relationships.size(), 1U);
// }
//
// // PatternPart in braces.
// TEST(CompilerStructuresTest, PatternPartBraces) {
// ParserTables parser("CREATE ((()--()))");
// ASSERT_EQ(parser.identifiers_map_.size(), 0U);
// ASSERT_EQ(parser.pattern_parts_.size(), 1U);
// ASSERT_EQ(parser.relationships_.size(), 1U);
// ASSERT_EQ(parser.nodes_.size(), 2U);
// ASSERT_EQ(parser.pattern_parts_.begin()->second.nodes.size(), 2U);
// ASSERT_EQ(parser.pattern_parts_.begin()->second.relationships.size(), 1U);
// }
//
// // PatternPart with variable.
// TEST(CompilerStructuresTest, PatternPartVariable) {
// ParserTables parser("CREATE var=()--()");
// ASSERT_EQ(parser.identifiers_map_.size(), 1U);
// ASSERT_EQ(parser.pattern_parts_.size(), 1U);
// ASSERT_EQ(parser.relationships_.size(), 1U);
// ASSERT_EQ(parser.nodes_.size(), 2U);
// ASSERT_EQ(parser.pattern_parts_.begin()->second.nodes.size(), 2U);
// ASSERT_EQ(parser.pattern_parts_.begin()->second.relationships.size(), 1U);
// ASSERT_NE(parser.identifiers_map_.find("var"), parser.identifiers_map_.end());
// auto output_identifier = parser.identifiers_map_["var"];
// ASSERT_NE(parser.pattern_parts_.find(output_identifier),
// parser.pattern_parts_.end());
// }
//
// // Multiple nodes with same variable and properties.
// TEST(CompilerStructuresTest, MultipleNodesWithVariableAndProperties) {
// ASSERT_THROW(ParserTables parser("CREATE (a {b: 5})-[]-(a {c: 5})"),
// SemanticException);
// }
//
// // Multiple nodes with same variable name.
// TEST(CompilerStructuresTest, MultipleNodesWithVariable) {
// ParserTables parser("CREATE (a {b: 5, c: 5})-[]-(a)");
// ASSERT_EQ(parser.identifiers_map_.size(), 1U);
// ASSERT_EQ(parser.pattern_parts_.size(), 1U);
// ASSERT_EQ(parser.relationships_.size(), 1U);
// ASSERT_EQ(parser.nodes_.size(), 1U);
// auto pattern_part = parser.pattern_parts_.begin()->second;
// ASSERT_EQ(pattern_part.nodes.size(), 2U);
// ASSERT_EQ(pattern_part.relationships.size(), 1U);
// ASSERT_EQ(pattern_part.nodes[0], pattern_part.nodes[1]);
// }
//
// // Multiple relationships with same variable name and properties.
// TEST(CompilerStructuresTest, MultipleRelationshipsWithVariableAndProperties) {
// ASSERT_THROW(ParserTables parser("CREATE ()-[e {a: 5}]-()-[e {c: 5}]-()"),
// SemanticException);
// }
//
// // Multiple relationships with same variable name.
// TEST(CompilerStructuresTest, MultipleRelationshipsWithVariable) {
// ParserTables parser("CREATE ()-[a {a: 5}]-()-[a]-()");
// ASSERT_EQ(parser.identifiers_map_.size(), 1U);
// ASSERT_EQ(parser.pattern_parts_.size(), 1U);
// ASSERT_EQ(parser.relationships_.size(), 1U);
// ASSERT_EQ(parser.nodes_.size(), 3U);
// auto pattern_part = parser.pattern_parts_.begin()->second;
// ASSERT_EQ(pattern_part.nodes.size(), 3U);
// ASSERT_EQ(pattern_part.relationships.size(), 2U);
// ASSERT_NE(pattern_part.nodes[0], pattern_part.nodes[1]);
// ASSERT_NE(pattern_part.nodes[1], pattern_part.nodes[2]);
// ASSERT_NE(pattern_part.nodes[0], pattern_part.nodes[2]);
// ASSERT_EQ(pattern_part.relationships[0], pattern_part.relationships[1]);
// }
//
// // Different structures (nodes, realtionships, patterns) with same variable
// // name.
// TEST(CompilerStructuresTest, DifferentTypesWithVariable) {
// ASSERT_THROW(ParserTables parser("CREATE a=(a)"), SemanticException);
// ASSERT_THROW(ParserTables parser("CREATE (a)-[a]-()"), SemanticException);
// ASSERT_THROW(ParserTables parser("CREATE a=()-[a]-()"), SemanticException);
// }
// }
int main(int argc, char **argv) {
InitGoogleTest(&argc, argv);