From 63207846877c8df6c4865fdc1de3639f4cfa80be Mon Sep 17 00:00:00 2001 From: Florijan Stamenkovic <florijan.stamenkovic@memgraph.io> Date: Sat, 11 Feb 2017 07:51:02 +0100 Subject: [PATCH] Major properties and storage refactoring in progress. UNSTABLE STATE --- .../bolt/v1/serialization/bolt_serializer.hpp | 115 ++-------- .../bolt/v1/serialization/record_stream.hpp | 199 ++++++++---------- include/communication/bolt/v1/session.hpp | 16 +- include/database/graph_db.hpp | 13 +- include/database/graph_db_accessor.hpp | 32 ++- include/query/i_plan_cpu.hpp | 5 +- include/query/strip/stripped.hpp | 32 ++- include/query/util.hpp | 89 ++++---- include/storage/edge.hpp | 2 +- include/storage/record_accessor.hpp | 94 +++++---- include/storage/typed_value_store.hpp | 8 +- include/storage/vertex.hpp | 2 +- include/transactions/transaction.hpp | 2 +- .../bolt/v1/serialization/bolt_serializer.cpp | 104 +++++++-- src/communication/bolt/v1/session.cpp | 2 +- src/database/graph_db_accessor.cpp | 23 +- src/storage/edge_accessor.cpp | 24 +-- src/storage/typed_value_store.cpp | 2 +- src/storage/vertex_accessor.cpp | 34 ++- 19 files changed, 388 insertions(+), 410 deletions(-) diff --git a/include/communication/bolt/v1/serialization/bolt_serializer.hpp b/include/communication/bolt/v1/serialization/bolt_serializer.hpp index 5ad24c8e6..919a0d37e 100644 --- a/include/communication/bolt/v1/serialization/bolt_serializer.hpp +++ b/include/communication/bolt/v1/serialization/bolt_serializer.hpp @@ -6,25 +6,14 @@ #include "storage/edge_accessor.hpp" #include "storage/vertex_accessor.hpp" -#include "storage/edge_type/edge_type.hpp" -#include "storage/label/label.hpp" -#include "storage/model/properties/all.hpp" -#include "storage/model/properties/properties.hpp" -#include "storage/vertex_record.hpp" +#include "storage/typed_value.hpp" -namespace bolt -{ +namespace bolt { -template <class Stream> -class BoltSerializer -{ - friend class Property; + template<class Stream> + class BoltSerializer { - // TODO: here shoud be friend but it doesn't work - // template <class Handler> - // friend void accept(const Property &property, Handler &h); - -public: + public: BoltSerializer(Stream &stream) : encoder(stream) {} /** Serializes the vertex accessor into the packstream format @@ -36,38 +25,9 @@ public: * } * */ - 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)); + void write(const VertexAccessor &vertex); - // IMPORTANT: here we write a hardcorded 0 because we don't - // 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 + /** Serializes the edge accessor into the packstream format * * struct[size = 5] Edge [signature = 0x52] { * Integer edge_id; // IMPORTANT: always 0 since we don't do IDs @@ -80,57 +40,18 @@ public: */ 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()); } - - 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: + protected: Stream &encoder; -}; + + }; } diff --git a/include/communication/bolt/v1/serialization/record_stream.hpp b/include/communication/bolt/v1/serialization/record_stream.hpp index e643257cf..ff0db58e4 100644 --- a/include/communication/bolt/v1/serialization/record_stream.hpp +++ b/include/communication/bolt/v1/serialization/record_stream.hpp @@ -7,83 +7,73 @@ #include "logging/default.hpp" -namespace bolt -{ +namespace bolt { /** * compiled queries have to use this class in order to return results * query code should not know about bolt protocol */ -template <class Socket> -class RecordStream -{ -public: - RecordStream(Socket &socket) : socket(socket) - { - logger = logging::log->logger("Record Stream"); + template<class Socket> + class RecordStream { + public: + RecordStream(Socket &socket) : socket(socket) { + logger = logging::log->logger("Record Stream"); } ~RecordStream() = default; // TODO: create apstract methods that are not bolt specific --------------- - void write_success() - { - logger.trace("write_success"); - bolt_encoder.message_success(); + void write_success() { + logger.trace("write_success"); + bolt_encoder.message_success(); } - void write_success_empty() - { - logger.trace("write_success_empty"); - bolt_encoder.message_success_empty(); + void write_success_empty() { + logger.trace("write_success_empty"); + bolt_encoder.message_success_empty(); } - void write_ignored() - { - logger.trace("write_ignored"); - bolt_encoder.message_ignored(); + void write_ignored() { + logger.trace("write_ignored"); + bolt_encoder.message_ignored(); } - void write_empty_fields() - { - bolt_encoder.message_success(); - bolt_encoder.write_map_header(1); - bolt_encoder.write_string("fields"); - write_list_header(0); - chunk(); + void write_empty_fields() { + bolt_encoder.message_success(); + bolt_encoder.write_map_header(1); + bolt_encoder.write_string("fields"); + write_list_header(0); + chunk(); } - void write_fields(const std::vector<std::string> &fields) - { - // TODO: that should be one level below? - bolt_encoder.message_success(); + void write_fields(const std::vector<std::string> &fields) { + // TODO: that should be one level below? + bolt_encoder.message_success(); - bolt_encoder.write_map_header(1); - bolt_encoder.write_string("fields"); - write_list_header(fields.size()); + bolt_encoder.write_map_header(1); + bolt_encoder.write_string("fields"); + write_list_header(fields.size()); - for (auto &name : fields) { - bolt_encoder.write_string(name); - } + for (auto &name : fields) { + bolt_encoder.write_string(name); + } - chunk(); - send(); + chunk(); + send(); } - void write_field(const std::string &field) - { - bolt_encoder.message_success(); - bolt_encoder.write_map_header(1); - bolt_encoder.write_string("fields"); - write_list_header(1); - bolt_encoder.write_string(field); - chunk(); - send(); + void write_field(const std::string &field) { + bolt_encoder.message_success(); + bolt_encoder.write_map_header(1); + bolt_encoder.write_string("fields"); + write_list_header(1); + bolt_encoder.write_string(field); + chunk(); + send(); } - void write_list_header(size_t size) - { - bolt_encoder.write_list_header(size); + void write_list_header(size_t size) { + bolt_encoder.write_list_header(size); } void write_record() { bolt_encoder.message_record(); } @@ -92,94 +82,73 @@ public: // TODO: write whole implementation (currently, only type is supported) // { "stats": { "nodes created": 1, "properties set": 1}, // "type": "r" | "rw" | ... - void write_meta(const std::string &type) - { - bolt_encoder.message_success(); - bolt_encoder.write_map_header(1); - bolt_encoder.write_string("type"); - bolt_encoder.write_string(type); - chunk(); + void write_meta(const std::string &type) { + bolt_encoder.message_success(); + bolt_encoder.write_map_header(1); + bolt_encoder.write_string("type"); + bolt_encoder.write_string(type); + chunk(); } - void write_failure(const std::map<std::string, std::string> &data) - { - serializer.write_failure(data); - chunk(); + void write_failure(const std::map<std::string, std::string> &data) { + serializer.write_failure(data); + chunk(); } - void write_count(const size_t count) - { - write_record(); - write_list_header(1); - write(Int64(count)); - chunk(); + void write_count(const size_t count) { + write_record(); + write_list_header(1); + write(count); + chunk(); } void write(const VertexAccessor &vertex) { serializer.write(vertex); } - void write_vertex_record(const VertexAccessor& va) - { - write_record(); - write_list_header(1); - write(va); - chunk(); + + void write_vertex_record(const VertexAccessor &va) { + write_record(); + write_list_header(1); + write(va); + chunk(); } void write(const EdgeAccessor &edge) { serializer.write(edge); } - void write_edge_record(const EdgeAccessor& ea) - { - write_record(); - write_list_header(1); - write(ea); - chunk(); + + void write_edge_record(const EdgeAccessor &ea) { + write_record(); + write_list_header(1); + write(ea); + chunk(); } - void write(const StoredProperty<TypeGroupEdge> &prop) - { - prop.accept(serializer); + void write(const TypedValue& value) { + serializer.write(value); } - 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 chunk() { chunked_encoder.write_chunk(); } - void _write_test() - { - logger.trace("write_test"); + // TODO WTF is this test doing here? + void _write_test() { + logger.trace("write_test"); - write_fields({{"name"}}); + write_fields({{"name"}}); - write_record(); - write_list_header(1); - write(String("max")); + write_record(); + write_list_header(1); + bolt_encoder.write("max"); - write_record(); - write_list_header(1); - write(String("paul")); + write_record(); + write_list_header(1); + bolt_encoder.write("paul"); - write_success_empty(); + write_success_empty(); } -protected: + protected: Logger logger; -private: + private: using socket_t = SocketStream<Socket>; using buffer_t = ChunkedBuffer<socket_t>; using chunked_encoder_t = ChunkedEncoder<buffer_t>; @@ -191,5 +160,5 @@ private: chunked_encoder_t chunked_encoder{chunked_buffer}; bolt_encoder_t bolt_encoder{chunked_encoder}; bolt_serializer_t serializer{bolt_encoder}; -}; + }; } diff --git a/include/communication/bolt/v1/session.hpp b/include/communication/bolt/v1/session.hpp index b0c5a147f..bcb007619 100644 --- a/include/communication/bolt/v1/session.hpp +++ b/include/communication/bolt/v1/session.hpp @@ -12,12 +12,10 @@ #include "logging/default.hpp" -namespace bolt -{ +namespace bolt { -class Session : public io::tcp::Stream<io::Socket> -{ -public: + class Session : public io::tcp::Stream<io::Socket> { + public: using Decoder = BoltDecoder; using OutputStream = communication::OutputStream; @@ -26,10 +24,12 @@ public: bool alive() const; void execute(const byte *data, size_t len); + void close(); Bolt &bolt; - Db &active_db(); + + GraphDb &active_db(); Decoder decoder; OutputStream output_stream{socket}; @@ -37,7 +37,7 @@ public: bool connected{false}; State *state; -protected: + protected: Logger logger; -}; + }; } diff --git a/include/database/graph_db.hpp b/include/database/graph_db.hpp index 36e4b325a..82b6d19a5 100644 --- a/include/database/graph_db.hpp +++ b/include/database/graph_db.hpp @@ -4,6 +4,7 @@ #include "transactions/engine.hpp" #include "mvcc/version_list.hpp" #include "utils/pass_key.hpp" +#include "data_structures/concurrent/concurrent_set.hpp" #include "storage/unique_object_store.hpp" // forward declaring Edge and Vertex because they use @@ -26,9 +27,9 @@ class GraphDb { public: // definitions for what data types are used for a Label, Property, EdgeType - using Label = uint32_t; - using EdgeType = uint32_t; - using Property = uint32_t; + using Label = std::string*; + using EdgeType = std::string*; + using Property = std::string*; /** * This constructor will create a database with the name "default" @@ -83,7 +84,7 @@ public: SkipList<mvcc::VersionList<Vertex>*> vertices_; // unique object stores - UniqueObjectStore<std::string, Label> labels_; - UniqueObjectStore<std::string, EdgeType> edge_types_; - UniqueObjectStore<std::string, Property> properties_; + ConcurrentSet<std::string> labels_; + ConcurrentSet<std::string> edge_types_; + ConcurrentSet<std::string> properties_; }; diff --git a/include/database/graph_db_accessor.hpp b/include/database/graph_db_accessor.hpp index cbc5922c0..decd0c2a0 100644 --- a/include/database/graph_db_accessor.hpp +++ b/include/database/graph_db_accessor.hpp @@ -3,14 +3,14 @@ // Created by Florijan Stamenkovic on 03.02.17. // -#pragma +#pragma once #include "graph_db.hpp" #include "transactions/transaction.hpp" class GraphDbAccessor { - GraphDbAccessor(GraphDb& db) : db_(db), transaction_(db.tx_engine.begin()) {} + GraphDbAccessor(GraphDb& db); public: /** @@ -36,24 +36,48 @@ public: */ 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. * @return See above. */ 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. * @return See above. */ 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 */ - tx::Transaction const transaction_; + tx::Transaction transaction_; private: GraphDb& db_; // for privileged access to some RecordAccessor functionality (and similar) - const PassKey<GraphDb> pass_key; + const PassKey<GraphDbAccessor> pass_key; }; diff --git a/include/query/i_plan_cpu.hpp b/include/query/i_plan_cpu.hpp index a8790f730..fcbb596f8 100644 --- a/include/query/i_plan_cpu.hpp +++ b/include/query/i_plan_cpu.hpp @@ -1,15 +1,14 @@ #pragma once #include "communication/communication.hpp" -#include "database/graph_db.hpp" -#include "database/db_accessor.hpp" +#include "database/graph_db_accessor.hpp" #include "query/strip/stripped.hpp" template <typename Stream> class IPlanCPU { 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() {} }; diff --git a/include/query/strip/stripped.hpp b/include/query/strip/stripped.hpp index b8627041c..ca6edb4c9 100644 --- a/include/query/strip/stripped.hpp +++ b/include/query/strip/stripped.hpp @@ -1,13 +1,8 @@ #pragma once #include <vector> +#include "storage/typed_value_store.hpp" -#include "storage/model/properties/property.hpp" - -/* - * Query Plan Arguments Type - */ -using plan_args_t = std::vector<Property>; /* * QueryStripped contains: @@ -15,18 +10,17 @@ using plan_args_t = std::vector<Property>; * * plan arguments stripped from query * * hash of stripped query */ -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; +struct QueryStripped { - std::string query; - plan_args_t arguments; - uint64_t hash; + QueryStripped(const std::string &&query, const TypedValueStore &&arguments, uint64_t hash) + : query(std::forward<const std::string>(query)), + arguments(std::forward<const TypedValueStore>(arguments)), + hash(hash) {} + + QueryStripped(QueryStripped &other) = delete; + QueryStripped(QueryStripped &&other) = default; + + std::string query; + TypedValueStore arguments; + uint64_t hash; }; diff --git a/include/query/util.hpp b/include/query/util.hpp index 5fc05ab41..049821a27 100644 --- a/include/query/util.hpp +++ b/include/query/util.hpp @@ -7,10 +7,6 @@ #include "fmt/format.h" #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" using std::cout; @@ -20,76 +16,67 @@ using std::endl; // headers because it will create a unique namespace for each compilation unit // http://stackoverflow.com/questions/2727582/multiple-definition-in-header-file // but sometimes that might be a problem -namespace -{ +namespace { -class CodeLineFormatException : public BasicException -{ -public: + class CodeLineFormatException : public BasicException { + public: using BasicException::BasicException; -}; + }; -template <typename... Args> -std::string format(const std::string &format_str, const Args &... args) -{ + template<typename... Args> + std::string format(const std::string &format_str, const Args &... args) { return fmt::format(format_str, args...); -} + } -template <typename... Args> -std::string code_line(const std::string &format_str, const Args &... args) -{ + template<typename... Args> + std::string code_line(const std::string &format_str, const Args &... args) { try { - return "\t" + format(format_str, args...) + "\n"; + return "\t" + format(format_str, args...) + "\n"; } 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>>; -using indices_t = std::map<std::string, long>; + using name_properties_t = std::vector<std::pair<std::string, Property>>; + using indices_t = std::map<std::string, long>; -auto query_properties(indices_t &indices, properties_t &values) -{ + 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]))); + properties.push_back( + std::make_pair(std::move(property_index.first), + std::move(values[property_index.second]))); } return properties; -} + } -class CoutSocket -{ -public: + class CoutSocket { + public: CoutSocket() : logger(logging::log->logger("Cout Socket")) {} - int write(const std::string &str) - { - logger.info(str); - return str.size(); + int write(const std::string &str) { + logger.info(str); + return str.size(); } - int write(const char *data, size_t len) - { - logger.info(std::string(data, len)); - return len; + int write(const char *data, size_t len) { + logger.info(std::string(data, len)); + return len; } - int write(const byte *data, size_t len) - { - std::stringstream ss; - for (int i = 0; i < len; i++) { - ss << data[i]; - } - std::string output(ss.str()); - cout << output << endl; - logger.info(output); - return len; + int write(const byte *data, size_t len) { + std::stringstream ss; + for (int i = 0; i < len; i++) { + ss << data[i]; + } + std::string output(ss.str()); + cout << output << endl; + logger.info(output); + return len; } -private: + private: Logger logger; -}; + }; } diff --git a/include/storage/edge.hpp b/include/storage/edge.hpp index 8525ee355..5a56a922a 100644 --- a/include/storage/edge.hpp +++ b/include/storage/edge.hpp @@ -13,5 +13,5 @@ public: mvcc::VersionList<Vertex>* from_; mvcc::VersionList<Vertex>* to_; GraphDb::EdgeType edge_type_; - TypedValueStore properties_; + TypedValueStore<GraphDb::Property> properties_; }; diff --git a/include/storage/record_accessor.hpp b/include/storage/record_accessor.hpp index ca17f8152..9369d593a 100644 --- a/include/storage/record_accessor.hpp +++ b/include/storage/record_accessor.hpp @@ -1,49 +1,37 @@ #pragma once #include "mvcc/version_list.hpp" -#include "transactions/transaction.hpp" #include "storage/typed_value.hpp" #include "database/graph_db.hpp" #include "database/graph_db_accessor.hpp" #include "utils/pass_key.hpp" -template <typename TRecord, typename TDerived> +template<typename TRecord, typename TDerived> class RecordAccessor { public: - RecordAccessor(mvcc::VersionList<TRecord>* vlist, tx::Transaction &trans) - : vlist_(vlist), trans_(trans) { - record_ = vlist->find(trans_); + RecordAccessor(mvcc::VersionList<TRecord> *vlist, GraphDbAccessor *db_accessor) + : vlist_(vlist), db_accessor_(db_accessor) { + record_ = vlist->find(db_accessor->transaction_); } - /** - * Indicates if this record is visible to the current transaction. - * - * @return - */ - bool is_visible() const { - return record_ != nullptr; + template<typename TValue> + void PropsSet(GraphDb::Property key, TValue value) { + update()->props_.set(key, value); } - TypedValue at(GraphDb::Property key) const { - return record_->props_.at(key); + size_t PropsErase(GraphDb::Property key) { + return update()->props_.erase(key); } - template <typename TValue> - void set(GraphDb::Property key, TValue value) { - update(); - record_->props_.set(key, value); + const TypedValueStore<GraphDb::Property> &Properties() const { + return view().properties_; } - size_t erase(GraphDb::Property key) { - update(); - return record_->props_.erase(key); - } - - void Accept(std::function<void(const GraphDb::Property key, const TypedValue& prop)> handler, - std::function<void()> finish = {}) const { - record_->props_.Accept(handler, finish); + void PropertiesAccept(std::function<void(const GraphDb::Property key, const TypedValue &prop)> handler, + std::function<void()> finish = {}) const { + view()->props_.Accept(handler, finish); } // Assumes same transaction @@ -64,29 +52,57 @@ public: * @param pass_key Ignored. * @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_; } + /** + * Returns a GraphDB accessor of this record accessor. + * + * @return + */ + const GraphDbAccessor &db_accessor() const { + return db_accessor_; + } + 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 - * method that will change the record (in terms of the - * property graph data). + * @return See above. */ - void 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(trans_)) - return; - else - record_ = vlist_->update(trans_); + + if (!record_->is_visible_write(db_accessor_->transaction_)) + record_ = vlist_->update(db_accessor_->transaction_); + + return record_; } - mvcc::VersionList<TRecord>* vlist_; - tx::Transaction& trans_; - TRecord* record_; + /** + * Returns a version of the record that is only for viewing. + * + * @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_; }; diff --git a/include/storage/typed_value_store.hpp b/include/storage/typed_value_store.hpp index 47c4be9c3..f7838418b 100644 --- a/include/storage/typed_value_store.hpp +++ b/include/storage/typed_value_store.hpp @@ -10,14 +10,14 @@ * using a key of type Properties::TKey. * * 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 { 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. @@ -65,7 +65,7 @@ public: /** * @return The number of Properties in this collection. */ - size_t size(); + size_t size() const; /** diff --git a/include/storage/vertex.hpp b/include/storage/vertex.hpp index 095dd7e8c..85c901ea2 100644 --- a/include/storage/vertex.hpp +++ b/include/storage/vertex.hpp @@ -14,5 +14,5 @@ public: std::vector<mvcc::VersionList<Edge> *> out_; std::vector<mvcc::VersionList<Edge> *> in_; std::set<GraphDb::Label> labels_; - TypedValueStore properties_; + TypedValueStore<GraphDb::Property> properties_; }; diff --git a/include/transactions/transaction.hpp b/include/transactions/transaction.hpp index db3643f37..ef0a880f8 100644 --- a/include/transactions/transaction.hpp +++ b/include/transactions/transaction.hpp @@ -20,7 +20,7 @@ class Transaction : public TransactionRead public: Transaction(const Id &id, const Snapshot<Id> &snapshot, Engine &engine); Transaction(const Transaction &) = delete; - Transaction(Transaction &&) = delete; + Transaction(Transaction &&) = default; // Returns copy of transaction_read TransactionRead transaction_read(); diff --git a/src/communication/bolt/v1/serialization/bolt_serializer.cpp b/src/communication/bolt/v1/serialization/bolt_serializer.cpp index cb2e69fdc..375d4a178 100644 --- a/src/communication/bolt/v1/serialization/bolt_serializer.cpp +++ b/src/communication/bolt/v1/serialization/bolt_serializer.cpp @@ -5,32 +5,92 @@ #include "communication/bolt/v1/transport/socket_stream.hpp" #include "io/network/socket.hpp" -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)); +template<class Stream> +void bolt::BoltSerializer<Stream>::write(const VertexAccessor &vertex) { - // write the identifier for the node - encoder.write_integer(edge.id()); + // write signatures for the node struct and node data type + encoder.write_struct_header(3); + encoder.write(underlying_cast(pack::Node)); - encoder.write_integer(edge.from().id()); - encoder.write_integer(edge.to().id()); + // 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); // uID - // write the type of the edge - encoder.write_string(edge.edge_type()); + // write the list of labels + 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 - auto props = edge.properties(); - - encoder.write_map_header(props.size()); - - for (auto &prop : props) { - write(prop.key.family_name()); - prop.accept(*this); - } + // write the properties + const TypedValueStore &props = vertex.Properties(); + encoder.write_map_header(props.size()); + props.Accept([&vertex](const TypedValueStore::TKey &prop_name, const TypedValue &value) { + write(vertex.db_accessor().property_name(prop_name)); + write(value); + }); } -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>>>>>; diff --git a/src/communication/bolt/v1/session.cpp b/src/communication/bolt/v1/session.cpp index 3234d4263..2e8a504b1 100644 --- a/src/communication/bolt/v1/session.cpp +++ b/src/communication/bolt/v1/session.cpp @@ -43,5 +43,5 @@ void Session::close() bolt.close(this); } -Db &Session::active_db() { return bolt.dbms.active(); } +GraphDb &Session::active_db() { return bolt.dbms.active(); } } diff --git a/src/database/graph_db_accessor.cpp b/src/database/graph_db_accessor.cpp index 6bf67f634..2b2c8ab27 100644 --- a/src/database/graph_db_accessor.cpp +++ b/src/database/graph_db_accessor.cpp @@ -1,6 +1,9 @@ +#include <database/creation_exception.hpp> #include "database/graph_db_accessor.hpp" +GraphDbAccessor::GraphDbAccessor(GraphDb& db) : db_(db), transaction_(std::move(db.tx_engine.begin())) {} + VertexAccessor GraphDbAccessor::insert_vertex() { auto vertex_vlist = new mvcc::VersionList<Vertex>(); vertex_vlist->insert(transaction_); @@ -32,8 +35,6 @@ EdgeAccessor GraphDbAccessor::insert_edge( // TODO connect the vertices to edge from.add_to_out(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 for (int i = 0; i < 5; ++i) { @@ -47,13 +48,25 @@ EdgeAccessor GraphDbAccessor::insert_edge( } 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){ - 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) { - 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; } diff --git a/src/storage/edge_accessor.cpp b/src/storage/edge_accessor.cpp index 2ab2fb7f3..7b252e954 100644 --- a/src/storage/edge_accessor.cpp +++ b/src/storage/edge_accessor.cpp @@ -2,37 +2,35 @@ #include "storage/vertex_accessor.hpp" 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 { - return this->record_->edge_type_; + return this->view()->edge_type_; } VertexAccessor EdgeAccessor::from() const { - return VertexAccessor(this->record_->from_, this->trans_); + return VertexAccessor(this->view()->from_, this->db_accessor_->transaction_); } VertexAccessor EdgeAccessor::to() const { - return VertexAccessor(this->record_->to_, this->trans_); + return VertexAccessor(this->view()->to_, this->db_accessor_->transaction_); } void EdgeAccessor::remove() { // remove this edge's reference from the "from" vertex - auto vertex_from = from(); - vertex_from.update(); - std::remove(vertex_from.record_->out_.begin(), - vertex_from.record_->out_.end(), + auto vertex_from = from().update(); + std::remove(vertex_from->out_.begin(), + vertex_from->out_.end(), vlist_); // remove this edge's reference from the "to" vertex - auto vertex_to = to(); - vertex_to.update(); - std::remove(vertex_to.record_->in_.begin(), - vertex_to.record_->in_.end(), + auto vertex_to = to().update(); + std::remove(vertex_to->in_.begin(), + vertex_to->in_.end(), vlist_); // remove this record from the database via MVCC - vlist_->remove(record_, trans_); + vlist_->remove(update(), db_accessor_->transaction_); } diff --git a/src/storage/typed_value_store.cpp b/src/storage/typed_value_store.cpp index e3daa0283..725b13a84 100644 --- a/src/storage/typed_value_store.cpp +++ b/src/storage/typed_value_store.cpp @@ -43,7 +43,7 @@ void TypedValueStore::set(const TKey &key, const char *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;}); if (found != props_.end()) { props_.erase(found); diff --git a/src/storage/vertex_accessor.cpp b/src/storage/vertex_accessor.cpp index 17eb5c8f4..2378f75b6 100644 --- a/src/storage/vertex_accessor.cpp +++ b/src/storage/vertex_accessor.cpp @@ -2,30 +2,28 @@ #include "storage/vertex_accessor.hpp" size_t VertexAccessor::out_degree() const { - return this->record_->out_.size(); + return this->view()->out_.size(); } size_t VertexAccessor::in_degree() const { - return this->record_->in_.size(); + return this->view()->in_.size(); } bool VertexAccessor::add_label(GraphDb::Label label) { - update(); - return this->record_->labels_.emplace(label).second; + return this->update()->labels_.emplace(label).second; } size_t VertexAccessor::remove_label(GraphDb::Label label) { - update(); - return this->record_->labels_.erase(label); + return this->update()->labels_.erase(label); } 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(); } -const std::set<GraphDb::Label> &VertexAccessor::labels() const { - return this->record_->labels_; +const std::set<GraphDb::Label>& VertexAccessor::labels() const { + return this->view()->labels_; } bool VertexAccessor::remove() { @@ -33,7 +31,7 @@ bool VertexAccessor::remove() { if (out_degree() > 0 || in_degree() > 0) return false; - vlist_->remove(record_, trans_); + vlist_->remove(view(), db_accessor_->transaction_); return true; } @@ -41,21 +39,19 @@ void VertexAccessor::detach_remove() { // removing edges via accessors is both safe // and it should remove all the pointers in the relevant // vertices (including this one) - for (auto edge_vlist : record_->out_) - EdgeAccessor(edge_vlist, trans_).remove(); + for (auto edge_vlist : view()->out_) + EdgeAccessor(edge_vlist, db_accessor_->transaction_).remove(); - for (auto edge_vlist : record_->in_) - EdgeAccessor(edge_vlist, trans_).remove(); + for (auto edge_vlist : view()->in_) + 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>) { - update(); - this->record_->in_.emplace_back(edge_vlist); + this->update()->in_.emplace_back(edge_vlist); } void VertexAccessor::attach_out(mvcc::VersionList<Edge>* edge_vlist, PassKey<GraphDb>) { - update(); - this->record_->out_.emplace_back(edge_vlist); + this->update()->out_.emplace_back(edge_vlist); }