Major properties and storage refactoring in progress. UNSTABLE STATE

This commit is contained in:
Florijan Stamenkovic 2017-02-11 07:51:02 +01:00 committed by florijan
parent ee523d5080
commit 9adf5699d9
22 changed files with 479 additions and 539 deletions

View File

@ -52,9 +52,9 @@ FILE(COPY ${include_dir}/transactions/engine.hpp DESTINATION ${build_include_dir
FILE(COPY ${include_dir}/transactions/transaction_store.hpp DESTINATION ${build_include_dir}/transactions) FILE(COPY ${include_dir}/transactions/transaction_store.hpp DESTINATION ${build_include_dir}/transactions)
FILE(COPY ${include_dir}/transactions/transaction_read.hpp DESTINATION ${build_include_dir}/transactions) FILE(COPY ${include_dir}/transactions/transaction_read.hpp DESTINATION ${build_include_dir}/transactions)
FILE(COPY ${include_dir}/storage/typed_value.hpp DESTINATION ${build_include_dir}/storage/model) FILE(COPY ${include_dir}/storage/typed_value.hpp DESTINATION ${build_include_dir}/storage)
FILE(COPY ${include_dir}/storage/typed_value_store.hpp DESTINATION ${build_include_dir}/storage/model) FILE(COPY ${include_dir}/storage/typed_value_store.hpp DESTINATION ${build_include_dir}/storage)
FILE(COPY ${include_dir}/storage/typed_value_utils.hpp DESTINATION ${build_include_dir}/storage/model) FILE(COPY ${include_dir}/storage/typed_value_utils.hpp DESTINATION ${build_include_dir}/storage)
FILE(COPY ${include_dir}/storage/garbage/delete_sensitive.hpp DESTINATION ${build_include_dir}/storage/garbage) FILE(COPY ${include_dir}/storage/garbage/delete_sensitive.hpp DESTINATION ${build_include_dir}/storage/garbage)
FILE(COPY ${include_dir}/storage/garbage/garbage.hpp DESTINATION ${build_include_dir}/storage/garbage) FILE(COPY ${include_dir}/storage/garbage/garbage.hpp DESTINATION ${build_include_dir}/storage/garbage)

View File

@ -6,25 +6,14 @@
#include "storage/edge_accessor.hpp" #include "storage/edge_accessor.hpp"
#include "storage/vertex_accessor.hpp" #include "storage/vertex_accessor.hpp"
#include "storage/edge_type/edge_type.hpp" #include "storage/typed_value.hpp"
#include "storage/label/label.hpp"
#include "storage/model/properties/all.hpp"
#include "storage/model/properties/properties.hpp"
#include "storage/vertex_record.hpp"
namespace bolt namespace bolt {
{
template <class Stream> template<class Stream>
class BoltSerializer class BoltSerializer {
{
friend class Property;
// TODO: here shoud be friend but it doesn't work public:
// template <class Handler>
// friend void accept(const Property &property, Handler &h);
public:
BoltSerializer(Stream &stream) : encoder(stream) {} BoltSerializer(Stream &stream) : encoder(stream) {}
/** Serializes the vertex accessor into the packstream format /** Serializes the vertex accessor into the packstream format
@ -36,38 +25,9 @@ public:
* } * }
* *
*/ */
void write(const VertexAccessor &vertex) void write(const VertexAccessor &vertex);
{
// write signatures for the node struct and node data type
encoder.write_struct_header(3);
encoder.write(underlying_cast(pack::Node));
// IMPORTANT: here we write a hardcorded 0 because we don't /** Serializes the edge accessor into the packstream format
// use internal IDs, but need to give something to Bolt
// note that OpenCypther has no id(x) function, so the client
// should not be able to do anything with this value anyway
encoder.write_integer(0);
// write the list of labels
auto labels = vertex.labels();
encoder.write_list_header(labels.size());
for (auto &label : labels)
encoder.write_string(label.get());
// write the property map
auto props = vertex.properties();
encoder.write_map_header(props.size());
for (auto &prop : props) {
write(prop.key.family_name());
prop.accept(*this);
}
}
/** Serializes the vertex accessor into the packstream format
* *
* struct[size = 5] Edge [signature = 0x52] { * struct[size = 5] Edge [signature = 0x52] {
* Integer edge_id; // IMPORTANT: always 0 since we don't do IDs * Integer edge_id; // IMPORTANT: always 0 since we don't do IDs
@ -80,57 +40,18 @@ public:
*/ */
void write(const EdgeAccessor &edge); void write(const EdgeAccessor &edge);
void write_null() { encoder.write_null(); } // TODO document
void write_failure(const std::map<std::string, std::string> &data);
void write(const Null &) { encoder.write_null(); } /**
* Writes a TypedValue (typically a property value in the edge or vertex).
*
* @param value The value to write.
*/
void write(const TypedValue& value);
void write(const Bool &prop) { encoder.write_bool(prop.value()); } protected:
void write(const Float &prop) { encoder.write_double(prop.value()); }
void write(const Double &prop) { encoder.write_double(prop.value()); }
void write(const Int32 &prop) { encoder.write_integer(prop.value()); }
void write(const Int64 &prop) { encoder.write_integer(prop.value()); }
void write(const String &value) { encoder.write_string(value.value()); }
// Not yet implemented
void write(const ArrayBool &) { assert(false); }
// Not yet implemented
void write(const ArrayInt32 &) { assert(false); }
// Not yet implemented
void write(const ArrayInt64 &) { assert(false); }
// Not yet implemented
void write(const ArrayFloat &) { assert(false); }
// Not yet implemented
void write(const ArrayDouble &) { assert(false); }
// Not yet implemented
void write(const ArrayString &) { assert(false); }
void write_failure(const std::map<std::string, std::string> &data)
{
encoder.message_failure();
encoder.write_map_header(data.size());
for (auto const &kv : data) {
write(kv.first);
write(kv.second);
}
}
template <class T>
void handle(const T &prop)
{
write(prop);
}
protected:
Stream &encoder; Stream &encoder;
};
};
} }

View File

@ -7,83 +7,73 @@
#include "logging/default.hpp" #include "logging/default.hpp"
namespace bolt namespace bolt {
{
/** /**
* compiled queries have to use this class in order to return results * compiled queries have to use this class in order to return results
* query code should not know about bolt protocol * query code should not know about bolt protocol
*/ */
template <class Socket> template<class Socket>
class RecordStream class RecordStream {
{ public:
public: RecordStream(Socket &socket) : socket(socket) {
RecordStream(Socket &socket) : socket(socket) logger = logging::log->logger("Record Stream");
{
logger = logging::log->logger("Record Stream");
} }
~RecordStream() = default; ~RecordStream() = default;
// TODO: create apstract methods that are not bolt specific --------------- // TODO: create apstract methods that are not bolt specific ---------------
void write_success() void write_success() {
{ logger.trace("write_success");
logger.trace("write_success"); bolt_encoder.message_success();
bolt_encoder.message_success();
} }
void write_success_empty() void write_success_empty() {
{ logger.trace("write_success_empty");
logger.trace("write_success_empty"); bolt_encoder.message_success_empty();
bolt_encoder.message_success_empty();
} }
void write_ignored() void write_ignored() {
{ logger.trace("write_ignored");
logger.trace("write_ignored"); bolt_encoder.message_ignored();
bolt_encoder.message_ignored();
} }
void write_empty_fields() void write_empty_fields() {
{ bolt_encoder.message_success();
bolt_encoder.message_success(); bolt_encoder.write_map_header(1);
bolt_encoder.write_map_header(1); bolt_encoder.write_string("fields");
bolt_encoder.write_string("fields"); write_list_header(0);
write_list_header(0); chunk();
chunk();
} }
void write_fields(const std::vector<std::string> &fields) void write_fields(const std::vector<std::string> &fields) {
{ // TODO: that should be one level below?
// TODO: that should be one level below? bolt_encoder.message_success();
bolt_encoder.message_success();
bolt_encoder.write_map_header(1); bolt_encoder.write_map_header(1);
bolt_encoder.write_string("fields"); bolt_encoder.write_string("fields");
write_list_header(fields.size()); write_list_header(fields.size());
for (auto &name : fields) { for (auto &name : fields) {
bolt_encoder.write_string(name); bolt_encoder.write_string(name);
} }
chunk(); chunk();
send(); send();
} }
void write_field(const std::string &field) void write_field(const std::string &field) {
{ bolt_encoder.message_success();
bolt_encoder.message_success(); bolt_encoder.write_map_header(1);
bolt_encoder.write_map_header(1); bolt_encoder.write_string("fields");
bolt_encoder.write_string("fields"); write_list_header(1);
write_list_header(1); bolt_encoder.write_string(field);
bolt_encoder.write_string(field); chunk();
chunk(); send();
send();
} }
void write_list_header(size_t size) void write_list_header(size_t size) {
{ bolt_encoder.write_list_header(size);
bolt_encoder.write_list_header(size);
} }
void write_record() { bolt_encoder.message_record(); } void write_record() { bolt_encoder.message_record(); }
@ -92,94 +82,73 @@ public:
// TODO: write whole implementation (currently, only type is supported) // TODO: write whole implementation (currently, only type is supported)
// { "stats": { "nodes created": 1, "properties set": 1}, // { "stats": { "nodes created": 1, "properties set": 1},
// "type": "r" | "rw" | ... // "type": "r" | "rw" | ...
void write_meta(const std::string &type) void write_meta(const std::string &type) {
{ bolt_encoder.message_success();
bolt_encoder.message_success(); bolt_encoder.write_map_header(1);
bolt_encoder.write_map_header(1); bolt_encoder.write_string("type");
bolt_encoder.write_string("type"); bolt_encoder.write_string(type);
bolt_encoder.write_string(type); chunk();
chunk();
} }
void write_failure(const std::map<std::string, std::string> &data) void write_failure(const std::map<std::string, std::string> &data) {
{ serializer.write_failure(data);
serializer.write_failure(data); chunk();
chunk();
} }
void write_count(const size_t count) void write_count(const size_t count) {
{ write_record();
write_record(); write_list_header(1);
write_list_header(1); write(count);
write(Int64(count)); chunk();
chunk();
} }
void write(const VertexAccessor &vertex) { serializer.write(vertex); } void write(const VertexAccessor &vertex) { serializer.write(vertex); }
void write_vertex_record(const VertexAccessor& va)
{ void write_vertex_record(const VertexAccessor &va) {
write_record(); write_record();
write_list_header(1); write_list_header(1);
write(va); write(va);
chunk(); chunk();
} }
void write(const EdgeAccessor &edge) { serializer.write(edge); } void write(const EdgeAccessor &edge) { serializer.write(edge); }
void write_edge_record(const EdgeAccessor& ea)
{ void write_edge_record(const EdgeAccessor &ea) {
write_record(); write_record();
write_list_header(1); write_list_header(1);
write(ea); write(ea);
chunk(); chunk();
} }
void write(const StoredProperty<TypeGroupEdge> &prop) void write(const TypedValue& value) {
{ serializer.write(value);
prop.accept(serializer);
} }
void write(const StoredProperty<TypeGroupVertex> &prop)
{
prop.accept(serializer);
}
void write(const Null &prop) { serializer.write(prop); }
void write(const Bool &prop) { serializer.write(prop); }
void write(const Float &prop) { serializer.write(prop); }
void write(const Int32 &prop) { serializer.write(prop); }
void write(const Int64 &prop) { serializer.write(prop); }
void write(const Double &prop) { serializer.write(prop); }
void write(const String &prop) { serializer.write(prop); }
void write(const ArrayBool &prop) { serializer.write(prop); }
void write(const ArrayInt32 &prop) { serializer.write(prop); }
void write(const ArrayInt64 &prop) { serializer.write(prop); }
void write(const ArrayFloat &prop) { serializer.write(prop); }
void write(const ArrayDouble &prop) { serializer.write(prop); }
void write(const ArrayString &prop) { serializer.write(prop); }
void send() { chunked_buffer.flush(); } void send() { chunked_buffer.flush(); }
void chunk() { chunked_encoder.write_chunk(); } void chunk() { chunked_encoder.write_chunk(); }
void _write_test() // TODO WTF is this test doing here?
{ void _write_test() {
logger.trace("write_test"); logger.trace("write_test");
write_fields({{"name"}}); write_fields({{"name"}});
write_record(); write_record();
write_list_header(1); write_list_header(1);
write(String("max")); bolt_encoder.write("max");
write_record(); write_record();
write_list_header(1); write_list_header(1);
write(String("paul")); bolt_encoder.write("paul");
write_success_empty(); write_success_empty();
} }
protected: protected:
Logger logger; Logger logger;
private: private:
using socket_t = SocketStream<Socket>; using socket_t = SocketStream<Socket>;
using buffer_t = ChunkedBuffer<socket_t>; using buffer_t = ChunkedBuffer<socket_t>;
using chunked_encoder_t = ChunkedEncoder<buffer_t>; using chunked_encoder_t = ChunkedEncoder<buffer_t>;
@ -191,5 +160,5 @@ private:
chunked_encoder_t chunked_encoder{chunked_buffer}; chunked_encoder_t chunked_encoder{chunked_buffer};
bolt_encoder_t bolt_encoder{chunked_encoder}; bolt_encoder_t bolt_encoder{chunked_encoder};
bolt_serializer_t serializer{bolt_encoder}; bolt_serializer_t serializer{bolt_encoder};
}; };
} }

View File

@ -12,12 +12,10 @@
#include "logging/default.hpp" #include "logging/default.hpp"
namespace bolt namespace bolt {
{
class Session : public io::tcp::Stream<io::Socket> class Session : public io::tcp::Stream<io::Socket> {
{ public:
public:
using Decoder = BoltDecoder; using Decoder = BoltDecoder;
using OutputStream = communication::OutputStream; using OutputStream = communication::OutputStream;
@ -26,10 +24,12 @@ public:
bool alive() const; bool alive() const;
void execute(const byte *data, size_t len); void execute(const byte *data, size_t len);
void close(); void close();
Bolt &bolt; Bolt &bolt;
Db &active_db();
GraphDb &active_db();
Decoder decoder; Decoder decoder;
OutputStream output_stream{socket}; OutputStream output_stream{socket};
@ -37,7 +37,7 @@ public:
bool connected{false}; bool connected{false};
State *state; State *state;
protected: protected:
Logger logger; Logger logger;
}; };
} }

View File

@ -4,6 +4,7 @@
#include "transactions/engine.hpp" #include "transactions/engine.hpp"
#include "mvcc/version_list.hpp" #include "mvcc/version_list.hpp"
#include "utils/pass_key.hpp" #include "utils/pass_key.hpp"
#include "data_structures/concurrent/concurrent_set.hpp"
#include "storage/unique_object_store.hpp" #include "storage/unique_object_store.hpp"
// forward declaring Edge and Vertex because they use // forward declaring Edge and Vertex because they use
@ -26,9 +27,9 @@ class GraphDb {
public: public:
// definitions for what data types are used for a Label, Property, EdgeType // definitions for what data types are used for a Label, Property, EdgeType
using Label = uint32_t; using Label = std::string*;
using EdgeType = uint32_t; using EdgeType = std::string*;
using Property = uint32_t; using Property = std::string*;
/** /**
* This constructor will create a database with the name "default" * This constructor will create a database with the name "default"
@ -83,7 +84,7 @@ public:
SkipList<mvcc::VersionList<Vertex>*> vertices_; SkipList<mvcc::VersionList<Vertex>*> vertices_;
// unique object stores // unique object stores
UniqueObjectStore<std::string, Label> labels_; ConcurrentSet<std::string> labels_;
UniqueObjectStore<std::string, EdgeType> edge_types_; ConcurrentSet<std::string> edge_types_;
UniqueObjectStore<std::string, Property> properties_; ConcurrentSet<std::string> properties_;
}; };

View File

@ -3,14 +3,14 @@
// Created by Florijan Stamenkovic on 03.02.17. // Created by Florijan Stamenkovic on 03.02.17.
// //
#pragma #pragma once
#include "graph_db.hpp" #include "graph_db.hpp"
#include "transactions/transaction.hpp" #include "transactions/transaction.hpp"
class GraphDbAccessor { class GraphDbAccessor {
GraphDbAccessor(GraphDb& db) : db_(db), transaction_(db.tx_engine.begin()) {} GraphDbAccessor(GraphDb& db);
public: public:
/** /**
@ -36,24 +36,48 @@ public:
*/ */
GraphDb::Label label(const std::string& label_name); GraphDb::Label label(const std::string& label_name);
/**
* Obtains the label name (a string) for the given label.
*
* @param label a Label.
* @return See above.
*/
std::string& label_name(const GraphDb::Label label) const;
/** /**
* Obtains the EdgeType for it's name. * Obtains the EdgeType for it's name.
* @return See above. * @return See above.
*/ */
GraphDb::EdgeType edge_type(const std::string& edge_type_name); GraphDb::EdgeType edge_type(const std::string& edge_type_name);
/**
* Obtains the edge type name (a string) for the given edge type.
*
* @param edge_type an EdgeType.
* @return See above.
*/
std::string& edge_type_name(const GraphDb::EdgeType edge_type) const;
/** /**
* Obtains the Property for it's name. * Obtains the Property for it's name.
* @return See above. * @return See above.
*/ */
GraphDb::Property property(const std::string& property_name); GraphDb::Property property(const std::string& property_name);
/**
* Obtains the property name (a string) for the given property.
*
* @param property a Property.
* @return See above.
*/
std::string& property_name(const GraphDb::Property property) const;
/** The current transaction */ /** The current transaction */
tx::Transaction const transaction_; tx::Transaction transaction_;
private: private:
GraphDb& db_; GraphDb& db_;
// for privileged access to some RecordAccessor functionality (and similar) // for privileged access to some RecordAccessor functionality (and similar)
const PassKey<GraphDb> pass_key; const PassKey<GraphDbAccessor> pass_key;
}; };

View File

@ -1,20 +1,19 @@
#pragma once #pragma once
#include "communication/communication.hpp" #include "communication/communication.hpp"
#include "database/graph_db.hpp" #include "database/graph_db_accessor.hpp"
#include "database/db_accessor.hpp"
#include "query/strip/stripped.hpp" #include "query/strip/stripped.hpp"
template <typename Stream> template<typename Stream>
class IPlanCPU class IPlanCPU {
{
public: public:
virtual bool run(Db &db, plan_args_t &args, Stream &stream) = 0; virtual bool run(GraphDbAccessor &db_accessor, TypedValueStore<>& args, Stream &stream) = 0;
virtual ~IPlanCPU() {}
virtual ~IPlanCPU() {}
}; };
template <typename Stream> template<typename Stream>
using produce_t = IPlanCPU<Stream> *(*)(); using produce_t = IPlanCPU<Stream> *(*)();
template <typename Stream> template<typename Stream>
using destruct_t = void (*)(IPlanCPU<Stream> *); using destruct_t = void (*)(IPlanCPU<Stream> *);

View File

@ -7,7 +7,6 @@
#include "config/config.hpp" #include "config/config.hpp"
#include "logging/default.hpp" #include "logging/default.hpp"
#include "query/backend/cpp_old/cypher.hpp"
#include "query/dynamic_lib.hpp" #include "query/dynamic_lib.hpp"
#include "query/frontend/cypher.hpp" #include "query/frontend/cypher.hpp"
#include "query/plan/compiler.hpp" #include "query/plan/compiler.hpp"
@ -99,9 +98,7 @@ private:
QueryPreprocessor preprocessor; QueryPreprocessor preprocessor;
// TODO: compile time switch between frontends and backends // TODO: compile time switch between frontends and backends
using frontend_t = cypher::Frontend; PlanGenerator<cypher::Frontend, CypherBackend<Stream>> plan_generator;
using backend_t = CypherBackend<Stream>;
PlanGenerator<frontend_t, backend_t> plan_generator;
PlanCompiler plan_compiler; PlanCompiler plan_compiler;

View File

@ -2,12 +2,7 @@
#include <vector> #include <vector>
#include "storage/model/properties/property.hpp" #include "storage/typed_value_store.hpp"
/*
* Query Plan Arguments Type
*/
using plan_args_t = std::vector<Property>;
/* /*
* QueryStripped contains: * QueryStripped contains:
@ -15,18 +10,17 @@ using plan_args_t = std::vector<Property>;
* * plan arguments stripped from query * * plan arguments stripped from query
* * hash of stripped query * * hash of stripped query
*/ */
struct QueryStripped struct QueryStripped {
{
QueryStripped(const std::string &&query, plan_args_t &&arguments,
uint64_t hash)
: query(std::forward<const std::string>(query)),
arguments(std::forward<plan_args_t>(arguments)), hash(hash)
{
}
QueryStripped(QueryStripped &other) = delete;
QueryStripped(QueryStripped &&other) = default;
std::string query; QueryStripped(const std::string &&query, const TypedValueStore<> &&arguments, uint64_t hash)
plan_args_t arguments; : query(std::forward<const std::string>(query)),
uint64_t hash; arguments(std::forward<const TypedValueStore<>>(arguments)),
hash(hash) {}
QueryStripped(QueryStripped &other) = delete;
QueryStripped(QueryStripped &&other) = default;
std::string query;
TypedValueStore<> arguments;
uint64_t hash;
}; };

View File

@ -10,136 +10,114 @@
#include "logging/loggable.hpp" #include "logging/loggable.hpp"
#include "query/language/cypher/tokenizer/cypher_lexer.hpp" #include "query/language/cypher/tokenizer/cypher_lexer.hpp"
#include "query/strip/stripped.hpp" #include "query/strip/stripped.hpp"
#include "storage/model/properties/all.hpp" #include "storage/typed_value_store.hpp"
#include "utils/hashing/fnv.hpp" #include "utils/hashing/fnv.hpp"
#include "utils/string/transform.hpp" #include "utils/string/transform.hpp"
#include "utils/variadic/variadic.hpp" #include "utils/variadic/variadic.hpp"
// TODO: Maybe std::move(v) is faster, but it must be cheked for validity.
template <class T, class V>
void store_query_param(plan_args_t &arguments, V &&v)
{
arguments.emplace_back(Property(T(std::move(v)), T::type));
}
template <typename... Ts> template<typename... Ts>
class QueryStripper : public Loggable class QueryStripper : public Loggable {
{
public: public:
QueryStripper(Ts &&... strip_types) QueryStripper(Ts &&... strip_types)
: Loggable("QueryStripper"), : Loggable("QueryStripper"),
strip_types(std::make_tuple(std::forward<Ts>(strip_types)...)), strip_types(std::make_tuple(std::forward<Ts>(strip_types)...)),
lexer(std::make_unique<CypherLexer>()) lexer(std::make_unique<CypherLexer>()) {
{ }
}
QueryStripper(QueryStripper &other) = delete; QueryStripper(QueryStripper &other) = delete;
QueryStripper(QueryStripper &&other) QueryStripper(QueryStripper &&other)
: Loggable("QueryStripper"), strip_types(std::move(other.strip_types)), : Loggable("QueryStripper"), strip_types(std::move(other.strip_types)),
lexer(std::move(other.lexer)) lexer(std::move(other.lexer)) {
{ }
}
auto strip_space(const std::string &query) { return strip(query, " "); } auto strip_space(const std::string &query) { return strip(query, " "); }
auto strip(const std::string &query, const std::string &separator = "") auto strip(const std::string &query, const std::string &separator = "") {
{ // -------------------------------------------------------------------
// ------------------------------------------------------------------- // TODO: write speed tests and then optimize, because this
// TODO: write speed tests and then optimize, because this // function is called before every query execution !
// function is called before every query execution ! // -------------------------------------------------------------------
// -------------------------------------------------------------------
// TODO write this more optimal (resplace string // TODO write this more optimal (resplace string
// concatenation with something smarter) // concatenation with something smarter)
// TODO: in place substring replacement // TODO: in place substring replacement
auto tokenizer = lexer->tokenize(query); auto tokenizer = lexer->tokenize(query);
// TMP size of supported token types // TMP size of supported token types
constexpr auto size = std::tuple_size<decltype(strip_types)>::value; constexpr auto size = std::tuple_size<decltype(strip_types)>::value;
int counter = 0; TypedValueStore<> stripped_arguments;
plan_args_t stripped_arguments; std::string stripped_query;
std::string stripped_query; stripped_query.reserve(query.size());
stripped_query.reserve(query.size());
while (auto token = tokenizer.lookup()) int counter = 0; // how many arguments have we processed so far
{ while (auto token = tokenizer.lookup()) {
if (_or(token.id, strip_types, std::make_index_sequence<size>{})) if (_or(token.id, strip_types, std::make_index_sequence < size > {})) {
{ switch (token.id) {
auto index = counter++; case TK_LONG:
switch (token.id) stripped_arguments.set(counter, std::stoi(token.value));
{ break;
case TK_LONG: case TK_STR:
store_query_param<Int64>(stripped_arguments, // TODO: remove quotes view lexertl
std::stol(token.value)); token.value.erase(0, 1);
break; token.value.erase(token.value.length() - 1, 1);
case TK_STR: // TODO: remove
// TODO: remove quotes view lexertl stripped_arguments.set(counter, token.value);
token.value.erase(0, 1); break;
token.value.erase(token.value.length() - 1, 1); case TK_BOOL: {
// TODO: remove bool value = token.value[0] == 'T' || token.value[0] == 't';
store_query_param<String>(stripped_arguments, token.value); stripped_arguments.set(counter, value);
break; break;
case TK_BOOL: }
{ case TK_FLOAT:
bool value = token.value[0] == 'T' || token.value[0] == 't'; stripped_arguments.set(counter, std::stof(token.value));
store_query_param<Bool>(stripped_arguments, value); break;
break; default:
} // TODO: other properties
case TK_FLOAT: assert(false);
store_query_param<Float>(stripped_arguments,
std::stof(token.value));
break;
default:
// TODO: other properties
assert(false);
}
stripped_query += std::to_string(index) + separator;
}
else
{
// if token is keyword then lowercase because query hash
// should be the same
// TODO: probably we shoud do the lowercase before
// or during the tokenization (SPEED TESTS)
if (token.id == TK_OR || token.id == TK_AND ||
token.id == TK_NOT || token.id == TK_WITH ||
token.id == TK_SET || token.id == TK_CREATE ||
token.id == TK_MERGE || token.id == TK_MATCH ||
token.id == TK_DELETE || token.id == TK_DETACH ||
token.id == TK_WHERE || token.id == TK_RETURN ||
token.id == TK_DISTINCT || token.id == TK_COUNT ||
token.id == TK_LABELS)
{
std::transform(token.value.begin(), token.value.end(),
token.value.begin(), ::tolower);
}
stripped_query += token.value + separator;
}
} }
stripped_query += std::to_string(counter++) + separator;
// TODO: hash function should be a template parameter } else {
auto hash = fnv(stripped_query); // if token is keyword then lowercase because query hash
return QueryStripped(std::move(stripped_query), // should be the same
std::move(stripped_arguments), hash); // TODO: probably we shoud do the lowercase before
// or during the tokenization (SPEED TESTS)
if (token.id == TK_OR || token.id == TK_AND ||
token.id == TK_NOT || token.id == TK_WITH ||
token.id == TK_SET || token.id == TK_CREATE ||
token.id == TK_MERGE || token.id == TK_MATCH ||
token.id == TK_DELETE || token.id == TK_DETACH ||
token.id == TK_WHERE || token.id == TK_RETURN ||
token.id == TK_DISTINCT || token.id == TK_COUNT ||
token.id == TK_LABELS) {
std::transform(token.value.begin(), token.value.end(),
token.value.begin(), ::tolower);
}
stripped_query += token.value + separator;
}
} }
// TODO: hash function should be a template parameter
auto hash = fnv(stripped_query);
return QueryStripped(std::move(stripped_query),
std::move(stripped_arguments), hash);
}
private: private:
std::tuple<Ts...> strip_types; std::tuple<Ts...> strip_types;
CypherLexer::uptr lexer; CypherLexer::uptr lexer;
template <typename Value, typename Tuple, std::size_t... index> template<typename Value, typename Tuple, std::size_t... index>
bool _or(Value &&value, Tuple &&tuple, std::index_sequence<index...>) bool _or(Value &&value, Tuple &&tuple, std::index_sequence<index...>) {
{ return utils::or_vargs(std::forward<Value>(value),
return utils::or_vargs(std::forward<Value>(value), std::get<index>(std::forward<Tuple>(tuple))...);
std::get<index>(std::forward<Tuple>(tuple))...); }
}
}; };
template <typename... Ts> template<typename... Ts>
decltype(auto) make_query_stripper(Ts &&... ts) decltype(auto) make_query_stripper(Ts &&... ts) {
{ return QueryStripper<Ts...>(std::forward<Ts>(ts)...);
return QueryStripper<Ts...>(std::forward<Ts>(ts)...);
} }

View File

@ -7,10 +7,6 @@
#include "fmt/format.h" #include "fmt/format.h"
#include "logging/default.hpp" #include "logging/default.hpp"
#include "storage/model/properties/properties.hpp"
#include "storage/model/properties/property.hpp"
#include "storage/model/properties/json_writer.hpp"
#include "utils/types/byte.hpp"
#include "utils/exceptions/basic_exception.hpp" #include "utils/exceptions/basic_exception.hpp"
using std::cout; using std::cout;
@ -20,76 +16,54 @@ using std::endl;
// headers because it will create a unique namespace for each compilation unit // headers because it will create a unique namespace for each compilation unit
// http://stackoverflow.com/questions/2727582/multiple-definition-in-header-file // http://stackoverflow.com/questions/2727582/multiple-definition-in-header-file
// but sometimes that might be a problem // but sometimes that might be a problem
namespace namespace {
{
class CodeLineFormatException : public BasicException class CodeLineFormatException : public BasicException {
{ public:
public:
using BasicException::BasicException; using BasicException::BasicException;
}; };
template <typename... Args> template<typename... Args>
std::string format(const std::string &format_str, const Args &... args) std::string format(const std::string &format_str, const Args &... args) {
{
return fmt::format(format_str, args...); return fmt::format(format_str, args...);
} }
template <typename... Args> template<typename... Args>
std::string code_line(const std::string &format_str, const Args &... args) std::string code_line(const std::string &format_str, const Args &... args) {
{
try { try {
return "\t" + format(format_str, args...) + "\n"; return "\t" + format(format_str, args...) + "\n";
} catch (std::runtime_error &e) { } catch (std::runtime_error &e) {
throw CodeLineFormatException(std::string(e.what()) + " " + format_str); throw CodeLineFormatException(std::string(e.what()) + " " + format_str);
} }
} }
using name_properties_t = std::vector<std::pair<std::string, Property>>; class CoutSocket {
using indices_t = std::map<std::string, long>; public:
auto query_properties(indices_t &indices, properties_t &values)
{
name_properties_t properties;
for (auto &property_index : indices) {
properties.push_back(
std::make_pair(std::move(property_index.first),
std::move(values[property_index.second])));
}
return properties;
}
class CoutSocket
{
public:
CoutSocket() : logger(logging::log->logger("Cout Socket")) {} CoutSocket() : logger(logging::log->logger("Cout Socket")) {}
int write(const std::string &str) int write(const std::string &str) {
{ logger.info(str);
logger.info(str); return str.size();
return str.size();
} }
int write(const char *data, size_t len) int write(const char *data, size_t len) {
{ logger.info(std::string(data, len));
logger.info(std::string(data, len)); return len;
return len;
} }
int write(const byte *data, size_t len) int write(const byte *data, size_t len) {
{ std::stringstream ss;
std::stringstream ss; for (int i = 0; i < len; i++) {
for (int i = 0; i < len; i++) { ss << data[i];
ss << data[i]; }
} std::string output(ss.str());
std::string output(ss.str()); cout << output << endl;
cout << output << endl; logger.info(output);
logger.info(output); return len;
return len;
} }
private: private:
Logger logger; Logger logger;
}; };
} }

View File

@ -13,5 +13,5 @@ public:
mvcc::VersionList<Vertex>* from_; mvcc::VersionList<Vertex>* from_;
mvcc::VersionList<Vertex>* to_; mvcc::VersionList<Vertex>* to_;
GraphDb::EdgeType edge_type_; GraphDb::EdgeType edge_type_;
TypedValueStore properties_; TypedValueStore<GraphDb::Property> properties_;
}; };

View File

@ -1,49 +1,37 @@
#pragma once #pragma once
#include "mvcc/version_list.hpp" #include "mvcc/version_list.hpp"
#include "transactions/transaction.hpp"
#include "storage/typed_value.hpp" #include "storage/typed_value.hpp"
#include "database/graph_db.hpp" #include "database/graph_db.hpp"
#include "database/graph_db_accessor.hpp" #include "database/graph_db_accessor.hpp"
#include "utils/pass_key.hpp" #include "utils/pass_key.hpp"
template <typename TRecord, typename TDerived> template<typename TRecord, typename TDerived>
class RecordAccessor { class RecordAccessor {
public: public:
RecordAccessor(mvcc::VersionList<TRecord>* vlist, tx::Transaction &trans) RecordAccessor(mvcc::VersionList<TRecord> *vlist, GraphDbAccessor *db_accessor)
: vlist_(vlist), trans_(trans) { : vlist_(vlist), db_accessor_(db_accessor) {
record_ = vlist->find(trans_); record_ = vlist->find(db_accessor->transaction_);
} }
/** template<typename TValue>
* Indicates if this record is visible to the current transaction. void PropsSet(GraphDb::Property key, TValue value) {
* update()->props_.set(key, value);
* @return
*/
bool is_visible() const {
return record_ != nullptr;
} }
TypedValue at(GraphDb::Property key) const { size_t PropsErase(GraphDb::Property key) {
return record_->props_.at(key); return update()->props_.erase(key);
} }
template <typename TValue> const TypedValueStore<GraphDb::Property> &Properties() const {
void set(GraphDb::Property key, TValue value) { return view().properties_;
update();
record_->props_.set(key, value);
} }
size_t erase(GraphDb::Property key) { void PropertiesAccept(std::function<void(const GraphDb::Property key, const TypedValue &prop)> handler,
update(); std::function<void()> finish = {}) const {
return record_->props_.erase(key); view()->props_.Accept(handler, finish);
}
void Accept(std::function<void(const GraphDb::Property key, const TypedValue& prop)> handler,
std::function<void()> finish = {}) const {
record_->props_.Accept(handler, finish);
} }
// Assumes same transaction // Assumes same transaction
@ -64,29 +52,57 @@ public:
* @param pass_key Ignored. * @param pass_key Ignored.
* @return The version list of this accessor. * @return The version list of this accessor.
*/ */
mvcc::VersionList<TRecord>* vlist(PassKey<GraphDbAccessor> pass_key) const { mvcc::VersionList<TRecord> *vlist(PassKey<GraphDbAccessor> pass_key) const {
return vlist_; return vlist_;
} }
/**
* Returns a GraphDB accessor of this record accessor.
*
* @return
*/
const GraphDbAccessor &db_accessor() const {
return db_accessor_;
}
protected: protected:
/** /**
* Ensures this accessor is fit for updating functions. * Returns the update-ready version of the record.
* *
* IMPORTANT: This function should be called from any * @return See above.
* method that will change the record (in terms of the
* property graph data).
*/ */
void update() { TRecord *update() {
// TODO consider renaming this to something more indicative // TODO consider renaming this to something more indicative
// of the underlying MVCC functionality (like "new_version" or so) // of the underlying MVCC functionality (like "new_version" or so)
if (record_->is_visible_write(trans_))
return; if (!record_->is_visible_write(db_accessor_->transaction_))
else record_ = vlist_->update(db_accessor_->transaction_);
record_ = vlist_->update(trans_);
return record_;
} }
mvcc::VersionList<TRecord>* vlist_; /**
tx::Transaction& trans_; * Returns a version of the record that is only for viewing.
TRecord* record_; *
* @return See above.
*/
const TRecord *view() const {
return record_;
}
// The record (edge or vertex) this accessor provides access to.
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_;
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.
*/
TRecord *record_;
}; };

View File

@ -10,14 +10,14 @@
* using a key of type Properties::TKey. * using a key of type Properties::TKey.
* *
* The underlying implementation is not necessarily std::map. * The underlying implementation is not necessarily std::map.
*
* @tparam TKey The type of key used in this value store.
*/ */
template <typename TKey = uint32_t>
class TypedValueStore { class TypedValueStore {
public: public:
using sptr = std::shared_ptr<TypedValueStore>; 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. * Returns a TypedValue (by reference) at the given key.
* If the key does not exist, the Null property is returned. * If the key does not exist, the Null property is returned.
@ -65,7 +65,7 @@ public:
/** /**
* @return The number of Properties in this collection. * @return The number of Properties in this collection.
*/ */
size_t size(); size_t size() const;
/** /**

View File

@ -14,5 +14,5 @@ public:
std::vector<mvcc::VersionList<Edge> *> out_; std::vector<mvcc::VersionList<Edge> *> out_;
std::vector<mvcc::VersionList<Edge> *> in_; std::vector<mvcc::VersionList<Edge> *> in_;
std::set<GraphDb::Label> labels_; std::set<GraphDb::Label> labels_;
TypedValueStore properties_; TypedValueStore<GraphDb::Property> properties_;
}; };

View File

@ -20,7 +20,7 @@ class Transaction : public TransactionRead
public: public:
Transaction(const Id &id, const Snapshot<Id> &snapshot, Engine &engine); Transaction(const Id &id, const Snapshot<Id> &snapshot, Engine &engine);
Transaction(const Transaction &) = delete; Transaction(const Transaction &) = delete;
Transaction(Transaction &&) = delete; Transaction(Transaction &&) = default;
// Returns copy of transaction_read // Returns copy of transaction_read
TransactionRead transaction_read(); TransactionRead transaction_read();

View File

@ -5,32 +5,92 @@
#include "communication/bolt/v1/transport/socket_stream.hpp" #include "communication/bolt/v1/transport/socket_stream.hpp"
#include "io/network/socket.hpp" #include "io/network/socket.hpp"
template <class Stream> template<class Stream>
void bolt::BoltSerializer<Stream>::write(const EdgeAccessor &edge) void bolt::BoltSerializer<Stream>::write(const VertexAccessor &vertex) {
{
// write signatures for the edge struct and edge data type
encoder.write_struct_header(5);
encoder.write(underlying_cast(pack::Relationship));
// write the identifier for the node // write signatures for the node struct and node data type
encoder.write_integer(edge.id()); encoder.write_struct_header(3);
encoder.write(underlying_cast(pack::Node));
encoder.write_integer(edge.from().id()); // IMPORTANT: here we write a hardcoded 0 because we don't
encoder.write_integer(edge.to().id()); // use internal IDs, but need to give something to Bolt
// note that OpenCypher has no id(x) function, so the client
// should not be able to do anything with this value anyway
encoder.write_integer(0); // uID
// write the type of the edge // write the list of labels
encoder.write_string(edge.edge_type()); auto labels = vertex.labels();
encoder.write_list_header(labels.size());
for (auto label : labels)
encoder.write_string(vertex.db_accessor().label_name(label));
// write the property map // write the properties
auto props = edge.properties(); const TypedValueStore &props = vertex.Properties();
encoder.write_map_header(props.size());
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));
for (auto &prop : props) { write(value);
write(prop.key.family_name()); });
prop.accept(*this);
}
} }
template class bolt::BoltSerializer<bolt::BoltEncoder< template<class Stream>
void bolt::BoltSerializer<Stream>::write(const EdgeAccessor &edge) {
// write signatures for the edge struct and edge data type
encoder.write_struct_header(5);
encoder.write(underlying_cast(pack::Relationship));
// IMPORTANT: here we write a hardcoded 0 because we don't
// use internal IDs, but need to give something to Bolt
// note that OpenCypher has no id(x) function, so the client
// should not be able to do anything with this value anyway
encoder.write_integer(0);
encoder.write_integer(0);
encoder.write_integer(0);
// write the type of the edge
encoder.write_string(edge.edge_type());
// write the property map
const TypedValueStore& 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);
});
}
template<class Stream>
void bolt::BoltSerializer<Stream>::write(const TypedValue& value) {
switch (value.type_) {
case TypedValue::Type::Null:
encoder.write_null();
return;
case TypedValue::Type::Bool:
encoder.write_bool(value.Value<bool>());
return;
case TypedValue::Type::String:
encoder.write_string(value.Value<std::string>());
return;
case TypedValue::Type::Int:
encoder.write_integer(value.Value<int>());
return;
case TypedValue::Type::Float:
encoder.write_double(value.Value<float>());
return;
}
}
template<class Stream>
void bolt::BoltSerializer<Stream>::write_failure(const std::map<std::string, std::string> &data) {
encoder.message_failure();
encoder.write_map_header(data.size());
for (auto const &kv : data) {
write(kv.first);
write(kv.second);
}
}
template
class bolt::BoltSerializer<bolt::BoltEncoder<
bolt::ChunkedEncoder<bolt::ChunkedBuffer<bolt::SocketStream<io::Socket>>>>>; bolt::ChunkedEncoder<bolt::ChunkedBuffer<bolt::SocketStream<io::Socket>>>>>;

View File

@ -43,5 +43,5 @@ void Session::close()
bolt.close(this); bolt.close(this);
} }
Db &Session::active_db() { return bolt.dbms.active(); } GraphDb &Session::active_db() { return bolt.dbms.active(); }
} }

View File

@ -1,6 +1,9 @@
#include <database/creation_exception.hpp>
#include "database/graph_db_accessor.hpp" #include "database/graph_db_accessor.hpp"
GraphDbAccessor::GraphDbAccessor(GraphDb& db) : db_(db), transaction_(std::move(db.tx_engine.begin())) {}
VertexAccessor GraphDbAccessor::insert_vertex() { VertexAccessor GraphDbAccessor::insert_vertex() {
auto vertex_vlist = new mvcc::VersionList<Vertex>(); auto vertex_vlist = new mvcc::VersionList<Vertex>();
vertex_vlist->insert(transaction_); vertex_vlist->insert(transaction_);
@ -32,8 +35,6 @@ EdgeAccessor GraphDbAccessor::insert_edge(
// TODO connect the vertices to edge // TODO connect the vertices to edge
from.add_to_out(edge_vlist, pass_key); from.add_to_out(edge_vlist, pass_key);
to.add_to_in(edge_vlist, pass_key); to.add_to_in(edge_vlist, pass_key);
// from.vlist(pass_key).out_.emplace(edge_vlist);
// to.vlist(pass_key).in_.emplace(edge_vlist);
// TODO make this configurable // TODO make this configurable
for (int i = 0; i < 5; ++i) { for (int i = 0; i < 5; ++i) {
@ -47,13 +48,25 @@ EdgeAccessor GraphDbAccessor::insert_edge(
} }
GraphDb::Label GraphDbAccessor::label(const std::string& label_name) { GraphDb::Label GraphDbAccessor::label(const std::string& label_name) {
return db_.labels_.GetKey(label_name); return &(*db_.labels_.access().insert(label_name).first);
}
std::string& GraphDbAccessor::label_name(const GraphDb::Label label) const {
return *label;
} }
GraphDb::EdgeType GraphDbAccessor::edge_type(const std::string& edge_type_name){ GraphDb::EdgeType GraphDbAccessor::edge_type(const std::string& edge_type_name){
return db_.edge_types_.GetKey(edge_type_name); return &(*db_.edge_types_.access().insert(edge_type_name).first);
}
std::string& GraphDbAccessor::edge_type_name(const GraphDb::EdgeType edge_type) const {
return *edge_type;
} }
GraphDb::Property GraphDbAccessor::property(const std::string& property_name) { GraphDb::Property GraphDbAccessor::property(const std::string& property_name) {
return db_.properties_.GetKey(property_name); return &(*db_.properties_.access().insert(property_name).first);
}
std::string& GraphDbAccessor::property_name(const GraphDb::Property property) const {
return *property;
} }

View File

@ -2,37 +2,35 @@
#include "storage/vertex_accessor.hpp" #include "storage/vertex_accessor.hpp"
void EdgeAccessor::set_edge_type(GraphDb::EdgeType edge_type) { void EdgeAccessor::set_edge_type(GraphDb::EdgeType edge_type) {
this->record_->edge_type_ = edge_type; this->update()->edge_type_ = edge_type;
} }
GraphDb::EdgeType EdgeAccessor::edge_type() const { GraphDb::EdgeType EdgeAccessor::edge_type() const {
return this->record_->edge_type_; return this->view()->edge_type_;
} }
VertexAccessor EdgeAccessor::from() const { VertexAccessor EdgeAccessor::from() const {
return VertexAccessor(this->record_->from_, this->trans_); return VertexAccessor(this->view()->from_, this->db_accessor_->transaction_);
} }
VertexAccessor EdgeAccessor::to() const { VertexAccessor EdgeAccessor::to() const {
return VertexAccessor(this->record_->to_, this->trans_); return VertexAccessor(this->view()->to_, this->db_accessor_->transaction_);
} }
void EdgeAccessor::remove() { void EdgeAccessor::remove() {
// remove this edge's reference from the "from" vertex // remove this edge's reference from the "from" vertex
auto vertex_from = from(); auto vertex_from = from().update();
vertex_from.update(); std::remove(vertex_from->out_.begin(),
std::remove(vertex_from.record_->out_.begin(), vertex_from->out_.end(),
vertex_from.record_->out_.end(),
vlist_); vlist_);
// remove this edge's reference from the "to" vertex // remove this edge's reference from the "to" vertex
auto vertex_to = to(); auto vertex_to = to().update();
vertex_to.update(); std::remove(vertex_to->in_.begin(),
std::remove(vertex_to.record_->in_.begin(), vertex_to->in_.end(),
vertex_to.record_->in_.end(),
vlist_); vlist_);
// remove this record from the database via MVCC // remove this record from the database via MVCC
vlist_->remove(record_, trans_); vlist_->remove(update(), db_accessor_->transaction_);
} }

View File

@ -43,7 +43,7 @@ void TypedValueStore::set(const TKey &key, const char *value) {
set(key, std::string(value)); set(key, std::string(value));
} }
size_t TypedValueStore::erase(const TKey &key) { size_t TypedValueStore::erase(const TKey &key) const {
auto found = std::find_if(props_.begin(), props_.end(), [&key](std::pair<TKey, TypedValue> &kv){return kv.first == key;}); auto found = std::find_if(props_.begin(), props_.end(), [&key](std::pair<TKey, TypedValue> &kv){return kv.first == key;});
if (found != props_.end()) { if (found != props_.end()) {
props_.erase(found); props_.erase(found);

View File

@ -2,30 +2,28 @@
#include "storage/vertex_accessor.hpp" #include "storage/vertex_accessor.hpp"
size_t VertexAccessor::out_degree() const { size_t VertexAccessor::out_degree() const {
return this->record_->out_.size(); return this->view()->out_.size();
} }
size_t VertexAccessor::in_degree() const { size_t VertexAccessor::in_degree() const {
return this->record_->in_.size(); return this->view()->in_.size();
} }
bool VertexAccessor::add_label(GraphDb::Label label) { bool VertexAccessor::add_label(GraphDb::Label label) {
update(); return this->update()->labels_.emplace(label).second;
return this->record_->labels_.emplace(label).second;
} }
size_t VertexAccessor::remove_label(GraphDb::Label label) { size_t VertexAccessor::remove_label(GraphDb::Label label) {
update(); return this->update()->labels_.erase(label);
return this->record_->labels_.erase(label);
} }
bool VertexAccessor::has_label(GraphDb::Label label) const { bool VertexAccessor::has_label(GraphDb::Label label) const {
auto &label_set = this->record_->labels_; auto &label_set = this->view()->labels_;
return label_set.find(label) != label_set.end(); return label_set.find(label) != label_set.end();
} }
const std::set<GraphDb::Label> &VertexAccessor::labels() const { const std::set<GraphDb::Label>& VertexAccessor::labels() const {
return this->record_->labels_; return this->view()->labels_;
} }
bool VertexAccessor::remove() { bool VertexAccessor::remove() {
@ -33,7 +31,7 @@ bool VertexAccessor::remove() {
if (out_degree() > 0 || in_degree() > 0) if (out_degree() > 0 || in_degree() > 0)
return false; return false;
vlist_->remove(record_, trans_); vlist_->remove(view(), db_accessor_->transaction_);
return true; return true;
} }
@ -41,21 +39,19 @@ void VertexAccessor::detach_remove() {
// removing edges via accessors is both safe // removing edges via accessors is both safe
// and it should remove all the pointers in the relevant // and it should remove all the pointers in the relevant
// vertices (including this one) // vertices (including this one)
for (auto edge_vlist : record_->out_) for (auto edge_vlist : view()->out_)
EdgeAccessor(edge_vlist, trans_).remove(); EdgeAccessor(edge_vlist, db_accessor_->transaction_).remove();
for (auto edge_vlist : record_->in_) for (auto edge_vlist : view()->in_)
EdgeAccessor(edge_vlist, trans_).remove(); EdgeAccessor(edge_vlist, db_accessor_->transaction_).remove();
vlist_->remove(record_, trans_); vlist_->remove(view(), db_accessor_->transaction_);
} }
void VertexAccessor::attach_in(mvcc::VersionList<Edge>* edge_vlist, PassKey<GraphDb>) { void VertexAccessor::attach_in(mvcc::VersionList<Edge>* edge_vlist, PassKey<GraphDb>) {
update(); this->update()->in_.emplace_back(edge_vlist);
this->record_->in_.emplace_back(edge_vlist);
} }
void VertexAccessor::attach_out(mvcc::VersionList<Edge>* edge_vlist, PassKey<GraphDb>) { void VertexAccessor::attach_out(mvcc::VersionList<Edge>* edge_vlist, PassKey<GraphDb>) {
update(); this->update()->out_.emplace_back(edge_vlist);
this->record_->out_.emplace_back(edge_vlist);
} }