Major properties system and database accessor refactor: first stable state (compiles).
This commit is contained in:
parent
9e09186d30
commit
70a8b93b0b
CMakeLists.txt
include
communication/bolt/v1
database
dbms
mvcc
query
backend/cpp_old
clause_action.hppcode.hppcpp_generator.hppcypher.hppcypher_state.hppentity_search.hpp
engine.hppplan_generator.hppplan_interface.hpppreprocessor.hppstripped.hppstripper.hpphandlers
all.hppcreate.hppdelete.hppincludes.hppmatch.hppreturn.hppset.hpptransaction_begin.hpptransaction_commit.hpp
namer.hppquery_action.hppquery_action_data.hppstructures.hppstorage
edge.hppedge_accessor.hpp
model
record_accessor.hpptyped_value_store.hpputil.hppvertex.hppvertex_accessor.hpputils/hashing
src
communication/bolt/v1
database
dbms
storage
tests
@ -314,7 +314,6 @@ set(memgraph_src_files
|
||||
# ${src_dir}/snapshot/snapshot_encoder.cpp
|
||||
# ${src_dir}/snapshot/snapshot_decoder.cpp
|
||||
${src_dir}/storage/typed_value.cpp
|
||||
${src_dir}/storage/typed_value_store.cpp
|
||||
${src_dir}/storage/locking/record_lock.cpp
|
||||
# ${src_dir}/storage/garbage/garbage.cpp
|
||||
${src_dir}/storage/vertex_accessor.cpp
|
||||
@ -330,7 +329,6 @@ set(memgraph_src_files
|
||||
${src_dir}/logging/logs/async_log.cpp
|
||||
${src_dir}/logging/default.cpp
|
||||
${src_dir}/logging/log.cpp
|
||||
${src_dir}/io/network/tls.cpp
|
||||
${src_dir}/database/graph_db.cpp
|
||||
${src_dir}/database/graph_db_accessor.cpp
|
||||
)
|
||||
@ -388,7 +386,7 @@ if (MEMGRAPH)
|
||||
target_link_libraries(${MEMGRAPH_BUILD_NAME} cypher_lib)
|
||||
|
||||
if (UNIX)
|
||||
target_link_libraries(${MEMGRAPH_BUILD_NAME} crypto)
|
||||
# target_link_libraries(${MEMGRAPH_BUILD_NAME} crypto)
|
||||
# target_link_libraries(${MEMGRAPH_BUILD_NAME} ssl)
|
||||
target_link_libraries(${MEMGRAPH_BUILD_NAME} ${fmt_static_lib})
|
||||
target_link_libraries(${MEMGRAPH_BUILD_NAME} ${yaml_static_lib})
|
||||
|
@ -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
|
||||
|
||||
}
|
||||
|
@ -5,6 +5,9 @@
|
||||
#include "communication/bolt/v1/transport/socket_stream.hpp"
|
||||
#include "io/network/socket.hpp"
|
||||
|
||||
#include "database/graph_db.hpp"
|
||||
#include "storage/typed_value_store.hpp"
|
||||
|
||||
template<class Stream>
|
||||
void bolt::BoltSerializer<Stream>::write(const VertexAccessor &vertex) {
|
||||
|
||||
@ -25,11 +28,11 @@ void bolt::BoltSerializer<Stream>::write(const VertexAccessor &vertex) {
|
||||
encoder.write_string(vertex.db_accessor().label_name(label));
|
||||
|
||||
// write the properties
|
||||
const TypedValueStore &props = vertex.Properties();
|
||||
const TypedValueStore<GraphDb::Property> &props = vertex.Properties();
|
||||
encoder.write_map_header(props.size());
|
||||
props.Accept([&vertex](const TypedValueStore::TKey &prop_name, const TypedValue &value) {
|
||||
write(vertex.db_accessor().property_name(prop_name));
|
||||
write(value);
|
||||
props.Accept([this, &vertex](const GraphDb::Property prop, const TypedValue &value) {
|
||||
this->encoder.write(vertex.db_accessor().property_name(prop));
|
||||
this->write(value);
|
||||
});
|
||||
}
|
||||
|
||||
@ -48,14 +51,14 @@ void bolt::BoltSerializer<Stream>::write(const EdgeAccessor &edge) {
|
||||
encoder.write_integer(0);
|
||||
|
||||
// write the type of the edge
|
||||
encoder.write_string(edge.edge_type());
|
||||
encoder.write(edge.db_accessor().edge_type_name(edge.edge_type()));
|
||||
|
||||
// write the property map
|
||||
const TypedValueStore& props = edge.Properties();
|
||||
const TypedValueStore<GraphDb::Property>& props = edge.Properties();
|
||||
encoder.write_map_header(props.size());
|
||||
props.Accept([&edge](const TypedValueStore::TKey &prop_name, const TypedValue &value) {
|
||||
write(edge.db_accessor().property_name(prop_name));
|
||||
write(value);
|
||||
props.Accept([this, &edge](GraphDb::Property prop, const TypedValue &value) {
|
||||
this->encoder.write(edge.db_accessor().property_name(prop));
|
||||
this->write(value);
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -43,5 +43,5 @@ void Session::close()
|
||||
bolt.close(this);
|
||||
}
|
||||
|
||||
GraphDb &Session::active_db() { return bolt.dbms.active(); }
|
||||
GraphDbAccessor Session::active_db() { return bolt.dbms.active(); }
|
||||
}
|
||||
|
@ -1,5 +1,6 @@
|
||||
#include "communication/bolt/v1/states/executor.hpp"
|
||||
#include "communication/bolt/v1/messaging/codes.hpp"
|
||||
#include "database/graph_db_accessor.hpp"
|
||||
|
||||
#ifdef BARRIER
|
||||
#include "barrier/barrier.cpp"
|
||||
@ -83,11 +84,11 @@ State *Executor::run(Session &session, Query &query)
|
||||
{
|
||||
logger.trace("[Run] '{}'", query.statement);
|
||||
|
||||
auto &db = session.active_db();
|
||||
logger.debug("[ActiveDB] '{}'", db.name());
|
||||
auto db_accessor = session.active_db();
|
||||
logger.debug("[ActiveDB] '{}'", db_accessor.name());
|
||||
|
||||
auto is_successfully_executed =
|
||||
query_engine.Run(query.statement, db, session.output_stream);
|
||||
query_engine.Run(query.statement, db_accessor, session.output_stream);
|
||||
|
||||
if (!is_successfully_executed)
|
||||
{
|
||||
|
@ -2,20 +2,9 @@
|
||||
#include <storage/edge.hpp>
|
||||
#include "database/creation_exception.hpp"
|
||||
#include "database/graph_db.hpp"
|
||||
#include "snapshot/snapshoter.hpp"
|
||||
//#include "snapshot/snapshoter.hpp"
|
||||
|
||||
#include "storage/vertex.hpp"
|
||||
#include "storage/vertex_accessor.hpp"
|
||||
#include "storage/edge.hpp"
|
||||
#include "storage/edge_accessor.hpp"
|
||||
|
||||
GraphDb::GraphDb(bool import_snapshot) : GraphDb("default", import_snapshot) {}
|
||||
|
||||
GraphDb::GraphDb(const std::string &name, bool import_snapshot)
|
||||
: GraphDb(name.c_str(), import_snapshot) {
|
||||
GraphDb::GraphDb(const std::string &name, bool import_snapshot) : name_(name) {
|
||||
// if (import_snapshot)
|
||||
// snap_engine.import();
|
||||
}
|
||||
|
||||
//GraphDb::GraphDb(const char *name, bool import_snapshot) : name_(name) {
|
||||
// if (import_snapshot) snap_engine.import();
|
||||
//}
|
||||
|
||||
|
@ -1,52 +1,106 @@
|
||||
#include <database/creation_exception.hpp>
|
||||
#include "database/graph_db_accessor.hpp"
|
||||
|
||||
#include "storage/vertex.hpp"
|
||||
#include "storage/vertex_accessor.hpp"
|
||||
#include "storage/edge.hpp"
|
||||
#include "storage/edge_accessor.hpp"
|
||||
|
||||
|
||||
GraphDbAccessor::GraphDbAccessor(GraphDb& db) : db_(db), transaction_(std::move(db.tx_engine.begin())) {}
|
||||
|
||||
VertexAccessor GraphDbAccessor::insert_vertex() {
|
||||
auto vertex_vlist = new mvcc::VersionList<Vertex>();
|
||||
vertex_vlist->insert(transaction_);
|
||||
const std::string& GraphDbAccessor::name() const {
|
||||
return db_.name_;
|
||||
}
|
||||
|
||||
// TODO make this configurable
|
||||
VertexAccessor GraphDbAccessor::insert_vertex() {
|
||||
|
||||
// create a vertex
|
||||
auto vertex_vlist = new mvcc::VersionList<Vertex>();
|
||||
Vertex *vertex = vertex_vlist->insert(transaction_);
|
||||
|
||||
// insert the newly created record into the main storage
|
||||
// TODO make the number of tries configurable configurable
|
||||
for (int i = 0; i < 5; ++i) {
|
||||
bool success = db_.vertices_.access().insert(vertex_vlist).second;
|
||||
if (success)
|
||||
return VertexAccessor(vertex_vlist, transaction_);
|
||||
// TODO sleep for some amount of time
|
||||
return VertexAccessor(*vertex_vlist, *vertex, *this);
|
||||
// TODO sleep for some configurable amount of time
|
||||
}
|
||||
|
||||
throw CreationException("Unable to create a Vertex after 5 attempts");
|
||||
}
|
||||
|
||||
bool GraphDbAccessor::remove_vertex(VertexAccessor &vertex_accessor) {
|
||||
// TODO consider if this works well with MVCC
|
||||
if (vertex_accessor.out_degree() > 0 || vertex_accessor.in_degree() > 0)
|
||||
return false;
|
||||
|
||||
vertex_accessor.vlist_.remove(&vertex_accessor.update(), transaction_);
|
||||
|
||||
// TODO remove the vertex from the main storage once it gets garbage collected
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void GraphDbAccessor::detach_remove_vertex(VertexAccessor &vertex_accessor) {
|
||||
// removing edges via accessors is both safe
|
||||
// and it should remove all the pointers in the relevant
|
||||
// vertices (including this one)
|
||||
for (auto edge_accessor : vertex_accessor.in())
|
||||
remove_edge(edge_accessor);
|
||||
|
||||
for (auto edge_accessor : vertex_accessor.out())
|
||||
remove_edge(edge_accessor);
|
||||
|
||||
// mvcc removal of the vertex
|
||||
vertex_accessor.vlist_.remove(&vertex_accessor.update(), transaction_);
|
||||
|
||||
// TODO remove the vertex from the main storage once it gets garbage collected
|
||||
}
|
||||
|
||||
EdgeAccessor GraphDbAccessor::insert_edge(
|
||||
VertexAccessor& from,
|
||||
VertexAccessor& to,
|
||||
GraphDb::EdgeType type) {
|
||||
GraphDb::EdgeType edge_type) {
|
||||
|
||||
// create an edge
|
||||
auto edge_vlist = new mvcc::VersionList<Edge>();
|
||||
Edge* edge = edge_vlist->insert(transaction_);
|
||||
Edge* edge = edge_vlist->insert(transaction_, from.vlist_, to.vlist_, edge_type);
|
||||
|
||||
// set the given values of the new edge
|
||||
edge->edge_type_ = type;
|
||||
// connect the edge to vertices
|
||||
edge->from_ = from.vlist(pass_key);
|
||||
edge->to_ = to.vlist(pass_key);
|
||||
// TODO connect the vertices to edge
|
||||
from.add_to_out(edge_vlist, pass_key);
|
||||
to.add_to_in(edge_vlist, pass_key);
|
||||
// set the vertex connections to this edge
|
||||
from.update().out_.emplace_back(edge_vlist);
|
||||
to.update().in_.emplace_back(edge_vlist);
|
||||
|
||||
// TODO make this configurable
|
||||
// insert the newly created record into the main storage
|
||||
// TODO make the number of tries configurable
|
||||
for (int i = 0; i < 5; ++i) {
|
||||
bool success = db_.edges_.access().insert(edge_vlist).second;
|
||||
if (success)
|
||||
return EdgeAccessor(edge_vlist, transaction_);
|
||||
return EdgeAccessor(*edge_vlist, *edge, *this);
|
||||
// TODO sleep for some amount of time
|
||||
}
|
||||
|
||||
throw CreationException("Unable to create an Edge after 5 attempts");
|
||||
}
|
||||
|
||||
void GraphDbAccessor::remove_edge(EdgeAccessor& edge_accessor) {
|
||||
// remove this edge's reference from the "from" vertex
|
||||
auto& vertex_from = edge_accessor.from().update();
|
||||
std::remove(vertex_from.out_.begin(),
|
||||
vertex_from.out_.end(),
|
||||
&edge_accessor.vlist_);
|
||||
|
||||
// remove this edge's reference from the "to" vertex
|
||||
auto& vertex_to = edge_accessor.to().update();
|
||||
std::remove(vertex_to.in_.begin(),
|
||||
vertex_to.in_.end(),
|
||||
&edge_accessor.vlist_);
|
||||
|
||||
// remove this record from the database via MVCC
|
||||
edge_accessor.vlist_.remove(&edge_accessor.update(), transaction_);
|
||||
}
|
||||
|
||||
GraphDb::Label GraphDbAccessor::label(const std::string& label_name) {
|
||||
return &(*db_.labels_.access().insert(label_name).first);
|
||||
}
|
||||
|
@ -1,21 +1,10 @@
|
||||
#include "dbms/dbms.hpp"
|
||||
|
||||
// returns active database
|
||||
GraphDb &Dbms::active()
|
||||
{
|
||||
GraphDb *active = active_db.load(std::memory_order_acquire);
|
||||
if (UNLIKELY(active == nullptr)) {
|
||||
// There is no active database.
|
||||
return create_default();
|
||||
} else {
|
||||
return *active;
|
||||
}
|
||||
GraphDbAccessor Dbms::active() {
|
||||
return GraphDbAccessor(*active_db.load(std::memory_order_acquire));
|
||||
}
|
||||
|
||||
// set active database
|
||||
// if active database doesn't exist create one
|
||||
GraphDb &Dbms::active(const std::string &name)
|
||||
{
|
||||
GraphDbAccessor Dbms::active(const std::string &name) {
|
||||
auto acc = dbs.access();
|
||||
// create db if it doesn't exist
|
||||
auto it = acc.find(name);
|
||||
@ -28,5 +17,5 @@ GraphDb &Dbms::active(const std::string &name)
|
||||
// set and return active db
|
||||
auto &db = it->second;
|
||||
active_db.store(&db, std::memory_order_release);
|
||||
return db;
|
||||
return GraphDbAccessor(db);
|
||||
}
|
||||
|
@ -2,35 +2,35 @@
|
||||
#include "storage/vertex_accessor.hpp"
|
||||
|
||||
void EdgeAccessor::set_edge_type(GraphDb::EdgeType edge_type) {
|
||||
this->update()->edge_type_ = edge_type;
|
||||
this->update().edge_type_ = edge_type;
|
||||
}
|
||||
|
||||
GraphDb::EdgeType EdgeAccessor::edge_type() const {
|
||||
return this->view()->edge_type_;
|
||||
return this->view().edge_type_;
|
||||
}
|
||||
|
||||
VertexAccessor EdgeAccessor::from() const {
|
||||
return VertexAccessor(this->view()->from_, this->db_accessor_->transaction_);
|
||||
return VertexAccessor(view().from_, db_accessor_);
|
||||
}
|
||||
|
||||
VertexAccessor EdgeAccessor::to() const {
|
||||
return VertexAccessor(this->view()->to_, this->db_accessor_->transaction_);
|
||||
return VertexAccessor(view().to_, db_accessor_);
|
||||
}
|
||||
|
||||
void EdgeAccessor::remove() {
|
||||
// remove this edge's reference from the "from" vertex
|
||||
auto vertex_from = from().update();
|
||||
std::remove(vertex_from->out_.begin(),
|
||||
vertex_from->out_.end(),
|
||||
vlist_);
|
||||
|
||||
// remove this edge's reference from the "to" vertex
|
||||
auto vertex_to = to().update();
|
||||
std::remove(vertex_to->in_.begin(),
|
||||
vertex_to->in_.end(),
|
||||
vlist_);
|
||||
|
||||
// remove this record from the database via MVCC
|
||||
vlist_->remove(update(), db_accessor_->transaction_);
|
||||
}
|
||||
//void EdgeAccessor::remove() {
|
||||
// // remove this edge's reference from the "from" vertex
|
||||
// auto& vertex_from = from().update();
|
||||
// std::remove(vertex_from.out_.begin(),
|
||||
// vertex_from.out_.end(),
|
||||
// vlist_);
|
||||
//
|
||||
// // remove this edge's reference from the "to" vertex
|
||||
// auto& vertex_to = to().update();
|
||||
// std::remove(vertex_to.in_.begin(),
|
||||
// vertex_to.in_.end(),
|
||||
// vlist_);
|
||||
//
|
||||
// // remove this record from the database via MVCC
|
||||
// vlist_.remove(&update(), db_accessor_.transaction_);
|
||||
//}
|
||||
|
||||
|
@ -1,363 +0,0 @@
|
||||
#include <memory>
|
||||
#include <iostream>
|
||||
#include <cmath>
|
||||
#include <fmt/format.h>
|
||||
|
||||
#include "storage/model/typed_value.hpp"
|
||||
#include "utils/assert.hpp"
|
||||
|
||||
// Value extraction template instantiations
|
||||
template<>
|
||||
bool TypedValue::Value<bool>() const {
|
||||
runtime_assert(type_ == TypedValue::Type::Bool, "Incompatible template param and type");
|
||||
return bool_v;
|
||||
}
|
||||
|
||||
template<>
|
||||
std::string TypedValue::Value<std::string>() const {
|
||||
runtime_assert(type_ == TypedValue::Type::String, "Incompatible template param and type");
|
||||
return *string_v;
|
||||
}
|
||||
|
||||
template<>
|
||||
int TypedValue::Value<int>() const {
|
||||
runtime_assert(type_ == TypedValue::Type::Int, "Incompatible template param and type");
|
||||
return int_v;
|
||||
}
|
||||
|
||||
template<>
|
||||
float TypedValue::Value<float>() const {
|
||||
runtime_assert(type_ == TypedValue::Type::Float, "Incompatible template param and type");
|
||||
return float_v;
|
||||
}
|
||||
|
||||
TypedValue::TypedValue(const TypedValue &other) : type_(other.type_) {
|
||||
switch (other.type_) {
|
||||
case TypedValue::Type::Null:
|
||||
return;
|
||||
|
||||
case TypedValue::Type::Bool:
|
||||
this->bool_v = other.bool_v;
|
||||
return;
|
||||
|
||||
case TypedValue::Type::String:
|
||||
new(&string_v) std::shared_ptr<std::string>(other.string_v);
|
||||
return;
|
||||
|
||||
case Type::Int:
|
||||
this->int_v = other.int_v;
|
||||
return;
|
||||
|
||||
case Type::Float:
|
||||
this->float_v = other.float_v;
|
||||
return;
|
||||
}
|
||||
|
||||
permanent_fail("Unsupported TypedValue::Type");
|
||||
}
|
||||
|
||||
std::ostream &operator<<(std::ostream &os, const TypedValue::Type type) {
|
||||
switch (type) {
|
||||
case TypedValue::Type::Null:
|
||||
return os << "null";
|
||||
case TypedValue::Type::Bool:
|
||||
return os << "bool";
|
||||
case TypedValue::Type::String:
|
||||
return os << "string";
|
||||
case TypedValue::Type::Int:
|
||||
return os << "int";
|
||||
case TypedValue::Type::Float:
|
||||
return os << "float";
|
||||
}
|
||||
permanent_fail("Unsupported TypedValue::Type");
|
||||
}
|
||||
|
||||
std::ostream &operator<<(std::ostream &os, const TypedValue &property) {
|
||||
switch (property.type_) {
|
||||
case TypedValue::Type::Null:
|
||||
return os << "Null";
|
||||
case TypedValue::Type::Bool:
|
||||
return os << (property.Value<bool>() ? "true" : "false");
|
||||
case TypedValue::Type::String:
|
||||
return os << property.Value<std::string>();
|
||||
case TypedValue::Type::Int:
|
||||
return os << property.Value<int>();
|
||||
case TypedValue::Type::Float:
|
||||
return os << property.Value<float>();
|
||||
}
|
||||
permanent_fail("Unsupported TypedValue::Type");
|
||||
}
|
||||
|
||||
TypedValue &TypedValue::operator=(TypedValue &&other) {
|
||||
|
||||
// set the type of this
|
||||
const_cast<Type&>(type_) = other.type_;
|
||||
|
||||
if (this != &other) {
|
||||
switch (other.type_) {
|
||||
case TypedValue::Type::Null:
|
||||
case TypedValue::Type::Bool:
|
||||
this->bool_v = other.bool_v;
|
||||
return *this;
|
||||
case TypedValue::Type::String:
|
||||
this->string_v = std::move(other.string_v);
|
||||
return *this;
|
||||
case TypedValue::Type::Int:
|
||||
this->int_v = other.int_v;
|
||||
return *this;
|
||||
case TypedValue::Type::Float:
|
||||
this->float_v = other.float_v;
|
||||
return *this;
|
||||
}
|
||||
}
|
||||
permanent_fail("Unsupported TypedValue::Type");
|
||||
}
|
||||
|
||||
const TypedValue TypedValue::Null = TypedValue();
|
||||
|
||||
TypedValue::~TypedValue() {
|
||||
|
||||
switch (type_) {
|
||||
// destructor for primitive types does nothing
|
||||
case Type::Null:
|
||||
case Type::Bool:
|
||||
case Type::Int:
|
||||
case Type::Float:
|
||||
return;
|
||||
|
||||
// destructor for shared pointer must release
|
||||
case Type::String:
|
||||
string_v.~shared_ptr<std::string>();
|
||||
return;
|
||||
}
|
||||
permanent_fail("Unsupported TypedValue::Type");
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the float value of a property.
|
||||
* The property MUST be either Float or Int.
|
||||
*
|
||||
* @param prop
|
||||
* @return
|
||||
*/
|
||||
float ToFloat(const TypedValue& prop) {
|
||||
switch (prop.type_) {
|
||||
case TypedValue::Type::Int:
|
||||
return (float)prop.Value<int>();
|
||||
case TypedValue::Type::Float:
|
||||
return prop.Value<float>();
|
||||
|
||||
default:
|
||||
permanent_fail("Unsupported TypedValue::Type");
|
||||
}
|
||||
}
|
||||
|
||||
TypedValue operator<(const TypedValue& a, const TypedValue& b) {
|
||||
if (a.type_ == TypedValue::Type::Bool || b.type_ == TypedValue::Type::Bool)
|
||||
throw TypedValueException("Invalid 'less' operand types({} + {})", a.type_, b.type_);
|
||||
|
||||
if (a.type_ == TypedValue::Type::Null || b.type_ == TypedValue::Type::Null)
|
||||
return TypedValue::Null;
|
||||
|
||||
if (a.type_ == TypedValue::Type::String || b.type_ == TypedValue::Type::String) {
|
||||
if (a.type_ != b.type_)
|
||||
throw TypedValueException("Invalid equality operand types({} + {})", a.type_, b.type_);
|
||||
else
|
||||
return a.Value<std::string>() < b.Value<std::string>();
|
||||
}
|
||||
|
||||
// at this point we only have int and float
|
||||
if (a.type_ == TypedValue::Type::Float || b.type_ == TypedValue::Type::Float)
|
||||
return ToFloat(a) < ToFloat(b);
|
||||
else
|
||||
return a.Value<int>() < b.Value<int>();
|
||||
}
|
||||
|
||||
TypedValue operator==(const TypedValue& a, const TypedValue& b) {
|
||||
|
||||
if (a.type_ == TypedValue::Type::Null || b.type_ == TypedValue::Type::Null)
|
||||
return TypedValue::Null;
|
||||
|
||||
if (a.type_ == TypedValue::Type::String || b.type_ == TypedValue::Type::String) {
|
||||
if (a.type_ != b.type_)
|
||||
throw TypedValueException("Invalid equality operand types({} + {})", a.type_, b.type_);
|
||||
else
|
||||
return a.Value<std::string>() == b.Value<std::string>();
|
||||
}
|
||||
|
||||
if (a.type_ == TypedValue::Type::Bool || b.type_ == TypedValue::Type::Bool) {
|
||||
if (a.type_ != b.type_)
|
||||
throw TypedValueException("Invalid equality operand types({} + {})", a.type_, b.type_);
|
||||
else
|
||||
return a.Value<bool>() == b.Value<bool>();
|
||||
}
|
||||
// at this point we only have int and float
|
||||
if (a.type_ == TypedValue::Type::Float || b.type_ == TypedValue::Type::Float){
|
||||
return ToFloat(a) == ToFloat(b);
|
||||
} else
|
||||
return a.Value<int>() == b.Value<int>();
|
||||
|
||||
}
|
||||
|
||||
TypedValue operator!(const TypedValue& a) {
|
||||
switch (a.type_) {
|
||||
case TypedValue::Type::Null:
|
||||
return TypedValue::Null;
|
||||
case TypedValue::Type::Bool:
|
||||
return TypedValue(!a.Value<bool>());
|
||||
|
||||
default:
|
||||
throw TypedValueException("Invalid logical not operand type (!{})", a.type_);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Turns a numeric or string property into a string.
|
||||
*
|
||||
* @param prop Property.
|
||||
* @return A string.
|
||||
*/
|
||||
std::string PropToString(const TypedValue& prop) {
|
||||
switch (prop.type_) {
|
||||
case TypedValue::Type::String:
|
||||
return prop.Value<std::string>();
|
||||
case TypedValue::Type::Int:
|
||||
return std::to_string(prop.Value<int>());
|
||||
case TypedValue::Type::Float:
|
||||
return fmt::format("{}", prop.Value<float>());
|
||||
|
||||
// unsupported situations
|
||||
default:
|
||||
permanent_fail("Unsupported TypedValue::Type");
|
||||
}
|
||||
}
|
||||
|
||||
TypedValue operator-(const TypedValue &a) {
|
||||
switch (a.type_) {
|
||||
case TypedValue::Type::Null:
|
||||
return TypedValue::Null;
|
||||
case TypedValue::Type::Int:
|
||||
return -a.Value<int>();
|
||||
case TypedValue::Type::Float:
|
||||
return -a.Value<float>();
|
||||
|
||||
default:
|
||||
throw TypedValueException("Invalid unary minus operand type (-{})", a.type_);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Raises a PropertyException if the given properties do not support arithmetic
|
||||
* operations. If they do, nothing happens.
|
||||
*
|
||||
* @param a First prop.
|
||||
* @param b Second prop.
|
||||
* @param string_ok If or not for the given operation it's valid to work with
|
||||
* String values (typically it's OK only for sum).
|
||||
* @param op_name Name of the operation, used only for exception description,
|
||||
* if raised.
|
||||
*/
|
||||
inline void EnsureArithmeticallyOk(const TypedValue& a, const TypedValue& b,
|
||||
bool string_ok, const std::string& op_name) {
|
||||
if (a.type_ == TypedValue::Type::Bool || b.type_ == TypedValue::Type::Bool)
|
||||
throw TypedValueException("Invalid {} operand types {}, {}", op_name, a.type_, b.type_);
|
||||
|
||||
if (string_ok)
|
||||
return;
|
||||
|
||||
if (a.type_ == TypedValue::Type::String || b.type_ == TypedValue::Type::String)
|
||||
throw TypedValueException("Invalid subtraction operands types {}, {}", a.type_, b.type_);
|
||||
}
|
||||
|
||||
TypedValue operator+(const TypedValue& a, const TypedValue& b){
|
||||
EnsureArithmeticallyOk(a, b, true, "addition");
|
||||
if (a.type_ == TypedValue::Type::Null || b.type_ == TypedValue::Type::Null)
|
||||
return TypedValue::Null;
|
||||
|
||||
// no more Bool nor Null, summing works on anything from here onward
|
||||
|
||||
if (a.type_ == TypedValue::Type::String || b.type_ == TypedValue::Type::String)
|
||||
return PropToString(a) + PropToString(b);
|
||||
|
||||
// at this point we only have int and float
|
||||
if (a.type_ == TypedValue::Type::Float || b.type_ == TypedValue::Type::Float){
|
||||
return ToFloat(a) + ToFloat(b);
|
||||
} else
|
||||
return a.Value<int>() + b.Value<int>();
|
||||
}
|
||||
|
||||
TypedValue operator-(const TypedValue& a, const TypedValue& b){
|
||||
EnsureArithmeticallyOk(a, b, false, "subtraction");
|
||||
|
||||
if (a.type_ == TypedValue::Type::Null || b.type_ == TypedValue::Type::Null)
|
||||
return TypedValue::Null;
|
||||
|
||||
// at this point we only have int and float
|
||||
if (a.type_ == TypedValue::Type::Float || b.type_ == TypedValue::Type::Float){
|
||||
return ToFloat(a) - ToFloat(b);
|
||||
} else
|
||||
return a.Value<int>() - b.Value<int>();
|
||||
}
|
||||
|
||||
TypedValue operator/(const TypedValue& a, const TypedValue& b){
|
||||
EnsureArithmeticallyOk(a, b, false, "division");
|
||||
|
||||
if (a.type_ == TypedValue::Type::Null || b.type_ == TypedValue::Type::Null)
|
||||
return TypedValue::Null;
|
||||
|
||||
// at this point we only have int and float
|
||||
if (a.type_ == TypedValue::Type::Float || b.type_ == TypedValue::Type::Float){
|
||||
return ToFloat(a) / ToFloat(b);
|
||||
} else
|
||||
return a.Value<int>() / b.Value<int>();
|
||||
}
|
||||
|
||||
TypedValue operator*(const TypedValue& a, const TypedValue& b){
|
||||
EnsureArithmeticallyOk(a, b, false, "multiplication");
|
||||
|
||||
if (a.type_ == TypedValue::Type::Null || b.type_ == TypedValue::Type::Null)
|
||||
return TypedValue::Null;
|
||||
|
||||
// at this point we only have int and float
|
||||
if (a.type_ == TypedValue::Type::Float || b.type_ == TypedValue::Type::Float){
|
||||
return ToFloat(a) * ToFloat(b);
|
||||
} else
|
||||
return a.Value<int>() * b.Value<int>();
|
||||
}
|
||||
|
||||
TypedValue operator%(const TypedValue& a, const TypedValue& b){
|
||||
EnsureArithmeticallyOk(a, b, false, "modulo");
|
||||
|
||||
if (a.type_ == TypedValue::Type::Null || b.type_ == TypedValue::Type::Null)
|
||||
return TypedValue::Null;
|
||||
|
||||
// at this point we only have int and float
|
||||
if (a.type_ == TypedValue::Type::Float || b.type_ == TypedValue::Type::Float){
|
||||
return (float)fmod(ToFloat(a), ToFloat(b));
|
||||
} else
|
||||
return a.Value<int>() % b.Value<int>();
|
||||
}
|
||||
|
||||
inline bool IsLogicallyOk(const TypedValue& a) {
|
||||
return a.type_ == TypedValue::Type::Bool || a.type_ == TypedValue::Type::Null;
|
||||
}
|
||||
|
||||
TypedValue operator&&(const TypedValue& a, const TypedValue& b) {
|
||||
if(IsLogicallyOk(a) && IsLogicallyOk(b)){
|
||||
if (a.type_ == TypedValue::Type::Null || b.type_ == TypedValue::Type::Null)
|
||||
return TypedValue::Null;
|
||||
else
|
||||
return a.Value<bool>() && b.Value<bool>();
|
||||
} else
|
||||
throw TypedValueException("Invalid logical and operand types({} && {})", a.type_, b.type_);
|
||||
}
|
||||
|
||||
TypedValue operator||(const TypedValue& a, const TypedValue& b) {
|
||||
if(IsLogicallyOk(a) && IsLogicallyOk(b)){
|
||||
if (a.type_ == TypedValue::Type::Null || b.type_ == TypedValue::Type::Null)
|
||||
return TypedValue::Null;
|
||||
else
|
||||
return a.Value<bool>() || b.Value<bool>();
|
||||
} else
|
||||
throw TypedValueException("Invalid logical and operand types({} && {})", a.type_, b.type_);
|
||||
}
|
@ -1,68 +0,0 @@
|
||||
#include <algorithm>
|
||||
|
||||
#include "storage/model/typed_value_store.hpp"
|
||||
|
||||
const TypedValue& TypedValueStore::at(const TKey &key) const {
|
||||
for (const auto& kv : props_)
|
||||
if (kv.first == key)
|
||||
return kv.second;
|
||||
|
||||
return TypedValue::Null;
|
||||
}
|
||||
|
||||
template<typename TValue>
|
||||
void TypedValueStore::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)));
|
||||
}
|
||||
|
||||
// instantiations of the TypedValueStore::set function
|
||||
// instances must be made for all of the supported C++ types
|
||||
template void TypedValueStore::set<std::string>(const TKey &key, const std::string &value);
|
||||
template void TypedValueStore::set<bool>(const TKey &key, const bool &value);
|
||||
template void TypedValueStore::set<int>(const TKey &key, const int &value);
|
||||
template void TypedValueStore::set<float>(const TKey &key, const float &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 TypedValueStore::set(const TKey &key, const char *value) {
|
||||
set(key, std::string(value));
|
||||
}
|
||||
|
||||
size_t TypedValueStore::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;
|
||||
}
|
||||
|
||||
size_t TypedValueStore::size() const { return props_.size(); }
|
||||
|
||||
void TypedValueStore::Accept(std::function<void(const TypedValueStore::TKey, const TypedValue &)> handler,
|
||||
std::function<void()> finish) const {
|
||||
if (handler)
|
||||
for (const auto& prop : props_)
|
||||
handler(prop.first, prop.second);
|
||||
|
||||
if (finish)
|
||||
finish();
|
||||
|
||||
}
|
||||
|
||||
|
@ -72,18 +72,18 @@ std::ostream &operator<<(std::ostream &os, const TypedValue::Type type) {
|
||||
permanent_fail("Unsupported TypedValue::Type");
|
||||
}
|
||||
|
||||
std::ostream &operator<<(std::ostream &os, const TypedValue &property) {
|
||||
switch (property.type_) {
|
||||
std::ostream &operator<<(std::ostream &os, const TypedValue &value) {
|
||||
switch (value.type_) {
|
||||
case TypedValue::Type::Null:
|
||||
return os << "Null";
|
||||
case TypedValue::Type::Bool:
|
||||
return os << (property.Value<bool>() ? "true" : "false");
|
||||
return os << (value.Value<bool>() ? "true" : "false");
|
||||
case TypedValue::Type::String:
|
||||
return os << property.Value<std::string>();
|
||||
return os << value.Value<std::string>();
|
||||
case TypedValue::Type::Int:
|
||||
return os << property.Value<int>();
|
||||
return os << value.Value<int>();
|
||||
case TypedValue::Type::Float:
|
||||
return os << property.Value<float>();
|
||||
return os << value.Value<float>();
|
||||
}
|
||||
permanent_fail("Unsupported TypedValue::Type");
|
||||
}
|
||||
@ -134,18 +134,18 @@ TypedValue::~TypedValue() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the float value of a property.
|
||||
* The property MUST be either Float or Int.
|
||||
* Returns the float value of a value.
|
||||
* The value MUST be either Float or Int.
|
||||
*
|
||||
* @param prop
|
||||
* @param value
|
||||
* @return
|
||||
*/
|
||||
float ToFloat(const TypedValue& prop) {
|
||||
switch (prop.type_) {
|
||||
float ToFloat(const TypedValue& value) {
|
||||
switch (value.type_) {
|
||||
case TypedValue::Type::Int:
|
||||
return (float)prop.Value<int>();
|
||||
return (float)value.Value<int>();
|
||||
case TypedValue::Type::Float:
|
||||
return prop.Value<float>();
|
||||
return value.Value<float>();
|
||||
|
||||
default:
|
||||
permanent_fail("Unsupported TypedValue::Type");
|
||||
@ -212,19 +212,19 @@ TypedValue operator!(const TypedValue& a) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Turns a numeric or string property into a string.
|
||||
* Turns a numeric or string value into a string.
|
||||
*
|
||||
* @param prop Property.
|
||||
* @param value a value.
|
||||
* @return A string.
|
||||
*/
|
||||
std::string PropToString(const TypedValue& prop) {
|
||||
switch (prop.type_) {
|
||||
std::string ValueToString(const TypedValue &value) {
|
||||
switch (value.type_) {
|
||||
case TypedValue::Type::String:
|
||||
return prop.Value<std::string>();
|
||||
return value.Value<std::string>();
|
||||
case TypedValue::Type::Int:
|
||||
return std::to_string(prop.Value<int>());
|
||||
return std::to_string(value.Value<int>());
|
||||
case TypedValue::Type::Float:
|
||||
return fmt::format("{}", prop.Value<float>());
|
||||
return fmt::format("{}", value.Value<float>());
|
||||
|
||||
// unsupported situations
|
||||
default:
|
||||
@ -247,11 +247,11 @@ TypedValue operator-(const TypedValue &a) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Raises a PropertyException if the given properties do not support arithmetic
|
||||
* Raises a TypedValueException if the given values do not support arithmetic
|
||||
* operations. If they do, nothing happens.
|
||||
*
|
||||
* @param a First prop.
|
||||
* @param b Second prop.
|
||||
* @param a First value.
|
||||
* @param b Second value.
|
||||
* @param string_ok If or not for the given operation it's valid to work with
|
||||
* String values (typically it's OK only for sum).
|
||||
* @param op_name Name of the operation, used only for exception description,
|
||||
@ -277,7 +277,7 @@ TypedValue operator+(const TypedValue& a, const TypedValue& b){
|
||||
// no more Bool nor Null, summing works on anything from here onward
|
||||
|
||||
if (a.type_ == TypedValue::Type::String || b.type_ == TypedValue::Type::String)
|
||||
return PropToString(a) + PropToString(b);
|
||||
return ValueToString(a) + ValueToString(b);
|
||||
|
||||
// at this point we only have int and float
|
||||
if (a.type_ == TypedValue::Type::Float || b.type_ == TypedValue::Type::Float){
|
||||
|
@ -2,6 +2,7 @@
|
||||
|
||||
#include "storage/typed_value_store.hpp"
|
||||
|
||||
template <typename TKey>
|
||||
const TypedValue& TypedValueStore::at(const TKey &key) const {
|
||||
for (const auto& kv : props_)
|
||||
if (kv.first == key)
|
||||
@ -10,7 +11,7 @@ const TypedValue& TypedValueStore::at(const TKey &key) const {
|
||||
return TypedValue::Null;
|
||||
}
|
||||
|
||||
template<typename TValue>
|
||||
template<typename TKey, typename TValue>
|
||||
void TypedValueStore::set(const TKey &key, const TValue &value) {
|
||||
for (auto& kv: props_)
|
||||
if (kv.first == key) {
|
||||
|
@ -1,57 +1,65 @@
|
||||
#include "storage/edge_accessor.hpp"
|
||||
#include "storage/vertex_accessor.hpp"
|
||||
#include "storage/util.hpp"
|
||||
|
||||
size_t VertexAccessor::out_degree() const {
|
||||
return this->view()->out_.size();
|
||||
return this->view().out_.size();
|
||||
}
|
||||
|
||||
size_t VertexAccessor::in_degree() const {
|
||||
return this->view()->in_.size();
|
||||
return this->view().in_.size();
|
||||
}
|
||||
|
||||
bool VertexAccessor::add_label(GraphDb::Label label) {
|
||||
return this->update()->labels_.emplace(label).second;
|
||||
return this->update().labels_.emplace(label).second;
|
||||
}
|
||||
|
||||
size_t VertexAccessor::remove_label(GraphDb::Label label) {
|
||||
return this->update()->labels_.erase(label);
|
||||
return this->update().labels_.erase(label);
|
||||
}
|
||||
|
||||
bool VertexAccessor::has_label(GraphDb::Label label) const {
|
||||
auto &label_set = this->view()->labels_;
|
||||
auto &label_set = this->view().labels_;
|
||||
return label_set.find(label) != label_set.end();
|
||||
}
|
||||
|
||||
const std::set<GraphDb::Label>& VertexAccessor::labels() const {
|
||||
return this->view()->labels_;
|
||||
return this->view().labels_;
|
||||
}
|
||||
|
||||
bool VertexAccessor::remove() {
|
||||
// TODO consider if this works well with MVCC
|
||||
if (out_degree() > 0 || in_degree() > 0)
|
||||
return false;
|
||||
std::vector<EdgeAccessor> VertexAccessor::in() {
|
||||
const Vertex& record = view();
|
||||
std::vector<EdgeAccessor> in;
|
||||
in.reserve(record.in_.size());
|
||||
for (auto edge_vlist_ptr : record.in_)
|
||||
in.emplace_back(EdgeAccessor(*edge_vlist_ptr, db_accessor_));
|
||||
|
||||
vlist_->remove(view(), db_accessor_->transaction_);
|
||||
return true;
|
||||
return in;
|
||||
}
|
||||
|
||||
void VertexAccessor::detach_remove() {
|
||||
// removing edges via accessors is both safe
|
||||
// and it should remove all the pointers in the relevant
|
||||
// vertices (including this one)
|
||||
for (auto edge_vlist : view()->out_)
|
||||
EdgeAccessor(edge_vlist, db_accessor_->transaction_).remove();
|
||||
|
||||
for (auto edge_vlist : view()->in_)
|
||||
EdgeAccessor(edge_vlist, db_accessor_->transaction_).remove();
|
||||
|
||||
vlist_->remove(view(), db_accessor_->transaction_);
|
||||
std::vector<EdgeAccessor> VertexAccessor::out() {
|
||||
return make_accessors<EdgeAccessor>(view().in_, db_accessor_);
|
||||
}
|
||||
|
||||
void VertexAccessor::attach_in(mvcc::VersionList<Edge>* edge_vlist, PassKey<GraphDb>) {
|
||||
this->update()->in_.emplace_back(edge_vlist);
|
||||
}
|
||||
|
||||
void VertexAccessor::attach_out(mvcc::VersionList<Edge>* edge_vlist, PassKey<GraphDb>) {
|
||||
this->update()->out_.emplace_back(edge_vlist);
|
||||
}
|
||||
//bool VertexAccessor::remove() {
|
||||
// // TODO consider if this works well with MVCC
|
||||
// if (out_degree() > 0 || in_degree() > 0)
|
||||
// return false;
|
||||
//
|
||||
// vlist_.remove(&update(), db_accessor_.transaction_);
|
||||
// return true;
|
||||
//}
|
||||
//
|
||||
//void VertexAccessor::detach_remove() {
|
||||
// // removing edges via accessors is both safe
|
||||
// // and it should remove all the pointers in the relevant
|
||||
// // vertices (including this one)
|
||||
// for (auto edge_vlist : view().out_)
|
||||
// EdgeAccessor(*edge_vlist, db_accessor_).remove();
|
||||
//
|
||||
// for (auto edge_vlist : view().in_)
|
||||
// EdgeAccessor(*edge_vlist, db_accessor_).remove();
|
||||
//
|
||||
// vlist_.remove(&update(), db_accessor_.transaction_);
|
||||
//}
|
||||
|
@ -27,7 +27,7 @@ int main(int argc, char **argv)
|
||||
|
||||
QueryPreprocessor processor;
|
||||
using std::placeholders::_1;
|
||||
std::function<StrippedQuery<QueryPreprocessor::HashT>(const std::string &query)> preprocess =
|
||||
std::function<StrippedQuery(const std::string &query)> preprocess =
|
||||
std::bind(&QueryPreprocessor::preprocess, &processor, _1);
|
||||
|
||||
auto tests = dataset["benchmark_queries"].as<std::vector<std::string>>();
|
||||
|
@ -1,109 +0,0 @@
|
||||
// TODO: refactor (backlog task)
|
||||
|
||||
#include "_hardcoded_query/basic.hpp"
|
||||
#include "logging/default.hpp"
|
||||
#include "logging/streams/stdout.hpp"
|
||||
#include "query/preprocessor.hpp"
|
||||
#include "query/stripper.hpp"
|
||||
#include "utils/assert.hpp"
|
||||
#include "utils/sysinfo/memory.hpp"
|
||||
|
||||
template <class Q>
|
||||
void run(size_t n, std::string &query, Q &qf)
|
||||
{
|
||||
QueryPreprocessor preprocessor;
|
||||
auto stripped = preprocessor.preprocess(query);
|
||||
|
||||
logging::info("Running query [{}] x {}.", stripped.hash, n);
|
||||
|
||||
for (int i = 0; i < n; i++)
|
||||
{
|
||||
properties_t vec = stripped.arguments;
|
||||
permanent_assert(qf[stripped.hash](std::move(vec)), "Query failed!");
|
||||
}
|
||||
}
|
||||
|
||||
void clean_vertex(Db &db)
|
||||
{
|
||||
DbTransaction t(db);
|
||||
t.clean_vertex_section();
|
||||
t.trans.commit();
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
logging::init_sync();
|
||||
logging::log->pipe(std::make_unique<Stdout>());
|
||||
|
||||
Db db("cleaning");
|
||||
|
||||
size_t entities_number = 1000;
|
||||
auto query_functions = hardcode::load_basic_functions(db);
|
||||
|
||||
std::string create_vertex_label =
|
||||
"CREATE (n:LABEL {name: \"cleaner_test\"}) RETURN n";
|
||||
std::string create_vertex_other =
|
||||
"CREATE (n:OTHER {name: \"cleaner_test\"}) RETURN n";
|
||||
std::string delete_label_vertices = "MATCH (n:LABEL) DELETE n";
|
||||
std::string delete_all_vertices = "MATCH (n) DELETE n";
|
||||
|
||||
// ******************************* TEST 1 ********************************//
|
||||
// add vertices a
|
||||
// clean vertices
|
||||
// delete vertices a
|
||||
// clean vertices
|
||||
run(entities_number, create_vertex_label, query_functions);
|
||||
permanent_assert(db.graph.vertices.access().size() == entities_number,
|
||||
"Entities number doesn't match");
|
||||
|
||||
clean_vertex(db);
|
||||
permanent_assert(db.graph.vertices.access().size() == entities_number,
|
||||
"Entities number doesn't match (after cleaning)");
|
||||
|
||||
run(1, delete_label_vertices, query_functions);
|
||||
permanent_assert(db.graph.vertices.access().size() == entities_number,
|
||||
"Entities number doesn't match (delete label vertices)");
|
||||
|
||||
clean_vertex(db);
|
||||
permanent_assert(db.graph.vertices.access().size() == 0,
|
||||
"Db should be empty");
|
||||
|
||||
// ******************************* TEST 2 ********************************//
|
||||
// add vertices a
|
||||
// add vertices b
|
||||
// clean vertices
|
||||
// delete vertices a
|
||||
// clean vertices
|
||||
// delete vertices all
|
||||
run(entities_number, create_vertex_label, query_functions);
|
||||
permanent_assert(db.graph.vertices.access().size() == entities_number,
|
||||
"Entities number doesn't match");
|
||||
|
||||
run(entities_number, create_vertex_other, query_functions);
|
||||
permanent_assert(db.graph.vertices.access().size() == entities_number * 2,
|
||||
"Entities number doesn't match");
|
||||
|
||||
clean_vertex(db);
|
||||
permanent_assert(db.graph.vertices.access().size() == entities_number * 2,
|
||||
"Entities number doesn't match");
|
||||
|
||||
run(1, delete_label_vertices, query_functions);
|
||||
permanent_assert(db.graph.vertices.access().size() == entities_number * 2,
|
||||
"Entities number doesn't match");
|
||||
|
||||
clean_vertex(db);
|
||||
permanent_assert(db.graph.vertices.access().size() == entities_number,
|
||||
"Entities number doesn't match");
|
||||
|
||||
run(1, delete_all_vertices, query_functions);
|
||||
permanent_assert(db.graph.vertices.access().size() == entities_number,
|
||||
"Entities number doesn't match");
|
||||
|
||||
clean_vertex(db);
|
||||
permanent_assert(db.graph.vertices.access().size() == 0,
|
||||
"Db should be empty");
|
||||
|
||||
// TODO: more tests
|
||||
|
||||
return 0;
|
||||
}
|
@ -1,283 +0,0 @@
|
||||
// TODO: refactor (backlog task)
|
||||
|
||||
#include <random>
|
||||
|
||||
#include "_hardcoded_query/basic.hpp"
|
||||
#include "logging/default.hpp"
|
||||
#include "logging/streams/stdout.hpp"
|
||||
#include "query/preprocessor.hpp"
|
||||
#include "query/stripper.hpp"
|
||||
#include "storage/indexes/indexes.hpp"
|
||||
#include "utils/assert.hpp"
|
||||
#include "utils/signals/handler.hpp"
|
||||
#include "utils/stacktrace/log.hpp"
|
||||
#include "utils/sysinfo/memory.hpp"
|
||||
|
||||
// Returns uniform random size_t generator from range [0,n>
|
||||
auto rand_gen(size_t n)
|
||||
{
|
||||
std::default_random_engine generator;
|
||||
std::uniform_int_distribution<size_t> distribution(0, n - 1);
|
||||
return std::bind(distribution, generator);
|
||||
}
|
||||
|
||||
void run(size_t n, std::string &query, Db &db)
|
||||
{
|
||||
auto qf = hardcode::load_basic_functions(db);
|
||||
QueryPreprocessor preprocessor;
|
||||
auto stripped = preprocessor.preprocess(query);
|
||||
|
||||
logging::info("Running query [{}] x {}.", stripped.hash, n);
|
||||
|
||||
for (int i = 0; i < n; i++)
|
||||
{
|
||||
properties_t vec = stripped.arguments;
|
||||
auto commited = qf[stripped.hash](std::move(vec));
|
||||
permanent_assert(commited, "Query execution failed");
|
||||
}
|
||||
}
|
||||
|
||||
void add_edge(size_t n, Db &db)
|
||||
{
|
||||
auto qf = hardcode::load_basic_functions(db);
|
||||
std::string query = "MATCH (n1), (n2) WHERE ID(n1)=0 AND "
|
||||
"ID(n2)=1 CREATE (n1)<-[r:IS {age: "
|
||||
"25,weight: 70}]-(n2) RETURN r";
|
||||
QueryPreprocessor preprocessor;
|
||||
auto stripped = preprocessor.preprocess(query);
|
||||
|
||||
logging::info("Running query [{}] (add edge) x {}", stripped.hash, n);
|
||||
|
||||
std::vector<int64_t> vertices;
|
||||
for (auto &v : db.graph.vertices.access())
|
||||
{
|
||||
vertices.push_back(v.second.id);
|
||||
}
|
||||
permanent_assert(vertices.size() > 0, "Vertices size is zero");
|
||||
|
||||
auto rand = rand_gen(vertices.size());
|
||||
for (int i = 0; i < n; i++)
|
||||
{
|
||||
properties_t vec = stripped.arguments;
|
||||
vec[0] = Property(Int64(vertices[rand()]), Flags::Int64);
|
||||
vec[1] = Property(Int64(vertices[rand()]), Flags::Int64);
|
||||
permanent_assert(qf[stripped.hash](std::move(vec)), "Add edge failed");
|
||||
}
|
||||
}
|
||||
|
||||
void add_property(Db &db, StoredProperty<TypeGroupVertex> &prop)
|
||||
{
|
||||
DbAccessor t(db);
|
||||
|
||||
t.vertex_access().fill().update().for_all([&](auto va) { va.set(prop); });
|
||||
|
||||
permanent_assert(t.commit(), "Add property failed");
|
||||
}
|
||||
|
||||
void add_vertex_property_serial_int(Db &db, PropertyFamily<TypeGroupVertex> &f)
|
||||
{
|
||||
DbAccessor t(db);
|
||||
|
||||
auto key = f.get(Int64::type).family_key();
|
||||
|
||||
size_t i = 0;
|
||||
t.vertex_access().fill().update().for_all([&](auto va) mutable {
|
||||
va.set(StoredProperty<TypeGroupVertex>(Int64(i), key));
|
||||
i++;
|
||||
});
|
||||
|
||||
permanent_assert(t.commit(), "Add vertex property serial int failed");
|
||||
}
|
||||
|
||||
void add_edge_property_serial_int(Db &db, PropertyFamily<TypeGroupEdge> &f)
|
||||
{
|
||||
DbAccessor t(db);
|
||||
|
||||
auto key = f.get(Int64::type).family_key();
|
||||
|
||||
size_t i = 0;
|
||||
t.edge_access().fill().update().for_all([&](auto va) mutable {
|
||||
va.set(StoredProperty<TypeGroupEdge>(Int64(i), key));
|
||||
i++;
|
||||
});
|
||||
|
||||
permanent_assert(t.commit(), "Add Edge property serial int failed");
|
||||
}
|
||||
|
||||
template <class TG>
|
||||
size_t size(Db &db, IndexHolder<TG, std::nullptr_t> &h)
|
||||
{
|
||||
DbAccessor t(db);
|
||||
|
||||
size_t count = 0;
|
||||
auto oin = h.get_read();
|
||||
if (oin.is_present())
|
||||
{
|
||||
oin.get()->for_range(t).for_all([&](auto va) mutable { count++; });
|
||||
}
|
||||
|
||||
t.commit();
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
void assert_empty(Db &db)
|
||||
{
|
||||
permanent_assert(db.graph.vertices.access().size() == 0,
|
||||
"DB isn't empty (vertices)");
|
||||
permanent_assert(db.graph.edges.access().size() == 0,
|
||||
"DB isn't empty (edges)");
|
||||
}
|
||||
|
||||
void clean_vertex(Db &db)
|
||||
{
|
||||
DbTransaction t(db);
|
||||
t.clean_vertex_section();
|
||||
t.trans.commit();
|
||||
}
|
||||
|
||||
void clean_edge(Db &db)
|
||||
{
|
||||
DbTransaction t(db);
|
||||
t.clean_edge_section();
|
||||
t.trans.commit();
|
||||
}
|
||||
|
||||
void clear_database(Db &db)
|
||||
{
|
||||
std::string delete_all_vertices = "MATCH (n) DELETE n";
|
||||
std::string delete_all_edges = "MATCH ()-[r]-() DELETE r";
|
||||
|
||||
run(1, delete_all_edges, db);
|
||||
run(1, delete_all_vertices, db);
|
||||
clean_vertex(db);
|
||||
clean_edge(db);
|
||||
assert_empty(db);
|
||||
}
|
||||
|
||||
bool equal(Db &a, Db &b)
|
||||
{
|
||||
{
|
||||
auto acc_a = a.graph.vertices.access();
|
||||
auto acc_b = b.graph.vertices.access();
|
||||
|
||||
if (acc_a.size() != acc_b.size())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
auto it_a = acc_a.begin();
|
||||
auto it_b = acc_b.begin();
|
||||
|
||||
for (auto i = acc_a.size(); i > 0; i--)
|
||||
{
|
||||
// TODO: compare
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
auto acc_a = a.graph.edges.access();
|
||||
auto acc_b = b.graph.edges.access();
|
||||
|
||||
if (acc_a.size() != acc_b.size())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
auto it_a = acc_a.begin();
|
||||
auto it_b = acc_b.begin();
|
||||
|
||||
for (auto i = acc_a.size(); i > 0; i--)
|
||||
{
|
||||
// TODO: compare
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
logging::init_sync();
|
||||
logging::log->pipe(std::make_unique<Stdout>());
|
||||
|
||||
SignalHandler::register_handler(Signal::SegmentationFault, []() {
|
||||
log_stacktrace("SegmentationFault signal raised");
|
||||
std::exit(EXIT_FAILURE);
|
||||
});
|
||||
|
||||
SignalHandler::register_handler(Signal::BusError, []() {
|
||||
log_stacktrace("Bus error signal raised");
|
||||
std::exit(EXIT_FAILURE);
|
||||
});
|
||||
|
||||
size_t cvl_n = 1;
|
||||
|
||||
std::string create_vertex_label =
|
||||
"CREATE (n:LABEL {name: \"cleaner_test\"}) RETURN n";
|
||||
std::string create_vertex_other =
|
||||
"CREATE (n:OTHER {name: \"cleaner_test\"}) RETURN n";
|
||||
std::string delete_label_vertices = "MATCH (n:LABEL) DELETE n";
|
||||
std::string delete_all_vertices = "MATCH (n) DELETE n";
|
||||
|
||||
IndexDefinition vertex_property_nonunique_unordered = {
|
||||
IndexLocation{VertexSide, Option<std::string>("prop"),
|
||||
Option<std::string>(), Option<std::string>()},
|
||||
IndexType{false, None}};
|
||||
IndexDefinition edge_property_nonunique_unordered = {
|
||||
IndexLocation{EdgeSide, Option<std::string>("prop"),
|
||||
Option<std::string>(), Option<std::string>()},
|
||||
IndexType{false, None}};
|
||||
IndexDefinition edge_property_unique_ordered = {
|
||||
IndexLocation{EdgeSide, Option<std::string>("prop"),
|
||||
Option<std::string>(), Option<std::string>()},
|
||||
IndexType{true, Ascending}};
|
||||
IndexDefinition vertex_property_unique_ordered = {
|
||||
IndexLocation{VertexSide, Option<std::string>("prop"),
|
||||
Option<std::string>(), Option<std::string>()},
|
||||
IndexType{true, Ascending}};
|
||||
|
||||
// ******************************* TEST 1 ********************************//
|
||||
{
|
||||
logging::info("TEST 1");
|
||||
// add indexes
|
||||
// add vertices LABEL
|
||||
// add edges
|
||||
// add vertices property
|
||||
// assert index size.
|
||||
Db db("index", false);
|
||||
permanent_assert(
|
||||
db.indexes().add_index(vertex_property_nonunique_unordered),
|
||||
"Add vertex index failed");
|
||||
permanent_assert(
|
||||
db.indexes().add_index(edge_property_nonunique_unordered),
|
||||
"Add edge index failed");
|
||||
|
||||
run(cvl_n, create_vertex_label, db);
|
||||
auto sp = StoredProperty<TypeGroupVertex>(
|
||||
Int64(0), db.graph.vertices.property_family_find_or_create("prop")
|
||||
.get(Int64::type)
|
||||
.family_key());
|
||||
add_property(db, sp);
|
||||
|
||||
permanent_assert(
|
||||
cvl_n == size(db, db.graph.vertices
|
||||
.property_family_find_or_create("prop")
|
||||
.index),
|
||||
"Create vertex property failed");
|
||||
|
||||
add_edge(cvl_n, db);
|
||||
add_edge_property_serial_int(
|
||||
db, db.graph.edges.property_family_find_or_create("prop"));
|
||||
|
||||
permanent_assert(
|
||||
cvl_n ==
|
||||
size(db, db.graph.edges.property_family_find_or_create("prop")
|
||||
.index),
|
||||
"Create edge property failed");
|
||||
}
|
||||
|
||||
// TODO: more tests
|
||||
|
||||
return 0;
|
||||
}
|
@ -1,4 +1,5 @@
|
||||
#include "query_engine_common.hpp"
|
||||
#include "dbms/dbms.hpp"
|
||||
|
||||
using namespace std::chrono_literals;
|
||||
using namespace tests::integration;
|
||||
@ -23,13 +24,14 @@ int main(int argc, char *argv[])
|
||||
* init engine
|
||||
*/
|
||||
auto log = init_logging("IntegrationQueryEngine");
|
||||
Db db;
|
||||
Dbms dbms;
|
||||
StreamT stream(std::cout);
|
||||
QueryEngineT query_engine;
|
||||
// IMPORTANT: PrintRecordStream can be replaces with a smarter
|
||||
// object that can test the results
|
||||
|
||||
WarmUpEngine(log, query_engine, db, stream);
|
||||
|
||||
auto db_accessor = dbms.active();
|
||||
WarmUpEngine(log, query_engine, db_accessor, stream);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -3,7 +3,7 @@
|
||||
#include <experimental/filesystem>
|
||||
#include <set>
|
||||
namespace fs = std::experimental::filesystem;
|
||||
#include "database/db.hpp"
|
||||
#include "database/graph_db_accessor.hpp"
|
||||
#include "logging/default.hpp"
|
||||
#include "logging/streams/stdout.cpp"
|
||||
#include "query/engine.hpp"
|
||||
@ -20,7 +20,7 @@ namespace integration
|
||||
{
|
||||
|
||||
using namespace utils;
|
||||
using QueryHashesT = std::set<QueryPreprocessor::HashT>;
|
||||
using QueryHashesT = std::set<HashType>;
|
||||
using QueryEngineT = QueryEngine<PrintRecordStream>;
|
||||
using StreamT = PrintRecordStream;
|
||||
|
||||
@ -123,13 +123,13 @@ auto LoadQueryPlans(Logger &log, QueryEngineT &engine,
|
||||
*
|
||||
* @param log external logger reference
|
||||
* @param engine query engine
|
||||
* @param db a database agains the query plans are going to be executed
|
||||
* @param db_accessor a database accessor on which the query plans are executed
|
||||
* @param path path a queries file
|
||||
* @param stream used by query plans to output the results
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
auto ExecuteQueryPlans(Logger &log, QueryEngineT &engine, Db &db,
|
||||
auto ExecuteQueryPlans(Logger &log, QueryEngineT &engine, GraphDbAccessor &db_accessor,
|
||||
const fs::path &path, StreamT &stream)
|
||||
{
|
||||
log.info("*** Execute the queries from the queries_file ***");
|
||||
@ -140,7 +140,7 @@ auto ExecuteQueryPlans(Logger &log, QueryEngineT &engine, Db &db,
|
||||
if (query.empty()) continue;
|
||||
permanent_assert(engine.Loaded(trim(query)),
|
||||
"Implementation wasn't loaded");
|
||||
engine.Run(query, db, stream);
|
||||
engine.Run(query, db_accessor, stream);
|
||||
}
|
||||
}
|
||||
|
||||
@ -152,12 +152,13 @@ auto ExecuteQueryPlans(Logger &log, QueryEngineT &engine, Db &db,
|
||||
*
|
||||
* @param log external logger reference
|
||||
* @param engine query engine
|
||||
* @param db a database agains the query plans are going to be executed
|
||||
* @param db_accessor a database accessor on which the query plans are executed
|
||||
* @param stream used by query plans to output the results
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
auto WarmUpEngine(Logger &log, QueryEngineT &engine, Db &db, StreamT &stream)
|
||||
auto WarmUpEngine(Logger &log, QueryEngineT &engine, GraphDbAccessor &db_accessor,
|
||||
StreamT &stream)
|
||||
{
|
||||
// path to a file with queries
|
||||
auto queries_file = fs::path(
|
||||
@ -173,7 +174,7 @@ auto WarmUpEngine(Logger &log, QueryEngineT &engine, Db &db, StreamT &stream)
|
||||
LoadQueryPlans(log, engine, query_hashes, implementations_folder);
|
||||
|
||||
// execute all loaded query plasn
|
||||
ExecuteQueryPlans(log, engine, db, queries_file, stream);
|
||||
ExecuteQueryPlans(log, engine, db_accessor, queries_file, stream);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,292 +0,0 @@
|
||||
// TODO: refactor (backlog task)
|
||||
|
||||
#include <random>
|
||||
|
||||
#include "_hardcoded_query/basic.hpp"
|
||||
#include "logging/default.hpp"
|
||||
#include "logging/streams/stdout.hpp"
|
||||
#include "query/preprocessor.hpp"
|
||||
#include "query/stripper.hpp"
|
||||
#include "storage/indexes/indexes.hpp"
|
||||
#include "utils/assert.hpp"
|
||||
#include "utils/signals/handler.hpp"
|
||||
#include "utils/stacktrace/log.hpp"
|
||||
#include "utils/sysinfo/memory.hpp"
|
||||
|
||||
// Returns uniform random size_t generator from range [0,n>
|
||||
auto rand_gen(size_t n)
|
||||
{
|
||||
std::default_random_engine generator;
|
||||
std::uniform_int_distribution<size_t> distribution(0, n - 1);
|
||||
return std::bind(distribution, generator);
|
||||
}
|
||||
|
||||
void run(size_t n, std::string &query, Db &db)
|
||||
{
|
||||
auto qf = hardcode::load_basic_functions(db);
|
||||
QueryPreprocessor preprocessor;
|
||||
auto stripped = preprocessor.preprocess(query);
|
||||
|
||||
logging::info("Running query {} [{}] x {}.", query, stripped.hash, n);
|
||||
|
||||
for (int i = 0; i < n; i++)
|
||||
{
|
||||
properties_t vec = stripped.arguments;
|
||||
permanent_assert(qf[stripped.hash](std::move(vec)), "Query aborted");
|
||||
}
|
||||
}
|
||||
|
||||
void add_edge(size_t n, Db &db)
|
||||
{
|
||||
auto qf = hardcode::load_basic_functions(db);
|
||||
|
||||
std::string query = "MATCH (n1), (n2) WHERE ID(n1)=0 AND "
|
||||
"ID(n2)=1 CREATE (n1)<-[r:IS {age: "
|
||||
"25,weight: 70}]-(n2) RETURN r";
|
||||
QueryPreprocessor preprocessor;
|
||||
auto stripped = preprocessor.preprocess(query);
|
||||
|
||||
logging::info("Running query {} [{}] x {}.", query, stripped.hash, n);
|
||||
|
||||
std::vector<int64_t> vertices;
|
||||
for (auto &v : db.graph.vertices.access())
|
||||
{
|
||||
vertices.push_back(v.second.id);
|
||||
}
|
||||
|
||||
auto rand = rand_gen(vertices.size());
|
||||
for (int i = 0; i < n; i++)
|
||||
{
|
||||
properties_t vec = stripped.arguments;
|
||||
vec[0] = Property(Int64(vertices[rand()]), Flags::Int64);
|
||||
vec[1] = Property(Int64(vertices[rand()]), Flags::Int64);
|
||||
permanent_assert(qf[stripped.hash](std::move(vec)), "Query aborted");
|
||||
}
|
||||
}
|
||||
|
||||
void add_property(Db &db, StoredProperty<TypeGroupVertex> &prop)
|
||||
{
|
||||
DbAccessor t(db);
|
||||
|
||||
t.vertex_access().fill().for_all([&](auto va) { va.set(prop); });
|
||||
|
||||
permanent_assert(t.commit(), "add property query aborted");
|
||||
;
|
||||
}
|
||||
|
||||
void add_property_different_int(Db &db, PropertyFamily<TypeGroupVertex> &f)
|
||||
{
|
||||
DbAccessor t(db);
|
||||
|
||||
auto key = f.get(Int64::type).family_key();
|
||||
|
||||
size_t i = 0;
|
||||
t.vertex_access().fill().for_all([&](auto va) mutable {
|
||||
va.set(StoredProperty<TypeGroupVertex>(Int64(i), key));
|
||||
i++;
|
||||
});
|
||||
|
||||
permanent_assert(t.commit(), "add property different int aborted");
|
||||
}
|
||||
|
||||
size_t size(Db &db, IndexHolder<TypeGroupVertex, std::nullptr_t> &h)
|
||||
{
|
||||
DbAccessor t(db);
|
||||
|
||||
size_t count = 0;
|
||||
auto oin = h.get_read();
|
||||
if (oin.is_present())
|
||||
{
|
||||
oin.get()->for_range(t).for_all([&](auto va) mutable { count++; });
|
||||
}
|
||||
|
||||
t.commit();
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
void assert_empty(Db &db)
|
||||
{
|
||||
permanent_assert(db.graph.vertices.access().size() == 0, "Db isn't empty");
|
||||
permanent_assert(db.graph.edges.access().size() == 0, "Db isn't empty");
|
||||
}
|
||||
|
||||
void clean_vertex(Db &db)
|
||||
{
|
||||
DbTransaction t(db);
|
||||
t.clean_vertex_section();
|
||||
t.trans.commit();
|
||||
}
|
||||
|
||||
void clean_edge(Db &db)
|
||||
{
|
||||
DbTransaction t(db);
|
||||
t.clean_edge_section();
|
||||
t.trans.commit();
|
||||
}
|
||||
|
||||
void clear_database(Db &db)
|
||||
{
|
||||
std::string delete_all_vertices = "MATCH (n) DELETE n";
|
||||
std::string delete_all_edges = "MATCH ()-[r]-() DELETE r";
|
||||
|
||||
run(1, delete_all_edges, db);
|
||||
run(1, delete_all_vertices, db);
|
||||
clean_vertex(db);
|
||||
clean_edge(db);
|
||||
assert_empty(db);
|
||||
}
|
||||
|
||||
bool equal(Db &a, Db &b)
|
||||
{
|
||||
{
|
||||
auto acc_a = a.graph.vertices.access();
|
||||
auto acc_b = b.graph.vertices.access();
|
||||
|
||||
if (acc_a.size() != acc_b.size())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
auto it_a = acc_a.begin();
|
||||
auto it_b = acc_b.begin();
|
||||
|
||||
for (auto i = acc_a.size(); i > 0; i--)
|
||||
{
|
||||
// TODO: compare
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
auto acc_a = a.graph.edges.access();
|
||||
auto acc_b = b.graph.edges.access();
|
||||
|
||||
if (acc_a.size() != acc_b.size())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
auto it_a = acc_a.begin();
|
||||
auto it_b = acc_b.begin();
|
||||
|
||||
for (auto i = acc_a.size(); i > 0; i--)
|
||||
{
|
||||
// TODO: compare
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
logging::init_sync();
|
||||
logging::log->pipe(std::make_unique<Stdout>());
|
||||
|
||||
SignalHandler::register_handler(Signal::SegmentationFault, []() {
|
||||
log_stacktrace("SegmentationFault signal raised");
|
||||
std::exit(EXIT_FAILURE);
|
||||
});
|
||||
|
||||
size_t cvl_n = 1000;
|
||||
|
||||
std::string create_vertex_label =
|
||||
"CREATE (n:LABEL {name: \"cleaner_test\"}) RETURN n";
|
||||
std::string create_vertex_other =
|
||||
"CREATE (n:OTHER {name: \"cleaner_test\"}) RETURN n";
|
||||
std::string delete_label_vertices = "MATCH (n:LABEL) DELETE n";
|
||||
std::string delete_all_vertices = "MATCH (n) DELETE n";
|
||||
|
||||
{
|
||||
logging::info("TEST 1");
|
||||
// make snapshot of empty db
|
||||
// add vertexs
|
||||
// add edges
|
||||
// empty database
|
||||
// import snapshot
|
||||
// assert database empty
|
||||
Db db("snapshot", false);
|
||||
db.snap_engine.make_snapshot();
|
||||
run(cvl_n, create_vertex_label, db);
|
||||
add_edge(cvl_n, db);
|
||||
clear_database(db);
|
||||
db.snap_engine.import();
|
||||
assert_empty(db);
|
||||
logging::info("END of TEST 1");
|
||||
}
|
||||
|
||||
{
|
||||
logging::info("TEST 2");
|
||||
// add vertexs
|
||||
// add edges
|
||||
// make snapshot of db
|
||||
// empty database
|
||||
// import snapshot
|
||||
// create new db
|
||||
// compare database with new db
|
||||
Db db("snapshot", false);
|
||||
run(cvl_n, create_vertex_label, db);
|
||||
add_edge(cvl_n, db);
|
||||
db.snap_engine.make_snapshot();
|
||||
clear_database(db);
|
||||
db.snap_engine.import();
|
||||
{
|
||||
Db db2("snapshot");
|
||||
permanent_assert(equal(db, db2), "Dbs aren't equal");
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
logging::info("TEST 3");
|
||||
// add vertexs
|
||||
// add edges
|
||||
// make snapshot of db
|
||||
// compare database with different named database
|
||||
Db db("snapshot", false);
|
||||
run(cvl_n, create_vertex_label, db);
|
||||
add_edge(cvl_n, db);
|
||||
db.snap_engine.make_snapshot();
|
||||
{
|
||||
Db db2("not_snapshot");
|
||||
permanent_assert(!equal(db, db2), "Dbs are equal");
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
logging::info("TEST 4");
|
||||
// add vertices LABEL
|
||||
// add properties
|
||||
// add vertices LABEL
|
||||
// add index on proprety
|
||||
// assert index containts vertices
|
||||
// make snapshot
|
||||
// create new db
|
||||
// assert index on LABEL in new db exists
|
||||
// assert index in new db containts vertice
|
||||
Db db("snapshot", false);
|
||||
run(cvl_n, create_vertex_label, db);
|
||||
auto &family = db.graph.vertices.property_family_find_or_create("prop");
|
||||
add_property_different_int(db, family);
|
||||
run(cvl_n, create_vertex_other, db);
|
||||
IndexDefinition idef = {
|
||||
IndexLocation{VertexSide, Option<std::string>("prop"),
|
||||
Option<std::string>(), Option<std::string>()},
|
||||
IndexType{false, None}};
|
||||
permanent_assert(db.indexes().add_index(idef), "Index isn't added");
|
||||
permanent_assert(cvl_n == size(db, family.index),
|
||||
"Index size isn't valid");
|
||||
db.snap_engine.make_snapshot();
|
||||
{
|
||||
Db db2("snapshot");
|
||||
permanent_assert(
|
||||
cvl_n == size(db, db2.graph.vertices
|
||||
.property_family_find_or_create("prop")
|
||||
.index),
|
||||
"Index size isn't valid");
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: more tests
|
||||
|
||||
return 0;
|
||||
}
|
@ -7,134 +7,82 @@
|
||||
|
||||
#include "utils/exceptions/not_yet_implemented.hpp"
|
||||
|
||||
class PrintRecordStream
|
||||
{
|
||||
class PrintRecordStream {
|
||||
private:
|
||||
std::ostream& stream;
|
||||
std::ostream &stream;
|
||||
|
||||
public:
|
||||
PrintRecordStream(std::ostream &stream) : stream(stream) {}
|
||||
PrintRecordStream(std::ostream &stream) : stream(stream) {}
|
||||
|
||||
void write_success()
|
||||
{
|
||||
stream << "SUCCESS\n";
|
||||
void write_success() {
|
||||
stream << "SUCCESS\n";
|
||||
}
|
||||
|
||||
void write_success_empty() {
|
||||
stream << "SUCCESS EMPTY\n";
|
||||
}
|
||||
|
||||
void write_ignored() {
|
||||
stream << "IGNORED\n";
|
||||
}
|
||||
|
||||
void write_empty_fields() {
|
||||
stream << "EMPTY FIELDS\n";
|
||||
}
|
||||
|
||||
void write_fields(const std::vector <std::string> &fields) {
|
||||
stream << "FIELDS:";
|
||||
for (auto &field : fields) {
|
||||
stream << " " << field;
|
||||
}
|
||||
stream << '\n';
|
||||
}
|
||||
|
||||
void write_success_empty()
|
||||
{
|
||||
stream << "SUCCESS EMPTY\n";
|
||||
}
|
||||
void write_field(const std::string &field) {
|
||||
stream << "Field: " << field << '\n';
|
||||
}
|
||||
|
||||
void write_ignored()
|
||||
{
|
||||
stream << "IGNORED\n";
|
||||
}
|
||||
void write_list_header(size_t size) {
|
||||
stream << "List: " << size << '\n';
|
||||
}
|
||||
|
||||
void write_empty_fields()
|
||||
{
|
||||
stream << "EMPTY FIELDS\n";
|
||||
}
|
||||
void write_record() {
|
||||
stream << "Record\n";
|
||||
}
|
||||
|
||||
void write_fields(const std::vector<std::string> &fields)
|
||||
{
|
||||
stream << "FIELDS:";
|
||||
for (auto &field : fields)
|
||||
{
|
||||
stream << " " << field;
|
||||
}
|
||||
stream << '\n';
|
||||
}
|
||||
void write_meta(const std::string &type) {
|
||||
stream << "Meta: " << type << std::endl;
|
||||
}
|
||||
|
||||
void write_field(const std::string &field)
|
||||
{
|
||||
stream << "Field: " << field << '\n';
|
||||
}
|
||||
void write_failure(const std::map <std::string, std::string> &data) {
|
||||
throw NotYetImplemented();
|
||||
}
|
||||
|
||||
void write_list_header(size_t size)
|
||||
{
|
||||
stream << "List: " << size << '\n';
|
||||
}
|
||||
void write_count(const size_t count) {
|
||||
throw NotYetImplemented();
|
||||
}
|
||||
|
||||
void write_record()
|
||||
{
|
||||
stream << "Record\n";
|
||||
}
|
||||
void write(const VertexAccessor &vertex) {
|
||||
throw NotYetImplemented();
|
||||
}
|
||||
|
||||
void write_meta(const std::string &type)
|
||||
{
|
||||
stream << "Meta: " << type << std::endl;
|
||||
}
|
||||
void write_vertex_record(const VertexAccessor &va) {
|
||||
throw NotYetImplemented();
|
||||
}
|
||||
|
||||
void write_failure(const std::map<std::string, std::string> &data)
|
||||
{
|
||||
throw NotYetImplemented();
|
||||
}
|
||||
void write(const EdgeAccessor &edge) {
|
||||
throw NotYetImplemented();
|
||||
}
|
||||
|
||||
void write_count(const size_t count)
|
||||
{
|
||||
throw NotYetImplemented();
|
||||
}
|
||||
void write_edge_record(const EdgeAccessor &ea) {
|
||||
throw NotYetImplemented();
|
||||
}
|
||||
|
||||
void write(const VertexAccessor &vertex)
|
||||
{
|
||||
throw NotYetImplemented();
|
||||
}
|
||||
void send() {
|
||||
throw NotYetImplemented();
|
||||
}
|
||||
|
||||
void write_vertex_record(const VertexAccessor& va)
|
||||
{
|
||||
va.stream_repr(stream);
|
||||
stream << std::endl;
|
||||
}
|
||||
|
||||
void write(const EdgeAccessor &edge)
|
||||
{
|
||||
throw NotYetImplemented();
|
||||
}
|
||||
|
||||
void write_edge_record(const EdgeAccessor& ea)
|
||||
{
|
||||
throw NotYetImplemented();
|
||||
}
|
||||
|
||||
void write(const StoredProperty<TypeGroupEdge> &prop)
|
||||
{
|
||||
throw NotYetImplemented();
|
||||
}
|
||||
|
||||
void write(const StoredProperty<TypeGroupVertex> &prop)
|
||||
{
|
||||
throw NotYetImplemented();
|
||||
}
|
||||
|
||||
void write(const Null &prop)
|
||||
{
|
||||
throw NotYetImplemented();
|
||||
}
|
||||
|
||||
void write(const Bool &prop)
|
||||
{
|
||||
throw NotYetImplemented();
|
||||
}
|
||||
|
||||
void write(const Float &prop) { throw NotYetImplemented(); }
|
||||
void write(const Int32 &prop) { throw NotYetImplemented(); }
|
||||
void write(const Int64 &prop) { throw NotYetImplemented(); }
|
||||
void write(const Double &prop) { throw NotYetImplemented(); }
|
||||
void write(const String &prop) { throw NotYetImplemented(); }
|
||||
void write(const ArrayBool &prop) { throw NotYetImplemented(); }
|
||||
void write(const ArrayInt32 &prop) { throw NotYetImplemented(); }
|
||||
void write(const ArrayInt64 &prop) { throw NotYetImplemented(); }
|
||||
void write(const ArrayFloat &prop) { throw NotYetImplemented(); }
|
||||
void write(const ArrayDouble &prop) { throw NotYetImplemented(); }
|
||||
void write(const ArrayString &prop) { throw NotYetImplemented(); }
|
||||
|
||||
void send()
|
||||
{
|
||||
throw NotYetImplemented();
|
||||
}
|
||||
|
||||
void chunk()
|
||||
{
|
||||
throw NotYetImplemented();
|
||||
}
|
||||
void chunk() {
|
||||
throw NotYetImplemented();
|
||||
}
|
||||
};
|
||||
|
@ -1,4 +1,5 @@
|
||||
#include "../integration/query_engine_common.hpp"
|
||||
#include "dbms/dbms.hpp"
|
||||
|
||||
#include "utils/fswatcher.hpp"
|
||||
|
||||
@ -16,13 +17,14 @@ int main(int argc, char *argv[])
|
||||
|
||||
// init engine
|
||||
auto log = init_logging("ManualQueryEngine");
|
||||
Db db;
|
||||
Dbms dbms;
|
||||
auto db_accessor = dbms.active();
|
||||
StreamT stream(std::cout);
|
||||
QueryEngineT query_engine;
|
||||
// IMPORTANT: PrintRecordStream can be replaces with a smarter
|
||||
// object that can test the results
|
||||
|
||||
WarmUpEngine(log, query_engine, db, stream);
|
||||
WarmUpEngine(log, query_engine, db_accessor, stream);
|
||||
|
||||
// init watcher
|
||||
FSWatcher watcher;
|
||||
@ -51,7 +53,7 @@ int main(int argc, char *argv[])
|
||||
query_engine.Unload(query);
|
||||
try {
|
||||
query_engine.ReloadCustom(query, event.path);
|
||||
query_engine.Run(query, db, stream);
|
||||
query_engine.Run(query, db_accessor, stream);
|
||||
} catch (PlanCompilationException& e) {
|
||||
log.info("Query compilation failed: {}", e.what());
|
||||
} catch (std::exception& e) {
|
||||
|
@ -34,7 +34,7 @@ int main(int argc, char **argv)
|
||||
println("Query hash: ", preprocessed.hash);
|
||||
println("Property values:");
|
||||
for (auto property : preprocessed.arguments) {
|
||||
println(" ", property);
|
||||
println(" ", property.second);
|
||||
}
|
||||
println("");
|
||||
|
||||
|
@ -1,39 +0,0 @@
|
||||
#include <iostream>
|
||||
|
||||
#include "gtest/gtest.h"
|
||||
|
||||
#include "query/backend/cpp_old/entity_search.hpp"
|
||||
#include "utils/assert.hpp"
|
||||
#include "utils/underlying_cast.hpp"
|
||||
|
||||
TEST(CypherStateMachine, Basic)
|
||||
{
|
||||
// initialize cypher state machine
|
||||
CypherStateMachine csm;
|
||||
|
||||
// set cost for label index
|
||||
auto test_cost = static_cast<uint64_t>(30);
|
||||
csm.search_cost("n", entity_search::search_label_index, test_cost);
|
||||
|
||||
// check all costs
|
||||
auto max_cost = entity_search::max<uint64_t>();
|
||||
permanent_assert(csm.search_cost("n", entity_search::search_internal_id) ==
|
||||
max_cost,
|
||||
"Search internal id cost should be max cost value");
|
||||
permanent_assert(csm.search_cost("n", entity_search::search_label_index) ==
|
||||
test_cost,
|
||||
"Search label index cost should be test cost value");
|
||||
permanent_assert(
|
||||
csm.search_cost("n", entity_search::search_property_index) == max_cost,
|
||||
"Search property index should be max cost value");
|
||||
|
||||
// check minimum cost
|
||||
permanent_assert(csm.min("n") == entity_search::search_label_index,
|
||||
"Search place should be label index");
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
::testing::InitGoogleTest(&argc, argv);
|
||||
return RUN_ALL_TESTS();
|
||||
}
|
@ -1,24 +0,0 @@
|
||||
#include "gtest/gtest.h"
|
||||
|
||||
#include "query/backend/cpp_old/query_action_data.hpp"
|
||||
#include "utils/assert.hpp"
|
||||
|
||||
using ParameterIndexKey::Type::InternalId;
|
||||
using ParameterIndexKey::Type::Projection;
|
||||
|
||||
TEST(ParameterIndexKey, Basic)
|
||||
{
|
||||
std::map<ParameterIndexKey, uint64_t> parameter_index;
|
||||
|
||||
parameter_index[ParameterIndexKey(InternalId, "n1")] = 0;
|
||||
parameter_index[ParameterIndexKey(InternalId, "n2")] = 1;
|
||||
|
||||
permanent_assert(parameter_index.size() == 2,
|
||||
"Parameter index size should be 2");
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
::testing::InitGoogleTest(&argc, argv);
|
||||
return RUN_ALL_TESTS();
|
||||
}
|
@ -7,11 +7,11 @@
|
||||
|
||||
#include "gtest/gtest.h"
|
||||
|
||||
#include "storage/model/typed_value.hpp"
|
||||
#include "storage/model/typed_value_store.hpp"
|
||||
#include "storage/typed_value.hpp"
|
||||
#include "storage/typed_value_store.hpp"
|
||||
|
||||
TypedValueStore MakePropsAllTypes() {
|
||||
TypedValueStore props;
|
||||
TypedValueStore<> MakePropsAllTypes() {
|
||||
TypedValueStore<> props;
|
||||
props.set(0, true);
|
||||
props.set(1, "something");
|
||||
props.set(2, 42);
|
||||
@ -87,7 +87,7 @@ TEST(TypedValue, Equals) {
|
||||
|
||||
TEST(TypedValue, Less) {
|
||||
// not_bool_type < bool -> exception
|
||||
TypedValueStore props = MakePropsAllTypes();
|
||||
TypedValueStore<> props = MakePropsAllTypes();
|
||||
for (int i = 0; i < props.size() + 1; ++i) {
|
||||
if (props.at(i).type_ == TypedValue::Type::Bool)
|
||||
continue;
|
||||
@ -157,7 +157,7 @@ TEST(TypedValue, UnaryMinus) {
|
||||
*/
|
||||
void ExpectArithmeticThrowsAndNull(bool string_ok, std::function<TypedValue(const TypedValue&, const TypedValue&)> op) {
|
||||
// arithmetic ops that raise
|
||||
TypedValueStore props = MakePropsAllTypes();
|
||||
TypedValueStore<> props = MakePropsAllTypes();
|
||||
for (int i = 0; i < props.size() + 1; ++i) {
|
||||
EXPECT_THROW(op(TypedValue(true), props.at(i)), TypedValueException);
|
||||
EXPECT_THROW(op(props.at(i), TypedValue(true)), TypedValueException);
|
||||
@ -243,7 +243,7 @@ TEST(TypedValue, Modulo) {
|
||||
}
|
||||
|
||||
TEST(TypedValue, TypeIncompatibility) {
|
||||
TypedValueStore props = MakePropsAllTypes();
|
||||
TypedValueStore<> props = MakePropsAllTypes();
|
||||
|
||||
// iterate over all the props, plus one, what will return
|
||||
// the Null property, which must be incompatible with all
|
||||
@ -262,7 +262,7 @@ TEST(TypedValue, TypeIncompatibility) {
|
||||
* @param op The logical operation to test.
|
||||
*/
|
||||
void TestLogicalThrows(std::function<TypedValue(const TypedValue&, const TypedValue&)> op) {
|
||||
TypedValueStore props = MakePropsAllTypes();
|
||||
TypedValueStore<> props = MakePropsAllTypes();
|
||||
for (int i = 0; i < props.size() + 1; ++i) {
|
||||
auto p1 = props.at(i);
|
||||
for (int j = 0; j < props.size() + 1; ++j) {
|
||||
|
@ -6,8 +6,7 @@
|
||||
|
||||
#include "gtest/gtest.h"
|
||||
|
||||
#include "storage/model/typed_value.hpp"
|
||||
#include "storage/model/typed_value_store.hpp"
|
||||
#include "storage/typed_value_store.hpp"
|
||||
|
||||
using std::string;
|
||||
|
||||
@ -15,7 +14,7 @@ TEST(TypedValueStore, At) {
|
||||
|
||||
std::string some_string = "something";
|
||||
|
||||
TypedValueStore props;
|
||||
TypedValueStore<> props;
|
||||
EXPECT_EQ(TypedValue(props.at(0)).type_, TypedValue::Type::Null);
|
||||
props.set(0, some_string);
|
||||
EXPECT_EQ(TypedValue(props.at(0)).Value<string>(), some_string);
|
||||
@ -24,7 +23,7 @@ TEST(TypedValueStore, At) {
|
||||
}
|
||||
|
||||
TEST(TypedValueStore, AtNull) {
|
||||
TypedValueStore props;
|
||||
TypedValueStore<> props;
|
||||
EXPECT_EQ(props.at(0).type_, TypedValue::Type::Null);
|
||||
EXPECT_EQ(props.at(100).type_, TypedValue::Type::Null);
|
||||
|
||||
@ -36,7 +35,7 @@ TEST(TypedValueStore, AtNull) {
|
||||
|
||||
TEST(TypedValueStore, Remove) {
|
||||
// set some props
|
||||
TypedValueStore props;
|
||||
TypedValueStore<> props;
|
||||
props.set(11, "a");
|
||||
props.set(30, "b");
|
||||
EXPECT_NE(props.at(11).type_, TypedValue::Type::Null);
|
||||
@ -55,7 +54,7 @@ TEST(TypedValueStore, Remove) {
|
||||
}
|
||||
|
||||
TEST(TypedValueStore, Replace) {
|
||||
TypedValueStore props;
|
||||
TypedValueStore<> props;
|
||||
props.set(10, 42);
|
||||
EXPECT_EQ(props.at(10).Value<int>(), 42);
|
||||
props.set(10, 0.25f);
|
||||
@ -65,7 +64,7 @@ TEST(TypedValueStore, Replace) {
|
||||
|
||||
TEST(TypedValueStore, Size) {
|
||||
|
||||
TypedValueStore props;
|
||||
TypedValueStore<> props;
|
||||
EXPECT_EQ(props.size(), 0);
|
||||
|
||||
props.set(0, "something");
|
||||
@ -92,14 +91,14 @@ TEST(TypedValueStore, Accept) {
|
||||
int count_props = 0;
|
||||
int count_finish = 0;
|
||||
|
||||
auto handler = [&](const TypedValueStore::TKey key, const TypedValue& prop) {
|
||||
auto handler = [&](const uint32_t key, const TypedValue& prop) {
|
||||
count_props += 1;
|
||||
};
|
||||
auto finish = [&]() {
|
||||
count_finish += 1;
|
||||
};
|
||||
|
||||
TypedValueStore props;
|
||||
TypedValueStore<uint32_t> props;
|
||||
props.Accept(handler, finish);
|
||||
EXPECT_EQ(count_props, 0);
|
||||
EXPECT_EQ(count_finish, 1);
|
||||
|
Loading…
Reference in New Issue
Block a user