diff --git a/src/query_engine/code_generator.hpp b/src/query_engine/code_generator.hpp index b68e1cab5..3bf37f6a4 100644 --- a/src/query_engine/code_generator.hpp +++ b/src/query_engine/code_generator.hpp @@ -30,6 +30,9 @@ public: {"stripped_hash", std::to_string(stripped_hash)}, {"query", query}, {"code", code_traverser.code}}); + // TODO: ifndef + std::cout << generated << std::endl; + utils::write_file(generated, path); } diff --git a/src/query_engine/compiled/cpu/.gitignore b/src/query_engine/compiled/cpu/.gitignore deleted file mode 100644 index 5e7d2734c..000000000 --- a/src/query_engine/compiled/cpu/.gitignore +++ /dev/null @@ -1,4 +0,0 @@ -# Ignore everything in this directory -* -# Except this file -!.gitignore diff --git a/src/query_engine/compiled/gpu/.gitignore b/src/query_engine/compiled/gpu/.gitignore deleted file mode 100644 index 5e7d2734c..000000000 --- a/src/query_engine/compiled/gpu/.gitignore +++ /dev/null @@ -1,4 +0,0 @@ -# Ignore everything in this directory -* -# Except this file -!.gitignore diff --git a/src/query_engine/program_loader.hpp b/src/query_engine/program_loader.hpp index 3a407ad63..54f26e004 100644 --- a/src/query_engine/program_loader.hpp +++ b/src/query_engine/program_loader.hpp @@ -61,6 +61,7 @@ public: code_libs.insert({{stripped.hash, code_lib}}); // return instance of runnable code (ICodeCPU) + LOG_INFO("new code compiled and placed into cache"); return QueryProgram(code_lib->instance(), std::move(stripped)); } diff --git a/src/query_engine/state_machine/cypher.hpp b/src/query_engine/state_machine/cypher.hpp new file mode 100644 index 000000000..758ce0f86 --- /dev/null +++ b/src/query_engine/state_machine/cypher.hpp @@ -0,0 +1,124 @@ +#pragma once + +#include <algorithm> +#include <limits> +#include <map> + +// TODO: remove +#include "utils/underlying_cast.hpp" +#include <iostream> + +// entities are nodes or relationship + +namespace entity_search +{ +// returns maximum value for given template argument (for give type) +template <typename T> +constexpr T max() +{ + return std::numeric_limits<uint64_t>::max(); +} + +using cost_t = uint64_t; + +// TODO: rething +// at least load hard coded values from somewhere +constexpr cost_t internal_id_cost = 10; +constexpr cost_t property_cost = 100; +constexpr cost_t label_cost = 1000; +constexpr cost_t max_cost = max<cost_t>(); + +template <typename T> +class SearchCost +{ +public: + enum class SearchPlace : int + { + internal_id, + label_index, + property_index, + main_storage + }; + + using costs_t = std::map<SearchPlace, T>; + using cost_pair_t = std::pair<SearchPlace, T>; + + SearchCost() + { + costs[SearchPlace::internal_id] = max<T>(); + costs[SearchPlace::label_index] = max<T>(); + costs[SearchPlace::property_index] = max<T>(); + costs[SearchPlace::main_storage] = max<T>(); + } + + SearchCost(const SearchCost &other) = default; + + SearchCost(SearchCost &&other) : costs(std::move(other.costs)) {} + + void set(SearchPlace place, T cost) { costs[place] = cost; } + + T get(SearchPlace place) const { return costs.at(place); } + + SearchPlace min() const + { + auto min_pair = std::min_element( + costs.begin(), costs.end(), + [](const cost_pair_t &l, const cost_pair_t &r) -> bool { + return l.second < r.second; + }); + + if (min_pair->second == max_cost) return SearchPlace::main_storage; + + return min_pair->first; + } + +private: + costs_t costs; +}; + +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_property_index = + search_cost_t::SearchPlace::property_index; +constexpr auto search_main_storage = search_cost_t::SearchPlace::main_storage; +} + +class CypherStateMachine +{ +public: + void init_cost(const std::string &entity) + { + entity_search::search_cost_t search_cost; + _search_costs.emplace(entity, search_cost); + } + + void search_cost(const std::string &entity, + entity_search::search_cost_t::SearchPlace search_place, + entity_search::cost_t cost) + { + if (_search_costs.find(entity) != _search_costs.end()) { + entity_search::search_cost_t search_cost; + _search_costs.emplace(entity, std::move(search_cost)); + } + + _search_costs[entity].set(search_place, cost); + } + + entity_search::cost_t + search_cost(const std::string &entity, + entity_search::search_cost_t::SearchPlace search_place) const + { + return _search_costs.at(entity).get(search_place); + } + + entity_search::search_cost_t::SearchPlace + min(const std::string &entity) const + { + return _search_costs.at(entity).min(); + } + +private: + std::map<std::string, entity_search::search_cost_t> _search_costs; +}; diff --git a/src/query_engine/traverser/code.hpp b/src/query_engine/traverser/code.hpp index 5af6bfbc2..31a1c1d10 100644 --- a/src/query_engine/traverser/code.hpp +++ b/src/query_engine/traverser/code.hpp @@ -2,9 +2,54 @@ #include <string> +#include "query_engine/util.hpp" + struct Code { void reset() { code = ""; } std::string code; }; + +namespace code +{ + +// TODO: UNIT tests + +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);"; + +const std::string args_id = "auto id = args[{}]->as<Int32>();"; + +const std::string vertex_accessor_args_id = + "auto vertex_accessor = db.graph.vertices.find(t, id.value);"; + +const std::string vertex_set_property = + "vertex_accessor.property(\"{}\", args[{}]);"; + +const std::string return_empty_result = + "return std::make_shared<QueryResult>();"; + +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: {}"; + +std::string debug_print_vertex_labels() +{ +#ifdef DEBUG + return LINE("PRINT_PROPS(vertex_accessor.properties());") + + LINE("cout << \"LABELS:\" << endl;") + + LINE("for (auto label_ref : vertex_accessor.labels()) {") + + LINE("cout << label_ref.get() << endl;") + LINE("}"); +#else + return ""; +#endif +} +} diff --git a/src/query_engine/traverser/read_traverser.hpp b/src/query_engine/traverser/read_traverser.hpp index 8afb123f2..518b596ae 100644 --- a/src/query_engine/traverser/read_traverser.hpp +++ b/src/query_engine/traverser/read_traverser.hpp @@ -1,48 +1,99 @@ #pragma once +// TODO: wrap fmt format +#include <fmt/format.h> + #include "code.hpp" #include "cypher/visitor/traverser.hpp" +#include "query_engine/state_machine/cypher.hpp" #include "query_engine/util.hpp" +#include "utils/underlying_cast.hpp" + +using namespace entity_search; class ReadTraverser : public Traverser, public Code { private: + // TODO: change int to something bigger (int64_t) + std::map<std::string, int> internal_ids; + + std::set<std::string> entities; + CypherStateMachine csm; uint32_t index{0}; + std::map<std::string, uint32_t> property_indices; public: std::string code; - void visit(ast::Match& match) override + void update_entities(const std::string &name) { - code += line("auto& t = db.tx_engine.begin();"); + 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::Property& property) override + void visit(ast::Node &node) override { - code += line("auto id = args[" + std::to_string(index) + "]->as<Int32>();"); - code += line("auto vertex_accessor = db.graph.vertices.find(t, id.value);"); + auto identifier = node.idn; + if (identifier == nullptr) return; - ++index; + 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) override + 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 + void visit(ast::Return &ret) override { -#ifdef DEBUG - // TODO: remove from here - code += line("PRINT_PROPS(vertex_accessor.properties());"); - code += line("cout << \"LABELS:\" << endl;"); - code += line("for (auto label_ref : vertex_accessor.labels()) {"); - code += line("cout << label_ref.get() << endl;"); - code += line("}"); -#endif + 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; + } - code += line("t.commit();"); - code += line("return std::make_shared<QueryResult>();"); + 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/write_traverser.hpp b/src/query_engine/traverser/write_traverser.hpp index 9436ddba2..aadf7f23a 100644 --- a/src/query_engine/traverser/write_traverser.hpp +++ b/src/query_engine/traverser/write_traverser.hpp @@ -3,6 +3,7 @@ #include <iostream> #include <map> #include <typeinfo> +#include <fmt/format.h> #include "cypher/visitor/traverser.hpp" #include "query_engine/util.hpp" @@ -19,8 +20,8 @@ public: void visit(ast::Create &create) override { - code += line("auto& t = db.tx_engine.begin();"); - code += line("auto vertex_accessor = db.graph.vertices.insert(t);"); + code += LINE(code::transaction_begin); + code += LINE(code::vertex_accessor); Traverser::visit(create); }; @@ -34,10 +35,8 @@ public: if (collecting_labels) { for (auto label : labels) { - code += line("auto &" + label + - " = db.graph.label_store.find_or_create(\"" + - label + "\");"); - code += line("vertex_accessor.add_label(" + label + ");"); + code += LINE(fmt::format(code::create_label, label, label)); + code += LINE(fmt::format(code::add_label, label)); } labels.clear(); collecting_labels = false; @@ -52,22 +51,13 @@ public: void visit(ast::Property &property) override { auto key = property.idn->name; - - code += line("vertex_accessor.property("); - code += line("\t\"" + key + "\", args[" + std::to_string(index) + "]"); - code += line(");"); - + code += LINE(fmt::format(code::vertex_set_property, key, index)); ++index; } void visit(ast::Return &ret) override { -#ifdef DEBUG - // TODO: remove from here - code += line("PRINT_PROPS(vertex_accessor.properties());"); -#endif - - code += line("t.commit();"); - code += line("return std::make_shared<QueryResult>();"); + code += LINE(code::transaction_commit); + code += LINE(code::return_empty_result); } }; diff --git a/src/query_engine/util.hpp b/src/query_engine/util.hpp index b84448298..f773dbd11 100644 --- a/src/query_engine/util.hpp +++ b/src/query_engine/util.hpp @@ -2,4 +2,4 @@ #include <string> -std::string line(std::string line) { return "\t\t" + line + "\n"; } +std::string LINE(std::string line) { return "\t" + line + "\n"; } diff --git a/tests/unit/cypher_state_machine.cpp b/tests/unit/cypher_state_machine.cpp new file mode 100644 index 000000000..0ebdafb35 --- /dev/null +++ b/tests/unit/cypher_state_machine.cpp @@ -0,0 +1,36 @@ +#include <iostream> + +#include "query_engine/state_machine/cypher.hpp" +#include "utils/assert.hpp" +#include "utils/underlying_cast.hpp" + +using std::cout; +using std::endl; + +int main() +{ + // initialize cypher state machine + CypherStateMachine csm; + + // set cost for label index + auto test_cost = static_cast<uint64_t>(30); + csm.search_cost("n", entity_search::search_label_index, test_cost); + + // check all costs + auto max_cost = entity_search::max<uint64_t>(); + permanent_assert(csm.search_cost("n", entity_search::search_internal_id) == + max_cost, + "Search internal id cost should be max cost value"); + permanent_assert(csm.search_cost("n", entity_search::search_label_index) == + test_cost, + "Search label index cost should be test cost value"); + permanent_assert( + csm.search_cost("n", entity_search::search_property_index) == max_cost, + "Search property index should be max cost value"); + + // check minimum cost + permanent_assert(csm.min("n") == entity_search::search_label_index, + "Search place should be label index"); + + return 0; +}