From d04cf101b46eb050aef4f7d3ef28da96f09028aa Mon Sep 17 00:00:00 2001
From: Marko Budiselic <mbudiselicbuda@gmail.com>
Date: Sun, 31 Jul 2016 18:58:12 +0100
Subject: [PATCH] Cypher CRUD support is becomes bigger and bigger.
 Implemented: create relationship, match relationship and update relationship

---
 CMakeLists.txt                                | 48 +++++++++---
 src/memgraph_bolt.cpp                         | 17 ++++
 src/{memgraph.cpp => memgraph_http.cpp}       |  0
 .../code_generator/handlers/create.hpp        | 77 +++++++++++++++++--
 .../code_generator/handlers/match.hpp         |  4 +-
 .../code_generator/handlers/set.hpp           | 15 ++--
 .../code_generator/query_action_data.hpp      |  2 +-
 .../code_generator/structures.hpp             |  2 +-
 src/query_engine/exceptions/errors.hpp        |  7 ++
 src/query_engine/hardcode/queries.hpp         | 33 ++++++++
 src/query_engine/traverser/code.hpp           | 28 ++++---
 src/query_engine/traverser/cpp_traverser.hpp  | 50 +++++++-----
 12 files changed, 222 insertions(+), 61 deletions(-)
 create mode 100644 src/memgraph_bolt.cpp
 rename src/{memgraph.cpp => memgraph_http.cpp} (100%)

diff --git a/CMakeLists.txt b/CMakeLists.txt
index faf0da662..1337a7772 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -9,8 +9,11 @@ project(${ProjectId})
 
 find_package(Threads REQUIRED)
 
+# flags
 # c++14
 set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++1y")
+# glibcxx debug
+# set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -D_GLIBCXX_DEBUG")
 
 # functions
 
@@ -175,14 +178,13 @@ EXECUTE_PROCESS(
 )
 
 # # memgraph executable
-# add_executable(memgraph src/memgraph.cpp)
-# add_dependencies(memgraph cypher_lib)
-# # memgraph link libraries
-# target_link_libraries(memgraph Threads::Threads)
-# target_link_libraries(memgraph pcre)
-# target_link_libraries(memgraph ${libuv_static_lib})
-# target_link_libraries(memgraph ${r3_static_lib})
-# target_link_libraries(memgraph ${http_parser_static_lib})
+# add_executable(memgraph_http src/memgraph.cpp)
+# add_dependencies(memgraph_http cypher_lib)
+# target_link_libraries(memgraph_http Threads::Threads)
+# target_link_libraries(memgraph_http pcre)
+# target_link_libraries(memgraph_http ${libuv_static_lib})
+# target_link_libraries(memgraph_http ${r3_static_lib})
+# target_link_libraries(memgraph_http ${http_parser_static_lib})
 
 # # query_engine executable
 # add_executable(query_engine src/query_engine/main_query_engine.cpp)
@@ -214,11 +216,33 @@ set(memgraph_src_files
     ${src_dir}/transactions/transaction.cpp
     ${src_dir}/template_engine/engine.cpp
 )
-
-# hard coded implementation of queries
-# add_executable(queries src/query_engine/main_queries.cpp ${memgraph_src_files})
-# target_link_libraries(queries ${fmt_static_lib})
+add_library(libmemgraph ${memgraph_src_files})
 
 # tests
 enable_testing()
 add_subdirectory(tests)
+
+# memgraph build name
+execute_process(
+    OUTPUT_VARIABLE COMMIT_NO 
+    COMMAND git rev-list --count HEAD
+)
+execute_process(
+    OUTPUT_VARIABLE COMMIT_HASH
+    COMMAND git rev-parse --short HEAD
+)
+string(STRIP ${COMMIT_HASH} COMMIT_HASH)
+string(STRIP ${COMMIT_NO} COMMIT_NO)
+set(MEMGRAPH_BUILD_NAME "memgraph_${COMMIT_HASH}_${COMMIT_NO}")
+
+# DEBUG BUILD
+add_executable(${MEMGRAPH_BUILD_NAME}_debug ${src_dir}/memgraph_bolt.cpp)
+
+# TEST BUILD
+# TODO
+
+# O-OPTIMIZED BUILD
+# TODO
+
+# RELEASE BUILD
+# TODO
diff --git a/src/memgraph_bolt.cpp b/src/memgraph_bolt.cpp
new file mode 100644
index 000000000..fb59219de
--- /dev/null
+++ b/src/memgraph_bolt.cpp
@@ -0,0 +1,17 @@
+#include "utils/terminate_handler.hpp"
+
+int main(int argc, char *argv[])
+{
+    if (argc < 2) {
+        std::cout << "Port not defined" << std::endl;
+        std::exit(0);
+    }
+
+    auto port = std::stoi(argv[1]);
+
+    std::cout << "Port is: " << port << std::endl;
+
+    std::set_terminate(&terminate_handler);
+
+    return 0;
+}
diff --git a/src/memgraph.cpp b/src/memgraph_http.cpp
similarity index 100%
rename from src/memgraph.cpp
rename to src/memgraph_http.cpp
diff --git a/src/query_engine/code_generator/handlers/create.hpp b/src/query_engine/code_generator/handlers/create.hpp
index 29bd1e614..6863aa500 100644
--- a/src/query_engine/code_generator/handlers/create.hpp
+++ b/src/query_engine/code_generator/handlers/create.hpp
@@ -2,6 +2,23 @@
 
 #include "query_engine/code_generator/handlers/includes.hpp"
 
+using Direction = RelationshipData::Direction;
+
+auto update_properties(const QueryActionData &action_data,
+                       const std::string &name)
+{
+    std::string code = "";
+
+    auto entity_data = action_data.get_entity_property(name);
+    for (auto &property : entity_data.properties) {
+        auto index =
+            action_data.parameter_index.at(ParameterIndexKey(name, property));
+        code += LINE(fmt::format(code::set_property, name, property, index));
+    }
+
+    return code;
+}
+
 auto create_query_action =
     [](CypherStateData &cypher_data,
        const QueryActionData &action_data) -> std::string {
@@ -11,26 +28,72 @@ auto create_query_action =
     for (auto const &kv : action_data.actions) {
 
         if (kv.second == ClauseAction::CreateNode) {
-            // 1. create node 2. update labels 3. update properties
+            // create node
             auto &name = kv.first;
             code += LINE(fmt::format(code::create_vertex, name));
+
+            // update properties
+            code += update_properties(action_data, name);
+
+            // update labels
             auto entity_data = action_data.get_entity_property(name);
-            for (auto &property : entity_data.properties) {
-                auto index = action_data.parameter_index.at(
-                    ParameterIndexKey(name, property));
-                code += LINE(fmt::format(code::set_vertex_property, name,
-                                         property, index));
-            }
             for (auto &label : entity_data.tags) {
                 code += LINE(fmt::format(code::create_label, label));
                 code += LINE(fmt::format(code::add_label, name, label));
             }
+
+            // mark node as created
             cypher_data.node_created(name);
         }
 
         if (kv.second == ClauseAction::CreateRelationship) {
+            // create relationship
             auto name = kv.first;
             code += LINE(fmt::format(code::create_edge, name));
+
+            // update properties
+            code += update_properties(action_data, name);
+
+            // update tag
+            auto entity_data = action_data.get_entity_property(name);
+            for (auto &tag : entity_data.tags) {
+                code += LINE(fmt::format(code::find_type, tag));
+                code += LINE(fmt::format(code::set_type, name, tag));
+            }
+
+            // find start and end node
+            auto &relationships_data = action_data.relationship_data;
+            if (relationships_data.find(name) == relationships_data.end())
+                throw CodeGenerationError("Unable to find data for: " + name);
+            auto &relationship_data = relationships_data.at(name);
+            auto left_node = relationship_data.nodes.first;
+            auto right_node = relationship_data.nodes.second;
+
+            // TODO: If node isn't already matched or created it has to be
+            // created here. It is not possible for now.
+            if (cypher_data.status(left_node) != EntityStatus::Matched) {
+                throw SemanticError("Create Relationship: node " + left_node +
+                                    " can't be found");
+            }
+            if (cypher_data.status(right_node) != EntityStatus::Matched) {
+                throw SemanticError("Create Relationship: node " + right_node +
+                                    " can't be found");
+            }
+
+            // define direction
+            if (relationship_data.direction == Direction::Right) {
+                code += LINE(fmt::format(code::node_out, left_node, name));
+                code += LINE(fmt::format(code::node_in, right_node, name));
+                code += LINE(fmt::format(code::edge_from, name, left_node));
+                code += LINE(fmt::format(code::edge_to, name, right_node));
+            } else if (relationship_data.direction == Direction::Left) {
+                code += LINE(fmt::format(code::node_out, right_node, name));
+                code += LINE(fmt::format(code::node_in, left_node, name));
+                code += LINE(fmt::format(code::edge_from, name, right_node));
+                code += LINE(fmt::format(code::edge_to, name, left_node));
+            }
+
+            // mark relationship as created
             cypher_data.relationship_created(name);
         }
     }
diff --git a/src/query_engine/code_generator/handlers/match.hpp b/src/query_engine/code_generator/handlers/match.hpp
index 8b9e16603..47fa263e5 100644
--- a/src/query_engine/code_generator/handlers/match.hpp
+++ b/src/query_engine/code_generator/handlers/match.hpp
@@ -31,12 +31,12 @@ auto match_query_action =
         if (kv.second == ClauseAction::MatchNode) {
             auto name = kv.first;
             if (already_matched(cypher_data, name, EntityType::Node)) continue;
+            cypher_data.node_matched(name);
             auto place = action_data.csm.min(kv.first);
             if (place == entity_search::search_internal_id) {
                 auto index = fetch_internal_index(action_data, name);
                 code +=
                     LINE(fmt::format(code::match_vertex_by_id, name, index));
-                cypher_data.node_matched(name);
             }
         }
 
@@ -45,11 +45,11 @@ auto match_query_action =
             auto name = kv.first;
             if (already_matched(cypher_data, name, EntityType::Relationship))
                 continue;
+            cypher_data.relationship_matched(name);
             auto place = action_data.csm.min(kv.first);
             if (place == entity_search::search_internal_id) {
                 auto index = fetch_internal_index(action_data, name);
                 code += LINE(fmt::format(code::match_edge_by_id, name, index));
-                cypher_data.relationship_matched(name);
             }
         }
     }
diff --git a/src/query_engine/code_generator/handlers/set.hpp b/src/query_engine/code_generator/handlers/set.hpp
index bce2cfa73..e1a582e42 100644
--- a/src/query_engine/code_generator/handlers/set.hpp
+++ b/src/query_engine/code_generator/handlers/set.hpp
@@ -9,16 +9,17 @@ auto set_query_action = [](CypherStateData &cypher_data,
 
     for (auto const &kv : action_data.actions) {
         auto name = kv.first;
+
         if (kv.second == ClauseAction::UpdateNode &&
             cypher_data.status(name) == EntityStatus::Matched &&
             cypher_data.type(name) == EntityType::Node) {
-            auto entity_data = action_data.get_entity_property(name);
-            for (auto &property : entity_data.properties) {
-                auto index = action_data.parameter_index.at(
-                    ParameterIndexKey(name, property));
-                code += LINE(
-                    fmt::format(code::update_property, name, property, index));
-            }
+            code += update_properties(action_data, name);
+        }
+
+        if (kv.second == ClauseAction::UpdateRelationship &&
+            cypher_data.status(name) == EntityStatus::Matched &&
+            cypher_data.type(name) == EntityType::Relationship) {
+            code += update_properties(action_data, name);
         }
     }
 
diff --git a/src/query_engine/code_generator/query_action_data.hpp b/src/query_engine/code_generator/query_action_data.hpp
index abcb805d3..7d45b9b01 100644
--- a/src/query_engine/code_generator/query_action_data.hpp
+++ b/src/query_engine/code_generator/query_action_data.hpp
@@ -76,7 +76,7 @@ struct ParameterIndexKey
     }
 };
 
-struct RelationshipData : public EntityData
+struct RelationshipData
 {
     enum class Direction
     {
diff --git a/src/query_engine/code_generator/structures.hpp b/src/query_engine/code_generator/structures.hpp
index b4884c72b..f24247b94 100644
--- a/src/query_engine/code_generator/structures.hpp
+++ b/src/query_engine/code_generator/structures.hpp
@@ -14,6 +14,6 @@ public:
     {
         runtime_assert(this->size() > 1, "Array size shoud be bigger than 1");
 
-        return std::make_pair(*(this->end() - 2), *(this->end() - 1));
+        return std::make_pair(*(this->end() - 1), *(this->end() - 2));
     }
 };
diff --git a/src/query_engine/exceptions/errors.hpp b/src/query_engine/exceptions/errors.hpp
index 70f6d2ddb..8eb87318f 100644
--- a/src/query_engine/exceptions/errors.hpp
+++ b/src/query_engine/exceptions/errors.hpp
@@ -10,3 +10,10 @@ public:
     SemanticError(const std::string& what) :
         BasicException("Semantic error: " + what) {}
 };
+
+class CodeGenerationError : public BasicException
+{
+public:
+    CodeGenerationError(const std::string& what) :
+        BasicException("Code Generation error: " + what) {}
+};
diff --git a/src/query_engine/hardcode/queries.hpp b/src/query_engine/hardcode/queries.hpp
index 17fe9080f..c898f1350 100644
--- a/src/query_engine/hardcode/queries.hpp
+++ b/src/query_engine/hardcode/queries.hpp
@@ -16,6 +16,16 @@ auto load_queries(Db& db)
 {
     std::map<uint64_t, std::function<bool(const properties_t &)>> queries;
 
+    // CREATE (n {prop: 0}) RETURN n)
+    auto create_node = [&db](const properties_t &args) {
+        auto &t = db.tx_engine.begin();
+        auto vertex_accessor = db.graph.vertices.insert(t);
+        vertex_accessor.property("prop", args[0]);
+        t.commit();
+        return true;
+    };
+    queries[11597417457737499503u] = create_node;
+
     auto create_labeled_and_named_node = [&db](const properties_t &args) {
         auto &t = db.tx_engine.begin();
         auto vertex_accessor = db.graph.vertices.insert(t);
@@ -137,6 +147,28 @@ auto load_queries(Db& db)
         return true;
     };
 
+    // MATCH (n1), (n2) WHERE ID(n1)=0 AND ID(n2)=1 CREATE (n1)<-[r:IS {age: 25, weight: 70}]-(n2) RETURN r]
+    auto create_edge_v2 = [&db](const properties_t &args)
+    {
+        auto& t = db.tx_engine.begin();
+        auto n1 = db.graph.vertices.find(t, args[0]->as<Int64>().value);
+        if (!n1) return t.commit(), false;
+        auto n2 = db.graph.vertices.find(t, args[1]->as<Int64>().value);
+        if (!n2) return t.commit(), false;
+        auto r = db.graph.edges.insert(t);
+        r.property("age", args[2]);
+        r.property("weight", args[3]);
+        auto &IS = db.graph.edge_type_store.find_or_create("IS");
+        r.edge_type(IS);
+        n2.vlist->update(t)->data.out.add(r.vlist);
+        n1.vlist->update(t)->data.in.add(r.vlist);
+        r.from(n2.vlist);
+        r.to(n1.vlist);
+        t.commit();
+        return true;
+    };
+    queries[15648836733456301916u] = create_edge_v2;
+
     queries[10597108978382323595u] = create_account;
     queries[5397556489557792025u] = create_labeled_and_named_node;
     queries[7939106225150551899u] = create_edge;
@@ -146,5 +178,6 @@ auto load_queries(Db& db)
     queries[6813335159006269041u] = update_node;
     queries[4857652843629217005u] = find_by_label;
 
+
     return queries;
 }
diff --git a/src/query_engine/traverser/code.hpp b/src/query_engine/traverser/code.hpp
index f601b7e88..d7ad495b1 100644
--- a/src/query_engine/traverser/code.hpp
+++ b/src/query_engine/traverser/code.hpp
@@ -20,15 +20,24 @@ const std::string transaction_begin = "auto& t = db.tx_engine.begin();";
 
 const std::string transaction_commit = "t.commit();";
 
+const std::string set_property = "{}.property(\"{}\", args[{}]);";
+
 // create vertex e.g. CREATE (n:PERSON {name: "Test", age: 23})
-const std::string create_vertex =
-    "auto {} = db.graph.vertices.insert(t);";
-const std::string set_vertex_property =
-    "{}.property(\"{}\", args[{}]);";
+const std::string create_vertex = "auto {} = db.graph.vertices.insert(t);";
 const std::string create_label =
     "auto &{0} = db.graph.label_store.find_or_create(\"{0}\");";
 const std::string add_label = "{}.add_label({});";
 
+// create edge e.g CREATE (n1)-[r:COST {cost: 100}]->(n2)
+const std::string create_edge = "auto {} = db.graph.edges.insert(t);";
+const std::string find_type =
+    "auto &{0} = db.graph.edge_type_store.find_or_create(\"{0}\");";
+const std::string set_type = "{}.edge_type({});";
+const std::string node_out = "{}.vlist->update(t)->data.out.add({}.vlist);";
+const std::string node_in = "{}.vlist->update(t)->data.in.add({}.vlist);"; 
+const std::string edge_from = "{}.from({}.vlist);";
+const std::string edge_to = "{}.to({}.vlist);";
+
 const std::string args_id = "auto id = args[{}]->as<Int32>();";
 
 const std::string vertex_accessor_args_id =
@@ -36,21 +45,16 @@ const std::string vertex_accessor_args_id =
 
 const std::string match_vertex_by_id =
     "auto {0} = db.graph.vertices.find(t, args[{1}]->as<Int64>().value);\n"
-    "if (!{0}) return t.commit(), std::make_shared<QueryResult>();";
+    "        if (!{0}) return t.commit(), std::make_shared<QueryResult>();";
 const std::string match_edge_by_id =
     "auto {0} = db.graph.edges.find(t, args[{1}]->as<Int64>().value);\n"
-    "if (!{0}) return t.commit(), std::make_shared<QueryResult>();";
-
-const std::string create_edge =
-    "auto {} = db.graph.edges.insert(t);";
+    "        if (!{0}) return t.commit(), std::make_shared<QueryResult>();";
 
 
 const std::string return_empty_result =
     "return std::make_shared<QueryResult>();";
 
-const std::string update_property =
-    "{}.property(\"{}\", args[{}]);";
-
+const std::string update_property = "{}.property(\"{}\", args[{}]);";
 
 const std::string todo = "// TODO: {}";
 
diff --git a/src/query_engine/traverser/cpp_traverser.hpp b/src/query_engine/traverser/cpp_traverser.hpp
index 8eae1235f..7c14bb278 100644
--- a/src/query_engine/traverser/cpp_traverser.hpp
+++ b/src/query_engine/traverser/cpp_traverser.hpp
@@ -3,6 +3,7 @@
 #include <string>
 
 #include "cypher/visitor/traverser.hpp"
+
 #include "query_engine/code_generator/cpp_generator.hpp"
 #include "query_engine/code_generator/entity_search.hpp"
 #include "query_engine/code_generator/structures.hpp"
@@ -209,10 +210,9 @@ public:
     {
         // TODO: Is that traversal order OK for all cases? Probably NOT.
         if (ast_pattern.has_next()) {
-            Traverser::visit(*(ast_pattern.next));
-            if (ast_pattern.has_node()) Traverser::visit(*(ast_pattern.node));
-            if (ast_pattern.has_relationship())
-                Traverser::visit(*(ast_pattern.relationship));
+            visit(*ast_pattern.next);
+            visit(*ast_pattern.node);
+            visit(*ast_pattern.relationship);
         } else {
             Traverser::visit(ast_pattern);
         }
@@ -252,7 +252,7 @@ public:
         if (!internal_id_expr.has_id()) return;
 
         auto name = internal_id_expr.entity_name();
-        // because entity_id will be value index inside the parameters array
+        // because entity_id value will be index inside the parameters array
         auto index = internal_id_expr.entity_id();
 
         auto &data = generator.action_data();
@@ -275,19 +275,11 @@ public:
     void visit(ast::Relationship &ast_relationship) override
     {
         auto &cypher_data = generator.cypher_data();
+        auto &action_data = generator.action_data();
 
-        if (ast_relationship.has_name()) entity = ast_relationship.name();
-
-        // TODO: simplify somehow
-        if (state == CypherState::Create) {
-            if (ast_relationship.has_name()) {
-                auto name = ast_relationship.name();
-                if (!cypher_data.exist(name)) {
-                    clause_action = ClauseAction::CreateRelationship;
-                    create_relationship(name);
-                }
-            }
-        }
+        if (!ast_relationship.has_name()) 
+            return;
+        entity = ast_relationship.name();
 
         using ast_direction = ast::Relationship::Direction;
         using generator_direction = RelationshipData::Direction;
@@ -298,9 +290,23 @@ public:
         if (ast_relationship.direction == ast_direction::Right)
             direction = generator_direction::Right;
 
-        Traverser::visit(ast_relationship);
-
         // TODO: add suport for Direction::Both
+
+        // TODO: simplify somehow
+        if (state == CypherState::Create) {
+            if (!cypher_data.exist(entity)) {
+                clause_action = ClauseAction::CreateRelationship;
+                create_relationship(entity);
+            }
+        }
+
+        if (state == CypherState::Match) {
+            if (!cypher_data.exist(entity)) {
+                action_data.actions[entity] = ClauseAction::MatchRelationship;
+            }
+        }
+
+        Traverser::visit(ast_relationship);
     }
 
     void visit(ast::RelationshipSpecs &ast_relationship_specs) override
@@ -317,6 +323,12 @@ public:
             }
         }
 
+        if (state == CypherState::Create) {
+            if (ast_relationship_specs.has_identifier()) {
+                entity = ast_relationship_specs.name();
+            }
+        }
+
         Traverser::visit(ast_relationship_specs);
     }