From 6970170f69af636824f8cd39322c405c8fcda84b Mon Sep 17 00:00:00 2001 From: Marko Budiselic Date: Mon, 25 Jul 2016 02:09:40 +0100 Subject: [PATCH] Query engine work in progress: code_generator handlers, update clause generator, delete clause generator, return clause generator, SemanticError --- src/cypher/ast/accessor.hpp | 4 +- src/cypher/errors.hpp | 18 +- src/query_engine/code_generator.hpp | 15 +- .../code_generator/cpp_generator.hpp | 2 + .../code_generator/handlers/all.hpp | 9 + .../code_generator/handlers/create.hpp | 39 +++ .../code_generator/handlers/delete.hpp | 22 ++ .../code_generator/handlers/includes.hpp | 14 + .../code_generator/handlers/match.hpp | 58 ++++ .../code_generator/handlers/return.hpp | 30 ++ .../code_generator/handlers/set.hpp | 26 ++ .../handlers/transaction_begin.hpp | 8 + .../handlers/transaction_commit.hpp | 8 + .../code_generator/query_action.hpp | 98 +------ .../code_generator/query_action_data.hpp | 19 +- src/query_engine/exceptions/errors.hpp | 12 + src/query_engine/exceptions/exceptions.hpp | 5 - src/query_engine/traverser/code.hpp | 21 +- src/query_engine/traverser/cpp_traverser.hpp | 85 +++++- .../traverser/del_read_traverser.hpp | 97 ------- .../traverser/del_read_write_traverser.hpp | 270 ------------------ .../traverser/del_write_traverser.hpp | 63 ---- 22 files changed, 360 insertions(+), 563 deletions(-) create mode 100644 src/query_engine/code_generator/handlers/all.hpp create mode 100644 src/query_engine/code_generator/handlers/create.hpp create mode 100644 src/query_engine/code_generator/handlers/delete.hpp create mode 100644 src/query_engine/code_generator/handlers/includes.hpp create mode 100644 src/query_engine/code_generator/handlers/match.hpp create mode 100644 src/query_engine/code_generator/handlers/return.hpp create mode 100644 src/query_engine/code_generator/handlers/set.hpp create mode 100644 src/query_engine/code_generator/handlers/transaction_begin.hpp create mode 100644 src/query_engine/code_generator/handlers/transaction_commit.hpp create mode 100644 src/query_engine/exceptions/errors.hpp delete mode 100644 src/query_engine/traverser/del_read_traverser.hpp delete mode 100644 src/query_engine/traverser/del_read_write_traverser.hpp delete mode 100644 src/query_engine/traverser/del_write_traverser.hpp diff --git a/src/cypher/ast/accessor.hpp b/src/cypher/ast/accessor.hpp index bdddd7074..2381e0c91 100644 --- a/src/cypher/ast/accessor.hpp +++ b/src/cypher/ast/accessor.hpp @@ -20,8 +20,8 @@ struct Accessor : public ValueExpr bool has_entity() const { return entity != nullptr; } bool has_prop() const { return prop != nullptr; } - std::string entity_name() const { return entity->name; } - std::string entity_prop() const { return prop->name; } + std::string &entity_name() const { return entity->name; } + std::string &entity_prop() const { return prop->name; } }; } diff --git a/src/cypher/errors.hpp b/src/cypher/errors.hpp index c4897ba7d..1afcb3fc6 100644 --- a/src/cypher/errors.hpp +++ b/src/cypher/errors.hpp @@ -1,20 +1,28 @@ #pragma once -#include #include "token.hpp" +#include + +// TODO: optimaze exceptions in respect to +// query_engine/exceptions/error.hpp class SyntaxError : public std::runtime_error { public: - SyntaxError(const std::string& near) - : std::runtime_error("Syntax error near '" + near + "'.") {} + SyntaxError(const std::string &near) + : std::runtime_error("Syntax error: near '" + near + "'.") + { + } }; class LexicalError : public std::runtime_error { public: - LexicalError(const Token& token) - : std::runtime_error("Unrecognized token '" + token.value + "'.") {} + LexicalError(const Token &token) + : std::runtime_error("Lexical error: unrecognized token '" + + token.value + "'.") + { + } }; class ParserError : public std::runtime_error diff --git a/src/query_engine/code_generator.hpp b/src/query_engine/code_generator.hpp index c6caadd94..1115b511b 100644 --- a/src/query_engine/code_generator.hpp +++ b/src/query_engine/code_generator.hpp @@ -3,7 +3,7 @@ #include "config/config.hpp" #include "cypher/ast/ast.hpp" #include "cypher/compiler.hpp" -#include "query_engine/exceptions/exceptions.hpp" +#include "query_engine/exceptions/errors.hpp" #include "template_engine/engine.hpp" #include "traverser/cpp_traverser.hpp" #include "utils/string/file.hpp" @@ -20,8 +20,8 @@ public: void generate_cpp(const std::string &query, const uint64_t stripped_hash, const std::string &path) { - // TODO: optimize initialize only once -> be careful that object has - // a state + // TODO: optimize; one time initialization -> be careful that object + // has a state // TODO: multithread test CppTraverser cpp_traverser; @@ -32,9 +32,8 @@ public: // syntax tree generation try { tree = compiler.syntax_tree(query); - } catch (const std::exception &e) { - // TODO: extract more information - throw QueryEngineException("Syntax tree generation error"); + } catch (const std::runtime_error &e) { + throw QueryEngineException(std::string(e.what())); } cpp_traverser.reset(); @@ -42,10 +41,10 @@ public: // code generation try { tree.root->accept(cpp_traverser); - } catch (const SemanticException &e) { + } catch (const SemanticError &e) { throw e; } catch (const std::exception &e) { - throw QueryEngineException("Code generation error"); + throw QueryEngineException("Unknown code generation error"); } // save the code diff --git a/src/query_engine/code_generator/cpp_generator.hpp b/src/query_engine/code_generator/cpp_generator.hpp index 48d080624..6433c9fd1 100644 --- a/src/query_engine/code_generator/cpp_generator.hpp +++ b/src/query_engine/code_generator/cpp_generator.hpp @@ -3,6 +3,7 @@ #include #include "query_engine/code_generator/cypher_state.hpp" +#include "query_engine/code_generator/handlers/all.hpp" #include "query_engine/code_generator/query_action.hpp" #include "query_engine/exceptions/exceptions.hpp" @@ -66,6 +67,7 @@ private: action_functions[QueryAction::Match] = match_query_action; action_functions[QueryAction::Return] = return_query_action; action_functions[QueryAction::Set] = set_query_action; + action_functions[QueryAction::Delete] = delete_query_action; action_functions[QueryAction::TransactionCommit] = transaction_commit_action; } diff --git a/src/query_engine/code_generator/handlers/all.hpp b/src/query_engine/code_generator/handlers/all.hpp new file mode 100644 index 000000000..87de858f8 --- /dev/null +++ b/src/query_engine/code_generator/handlers/all.hpp @@ -0,0 +1,9 @@ +#pragma once + +#include "query_engine/code_generator/handlers/create.hpp" +#include "query_engine/code_generator/handlers/match.hpp" +#include "query_engine/code_generator/handlers/return.hpp" +#include "query_engine/code_generator/handlers/set.hpp" +#include "query_engine/code_generator/handlers/delete.hpp" +#include "query_engine/code_generator/handlers/transaction_begin.hpp" +#include "query_engine/code_generator/handlers/transaction_commit.hpp" diff --git a/src/query_engine/code_generator/handlers/create.hpp b/src/query_engine/code_generator/handlers/create.hpp new file mode 100644 index 000000000..29bd1e614 --- /dev/null +++ b/src/query_engine/code_generator/handlers/create.hpp @@ -0,0 +1,39 @@ +#pragma once + +#include "query_engine/code_generator/handlers/includes.hpp" + +auto create_query_action = + [](CypherStateData &cypher_data, + const QueryActionData &action_data) -> std::string { + + std::string code = ""; + + for (auto const &kv : action_data.actions) { + + if (kv.second == ClauseAction::CreateNode) { + // 1. create node 2. update labels 3. update properties + auto &name = kv.first; + code += LINE(fmt::format(code::create_vertex, name)); + 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)); + } + cypher_data.node_created(name); + } + + if (kv.second == ClauseAction::CreateRelationship) { + auto name = kv.first; + code += LINE(fmt::format(code::create_edge, name)); + cypher_data.relationship_created(name); + } + } + + return code; +}; diff --git a/src/query_engine/code_generator/handlers/delete.hpp b/src/query_engine/code_generator/handlers/delete.hpp new file mode 100644 index 000000000..747a05277 --- /dev/null +++ b/src/query_engine/code_generator/handlers/delete.hpp @@ -0,0 +1,22 @@ +#pragma once + +#include "query_engine/code_generator/handlers/includes.hpp" + +auto delete_query_action = + [](CypherStateData &cypher_data, + const QueryActionData &action_data) -> std::string { + + std::string code = ""; + + for (auto const &kv : action_data.actions) { + auto entity = kv.first; + if (kv.second == ClauseAction::DeleteNode) { + code += LINE(fmt::format("// DELETE Node({})", entity)); + } + if (kv.second == ClauseAction::DeleteRelationship) { + code += LINE(fmt::format("// DELETE Relationship({})", entity)); + } + } + + return code; +}; diff --git a/src/query_engine/code_generator/handlers/includes.hpp b/src/query_engine/code_generator/handlers/includes.hpp new file mode 100644 index 000000000..dfaf61e10 --- /dev/null +++ b/src/query_engine/code_generator/handlers/includes.hpp @@ -0,0 +1,14 @@ +#pragma once + +#include +#include +#include +#include +#include + +#include "query_engine/code_generator/cypher_state.hpp" +#include "query_engine/code_generator/query_action_data.hpp" +#include "query_engine/traverser/code.hpp" +#include "query_engine/exceptions/errors.hpp" + +using ParameterIndexKey::Type::InternalId; diff --git a/src/query_engine/code_generator/handlers/match.hpp b/src/query_engine/code_generator/handlers/match.hpp new file mode 100644 index 000000000..8b9e16603 --- /dev/null +++ b/src/query_engine/code_generator/handlers/match.hpp @@ -0,0 +1,58 @@ +#pragma once + +#include "query_engine/code_generator/handlers/includes.hpp" + +bool already_matched(CypherStateData &cypher_data, const std::string &name, + EntityType type) +{ + if (cypher_data.type(name) == type && + cypher_data.status(name) == EntityStatus::Matched) + return true; + else + return false; +} + +auto fetch_internal_index(const QueryActionData &action_data, + const std::string &name) +{ + return action_data.parameter_index.at(ParameterIndexKey(InternalId, name)); +} + +auto match_query_action = + [](CypherStateData &cypher_data, + const QueryActionData &action_data) -> std::string { + + std::string code = ""; + + for (auto const &kv : action_data.actions) { + + // TODO: the same code REFACTOR! + // find node + if (kv.second == ClauseAction::MatchNode) { + auto name = kv.first; + if (already_matched(cypher_data, name, EntityType::Node)) continue; + 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); + } + } + + // find relationship + if (kv.second == ClauseAction::MatchRelationship) { + auto name = kv.first; + if (already_matched(cypher_data, name, EntityType::Relationship)) + continue; + 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); + } + } + } + + return code; +}; diff --git a/src/query_engine/code_generator/handlers/return.hpp b/src/query_engine/code_generator/handlers/return.hpp new file mode 100644 index 000000000..b82efe4d1 --- /dev/null +++ b/src/query_engine/code_generator/handlers/return.hpp @@ -0,0 +1,30 @@ +#pragma once + +#include "query_engine/code_generator/handlers/includes.hpp" + +auto return_query_action = + [](CypherStateData &cypher_data, + const QueryActionData &action_data) -> std::string { + + std::string code = ""; + + const auto &elements = action_data.return_elements; + code += LINE(fmt::format("// number of elements {}", elements.size())); + + // TODO: call bolt serialization + for (const auto& element : elements) { + auto &entity = element.entity; + if (!cypher_data.exist(entity)) { + throw SemanticError( + fmt::format("{} couldn't be found (RETURN clause).", entity)); + } + if (element.is_entity_only()) { + code += LINE(fmt::format("// RETURN: {}", entity)); + } else if (element.is_projection()) { + auto &property = element.property; + code += LINE(fmt::format("// RETURN: {}.{}", entity, property)); + } + } + + return code; +}; diff --git a/src/query_engine/code_generator/handlers/set.hpp b/src/query_engine/code_generator/handlers/set.hpp new file mode 100644 index 000000000..bce2cfa73 --- /dev/null +++ b/src/query_engine/code_generator/handlers/set.hpp @@ -0,0 +1,26 @@ +#pragma once + +#include "query_engine/code_generator/handlers/includes.hpp" + +auto set_query_action = [](CypherStateData &cypher_data, + const QueryActionData &action_data) -> std::string { + + std::string code = ""; + + 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)); + } + } + } + + return code; +}; diff --git a/src/query_engine/code_generator/handlers/transaction_begin.hpp b/src/query_engine/code_generator/handlers/transaction_begin.hpp new file mode 100644 index 000000000..8d0567c21 --- /dev/null +++ b/src/query_engine/code_generator/handlers/transaction_begin.hpp @@ -0,0 +1,8 @@ +#pragma once + +#include "query_engine/code_generator/handlers/includes.hpp" + +auto transaction_begin_action = [](CypherStateData &, + const QueryActionData &) -> std::string { + return LINE(code::transaction_begin); +}; diff --git a/src/query_engine/code_generator/handlers/transaction_commit.hpp b/src/query_engine/code_generator/handlers/transaction_commit.hpp new file mode 100644 index 000000000..48e153027 --- /dev/null +++ b/src/query_engine/code_generator/handlers/transaction_commit.hpp @@ -0,0 +1,8 @@ +#pragma once + +#include "query_engine/code_generator/handlers/includes.hpp" + +auto transaction_commit_action = [](CypherStateData &, + const QueryActionData &) -> std::string { + return LINE(code::transaction_commit) + LINE(code::return_empty_result); +}; diff --git a/src/query_engine/code_generator/query_action.hpp b/src/query_engine/code_generator/query_action.hpp index 2eeccbea2..3f130d304 100644 --- a/src/query_engine/code_generator/query_action.hpp +++ b/src/query_engine/code_generator/query_action.hpp @@ -1,16 +1,6 @@ #pragma once -#include -#include -#include -#include -#include - -#include "query_engine/code_generator/cypher_state.hpp" -#include "query_engine/code_generator/query_action_data.hpp" -#include "query_engine/traverser/code.hpp" - -using ParameterIndexKey::Type::InternalId; +#include // any entity (node or relationship) inside cypher query has an action // that is associated with that entity @@ -21,90 +11,6 @@ enum class QueryAction : uint32_t Match, Set, Return, + Delete, TransactionCommit }; - -auto transaction_begin_action = [](CypherStateData &, - const QueryActionData &) -> std::string { - return LINE(code::transaction_begin); -}; - -auto create_query_action = - [](CypherStateData &cypher_data, - const QueryActionData &action_data) -> std::string { - - std::string code = ""; - - for (auto const &kv : action_data.actions) { - - if (kv.second == ClauseAction::CreateRelationship) { - auto name = kv.first; - code += LINE(fmt::format(code::create_edge, name)); - cypher_data.relationship_created(name); - } - } - - return code; -}; - -auto match_query_action = - [](CypherStateData &cypher_data, - const QueryActionData &action_data) -> std::string { - - std::string code = ""; - - for (auto const &kv : action_data.actions) { - - if (kv.second == ClauseAction::MatchNode) { - auto name = kv.first; - if (cypher_data.type(name) == EntityType::Node && - cypher_data.status(name) == EntityStatus::Matched) - continue; - } - - // find node - if (kv.second == ClauseAction::MatchNode) { - auto name = kv.first; - auto place = action_data.csm.min(kv.first); - if (place == entity_search::search_internal_id) { - auto index = action_data.parameter_index.at( - ParameterIndexKey(InternalId, name)); - code += - LINE(fmt::format(code::match_vertex_by_id, name, index)); - cypher_data.node_matched(name); - } - } - } - - return code; -}; - -auto set_query_action = - [](CypherStateData &cypher_data, - const QueryActionData &action_data) -> std::string { - - std::string code = ""; - - 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)); - } - } - } - - return code; -}; - -auto return_query_action = []( - CypherStateData &, const QueryActionData &) -> std::string { return ""; }; - -auto transaction_commit_action = [](CypherStateData &, - const QueryActionData &) -> std::string { - return LINE(code::transaction_commit) + LINE(code::return_empty_result); -}; diff --git a/src/query_engine/code_generator/query_action_data.hpp b/src/query_engine/code_generator/query_action_data.hpp index 1745bd0bb..abcb805d3 100644 --- a/src/query_engine/code_generator/query_action_data.hpp +++ b/src/query_engine/code_generator/query_action_data.hpp @@ -95,12 +95,29 @@ struct RelationshipData : public EntityData Direction direction; }; +struct ReturnElement +{ + ReturnElement(const std::string& entity) : entity(entity) {} + ReturnElement(const std::string& entity, const std::string& property) : + entity(entity), property(property) {}; + + std::string entity; + std::string property; + + bool has_entity() const { return !entity.empty(); } + bool has_property() const { return !property.empty(); } + + bool is_entity_only() const { return has_entity() && !has_property(); } + bool is_projection() const { return has_entity() && has_property(); } +}; + struct QueryActionData { std::map parameter_index; std::map actions; std::map entity_data; std::map relationship_data; + std::vector return_elements; CypherStateMachine csm; QueryActionData() = default; @@ -129,7 +146,7 @@ struct QueryActionData auto get_entity_property(const std::string& entity) const { if (entity_data.find(entity) == entity_data.end()) - throw CppGeneratorException("Entity doesn't exist"); + throw CppGeneratorException("Entity " + entity + " doesn't exist"); return entity_data.at(entity); } diff --git a/src/query_engine/exceptions/errors.hpp b/src/query_engine/exceptions/errors.hpp new file mode 100644 index 000000000..70f6d2ddb --- /dev/null +++ b/src/query_engine/exceptions/errors.hpp @@ -0,0 +1,12 @@ +#pragma once + +#include "utils/exceptions/basic_exception.hpp" + +// TODO: optimaze exceptions in respect to cypher/errors.hpp + +class SemanticError : public BasicException +{ +public: + SemanticError(const std::string& what) : + BasicException("Semantic error: " + what) {} +}; diff --git a/src/query_engine/exceptions/exceptions.hpp b/src/query_engine/exceptions/exceptions.hpp index 5d43fc696..f542b1fdf 100644 --- a/src/query_engine/exceptions/exceptions.hpp +++ b/src/query_engine/exceptions/exceptions.hpp @@ -7,11 +7,6 @@ class QueryEngineException : public BasicException using BasicException::BasicException; }; -class SemanticException : public BasicException -{ - using BasicException::BasicException; -}; - class CppGeneratorException : public BasicException { using BasicException::BasicException; diff --git a/src/query_engine/traverser/code.hpp b/src/query_engine/traverser/code.hpp index b18672b11..f601b7e88 100644 --- a/src/query_engine/traverser/code.hpp +++ b/src/query_engine/traverser/code.hpp @@ -20,8 +20,14 @@ const std::string transaction_begin = "auto& t = db.tx_engine.begin();"; const std::string transaction_commit = "t.commit();"; -const std::string vertex_accessor = - "auto vertex_accessor = db.graph.vertices.insert(t);"; +// 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_label = + "auto &{0} = db.graph.label_store.find_or_create(\"{0}\");"; +const std::string add_label = "{}.add_label({});"; const std::string args_id = "auto id = args[{}]->as();"; @@ -30,13 +36,14 @@ const std::string vertex_accessor_args_id = const std::string match_vertex_by_id = "auto {0} = db.graph.vertices.find(t, args[{1}]->as().value);\n" - "\tif (!{0}) return t.commit(), std::make_shared();"; + "if (!{0}) return t.commit(), std::make_shared();"; +const std::string match_edge_by_id = + "auto {0} = db.graph.edges.find(t, args[{1}]->as().value);\n" + "if (!{0}) return t.commit(), std::make_shared();"; const std::string create_edge = "auto {} = db.graph.edges.insert(t);"; -const std::string vertex_set_property = - "vertex_accessor.property(\"{}\", args[{}]);"; const std::string return_empty_result = "return std::make_shared();"; @@ -44,10 +51,6 @@ const std::string return_empty_result = const std::string update_property = "{}.property(\"{}\", args[{}]);"; -const std::string create_label = - "auto &{} = db.graph.label_store.find_or_create(\"{}\");"; - -const std::string add_label = "vertex_accessor.add_label({});"; const std::string todo = "// TODO: {}"; diff --git a/src/query_engine/traverser/cpp_traverser.hpp b/src/query_engine/traverser/cpp_traverser.hpp index 08bc06f33..8eae1235f 100644 --- a/src/query_engine/traverser/cpp_traverser.hpp +++ b/src/query_engine/traverser/cpp_traverser.hpp @@ -86,7 +86,20 @@ private: property_state.clear(); } + // for the first release every query has to have a RETURN clause + // problem is where to put t.commit() + // TODO: remove this constraint + bool has_return; + public: + void semantic_check() const + { + if (!has_return) + throw SemanticError("The query doesn't have RETURN clause. Next " + "releases will support query without RETURN " + "clause"); + } + void visit(ast::WriteQuery &write_query) override { // TODO: crate top level node (TransactionBegin can be @@ -153,7 +166,7 @@ public: void visit(ast::Set &ast_set) override { - code += generator.generate(); + code += generator.generate(); generator.add_action(QueryAction::Set); @@ -165,6 +178,8 @@ public: void visit(ast::Return &ast_return) override { + has_return = true; + generator.add_action(QueryAction::Return); state = CypherState::Return; @@ -180,6 +195,11 @@ public: generator.clear(); } + void visit(ast::ReturnList &ast_return_list) override + { + Traverser::visit(ast_return_list); + } + void visit(ast::PatternList &ast_pattern_list) override { Traverser::visit(ast_pattern_list); @@ -232,7 +252,7 @@ public: if (!internal_id_expr.has_id()) return; auto name = internal_id_expr.entity_name(); - // because entity_id will be index of value inside parameters array + // because entity_id will be value index inside the parameters array auto index = internal_id_expr.entity_id(); auto &data = generator.action_data(); @@ -285,6 +305,18 @@ public: void visit(ast::RelationshipSpecs &ast_relationship_specs) override { + if (state == CypherState::Match) { + if (ast_relationship_specs.has_identifier()) { + auto name = ast_relationship_specs.name(); + auto &cypher_data = generator.cypher_data(); + if (!cypher_data.exist(name)) { + clause_action = ClauseAction::MatchRelationship; + auto &data = generator.action_data(); + data.actions[name] = ClauseAction::MatchRelationship; + } + } + } + Traverser::visit(ast_relationship_specs); } @@ -343,6 +375,16 @@ public: void visit(ast::Identifier &ast_identifier) override { property_state.property_name = ast_identifier.name; + + if (state == CypherState::Delete) { + auto &action_data = generator.action_data(); + auto name = ast_identifier.name; + auto &cypher_data = generator.cypher_data(); + if (cypher_data.type(name) == EntityType::Node) + action_data.actions[name] = ClauseAction::DeleteNode; + if (cypher_data.type(name) == EntityType::Relationship) + action_data.actions[name] = ClauseAction::DeleteRelationship; + } } void visit(ast::Long &ast_long) override @@ -378,8 +420,7 @@ public: auto entity_type = cypher_data.type(entity); if (entity_type == EntityType::NotFound) - throw SemanticException("Set Entity (" + entity + - ") doesn't exist"); + throw SemanticError("Entity (" + entity + ") doesn't exist"); auto &action_data = generator.action_data(); @@ -388,7 +429,8 @@ public: if (entity_type == EntityType::Relationship) action_data.actions[entity] = ClauseAction::UpdateRelationship; - action_data.parameter_index.emplace(ParameterIndexKey(entity, prop), index); + action_data.parameter_index.emplace(ParameterIndexKey(entity, prop), + index); action_data.add_entitiy_property(entity, prop); clear_state(); @@ -397,14 +439,43 @@ public: void visit(ast::Accessor &ast_accessor) override { if (!ast_accessor.has_entity()) return; + + auto &action_data = generator.action_data(); + + if (state == CypherState::Return) { + auto &return_elements = action_data.return_elements; + auto &entity = ast_accessor.entity_name(); + if (!ast_accessor.has_prop()) { + return_elements.emplace_back(ReturnElement(entity)); + } else { + auto &property = ast_accessor.entity_prop(); + return_elements.emplace_back(ReturnElement(entity, property)); + } + } + if (!ast_accessor.has_prop()) return; - set_element_state.set_entity = ast_accessor.entity_name(); - set_element_state.set_prop = ast_accessor.entity_prop(); + if (state == CypherState::Set) { + set_element_state.set_entity = ast_accessor.entity_name(); + set_element_state.set_prop = ast_accessor.entity_prop(); + } } void visit(ast::SetValue &ast_set_value) override { Traverser::visit(ast_set_value); } + + void visit(ast::Delete& ast_delete) override + { + code += generator.generate(); + + state = CypherState::Delete; + + generator.add_action(QueryAction::Delete); + + Traverser::visit(ast_delete); + + code += generator.generate(); + } }; diff --git a/src/query_engine/traverser/del_read_traverser.hpp b/src/query_engine/traverser/del_read_traverser.hpp deleted file mode 100644 index 5da89efaf..000000000 --- a/src/query_engine/traverser/del_read_traverser.hpp +++ /dev/null @@ -1,97 +0,0 @@ -#pragma once - -// TODO: wrap fmt format -#include - -#include "code.hpp" -#include "cypher/visitor/traverser.hpp" -#include "query_engine/util.hpp" -#include "utils/underlying_cast.hpp" -#include "query_engine/code_generator/entity_search.hpp" - -using namespace entity_search; - -class ReadTraverser : public Traverser, public Code -{ -private: - // TODO: change int to something bigger (int64_t) - std::map internal_ids; - - std::set entities; - CypherStateMachine csm; - uint32_t index{0}; - std::map property_indices; - -public: - void update_entities(const std::string &name) - { - std::cout << name << std::endl; - if (entities.find(name) == entities.end()) { - csm.init_cost(name); - property_indices[name] = index++; - entities.insert(std::move(name)); - } - } - - void visit(ast::Match &match) override - { - code += LINE(code::transaction_begin); - Traverser::visit(match); - }; - - void visit(ast::Node &node) override - { - auto identifier = node.idn; - if (identifier == nullptr) return; - - auto name = identifier->name; - update_entities(name); - - if (node.labels != nullptr && node.labels->value != nullptr) - csm.search_cost(name, search_label_index, label_cost); - } - - void visit(ast::InternalIdExpr &internal_id_expr) override - { - if (internal_id_expr.identifier == nullptr) return; - auto name = internal_id_expr.identifier->name; - - // value has to exist - if (internal_id_expr.value == nullptr) { - // TODO: raise exception - } - - update_entities(name); - csm.search_cost(name, search_internal_id, internal_id_cost); - } - - void visit(ast::Return &ret) override - { - for (auto const &entity : entities) { - std::cout << entity << std::endl; - std::cout << underlying_cast(csm.min(entity)) << std::endl; - if (csm.min(entity) == search_internal_id) { - auto property_index = property_indices.at(entity); - code += LINE( - fmt::format(code::args_id, std::to_string(property_index))); - code += LINE(code::vertex_accessor_args_id); - continue; - } - - if (csm.min(entity) == search_label_index) { - code += LINE(fmt::format(code::todo, "search label index")); - continue; - } - - if (csm.min(entity) == search_main_storage) { - code += LINE(fmt::format(code::todo, "return all vertices")); - continue; - } - } - - code::debug_print_vertex_labels(); - - code += LINE(code::transaction_commit); - code += LINE(code::return_empty_result); - } -}; diff --git a/src/query_engine/traverser/del_read_write_traverser.hpp b/src/query_engine/traverser/del_read_write_traverser.hpp deleted file mode 100644 index 3f22cdc18..000000000 --- a/src/query_engine/traverser/del_read_write_traverser.hpp +++ /dev/null @@ -1,270 +0,0 @@ -#pragma once - -#include "code.hpp" -#include "cypher/visitor/traverser.hpp" -#include "query_engine/code_generator/cypher_state.hpp" -#include "query_engine/code_generator/cpp_generator.hpp" -#include "query_engine/code_generator/structures.hpp" - -// k -// auto v1 = db.graph.vertices.find(t, args[0]->as().value); -// if (!v1) return t.commit(), false; - -// auto v2 = db.graph.vertices.find(t, args[1]->as().value); -// if (!v2) return t.commit(), false; - -// create -// auto edge_accessor = db.graph.edges.insert(t); - -// from -// v1.vlist->update(t)->data.out.add(edge_accessor.vlist); -// edge_accessor.from(v1.vlist); - -// to -// v2.vlist->update(t)->data.in.add(edge_accessor.vlist); -// edge_accessor.to(v2.vlist); - -// edge type -// auto &edge_type = db.graph.edge_type_store.find_or_create("IS"); -// edge_accessor.edge_type(edge_type); - -class ReadWriteTraverser : public Traverser, public Code -{ -private: - // currently active entity name (node or relationship name) - std::string entity; - - // currenlty active parameter index - uint64_t index; - - // currently active state - CypherState state; // TODO: move to generator - QueryAction query_action; - ClauseAction clause_action; - - // linearization - CppGenerator generator; - - Vector visited_nodes; - - RelationshipData::Direction direction; - -public: - void visit(ast::Match &ast_match) override - { - state = CypherState::Match; - - generator.add_action(QueryAction::TransactionBegin); - generator.add_action(QueryAction::Match); - - Traverser::visit(ast_match); - } - - void visit(ast::Where &ast_where) override - { - state = CypherState::Where; - - Traverser::visit(ast_where); - } - - void visit(ast::Create &ast_create) override - { - // generate state before - generator.generate(); - - // save current action - generator.add_action(QueryAction::Create); - - state = CypherState::Create; - query_action = QueryAction::Create; - - Traverser::visit(ast_create); - } - - void visit(ast::Return &ast_return) override - { - generator.add_action(QueryAction::Return); - generator.add_action(QueryAction::TransactionCommit); - - state = CypherState::Return; - query_action = QueryAction::Return; - - Traverser::visit(ast_return); - - code = generator.generate(); - } - - void visit(ast::PatternList &ast_pattern_list) override - { - Traverser::visit(ast_pattern_list); - } - - void visit(ast::Pattern &ast_pattern) override - { - // 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)); - } else { - Traverser::visit(ast_pattern); - } - } - - void visit(ast::Node &ast_node) override - { - auto &action_data = generator.action_data(); - auto &cypher_data = generator.cypher_data(); - - if (!ast_node.has_identifier()) return; - - auto name = ast_node.idn->name; - entity = name; - visited_nodes.push_back(name); - - if (state == CypherState::Match) { - action_data.actions[name] = ClauseAction::MatchNode; - } - - if (state == CypherState::Create) { - if (cypher_data.status(name) == EntityStatus::Matched) { - action_data.actions[name] = ClauseAction::MatchNode; - } else { - action_data.actions[name] = ClauseAction::CreateNode; - } - } - - Traverser::visit(ast_node); - } - - void visit(ast::And &ast_and) override { Traverser::visit(ast_and); } - - void visit(ast::InternalIdExpr &internal_id_expr) override - { - if (internal_id_expr.identifier == nullptr) return; - auto name = internal_id_expr.identifier->name; - - // value has to exist - if (internal_id_expr.value == nullptr) { - // TODO: raise exception - } - - auto &data = generator.action_data(); - data.parameter_index[ParameterIndexKey(InternalId, name)] = index++; - data.csm.search_cost(name, search_internal_id, internal_id_cost); - } - - // -- RELATIONSHIP -- - void create_relationship(const std::string &name) - { - auto &data = generator.action_data(); - data.actions[name] = ClauseAction::CreateRelationship; - auto nodes = visited_nodes.last_two(); - data.relationship_data.emplace(name, - RelationshipData(nodes, direction)); - } - - void visit(ast::Relationship &ast_relationship) override - { - auto &cypher_data = generator.cypher_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); - } - } - } - - using ast_direction = ast::Relationship::Direction; - using generator_direction = RelationshipData::Direction; - - if (ast_relationship.direction == ast_direction::Left) - direction = generator_direction::Left; - - if (ast_relationship.direction == ast_direction::Right) - direction = generator_direction::Right; - - Traverser::visit(ast_relationship); - - // TODO: add suport for Direction::Both - } - - void visit(ast::RelationshipSpecs &ast_relationship_specs) override - { - Traverser::visit(ast_relationship_specs); - } - - void visit(ast::RelationshipTypeList &ast_relationship_type_list) override - { - auto &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); - } - - Traverser::visit(ast_relationship_type_list); - } - - void visit(ast::LabelList &ast_label_list) override - { - auto &data = generator.action_data(); - - if (!ast_label_list.has_value()) return; - - auto label = ast_label_list.value->name; - - data.add_entity_tag(entity, label); - - Traverser::visit(ast_label_list); - } - - void visit(ast::PropertyList& ast_property_list) override - { - Traverser::visit(ast_property_list); - } - - void visit(ast::Property &ast_property) override - { - auto &data = generator.action_data(); - - if (!ast_property.has_name()) return; - - auto name = ast_property.name(); - - data.parameter_index.emplace(ParameterIndexKey(entity, name), index++); - - Traverser::visit(ast_property); - } - - // -- SET -- - - void visit(ast::SetList &ast_set_list) override - { - Traverser::visit(ast_set_list); - } - - void visit(ast::SetElement &ast_set_element) override - { - Traverser::visit(ast_set_element); - } - - void visit(ast::Accessor &ast_accessor) override - { - auto &data = generator.action_data(); - - if (ast_accessor.has_entity() && ast_accessor.has_prop()) { - auto entity = ast_accessor.entity_name(); - auto prop = ast_accessor.entity_prop(); - data.parameter_index.emplace(ParameterIndexKey(entity, prop), - index++); - } - } -}; diff --git a/src/query_engine/traverser/del_write_traverser.hpp b/src/query_engine/traverser/del_write_traverser.hpp deleted file mode 100644 index aadf7f23a..000000000 --- a/src/query_engine/traverser/del_write_traverser.hpp +++ /dev/null @@ -1,63 +0,0 @@ -#pragma once - -#include -#include -#include -#include - -#include "cypher/visitor/traverser.hpp" -#include "query_engine/util.hpp" - -class WriteTraverser : public Traverser -{ -private: - uint32_t index{0}; - std::vector labels; - bool collecting_labels{false}; - -public: - std::string code; - - void visit(ast::Create &create) override - { - code += LINE(code::transaction_begin); - code += LINE(code::vertex_accessor); - - Traverser::visit(create); - }; - - void visit(ast::LabelList &label_list) override - { - // TODO: dummy approach -> discussion - if (!collecting_labels) collecting_labels = true; - - Traverser::visit(label_list); - - if (collecting_labels) { - for (auto label : labels) { - code += LINE(fmt::format(code::create_label, label, label)); - code += LINE(fmt::format(code::add_label, label)); - } - labels.clear(); - collecting_labels = false; - } - } - - void visit(ast::Identifier &identifier) override - { - if (collecting_labels) labels.push_back(identifier.name); - } - - void visit(ast::Property &property) override - { - auto key = property.idn->name; - code += LINE(fmt::format(code::vertex_set_property, key, index)); - ++index; - } - - void visit(ast::Return &ret) override - { - code += LINE(code::transaction_commit); - code += LINE(code::return_empty_result); - } -};