#include #include #include #include #include #include "antlr4-runtime.h" #include "gmock/gmock.h" #include "query/context.hpp" #include "query/frontend/ast/cypher_main_visitor.hpp" #include "query/frontend/opencypher/parser.hpp" #include "gtest/gtest.h" using namespace ::testing; // namespace { // // using query::Context; // using namespace query::frontend; // // class ParserTables { // template // auto FilterAnies(std::unordered_map map) { // std::unordered_map filtered; // for (auto x : map) { // if (x.second.is()) { // filtered[x.first] = x.second.as(); // } // } // 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(symbol_table_); // nodes_ = FilterAnies(symbol_table_); // relationships_ = FilterAnies(symbol_table_); // } // // std::unordered_map identifiers_map_; // std::unordered_map symbol_table_; // std::unordered_map pattern_parts_; // std::unordered_map nodes_; // std::unordered_map relationships_; // }; // // // TODO: Once expression evaluation is implemented, we should also test if // // property values are equal. // void CompareNodes(std::pair node_entry, // std::vector labels, // std::vector 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 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 relationship_entry, // Relationship::Direction direction, std::vector types, // std::vector 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 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); return RUN_ALL_TESTS(); }