diff --git a/include/communication/bolt/v1/states/error.hpp b/include/communication/bolt/v1/states/error.hpp index 0748fc5e5..6c9b37d6c 100644 --- a/include/communication/bolt/v1/states/error.hpp +++ b/include/communication/bolt/v1/states/error.hpp @@ -9,6 +9,8 @@ namespace bolt class Error : public State { public: + Error(); + State *run(Session &session) override; }; diff --git a/include/communication/bolt/v1/states/executor.hpp b/include/communication/bolt/v1/states/executor.hpp index 6183dd457..e614d57d2 100644 --- a/include/communication/bolt/v1/states/executor.hpp +++ b/include/communication/bolt/v1/states/executor.hpp @@ -20,12 +20,10 @@ public: State* run(Session& session) override final; protected: - Logger logger; - /* Execute an incoming query * */ - void run(Session& session, Query& query); + State* run(Session& session, Query& query); /* Send all remaining results to the client * diff --git a/include/communication/bolt/v1/states/state.hpp b/include/communication/bolt/v1/states/state.hpp index 50a38494c..2915cc29d 100644 --- a/include/communication/bolt/v1/states/state.hpp +++ b/include/communication/bolt/v1/states/state.hpp @@ -4,6 +4,8 @@ #include <cstdint> #include <memory> +#include "logging/default.hpp" + namespace bolt { @@ -15,9 +17,14 @@ public: using uptr = std::unique_ptr<State>; State() = default; + State(Logger logger) : logger(logger) {} + virtual ~State() = default; virtual State* run(Session& session) = 0; + +protected: + Logger logger; }; } diff --git a/include/communication/bolt/v1/transport/bolt_encoder.hpp b/include/communication/bolt/v1/transport/bolt_encoder.hpp index dba6ab9be..8b35dec3e 100644 --- a/include/communication/bolt/v1/transport/bolt_encoder.hpp +++ b/include/communication/bolt/v1/transport/bolt_encoder.hpp @@ -249,7 +249,7 @@ public: void message_ignored() { - write_struct_header(1); + write_struct_header(0); write(underlying_cast(MessageCode::Ignored)); } diff --git a/include/query_engine/code_generator/cypher_state.hpp b/include/query_engine/code_generator/cypher_state.hpp index ea4a2ebab..91b701b0b 100644 --- a/include/query_engine/code_generator/cypher_state.hpp +++ b/include/query_engine/code_generator/cypher_state.hpp @@ -42,6 +42,7 @@ enum class EntitySource : uint8_t None, InternalId, LabelIndex, + TypeIndex, MainStorage }; diff --git a/include/query_engine/code_generator/entity_search.hpp b/include/query_engine/code_generator/entity_search.hpp index 3db500d48..30f06ea62 100644 --- a/include/query_engine/code_generator/entity_search.hpp +++ b/include/query_engine/code_generator/entity_search.hpp @@ -26,6 +26,7 @@ using cost_t = uint64_t; constexpr cost_t internal_id_cost = 10; constexpr cost_t property_cost = 100; constexpr cost_t label_cost = 1000; +constexpr cost_t type_cost = 1000; constexpr cost_t max_cost = max<cost_t>(); template <typename T> @@ -36,6 +37,7 @@ public: { internal_id, label_index, + type_index, property_index, main_storage }; @@ -47,6 +49,7 @@ public: { costs[SearchPlace::internal_id] = max<T>(); costs[SearchPlace::label_index] = max<T>(); + costs[SearchPlace::type_index] = max<T>(); costs[SearchPlace::property_index] = max<T>(); costs[SearchPlace::main_storage] = max<T>(); } @@ -80,6 +83,7 @@ using search_cost_t = SearchCost<cost_t>; constexpr auto search_internal_id = search_cost_t::SearchPlace::internal_id; constexpr auto search_label_index = search_cost_t::SearchPlace::label_index; +constexpr auto search_type_index = search_cost_t::SearchPlace::type_index; constexpr auto search_property_index = search_cost_t::SearchPlace::property_index; constexpr auto search_main_storage = search_cost_t::SearchPlace::main_storage; diff --git a/include/query_engine/code_generator/handlers/match.hpp b/include/query_engine/code_generator/handlers/match.hpp index c6e928e4b..eeb1ce4bc 100644 --- a/include/query_engine/code_generator/handlers/match.hpp +++ b/include/query_engine/code_generator/handlers/match.hpp @@ -32,6 +32,7 @@ auto match_query_action = auto name = kv.first; + // TODO: duplicated code -> BIG PROBLEM // find node if (kv.second == ClauseAction::MatchNode) { if (already_matched(cypher_data, name, EntityType::Node)) @@ -69,6 +70,13 @@ auto match_query_action = if (place == entity_search::search_main_storage) { cypher_data.source(name, EntitySource::MainStorage); } + if (place == entity_search::search_type_index) { + if (action_data.entity_data.at(name).tags.size() > 1) { + throw SemanticError("Multiple type match (currently NOT supported)"); + } + cypher_data.source(name, EntitySource::TypeIndex); + cypher_data.tags(name, action_data.entity_data.at(name).tags); + } } } diff --git a/include/query_engine/code_generator/handlers/return.hpp b/include/query_engine/code_generator/handlers/return.hpp index e4d631fd4..6a77c9615 100644 --- a/include/query_engine/code_generator/handlers/return.hpp +++ b/include/query_engine/code_generator/handlers/return.hpp @@ -43,12 +43,23 @@ auto return_query_action = { if (cypher_data.type(entity) == EntityType::Node) { if (cypher_data.tags(entity).size() == 0) - throw CppGeneratorException("entity has no tags"); + throw CppGeneratorException("node has no labels"); auto label = cypher_data.tags(entity).at(0); - code += code_line(code::fine_and_write_vertices_by_label, + code += code_line(code::find_and_write_vertices_by_label, entity, label); } } + + if (cypher_data.source(entity) == EntitySource::TypeIndex) + { + if (cypher_data.type(entity) == EntityType::Relationship) { + if (cypher_data.tags(entity).size() == 0) + throw CppGeneratorException("edge has no tag"); + auto type = cypher_data.tags(entity).at(0); + code += code_line(code::find_and_write_edges_by_type, + entity, type); + } + } } else if (element.is_projection()) { diff --git a/include/query_engine/hardcode/queries.hpp b/include/query_engine/hardcode/queries.hpp index 6424b745a..2ef4ab2c7 100644 --- a/include/query_engine/hardcode/queries.hpp +++ b/include/query_engine/hardcode/queries.hpp @@ -332,13 +332,13 @@ auto load_queries(Db &db) if (it_type.count() > it_vertex.count()) { // Going through vertices wiil probably be faster it_vertex.to().for_all([&](auto m) { - // PRINT n,m + // PRINT n, m }); } else { // Going through edges wiil probably be faster it_type.to().for_all([&](auto m) { - // PRINT n,m + // PRINT n, m }); } diff --git a/include/query_engine/traverser/code.hpp b/include/query_engine/traverser/code.hpp index 5bdd58834..23b8d6b5d 100644 --- a/include/query_engine/traverser/code.hpp +++ b/include/query_engine/traverser/code.hpp @@ -71,7 +71,7 @@ const std::string write_all_vertices = " }});\n" " stream.write_meta(\"rw\");\n"; -const std::string fine_and_write_vertices_by_label = +const std::string find_and_write_vertices_by_label = "auto &label = t.label_find_or_create(\"{1}\");\n" " stream.write_field(\"{0}\");\n" " label.index().for_range(t)->for_all([&](auto vertex) {{\n" @@ -94,6 +94,17 @@ const std::string write_all_edges = " }});\n" " stream.write_meta(\"rw\");\n"; +const std::string find_and_write_edges_by_type = + "auto &type = t.type_find_or_create(\"{1}\");\n" + " stream.write_field(\"{0}\");\n" + " type.index().for_range(t)->for_all([&](auto edge) {{\n" + " stream.write_record();\n" + " stream.write_list_header(1);\n" + " stream.write(edge);\n" + " stream.chunk();\n" + " }});\n" + " stream.write_meta(\"rw\");\n"; + const std::string return_true = "return true;"; const std::string todo = "// TODO: {}"; diff --git a/include/query_engine/traverser/cpp_traverser.hpp b/include/query_engine/traverser/cpp_traverser.hpp index 466500943..15e2bbfef 100644 --- a/include/query_engine/traverser/cpp_traverser.hpp +++ b/include/query_engine/traverser/cpp_traverser.hpp @@ -358,11 +358,14 @@ public: void visit(ast::RelationshipTypeList &ast_relationship_type_list) override { - auto &data = generator.action_data(); + auto &action_data = generator.action_data(); if (ast_relationship_type_list.has_value()) { auto type = ast_relationship_type_list.value->name; - data.add_entity_tag(entity, type); + action_data.add_entity_tag(entity, type); + action_data.csm.search_cost( + entity, entity_search::search_type_index, + entity_search::type_cost); } Traverser::visit(ast_relationship_type_list); diff --git a/include/utils/exceptions/basic_exception.hpp b/include/utils/exceptions/basic_exception.hpp index 79ea3d5f9..f358b17ee 100644 --- a/include/utils/exceptions/basic_exception.hpp +++ b/include/utils/exceptions/basic_exception.hpp @@ -9,16 +9,26 @@ class BasicException : public std::exception { public: - BasicException(const std::string& message) noexcept : message(message) + BasicException(const std::string& message) noexcept + : message(message), + stacktrace_size(3) { #ifndef NDEBUG this->message += '\n'; Stacktrace stacktrace; - for(auto& line : stacktrace) + // TODO: write this better + // (limit the size of stacktrace) + uint64_t count = 0; + + for(auto& line : stacktrace) { this->message += fmt::format(" at {} ({})\n", line.function, line.location); + + if (++count >= stacktrace_size) + break; + } #endif } @@ -33,5 +43,6 @@ public: private: std::string message; + uint64_t stacktrace_size; }; diff --git a/src/communication/bolt/v1/states/error.cpp b/src/communication/bolt/v1/states/error.cpp index cfcfc9391..27f6bb339 100644 --- a/src/communication/bolt/v1/states/error.cpp +++ b/src/communication/bolt/v1/states/error.cpp @@ -3,13 +3,28 @@ namespace bolt { +Error::Error() : State(logging::log->logger("Error State")) {} + State* Error::run(Session& session) { + logger.trace("Run"); + + session.decoder.read_byte(); auto message_type = session.decoder.read_byte(); - if(message_type == MessageCode::AckFailure) + logger.trace("Message type byte is: {:02X}", message_type); + + if (message_type == MessageCode::PullAll) + { + session.output_stream.write_ignored(); + session.output_stream.chunk(); + session.output_stream.send(); + return this; + } + else if(message_type == MessageCode::AckFailure) { // TODO reset current statement? is it even necessary? + logger.trace("AckFailure received"); session.output_stream.write_success_empty(); session.output_stream.chunk(); diff --git a/src/communication/bolt/v1/states/executor.cpp b/src/communication/bolt/v1/states/executor.cpp index b8e7e9777..fcf25cc30 100644 --- a/src/communication/bolt/v1/states/executor.cpp +++ b/src/communication/bolt/v1/states/executor.cpp @@ -8,7 +8,7 @@ namespace bolt { -Executor::Executor() : logger(logging::log->logger("Executor")) {} +Executor::Executor() : State(logging::log->logger("Executor")) {} State *Executor::run(Session &session) { @@ -25,15 +25,22 @@ State *Executor::run(Session &session) q.statement = session.decoder.read_string(); - this->run(session, q); + try { + return this->run(session, q); + } catch (QueryEngineException &e) { + session.output_stream.write_failure( + {{"code", "Memgraph.QueryEngineException"}, + {"message", e.what()}}); + session.output_stream.send(); + return session.bolt.states.error.get(); + } } else if (message_type == MessageCode::PullAll) { pull_all(session); } else if (message_type == MessageCode::DiscardAll) { discard_all(session); } else if (message_type == MessageCode::Reset) { - // todo rollback current transaction + // TODO: rollback current transaction // discard all records waiting to be sent - return this; } else { logger.error("Unrecognized message recieved"); @@ -45,20 +52,28 @@ State *Executor::run(Session &session) return this; } -void Executor::run(Session &session, Query &query) +State* Executor::run(Session &session, Query &query) { logger.trace("[Run] '{}'", query.statement); auto &db = session.active_db(); logger.debug("[ActiveDB] '{}'", db.name()); - try { + auto is_successfully_executed = query_engine.execute(query.statement, db, session.output_stream); - } catch (QueryEngineException &e) { + + if (!is_successfully_executed) { session.output_stream.write_failure( - {{"code", "unknown"}, {"message", e.what()}}); + {{"code", "Memgraph.QueryExecutionFail"}, + {"message", "Query execution has failed (probably there is no " + "element or there are some problems with concurrent " + "access -> client has to resolve problems with " + "concurrent access)"}}); session.output_stream.send(); + return session.bolt.states.error.get(); } + + return this; } void Executor::pull_all(Session &session) diff --git a/src/examples/bolt_py_client/initial_test.py b/src/examples/bolt_py_client/initial_test.py index 358807648..f542944d9 100644 --- a/src/examples/bolt_py_client/initial_test.py +++ b/src/examples/bolt_py_client/initial_test.py @@ -21,14 +21,20 @@ queries.append((True, "MATCH (n) WHERE ID(n)=2 RETURN n")) queries.append((True, "MATCH (n) WHERE ID(n)=3 RETURN n")) queries.append((True, "MATCH (n) WHERE ID(n)=4 RETURN n")) queries.append((True, "MATCH (n) WHERE ID(n)=5 RETURN n")) +queries.append((True, "MATCH (n) RETURN n")); +queries.append((True, "MATCH (n:PERSON) RETURN n")); queries.append((True, "MATCH (n1), (n2) WHERE ID(n1)=0 AND ID(n2)=1 CREATE (n1)-[r:IS]->(n2) RETURN r")) queries.append((True, "MATCH (n1), (n2) WHERE ID(n1)=1 AND ID(n2)=2 CREATE (n1)-[r:IS {name: \"test\", age: 23}]->(n2) RETURN r")) queries.append((True, "MATCH (n1), (n2) WHERE ID(n1)=2 AND ID(n2)=0 CREATE (n1)-[r:IS {name: \"test\", age: 23}]->(n2) RETURN r")) +queries.append((True, "MATCH (n1), (n2) WHERE ID(n1)=2 AND ID(n2)=0 CREATE (n1)-[r:ARE {name: \"test\", age: 23}]->(n2) RETURN r")) queries.append((True, "MATCH ()-[r]-() WHERE ID(r)=0 RETURN r")) queries.append((True, "MATCH ()-[r]-() WHERE ID(r)=1 RETURN r")) queries.append((True, "MATCH ()-[r]-() WHERE ID(r)=2 RETURN r")) +queries.append((True, "MATCH ()-[r:IS]-() RETURN r")) +queries.append((True, "MATCH ()-[r:ARE]-() RETURN r")) +queries.append((True, "MATCH ()-[r]-() RETURN r")) queries.append((True, "MATCH (n) WHERE ID(n)=1 SET n.name = \"updated_name\" RETURN n")) queries.append((True, "MATCH (n) WHERE ID(n)=1 RETURN n"))