Major properties system and database accessor refactor: first stable state (compiles).

This commit is contained in:
florijan
2017-02-15 14:10:16 +01:00
parent 9e09186d30
commit 70a8b93b0b
69 changed files with 535 additions and 3324 deletions

View File

@@ -29,7 +29,7 @@ namespace bolt {
Bolt &bolt;
GraphDb &active_db();
GraphDbAccessor active_db();
Decoder decoder;
OutputStream output_stream{socket};

View File

@@ -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());

View File

@@ -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_;

View File

@@ -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;
};

View File

@@ -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;

View File

@@ -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

View File

@@ -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);

View File

@@ -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
};

View File

@@ -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;

View File

@@ -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;
};

View File

@@ -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;
};

View File

@@ -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;
}
};

View File

@@ -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;
};

View File

@@ -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"

View File

@@ -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;
};

View File

@@ -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;
};

View File

@@ -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;
}
}

View File

@@ -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;
};

View File

@@ -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;
};

View File

@@ -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;
};

View File

@@ -1,8 +0,0 @@
#pragma once
#include "includes.hpp"
auto transaction_begin_action = [](CypherStateData &,
const QueryActionData &) -> std::string {
return code_line(code::transaction_begin);
};

View File

@@ -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);
};

View File

@@ -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()); }
}
}

View File

@@ -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
};

View File

@@ -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;
}
}
};

View File

@@ -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));
}
};

View File

@@ -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;
};

View File

@@ -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;
};

View File

@@ -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.

View File

@@ -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))
{

View File

@@ -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;
};

View File

@@ -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:

View File

@@ -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_;
};

View File

@@ -20,5 +20,5 @@ public:
VertexAccessor to() const;
void remove();
// void remove();
};

View File

@@ -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);

View File

@@ -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_;
};

View File

@@ -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);
}

View File

@@ -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_;
};

View File

@@ -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
View 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;
}

View File

@@ -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_;
};

View File

@@ -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();
};

View File

@@ -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
}