Major properties system and database accessor refactor: first stable state (compiles).
This commit is contained in:
@@ -29,7 +29,7 @@ namespace bolt {
|
||||
|
||||
Bolt &bolt;
|
||||
|
||||
GraphDb &active_db();
|
||||
GraphDbAccessor active_db();
|
||||
|
||||
Decoder decoder;
|
||||
OutputStream output_stream{socket};
|
||||
|
||||
@@ -140,6 +140,10 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
void write(const std::string& str) {
|
||||
write_string(str);
|
||||
}
|
||||
|
||||
void write_string(const std::string &str)
|
||||
{
|
||||
write_string(str.c_str(), str.size());
|
||||
|
||||
@@ -31,26 +31,6 @@ public:
|
||||
using EdgeType = std::string*;
|
||||
using Property = std::string*;
|
||||
|
||||
/**
|
||||
* This constructor will create a database with the name "default"
|
||||
*
|
||||
* NOTE: explicit is here to prevent compiler from evaluating const char *
|
||||
* into a bool.
|
||||
*
|
||||
* @param import_snapshot will in constructor import latest snapshot
|
||||
* into the db.
|
||||
*/
|
||||
explicit GraphDb(bool import_snapshot = true);
|
||||
|
||||
/**
|
||||
* Construct database with a custom name.
|
||||
*
|
||||
* @param name database name
|
||||
* @param import_snapshot will in constructor import latest snapshot
|
||||
* into the db.
|
||||
*/
|
||||
GraphDb(const char *name, bool import_snapshot = true);
|
||||
|
||||
/**
|
||||
* Construct database with a custom name.
|
||||
*
|
||||
@@ -80,8 +60,8 @@ public:
|
||||
const std::string name_;
|
||||
|
||||
// main storage for the graph
|
||||
SkipList<mvcc::VersionList<Edge>*> edges_;
|
||||
SkipList<mvcc::VersionList<Vertex>*> vertices_;
|
||||
SkipList<mvcc::VersionList<Edge>*> edges_;
|
||||
|
||||
// unique object stores
|
||||
ConcurrentSet<std::string> labels_;
|
||||
|
||||
@@ -10,9 +10,21 @@
|
||||
|
||||
|
||||
class GraphDbAccessor {
|
||||
GraphDbAccessor(GraphDb& db);
|
||||
|
||||
public:
|
||||
|
||||
/**
|
||||
* Creates an accessor for the given database.
|
||||
*
|
||||
* @param db The database
|
||||
*/
|
||||
GraphDbAccessor(GraphDb& db);
|
||||
|
||||
/**
|
||||
* Returns the name of the database of this accessor.
|
||||
*/
|
||||
const std::string& name() const;
|
||||
|
||||
/**
|
||||
* Creates a new Vertex and returns an accessor to it.
|
||||
*
|
||||
@@ -20,6 +32,24 @@ public:
|
||||
*/
|
||||
VertexAccessor insert_vertex();
|
||||
|
||||
/**
|
||||
* Removes the vertex of the given accessor. If the vertex has any outgoing
|
||||
* or incoming edges, it is not deleted. See `detach_remove_vertex` if you
|
||||
* want to remove a vertex regardless of connectivity.
|
||||
*
|
||||
* @param vertex_accessor Accessor to vertex.
|
||||
* @return If or not the vertex was deleted.
|
||||
*/
|
||||
bool remove_vertex(VertexAccessor &vertex_accessor);
|
||||
|
||||
/**
|
||||
* Removes the vertex of the given accessor along with all it's outgoing
|
||||
* and incoming connections.
|
||||
*
|
||||
* @param vertex_accessor Accessor to a vertex.
|
||||
*/
|
||||
void detach_remove_vertex(VertexAccessor &vertex_accessor);
|
||||
|
||||
/**
|
||||
* Creates a new Edge and returns an accessor to it.
|
||||
*
|
||||
@@ -30,6 +60,13 @@ public:
|
||||
*/
|
||||
EdgeAccessor insert_edge(VertexAccessor& from, VertexAccessor& to, GraphDb::EdgeType type);
|
||||
|
||||
/**
|
||||
* Removes an edge from the graph.
|
||||
*
|
||||
* @param edge_accessor The accessor to an edge.
|
||||
*/
|
||||
void remove_edge(EdgeAccessor& edge_accessor);
|
||||
|
||||
/**
|
||||
* Obtains the Label for the label's name.
|
||||
* @return See above.
|
||||
@@ -77,7 +114,4 @@ public:
|
||||
|
||||
private:
|
||||
GraphDb& db_;
|
||||
|
||||
// for privileged access to some RecordAccessor functionality (and similar)
|
||||
const PassKey<GraphDbAccessor> pass_key;
|
||||
};
|
||||
|
||||
@@ -3,27 +3,36 @@
|
||||
#include "config/config.hpp"
|
||||
#include "data_structures/concurrent/concurrent_map.hpp"
|
||||
#include "database/graph_db.hpp"
|
||||
#include "database/graph_db_accessor.hpp"
|
||||
|
||||
//#include "dbms/cleaner.hpp"
|
||||
//#include "snapshot/snapshoter.hpp"
|
||||
|
||||
class Dbms
|
||||
{
|
||||
public:
|
||||
Dbms() { create_default(); }
|
||||
Dbms() {
|
||||
// create the default database and set is a active
|
||||
active("default");
|
||||
}
|
||||
|
||||
// returns active database
|
||||
GraphDb &active();
|
||||
/**
|
||||
* Returns an accessor to the active database.
|
||||
*/
|
||||
GraphDbAccessor active();
|
||||
|
||||
// set active database
|
||||
// if active database doesn't exist creates one
|
||||
GraphDb &active(const std::string &name);
|
||||
/**
|
||||
* Set the database with the given name to be active.
|
||||
* If there is no database with the given name,
|
||||
* it's created.
|
||||
*
|
||||
* @return an accessor to the database with the given name.
|
||||
*/
|
||||
GraphDbAccessor active(const std::string &name);
|
||||
|
||||
// TODO: DELETE action
|
||||
|
||||
private:
|
||||
// creates default database
|
||||
GraphDb &create_default() { return active("default"); }
|
||||
|
||||
// dbs container
|
||||
ConcurrentMap<std::string, GraphDb> dbs;
|
||||
|
||||
|
||||
@@ -23,6 +23,18 @@ template <class T>
|
||||
class Record : public Version<T>
|
||||
{
|
||||
public:
|
||||
|
||||
Record() = default;
|
||||
|
||||
// The copy constructor ignores tx, cmd, hints and super because
|
||||
// they contain atomic variables that can't be copied
|
||||
// it's still useful to have this copy constructor so that subclass
|
||||
// data can easily be copied
|
||||
// TODO maybe disable the copy-constructor and instead use a
|
||||
// data variable in the version_list update() function (and similar)
|
||||
// like it was in Dominik's implementation
|
||||
Record(const Record &other) {}
|
||||
|
||||
// tx.cre is the id of the transaction that created the record
|
||||
// and tx.exp is the id of the transaction that deleted the record
|
||||
// these values are used to determine the visibility of the record
|
||||
|
||||
@@ -118,12 +118,16 @@ namespace mvcc {
|
||||
return r;
|
||||
}
|
||||
|
||||
T *insert(tx::Transaction &t) {
|
||||
/**
|
||||
* @Args forwarded to the constructor of T
|
||||
*/
|
||||
template <typename... Args>
|
||||
T *insert(tx::Transaction &t, Args&&... args) {
|
||||
assert(head == nullptr);
|
||||
|
||||
// create a first version of the record
|
||||
// TODO replace 'new' with something better
|
||||
auto v1 = new T();
|
||||
auto v1 = new T(std::forward<Args>(args)...);
|
||||
|
||||
// mark the record as created by the transaction t
|
||||
v1->mark_created(t);
|
||||
@@ -150,8 +154,7 @@ namespace mvcc {
|
||||
// It could be done with unique_ptr but while this could mean memory
|
||||
// leak on exception, unique_ptr could mean use after free. Memory
|
||||
// leak is less dangerous.
|
||||
auto updated = new T();
|
||||
updated->data = record->data;
|
||||
auto updated = new T(*record);
|
||||
|
||||
updated->mark_created(t);
|
||||
record->mark_deleted(t);
|
||||
|
||||
@@ -1,26 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
enum class ClauseAction : uint32_t
|
||||
{
|
||||
Undefined,
|
||||
CreateNode,
|
||||
MatchNode,
|
||||
UpdateNode,
|
||||
DeleteNode,
|
||||
CreateRelationship,
|
||||
MatchRelationship,
|
||||
UpdateRelationship,
|
||||
DeleteRelationship,
|
||||
ReturnNode,
|
||||
ReturnRelationship,
|
||||
ReturnPack,
|
||||
ReturnProjection,
|
||||
ReturnCount,
|
||||
ReturnLabels,
|
||||
|
||||
UpdateEntityLabels,
|
||||
UpdateEntityLabels_Identifier,
|
||||
UpdateEntityLabels_Labels
|
||||
};
|
||||
@@ -1,226 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
// !! DEPRICATED !!
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "query/util.hpp"
|
||||
|
||||
class Code
|
||||
{
|
||||
public:
|
||||
std::string code;
|
||||
|
||||
void reset() { code = ""; }
|
||||
};
|
||||
|
||||
namespace code
|
||||
{
|
||||
|
||||
// TODO: one more abstraction level
|
||||
// TODO: UNIT tests
|
||||
|
||||
const std::string transaction_begin = "DbAccessor t(db);";
|
||||
|
||||
const std::string transaction_commit = "t.commit();";
|
||||
|
||||
const std::string set_property = "{}.set({}, std::move(args[{}]));";
|
||||
|
||||
// create vertex e.g. CREATE (n:PERSON {name: "Test", age: 23})
|
||||
const std::string create_vertex = "auto {} = t.vertex_insert();";
|
||||
const std::string create_label = "auto &{0} = t.label_find_or_create(\"{0}\");";
|
||||
const std::string add_label = "{}.add_label({});";
|
||||
|
||||
const std::string vertex_property_key =
|
||||
"auto {}=t.vertex_property_key(\"{}\",args[{}].key.flags());";
|
||||
const std::string edge_property_key =
|
||||
"auto {}=t.edge_property_key(\"{}\",args[{}].key.flags());";
|
||||
|
||||
// create edge e.g CREATE (n1)-[r:COST {cost: 100}]->(n2)
|
||||
const std::string create_edge = "auto {} = t.edge_insert({},{});";
|
||||
const std::string find_type = "auto &{0} = t.type_find_or_create(\"{0}\");";
|
||||
const std::string set_type = "{}.edge_type({});";
|
||||
|
||||
const std::string args_id = "Int32 id = args[{}].as<Int32>();";
|
||||
|
||||
const std::string vertex_accessor_args_id =
|
||||
"auto vertex_accessor = t.vertex_find(id.value());";
|
||||
|
||||
const std::string match_vertex_by_id =
|
||||
"auto option_{0} = t.vertex_find(args[{1}].as<Int64>().value());\n"
|
||||
" if (!option_fill(option_{0})) return t.commit(), false;\n"
|
||||
" auto {0}=option_{0}.take();";
|
||||
const std::string match_edge_by_id =
|
||||
"auto option_{0} = t.edge_find(args[{1}].as<Int64>().value());\n"
|
||||
" if (!option_fill(option_{0})) return t.commit(), false;\n"
|
||||
" auto {0}=option_{0}.take();";
|
||||
|
||||
const std::string write_entity = "stream.write_field(\"{0}\");\n"
|
||||
" stream.write_record();\n"
|
||||
" stream.write_list_header(1);\n"
|
||||
" stream.write({0});\n"
|
||||
" stream.chunk();"
|
||||
" stream.write_meta(\"rw\");\n";
|
||||
|
||||
const std::string write_vertex_accessor =
|
||||
" stream.write_record();\n"
|
||||
" stream.write_list_header(1);\n"
|
||||
" stream.write(vertex_accessor);\n"
|
||||
" stream.chunk();\n";
|
||||
|
||||
const std::string write_all_vertices =
|
||||
"stream.write_field(\"{0}\");\n"
|
||||
" iter::for_all(t.vertex_access(), [&](auto vertex_accessor) {{\n"
|
||||
" if (vertex_accessor.fill()) {{\n"
|
||||
+ write_vertex_accessor +
|
||||
" }}\n"
|
||||
" }});\n"
|
||||
" stream.write_meta(\"rw\");\n";
|
||||
|
||||
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_accessor) {{\n"
|
||||
+ write_vertex_accessor +
|
||||
" }});\n"
|
||||
" stream.write_meta(\"rw\");\n";
|
||||
|
||||
const std::string find_and_write_vertices_by_label_and_properties =
|
||||
"{{\n"
|
||||
" DbAccessor _t(db);\n"
|
||||
" {0}\n"
|
||||
" auto properties = query_properties(indices, args);\n"
|
||||
" auto &label = _t.label_find_or_create(\"{1}\");\n"
|
||||
" stream.write_field(\"{2}\");\n"
|
||||
" label.index().for_range(_t).properties_filter(_t, properties).for_all(\n"
|
||||
" [&](auto vertex_accessor) -> void {{\n"
|
||||
" "+ write_vertex_accessor +
|
||||
" }});\n"
|
||||
" stream.write_meta(\"rw\");\n"
|
||||
" _t.commit();"
|
||||
"}}\n";
|
||||
|
||||
// -- LABELS
|
||||
const std::string set_vertex_element =
|
||||
"{{\n"
|
||||
" DbAccessor _t(db);" // TODO: HACK (set labels should somehow persist the state)
|
||||
" {0}\n"
|
||||
" auto properties = query_properties(indices, args);\n"
|
||||
" auto &label = _t.label_find_or_create(\"{1}\");\n"
|
||||
" label.index().for_range(_t).properties_filter(_t, properties).for_all(\n"
|
||||
" [&](auto vertex_accessor) -> void {{\n"
|
||||
" auto {2} = _t.vertex_property_key(\"{2}\", args[{3}].key.flags());\n"
|
||||
" vertex_accessor.set({2}, std::move(args[{3}]));\n"
|
||||
" }}\n"
|
||||
" );\n"
|
||||
" _t.commit();\n"
|
||||
"}}";
|
||||
|
||||
const std::string set_labels_start =
|
||||
" {{\n"
|
||||
" DbAccessor _t(db);" // TODO: HACK (set labels should somehow persist the state)
|
||||
" {0}\n"
|
||||
" auto properties = query_properties(indices, args);\n"
|
||||
" auto &label = _t.label_find_or_create(\"{1}\");\n"
|
||||
" label.index().for_range(_t).properties_filter(_t, properties).for_all(\n"
|
||||
" [&](auto vertex_accessor) -> void {{\n";
|
||||
const std::string set_label =
|
||||
" auto &{0} = _t.label_find_or_create(\"{0}\");\n"
|
||||
" vertex_accessor.add_label({0});\n";
|
||||
const std::string set_labels_end =
|
||||
" }}\n"
|
||||
" );\n"
|
||||
" _t.commit();"
|
||||
" }}";
|
||||
|
||||
const std::string return_labels =
|
||||
"{{\n"
|
||||
" DbAccessor _t(db);" // TODO: HACK (set labels should somehow persist the state)
|
||||
" {0}\n"
|
||||
" auto properties = query_properties(indices, args);\n"
|
||||
" auto &label = _t.label_find_or_create(\"{1}\");\n"
|
||||
" stream.write_field(\"labels({2})\");\n"
|
||||
" label.index().for_range(_t).properties_filter(_t, properties).for_all(\n"
|
||||
" [&](auto vertex_accessor) -> void {{\n"
|
||||
" auto &labels = vertex_accessor.labels();\n"
|
||||
" stream.write_record();\n"
|
||||
" stream.write_list_header(1);\n" // TODO: figure out why
|
||||
" stream.write_list_header(labels.size());\n"
|
||||
" for (auto &label : labels) {{\n"
|
||||
" stream.write(label.get().str());\n"
|
||||
" }}\n"
|
||||
" stream.chunk();\n"
|
||||
" }}\n"
|
||||
" );\n"
|
||||
" stream.write_meta(\"rw\");\n"
|
||||
" _t.commit();\n"
|
||||
"}}";
|
||||
|
||||
const std::string write_all_edges =
|
||||
"stream.write_field(\"{0}\");\n"
|
||||
" iter::for_all(t.edge_access(), [&](auto edge) {{\n"
|
||||
" if (edge.fill()) {{\n"
|
||||
" stream.write_record();\n"
|
||||
" stream.write_list_header(1);\n"
|
||||
" stream.write(edge);\n"
|
||||
" stream.chunk();\n"
|
||||
" }}\n"
|
||||
" }});\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 count_vertices_for_one_label =
|
||||
"size_t count = 0;\n"
|
||||
"auto &label = t.label_find_or_create(\"{1}\");\n"
|
||||
" label.index().for_range(t).for_all([&](auto vertex) {{\n"
|
||||
" count++;\n"
|
||||
" }});\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";
|
||||
|
||||
// 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: {}";
|
||||
const std::string print_properties =
|
||||
"cout << \"{0}\" << endl;\n"
|
||||
" cout_properties({0}.properties());";
|
||||
const std::string print_property =
|
||||
"cout_property(\"{0}\", {0}.property(\"{1}\"));";
|
||||
}
|
||||
|
||||
// DELETE
|
||||
const std::string delete_all_detached_nodes =
|
||||
"t.vertex_access().fill().isolated().for_all(\n"
|
||||
" [&](auto a) {{ a.remove(); }});\n"
|
||||
" stream.write_empty_fields();\n"
|
||||
" stream.write_meta(\"w\");\n";
|
||||
const std::string delete_whole_graph =
|
||||
"t.edge_access().fill().for_all(\n"
|
||||
" [&](auto e) { e.remove(); }\n"
|
||||
");\n" + delete_all_detached_nodes;
|
||||
@@ -1,85 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include "query/backend/cpp_old/cypher_state.hpp"
|
||||
#include "query/backend/cpp_old/handlers/all.hpp"
|
||||
#include "query/backend/cpp_old/query_action.hpp"
|
||||
#include "query/exception/cpp_code_generator.hpp"
|
||||
|
||||
class CppGenerator
|
||||
{
|
||||
public:
|
||||
// !! multithread problem
|
||||
// two threads shouldn't use this implementation at the same time
|
||||
// !! TODO: REFACTOR
|
||||
|
||||
CppGenerator() : unprocessed_index(0), processed_index(0) { setup(); }
|
||||
|
||||
void state(CypherState state) { _cypher_state = state; }
|
||||
|
||||
CypherState state() { return _cypher_state; }
|
||||
|
||||
std::string generate()
|
||||
{
|
||||
std::string code = "";
|
||||
|
||||
for (uint64_t i = processed_index; i < unprocessed_index; ++i)
|
||||
{
|
||||
auto &action = actions.at(i);
|
||||
auto query_action = action.first;
|
||||
if (action_functions.find(query_action) == action_functions.end())
|
||||
throw CppCodeGeneratorException(
|
||||
"Query Action Function is not defined");
|
||||
auto &action_data = action.second;
|
||||
code += action_functions[query_action](_cypher_data, action_data);
|
||||
++processed_index;
|
||||
}
|
||||
|
||||
return code;
|
||||
}
|
||||
|
||||
QueryActionData &add_action(const QueryAction &query_action)
|
||||
{
|
||||
unprocessed_index++;
|
||||
actions.push_back(std::make_pair(query_action, QueryActionData()));
|
||||
return action_data();
|
||||
}
|
||||
|
||||
QueryActionData &action_data() { return actions.back().second; }
|
||||
|
||||
CypherStateData &cypher_data() { return _cypher_data; }
|
||||
|
||||
void clear()
|
||||
{
|
||||
processed_index = 0;
|
||||
unprocessed_index = 0;
|
||||
actions.clear();
|
||||
}
|
||||
|
||||
private:
|
||||
// TODO: setup function is going to be called every time
|
||||
// when object of this class is constructed (optimize this)
|
||||
void setup()
|
||||
{
|
||||
action_functions[QueryAction::TransactionBegin] =
|
||||
transaction_begin_action;
|
||||
action_functions[QueryAction::Create] = create_query_action;
|
||||
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;
|
||||
}
|
||||
|
||||
uint64_t unprocessed_index;
|
||||
uint64_t processed_index;
|
||||
std::vector<std::pair<QueryAction, QueryActionData>> actions;
|
||||
std::map<QueryAction,
|
||||
std::function<std::string(CypherStateData &cypher_data,
|
||||
QueryActionData &action_data)>>
|
||||
action_functions;
|
||||
CypherState _cypher_state;
|
||||
CypherStateData _cypher_data;
|
||||
};
|
||||
@@ -1,57 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
// DEPRICATED!
|
||||
#include "config/config.hpp"
|
||||
#include "query/frontend/cypher/traverser.hpp"
|
||||
#include "query/language/cypher/errors.hpp"
|
||||
#include "template_engine/engine.hpp"
|
||||
#include "utils/string/file.hpp"
|
||||
#include "utils/type_discovery.hpp"
|
||||
|
||||
template <typename Stream>
|
||||
class CypherBackend
|
||||
{
|
||||
public:
|
||||
CypherBackend() : logger(logging::log->logger("CypherBackend"))
|
||||
{
|
||||
// load template file
|
||||
std::string template_path = CONFIG(config::TEMPLATE_CPP_PATH);
|
||||
template_text = utils::read_text(fs::path(template_path));
|
||||
}
|
||||
|
||||
template <typename Tree>
|
||||
// TODO: query and path shoud be distinct types
|
||||
void generate_code(const Tree &tree, const std::string &query,
|
||||
const uint64_t stripped_hash, const std::string &path)
|
||||
{
|
||||
CppTraverser cpp_traverser;
|
||||
cpp_traverser.reset();
|
||||
|
||||
try {
|
||||
tree.root->accept(cpp_traverser);
|
||||
} catch (const CypherSemanticError &e) {
|
||||
throw e;
|
||||
} catch (const std::exception &e) {
|
||||
logger.error("AST traversal error: {}", std::string(e.what()));
|
||||
throw e;
|
||||
}
|
||||
|
||||
// save the code
|
||||
std::string generated = template_engine::render(
|
||||
template_text.str(), {{"class_name", "CPUPlan"},
|
||||
{"stripped_hash", std::to_string(stripped_hash)},
|
||||
{"query", query},
|
||||
{"stream", type_name<Stream>().to_string()},
|
||||
{"code", cpp_traverser.code}});
|
||||
|
||||
logger.trace("generated code: {}", generated);
|
||||
|
||||
utils::write(utils::Text(generated), fs::path(path));
|
||||
}
|
||||
|
||||
protected:
|
||||
Logger logger;
|
||||
|
||||
private:
|
||||
utils::Text template_text;
|
||||
};
|
||||
@@ -1,215 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "query/backend/cpp_old/namer.hpp"
|
||||
#include "query/exception/cpp_code_generator.hpp"
|
||||
#include "storage/model/properties/flags.hpp"
|
||||
|
||||
// main states that are used while ast is traversed
|
||||
// in order to generate ActionSequence
|
||||
enum class CypherState : uint8_t
|
||||
{
|
||||
Undefined,
|
||||
Match,
|
||||
Where,
|
||||
Create,
|
||||
Set,
|
||||
Return,
|
||||
Delete
|
||||
};
|
||||
|
||||
enum class EntityStatus : uint8_t
|
||||
{
|
||||
None,
|
||||
Matched,
|
||||
Created
|
||||
};
|
||||
|
||||
enum class EntityType : uint8_t
|
||||
{
|
||||
None,
|
||||
Node,
|
||||
Relationship
|
||||
};
|
||||
|
||||
// where OR how entity can be found
|
||||
enum class EntitySource : uint8_t
|
||||
{
|
||||
None,
|
||||
InternalId,
|
||||
LabelIndex,
|
||||
TypeIndex,
|
||||
MainStorage
|
||||
};
|
||||
|
||||
// TODO: reduce copying
|
||||
class CypherStateData
|
||||
{
|
||||
public:
|
||||
using tags_type = std::vector<std::string>;
|
||||
using properties_type = std::map<std::string, int64_t>;
|
||||
|
||||
private:
|
||||
std::map<std::string, EntityStatus> entity_status;
|
||||
std::map<std::string, EntityType> entity_type;
|
||||
std::map<std::string, EntitySource> entity_source;
|
||||
std::map<std::string, tags_type> entity_tags;
|
||||
std::map<std::string, properties_type> entity_properties;
|
||||
|
||||
public:
|
||||
bool exist(const std::string &name) const
|
||||
{
|
||||
return entity_status.find(name) != entity_status.end();
|
||||
}
|
||||
|
||||
EntityStatus status(const std::string &name)
|
||||
{
|
||||
if (entity_status.find(name) == entity_status.end())
|
||||
return EntityStatus::None;
|
||||
|
||||
return entity_status.at(name);
|
||||
}
|
||||
|
||||
EntityType type(const std::string &name) const
|
||||
{
|
||||
if (entity_type.find(name) == entity_type.end())
|
||||
return EntityType::None;
|
||||
|
||||
return entity_type.at(name);
|
||||
}
|
||||
|
||||
EntitySource source(const std::string &name) const
|
||||
{
|
||||
if (entity_source.find(name) == entity_source.end())
|
||||
return EntitySource::None;
|
||||
return entity_source.at(name);
|
||||
}
|
||||
|
||||
const std::map<std::string, EntityType> &all_typed_enteties()
|
||||
{
|
||||
return entity_type;
|
||||
}
|
||||
|
||||
void node_matched(const std::string &name)
|
||||
{
|
||||
entity_type[name] = EntityType::Node;
|
||||
entity_status[name] = EntityStatus::Matched;
|
||||
}
|
||||
|
||||
void node_created(const std::string &name)
|
||||
{
|
||||
entity_type[name] = EntityType::Node;
|
||||
entity_status[name] = EntityStatus::Created;
|
||||
}
|
||||
|
||||
void relationship_matched(const std::string &name)
|
||||
{
|
||||
entity_type[name] = EntityType::Relationship;
|
||||
entity_status[name] = EntityStatus::Matched;
|
||||
}
|
||||
|
||||
void relationship_created(const std::string &name)
|
||||
{
|
||||
entity_type[name] = EntityType::Relationship;
|
||||
entity_status[name] = EntityStatus::Created;
|
||||
}
|
||||
|
||||
void source(const std::string &name, EntitySource source)
|
||||
{
|
||||
entity_source[name] = source;
|
||||
}
|
||||
|
||||
// entity tags
|
||||
auto tags(const std::string &name) const
|
||||
{
|
||||
if (entity_tags.find(name) == entity_tags.end())
|
||||
throw CppCodeGeneratorException("No tags for specified entity");
|
||||
return entity_tags.at(name);
|
||||
}
|
||||
|
||||
void tags(const std::string &name, tags_type tags)
|
||||
{
|
||||
entity_tags[name] = tags;
|
||||
}
|
||||
|
||||
void tag(const std::string &name, const std::string &new_tag)
|
||||
{
|
||||
if (entity_tags.find(name) == entity_tags.end())
|
||||
{
|
||||
entity_tags[name] = std::vector<std::string>{};
|
||||
}
|
||||
entity_tags[name].emplace_back(new_tag);
|
||||
}
|
||||
|
||||
auto has_properties(const std::string& name)
|
||||
{
|
||||
return entity_properties.find(name) != entity_properties.end();
|
||||
}
|
||||
|
||||
// entity properties
|
||||
auto properties(const std::string &name) const
|
||||
{
|
||||
if (entity_properties.find(name) == entity_properties.end())
|
||||
throw CppCodeGeneratorException(
|
||||
"No properties for specified entity: " + name);
|
||||
return entity_properties.at(name);
|
||||
}
|
||||
|
||||
void properties(const std::string &name, properties_type properties)
|
||||
{
|
||||
entity_properties[name] = properties;
|
||||
}
|
||||
|
||||
void index(const std::string &entity, const std::string &property,
|
||||
int64_t index)
|
||||
{
|
||||
if (entity_properties.find(entity) == entity_properties.end())
|
||||
{
|
||||
entity_properties[entity] = properties_type{};
|
||||
}
|
||||
entity_properties[entity][property] = index;
|
||||
}
|
||||
|
||||
auto index(const std::string &entity, const std::string &property_name)
|
||||
{
|
||||
if (entity_properties.find(entity) == entity_properties.end())
|
||||
throw CppCodeGeneratorException(
|
||||
"No properties for specified entity");
|
||||
|
||||
auto properties = entity_properties.at(entity);
|
||||
|
||||
if (properties.find(property_name) == properties.end())
|
||||
throw CppCodeGeneratorException(
|
||||
"No property for specified property name");
|
||||
|
||||
return properties[property_name];
|
||||
}
|
||||
|
||||
using iter_t = properties_type::iterator;
|
||||
auto print_indices(const std::string &name,
|
||||
const std::string &variable_name = "indices")
|
||||
{
|
||||
// TODO: find out smarter way it's not hard
|
||||
std::string print =
|
||||
"std::map<std::string, int64_t> " + variable_name + " = {";
|
||||
|
||||
auto indices = entity_properties.at(name);
|
||||
size_t i = 0;
|
||||
iter_t it = indices.begin();
|
||||
|
||||
for (; it != indices.end(); ++it, ++i)
|
||||
{
|
||||
print +=
|
||||
"{\"" + it->first + "\"," + std::to_string(it->second) + "}";
|
||||
if (i < indices.size() - 1) print += ",";
|
||||
}
|
||||
|
||||
print += "};";
|
||||
|
||||
return print;
|
||||
}
|
||||
};
|
||||
@@ -1,131 +0,0 @@
|
||||
#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 type_cost = 1000;
|
||||
constexpr cost_t max_cost = max<cost_t>();
|
||||
|
||||
template <typename T>
|
||||
class SearchCost
|
||||
{
|
||||
public:
|
||||
enum class SearchPlace : int
|
||||
{
|
||||
internal_id,
|
||||
label_index,
|
||||
type_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::type_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_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;
|
||||
}
|
||||
|
||||
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
|
||||
{
|
||||
if (_search_costs.find(entity) == _search_costs.end())
|
||||
return entity_search::search_cost_t::SearchPlace::main_storage;
|
||||
|
||||
return _search_costs.at(entity).min();
|
||||
}
|
||||
|
||||
private:
|
||||
std::map<std::string, entity_search::search_cost_t> _search_costs;
|
||||
};
|
||||
@@ -1,11 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
// TODO: refactor build state machine instead of ifs
|
||||
|
||||
#include "query/backend/cpp_old/handlers/create.hpp"
|
||||
#include "query/backend/cpp_old/handlers/delete.hpp"
|
||||
#include "query/backend/cpp_old/handlers/match.hpp"
|
||||
#include "query/backend/cpp_old/handlers/return.hpp"
|
||||
#include "query/backend/cpp_old/handlers/set.hpp"
|
||||
#include "query/backend/cpp_old/handlers/transaction_begin.hpp"
|
||||
#include "query/backend/cpp_old/handlers/transaction_commit.hpp"
|
||||
@@ -1,74 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "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) {
|
||||
// create node
|
||||
auto &name = kv.first;
|
||||
code += code_line(code::create_vertex, name);
|
||||
|
||||
// update properties
|
||||
code += update_properties(cypher_data, action_data, name);
|
||||
|
||||
// update labels
|
||||
auto entity_data = action_data.get_entity_property(name);
|
||||
for (auto &label : entity_data.tags) {
|
||||
code += code_line(code::create_label, label);
|
||||
code += code_line(code::add_label, name, label);
|
||||
}
|
||||
|
||||
// mark node as created
|
||||
cypher_data.node_created(name);
|
||||
}
|
||||
|
||||
if (kv.second == ClauseAction::CreateRelationship) {
|
||||
auto name = kv.first;
|
||||
|
||||
// find start and end node
|
||||
auto &relationships_data = action_data.relationship_data;
|
||||
if (relationships_data.find(name) == relationships_data.end())
|
||||
throw CppCodeGeneratorException("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 CypherSemanticError("Create Relationship: node " +
|
||||
left_node + " can't be found");
|
||||
}
|
||||
if (cypher_data.status(right_node) != EntityStatus::Matched) {
|
||||
throw CypherSemanticError("Create Relationship: node " +
|
||||
right_node + " can't be found");
|
||||
}
|
||||
|
||||
// create relationship
|
||||
code += code_line(code::create_edge, name, left_node, right_node);
|
||||
|
||||
// update properties
|
||||
code += update_properties(cypher_data, action_data, name);
|
||||
|
||||
// update tag
|
||||
auto entity_data = action_data.get_entity_property(name);
|
||||
for (auto &tag : entity_data.tags) {
|
||||
code += code_line(code::find_type, tag);
|
||||
code += code_line(code::set_type, name, tag);
|
||||
}
|
||||
|
||||
// mark relationship as created
|
||||
cypher_data.relationship_created(name);
|
||||
}
|
||||
}
|
||||
|
||||
return code;
|
||||
};
|
||||
@@ -1,34 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "includes.hpp"
|
||||
|
||||
auto delete_query_action =
|
||||
[](CypherStateData &cypher_data,
|
||||
const QueryActionData &action_data) -> std::string {
|
||||
|
||||
std::string code = "";
|
||||
|
||||
// TODO: don't delete the whole graph
|
||||
|
||||
for (auto const &kv : action_data.actions)
|
||||
{
|
||||
auto entity = kv.first;
|
||||
|
||||
if (kv.second == ClauseAction::DeleteNode && action_data.is_detach)
|
||||
{
|
||||
code += code_line(delete_whole_graph);
|
||||
}
|
||||
|
||||
if (kv.second == ClauseAction::DeleteNode && !action_data.is_detach)
|
||||
{
|
||||
code += code_line(delete_all_detached_nodes);
|
||||
}
|
||||
|
||||
if (kv.second == ClauseAction::DeleteRelationship)
|
||||
{
|
||||
code += code_line("// DELETE Relationship({})", entity);
|
||||
}
|
||||
}
|
||||
|
||||
return code;
|
||||
};
|
||||
@@ -1,44 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <iostream>
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include "query/backend/cpp_old/cypher_state.hpp"
|
||||
#include "query/backend/cpp_old/namer.hpp"
|
||||
#include "query/backend/cpp_old/query_action_data.hpp"
|
||||
#include "query/backend/cpp_old/code.hpp"
|
||||
#include "query/util.hpp"
|
||||
#include "query/exception/cpp_code_generator.hpp"
|
||||
#include "query/language/cypher/errors.hpp"
|
||||
|
||||
using ParameterIndexKey::Type::InternalId;
|
||||
using Direction = RelationshipData::Direction;
|
||||
|
||||
namespace
|
||||
{
|
||||
|
||||
auto update_properties(const CypherStateData &cypher_state,
|
||||
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));
|
||||
auto tmp_name = name::unique();
|
||||
// TODO: ERROR! why
|
||||
code += code_line((cypher_state.type(name) == EntityType::Node
|
||||
? code::vertex_property_key
|
||||
: code::edge_property_key),
|
||||
tmp_name, property, index);
|
||||
code += code_line(code::set_property, name, tmp_name, index);
|
||||
}
|
||||
|
||||
return code;
|
||||
}
|
||||
}
|
||||
@@ -1,84 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "includes.hpp"
|
||||
|
||||
namespace
|
||||
{
|
||||
|
||||
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) {
|
||||
|
||||
auto name = kv.first;
|
||||
|
||||
// TODO: duplicated code -> BIG PROBLEM
|
||||
// find node
|
||||
if (kv.second == ClauseAction::MatchNode) {
|
||||
if (already_matched(cypher_data, name, EntityType::Node))
|
||||
continue;
|
||||
cypher_data.node_matched(name);
|
||||
auto place = action_data.csm.min(name);
|
||||
if (place == entity_search::search_internal_id) {
|
||||
auto index = fetch_internal_index(action_data, name);
|
||||
code += code_line(code::match_vertex_by_id, name, index);
|
||||
cypher_data.source(name, EntitySource::InternalId);
|
||||
}
|
||||
if (place == entity_search::search_main_storage) {
|
||||
cypher_data.source(name, EntitySource::MainStorage);
|
||||
}
|
||||
if (place == entity_search::search_label_index) {
|
||||
if (action_data.entity_data.at(name).tags.size() > 1) {
|
||||
throw CypherSemanticError("Multiple label match (currently NOT supported)");
|
||||
}
|
||||
cypher_data.source(name, EntitySource::LabelIndex);
|
||||
cypher_data.tags(name, action_data.entity_data.at(name).tags);
|
||||
}
|
||||
}
|
||||
|
||||
// find relationship
|
||||
if (kv.second == ClauseAction::MatchRelationship) {
|
||||
if (already_matched(cypher_data, name, EntityType::Relationship))
|
||||
continue;
|
||||
cypher_data.relationship_matched(name);
|
||||
auto place = action_data.csm.min(name);
|
||||
if (place == entity_search::search_internal_id) {
|
||||
auto index = fetch_internal_index(action_data, name);
|
||||
code += code_line(code::match_edge_by_id, name, index);
|
||||
cypher_data.source(name, EntitySource::InternalId);
|
||||
}
|
||||
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 CypherSemanticError("Multiple type match (currently NOT supported)");
|
||||
}
|
||||
cypher_data.source(name, EntitySource::TypeIndex);
|
||||
cypher_data.tags(name, action_data.entity_data.at(name).tags);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return code;
|
||||
};
|
||||
@@ -1,132 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "includes.hpp"
|
||||
|
||||
// TODO: total fuck up; replace this with IR + Backend
|
||||
|
||||
auto return_query_action =
|
||||
[](CypherStateData &cypher_data,
|
||||
const QueryActionData &action_data) -> std::string {
|
||||
|
||||
std::string code = "";
|
||||
|
||||
const auto &elements = action_data.return_elements;
|
||||
code += code_line("// number of elements {}", elements.size());
|
||||
|
||||
for (const auto &element : elements)
|
||||
{
|
||||
auto &entity = element.entity;
|
||||
|
||||
if (!cypher_data.exist(entity))
|
||||
throw CypherSemanticError(
|
||||
fmt::format("{} couldn't be found (RETURN clause).", entity));
|
||||
|
||||
if (element.is_entity_only())
|
||||
{
|
||||
// if the node has just recently been created on can be found
|
||||
// with the internal id then it can be sent to the client
|
||||
if (cypher_data.status(entity) == EntityStatus::Created ||
|
||||
(cypher_data.source(entity) == EntitySource::InternalId &&
|
||||
cypher_data.status(entity) == EntityStatus::Matched))
|
||||
{
|
||||
code += code_line(code::write_entity, entity);
|
||||
}
|
||||
// the client has to receive all elements from the main storage
|
||||
if (cypher_data.source(entity) == EntitySource::MainStorage)
|
||||
{
|
||||
if (cypher_data.type(entity) == EntityType::Node)
|
||||
code += code_line(code::write_all_vertices, entity);
|
||||
else if (cypher_data.type(entity) == EntityType::Relationship)
|
||||
code += code_line(code::write_all_edges, entity);
|
||||
}
|
||||
// the client will receive entities from label index
|
||||
if (cypher_data.status(entity) != EntityStatus::Created &&
|
||||
cypher_data.source(entity) == EntitySource::LabelIndex)
|
||||
{
|
||||
if (cypher_data.tags(entity).size() == 0)
|
||||
throw CppCodeGeneratorException("node has no labels");
|
||||
|
||||
// node and no other property
|
||||
if (cypher_data.type(entity) == EntityType::Node)
|
||||
{
|
||||
auto label = cypher_data.tags(entity).at(0);
|
||||
if (cypher_data.has_properties(entity))
|
||||
{
|
||||
code += code_line(
|
||||
code::
|
||||
find_and_write_vertices_by_label_and_properties,
|
||||
cypher_data.print_indices(entity), label, entity);
|
||||
}
|
||||
else
|
||||
{
|
||||
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 CppCodeGeneratorException("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())
|
||||
{
|
||||
code += code_line("// TODO: implement projection");
|
||||
// auto &property = element.property;
|
||||
// code += code_line(code::print_property, entity, property);
|
||||
}
|
||||
}
|
||||
|
||||
// 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);
|
||||
}
|
||||
|
||||
if (cypher_data.source(name) == EntitySource::LabelIndex)
|
||||
{
|
||||
auto tags = cypher_data.tags(name);
|
||||
if (tags.size() == 1)
|
||||
{
|
||||
auto label = tags.at(0);
|
||||
code += code_line(code::count_vertices_for_one_label, name,
|
||||
label);
|
||||
}
|
||||
// TODO: do for more, isn't easy because of
|
||||
// multiple iterators, but we have iterator infrastructure
|
||||
// to do that
|
||||
}
|
||||
}
|
||||
if (kv.second == ClauseAction::ReturnLabels)
|
||||
{
|
||||
if (cypher_data.source(name) == EntitySource::LabelIndex)
|
||||
{
|
||||
auto tags = cypher_data.tags(name);
|
||||
if (tags.size() == 1)
|
||||
{
|
||||
auto label = tags.at(0);
|
||||
code += code_line(code::return_labels,
|
||||
cypher_data.print_indices(name), label,
|
||||
name);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return code;
|
||||
};
|
||||
@@ -1,72 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "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.source(name) == EntitySource::InternalId &&
|
||||
cypher_data.type(name) == EntityType::Node)
|
||||
{
|
||||
code += update_properties(cypher_data, action_data, name);
|
||||
}
|
||||
|
||||
if (kv.second == ClauseAction::UpdateNode &&
|
||||
cypher_data.status(name) == EntityStatus::Matched &&
|
||||
cypher_data.source(name) == EntitySource::LabelIndex &&
|
||||
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));
|
||||
auto tmp_name = name::unique();
|
||||
auto label = cypher_data.tags(name).at(0);
|
||||
// TODO: move this code inside the loop (in generated code)
|
||||
code += code_line(code::set_vertex_element,
|
||||
cypher_data.print_indices(name), label,
|
||||
property, index);
|
||||
}
|
||||
}
|
||||
|
||||
if (kv.second == ClauseAction::UpdateRelationship &&
|
||||
cypher_data.status(name) == EntityStatus::Matched &&
|
||||
cypher_data.type(name) == EntityType::Relationship)
|
||||
{
|
||||
code += update_properties(cypher_data, action_data, name);
|
||||
}
|
||||
}
|
||||
|
||||
for (auto const &set_entity_labels : action_data.label_set_elements)
|
||||
{
|
||||
auto &entity = set_entity_labels.entity;
|
||||
|
||||
if (cypher_data.status(entity) == EntityStatus::Matched &&
|
||||
cypher_data.source(entity) == EntitySource::LabelIndex)
|
||||
{
|
||||
auto label = cypher_data.tags(entity).at(0);
|
||||
if (cypher_data.has_properties(entity))
|
||||
{
|
||||
code += code_line(code::set_labels_start,
|
||||
cypher_data.print_indices(entity), label);
|
||||
auto labels = set_entity_labels.labels;
|
||||
for (auto const &set_label : labels)
|
||||
{
|
||||
code += code_line(code::set_label, set_label);
|
||||
}
|
||||
code += code_line(code::set_labels_end);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return code;
|
||||
};
|
||||
@@ -1,8 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "includes.hpp"
|
||||
|
||||
auto transaction_begin_action = [](CypherStateData &,
|
||||
const QueryActionData &) -> std::string {
|
||||
return code_line(code::transaction_begin);
|
||||
};
|
||||
@@ -1,9 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "includes.hpp"
|
||||
|
||||
auto transaction_commit_action = [](CypherStateData &,
|
||||
const QueryActionData &) -> std::string {
|
||||
return code_line(code::transaction_commit) +
|
||||
code_line(code::return_true);
|
||||
};
|
||||
@@ -1,18 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "storage/model/properties/flags.hpp"
|
||||
|
||||
// This namespace names stuff
|
||||
namespace
|
||||
{
|
||||
namespace name
|
||||
{
|
||||
|
||||
std::string variable_property_key(const std::string &property_name, Type type)
|
||||
{
|
||||
return "prop_" + property_name + "_" + type.to_str();
|
||||
}
|
||||
|
||||
std::string unique() { return "unique_" + std::to_string(std::rand()); }
|
||||
}
|
||||
}
|
||||
@@ -1,16 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
// any entity (node or relationship) inside cypher query has an action
|
||||
// that is associated with that entity
|
||||
enum class QueryAction : uint32_t
|
||||
{
|
||||
TransactionBegin,
|
||||
Create,
|
||||
Match,
|
||||
Set,
|
||||
Return,
|
||||
Delete,
|
||||
TransactionCommit
|
||||
};
|
||||
@@ -1,187 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <iostream>
|
||||
#include <map>
|
||||
#include <vector>
|
||||
|
||||
#include "query/backend/cpp_old/clause_action.hpp"
|
||||
#include "query/backend/cpp_old/entity_search.hpp"
|
||||
#include "query/exception/cpp_code_generator.hpp"
|
||||
#include "storage/model/properties/all.hpp"
|
||||
#include "utils/assert.hpp"
|
||||
#include "utils/underlying_cast.hpp"
|
||||
|
||||
// used for storing data related to an entity (node or relationship)
|
||||
// data can be:
|
||||
// * tags: labels or type
|
||||
// * props: property name, property value
|
||||
struct EntityData
|
||||
{
|
||||
std::vector<std::string> tags;
|
||||
std::vector<std::string> properties;
|
||||
|
||||
void add_tag(const std::string &tag) { tags.push_back(tag); }
|
||||
void add_property(const std::string &property)
|
||||
{
|
||||
properties.push_back(property);
|
||||
}
|
||||
};
|
||||
|
||||
// used for storing indices of parameters (parameters are stripped before
|
||||
// compiling process into the array), so somehow the compiler has to know
|
||||
// how to find appropriate parameter during the compile process
|
||||
//
|
||||
// parameter index key can be related to:
|
||||
// * internal_id and entity_name, e.g.:
|
||||
// ID(n)=35445 -> ID(n)=2 -> index[PropertyIndexKey(Type::InternalId, n)] =
|
||||
// 2
|
||||
// * entity_name and entity_property, e.g.:
|
||||
// n.name = "test" -> n.name = 3 -> index[PropertyIndexKey(entity_name,
|
||||
// entity_property)] = 3
|
||||
struct ParameterIndexKey
|
||||
{
|
||||
enum class Type : uint8_t
|
||||
{
|
||||
InternalId,
|
||||
Projection
|
||||
};
|
||||
|
||||
ParameterIndexKey(Type type, const std::string &entity_name)
|
||||
: type(type), entity_name(entity_name)
|
||||
{
|
||||
}
|
||||
|
||||
ParameterIndexKey(const std::string &entity_name,
|
||||
const std::string &entity_property)
|
||||
: type(Type::Projection), entity_name(entity_name),
|
||||
entity_property(entity_property)
|
||||
{
|
||||
}
|
||||
|
||||
const Type type;
|
||||
const std::string entity_name;
|
||||
const std::string entity_property;
|
||||
|
||||
bool operator<(const ParameterIndexKey &rhs) const
|
||||
{
|
||||
runtime_assert(type == rhs.type,
|
||||
"ParameterIndexKey types should be the same");
|
||||
|
||||
if (type == Type::InternalId) return entity_name < rhs.entity_name;
|
||||
|
||||
if (entity_name == rhs.entity_name)
|
||||
return entity_property < rhs.entity_property;
|
||||
|
||||
return entity_name < rhs.entity_name;
|
||||
}
|
||||
};
|
||||
|
||||
struct RelationshipData
|
||||
{
|
||||
enum class Direction
|
||||
{
|
||||
Left,
|
||||
Right
|
||||
};
|
||||
|
||||
using nodes_t = std::pair<std::string, std::string>;
|
||||
|
||||
RelationshipData(nodes_t nodes, Direction direction)
|
||||
: nodes(nodes), direction(direction)
|
||||
{
|
||||
}
|
||||
|
||||
std::pair<std::string, std::string> nodes;
|
||||
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 LabelSetElement
|
||||
{
|
||||
std::string entity;
|
||||
std::vector<std::string> labels;
|
||||
|
||||
LabelSetElement() = default;
|
||||
LabelSetElement(const LabelSetElement&) = default;
|
||||
LabelSetElement(LabelSetElement&&) = default;
|
||||
|
||||
void clear()
|
||||
{
|
||||
entity.clear();
|
||||
labels.clear();
|
||||
}
|
||||
};
|
||||
|
||||
struct QueryActionData
|
||||
{
|
||||
std::map<ParameterIndexKey, uint64_t> parameter_index;
|
||||
std::map<std::string, ClauseAction> actions;
|
||||
std::map<std::string, EntityData> entity_data;
|
||||
std::map<std::string, RelationshipData> relationship_data;
|
||||
std::vector<ReturnElement> return_elements;
|
||||
std::vector<LabelSetElement> label_set_elements;
|
||||
bool is_detach;
|
||||
CypherStateMachine csm;
|
||||
|
||||
QueryActionData() = default;
|
||||
QueryActionData(QueryActionData &&other) = default;
|
||||
|
||||
void create_entity(const std::string &entity)
|
||||
{
|
||||
if (entity_data.find(entity) == entity_data.end())
|
||||
entity_data.emplace(entity, EntityData());
|
||||
}
|
||||
|
||||
void add_entity_tag(const std::string &entity, const std::string &tag)
|
||||
{
|
||||
create_entity(entity);
|
||||
entity_data.at(entity).add_tag(tag);
|
||||
}
|
||||
|
||||
void add_entitiy_property(const std::string &entity,
|
||||
const std::string &property)
|
||||
{
|
||||
create_entity(entity);
|
||||
entity_data.at(entity).add_property(property);
|
||||
}
|
||||
|
||||
// TODO: refactor name
|
||||
auto get_entity_property(const std::string &entity) const
|
||||
{
|
||||
if (entity_data.find(entity) == entity_data.end())
|
||||
throw CppCodeGeneratorException("Entity " + entity + " doesn't exist");
|
||||
|
||||
return entity_data.at(entity);
|
||||
}
|
||||
|
||||
auto get_tags(const std::string& entity) const
|
||||
{
|
||||
if (entity_data.find(entity) == entity_data.end())
|
||||
throw CppCodeGeneratorException("Entity " + entity + "doesn't exist");
|
||||
|
||||
return entity_data.at(entity).tags;
|
||||
}
|
||||
|
||||
void print() const
|
||||
{
|
||||
for (auto const &action : actions) {
|
||||
std::cout << action.first << " " << underlying_cast(action.second)
|
||||
<< std::endl;
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -1,19 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include "utils/assert.hpp"
|
||||
|
||||
template <class T>
|
||||
class Vector : public std::vector<T>
|
||||
{
|
||||
public:
|
||||
using pair = std::pair<T, T>;
|
||||
|
||||
pair last_two()
|
||||
{
|
||||
runtime_assert(this->size() > 1, "Array size shoud be bigger than 1");
|
||||
|
||||
return std::make_pair(*(this->end() - 1), *(this->end() - 2));
|
||||
}
|
||||
};
|
||||
@@ -3,11 +3,13 @@
|
||||
#include <experimental/filesystem>
|
||||
namespace fs = std::experimental::filesystem;
|
||||
|
||||
#include "utils/exceptions/not_yet_implemented.hpp"
|
||||
#include "config/config.hpp"
|
||||
|
||||
#include "database/graph_db.hpp"
|
||||
#include "logging/loggable.hpp"
|
||||
#include "query/exception/query_engine.hpp"
|
||||
#include "query/plan_compiler.hpp"
|
||||
#include "query/plan_generator.hpp"
|
||||
#include "query/plan_interface.hpp"
|
||||
#include "query/preprocessor.hpp"
|
||||
#include "utils/dynamic_lib.hpp"
|
||||
@@ -15,8 +17,6 @@ namespace fs = std::experimental::filesystem;
|
||||
|
||||
// TODO: replace with openCypher and Antlr
|
||||
#include "query/frontend/cypher.hpp"
|
||||
// TODO: depricated
|
||||
#include "query/backend/cpp_old/cypher.hpp"
|
||||
|
||||
/**
|
||||
* Responsible for query execution.
|
||||
@@ -34,7 +34,6 @@ class QueryEngine : public Loggable
|
||||
{
|
||||
private:
|
||||
using QueryPlanLib = DynamicLib<QueryPlanTrait<Stream>>;
|
||||
using HashT = QueryPreprocessor::HashT;
|
||||
|
||||
public:
|
||||
QueryEngine() : Loggable("QueryEngine") {}
|
||||
@@ -149,7 +148,7 @@ private:
|
||||
*
|
||||
* @return runnable query plan
|
||||
*/
|
||||
auto LoadCypher(const StrippedQuery<HashT> &stripped)
|
||||
auto LoadCypher(const StrippedQuery &stripped)
|
||||
{
|
||||
auto plans_accessor = query_plans.access();
|
||||
|
||||
@@ -169,8 +168,9 @@ private:
|
||||
// generate query plan
|
||||
auto generated_path =
|
||||
fs::path(CONFIG(config::COMPILE_PATH) + std::to_string(stripped.hash) + ".cpp");
|
||||
plan_generator.generate_plan(stripped.query, stripped.hash,
|
||||
generated_path);
|
||||
// TODO implement CPP generator
|
||||
// plan_generator.generate_plan(stripped.query, stripped.hash, generated_path);
|
||||
throw NotYetImplemented();
|
||||
return LoadCpp(generated_path, stripped.hash);
|
||||
}
|
||||
|
||||
@@ -183,7 +183,7 @@ private:
|
||||
*
|
||||
* @return runnable query plan
|
||||
*/
|
||||
auto LoadCpp(const fs::path &path_cpp, const QueryPreprocessor::HashT hash)
|
||||
auto LoadCpp(const fs::path &path_cpp, const HashType hash)
|
||||
{
|
||||
auto plans_accessor = query_plans.access();
|
||||
|
||||
@@ -216,8 +216,7 @@ private:
|
||||
}
|
||||
|
||||
QueryPreprocessor preprocessor;
|
||||
PlanGenerator<cypher::Frontend, CypherBackend<Stream>> plan_generator;
|
||||
PlanCompiler plan_compiler;
|
||||
ConcurrentMap<QueryPreprocessor::HashT, std::unique_ptr<QueryPlanLib>>
|
||||
ConcurrentMap<HashType, std::unique_ptr<QueryPlanLib>>
|
||||
query_plans;
|
||||
};
|
||||
|
||||
@@ -1,29 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "config/config.hpp"
|
||||
#include "query/language/cypher/ast/ast.hpp"
|
||||
#include "query/language/cypher/compiler.hpp"
|
||||
#include "logging/loggable.hpp"
|
||||
#include "template_engine/engine.hpp"
|
||||
#include "utils/string/file.hpp"
|
||||
#include "utils/type_discovery.hpp"
|
||||
|
||||
template <typename Frontend, typename Backend>
|
||||
class PlanGenerator : public Loggable
|
||||
{
|
||||
public:
|
||||
PlanGenerator() : Loggable("PlanGenerator") {}
|
||||
|
||||
void generate_plan(const std::string &query, const uint64_t stripped_hash,
|
||||
const std::string &path)
|
||||
{
|
||||
// TODO: multithread environment TEST
|
||||
// multiple connections for the same query at the beginning
|
||||
auto ir = frontend.generate_ir(query);
|
||||
backend.generate_code(ir, query, stripped_hash, path);
|
||||
}
|
||||
|
||||
private:
|
||||
Frontend frontend;
|
||||
Backend backend;
|
||||
};
|
||||
@@ -25,7 +25,7 @@ public:
|
||||
*
|
||||
* @return bool status after execution (success OR fail)
|
||||
*/
|
||||
virtual bool run(GraphDbAccessor &db_accessor, const PlanArgsT &args, Stream &stream) = 0;
|
||||
virtual bool run(GraphDbAccessor &db_accessor, const TypedValueStore<> &args, Stream &stream) = 0;
|
||||
|
||||
/**
|
||||
* Virtual destructor in base class.
|
||||
|
||||
@@ -28,8 +28,6 @@ private:
|
||||
using QueryStripperT = QueryStripper<int, int, int, int>;
|
||||
|
||||
public:
|
||||
using HashT = QueryStripperT::HashT;
|
||||
|
||||
QueryPreprocessor() : Loggable("QueryPreprocessor"),
|
||||
stripper(make_query_stripper(TK_LONG, TK_FLOAT, TK_STR, TK_BOOL))
|
||||
{
|
||||
|
||||
@@ -1,28 +1,19 @@
|
||||
#pragma once
|
||||
|
||||
#include "storage/model/properties/property.hpp"
|
||||
|
||||
/*
|
||||
* Query Plan Arguments Type
|
||||
*/
|
||||
using PlanArgsT = std::vector<Property>;
|
||||
#include "storage/typed_value_store.hpp"
|
||||
#include "utils/hashing/fnv.hpp"
|
||||
|
||||
/*
|
||||
* StrippedQuery contains:
|
||||
* * stripped query
|
||||
* * plan arguments stripped from query
|
||||
* * hash of stripped query
|
||||
*
|
||||
* @tparam THash a type of query hash
|
||||
*/
|
||||
template <typename THash>
|
||||
struct StrippedQuery
|
||||
{
|
||||
StrippedQuery(const std::string &&query, PlanArgsT &&arguments, THash hash)
|
||||
struct StrippedQuery {
|
||||
|
||||
StrippedQuery(const std::string &&query, TypedValueStore<> &&arguments, HashType hash)
|
||||
: query(std::forward<const std::string>(query)),
|
||||
arguments(std::forward<PlanArgsT>(arguments)), hash(hash)
|
||||
{
|
||||
}
|
||||
arguments(std::forward<TypedValueStore<>>(arguments)), hash(hash) {}
|
||||
|
||||
/**
|
||||
* Copy constructor is deleted because we don't want to make unecessary
|
||||
@@ -46,10 +37,10 @@ struct StrippedQuery
|
||||
/**
|
||||
* Stripped arguments
|
||||
*/
|
||||
const PlanArgsT arguments;
|
||||
const TypedValueStore<> arguments;
|
||||
|
||||
/**
|
||||
* Hash based on stripped query.
|
||||
*/
|
||||
const THash hash;
|
||||
const HashType hash;
|
||||
};
|
||||
|
||||
@@ -100,9 +100,9 @@ public:
|
||||
}
|
||||
|
||||
// TODO: hash function should be a template parameter
|
||||
auto hash = fnv(stripped_query);
|
||||
return StrippedQuery<HashT>(std::move(stripped_query),
|
||||
std::move(stripped_arguments), hash);
|
||||
HashType hash = fnv(stripped_query);
|
||||
return StrippedQuery(std::move(stripped_query),
|
||||
std::move(stripped_arguments), hash);
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
@@ -10,8 +10,14 @@ class Vertex;
|
||||
|
||||
class Edge : public mvcc::Record<Edge> {
|
||||
public:
|
||||
mvcc::VersionList<Vertex>* from_;
|
||||
mvcc::VersionList<Vertex>* to_;
|
||||
|
||||
Edge(mvcc::VersionList<Vertex>& from,
|
||||
mvcc::VersionList<Vertex>& to,
|
||||
GraphDb::EdgeType edge_type)
|
||||
: from_(from), to_(to), edge_type_(edge_type) {}
|
||||
|
||||
mvcc::VersionList<Vertex>& from_;
|
||||
mvcc::VersionList<Vertex>& to_;
|
||||
GraphDb::EdgeType edge_type_;
|
||||
TypedValueStore<GraphDb::Property> properties_;
|
||||
};
|
||||
|
||||
@@ -20,5 +20,5 @@ public:
|
||||
|
||||
VertexAccessor to() const;
|
||||
|
||||
void remove();
|
||||
// void remove();
|
||||
};
|
||||
|
||||
@@ -1,117 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <cassert>
|
||||
#include <iostream>
|
||||
#include <vector>
|
||||
|
||||
#include "utils/underlying_cast.hpp"
|
||||
#include "utils/total_ordering.hpp"
|
||||
#include "utils/exceptions/basic_exception.hpp"
|
||||
|
||||
/**
|
||||
* Encapsulation of a value and it's type encapsulated in a class that has no
|
||||
* compiled-time info about that type.
|
||||
*
|
||||
* Values can be of a number of predefined types that are enumerated in
|
||||
* TypedValue::Type. Each such type corresponds to exactly one C++ type.
|
||||
*/
|
||||
class TypedValue : public TotalOrdering<TypedValue, TypedValue, TypedValue> {
|
||||
|
||||
private:
|
||||
/** Private default constructor, makes Null */
|
||||
TypedValue() : type_(Type::Null) {}
|
||||
|
||||
public:
|
||||
|
||||
/** A value type. Each type corresponds to exactly one C++ type */
|
||||
enum class Type : unsigned {
|
||||
Null,
|
||||
String,
|
||||
Bool,
|
||||
Int,
|
||||
Float
|
||||
};
|
||||
|
||||
// single static reference to Null, used whenever Null should be returned
|
||||
static const TypedValue Null;
|
||||
|
||||
// constructors for primitive types
|
||||
TypedValue(bool value) : type_(Type::Bool) { bool_v = value; }
|
||||
TypedValue(int value) : type_(Type::Int) { int_v = value; }
|
||||
TypedValue(float value) : type_(Type::Float) { float_v = value; }
|
||||
|
||||
/// constructors for non-primitive types (shared pointers)
|
||||
TypedValue(const std::string &value) : type_(Type::String) {
|
||||
new (&string_v) std::shared_ptr<std::string>(new std::string(value));
|
||||
}
|
||||
TypedValue(const char* value) : type_(Type::String) {
|
||||
new (&string_v) std::shared_ptr<std::string>(new std::string(value));
|
||||
}
|
||||
|
||||
// assignment ops
|
||||
TypedValue& operator=(TypedValue& other);
|
||||
TypedValue& operator=(TypedValue&& other);
|
||||
|
||||
TypedValue(const TypedValue& other);
|
||||
~TypedValue();
|
||||
|
||||
/**
|
||||
* Returns the value of the property as given type T.
|
||||
* The behavior of this function is undefined if
|
||||
* T does not correspond to this property's type_.
|
||||
*
|
||||
* @tparam T Type to interpret the value as.
|
||||
* @return The value as type T.
|
||||
*/
|
||||
template <typename T> T Value() const;
|
||||
|
||||
friend std::ostream& operator<<(std::ostream& stream, const TypedValue& prop);
|
||||
|
||||
/**
|
||||
* The Type of property.
|
||||
*/
|
||||
const Type type_;
|
||||
|
||||
private:
|
||||
// storage for the value of the property
|
||||
union {
|
||||
bool bool_v;
|
||||
int int_v;
|
||||
float float_v;
|
||||
std::shared_ptr<std::string> string_v;
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* An exception raised by the TypedValue system. Typically when
|
||||
* trying to perform operations (such as addition) on TypedValues
|
||||
* of incompatible Types.
|
||||
*/
|
||||
class TypedValueException : public BasicException {
|
||||
|
||||
public:
|
||||
using ::BasicException::BasicException;
|
||||
};
|
||||
|
||||
// comparison operators
|
||||
// they return TypedValue because Null can be returned
|
||||
TypedValue operator==(const TypedValue& a, const TypedValue& b);
|
||||
TypedValue operator<(const TypedValue& a, const TypedValue& b);
|
||||
TypedValue operator!(const TypedValue& a);
|
||||
|
||||
// arithmetic operators
|
||||
TypedValue operator-(const TypedValue& a);
|
||||
TypedValue operator+(const TypedValue& a, const TypedValue& b);
|
||||
TypedValue operator-(const TypedValue& a, const TypedValue& b);
|
||||
TypedValue operator/(const TypedValue& a, const TypedValue& b);
|
||||
TypedValue operator*(const TypedValue& a, const TypedValue& b);
|
||||
TypedValue operator%(const TypedValue& a, const TypedValue& b);
|
||||
|
||||
// binary bool operators
|
||||
TypedValue operator&&(const TypedValue& a, const TypedValue& b);
|
||||
TypedValue operator||(const TypedValue& a, const TypedValue& b);
|
||||
|
||||
// stream output
|
||||
std::ostream &operator<<(std::ostream &os, const TypedValue::Type type);
|
||||
@@ -1,82 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <vector>
|
||||
#include <map>
|
||||
|
||||
#include "typed_value.hpp"
|
||||
|
||||
/**
|
||||
* A collection of properties accessed in a map-like way
|
||||
* using a key of type Properties::TKey.
|
||||
*
|
||||
* The underlying implementation is not necessarily std::map.
|
||||
*/
|
||||
class TypedValueStore {
|
||||
public:
|
||||
using sptr = std::shared_ptr<TypedValueStore>;
|
||||
|
||||
/** The type of key used to get and set properties */
|
||||
using TKey = uint32_t;
|
||||
|
||||
/**
|
||||
* Returns a TypedValue (by reference) at the given key.
|
||||
* If the key does not exist, the Null property is returned.
|
||||
*
|
||||
* This is NOT thread-safe, the reference might not be valid
|
||||
* when used in a multithreaded scenario.
|
||||
*
|
||||
* @param key The key for which a TypedValue is sought.
|
||||
* @return See above.
|
||||
*/
|
||||
const TypedValue &at(const TKey &key) const;
|
||||
|
||||
/**
|
||||
* Sets the value for the given key. A new TypedValue instance
|
||||
* is created for the given value (which is of raw type).
|
||||
*
|
||||
* @tparam TValue Type of value. It must be one of the
|
||||
* predefined possible TypedValue values (bool, string, int...)
|
||||
* @param key The key for which the property is set. The previous
|
||||
* value at the same key (if there was one) is replaced.
|
||||
* @param value The value to set.
|
||||
*/
|
||||
template<typename TValue>
|
||||
void set(const TKey &key, const TValue &value);
|
||||
|
||||
/**
|
||||
* Set overriding for character constants. Forces conversion
|
||||
* to std::string, otherwise templating might cast the pointer
|
||||
* to something else (bool) and mess things up.
|
||||
*
|
||||
* @param key The key for which the property is set. The previous
|
||||
* value at the same key (if there was one) is replaced.
|
||||
* @param value The value to set.
|
||||
*/
|
||||
void set(const TKey &key, const char *value);
|
||||
|
||||
/**
|
||||
* Removes the TypedValue for the given key.
|
||||
*
|
||||
* @param key The key for which to remove the property.
|
||||
* @return The number of removed properties (0 or 1).
|
||||
*/
|
||||
size_t erase(const TKey &key);
|
||||
|
||||
/**
|
||||
* @return The number of Properties in this collection.
|
||||
*/
|
||||
size_t size() const;
|
||||
|
||||
|
||||
/**
|
||||
* Accepts two functions.
|
||||
*
|
||||
* @param handler Called for each TypedValue in this collection.
|
||||
* @param finish Called once in the end.
|
||||
*/
|
||||
void Accept(std::function<void(const TKey key, const TypedValue& prop)> handler,
|
||||
std::function<void()> finish = {}) const;
|
||||
|
||||
private:
|
||||
std::vector<std::pair<TKey, TypedValue>> props_;
|
||||
};
|
||||
@@ -1,57 +0,0 @@
|
||||
//
|
||||
// Copyright 2017 Memgraph
|
||||
// Created by Florijan Stamenkovic on 01.02.17.
|
||||
//
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <ostream>
|
||||
#include "storage/model/typed_value_store.hpp"
|
||||
|
||||
|
||||
/**
|
||||
* Writes all of the values from the given store in JSON format
|
||||
* to the given output stream.
|
||||
*
|
||||
* @param store The store that should be serialized to JSON.
|
||||
* @param ostream The stream to write to.
|
||||
*/
|
||||
void TypedValuesToJson(const TypedValueStore& store,
|
||||
std::ostream& ostream=std::cout) {
|
||||
|
||||
bool first = true;
|
||||
|
||||
auto write_key = [&ostream, &first](const TypedValueStore::TKey &key) -> std::ostream& {
|
||||
if (first) {
|
||||
ostream << '{';
|
||||
first = false;
|
||||
} else
|
||||
ostream << ',';
|
||||
|
||||
return ostream << '"' << key << "\":";
|
||||
};
|
||||
|
||||
auto handler = [&ostream, &write_key](const TypedValueStore::TKey& key,
|
||||
const TypedValue& value) {
|
||||
switch (value.type_) {
|
||||
case TypedValue::Type::Null:
|
||||
break;
|
||||
case TypedValue::Type::Bool:
|
||||
write_key(key) << (value.Value<bool>() ? "true" : "false");
|
||||
break;
|
||||
case TypedValue::Type::String:
|
||||
write_key(key) << '"' << value.Value<std::string>() << '"';
|
||||
break;
|
||||
case TypedValue::Type::Int:
|
||||
write_key(key) << value.Value<int>();
|
||||
break;
|
||||
case TypedValue::Type::Float:
|
||||
write_key(key) << value.Value<float>();
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
||||
auto finish = [&ostream]() { ostream << '}' << std::endl; };
|
||||
|
||||
store.Accept(handler, finish);
|
||||
}
|
||||
@@ -11,18 +11,36 @@ class RecordAccessor {
|
||||
|
||||
public:
|
||||
|
||||
RecordAccessor(mvcc::VersionList<TRecord> *vlist, GraphDbAccessor *db_accessor)
|
||||
: vlist_(vlist), db_accessor_(db_accessor) {
|
||||
record_ = vlist->find(db_accessor->transaction_);
|
||||
/**
|
||||
* The GraphDbAccessor is friend to this accessor so it can
|
||||
* operate on it's data (mvcc version-list and the record itself).
|
||||
* This is legitemate because GraphDbAccessor creates RecordAccessors
|
||||
* and is semantically their parent/owner. It is necessary because
|
||||
* the GraphDbAccessor handles insertions and deletions, and these
|
||||
* operations modify data intensively.
|
||||
*/
|
||||
friend GraphDbAccessor;
|
||||
|
||||
RecordAccessor(mvcc::VersionList<TRecord>& vlist,
|
||||
GraphDbAccessor& db_accessor)
|
||||
: vlist_(vlist), record_(vlist_.find(db_accessor.transaction_)), db_accessor_(db_accessor) {
|
||||
assert(record_ != nullptr);
|
||||
}
|
||||
|
||||
RecordAccessor(mvcc::VersionList<TRecord>& vlist,
|
||||
TRecord& record,
|
||||
GraphDbAccessor& db_accessor)
|
||||
: vlist_(vlist), record_(&record), db_accessor_(db_accessor) {
|
||||
assert(record_ != nullptr);
|
||||
}
|
||||
|
||||
template<typename TValue>
|
||||
void PropsSet(GraphDb::Property key, TValue value) {
|
||||
update()->props_.set(key, value);
|
||||
update().props_.set(key, value);
|
||||
}
|
||||
|
||||
size_t PropsErase(GraphDb::Property key) {
|
||||
return update()->props_.erase(key);
|
||||
return update().props_.erase(key);
|
||||
}
|
||||
|
||||
const TypedValueStore<GraphDb::Property> &Properties() const {
|
||||
@@ -31,7 +49,7 @@ public:
|
||||
|
||||
void PropertiesAccept(std::function<void(const GraphDb::Property key, const TypedValue &prop)> handler,
|
||||
std::function<void()> finish = {}) const {
|
||||
view()->props_.Accept(handler, finish);
|
||||
view().props_.Accept(handler, finish);
|
||||
}
|
||||
|
||||
// Assumes same transaction
|
||||
@@ -47,21 +65,20 @@ public:
|
||||
}
|
||||
|
||||
/**
|
||||
* Exposes the version list only to the GraphDb.
|
||||
* Returns a GraphDB accessor of this record accessor.
|
||||
*
|
||||
* @param pass_key Ignored.
|
||||
* @return The version list of this accessor.
|
||||
* @return See above.
|
||||
*/
|
||||
mvcc::VersionList<TRecord> *vlist(PassKey<GraphDbAccessor> pass_key) const {
|
||||
return vlist_;
|
||||
GraphDbAccessor& db_accessor() {
|
||||
return db_accessor_;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a GraphDB accessor of this record accessor.
|
||||
* Returns a const GraphDB accessor of this record accessor.
|
||||
*
|
||||
* @return
|
||||
* @return See above.
|
||||
*/
|
||||
const GraphDbAccessor &db_accessor() const {
|
||||
const GraphDbAccessor& db_accessor() const {
|
||||
return db_accessor_;
|
||||
}
|
||||
|
||||
@@ -72,14 +89,13 @@ protected:
|
||||
*
|
||||
* @return See above.
|
||||
*/
|
||||
TRecord *update() {
|
||||
TRecord& update() {
|
||||
// TODO consider renaming this to something more indicative
|
||||
// of the underlying MVCC functionality (like "new_version" or so)
|
||||
if (!record_->is_visible_write(db_accessor_.transaction_))
|
||||
record_ = vlist_.update(record_, db_accessor_.transaction_);
|
||||
|
||||
if (!record_->is_visible_write(db_accessor_->transaction_))
|
||||
record_ = vlist_->update(db_accessor_->transaction_);
|
||||
|
||||
return record_;
|
||||
return *record_;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -87,22 +103,26 @@ protected:
|
||||
*
|
||||
* @return See above.
|
||||
*/
|
||||
const TRecord *view() const {
|
||||
return record_;
|
||||
const TRecord& view() const {
|
||||
return *record_;
|
||||
}
|
||||
|
||||
// The record (edge or vertex) this accessor provides access to.
|
||||
mvcc::VersionList<TRecord> *vlist_;
|
||||
// Immutable, set in the constructor and never changed.
|
||||
mvcc::VersionList<TRecord>& vlist_;
|
||||
|
||||
// The database accessor for which this record accessor is created
|
||||
// Provides means of getting to the transaction and database functions.
|
||||
GraphDbAccessor *db_accessor_;
|
||||
// Immutable, set in the constructor and never changed.
|
||||
GraphDbAccessor& db_accessor_;
|
||||
|
||||
private:
|
||||
/* The version of the record currently used in this transaction. Defaults to the
|
||||
* latest viewable version (set in the constructor). After the first update done
|
||||
* through this accessor a new, editable version, is created for this transaction,
|
||||
* and set as the value of this variable.
|
||||
*
|
||||
* Stored as a pointer due to it's mutability (the update() function changes it).
|
||||
*/
|
||||
TRecord *record_;
|
||||
TRecord* record_;
|
||||
};
|
||||
|
||||
@@ -28,7 +28,13 @@ public:
|
||||
* @param key The key for which a TypedValue is sought.
|
||||
* @return See above.
|
||||
*/
|
||||
const TypedValue &at(const TKey &key) const;
|
||||
const TypedValue& at(const TKey &key) const {
|
||||
for (const auto& kv : props_)
|
||||
if (kv.first == key)
|
||||
return kv.second;
|
||||
|
||||
return TypedValue::Null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the value for the given key. A new TypedValue instance
|
||||
@@ -41,7 +47,17 @@ public:
|
||||
* @param value The value to set.
|
||||
*/
|
||||
template<typename TValue>
|
||||
void set(const TKey &key, const TValue &value);
|
||||
void set(const TKey &key, const TValue &value) {
|
||||
for (auto& kv: props_)
|
||||
if (kv.first == key) {
|
||||
kv.second = TypedValue(value);
|
||||
return;
|
||||
}
|
||||
|
||||
// there is no value for the given key, add new
|
||||
// TODO consider vector size increment optimization
|
||||
props_.push_back(std::move(std::make_pair(key, value)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Set overriding for character constants. Forces conversion
|
||||
@@ -52,7 +68,9 @@ public:
|
||||
* value at the same key (if there was one) is replaced.
|
||||
* @param value The value to set.
|
||||
*/
|
||||
void set(const TKey &key, const char *value);
|
||||
void set(const TKey &key, const char *value) {
|
||||
set(key, std::string(value));
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the TypedValue for the given key.
|
||||
@@ -60,13 +78,41 @@ public:
|
||||
* @param key The key for which to remove the property.
|
||||
* @return The number of removed properties (0 or 1).
|
||||
*/
|
||||
size_t erase(const TKey &key);
|
||||
size_t erase(const TKey &key) {
|
||||
|
||||
auto found = std::find_if(
|
||||
props_.begin(),
|
||||
props_.end(),
|
||||
[&key](std::pair<TKey, TypedValue> &kv){return kv.first == key;}
|
||||
);
|
||||
|
||||
if (found != props_.end()) {
|
||||
props_.erase(found);
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return The number of Properties in this collection.
|
||||
*/
|
||||
size_t size() const;
|
||||
size_t size() const {
|
||||
return props_.size();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a const iterator over key-value pairs.
|
||||
*
|
||||
* @return See above.
|
||||
*/
|
||||
const auto begin() const { return props_.begin(); }
|
||||
|
||||
/**
|
||||
* Returns an end iterator.
|
||||
*
|
||||
* @return See above.
|
||||
*/
|
||||
const auto end() const { return props_.end(); }
|
||||
|
||||
/**
|
||||
* Accepts two functions.
|
||||
@@ -74,8 +120,16 @@ public:
|
||||
* @param handler Called for each TypedValue in this collection.
|
||||
* @param finish Called once in the end.
|
||||
*/
|
||||
void Accept(std::function<void(const TKey key, const TypedValue& prop)> handler,
|
||||
std::function<void()> finish = {}) const;
|
||||
void Accept(std::function<void(const TKey, const TypedValue &)> handler,
|
||||
std::function<void()> finish = {}) const {
|
||||
if (handler)
|
||||
for (const auto& prop : props_)
|
||||
handler(prop.first, prop.second);
|
||||
|
||||
if (finish)
|
||||
finish();
|
||||
|
||||
}
|
||||
|
||||
private:
|
||||
std::vector<std::pair<TKey, TypedValue>> props_;
|
||||
|
||||
24
include/storage/util.hpp
Normal file
24
include/storage/util.hpp
Normal file
@@ -0,0 +1,24 @@
|
||||
|
||||
/**
|
||||
* Creates a vector of records accessors (Edge or Vertex).
|
||||
*
|
||||
* @tparam TAccessor The type of accessor to create a vector of.
|
||||
* @tparam TCollection An iterable of pointers to version list objects.
|
||||
*
|
||||
* @param records An iterable of version list pointers for which accessors
|
||||
* need to be created.
|
||||
* @param db_accessor A database accessor to create the record accessors with.
|
||||
*/
|
||||
template <typename TAccessor, typename TCollection>
|
||||
std::vector<TAccessor> make_accessors(
|
||||
const TCollection &records,
|
||||
GraphDbAccessor &db_accessor) {
|
||||
|
||||
std::vector<TAccessor> accessors;
|
||||
accessors.reserve(records.size());
|
||||
|
||||
for (auto record : records)
|
||||
accessors.emplace_back(*record, db_accessor);
|
||||
|
||||
return accessors;
|
||||
}
|
||||
@@ -1,5 +1,8 @@
|
||||
#pragma once
|
||||
|
||||
#include <set>
|
||||
#include <vector>
|
||||
|
||||
#include "database/graph_db.hpp"
|
||||
#include "mvcc/record.hpp"
|
||||
#include "mvcc/version_list.hpp"
|
||||
@@ -11,8 +14,8 @@ class Edge;
|
||||
class Vertex : public mvcc::Record<Vertex> {
|
||||
|
||||
public:
|
||||
std::vector<mvcc::VersionList<Edge> *> out_;
|
||||
std::vector<mvcc::VersionList<Edge> *> in_;
|
||||
std::vector<mvcc::VersionList<Edge>*> out_;
|
||||
std::vector<mvcc::VersionList<Edge>*> in_;
|
||||
std::set<GraphDb::Label> labels_;
|
||||
TypedValueStore<GraphDb::Property> properties_;
|
||||
};
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
#include <vector>
|
||||
#include <set>
|
||||
|
||||
#include "storage/record_accessor.hpp"
|
||||
@@ -25,27 +26,12 @@ public:
|
||||
|
||||
const std::set<GraphDb::Label>& labels() const;
|
||||
|
||||
// TODO add in/out functions that return (collection|iterator) over EdgeAccessor
|
||||
std::vector<EdgeAccessor> in();
|
||||
|
||||
std::vector<EdgeAccessor> out();
|
||||
|
||||
// returns if remove was possible due to connections
|
||||
bool remove();
|
||||
|
||||
void detach_remove();
|
||||
|
||||
/**
|
||||
* Adds the given Edge version list to this Vertex's incoming edges.
|
||||
*
|
||||
* @param edge_vlist The Edge to add.
|
||||
* @param pass_key Ensures only GraphDb has access to this method.
|
||||
*/
|
||||
void attach_in(mvcc::VersionList<Edge>* edge_vlist, PassKey<GraphDb> pass_key);
|
||||
|
||||
/**
|
||||
* Adds the given Edge version list to this Vertex's outgoing edges.
|
||||
*
|
||||
* @param edge_vlist The Edge to add.
|
||||
* @param pass_key Ensures only GraphDb has access to this method.
|
||||
*/
|
||||
void attach_out(mvcc::VersionList<Edge>* edge_vlist, PassKey<GraphDb> pass_key);
|
||||
|
||||
// bool remove();
|
||||
//
|
||||
// void detach_remove();
|
||||
};
|
||||
|
||||
@@ -22,6 +22,8 @@ uint64_t fnv(const T& data)
|
||||
return fnv1a64<T>(data);
|
||||
}
|
||||
|
||||
using HashType = uint64_t;
|
||||
|
||||
#elif
|
||||
|
||||
template <class T>
|
||||
@@ -30,6 +32,8 @@ uint32_t fnv(const T& data)
|
||||
return fnv1a32<T>(data);
|
||||
}
|
||||
|
||||
using HashType = uint32_t;
|
||||
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user