diff --git a/build/.gitignore b/build/.gitignore index d6b7ef32c..051042cc0 100644 --- a/build/.gitignore +++ b/build/.gitignore @@ -1,2 +1,3 @@ -* +/* !.gitignore +!/compiled diff --git a/build/compiled/.gitignore b/build/compiled/.gitignore new file mode 100644 index 000000000..6a16f0329 --- /dev/null +++ b/build/compiled/.gitignore @@ -0,0 +1,3 @@ +/* +!.gitignore +!/cpu diff --git a/build/compiled/cpu/.gitignore b/build/compiled/cpu/.gitignore new file mode 100644 index 000000000..50ce2e0a4 --- /dev/null +++ b/build/compiled/cpu/.gitignore @@ -0,0 +1,3 @@ +/* +!.gitignore +!/hardcode diff --git a/build/compiled/cpu/hardcode/9470552549410791677.cpp b/build/compiled/cpu/hardcode/9470552549410791677.cpp new file mode 100644 index 000000000..036c84ad0 --- /dev/null +++ b/build/compiled/cpu/hardcode/9470552549410791677.cpp @@ -0,0 +1,149 @@ +#include <iostream> +#include <queue> +#include <string> +#include <vector> + +#include "query_engine/i_code_cpu.hpp" +#include "storage/model/properties/all.hpp" + +using std::cout; +using std::endl; + +// Dressipi astar query of 4 clicks. + +// BARRIER! +namespace barrier +{ + +// using STREAM = std::ostream; +using STREAM = RecordStream<::io::Socket>; + +constexpr size_t max_depth = 3; +constexpr size_t limit = 10; + +class Node +{ +public: + Node *parent = {nullptr}; + VertexPropertyType<Double> tkey; + double cost; + int depth = {0}; + VertexAccessor vacc; + + Node(VertexAccessor vacc, double cost, + VertexPropertyType<Double> const &tkey) + : cost(cost), vacc(vacc), tkey(tkey) + { + } + Node(VertexAccessor vacc, double cost, Node *parent, + VertexPropertyType<Double> const &tkey) + : cost(cost), vacc(vacc), parent(parent), depth(parent->depth + 1), + tkey(tkey) + { + } + + double sum_vertex_score() + { + auto now = this; + double sum = 0; + do { + sum += (now->vacc.at(tkey).get())->value(); + now = now->parent; + } while (now != nullptr); + return sum; + } +}; + +bool vertex_filter_contained(DbAccessor &t, VertexAccessor &v, Node *before) +{ + if (v.fill()) { + bool found; + do { + found = false; + before = before->parent; + if (before == nullptr) { + return true; + } + } while (v.in_contains(before->vacc)); + } + return false; +} + +void astar(DbAccessor &t, code_args_t &args, STREAM &stream) +{ + VertexPropertyType<Double> tkey = t.vertex_property_key<Double>("score"); + + auto cmp = [](Node *left, Node *right) { return left->cost > right->cost; }; + std::priority_queue<Node *, std::vector<Node *>, decltype(cmp)> queue(cmp); + std::vector<Node *> all_nodes; + + auto start_vr = t.vertex_find(Id(args[0].as<Int64>().value())); + if (!start_vr.is_present()) { + stream.write_failure({{}}); + return; + } + + start_vr.get().fill(); + Node *start = new Node(start_vr.take(), 0, tkey); + queue.push(start); + all_nodes.push_back(start); + + int count = 0; + do { + auto now = queue.top(); + queue.pop(); + + if (max_depth <= now->depth) { + // best.push_back(now); + count++; + if (count >= limit) { + break; + } + continue; + } + + iter::for_all(now->vacc.out(), [&](auto edge) { + VertexAccessor va = edge.to(); + if (vertex_filter_contained(t, va, now)) { + auto cost = 1 - va.at(tkey).get()->value(); + Node *n = new Node(va, now->cost + cost, now, tkey); + queue.push(n); + all_nodes.push_back(n); + } + }); + } while (!queue.empty()); + + stream.write_field("n"); + stream.write_record(); + stream.write_list_header(0); + stream.chunk(); + stream.write_meta("r"); + + for (auto n : all_nodes) { + delete n; + } +} + +class CodeCPU : public ICodeCPU<STREAM> +{ +public: + bool run(Db &db, code_args_t &args, STREAM &stream) override + { + DbAccessor t(db); + + astar(t, args, stream); + + return t.commit(); + } + + ~CodeCPU() {} +}; +} + +extern "C" ICodeCPU<barrier::STREAM> *produce() +{ + // BARRIER! + return new barrier::CodeCPU(); +} + +extern "C" void destruct(ICodeCPU<barrier::STREAM> *p) { delete p; } diff --git a/include/barrier/barrier.hpp b/include/barrier/barrier.hpp index 2d5edf002..0d52ef6ca 100644 --- a/include/barrier/barrier.hpp +++ b/include/barrier/barrier.hpp @@ -493,6 +493,7 @@ public: void write_list_header(size_t size); void write_record(); void write_meta(const std::string &type); + void write_failure(const std::map<std::string, std::string> &data); void send(); void chunk(); }; diff --git a/include/barrier/common.hpp b/include/barrier/common.hpp index ff89a9b1e..ed3f1a054 100644 --- a/include/barrier/common.hpp +++ b/include/barrier/common.hpp @@ -4,6 +4,7 @@ #include <type_traits> #include <utility> #include <vector> +#include <map> // THis shoul be the only place to include code from memgraph other than // barrier.cpp diff --git a/include/query_engine/code_generator/clause_action.hpp b/include/query_engine/code_generator/clause_action.hpp index b3c997651..6681259b5 100644 --- a/include/query_engine/code_generator/clause_action.hpp +++ b/include/query_engine/code_generator/clause_action.hpp @@ -16,5 +16,6 @@ enum class ClauseAction : uint32_t ReturnNode, ReturnRelationship, ReturnPack, - ReturnProjection + ReturnProjection, + ReturnCount }; diff --git a/include/query_engine/code_generator/handlers/return.hpp b/include/query_engine/code_generator/handlers/return.hpp index 6a77c9615..9d16cb550 100644 --- a/include/query_engine/code_generator/handlers/return.hpp +++ b/include/query_engine/code_generator/handlers/return.hpp @@ -69,5 +69,18 @@ auto return_query_action = } } + // return functions + for (auto const &kv : action_data.actions) + { + auto name = kv.first; + if (kv.second == ClauseAction::ReturnCount) + { + if (cypher_data.source(name) == EntitySource::MainStorage) + { + code += code_line(code::count, name); + } + } + } + return code; }; diff --git a/include/query_engine/program_loader.hpp b/include/query_engine/program_loader.hpp index aef72a4e3..ae7255ff2 100644 --- a/include/query_engine/program_loader.hpp +++ b/include/query_engine/program_loader.hpp @@ -12,6 +12,7 @@ #include "query_engine/query_stripper.hpp" #include "utils/hashing/fnv.hpp" #include "logging/default.hpp" +#include "utils/file.hpp" using std::string; @@ -46,16 +47,32 @@ public: return query_program_t(code, std::move(stripped)); } - // code has to be generated, compiled and loaded - // TODO load output path from config auto base_path = CONFIG(config::COMPILE_CPU_PATH); auto path_cpp = base_path + hash_string + ".cpp"; + auto hard_code_cpp = base_path + "hardcode/" + hash_string + ".cpp"; auto stripped_space = stripper.strip_space(query); - code_generator.generate_cpp(stripped_space.query, stripped.hash, - path_cpp); + // cpp files in the hardcode folder have bigger priority then + // other cpp files + if (!utils::fexists(hard_code_cpp)) + { + code_generator.generate_cpp(stripped_space.query, stripped.hash, + path_cpp); + } + + // compile the code auto path_so = base_path + hash_string + ".so"; - code_compiler.compile(path_cpp, path_so); + + // hardcoded queries are compiled to the same folder as generated + // queries (all .so files are in the same folder) + if (utils::fexists(hard_code_cpp)) + { + code_compiler.compile(hard_code_cpp, path_so); + } + else + { + code_compiler.compile(path_cpp, path_so); + } // loads dynamic lib and store it auto code_lib = load_code_lib(path_so); diff --git a/include/query_engine/traverser/code.hpp b/include/query_engine/traverser/code.hpp index 9bd035fae..e274efe66 100644 --- a/include/query_engine/traverser/code.hpp +++ b/include/query_engine/traverser/code.hpp @@ -105,6 +105,18 @@ const std::string find_and_write_edges_by_type = " }});\n" " stream.write_meta(\"rw\");\n"; +// TODO: vertices and edges +const std::string count = + "size_t count = 0;\n" + " t.vertex_access().fill().for_all(\n" + " [&](auto vertex) {{ ++count; }});\n" + " stream.write_field(\"count({0})\");\n" + " stream.write_record();\n" + " stream.write_list_header(1);\n" + " stream.write(Int64(count));\n" + " stream.chunk();\n" + " stream.write_meta(\"r\");\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 15e2bbfef..7dc69ebf3 100644 --- a/include/query_engine/traverser/cpp_traverser.hpp +++ b/include/query_engine/traverser/cpp_traverser.hpp @@ -519,4 +519,15 @@ public: code += generator.generate(); } + + void visit(ast::CountFunction& ast_count) override + { + auto &action_data = generator.action_data(); + auto &cypher_data = generator.cypher_data(); + + if (state == CypherState::Return) + { + action_data.actions[ast_count.argument] = ClauseAction::ReturnCount; + } + } }; diff --git a/include/utils/align.hpp b/include/utils/align.hpp new file mode 100644 index 000000000..b900b03ac --- /dev/null +++ b/include/utils/align.hpp @@ -0,0 +1,9 @@ +#pragma once + +// FROM: A Malloc Tutorial, Marwan Burelle, 2009 + +// align address x to 4 bytes +#define align_4(x) (((((x) - 1)>>2)<<2)+4) + +// align address x to 8 bytes +#define align_8(x) (((((x) - 1)>>3)<<3)+8) diff --git a/include/utils/file.hpp b/include/utils/file.hpp new file mode 100644 index 000000000..f07edd033 --- /dev/null +++ b/include/utils/file.hpp @@ -0,0 +1,20 @@ +#pragma once + +#include <fstream> + +namespace utils +{ + +inline bool fexists(const char *filename) +{ + std::ifstream ifile(filename); + return (bool)ifile; +} + +inline bool fexists(const std::string& filename) +{ + std::ifstream ifile(filename.c_str()); + return (bool)ifile; +} + +} diff --git a/poc/queries/astar.cpp b/poc/queries/astar.cpp index 6f1243217..5133e80f2 100644 --- a/poc/queries/astar.cpp +++ b/poc/queries/astar.cpp @@ -15,7 +15,8 @@ using std::endl; namespace barrier { -using STREAM = std::ostream; // RecordStream<::io::Socket>; +// using STREAM = std::ostream; +using STREAM = RecordStream<::io::Socket>; constexpr size_t max_depth = 3; constexpr size_t limit = 10; @@ -78,7 +79,7 @@ void astar(DbAccessor &t, code_args_t &args, STREAM &stream) auto start_vr = t.vertex_find(Id(args[0].as<Int64>().value())); if (!start_vr.is_present()) { - // stream.write_failure({{}}); + stream.write_failure({{}}); return; } @@ -93,7 +94,7 @@ void astar(DbAccessor &t, code_args_t &args, STREAM &stream) queue.pop(); if (max_depth <= now->depth) { - // stream.write_success_empty(); + stream.write_success_empty(); // best.push_back(now); count++; if (count >= limit) { diff --git a/src/barrier/barrier.cpp b/src/barrier/barrier.cpp index 4a9ab5320..659119a3f 100644 --- a/src/barrier/barrier.cpp +++ b/src/barrier/barrier.cpp @@ -674,6 +674,12 @@ void RecordStream<Stream>::write_meta(const std::string &type) HALF_CALL(write_meta(type)); } +template <class Stream> +void RecordStream<Stream>::write_failure(const std::map<std::string, std::string> &data) +{ + HALF_CALL(write_failure(data)); +} + template <class Stream> void RecordStream<Stream>::send() { diff --git a/src/cypher/ast/ast.hpp b/src/cypher/ast/ast.hpp index 29e2e9408..04b356ee6 100644 --- a/src/cypher/ast/ast.hpp +++ b/src/cypher/ast/ast.hpp @@ -19,3 +19,4 @@ #include "set.hpp" #include "expr.hpp" #include "with.hpp" +#include "functions.hpp" diff --git a/src/cypher/ast/ast_visitor.hpp b/src/cypher/ast/ast_visitor.hpp index ba53cc3e4..76a0cf331 100644 --- a/src/cypher/ast/ast_visitor.hpp +++ b/src/cypher/ast/ast_visitor.hpp @@ -36,6 +36,9 @@ struct Star; struct Slash; struct Rem; +// functions +struct CountFunction; + struct RelationshipSpecs; struct RelationshipTypeList; struct Relationship; @@ -83,6 +86,7 @@ struct AstVisitor PatternList, Match, ReadQuery, Start, Where, WriteQuery, Create, Return, Distinct, Delete, DeleteQuery, UpdateQuery, Set, SetKey, ReadWriteQuery, IdentifierList, WithList, WithClause, WithQuery, Long, + CountFunction, InternalIdExpr, SetValue, SetElement, SetList> { }; diff --git a/src/cypher/ast/expr.hpp b/src/cypher/ast/expr.hpp index 0e80100a0..f85e902ca 100644 --- a/src/cypher/ast/expr.hpp +++ b/src/cypher/ast/expr.hpp @@ -24,6 +24,16 @@ struct LeafExpr : public ValueExpr<Derived> T value; }; +// T is argument type +template <class T, class Derived> +struct FunctionExpr : public ValueExpr<Derived> +{ + FunctionExpr(const std::string& name, T argument) : name(name), argument(argument) {} + + std::string name; + T argument; +}; + template <class Derived> struct BinaryExpr : public ValueExpr<Derived> { @@ -43,4 +53,5 @@ struct PatternExpr : public Expr Pattern *pattern; }; + } diff --git a/src/cypher/ast/functions.hpp b/src/cypher/ast/functions.hpp new file mode 100644 index 000000000..53d2bb7b8 --- /dev/null +++ b/src/cypher/ast/functions.hpp @@ -0,0 +1,14 @@ +#pragma once + +#include "expr.hpp" + +namespace ast +{ + +struct CountFunction : public FunctionExpr<std::string, CountFunction> +{ + CountFunction(const std::string &argument) : FunctionExpr("count", argument) + { + } +}; +} diff --git a/src/cypher/cypher.y b/src/cypher/cypher.y index bfc9891d4..bd65e6a2a 100644 --- a/src/cypher/cypher.y +++ b/src/cypher/cypher.y @@ -495,6 +495,12 @@ pattern_expr(E) ::= pattern(P). { E = ast->create<ast::PatternExpr>(P); } +%type function_expr {ast::Expr*} + +function_expr(E) ::= COUNT LP IDN(A) RP. { + E = ast->create<ast::CountFunction>(A->value); +} + %type expr {ast::Expr*} expr(E) ::= value_expr(V). { @@ -505,6 +511,10 @@ expr(E) ::= pattern_expr(P). { E = P; } +expr(E) ::= function_expr(F). { + E = F; +} + //%type alias {ast::Alias*} // //alias(A) ::= IDN(X) AS IDN(Y). { diff --git a/src/cypher/debug/tree_print.hpp b/src/cypher/debug/tree_print.hpp index 1ea4658d6..d16fb30d9 100644 --- a/src/cypher/debug/tree_print.hpp +++ b/src/cypher/debug/tree_print.hpp @@ -294,6 +294,12 @@ public: Traverser::visit(rem); } + void visit(ast::CountFunction& count) override + { + auto entry = printer.advance("Count "); + entry << count.name << "(" << count.argument << ")"; + } + void visit(ast::PropertyList& prop_list) override { auto entry = printer.advance("Property List"); diff --git a/src/cypher/tokenizer/cypher_lexer.hpp b/src/cypher/tokenizer/cypher_lexer.hpp index 13c1b1758..3d708b48a 100644 --- a/src/cypher/tokenizer/cypher_lexer.hpp +++ b/src/cypher/tokenizer/cypher_lexer.hpp @@ -58,6 +58,9 @@ public: rule("(?i:AND)", TK_AND); rule("(?i:OR)", TK_OR); + // functions + rule("(?i:COUNT)", TK_COUNT); + // string literal TODO single quote escape rule("'(.*?)'", TK_STR); diff --git a/src/cypher/visitor/traverser.hpp b/src/cypher/visitor/traverser.hpp index 87d68ff77..66c5e9090 100644 --- a/src/cypher/visitor/traverser.hpp +++ b/src/cypher/visitor/traverser.hpp @@ -154,6 +154,10 @@ public: accept(rem.right); } + void visit(ast::CountFunction& count) override + { + } + void visit(ast::PropertyList& prop_list) override { accept(prop_list.value);