From df0bf6fa5fa51c002019bb7fa052c9162c788514 Mon Sep 17 00:00:00 2001 From: Kruno Tomola Fabro Date: Mon, 15 Aug 2016 00:09:58 +0100 Subject: [PATCH 1/4] Database interface refactor. DbAccessor: -Guarantees that access to Vertex and Edge is possible only through Vertex::Accessor and Edge::Accessor. -Guarantees that changing Vertex and Edge is possible only using Vertex::Accessor returned by vertex_insert() method and Edge::Accessor returned by edge_insert() method. -Offers CRUD for Vertex and Edge except iterating over all edges. Squashed commit messages: First step in database accessor refactoring done. It's compiling. All tests with exception of integration_querys pass Tests now initialize logging facilities. Refactored accessors. RecordAccessor now has 3 states. From,To,Out,In in there respecive Accessors return unfilled RecordAccessor. Added iterator classes into utils/itearator/. --- CMakeLists.txt | 10 +- .../bolt/v1/serialization/bolt_serializer.hpp | 72 +++----- include/database/db.hpp | 13 +- include/database/db_accessor.hpp | 91 +++++++++++ include/database/db_transaction.hpp | 25 +++ include/query_engine/hardcode/queries.hpp | 101 ++++++------ include/storage/edge_accessor.hpp | 20 +-- include/storage/edge_record.hpp | 2 +- include/storage/edges.hpp | 7 +- include/storage/model/edge_map.hpp | 2 +- include/storage/record_accessor.hpp | 60 +++++-- include/storage/vertex.hpp | 19 +-- include/storage/vertex_accessor.hpp | 12 +- include/storage/vertices.hpp | 13 +- include/utils/iterator/accessor.hpp | 28 ++++ include/utils/iterator/for_all.hpp | 17 ++ include/utils/iterator/iter.hpp | 48 ++++++ include/utils/iterator/iterator.hpp | 7 + include/utils/iterator/map.hpp | 39 +++++ include/utils/iterator/wrap.hpp | 60 +++++++ include/utils/option.hpp | 95 +++++++++++ include/utils/placeholder.hpp | 21 +-- poc/astar.cpp | 154 +++++++++--------- src/cypher/tokenizer/cypher_lexer.hpp | 4 +- src/data_structures/map/rh_common.hpp | 9 +- src/data_structures/map/rh_hashmultimap.hpp | 35 +++- src/database/db.cpp | 6 + src/database/db_accessor.cpp | 75 +++++++++ src/database/db_transaction.cpp | 8 + src/storage/edge_accessor.cpp | 22 +++ src/storage/edges.cpp | 18 +- src/storage/vertex_accessor.cpp | 37 ++++- src/storage/vertices.cpp | 38 +---- tests/concurrent/common.h | 8 + tests/concurrent/linkedlist.cpp | 72 ++++---- tests/concurrent/sl_insert.cpp | 1 + tests/concurrent/sl_insert_competetive.cpp | 1 + tests/concurrent/sl_map.cpp | 1 + tests/concurrent/sl_memory.cpp | 26 +-- tests/concurrent/sl_memory_leak.cpp | 1 + tests/concurrent/sl_multiiterator.cpp | 1 + tests/concurrent/sl_multiiterator_remove.cpp | 1 + .../sl_multiiterator_remove_duplicates.cpp | 1 + tests/concurrent/sl_multimap.cpp | 1 + tests/concurrent/sl_multiset.cpp | 1 + tests/concurrent/sl_remove_competetive.cpp | 1 + tests/concurrent/sl_remove_disjoint.cpp | 1 + tests/concurrent/sl_remove_joint.cpp | 1 + tests/concurrent/sl_set.cpp | 1 + tests/concurrent/sl_simulation.cpp | 1 + tests/unit/chunked_encoder.cpp | 37 ++--- tests/unit/concurrent_map.cpp | 83 +++++----- tests/unit/concurrent_set.cpp | 4 + 53 files changed, 999 insertions(+), 413 deletions(-) create mode 100644 include/database/db_accessor.hpp create mode 100644 include/database/db_transaction.hpp create mode 100644 include/utils/iterator/accessor.hpp create mode 100644 include/utils/iterator/for_all.hpp create mode 100644 include/utils/iterator/iter.hpp create mode 100644 include/utils/iterator/iterator.hpp create mode 100644 include/utils/iterator/map.hpp create mode 100644 include/utils/iterator/wrap.hpp create mode 100644 include/utils/option.hpp create mode 100644 src/database/db.cpp create mode 100644 src/database/db_accessor.cpp create mode 100644 src/database/db_transaction.cpp create mode 100644 src/storage/edge_accessor.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 74df3c068..9efec42c3 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -15,7 +15,7 @@ find_package(Threads REQUIRED) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++1y") # glibcxx debug (useful for gdb) -# the problem is that the query engine doesn't work as it should work if +# the problem is that the query engine doesn't work as it should work if # this flag is present # TODO: find more appropriate way to use this flag only when it is needed # set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -D_GLIBCXX_DEBUG") @@ -288,7 +288,7 @@ endif() # release flags set(CMAKE_CXX_FLAGS_RELEASE - "${CMAKE_CXX_FLAGS_RELEASE} -march=native -Wall -Werror") + "${CMAKE_CXX_FLAGS_RELEASE} -march=native -Wall") # -- configure defines -- default is ON | true | enabled ---------------------- # -- logging ------------------------------------------------------------------ @@ -412,6 +412,10 @@ set(memgraph_src_files ${src_dir}/logging/default.cpp ${src_dir}/logging/log.cpp ${src_dir}/io/network/tls.cpp + ${src_dir}/database/db.cpp + ${src_dir}/database/db_accessor.cpp + ${src_dir}/database/db_transaction.cpp + ${src_dir}/storage/edge_accessor.cpp ) # STATIC library used by memgraph executables @@ -434,7 +438,7 @@ endif() # memgraph build name execute_process( - OUTPUT_VARIABLE COMMIT_NO + OUTPUT_VARIABLE COMMIT_NO COMMAND git rev-list --count HEAD ) execute_process( diff --git a/include/communication/bolt/v1/serialization/bolt_serializer.hpp b/include/communication/bolt/v1/serialization/bolt_serializer.hpp index 6d7248b6f..b50f3de8b 100644 --- a/include/communication/bolt/v1/serialization/bolt_serializer.hpp +++ b/include/communication/bolt/v1/serialization/bolt_serializer.hpp @@ -1,13 +1,13 @@ #pragma once -#include "communication/bolt/v1/transport/bolt_encoder.hpp" #include "communication/bolt/v1/packing/codes.hpp" +#include "communication/bolt/v1/transport/bolt_encoder.hpp" -#include "storage/vertex_accessor.hpp" #include "storage/edge_accessor.hpp" +#include "storage/vertex_accessor.hpp" -#include "storage/model/properties/properties.hpp" #include "storage/model/properties/all.hpp" +#include "storage/model/properties/properties.hpp" namespace bolt { @@ -22,7 +22,7 @@ class BoltSerializer // 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 * @@ -33,7 +33,7 @@ public: * } * */ - void write(const Vertex::Accessor& vertex) + void write(const Vertex::Accessor &vertex) { // write signatures for the node struct and node data type encoder.write_struct_header(3); @@ -47,7 +47,7 @@ public: encoder.write_list_header(labels.size()); - for(auto& label : labels) + for (auto &label : labels) encoder.write_string(label.get()); // write the property map @@ -55,7 +55,7 @@ public: encoder.write_map_header(props.size()); - for(auto& prop : props) { + for (auto &prop : props) { write(prop.first); write(*prop.second); } @@ -72,7 +72,7 @@ public: * } * */ - void write(const Edge::Accessor& edge) + void write(const Edge::Accessor &edge) { // write signatures for the edge struct and edge data type encoder.write_struct_header(5); @@ -82,8 +82,8 @@ public: encoder.write_integer(edge.id()); // TODO refactor when from() and to() start returning Accessors - encoder.write_integer(edge.from()->id); - encoder.write_integer(edge.to()->id); + encoder.write_integer(edge.from().id()); + encoder.write_integer(edge.to().id()); // write the type of the edge encoder.write_string(edge.edge_type()); @@ -93,65 +93,37 @@ public: encoder.write_map_header(props.size()); - for(auto& prop : props) { + for (auto &prop : props) { write(prop.first); write(*prop.second); } } - void write(const Property& prop) - { - accept(prop, *this); - } + void write(const Property &prop) { accept(prop, *this); } - void write_null() - { - encoder.write_null(); - } + void write_null() { encoder.write_null(); } - void write(const Bool& prop) - { - encoder.write_bool(prop.value()); - } + void write(const Bool &prop) { encoder.write_bool(prop.value()); } - void write(const Float& prop) - { - encoder.write_double(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 Double &prop) { encoder.write_double(prop.value); } - void write(const Int32& prop) - { - encoder.write_integer(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 Int64 &prop) { encoder.write_integer(prop.value); } - void write(const std::string& value) - { - encoder.write_string(value); - } + void write(const std::string &value) { encoder.write_string(value); } - void write(const String& prop) - { - encoder.write_string(prop.value); - } + void write(const String &prop) { encoder.write_string(prop.value); } template - void handle(const T& prop) + void handle(const T &prop) { write(prop); } protected: - Stream& encoder; + Stream &encoder; }; - } diff --git a/include/database/db.hpp b/include/database/db.hpp index 8cee20063..626bb7dd7 100644 --- a/include/database/db.hpp +++ b/include/database/db.hpp @@ -1,25 +1,22 @@ #pragma once #include "storage/graph.hpp" +// #include "transactions/commit_log.hpp" #include "transactions/engine.hpp" -#include "transactions/commit_log.hpp" class Db { public: using sptr = std::shared_ptr; - Db() = default; - Db(const std::string& name) : name_(name) {} - Db(const Db& db) = delete; + Db(); + Db(const std::string &name); + Db(const Db &db) = delete; Graph graph; tx::Engine tx_engine; - std::string& name() - { - return name_; - } + std::string &name(); private: std::string name_; diff --git a/include/database/db_accessor.hpp b/include/database/db_accessor.hpp new file mode 100644 index 000000000..e4cbd86a6 --- /dev/null +++ b/include/database/db_accessor.hpp @@ -0,0 +1,91 @@ +#pragma once + +#include "database/db.hpp" +#include "database/db_accessor.hpp" +#include "storage/record_accessor.hpp" +#include "storage/vertex.hpp" +#include "storage/vertex_accessor.hpp" +#include "storage/vertices.hpp" +#include "transactions/transaction.hpp" +#include "utils/iterator/iterator.hpp" +#include "utils/option.hpp" + +/* +* DbAccessor +* -Guarantees that access to Vertex and Edge is possible only through +* Vertex::Accessor and Edge::Accessor. +* -Guarantees that changing Vertex and Edge is possible only using +* Vertex::Accessor returned by vertex_insert() method and +* Edge::Accessor returned by edge_insert() method. +* -Offers CRUD for Vertex and Edge except iterating over all edges. +* +* Vertex::Accessor +* By default Vertex::accessor is empty. Caller has to call fill() method +* to fetch valid data and check it's return value. fill() method returns +* true if there is valid data for current transaction false otherwise. +* Only exception to this rule is vertex_insert() method in DbAccessor +* which returns by default filled Vertex::Accessor. +* +* Edge::Accessor +* By default Edge::accessor is empty. Caller has to call fill() method +* to +* fetch valid data and check it's return value. fill() method returns +* true +* if there is valid data for current transaction false otherwise. +* Only exception to this rule is edge_insert() method in DbAccessor +* which +* returns by default filled Edge::Accessor. +*/ +class DbAccessor +{ + +public: + DbAccessor(Db &db); + + //*******************VERTEX METHODS + + auto vertex_access(); + + Option vertex_find(const Id &id); + + // Creates new Vertex and returns filled Vertex::Accessor. + Vertex::Accessor vertex_insert(); + + //*******************EDGE METHODS + + Option edge_find(const Id &id); + + // Creates new Edge and returns filled Edge::Accessor. + Edge::Accessor edge_insert(Vertex::Accessor const &from, + Vertex::Accessor const &to); + + //*******************LABEL METHODS + + const Label &label_find_or_create(const std::string &name); + + bool label_contains(const std::string &name); + + VertexIndexRecordCollection &label_find_index(const Label &label); + + //********************TYPE METHODS + + const EdgeType &type_find_or_create(const std::string &name); + + bool type_contains(const std::string &name); + + //********************TRANSACTION METHODS + + void commit(); + void abort(); + +private: + DbTransaction db; +}; + +//**********************CONVENIENT FUNCTIONS + +template +bool option_fill(Option &o) +{ + return o.is_present() && o.get().fill(); +} diff --git a/include/database/db_transaction.hpp b/include/database/db_transaction.hpp new file mode 100644 index 000000000..a7b862c82 --- /dev/null +++ b/include/database/db_transaction.hpp @@ -0,0 +1,25 @@ +#pragma once + +#include "storage/indexes/index_record.hpp" +#include "storage/label/label.hpp" +#include "transactions/transaction.hpp" + +class Db; +class DbAccessor; + +// Inner structures local to transaction can hold ref to this structure and use +// its methods. +class DbTransaction +{ + friend DbAccessor; + +public: + DbTransaction(Db &db, tx::Transaction &trans) : db(db), trans(trans) {} + + void update_label_index(const Label &label, + VertexIndexRecord &&index_record); + + tx::Transaction &trans; + + Db &db; +}; diff --git a/include/query_engine/hardcode/queries.hpp b/include/query_engine/hardcode/queries.hpp index 0c19af658..5ef8dbfe5 100644 --- a/include/query_engine/hardcode/queries.hpp +++ b/include/query_engine/hardcode/queries.hpp @@ -1,6 +1,8 @@ #pragma once #include "database/db.hpp" +#include "database/db_accessor.cpp" +#include "database/db_accessor.hpp" #include "query_engine/query_stripper.hpp" #include "query_engine/util.hpp" #include "storage/model/properties/property.hpp" @@ -12,8 +14,8 @@ auto load_queries(Db &db) // CREATE (n {prop: 0}) RETURN n) auto create_node = [&db](const properties_t &args) { - auto &t = db.tx_engine.begin(); - auto vertex_accessor = db.graph.vertices.insert(t); + DbAccessor t(db); + auto vertex_accessor = t.vertex_insert(); vertex_accessor.property("prop", args[0]); t.commit(); return true; @@ -21,10 +23,10 @@ auto load_queries(Db &db) queries[11597417457737499503u] = create_node; auto create_labeled_and_named_node = [&db](const properties_t &args) { - auto &t = db.tx_engine.begin(); - auto vertex_accessor = db.graph.vertices.insert(t); + DbAccessor t(db); + auto vertex_accessor = t.vertex_insert(); vertex_accessor.property("name", args[0]); - auto &label = db.graph.label_store.find_or_create("LABEL"); + auto &label = t.label_find_or_create("LABEL"); vertex_accessor.add_label(label); cout_properties(vertex_accessor.properties()); t.commit(); @@ -32,13 +34,13 @@ auto load_queries(Db &db) }; auto create_account = [&db](const properties_t &args) { - auto &t = db.tx_engine.begin(); - auto vertex_accessor = db.graph.vertices.insert(t); + DbAccessor t(db); + auto vertex_accessor = t.vertex_insert(); vertex_accessor.property("id", args[0]); vertex_accessor.property("name", args[1]); vertex_accessor.property("country", args[2]); vertex_accessor.property("created_at", args[3]); - auto &label = db.graph.label_store.find_or_create("ACCOUNT"); + auto &label = t.label_find_or_create("ACCOUNT"); vertex_accessor.add_label(label); cout_properties(vertex_accessor.properties()); t.commit(); @@ -46,14 +48,15 @@ auto load_queries(Db &db) }; auto find_node_by_internal_id = [&db](const properties_t &args) { - auto &t = db.tx_engine.begin(); + DbAccessor t(db); auto id = static_cast(*args[0]); - auto vertex_accessor = db.graph.vertices.find(t, Id(id.value)); - if (!vertex_accessor) { + auto maybe_va = t.vertex_find(Id(id.value)); + if (!option_fill(maybe_va)) { cout << "vertex doesn't exist" << endl; t.commit(); return false; } + auto vertex_accessor = maybe_va.get(); cout_properties(vertex_accessor.properties()); cout << "LABELS:" << endl; for (auto label_ref : vertex_accessor.labels()) { @@ -64,20 +67,17 @@ auto load_queries(Db &db) }; auto create_edge = [&db](const properties_t &args) { - auto &t = db.tx_engine.begin(); + DbAccessor t(db); - auto v1 = db.graph.vertices.find(t, args[0]->as().value); - if (!v1) return t.commit(), false; + auto v1 = t.vertex_find(args[0]->as().value); + if (!option_fill(v1)) return t.commit(), false; - auto v2 = db.graph.vertices.find(t, args[1]->as().value); - if (!v2) return t.commit(), false; + auto v2 = t.vertex_find(args[1]->as().value); + if (!option_fill(v2)) return t.commit(), false; - auto edge_accessor = db.graph.edges.insert(t, v1.vlist, v2.vlist); + auto edge_accessor = t.edge_insert(v1.get(), v2.get()); - v1.vlist->update(t)->data.out.add(edge_accessor.vlist); - v2.vlist->update(t)->data.in.add(edge_accessor.vlist); - - auto &edge_type = db.graph.edge_type_store.find_or_create("IS"); + auto &edge_type = t.type_find_or_create("IS"); edge_accessor.edge_type(edge_type); t.commit(); @@ -90,20 +90,25 @@ auto load_queries(Db &db) }; auto find_edge_by_internal_id = [&db](const properties_t &args) { - auto &t = db.tx_engine.begin(); - auto edge_accessor = db.graph.edges.find(t, args[0]->as().value); - if (!edge_accessor) return t.commit(), false; + DbAccessor t(db); + auto maybe_ea = t.edge_find(args[0]->as().value); + if (!option_fill(maybe_ea)) return t.commit(), false; + auto edge_accessor = maybe_ea.get(); // print edge type and properties cout << "EDGE_TYPE: " << edge_accessor.edge_type() << endl; auto from = edge_accessor.from(); + if (!from.fill()) return t.commit(), false; + cout << "FROM:" << endl; - cout_properties(from->find(t)->data.props); + cout_properties(from->data.props); auto to = edge_accessor.to(); + if (!to.fill()) return t.commit(), false; + cout << "TO:" << endl; - cout_properties(to->find(t)->data.props); + cout_properties(to->data.props); t.commit(); @@ -111,10 +116,11 @@ auto load_queries(Db &db) }; auto update_node = [&db](const properties_t &args) { - auto &t = db.tx_engine.begin(); + DbAccessor t(db); - auto v = db.graph.vertices.find(t, args[0]->as().value); - if (!v) return t.commit(), false; + auto maybe_v = t.vertex_find(args[0]->as().value); + if (!option_fill(maybe_v)) return t.commit(), false; + auto v = maybe_v.get(); v.property("name", args[1]); cout_properties(v.properties()); @@ -127,18 +133,17 @@ auto load_queries(Db &db) // MATCH (n1), (n2) WHERE ID(n1)=0 AND ID(n2)=1 CREATE (n1)<-[r:IS {age: 25, // weight: 70}]-(n2) RETURN r auto create_edge_v2 = [&db](const properties_t &args) { - auto &t = db.tx_engine.begin(); - auto n1 = db.graph.vertices.find(t, args[0]->as().value); - if (!n1) return t.commit(), false; - auto n2 = db.graph.vertices.find(t, args[1]->as().value); - if (!n2) return t.commit(), false; - auto r = db.graph.edges.insert(t, n2.vlist, n1.vlist); + DbAccessor t(db); + auto n1 = t.vertex_find(args[0]->as().value); + if (!option_fill(n1)) return t.commit(), false; + auto n2 = t.vertex_find(args[1]->as().value); + if (!option_fill(n2)) return t.commit(), false; + auto r = t.edge_insert(n2.get(), n1.get()); r.property("age", args[2]); r.property("weight", args[3]); - auto &IS = db.graph.edge_type_store.find_or_create("IS"); + auto &IS = t.type_find_or_create("IS"); r.edge_type(IS); - n2.vlist->update(t)->data.out.add(r.vlist); - n1.vlist->update(t)->data.in.add(r.vlist); + t.commit(); return true; }; @@ -146,14 +151,13 @@ auto load_queries(Db &db) // MATCH (n) RETURN n auto match_all_nodes = [&db](const properties_t &args) { - auto &t = db.tx_engine.begin(); + DbAccessor t(db); - auto vertices_accessor = db.graph.vertices.access(); - for (auto &it : vertices_accessor) { - auto vertex = it.second.find(t); - if (vertex == nullptr) continue; - cout_properties(vertex->data.props); - } + iter::for_all(t.vertex_access(), [&](auto vertex) { + if (vertex.fill()) { + cout_properties(vertex->data.props); + } + }); // TODO // db.graph.vertices.filter().all(t, handler); @@ -166,12 +170,11 @@ auto load_queries(Db &db) // MATCH (n:LABEL) RETURN n auto find_by_label = [&db](const properties_t &args) { - auto &t = db.tx_engine.begin(); + DbAccessor t(db); - auto &label = db.graph.label_store.find_or_create("LABEL"); + auto &label = t.label_find_or_create("LABEL"); - auto &index_record_collection = - db.graph.vertices.find_label_index(label); + auto &index_record_collection = t.label_find_index(label); auto accessor = index_record_collection.access(); cout << "VERTICES" << endl; for (auto &v : accessor) { diff --git a/include/storage/edge_accessor.hpp b/include/storage/edge_accessor.hpp index 192ce2884..c65ea5e0c 100644 --- a/include/storage/edge_accessor.hpp +++ b/include/storage/edge_accessor.hpp @@ -3,31 +3,23 @@ #include "storage/edge.hpp" #include "storage/edge_record.hpp" #include "storage/record_accessor.hpp" +#include "storage/vertex_accessor.hpp" #include "utils/assert.hpp" #include "utils/reference_wrapper.hpp" class Edges; // TODO: Edge, Db, Edge::Accessor -class Edge::Accessor - : public RecordAccessor +class Edge::Accessor : public RecordAccessor { public: using RecordAccessor::RecordAccessor; - void edge_type(edge_type_ref_t edge_type) - { - this->record->data.edge_type = &edge_type.get(); - } + void edge_type(edge_type_ref_t edge_type); - edge_type_ref_t edge_type() const - { - runtime_assert(this->record->data.edge_type != nullptr, - "EdgeType is null"); - return edge_type_ref_t(*this->record->data.edge_type); - } + edge_type_ref_t edge_type() const; - auto from() const { return this->vlist->from(); } + Vertex::Accessor from() const; - auto to() const { return this->vlist->to(); } + Vertex::Accessor to() const; }; diff --git a/include/storage/edge_record.hpp b/include/storage/edge_record.hpp index e04cde31b..aa1471abe 100644 --- a/include/storage/edge_record.hpp +++ b/include/storage/edge_record.hpp @@ -27,7 +27,7 @@ public: auto to() const { return this->to_v; } -private: +protected: VertexRecord *from_v; VertexRecord *to_v; }; diff --git a/include/storage/edges.hpp b/include/storage/edges.hpp index 85e2e1cdd..660962be8 100644 --- a/include/storage/edges.hpp +++ b/include/storage/edges.hpp @@ -4,12 +4,15 @@ #include "mvcc/version_list.hpp" #include "storage/common.hpp" #include "storage/edge_accessor.hpp" +#include "utils/option.hpp" class Edges { public: - Edge::Accessor find(tx::Transaction &t, const Id &id); - Edge::Accessor insert(tx::Transaction &t, VertexRecord *from, + Option find(DbTransaction &t, const Id &id); + + // Creates new Edge and returns filled Edge::Accessor. + Edge::Accessor insert(DbTransaction &t, VertexRecord *from, VertexRecord *to); private: diff --git a/include/storage/model/edge_map.hpp b/include/storage/model/edge_map.hpp index 8195351aa..e8d109cc5 100644 --- a/include/storage/model/edge_map.hpp +++ b/include/storage/model/edge_map.hpp @@ -23,7 +23,7 @@ public: edges.remove(edge); // Currently the return is ignored } - bool contains(VertexRecord *vr) { return edges.contains(vr); } + bool contains(VertexRecord *vr) const { return edges.contains(vr); } void clear() { edges.clear(); } diff --git a/include/storage/record_accessor.hpp b/include/storage/record_accessor.hpp index 327d43e27..e07f6da6f 100644 --- a/include/storage/record_accessor.hpp +++ b/include/storage/record_accessor.hpp @@ -1,45 +1,60 @@ #pragma once +#include "database/db_transaction.hpp" #include "mvcc/version_list.hpp" #include "storage/model/properties/properties.hpp" #include "storage/model/properties/property.hpp" #include "transactions/transaction.hpp" -template > +template > class RecordAccessor { -public: - RecordAccessor() = default; + friend DbAccessor; - RecordAccessor(T *record, vlist_t *vlist, Store *store) - : record(record), vlist(vlist), store(store) +public: + RecordAccessor(vlist_t *vlist, DbTransaction &db) : vlist(vlist), db(db) + { + assert(vlist != nullptr); + } + + RecordAccessor(T *t, vlist_t *vlist, DbTransaction &db) + : record(t), vlist(vlist), db(db) { assert(record != nullptr); assert(vlist != nullptr); - assert(store != nullptr); } + RecordAccessor(RecordAccessor const &other) = default; + RecordAccessor(RecordAccessor &&other) = default; + bool empty() const { return record == nullptr; } + // Fills accessor and returns true if there is valid data for current + // transaction false otherwise. + bool fill() const + { + const_cast(this)->record = vlist->find(db.trans); + return record != nullptr; + } + const Id &id() const { assert(!empty()); return vlist->id; } - Derived update(tx::Transaction &t) const + Derived update() const { assert(!empty()); - return Derived(vlist->update(t), vlist, store); + return Derived(vlist->update(db.trans), vlist, db); } - bool remove(tx::Transaction &t) const + bool remove() const { assert(!empty()); - return vlist->remove(record, t); + return vlist->remove(record, db.trans); } const Property &property(const std::string &key) const @@ -62,8 +77,23 @@ public: explicit operator bool() const { return record != nullptr; } - // protected: - T *const record{nullptr}; - vlist_t *const vlist{nullptr}; - Store *const store{nullptr}; + T const *operator->() const { return record; } + T *operator->() { return record; } + + // Assumes same transaction + friend bool operator==(const RecordAccessor &a, const RecordAccessor &b) + { + return a.vlist == b.vlist; + } + + // Assumes same transaction + friend bool operator!=(const RecordAccessor &a, const RecordAccessor &b) + { + return !(a == b); + } + +protected: + T *record{nullptr}; + vlist_t *const vlist; + DbTransaction &db; }; diff --git a/include/storage/vertex.hpp b/include/storage/vertex.hpp index 9d8f8e294..d73b4f32d 100644 --- a/include/storage/vertex.hpp +++ b/include/storage/vertex.hpp @@ -1,8 +1,8 @@ #pragma once #include "mvcc/record.hpp" -#include "storage/model/vertex_model.hpp" #include "storage/model/properties/traversers/jsonwriter.hpp" +#include "storage/model/vertex_model.hpp" class Vertex : public mvcc::Record { @@ -10,19 +10,19 @@ public: class Accessor; Vertex() = default; - Vertex(const VertexModel& data) : data(data) {} - Vertex(VertexModel&& data) : data(std::move(data)) {} + Vertex(const VertexModel &data) : data(data) {} + Vertex(VertexModel &&data) : data(std::move(data)) {} - Vertex(const Vertex&) = delete; - Vertex(Vertex&&) = delete; + Vertex(const Vertex &) = delete; + Vertex(Vertex &&) = delete; - Vertex& operator=(const Vertex&) = delete; - Vertex& operator=(Vertex&&) = delete; + Vertex &operator=(const Vertex &) = delete; + Vertex &operator=(Vertex &&) = delete; VertexModel data; }; -inline std::ostream& operator<<(std::ostream& stream, const Vertex& record) +inline std::ostream &operator<<(std::ostream &stream, const Vertex &record) { StringBuffer buffer; JsonWriter writer(buffer); @@ -33,6 +33,5 @@ inline std::ostream& operator<<(std::ostream& stream, const Vertex& record) return stream << "Vertex" << "(cre = " << record.tx.cre() - << ", exp = " << record.tx.exp() - << "): " << buffer.str(); + << ", exp = " << record.tx.exp() << "): " << buffer.str(); } diff --git a/include/storage/vertex_accessor.hpp b/include/storage/vertex_accessor.hpp index 6c02f2595..0222413ed 100644 --- a/include/storage/vertex_accessor.hpp +++ b/include/storage/vertex_accessor.hpp @@ -5,8 +5,7 @@ class Vertices; -class Vertex::Accessor - : public RecordAccessor +class Vertex::Accessor : public RecordAccessor { public: using RecordAccessor::RecordAccessor; @@ -21,5 +20,12 @@ public: bool has_label(const Label &label) const; - const std::set& labels() const; + const std::set &labels() const; + + auto out() const; + + auto in() const; + + // True if there exists edge other->this + bool in_contains(Vertex::Accessor const &other) const; }; diff --git a/include/storage/vertices.hpp b/include/storage/vertices.hpp index 9b4909ebe..8f5b78299 100644 --- a/include/storage/vertices.hpp +++ b/include/storage/vertices.hpp @@ -1,10 +1,12 @@ #pragma once #include "data_structures/concurrent/concurrent_map.hpp" +#include "database/db_transaction.hpp" #include "storage/common.hpp" #include "storage/indexes/index.hpp" #include "storage/indexes/index_record_collection.hpp" #include "storage/vertex_accessor.hpp" +#include "utils/option.hpp" class Vertices { @@ -13,17 +15,16 @@ public: vertices_t::Accessor access(); - const Vertex::Accessor find(tx::Transaction &t, const Id &id); + Option find(DbTransaction &t, const Id &id); - const Vertex::Accessor first(tx::Transaction &t); - - Vertex::Accessor insert(tx::Transaction &t); + // Creates new Vertex and returns filled Vertex::Accessor. + Vertex::Accessor insert(DbTransaction &t); void update_label_index(const Label &label, VertexIndexRecord &&index_record); - VertexIndexRecordCollection& find_label_index(const Label& label); - + VertexIndexRecordCollection &find_label_index(const Label &label); + private: vertices_t vertices; Index label_index; diff --git a/include/utils/iterator/accessor.hpp b/include/utils/iterator/accessor.hpp new file mode 100644 index 000000000..88d5f32b6 --- /dev/null +++ b/include/utils/iterator/accessor.hpp @@ -0,0 +1,28 @@ +#pragma once + +#include "utils/iterator/wrap.hpp" +#include "utils/option.hpp" + +namespace iter +{ +template +class OneTimeAccessor +{ +public: + OneTimeAccessor() : it(Option>()) {} + OneTimeAccessor(I &&it) : it(Wrap(std::move(it))) {} + + Wrap begin() { return it.take(); } + + Wrap end() { return Wrap(); } + +private: + Option> it; +}; + +template +auto make_one_time_accessor(I &&iter) +{ + return OneTimeAccessor(std::move(iter)); +} +} diff --git a/include/utils/iterator/for_all.hpp b/include/utils/iterator/for_all.hpp new file mode 100644 index 000000000..ba5b8f2dc --- /dev/null +++ b/include/utils/iterator/for_all.hpp @@ -0,0 +1,17 @@ + +#pragma once + +#include "utils/option.hpp" + +namespace iter +{ +template +void for_all(I &&iter, C &&consumer) +{ + auto e = iter.next(); + while (e.is_present()) { + consumer(e.take()); + e = iter.next(); + } +} +} diff --git a/include/utils/iterator/iter.hpp b/include/utils/iterator/iter.hpp new file mode 100644 index 000000000..cfb3c974a --- /dev/null +++ b/include/utils/iterator/iter.hpp @@ -0,0 +1,48 @@ +#pragma once + +#include "utils/option.hpp" + +namespace iter +{ +template +class Iter +{ +public: + Iter() = delete; + + Iter(A &&acc) : begin(std::move(acc.begin())), acc(std::forward(acc)) {} + // Iter(const Iter &other) = delete; + // Iter(Iter &&other) : + // begin(std::move(other.begin)),end(std::move(other.end)) {}; + + auto next() + { + if (begin != acc.end()) { + auto ret = Option(&(*(begin.operator->()))); + begin++; + return ret; + } else { + return Option(); + } + } + +private: + I begin; + A acc; +}; + +// TODO: Join to make functions into one +template +auto make_iter(A &&acc) +{ + return Iter()))), decltype(acc.begin()), + A>(std::move(acc)); +} + +template +auto make_iter_ref(A &acc) +{ + return Iter()))), decltype(acc.begin()), + A &>(acc); +} +} diff --git a/include/utils/iterator/iterator.hpp b/include/utils/iterator/iterator.hpp new file mode 100644 index 000000000..e3e8c1cd1 --- /dev/null +++ b/include/utils/iterator/iterator.hpp @@ -0,0 +1,7 @@ +#pragma once + +#include "utils/iterator/accessor.hpp" +#include "utils/iterator/for_all.hpp" +#include "utils/iterator/iter.hpp" +#include "utils/iterator/map.hpp" +#include "utils/iterator/wrap.hpp" diff --git a/include/utils/iterator/map.hpp b/include/utils/iterator/map.hpp new file mode 100644 index 000000000..7bc8ba474 --- /dev/null +++ b/include/utils/iterator/map.hpp @@ -0,0 +1,39 @@ +#pragma once + +#include "utils/option.hpp" + +namespace iter +{ +template +class Map +{ + +public: + Map() = delete; + template + Map(IT &&iter, OP &&op) : iter(std::move(iter)), op(std::move(op)) + { + } + + auto next() + { + auto item = iter.next(); + if (item.is_present()) { + return Option(op(item.take())); + } else { + return Option(); + } + } + +private: + I iter; + MapOperator op; +}; + +template +auto make_map(I &&iter, OP &&op) +{ + return Map(std::move(iter), + std::move(op)); +} +} diff --git a/include/utils/iterator/wrap.hpp b/include/utils/iterator/wrap.hpp new file mode 100644 index 000000000..a39fe6d2c --- /dev/null +++ b/include/utils/iterator/wrap.hpp @@ -0,0 +1,60 @@ +#pragma once + +#include "utils/option.hpp" + +namespace iter +{ +template +class Wrap +{ + +public: + Wrap() : iter(Option()), value(Option()){}; + + Wrap(I &&iter) : value(iter.next()), iter(Option(std::move(iter))) {} + + T &operator*() + { + assert(value.is_present()); + return value.get(); + } + + T *operator->() + { + assert(value.is_present()); + return &value.get(); + } + + operator T &() + { + assert(value.is_present()); + return value.get(); + } + + Wrap &operator++() + { + assert(iter.is_present()); + value = iter.get().next(); + return (*this); + } + + Wrap &operator++(int) { return operator++(); } + + friend bool operator==(const Wrap &a, const Wrap &b) + { + return a.value.is_present() == b.value.is_present(); + } + + friend bool operator!=(const Wrap &a, const Wrap &b) { return !(a == b); } + +private: + Option iter; + Option value; +}; + +template +auto make_wrap(I &&iter) +{ + return Wrap(std::move(iter)); +} +} diff --git a/include/utils/option.hpp b/include/utils/option.hpp new file mode 100644 index 000000000..8e1b66d44 --- /dev/null +++ b/include/utils/option.hpp @@ -0,0 +1,95 @@ +#pragma once + +#include +#include + +template +class Option +{ +public: + Option() {} + + Option(T const &item) + { + new (data._M_addr()) T(item); + initialized = true; + } + + Option(T &&item) + { + new (data._M_addr()) T(std::move(item)); + initialized = true; + } + + Option(Option &other) = default; + Option(Option &&other) noexcept + { + if (other.initialized) { + data = std::move(other.data); + other.initialized = false; + initialized = true; + } + } + + ~Option() + { + if (initialized) get().~T(); + } + + Option &operator=(Option &&other) + { + if (initialized) { + get().~T(); + initialized = false; + } + + if (other.initialized) { + data = std::move(other.data); + other.initialized = false; + initialized = true; + } + + return *this; + } + + bool is_present() const { return initialized; } + + T &get() noexcept + { + assert(initialized); + return *data._M_ptr(); + } + + const T &get() const noexcept { assert(initialized); } + + T take() + { + assert(initialized); + initialized = false; + return std::move(*data._M_ptr()); + } + + explicit operator bool() const { return initialized; } + +private: + __gnu_cxx::__aligned_buffer data; + bool initialized = false; +}; + +template +auto make_option() +{ + return Option(); +} + +template +auto make_option(T &&data) +{ + return Option(std::move(data)); +} + +template +auto make_option_const(const T &&data) +{ + return Option(std::move(data)); +} diff --git a/include/utils/placeholder.hpp b/include/utils/placeholder.hpp index 2cdd5c2fa..a1eb889c8 100644 --- a/include/utils/placeholder.hpp +++ b/include/utils/placeholder.hpp @@ -1,7 +1,7 @@ #pragma once -#include #include +#include template class Placeholder @@ -9,40 +9,41 @@ class Placeholder public: Placeholder() = default; - Placeholder(Placeholder&) = delete; - Placeholder(Placeholder&&) = delete; + Placeholder(Placeholder &) = delete; + Placeholder(Placeholder &&) = delete; ~Placeholder() { - if(initialized) - get().~T(); + if (initialized) get().~T(); }; - T& get() noexcept + bool is_initialized() { return initialized; } + + T &get() noexcept { assert(initialized); return *data._M_ptr(); } - const T& get() const noexcept + const T &get() const noexcept { assert(initialized); return *data._M_ptr(); } - void set(const T& item) + void set(const T &item) { new (data._M_addr()) T(item); initialized = true; } - void set(T&& item) + void set(T &&item) { new (data._M_addr()) T(std::move(item)); initialized = true; } private: - __gnu_cxx::__aligned_buffer data; + __gnu_cxx::__aligned_buffer data; bool initialized = false; }; diff --git a/poc/astar.cpp b/poc/astar.cpp index f30f1664d..f22918129 100644 --- a/poc/astar.cpp +++ b/poc/astar.cpp @@ -10,9 +10,12 @@ #include "data_structures/map/rh_hashmap.hpp" #include "database/db.hpp" +#include "database/db_accessor.hpp" +#include "storage/vertex_accessor.cpp" +#include "storage/vertex_accessor.hpp" using namespace std; - +typedef Vertex::Accessor VertexAccessor; void load_graph_dummy(Db &db); void load_csv(Db &db, char *file_path, char *edge_file_path); @@ -22,20 +25,13 @@ public: Node *parent = {nullptr}; double cost; int depth = {0}; - Vertex *vertex; - VertexRecord *record; + VertexAccessor vacc; - Node(Vertex *va, VertexRecord *record, double cost) - : cost(cost), vertex(va), record(record) + Node(VertexAccessor vacc, double cost) : cost(cost), vacc(vacc) {} + Node(VertexAccessor vacc, double cost, Node *parent) + : cost(cost), vacc(vacc), parent(parent), depth(parent->depth + 1) { } - Node(Vertex *va, VertexRecord *record, double cost, Node *parent) - : cost(cost), vertex(va), parent(parent), depth(parent->depth + 1), - record(record) - { - } - - VertexRecord *&get_key() { return record; } }; // class Iterator : public Crtp @@ -82,59 +78,66 @@ void found_result(Node *bef) { std::cout << "{score: " << bef->cost << endl; while (bef != nullptr) { - std::cout << " " << *(bef->vertex) << endl; + std::cout << " " << *(bef->vacc.operator->()) << endl; bef = bef->parent; } } -double calc_heuristic_cost_dummy(Edge *edge, Vertex *vertex) +double calc_heuristic_cost_dummy(Edge::Accessor &edge, Vertex::Accessor &vertex) { + assert(!vertex.empty()); return 1 - vertex->data.props.at("score").as().value; } -typedef bool (*EdgeFilter)(tx::Transaction &t, EdgeRecord *, Node *before); -typedef bool (*VertexFilter)(tx::Transaction &t, Vertex *, Node *before); +typedef bool (*EdgeFilter)(DbAccessor &t, Edge::Accessor &, Node *before); +typedef bool (*VertexFilter)(DbAccessor &t, Vertex::Accessor &, Node *before); -bool edge_filter_dummy(tx::Transaction &t, EdgeRecord *e, Node *before) +bool edge_filter_dummy(DbAccessor &t, Edge::Accessor &e, Node *before) { return true; } -bool vertex_filter_dummy(tx::Transaction &t, Vertex *v, Node *before) +bool vertex_filter_dummy(DbAccessor &t, Vertex::Accessor &va, Node *before) { - return true; + return va.fill(); } -bool vertex_filter_contained_dummy(tx::Transaction &t, Vertex *v, Node *before) +bool vertex_filter_contained_dummy(DbAccessor &t, Vertex::Accessor &v, + Node *before) { - bool found; - do { - found = false; - before = before->parent; - if (before == nullptr) { - return true; - } - for (auto edge : before->vertex->data.out) { - Vertex *e_v = edge->to()->find(t); - if (e_v == v) { - found = true; - break; + if (v.fill()) { + bool found; + do { + found = false; + before = before->parent; + if (before == nullptr) { + return true; } - } - } while (found); + auto it = before->vacc.out(); + for (auto e = it.next(); e.is_present(); e = it.next()) { + VertexAccessor va = e.get().to(); + if (va == v) { + found = true; + break; + } + } + } while (found); + } return false; } -bool vertex_filter_contained(tx::Transaction &t, Vertex *v, Node *before) +bool vertex_filter_contained(DbAccessor &t, Vertex::Accessor &v, Node *before) { - bool found; - do { - found = false; - before = before->parent; - if (before == nullptr) { - return true; - } - } while (v->data.in.contains(before->record)); + if (v.fill()) { + bool found; + do { + found = false; + before = before->parent; + if (before == nullptr) { + return true; + } + } while (v.in_contains(before->vacc)); + } return false; } @@ -143,23 +146,24 @@ bool vertex_filter_contained(tx::Transaction &t, Vertex *v, Node *before) // Filtri vracaju true ako element zadovoljava uvjete. void a_star(Db &db, int64_t sys_id_start, uint max_depth, EdgeFilter e_filter[], VertexFilter v_filter[], - double (*calc_heuristic_cost)(Edge *edge, Vertex *vertex), + double (*calc_heuristic_cost)(Edge::Accessor &edge, + Vertex::Accessor &vertex), int limit) { - auto &t = db.tx_engine.begin(); - RhHashMap visited; + DbAccessor t(db); auto cmp = [](Node *left, Node *right) { return left->cost > right->cost; }; std::priority_queue, decltype(cmp)> queue(cmp); - auto start_vr = db.graph.vertices.find(t, sys_id_start).vlist; - Node *start = new Node(start_vr->find(t), start_vr, 0); + auto start_vr = t.vertex_find(sys_id_start); + assert(start_vr); + start_vr.get().fill(); + Node *start = new Node(start_vr.take(), 0); queue.push(start); int count = 0; do { auto now = queue.top(); queue.pop(); - // if(!visited.insert(now)){ // continue; // } @@ -173,17 +177,16 @@ void a_star(Db &db, int64_t sys_id_start, uint max_depth, EdgeFilter e_filter[], continue; } - for (auto edge : now->vertex->data.out) { + iter::for_all(now->vacc.out(), [&](auto edge) { if (e_filter[now->depth](t, edge, now)) { - Vertex *v = edge->to()->find(t); - if (v_filter[now->depth](t, v, now)) { - Node *n = new Node( - v, edge->to(), - now->cost + calc_heuristic_cost(edge->find(t), v), now); + VertexAccessor va = edge.to(); + if (v_filter[now->depth](t, va, now)) { + auto cost = calc_heuristic_cost(edge, va); + Node *n = new Node(va, now->cost + cost, now); queue.push(n); } } - } + }); } while (!queue.empty()); std::cout << "Found: " << count << " resoults\n"; // TODO: GUBI SE MEMORIJA JER SE NODOVI NEBRISU @@ -266,7 +269,7 @@ void load_csv(Db &db, char *file_path, char *edge_file_path) std::string line; - auto &t = db.tx_engine.begin(); + DbAccessor t(db); int max_score = 1000000; // VERTEX import @@ -276,7 +279,7 @@ void load_csv(Db &db, char *file_path, char *edge_file_path) start_vertex_id = id; } - auto vertex_accessor = db.graph.vertices.insert(t); + auto vertex_accessor = t.vertex_insert(); vertex_accessor.property("id", std::make_shared(id)); vertex_accessor.property("garment_id", std::make_shared(gar_id)); vertex_accessor.property("garment_category_id", @@ -286,10 +289,11 @@ void load_csv(Db &db, char *file_path, char *edge_file_path) "score", std::make_shared((std::rand() % max_score) / (max_score + 0.0))); for (auto l_name : labels) { - auto &label = db.graph.label_store.find_or_create(l_name); + auto &label = t.label_find_or_create(l_name); vertex_accessor.add_label(label); } - return vertex_accessor.id(); + + return vertex_accessor; }; std::getline(file, line); @@ -305,11 +309,12 @@ void load_csv(Db &db, char *file_path, char *edge_file_path) auto splited = split(line, ','); vector labels(splited.begin() + 1, splited.begin() + splited.size() - 2); - auto id = v(stoi(splited[0]), labels, stoi(splited[splited.size() - 2]), - stoi(splited[splited.size() - 1])); + auto vacs = + v(stoi(splited[0]), labels, stoi(splited[splited.size() - 2]), + stoi(splited[splited.size() - 1])); - assert(va.size() == (uint64_t)id); - va.push_back(db.graph.vertices.find(t, id)); + assert(va.size() == (uint64_t)vacs.id()); + va.push_back(vacs); } // EDGE IMPORT @@ -318,12 +323,9 @@ void load_csv(Db &db, char *file_path, char *edge_file_path) auto v2 = va[to - start_vertex_id]; - auto edge_accessor = db.graph.edges.insert(t, v1.vlist, v2.vlist); + auto edge_accessor = t.edge_insert(v1, v2); - v1.vlist->update(t)->data.out.add(edge_accessor.vlist); - v2.vlist->update(t)->data.in.add(edge_accessor.vlist); - - auto &edge_type = db.graph.edge_type_store.find_or_create(type); + auto &edge_type = t.type_find_or_create(type); edge_accessor.edge_type(edge_type); }; @@ -343,9 +345,9 @@ void load_csv(Db &db, char *file_path, char *edge_file_path) void load_graph_dummy(Db &db) { - auto &t = db.tx_engine.begin(); + DbAccessor t(db); auto v = [&](auto id, auto score) { - auto vertex_accessor = db.graph.vertices.insert(t); + auto vertex_accessor = t.vertex_insert(); vertex_accessor.property("id", std::make_shared(id)); vertex_accessor.property("score", std::make_shared(score)); return vertex_accessor.id(); @@ -356,15 +358,13 @@ void load_graph_dummy(Db &db) }; auto e = [&](auto from, auto type, auto to) { - auto v1 = db.graph.vertices.find(t, va[from]); + auto v1 = t.vertex_find(va[from]); - auto v2 = db.graph.vertices.find(t, va[to]); + auto v2 = t.vertex_find(va[to]); - auto edge_accessor = db.graph.edges.insert(t, v1.vlist, v2.vlist); - v1.vlist->update(t)->data.out.add(edge_accessor.vlist); - v2.vlist->update(t)->data.in.add(edge_accessor.vlist); + auto edge_accessor = t.edge_insert(v1.get(), v2.get()); - auto &edge_type = db.graph.edge_type_store.find_or_create(type); + auto &edge_type = t.type_find_or_create(type); edge_accessor.edge_type(edge_type); }; diff --git a/src/cypher/tokenizer/cypher_lexer.hpp b/src/cypher/tokenizer/cypher_lexer.hpp index 42b4783ee..ed83cddfc 100644 --- a/src/cypher/tokenizer/cypher_lexer.hpp +++ b/src/cypher/tokenizer/cypher_lexer.hpp @@ -73,6 +73,6 @@ public: build(); } - CypherLexer(CypherLexer& other) = delete; - CypherLexer(CypherLexer&& other) = default; + CypherLexer(CypherLexer &other) = delete; + CypherLexer(CypherLexer &&other) = default; }; diff --git a/src/data_structures/map/rh_common.hpp b/src/data_structures/map/rh_common.hpp index 648666e56..69593bdb0 100644 --- a/src/data_structures/map/rh_common.hpp +++ b/src/data_structures/map/rh_common.hpp @@ -1,8 +1,9 @@ #pragma once -#include "utils/crtp.hpp" -#include "utils/option_ptr.hpp" +#include #include #include +#include "utils/crtp.hpp" +#include "utils/option_ptr.hpp" // RobinHood base. // Entrys are POINTERS alligned to 8B. @@ -240,6 +241,10 @@ protected: } Iterator create_it(size_t index) { return Iterator(this, index); } + ConstIterator create_it(size_t index) const + { + return ConstIterator(this, index); + } public: void clear() diff --git a/src/data_structures/map/rh_hashmultimap.hpp b/src/data_structures/map/rh_hashmultimap.hpp index e32a2f076..d46fcbc2a 100644 --- a/src/data_structures/map/rh_hashmultimap.hpp +++ b/src/data_structures/map/rh_hashmultimap.hpp @@ -1,9 +1,10 @@ +#include +#include #include "rh_common.hpp" #include "utils/crtp.hpp" #include "utils/likely.hpp" +#include "utils/option.hpp" #include "utils/option_ptr.hpp" -#include -#include // HashMultiMap with RobinHood collision resolution policy. // Single threaded. @@ -45,9 +46,30 @@ public: using typename base::ConstIterator; using typename base::Iterator; - bool contains(const K &key) { return find(key) != end(); } + bool contains(const K &key) const { return find_index(key).is_present(); } Iterator find(const K &key_in) + { + auto index = find_index(key_in); + if (index) { + return create_it(index.get()); + } else { + return end(); + } + } + + ConstIterator find(const K &key_in) const + { + auto index = find_index(key_in); + if (index) { + return create_it(index.get()); + } else { + return end(); + } + } + +private: + Option find_index(const K &key_in) const { if (count > 0) { auto key = std::ref(key_in); @@ -59,7 +81,7 @@ public: while (other.valid() && off < border) { auto other_off = other.off(); if (other_off == off && key == other.ptr()->get_key()) { - return create_it(now); + return Option(now); } else if (other_off < off) { // Other is rich break; @@ -73,9 +95,10 @@ public: } } - return end(); + return Option(); } +public: // Inserts element. void add(D *data) { add(data->get_key(), data); } @@ -211,7 +234,7 @@ public: private: // Skips same key valus as other. true if whole map is full of same key // values. - bool skip(size_t &now, Combined &other, size_t other_off, size_t mask) + bool skip(size_t &now, Combined &other, size_t other_off, size_t mask) const { auto other_key = other.ptr()->get_key(); size_t start = now; diff --git a/src/database/db.cpp b/src/database/db.cpp new file mode 100644 index 000000000..9ceb50a77 --- /dev/null +++ b/src/database/db.cpp @@ -0,0 +1,6 @@ +#include "database/db.hpp" + +Db::Db() = default; +Db::Db(const std::string &name) : name_(name) {} + +std::string &Db::name() { return name_; } diff --git a/src/database/db_accessor.cpp b/src/database/db_accessor.cpp new file mode 100644 index 000000000..b3972e29a --- /dev/null +++ b/src/database/db_accessor.cpp @@ -0,0 +1,75 @@ +#include "database/db_accessor.hpp" + +DbAccessor::DbAccessor(Db &db) : db(DbTransaction(db, db.tx_engine.begin())) {} + +// VERTEX METHODS +auto DbAccessor::vertex_access() +{ + return iter::make_map( + iter::make_iter(this->db.db.graph.vertices.access()), + [&](auto e) -> auto { return Vertex::Accessor(&(e->second), db); }); +} + +Option DbAccessor::vertex_find(const Id &id) +{ + return this->db.db.graph.vertices.find(db, id); +} + +Vertex::Accessor DbAccessor::vertex_insert() +{ + return this->db.db.graph.vertices.insert(db); +} + +// EDGE METHODS + +Option DbAccessor::edge_find(const Id &id) +{ + return db.db.graph.edges.find(db, id); +} + +Edge::Accessor DbAccessor::edge_insert(Vertex::Accessor const &from, + Vertex::Accessor const &to) +{ + auto edge_accessor = db.db.graph.edges.insert(db, from.vlist, to.vlist); + from.update()->data.out.add(edge_accessor.vlist); + to.update()->data.in.add(edge_accessor.vlist); + return edge_accessor; +} + +// LABEL METHODS +const Label &DbAccessor::label_find_or_create(const std::string &name) +{ + return db.db.graph.label_store.find_or_create( + std::forward(name)); +} + +bool DbAccessor::label_contains(const std::string &name) +{ + return db.db.graph.label_store.contains( + std::forward(name)); +} + +VertexIndexRecordCollection &DbAccessor::label_find_index(const Label &label) +{ + return db.db.graph.vertices.find_label_index(label); +} + +// TYPE METHODS +const EdgeType &DbAccessor::type_find_or_create(const std::string &name) +{ + return db.db.graph.edge_type_store.find_or_create( + std::forward(name)); +} + +bool DbAccessor::type_contains(const std::string &name) +{ + return db.db.graph.edge_type_store.contains( + std::forward(name)); +} + +// TRANSACTION METHODS +void DbAccessor::commit() { db.trans.commit(); } +void DbAccessor::abort() { db.trans.abort(); } + +// // EASE OF USE METHODS +// tx::Transaction &DbAccessor::operator*() { return db.trans; } diff --git a/src/database/db_transaction.cpp b/src/database/db_transaction.cpp new file mode 100644 index 000000000..3760ce479 --- /dev/null +++ b/src/database/db_transaction.cpp @@ -0,0 +1,8 @@ +#include "database/db.hpp" +#include "database/db_transaction.hpp" + +void DbTransaction::update_label_index(const Label &label, + VertexIndexRecord &&index_record) +{ + db.graph.vertices.update_label_index(label, std::move(index_record)); +} diff --git a/src/storage/edge_accessor.cpp b/src/storage/edge_accessor.cpp new file mode 100644 index 000000000..72f7e0582 --- /dev/null +++ b/src/storage/edge_accessor.cpp @@ -0,0 +1,22 @@ +#include "storage/edge_accessor.hpp" + +void Edge::Accessor::edge_type(edge_type_ref_t edge_type) +{ + this->record->data.edge_type = &edge_type.get(); +} + +edge_type_ref_t Edge::Accessor::edge_type() const +{ + runtime_assert(this->record->data.edge_type != nullptr, "EdgeType is null"); + return edge_type_ref_t(*this->record->data.edge_type); +} + +Vertex::Accessor Edge::Accessor::from() const +{ + return Vertex::Accessor(this->vlist->from(), this->db); +} + +Vertex::Accessor Edge::Accessor::to() const +{ + return Vertex::Accessor(this->vlist->to(), this->db); +} diff --git a/src/storage/edges.cpp b/src/storage/edges.cpp index 22a3d4810..5c142e9a8 100644 --- a/src/storage/edges.cpp +++ b/src/storage/edges.cpp @@ -1,21 +1,17 @@ #include "storage/edges.hpp" -Edge::Accessor Edges::find(tx::Transaction &t, const Id &id) +Option Edges::find(DbTransaction &t, const Id &id) { auto edges_accessor = edges.access(); auto edges_iterator = edges_accessor.find(id); - if (edges_iterator == edges_accessor.end()) return Edge::Accessor(); + if (edges_iterator == edges_accessor.end()) + return make_option(); - // find edge - auto edge = edges_iterator->second.find(t); - - if (edge == nullptr) return Edge::Accessor(); - - return Edge::Accessor(edge, &edges_iterator->second, this); + return make_option_const(Edge::Accessor(&edges_iterator->second, t)); } -Edge::Accessor Edges::insert(tx::Transaction &t, VertexRecord *from, +Edge::Accessor Edges::insert(DbTransaction &t, VertexRecord *from, VertexRecord *to) { // get next vertex id @@ -30,7 +26,7 @@ Edge::Accessor Edges::insert(tx::Transaction &t, VertexRecord *from, // create new vertex auto inserted_edge_record = result.first; - auto edge = inserted_edge_record->second.insert(t); + auto edge = inserted_edge_record->second.insert(t.trans); - return Edge::Accessor(edge, &inserted_edge_record->second, this); + return Edge::Accessor(edge, &inserted_edge_record->second, t); } diff --git a/src/storage/vertex_accessor.cpp b/src/storage/vertex_accessor.cpp index 380f30495..250910689 100644 --- a/src/storage/vertex_accessor.cpp +++ b/src/storage/vertex_accessor.cpp @@ -1,6 +1,7 @@ +#include "database/db.hpp" #include "storage/vertex_accessor.hpp" - #include "storage/vertices.hpp" +#include "utils/iterator/iterator.hpp" size_t Vertex::Accessor::out_degree() const { @@ -12,10 +13,7 @@ size_t Vertex::Accessor::in_degree() const return this->record->data.in.degree(); } -size_t Vertex::Accessor::degree() const -{ - return in_degree() + out_degree(); -} +size_t Vertex::Accessor::degree() const { return in_degree() + out_degree(); } void Vertex::Accessor::add_label(const Label &label) { @@ -23,8 +21,8 @@ void Vertex::Accessor::add_label(const Label &label) this->record->data.labels.add(label); // update index - this->store->update_label_index( - label, VertexIndexRecord(this->record, this->vlist)); + this->db.update_label_index(label, + VertexIndexRecord(this->record, this->vlist)); } bool Vertex::Accessor::has_label(const Label &label) const @@ -32,7 +30,30 @@ bool Vertex::Accessor::has_label(const Label &label) const return this->record->data.labels.has(label); } -const std::set& Vertex::Accessor::labels() const +const std::set &Vertex::Accessor::labels() const { return this->record->data.labels(); } + +// Returns unfilled accessors +auto Vertex::Accessor::out() const +{ + DbTransaction &t = this->db; + return iter::make_map( + iter::make_iter_ref(record->data.out), + [&](auto e) -> auto { return Edge::Accessor(*e, t); }); +} + +// Returns unfilled accessors +auto Vertex::Accessor::in() const +{ + DbTransaction &t = this->db; + return iter::make_one_time_accessor( + iter::make_map(iter::make_iter_ref(record->data.in), + [&](auto e) -> auto { return Edge::Accessor(e, t); })); +} + +bool Vertex::Accessor::in_contains(Vertex::Accessor const &other) const +{ + return record->data.in.contains(other.vlist); +} diff --git a/src/storage/vertices.cpp b/src/storage/vertices.cpp index ff94cb7dc..a6cf69c9a 100644 --- a/src/storage/vertices.cpp +++ b/src/storage/vertices.cpp @@ -1,41 +1,19 @@ #include "storage/vertices.hpp" -Vertices::vertices_t::Accessor Vertices::access() -{ - return vertices.access(); -} +Vertices::vertices_t::Accessor Vertices::access() { return vertices.access(); } -const Vertex::Accessor Vertices::find(tx::Transaction &t, const Id &id) +Option Vertices::find(DbTransaction &t, const Id &id) { auto vertices_accessor = vertices.access(); auto vertices_iterator = vertices_accessor.find(id); - if (vertices_iterator == vertices_accessor.end()) return Vertex::Accessor(); + if (vertices_iterator == vertices_accessor.end()) + return make_option(); - // find vertex - auto vertex = vertices_iterator->second.find(t); - - if (vertex == nullptr) return Vertex::Accessor(); - - return Vertex::Accessor(vertex, &vertices_iterator->second, this); + return make_option_const(Vertex::Accessor(&vertices_iterator->second, t)); } -// TODO -const Vertex::Accessor Vertices::first(tx::Transaction &t) -{ - auto vertices_accessor = vertices.access(); - auto vertices_iterator = vertices_accessor.begin(); - - if (vertices_iterator == vertices_accessor.end()) return Vertex::Accessor(); - - auto vertex = vertices_iterator->second.find(t); - - if (vertex == nullptr) return Vertex::Accessor(); - - return Vertex::Accessor(vertex, &vertices_iterator->second, this); -} - -Vertex::Accessor Vertices::insert(tx::Transaction &t) +Vertex::Accessor Vertices::insert(DbTransaction &t) { // get next vertex id auto next = counter.next(); @@ -50,9 +28,9 @@ Vertex::Accessor Vertices::insert(tx::Transaction &t) // create new vertex auto inserted_vertex_record = result.first; - auto vertex = inserted_vertex_record->second.insert(t); + auto vertex = inserted_vertex_record->second.insert(t.trans); - return Vertex::Accessor(vertex, &inserted_vertex_record->second, this); + return Vertex::Accessor(vertex, &inserted_vertex_record->second, t); } void Vertices::update_label_index(const Label &label, diff --git a/tests/concurrent/common.h b/tests/concurrent/common.h index 2a729b9ce..934c29a87 100644 --- a/tests/concurrent/common.h +++ b/tests/concurrent/common.h @@ -14,6 +14,8 @@ #include "data_structures/concurrent/skiplist.hpp" #include "data_structures/static_array.hpp" #include "utils/assert.hpp" +#include "logging/default.hpp" +#include "logging/streams/stdout.hpp" #include "utils/sysinfo/memory.hpp" using std::cout; @@ -231,3 +233,9 @@ void memory_check(size_t no_threads, std::function f) std::cout << "leaked: " << leaked << "\n"; permanent_assert(leaked <= 0, "Memory leak check"); } + +//Initializes loging faccilityes +void init_log(){ + logging::init_async(); + logging::log->pipe(std::make_unique()); +} diff --git a/tests/concurrent/linkedlist.cpp b/tests/concurrent/linkedlist.cpp index a05d29647..2539a2503 100644 --- a/tests/concurrent/linkedlist.cpp +++ b/tests/concurrent/linkedlist.cpp @@ -2,6 +2,7 @@ #include #include +#include "common.h" #include "data_structures/linked_list.hpp" using std::cout; @@ -10,51 +11,52 @@ using std::endl; template void test_concurrent_list_access(list_type &list, std::size_t size) { - // test concurrent access - for (int i = 0; i < 1000000; ++i) { + // test concurrent access + for (int i = 0; i < 1000000; ++i) { - std::thread t1([&list] { - list.push_front(1); - list.pop_front(); - }); + std::thread t1([&list] { + list.push_front(1); + list.pop_front(); + }); - std::thread t2([&list] { - list.push_front(2); - list.pop_front(); - }); + std::thread t2([&list] { + list.push_front(2); + list.pop_front(); + }); - t1.join(); - t2.join(); + t1.join(); + t2.join(); - assert(list.size() == size); - } + assert(list.size() == size); + } } int main() { - LinkedList list; + init_log(); + LinkedList list; - // push & pop operations - list.push_front(10); - list.push_front(20); - auto a = list.front(); - assert(a == 20); - list.pop_front(); - a = list.front(); - assert(a == 10); - list.pop_front(); - assert(list.size() == 0); + // push & pop operations + list.push_front(10); + list.push_front(20); + auto a = list.front(); + assert(a == 20); + list.pop_front(); + a = list.front(); + assert(a == 10); + list.pop_front(); + assert(list.size() == 0); - // concurrent test - LinkedList concurrent_list; - concurrent_list.push_front(1); - concurrent_list.push_front(1); - std::list no_concurrent_list; - no_concurrent_list.push_front(1); - no_concurrent_list.push_front(1); + // concurrent test + LinkedList concurrent_list; + concurrent_list.push_front(1); + concurrent_list.push_front(1); + std::list no_concurrent_list; + no_concurrent_list.push_front(1); + no_concurrent_list.push_front(1); - test_concurrent_list_access(concurrent_list, 2); - // test_concurrent_list_access(no_concurrent_list, 2); + test_concurrent_list_access(concurrent_list, 2); + // test_concurrent_list_access(no_concurrent_list, 2); - return 0; + return 0; } diff --git a/tests/concurrent/sl_insert.cpp b/tests/concurrent/sl_insert.cpp index e1d53f8eb..a0b83e18f 100644 --- a/tests/concurrent/sl_insert.cpp +++ b/tests/concurrent/sl_insert.cpp @@ -9,6 +9,7 @@ constexpr size_t key_range = elems_per_thread * THREADS_NO * 2; // Test checks for missing data and changed/overwriten data. int main() { + init_log(); memory_check(THREADS_NO, [] { map_t skiplist; diff --git a/tests/concurrent/sl_insert_competetive.cpp b/tests/concurrent/sl_insert_competetive.cpp index 2c78b073a..8050a377c 100644 --- a/tests/concurrent/sl_insert_competetive.cpp +++ b/tests/concurrent/sl_insert_competetive.cpp @@ -10,6 +10,7 @@ constexpr size_t key_range = elems_per_thread * THREADS_NO * 2; // Test checks for missing data and changed/overwriten data. int main() { + init_log(); memory_check(THREADS_NO, [] { map_t skiplist; diff --git a/tests/concurrent/sl_map.cpp b/tests/concurrent/sl_map.cpp index 094deb274..8b7684073 100644 --- a/tests/concurrent/sl_map.cpp +++ b/tests/concurrent/sl_map.cpp @@ -5,6 +5,7 @@ constexpr size_t elems_per_thread = 1e5; int main() { + init_log(); memory_check(THREADS_NO, [&] { ds::static_array threads; map_t skiplist; diff --git a/tests/concurrent/sl_memory.cpp b/tests/concurrent/sl_memory.cpp index 486385711..cc7522a3b 100644 --- a/tests/concurrent/sl_memory.cpp +++ b/tests/concurrent/sl_memory.cpp @@ -7,18 +7,20 @@ constexpr size_t elements = 2e6; // Test for simple memory leaks int main() { - memory_check(THREADS_NO, [] { - map_t skiplist; + init_log(); + memory_check(THREADS_NO, [] { + map_t skiplist; - auto futures = run(THREADS_NO, skiplist, [](auto acc, auto index) { - for (size_t i = 0; i < elements; i++) { - acc.insert(i, index); - } - return index; + auto futures = + run(THREADS_NO, skiplist, [](auto acc, auto index) { + for (size_t i = 0; i < elements; i++) { + acc.insert(i, index); + } + return index; + }); + collect(futures); + + auto accessor = skiplist.access(); + check_size(accessor, elements); }); - collect(futures); - - auto accessor = skiplist.access(); - check_size(accessor, elements); - }); } diff --git a/tests/concurrent/sl_memory_leak.cpp b/tests/concurrent/sl_memory_leak.cpp index dca1e9a8d..c5e384ee5 100644 --- a/tests/concurrent/sl_memory_leak.cpp +++ b/tests/concurrent/sl_memory_leak.cpp @@ -6,6 +6,7 @@ constexpr size_t elems_per_thread = 16e5; // Known memory leak at 1,600,000 elements. int main() { + init_log(); memory_check(THREADS_NO, [&] { ds::static_array threads; map_t skiplist; diff --git a/tests/concurrent/sl_multiiterator.cpp b/tests/concurrent/sl_multiiterator.cpp index 187bea1ca..ebff8c0ca 100644 --- a/tests/concurrent/sl_multiiterator.cpp +++ b/tests/concurrent/sl_multiiterator.cpp @@ -13,6 +13,7 @@ constexpr size_t no_insert_for_one_delete = 1; // succeed. int main() { + init_log(); memory_check(THREADS_NO, [] { multimap_t skiplist; diff --git a/tests/concurrent/sl_multiiterator_remove.cpp b/tests/concurrent/sl_multiiterator_remove.cpp index 99d9127ea..e8b8e3d07 100644 --- a/tests/concurrent/sl_multiiterator_remove.cpp +++ b/tests/concurrent/sl_multiiterator_remove.cpp @@ -14,6 +14,7 @@ constexpr size_t no_insert_for_one_delete = 1; // succeed. int main() { + init_log(); memory_check(THREADS_NO, [] { multimap_t skiplist; diff --git a/tests/concurrent/sl_multiiterator_remove_duplicates.cpp b/tests/concurrent/sl_multiiterator_remove_duplicates.cpp index cd6de40f8..da2215ec0 100644 --- a/tests/concurrent/sl_multiiterator_remove_duplicates.cpp +++ b/tests/concurrent/sl_multiiterator_remove_duplicates.cpp @@ -15,6 +15,7 @@ constexpr size_t no_insert_for_one_delete = 2; // succeed. int main() { + init_log(); memory_check(THREADS_NO, [] { multimap_t skiplist; diff --git a/tests/concurrent/sl_multimap.cpp b/tests/concurrent/sl_multimap.cpp index 2fb838275..5953a4ca5 100644 --- a/tests/concurrent/sl_multimap.cpp +++ b/tests/concurrent/sl_multimap.cpp @@ -13,6 +13,7 @@ constexpr size_t no_insert_for_one_delete = 1; // succeed. int main() { + init_log(); memory_check(THREADS_NO, [] { multimap_t skiplist; std::atomic size(0); diff --git a/tests/concurrent/sl_multiset.cpp b/tests/concurrent/sl_multiset.cpp index e8cc159bf..eaca13ca0 100644 --- a/tests/concurrent/sl_multiset.cpp +++ b/tests/concurrent/sl_multiset.cpp @@ -11,6 +11,7 @@ constexpr size_t no_insert_for_one_delete = 1; // succeed. int main() { + init_log(); memory_check(THREADS_NO, [] { multiset_t skiplist; diff --git a/tests/concurrent/sl_remove_competetive.cpp b/tests/concurrent/sl_remove_competetive.cpp index 4a21276d0..614d00d46 100644 --- a/tests/concurrent/sl_remove_competetive.cpp +++ b/tests/concurrent/sl_remove_competetive.cpp @@ -12,6 +12,7 @@ constexpr size_t no_insert_for_one_delete = 2; // Calls of remove method are interleaved with insert calls. int main() { + init_log(); memory_check(THREADS_NO, [] { map_t skiplist; diff --git a/tests/concurrent/sl_remove_disjoint.cpp b/tests/concurrent/sl_remove_disjoint.cpp index 6830f69ba..031d5106e 100644 --- a/tests/concurrent/sl_remove_disjoint.cpp +++ b/tests/concurrent/sl_remove_disjoint.cpp @@ -10,6 +10,7 @@ constexpr size_t no_insert_for_one_delete = 1; // Calls of remove method are interleaved with insert calls. int main() { + init_log(); memory_check(THREADS_NO, [] { map_t skiplist; diff --git a/tests/concurrent/sl_remove_joint.cpp b/tests/concurrent/sl_remove_joint.cpp index 76ba53ae4..b32bae485 100644 --- a/tests/concurrent/sl_remove_joint.cpp +++ b/tests/concurrent/sl_remove_joint.cpp @@ -12,6 +12,7 @@ constexpr size_t no_insert_for_one_delete = 2; // Calls of remove method are interleaved with insert calls. int main() { + init_log(); memory_check(THREADS_NO, [] { map_t skiplist; diff --git a/tests/concurrent/sl_set.cpp b/tests/concurrent/sl_set.cpp index 673c9750b..e86d0a6ae 100644 --- a/tests/concurrent/sl_set.cpp +++ b/tests/concurrent/sl_set.cpp @@ -10,6 +10,7 @@ constexpr size_t no_insert_for_one_delete = 2; // Calls of remove method are interleaved with insert calls. int main() { + init_log(); memory_check(THREADS_NO, [] { set_t skiplist; diff --git a/tests/concurrent/sl_simulation.cpp b/tests/concurrent/sl_simulation.cpp index 705ba416b..564d3d1eb 100644 --- a/tests/concurrent/sl_simulation.cpp +++ b/tests/concurrent/sl_simulation.cpp @@ -14,6 +14,7 @@ constexpr size_t no_insert_for_one_delete = 1; // no_find_per_change and no_insert_for_one_delete. int main() { + init_log(); memory_check(THREADS_NO, [] { map_t skiplist; diff --git a/tests/unit/chunked_encoder.cpp b/tests/unit/chunked_encoder.cpp index 0cbee4143..3cb9898b3 100644 --- a/tests/unit/chunked_encoder.cpp +++ b/tests/unit/chunked_encoder.cpp @@ -1,21 +1,20 @@ -#include -#include #include +#include +#include #include #include "communication/bolt/v1/transport/chunked_encoder.hpp" +#include "logging/default.hpp" +#include "logging/streams/stdout.hpp" using byte = unsigned char; -void print_hex(byte x) -{ - printf("%02X ", static_cast(x)); -} +void print_hex(byte x) { printf("%02X ", static_cast(x)); } class DummyStream { public: - void write(const byte* values, size_t n) + void write(const byte *values, size_t n) { num_calls++; data.insert(data.end(), values, values + n); @@ -28,36 +27,33 @@ public: return c; } - size_t pop_size() - { - return ((size_t)pop() << 8) | pop(); - } + size_t pop_size() { return ((size_t)pop() << 8) | pop(); } void print() { - for(size_t i = 0; i < data.size(); ++i) + for (size_t i = 0; i < data.size(); ++i) print_hex(data[i]); } std::deque data; - size_t num_calls {0}; + size_t num_calls{0}; }; using Encoder = bolt::ChunkedEncoder; -void write_ff(Encoder& encoder, size_t n) +void write_ff(Encoder &encoder, size_t n) { std::vector v; - for(size_t i = 0; i < n; ++i) + for (size_t i = 0; i < n; ++i) v.push_back('\xFF'); encoder.write(v.data(), v.size()); } -void check_ff(DummyStream& stream, size_t n) +void check_ff(DummyStream &stream, size_t n) { - for(size_t i = 0; i < n; ++i) + for (size_t i = 0; i < n; ++i) assert(stream.pop() == byte('\xFF')); (void)stream; @@ -65,6 +61,8 @@ void check_ff(DummyStream& stream, size_t n) int main(void) { + logging::init_async(); + logging::log->pipe(std::make_unique()); DummyStream stream; bolt::ChunkedEncoder encoder(stream); @@ -80,7 +78,7 @@ int main(void) write_ff(encoder, 67000); encoder.flush(); - for(int i = 0; i < 10000; ++i) + for (int i = 0; i < 10000; ++i) write_ff(encoder, 1500); encoder.flush(); @@ -100,8 +98,7 @@ int main(void) size_t k = 10000 * 1500; - while(k > 0) - { + while (k > 0) { auto size = k > encoder.chunk_size ? encoder.chunk_size : k; assert(stream.pop_size() == size); check_ff(stream, size); diff --git a/tests/unit/concurrent_map.cpp b/tests/unit/concurrent_map.cpp index b9e008332..4051e238b 100644 --- a/tests/unit/concurrent_map.cpp +++ b/tests/unit/concurrent_map.cpp @@ -1,6 +1,8 @@ #include #include "data_structures/concurrent/concurrent_map.hpp" +#include "logging/default.hpp" +#include "logging/streams/stdout.hpp" #include "utils/assert.hpp" using std::cout; @@ -8,59 +10,64 @@ using std::endl; using skiplist_t = ConcurrentMap; -void print_skiplist(const skiplist_t::Accessor &skiplist) { - cout << "---- skiplist now has: "; +void print_skiplist(const skiplist_t::Accessor &skiplist) +{ + cout << "---- skiplist now has: "; - for (auto &kv : skiplist) - cout << "(" << kv.first << ", " << kv.second << ") "; + for (auto &kv : skiplist) + cout << "(" << kv.first << ", " << kv.second << ") "; - cout << "----" << endl; + cout << "----" << endl; } -int main(void) { - skiplist_t skiplist; - auto accessor = skiplist.access(); +int main(void) +{ + logging::init_async(); + logging::log->pipe(std::make_unique()); + skiplist_t skiplist; + auto accessor = skiplist.access(); - // insert 10 - permanent_assert(accessor.insert(1, 10).second == true, "add first element"); + // insert 10 + permanent_assert(accessor.insert(1, 10).second == true, + "add first element"); - // try insert 10 again (should fail) - permanent_assert(accessor.insert(1, 10).second == false, - "add the same element, should fail"); + // try insert 10 again (should fail) + permanent_assert(accessor.insert(1, 10).second == false, + "add the same element, should fail"); - // insert 20 - permanent_assert(accessor.insert(2, 20).second == true, - "insert new unique element"); + // insert 20 + permanent_assert(accessor.insert(2, 20).second == true, + "insert new unique element"); - print_skiplist(accessor); + print_skiplist(accessor); - // value at key 3 shouldn't exist - permanent_assert((accessor.find(3) == accessor.end()) == true, - "try to find element which doesn't exist"); + // value at key 3 shouldn't exist + permanent_assert((accessor.find(3) == accessor.end()) == true, + "try to find element which doesn't exist"); - // value at key 2 should exist - permanent_assert((accessor.find(2) != accessor.end()) == true, - "find iterator"); + // value at key 2 should exist + permanent_assert((accessor.find(2) != accessor.end()) == true, + "find iterator"); - // at key 2 is 20 (true) - permanent_assert(accessor.find(2)->second == 20, "find element"); + // at key 2 is 20 (true) + permanent_assert(accessor.find(2)->second == 20, "find element"); - // removed existing (1) - permanent_assert(accessor.remove(1) == true, "try to remove element"); + // removed existing (1) + permanent_assert(accessor.remove(1) == true, "try to remove element"); - // removed non-existing (3) - permanent_assert(accessor.remove(3) == false, - "try to remove element which doesn't exist"); + // removed non-existing (3) + permanent_assert(accessor.remove(3) == false, + "try to remove element which doesn't exist"); - // insert (1, 10) - permanent_assert(accessor.insert(1, 10).second == true, - "insert unique element"); + // insert (1, 10) + permanent_assert(accessor.insert(1, 10).second == true, + "insert unique element"); - // insert (4, 40) - permanent_assert(accessor.insert(4, 40).second == true, - "insert unique element"); + // insert (4, 40) + permanent_assert(accessor.insert(4, 40).second == true, + "insert unique element"); - print_skiplist(accessor); + print_skiplist(accessor); - return 0; + return 0; } diff --git a/tests/unit/concurrent_set.cpp b/tests/unit/concurrent_set.cpp index cd0601b21..684a85cd4 100644 --- a/tests/unit/concurrent_set.cpp +++ b/tests/unit/concurrent_set.cpp @@ -1,6 +1,8 @@ #include #include "data_structures/concurrent/concurrent_set.hpp" +#include "logging/default.hpp" +#include "logging/streams/stdout.hpp" #include "utils/assert.hpp" using std::cout; @@ -18,6 +20,8 @@ void print_skiplist(const ConcurrentSet::Accessor &skiplist) int main(void) { + logging::init_async(); + logging::log->pipe(std::make_unique()); ConcurrentSet set; auto accessor = set.access(); From 530be96b3686b7f86efee0f48394fbee1e37f67a Mon Sep 17 00:00:00 2001 From: Kruno Tomola Fabro Date: Mon, 15 Aug 2016 13:21:38 +0100 Subject: [PATCH 2/4] .FIX T68 .FIX T67 .FIX T65 Modifed astar main for benchmark. Experimented with map of best visited and confirmed: -it is faster by 25% -observed that it founds best resoult -loses some non best resoults Added convinent method at() to get property from RecordAccessor. Method requires value_ref() method on property object. --- include/storage/model/properties/bool.hpp | 13 ++- include/storage/model/properties/double.hpp | 3 +- include/storage/model/properties/float.hpp | 9 +- include/storage/model/properties/int32.hpp | 10 +- include/storage/model/properties/int64.hpp | 3 +- include/storage/model/properties/string.hpp | 22 ++-- include/storage/record_accessor.hpp | 6 + poc/astar.cpp | 115 ++++++++++++++++---- src/storage/model/properties/bool.cpp | 26 ++--- 9 files changed, 144 insertions(+), 63 deletions(-) diff --git a/include/storage/model/properties/bool.hpp b/include/storage/model/properties/bool.hpp index c551ee3e6..8bffb2e86 100644 --- a/include/storage/model/properties/bool.hpp +++ b/include/storage/model/properties/bool.hpp @@ -8,20 +8,21 @@ public: static constexpr Flags type = Flags::Bool; Bool(bool value); - Bool(const Bool& other) = default; + Bool(const Bool &other) = default; bool value() const; + bool const &value_ref() const; + explicit operator bool() const; - bool operator==(const Property& other) const override; + bool operator==(const Property &other) const override; - bool operator==(const Bool& other) const; + bool operator==(const Bool &other) const; bool operator==(bool v) const; - std::ostream& print(std::ostream& stream) const override; + std::ostream &print(std::ostream &stream) const override; - friend std::ostream& operator<<(std::ostream& stream, const Bool& prop); + friend std::ostream &operator<<(std::ostream &stream, const Bool &prop); }; - diff --git a/include/storage/model/properties/double.hpp b/include/storage/model/properties/double.hpp index 6c4e8e9e7..a6c50bb47 100644 --- a/include/storage/model/properties/double.hpp +++ b/include/storage/model/properties/double.hpp @@ -8,6 +8,7 @@ struct Double : public Floating Double(double value) : Floating(Flags::Double), value(value) {} + double const &value_ref() const { return value; } + double value; }; - diff --git a/include/storage/model/properties/float.hpp b/include/storage/model/properties/float.hpp index 25d812c42..2961593cb 100644 --- a/include/storage/model/properties/float.hpp +++ b/include/storage/model/properties/float.hpp @@ -1,7 +1,7 @@ #pragma once -#include "storage/model/properties/floating.hpp" #include "storage/model/properties/double.hpp" +#include "storage/model/properties/floating.hpp" class Float : public Floating { @@ -10,10 +10,9 @@ public: Float(float value) : Floating(Flags::Float), value(value) {} - operator Double() const - { - return Double(value); - } + operator Double() const { return Double(value); } + + float const &value_ref() const { return value; } float value; }; diff --git a/include/storage/model/properties/int32.hpp b/include/storage/model/properties/int32.hpp index 644bd8c98..1312ea5f6 100644 --- a/include/storage/model/properties/int32.hpp +++ b/include/storage/model/properties/int32.hpp @@ -1,7 +1,7 @@ #pragma once -#include "storage/model/properties/integral.hpp" #include "storage/model/properties/int64.hpp" +#include "storage/model/properties/integral.hpp" class Int32 : public Integral { @@ -10,11 +10,9 @@ public: Int32(int32_t value) : Integral(Flags::Int32), value(value) {} - operator Int64() const - { - return Int64(value); - } + operator Int64() const { return Int64(value); } + + int32_t const &value_ref() const { return value; } int32_t value; }; - diff --git a/include/storage/model/properties/int64.hpp b/include/storage/model/properties/int64.hpp index 333a0f8aa..ea6be55cc 100644 --- a/include/storage/model/properties/int64.hpp +++ b/include/storage/model/properties/int64.hpp @@ -9,6 +9,7 @@ public: Int64(int64_t value) : Integral(Flags::Int64), value(value) {} + int64_t const &value_ref() const { return value; } + int64_t value; }; - diff --git a/include/storage/model/properties/string.hpp b/include/storage/model/properties/string.hpp index 6230c9d98..e23460101 100644 --- a/include/storage/model/properties/string.hpp +++ b/include/storage/model/properties/string.hpp @@ -7,23 +7,25 @@ class String : public Property public: static constexpr Flags type = Flags::String; - String(const String&) = default; - String(String&&) = default; + String(const String &) = default; + String(String &&) = default; - String(const std::string& value); - String(std::string&& value); + String(const std::string &value); + String(std::string &&value); - operator const std::string&() const; + operator const std::string &() const; - bool operator==(const Property& other) const override; + bool operator==(const Property &other) const override; - bool operator==(const String& other) const; + bool operator==(const String &other) const; - bool operator==(const std::string& other) const; + bool operator==(const std::string &other) const; - friend std::ostream& operator<<(std::ostream& stream, const String& prop); + friend std::ostream &operator<<(std::ostream &stream, const String &prop); - std::ostream& print(std::ostream& stream) const override; + std::ostream &print(std::ostream &stream) const override; + + std::string const &value_ref() const { return value; } std::string value; }; diff --git a/include/storage/record_accessor.hpp b/include/storage/record_accessor.hpp index e07f6da6f..cb7350c1a 100644 --- a/include/storage/record_accessor.hpp +++ b/include/storage/record_accessor.hpp @@ -75,6 +75,12 @@ public: Properties &properties() const { return record->data.props; } + template + auto at(const std::string &key) const + { + return properties().at(key).template as().value_ref(); + } + explicit operator bool() const { return record != nullptr; } T const *operator->() const { return record; } diff --git a/poc/astar.cpp b/poc/astar.cpp index f22918129..7e9be0e03 100644 --- a/poc/astar.cpp +++ b/poc/astar.cpp @@ -17,7 +17,7 @@ using namespace std; typedef Vertex::Accessor VertexAccessor; void load_graph_dummy(Db &db); -void load_csv(Db &db, char *file_path, char *edge_file_path); +int load_csv(Db &db, char *file_path, char *edge_file_path); class Node { @@ -32,6 +32,25 @@ public: : cost(cost), vacc(vacc), parent(parent), depth(parent->depth + 1) { } + + double sum_vertex_score() + { + auto now = this; + double sum = 0; + do { + sum += now->vacc.at("score"); + now = now->parent; + } while (now != nullptr); + return sum; + } +}; + +class Score +{ +public: + Score() : value(std::numeric_limits::max()) {} + Score(double v) : value(v) {} + double value; }; // class Iterator : public Crtp @@ -74,9 +93,12 @@ public: // Node *head; // }; -void found_result(Node *bef) +void found_result(Node *res) { - std::cout << "{score: " << bef->cost << endl; + double sum = res->sum_vertex_score(); + + std::cout << "{score: " << sum << endl; + auto bef = res; while (bef != nullptr) { std::cout << " " << *(bef->vacc.operator->()) << endl; bef = bef->parent; @@ -86,7 +108,7 @@ void found_result(Node *bef) double calc_heuristic_cost_dummy(Edge::Accessor &edge, Vertex::Accessor &vertex) { assert(!vertex.empty()); - return 1 - vertex->data.props.at("score").as().value; + return 1 - vertex.at("score"); } typedef bool (*EdgeFilter)(DbAccessor &t, Edge::Accessor &, Node *before); @@ -144,14 +166,16 @@ bool vertex_filter_contained(DbAccessor &t, Vertex::Accessor &v, Node *before) // Vertex filter ima max_depth funkcija te edge filter ima max_depth funkcija. // Jedan za svaku dubinu. // Filtri vracaju true ako element zadovoljava uvjete. -void a_star(Db &db, int64_t sys_id_start, uint max_depth, EdgeFilter e_filter[], +auto a_star(Db &db, int64_t sys_id_start, uint max_depth, EdgeFilter e_filter[], VertexFilter v_filter[], double (*calc_heuristic_cost)(Edge::Accessor &edge, Vertex::Accessor &vertex), int limit) { DbAccessor t(db); + auto best_found = new std::map[max_depth]; + std::vector best; auto cmp = [](Node *left, Node *right) { return left->cost > right->cost; }; std::priority_queue, decltype(cmp)> queue(cmp); @@ -169,14 +193,22 @@ void a_star(Db &db, int64_t sys_id_start, uint max_depth, EdgeFilter e_filter[], // } if (max_depth <= now->depth) { - found_result(now); + best.push_back(now); count++; if (count >= limit) { - return; + return best; } continue; } + // { // FOUND FILTER + // Score &bef = best_found[now->depth][now->vacc.id()]; + // if (bef.value <= now->cost) { + // continue; + // } + // bef.value = now->cost; + // } + iter::for_all(now->vacc.out(), [&](auto edge) { if (e_filter[now->depth](t, edge, now)) { VertexAccessor va = edge.to(); @@ -188,10 +220,11 @@ void a_star(Db &db, int64_t sys_id_start, uint max_depth, EdgeFilter e_filter[], } }); } while (!queue.empty()); - std::cout << "Found: " << count << " resoults\n"; + // std::cout << "Found: " << count << " resoults\n"; // TODO: GUBI SE MEMORIJA JER SE NODOVI NEBRISU t.commit(); + return best; } // class Data @@ -207,23 +240,66 @@ void a_star(Db &db, int64_t sys_id_start, uint max_depth, EdgeFilter e_filter[], // const int &get_key() { return key; } // }; -int main() +int main(int argc, char **argv) { + if (argc < 3) { + std::cout << "Not enough input values\n"; + return 0; + } else if (argc > 4) { + std::cout << "To much input values\n"; + return 0; + } + Db db; - load_csv(db, "neo4j_nodes_export_2000.csv", "neo4j_edges_export_2000.csv"); - // - // load_graph_dummy(db); - // + auto vertex_no = load_csv(db, argv[argc - 2], argv[argc - 1]); + EdgeFilter e_filters[] = {&edge_filter_dummy, &edge_filter_dummy, &edge_filter_dummy, &edge_filter_dummy}; VertexFilter f_filters[] = { &vertex_filter_contained, &vertex_filter_contained, &vertex_filter_contained, &vertex_filter_contained}; - auto begin = clock(); - a_star(db, 0, 3, e_filters, f_filters, &calc_heuristic_cost_dummy, 10); - clock_t end = clock(); - double elapsed_ms = (double(end - begin) / CLOCKS_PER_SEC) * 1000; - std::cout << "A-star: " << elapsed_ms << " [ms]\n"; + + // CONF + std::srand(time(0)); + auto best_n = 10; + auto bench_n = 1000; + auto best_print_n = 10; + bool pick_best_found = argc > 3 ? true : false; + + double sum = 0; + std::vector best; + for (int i = 0; i < bench_n; i++) { + auto start_vertex_index = std::rand() % vertex_no; + + auto begin = clock(); + auto found = a_star(db, start_vertex_index, 3, e_filters, f_filters, + &calc_heuristic_cost_dummy, best_n); + clock_t end = clock(); + + double elapsed_ms = (double(end - begin) / CLOCKS_PER_SEC) * 1000; + sum += elapsed_ms; + + if ((best.size() < best_print_n && found.size() > best.size()) || + (pick_best_found && found.size() > 0 && + found.front()->sum_vertex_score() > + best.front()->sum_vertex_score())) { + best = found; + } + + // Just to be safe + if (i + 1 == bench_n && best.size() == 0) { + bench_n++; + } + } + + std::cout << "\nSearch for best " << best_n + << " results has runing time of:\n avg: " << sum / bench_n + << " [ms]\n"; + std::cout << "\nExample of best result:\n"; + for (int i = 0; i < best_print_n && best.size() > 0; i++) { + found_result(best.front()); + best.erase(best.begin()); + } // RhHashMultiMap benchmark // const int n_pow2 = 20; @@ -262,7 +338,7 @@ vector split(const string &s, char delim) return elems; } -void load_csv(Db &db, char *file_path, char *edge_file_path) +int load_csv(Db &db, char *file_path, char *edge_file_path) { std::fstream file(file_path); std::fstream e_file(edge_file_path); @@ -341,6 +417,7 @@ void load_csv(Db &db, char *file_path, char *edge_file_path) << endl; t.commit(); + return v_count; } void load_graph_dummy(Db &db) diff --git a/src/storage/model/properties/bool.cpp b/src/storage/model/properties/bool.cpp index 83c8cf152..70bbc74cb 100644 --- a/src/storage/model/properties/bool.cpp +++ b/src/storage/model/properties/bool.cpp @@ -1,5 +1,8 @@ #include "storage/model/properties/bool.hpp" +const bool TRUE = true; +const bool FALSE = false; + Bool::Bool(bool value) : Property(value ? Flags::True : Flags::False) {} bool Bool::value() const @@ -15,32 +18,25 @@ bool Bool::value() const return (underlying_cast(flags) - underlying_cast(Flags::True)) == 0; } -Bool::operator bool() const -{ - return value(); -} +bool const &Bool::value_ref() const { return value() ? TRUE : FALSE; } -bool Bool::operator==(const Property& other) const +Bool::operator bool() const { return value(); } + +bool Bool::operator==(const Property &other) const { return other.is() && operator==(other.as()); } -bool Bool::operator==(const Bool& other) const -{ - return other.flags == flags; -} +bool Bool::operator==(const Bool &other) const { return other.flags == flags; } -bool Bool::operator==(bool v) const -{ - return value() == v; -} +bool Bool::operator==(bool v) const { return value() == v; } -std::ostream& Bool::print(std::ostream& stream) const +std::ostream &Bool::print(std::ostream &stream) const { return operator<<(stream, *this); } -std::ostream& operator<<(std::ostream& stream, const Bool& prop) +std::ostream &operator<<(std::ostream &stream, const Bool &prop) { return stream << prop.value(); } From 5a42e15c4a5aed7218c8abba710739971ca602c5 Mon Sep 17 00:00:00 2001 From: Kruno Tomola Fabro Date: Thu, 18 Aug 2016 15:34:36 +0100 Subject: [PATCH 3/4] Alpha version of label indexes. Squashed messages from 9 commits: 9. Properties now uses PropertyFamily and contained classes. Fetching,seting,clearing properties can be done with PropertyFamilyKey or PropertyTypeKey. Hierarchy of newly added clases is: Vertices -n-> PropertyFamily {name: String} <-1-n-> PropertyType {type: Property::Flags} Edges -n-> PropertyFamily {name: String} <-1-n-> PropertyType {type: Property::Flags} PropertyFamilyKey -> PropertyType PropertyTypeKey -> PropertyType PropertyType t0,t1; let t0!=t1 be true let t0.family==t1.family be true then next is true PropertyTypeKey{&t0}!=PropertyTypeKey{&t1} PropertyFamilyKey{&t0}==PropertyFamilyKey{&t1} PropertyFamilyKey{&t0}==PropertyTypeKey{&t1} PropertyTypeKey{&t0}==PropertyFamilyKey{&t1} 8. Intermedate commit. Noticed that integration queries throw SEGFAULT. 7. Defined interface for indexes. Fixed three memory leaks. Fixed integration_queries test which now passes. 6. Commit which return Xorshift128plus to valid shape. 5. Tmp commit. 4. Label Index is compiling. 3. tmp 2. Vertex::Accessor now updates Label index. 1. Applied changes for code review. --- CMakeLists.txt | 6 + .../data_structures/bitset/dynamic_bitset.hpp | 58 ++-- .../concurrent/concurrent_list.hpp | 284 ++++++++++++++++++ .../concurrent/concurrent_set.hpp | 2 +- .../data_structures/concurrent/skiplist.hpp | 13 +- include/database/db_accessor.hpp | 35 ++- include/database/db_transaction.hpp | 10 +- include/query_engine/hardcode/queries.hpp | 62 +++- include/query_engine/util.hpp | 1 - include/storage/edges.hpp | 12 + .../impl/nonunique_unordered_index.hpp | 38 +++ include/storage/indexes/index.hpp | 88 +++--- include/storage/indexes/index_base.hpp | 60 ++++ include/storage/indexes/index_record.hpp | 55 ++-- .../indexes/index_record_collection.hpp | 76 ++--- include/storage/indexes/sort_order.hpp | 4 +- include/storage/label/label.hpp | 32 +- include/storage/label/label_collection.hpp | 14 +- include/storage/model/properties/all.hpp | 1 - include/storage/model/properties/flags.hpp | 45 +++ include/storage/model/properties/handler.hpp | 34 +-- .../storage/model/properties/properties.hpp | 33 +- include/storage/model/properties/property.hpp | 65 +--- .../model/properties/property_family.hpp | 181 +++++++++++ .../properties/traversers/consolewriter.hpp | 8 +- .../properties/traversers/jsonwriter.hpp | 6 +- include/storage/record_accessor.hpp | 38 ++- include/storage/vertex_accessor.hpp | 12 +- include/storage/vertices.hpp | 23 +- include/utils/border.hpp | 51 ++++ include/utils/iterator/accessor.hpp | 19 +- include/utils/iterator/for_all.hpp | 10 + include/utils/iterator/func_iterator.hpp | 32 ++ include/utils/iterator/iter.hpp | 48 --- include/utils/iterator/iterator.hpp | 6 +- include/utils/iterator/iterator_accessor.hpp | 59 ++++ include/utils/iterator/iterator_base.hpp | 12 + include/utils/iterator/map.hpp | 29 +- include/utils/iterator/range_iterator.hpp | 74 +++++ include/utils/iterator/wrap.hpp | 60 ---- include/utils/option.hpp | 22 +- include/utils/void.hpp | 10 + poc/astar.cpp | 133 +++++--- src/data_structures/map/rh_common.hpp | 52 +++- src/database/db.cpp | 1 + src/database/db_accessor.cpp | 66 ++-- src/database/db_transaction.cpp | 10 +- src/query_engine/util.cpp | 2 +- src/storage/edges.cpp | 24 ++ .../impl/nonunique_unordered_index.cpp | 61 ++++ src/storage/label/label.cpp | 23 +- src/storage/label/label_collection.cpp | 22 +- src/storage/model/properties/properties.cpp | 62 ++-- .../model/properties/property_family.cpp | 39 +++ src/storage/record_accessor.cpp | 8 + src/storage/vertex_accessor.cpp | 16 +- src/storage/vertices.cpp | 23 +- tests/integration/queries.cpp | 16 +- tests/manual/queries.cpp | 4 + tests/unit/concurrent_list.cpp | 75 +++++ 60 files changed, 1791 insertions(+), 574 deletions(-) create mode 100644 include/data_structures/concurrent/concurrent_list.hpp create mode 100644 include/storage/indexes/impl/nonunique_unordered_index.hpp create mode 100644 include/storage/indexes/index_base.hpp create mode 100644 include/storage/model/properties/flags.hpp create mode 100644 include/storage/model/properties/property_family.hpp create mode 100644 include/utils/border.hpp create mode 100644 include/utils/iterator/func_iterator.hpp delete mode 100644 include/utils/iterator/iter.hpp create mode 100644 include/utils/iterator/iterator_accessor.hpp create mode 100644 include/utils/iterator/iterator_base.hpp create mode 100644 include/utils/iterator/range_iterator.hpp delete mode 100644 include/utils/iterator/wrap.hpp create mode 100644 include/utils/void.hpp create mode 100644 src/storage/indexes/impl/nonunique_unordered_index.cpp create mode 100644 src/storage/model/properties/property_family.cpp create mode 100644 src/storage/record_accessor.cpp create mode 100644 tests/unit/concurrent_list.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 9efec42c3..a4cbc58b6 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -213,10 +213,12 @@ FILE(COPY ${include_dir}/storage/model/properties/string.hpp DESTINATION ${build FILE(COPY ${include_dir}/storage/model/properties/floating.hpp DESTINATION ${build_include_dir}/storage/model/properties) FILE(COPY ${include_dir}/storage/model/properties/number.hpp DESTINATION ${build_include_dir}/storage/model/properties) FILE(COPY ${include_dir}/storage/model/properties/integral.hpp DESTINATION ${build_include_dir}/storage/model/properties) +FILE(COPY ${include_dir}/storage/model/properties/property_family.hpp DESTINATION ${build_include_dir}/storage/model/properties) FILE(COPY ${include_dir}/storage/model/properties/utils/math_operations.hpp DESTINATION ${build_include_dir}/storage/model/properties/utils) FILE(COPY ${include_dir}/storage/model/properties/utils/unary_negation.hpp DESTINATION ${build_include_dir}/storage/model/properties/utils) FILE(COPY ${include_dir}/storage/model/properties/utils/modulo.hpp DESTINATION ${build_include_dir}/storage/model/properties/utils) + FILE(COPY ${include_dir}/storage/model/edge_model.hpp DESTINATION ${build_include_dir}/storage/model) FILE(COPY ${include_dir}/storage/model/property_model.hpp DESTINATION ${build_include_dir}/storage/model) FILE(COPY ${include_dir}/storage/model/vertex_model.hpp DESTINATION ${build_include_dir}/storage/model) @@ -401,6 +403,8 @@ set(memgraph_src_files ${src_dir}/storage/model/properties/bool.cpp ${src_dir}/storage/model/properties/string.cpp ${src_dir}/storage/model/properties/properties.cpp + ${src_dir}/storage/model/properties/property_family.cpp + ${src_dir}/storage/indexes/impl/nonunique_unordered_index.cpp ${src_dir}/storage/locking/record_lock.cpp ${src_dir}/storage/vertex_accessor.cpp ${src_dir}/transactions/transaction.cpp @@ -416,6 +420,8 @@ set(memgraph_src_files ${src_dir}/database/db_accessor.cpp ${src_dir}/database/db_transaction.cpp ${src_dir}/storage/edge_accessor.cpp + ${src_dir}/storage/record_accessor.cpp + ) # STATIC library used by memgraph executables diff --git a/include/data_structures/bitset/dynamic_bitset.hpp b/include/data_structures/bitset/dynamic_bitset.hpp index c228e9571..43024fade 100644 --- a/include/data_structures/bitset/dynamic_bitset.hpp +++ b/include/data_structures/bitset/dynamic_bitset.hpp @@ -1,7 +1,7 @@ #pragma once -#include #include +#include #include "threading/sync/lockable.hpp" #include "threading/sync/spinlock.hpp" @@ -13,8 +13,8 @@ class DynamicBitset : Lockable { Block() = default; - Block(Block&) = delete; - Block(Block&&) = delete; + Block(Block &) = delete; + Block(Block &&) = delete; static constexpr size_t size = sizeof(block_t) * 8; @@ -41,7 +41,7 @@ class DynamicBitset : Lockable block.fetch_and(~(bitmask(n) << k), order); } - std::atomic block {0}; + std::atomic block{0}; }; struct Chunk @@ -49,16 +49,13 @@ class DynamicBitset : Lockable Chunk() : next(nullptr) { static_assert(chunk_size % sizeof(block_t) == 0, - "chunk size not divisible by block size"); + "chunk size not divisible by block size"); } - Chunk(Chunk&) = delete; - Chunk(Chunk&&) = delete; + Chunk(Chunk &) = delete; + Chunk(Chunk &&) = delete; - ~Chunk() - { - delete next; - } + ~Chunk() { delete next; } static constexpr size_t size = chunk_size * Block::size; static constexpr size_t n_blocks = chunk_size / sizeof(block_t); @@ -79,54 +76,62 @@ class DynamicBitset : Lockable } Block blocks[n_blocks]; - std::atomic next; + std::atomic next; }; public: DynamicBitset() : head(new Chunk()) {} - DynamicBitset(DynamicBitset&) = delete; - DynamicBitset(DynamicBitset&&) = delete; + DynamicBitset(DynamicBitset &) = delete; + DynamicBitset(DynamicBitset &&) = delete; + + ~DynamicBitset() + { + auto now = head.load(); + while (now != nullptr) { + auto next = now->next.load(); + delete now; + now = next; + } + } block_t at(size_t k, size_t n) { - auto& chunk = find_chunk(k); + auto &chunk = find_chunk(k); return chunk.at(k, n, std::memory_order_seq_cst); } bool at(size_t k) { - auto& chunk = find_chunk(k); + auto &chunk = find_chunk(k); return chunk.at(k, 1, std::memory_order_seq_cst); } void set(size_t k, size_t n = 1) { - auto& chunk = find_chunk(k); + auto &chunk = find_chunk(k); return chunk.set(k, n, std::memory_order_seq_cst); } void clear(size_t k, size_t n = 1) { - auto& chunk = find_chunk(k); + auto &chunk = find_chunk(k); return chunk.clear(k, n, std::memory_order_seq_cst); } private: - Chunk& find_chunk(size_t& k) + Chunk &find_chunk(size_t &k) { - Chunk* chunk = head.load(), *next = nullptr; + Chunk *chunk = head.load(), *next = nullptr; // while i'm not in the right chunk // (my index is bigger than the size of this chunk) - while(k >= Chunk::size) - { + while (k >= Chunk::size) { next = chunk->next.load(); // if a next chunk exists, switch to it and decrement my // pointer by the size of the current chunk - if(next != nullptr) - { + if (next != nullptr) { chunk = next; k -= Chunk::size; continue; @@ -139,8 +144,7 @@ private: // double-check locking. if the chunk exists now, some other thread // has just created it, continue searching for my chunk - if(chunk->next.load() != nullptr) - continue; + if (chunk->next.load() != nullptr) continue; chunk->next.store(new Chunk()); } @@ -149,5 +153,5 @@ private: return *chunk; } - std::atomic head; + std::atomic head; }; diff --git a/include/data_structures/concurrent/concurrent_list.hpp b/include/data_structures/concurrent/concurrent_list.hpp new file mode 100644 index 000000000..0e364a325 --- /dev/null +++ b/include/data_structures/concurrent/concurrent_list.hpp @@ -0,0 +1,284 @@ +#pragma once + +#include +#include +#include "utils/crtp.hpp" + +template +class List +{ + + template + static V load(std::atomic &atomic) + { + return atomic.load(std::memory_order_acquire); + } + + template + static void store(std::atomic &atomic, V desired) + { // Maybe could be relaxed + atomic.store(desired, std::memory_order_release); + } + + template + static bool cas(std::atomic &atomic, V expected, V desired) + { // Could be relaxed must be atleast Release. + return atomic.compare_exchange_strong(expected, desired, + std::memory_order_seq_cst); + } + + template + static V *swap(std::atomic &atomic, V *desired) + { // Could be relaxed + return atomic.exchange(desired, std::memory_order_seq_cst); + } + +private: + class Node + { + public: + Node(const T &data) : data(data) {} + Node(T &&data) : data(std::move(data)) {} + + T data; + std::atomic next{nullptr}; + std::atomic next_rem{nullptr}; + std::atomic removed{false}; + }; + + template + class IteratorBase : public Crtp + { + friend class List; + + protected: + IteratorBase() : list(nullptr), curr(nullptr) {} + + IteratorBase(List *list) : list(list) + { + assert(list != nullptr); + list->count++; + reset(); + } + + public: + IteratorBase(const IteratorBase &) = delete; + + IteratorBase(IteratorBase &&other) + : list(other.list), curr(other.curr), prev(other.prev) + { + other.list = nullptr; + other.curr = nullptr; + } + + ~IteratorBase() + { + if (list == nullptr) { + return; + } + + auto head_rem = load(list->removed); + // Fetch could be relaxed + // There exist possibility that no one will delete garbage at this + // time. + if (list->count.fetch_sub(1) == 1 && head_rem != nullptr && + cas( + list->removed, head_rem, + nullptr)) { // I am the last one and there is garbage to be + // removed. + auto now = head_rem; + do { + auto next = load(now->next_rem); + delete now; + now = next; + } while (now != nullptr); + } + } + + T &operator*() const + { + assert(valid()); + return curr->data; + } + T *operator->() const + { + assert(valid()); + return &(curr->data); + } + + bool valid() const { return curr != nullptr; } + + // Iterating is wait free. + It &operator++() + { + assert(valid()); + do { + prev = curr; + curr = load(curr->next); + } while (valid() && is_removed()); + return this->derived(); + } + It &operator++(int) { return operator++(); } + + bool is_removed() + { + assert(valid()); + return load(curr->removed); + } + + // Returns IteratorBase to begining + void reset() + { + prev = nullptr; + curr = load(list->head); + while (valid() && is_removed()) { + operator++(); + } + } + + // Adds to the begining of list + // It is lock free but it isn't wait free. + void push(T &&data) + { + auto node = new Node(data); + Node *next = nullptr; + do { + next = load(list->head); + store(node->next, next); + } while (!cas(list->head, next, node)); + } + + // True only if this call removed the element. Only reason for fail is + // if + // the element is already removed. + // Remove has deadlock if another thread dies between marking node for + // removal + // and the disconnection. + // This can be improved with combinig the removed flag with prev.next or + // curr.next + bool remove() + { + assert(valid()); + if (cas(curr->removed, false, true)) { + if (!disconnect()) { + find_and_disconnect(); + } + store(curr->next_rem, swap(list->removed, curr)); + return true; + } + return false; + } + + friend bool operator==(const It &a, const It &b) + { + return a.curr == b.curr; + } + + friend bool operator!=(const It &a, const It &b) { return !(a == b); } + + private: + void find_and_disconnect() + { + auto it = It(list); + auto next = load(curr->next); + while (it.valid()) { + if (it.curr == curr) { + if (it.disconnect()) { + return; + } + it.reset(); + } else if (it.curr == next) { // Comparison with next is + // optimization for early return. + return; + } else { + it++; + } + } + } + + bool disconnect() + { + auto next = load(curr->next); + if (prev != nullptr) { + store(prev->next, next); + if (load(prev->removed)) { + return false; + } + } else if (!cas(list->head, curr, next)) { + return false; + } + return true; + } + + List *list; + Node *prev{nullptr}; + Node *curr; + }; + +public: + class ConstIterator : public IteratorBase + { + friend class List; + + public: + using IteratorBase::IteratorBase; + + const T &operator*() const + { + return IteratorBase::operator*(); + } + + const T *operator->() const + { + return IteratorBase::operator->(); + } + + operator const T &() const + { + return IteratorBase::operator T &(); + } + }; + + class Iterator : public IteratorBase + { + friend class List; + + public: + using IteratorBase::IteratorBase; + }; + +public: + List() = default; + + List(List &) = delete; + List(List &&) = delete; + + ~List() + { + auto now = head.load(); + while (now != nullptr) { + auto next = now->next.load(); + delete now; + now = next; + } + } + + void operator=(List &) = delete; + + Iterator begin() { return Iterator(this); } + + // ConstIterator begin() { return ConstIterator(this); } + + ConstIterator cbegin() { return ConstIterator(this); } + + Iterator end() { return Iterator(); } + + // ConstIterator end() { return ConstIterator(); } + + ConstIterator cend() { return ConstIterator(); } + +private: + std::atomic count{0}; + std::atomic head{nullptr}; + std::atomic removed{nullptr}; +}; diff --git a/include/data_structures/concurrent/concurrent_set.hpp b/include/data_structures/concurrent/concurrent_set.hpp index c550c081b..036463ad2 100644 --- a/include/data_structures/concurrent/concurrent_set.hpp +++ b/include/data_structures/concurrent/concurrent_set.hpp @@ -30,7 +30,7 @@ public: std::pair insert(T &&item) { - return accessor.insert(std::forward(item)); + return accessor.insert(std::move(item)); } list_it_con find(const T &item) const { return accessor.find(item); } diff --git a/include/data_structures/concurrent/skiplist.hpp b/include/data_structures/concurrent/skiplist.hpp index a0367a57c..2722cc343 100644 --- a/include/data_structures/concurrent/skiplist.hpp +++ b/include/data_structures/concurrent/skiplist.hpp @@ -157,7 +157,7 @@ public: // we have raw memory and we need to construct an object // of type Node on it - return new (node) Node(std::forward(item), height); + return new (node) Node(std::move(item), height); } static void destroy(Node *node) @@ -182,7 +182,7 @@ public: Node(T &&data, uint8_t height) : Node(height) { - this->data.set(std::forward(data)); + this->data.set(std::move(data)); } ~Node() @@ -522,7 +522,7 @@ public: std::pair insert(T &&item) { - return skiplist->insert(std::forward(item), preds, succs); + return skiplist->insert(std::move(item), preds, succs); } Iterator insert_non_unique(const T &item) @@ -683,7 +683,7 @@ private: static bool lock_nodes(uint8_t height, guard_t guards[], Node *preds[], Node *succs[]) { - Node *prepred, *pred, *succ = nullptr; + Node *prepred = nullptr, *pred = nullptr, *succ = nullptr; bool valid = true; for (int level = 0; valid && level < height; ++level) { @@ -790,8 +790,7 @@ private: // has the locks if (!lock_nodes(height, guards, preds, succs)) continue; - return {insert_here(std::forward(data), preds, succs, height, - guards), + return {insert_here(std::move(data), preds, succs, height, guards), true}; } } @@ -801,7 +800,7 @@ private: guard_t guards[]) { // you have the locks, create a new node - auto new_node = Node::create(std::forward(data), height); + auto new_node = Node::create(std::move(data), height); // link the predecessors and successors, e.g. // diff --git a/include/database/db_accessor.hpp b/include/database/db_accessor.hpp index e4cbd86a6..be19ba520 100644 --- a/include/database/db_accessor.hpp +++ b/include/database/db_accessor.hpp @@ -1,15 +1,20 @@ #pragma once -#include "database/db.hpp" -#include "database/db_accessor.hpp" -#include "storage/record_accessor.hpp" -#include "storage/vertex.hpp" +#include "database/db_transaction.hpp" +// #include "storage/record_accessor.hpp" +// #include "storage/vertex.hpp" #include "storage/vertex_accessor.hpp" -#include "storage/vertices.hpp" -#include "transactions/transaction.hpp" -#include "utils/iterator/iterator.hpp" +// #include "storage/vertices.hpp" +// #include "transactions/transaction.hpp" +// #include "utils/iterator/iterator.hpp" +#include "utils/border.hpp" #include "utils/option.hpp" +namespace tx +{ +class Transaction; +} + /* * DbAccessor * -Guarantees that access to Vertex and Edge is possible only through @@ -65,21 +70,31 @@ public: bool label_contains(const std::string &name); - VertexIndexRecordCollection &label_find_index(const Label &label); - //********************TYPE METHODS const EdgeType &type_find_or_create(const std::string &name); bool type_contains(const std::string &name); + //********************PROPERTY METHODS + // Vertices::prop_familys_t::Accessor vertex_property_family_access(); + // + // auto edge_property_family_access(); + + PropertyFamily &vertex_property_family_get(const std::string &name); + + PropertyFamily &edge_property_family_get(const std::string &name); + //********************TRANSACTION METHODS void commit(); void abort(); private: - DbTransaction db; + template + friend class NonUniqueUnorderedIndex; + + DbTransaction db_transaction; }; //**********************CONVENIENT FUNCTIONS diff --git a/include/database/db_transaction.hpp b/include/database/db_transaction.hpp index a7b862c82..a36dd1d18 100644 --- a/include/database/db_transaction.hpp +++ b/include/database/db_transaction.hpp @@ -1,7 +1,5 @@ #pragma once -#include "storage/indexes/index_record.hpp" -#include "storage/label/label.hpp" #include "transactions/transaction.hpp" class Db; @@ -9,6 +7,8 @@ class DbAccessor; // Inner structures local to transaction can hold ref to this structure and use // its methods. +// Also serves as a barrier for calling methods defined public but meant for +// internal use. That kind of method should request DbTransaction&. class DbTransaction { friend DbAccessor; @@ -16,8 +16,10 @@ class DbTransaction public: DbTransaction(Db &db, tx::Transaction &trans) : db(db), trans(trans) {} - void update_label_index(const Label &label, - VertexIndexRecord &&index_record); + // Global transactional algorithms,operations and general methods meant for + // internal use should be here or should be routed through this object. + // This should provide cleaner hierarchy of operations on database. + // For example cleaner. tx::Transaction &trans; diff --git a/include/query_engine/hardcode/queries.hpp b/include/query_engine/hardcode/queries.hpp index 5ef8dbfe5..f413ae9bd 100644 --- a/include/query_engine/hardcode/queries.hpp +++ b/include/query_engine/hardcode/queries.hpp @@ -5,8 +5,11 @@ #include "database/db_accessor.hpp" #include "query_engine/query_stripper.hpp" #include "query_engine/util.hpp" +#include "storage/indexes/impl/nonunique_unordered_index.cpp" #include "storage/model/properties/property.hpp" +#include "storage/model/properties/property_family.hpp" #include "utils/command_line/arguments.hpp" +#include "utils/iterator/iterator.hpp" auto load_queries(Db &db) { @@ -15,8 +18,12 @@ auto load_queries(Db &db) // CREATE (n {prop: 0}) RETURN n) auto create_node = [&db](const properties_t &args) { DbAccessor t(db); + auto prop_key = t.vertex_property_family_get("prop") + .get(args[0]->flags) + .family_key(); + auto vertex_accessor = t.vertex_insert(); - vertex_accessor.property("prop", args[0]); + vertex_accessor.set(prop_key, args[0]); t.commit(); return true; }; @@ -24,8 +31,12 @@ auto load_queries(Db &db) auto create_labeled_and_named_node = [&db](const properties_t &args) { DbAccessor t(db); + auto prop_key = t.vertex_property_family_get("name") + .get(args[0]->flags) + .family_key(); + auto vertex_accessor = t.vertex_insert(); - vertex_accessor.property("name", args[0]); + vertex_accessor.set(prop_key, args[0]); auto &label = t.label_find_or_create("LABEL"); vertex_accessor.add_label(label); cout_properties(vertex_accessor.properties()); @@ -35,11 +46,23 @@ auto load_queries(Db &db) auto create_account = [&db](const properties_t &args) { DbAccessor t(db); + auto prop_id = + t.vertex_property_family_get("id").get(args[0]->flags).family_key(); + auto prop_name = t.vertex_property_family_get("name") + .get(args[1]->flags) + .family_key(); + auto prop_country = t.vertex_property_family_get("country") + .get(args[2]->flags) + .family_key(); + auto prop_created = t.vertex_property_family_get("created_at") + .get(args[3]->flags) + .family_key(); + auto vertex_accessor = t.vertex_insert(); - vertex_accessor.property("id", args[0]); - vertex_accessor.property("name", args[1]); - vertex_accessor.property("country", args[2]); - vertex_accessor.property("created_at", args[3]); + vertex_accessor.set(prop_id, args[0]); + vertex_accessor.set(prop_name, args[1]); + vertex_accessor.set(prop_country, args[2]); + vertex_accessor.set(prop_created, args[3]); auto &label = t.label_find_or_create("ACCOUNT"); vertex_accessor.add_label(label); cout_properties(vertex_accessor.properties()); @@ -49,8 +72,7 @@ auto load_queries(Db &db) auto find_node_by_internal_id = [&db](const properties_t &args) { DbAccessor t(db); - auto id = static_cast(*args[0]); - auto maybe_va = t.vertex_find(Id(id.value)); + auto maybe_va = t.vertex_find(Id(args[0]->as().value)); if (!option_fill(maybe_va)) { cout << "vertex doesn't exist" << endl; t.commit(); @@ -117,12 +139,15 @@ auto load_queries(Db &db) auto update_node = [&db](const properties_t &args) { DbAccessor t(db); + auto prop_name = t.vertex_property_family_get("name") + .get(args[1]->flags) + .family_key(); auto maybe_v = t.vertex_find(args[0]->as().value); if (!option_fill(maybe_v)) return t.commit(), false; auto v = maybe_v.get(); - v.property("name", args[1]); + v.set(prop_name, args[1]); cout_properties(v.properties()); t.commit(); @@ -134,13 +159,19 @@ auto load_queries(Db &db) // weight: 70}]-(n2) RETURN r auto create_edge_v2 = [&db](const properties_t &args) { DbAccessor t(db); + auto prop_age = + t.edge_property_family_get("age").get(args[2]->flags).family_key(); + auto prop_weight = t.edge_property_family_get("weight") + .get(args[3]->flags) + .family_key(); + auto n1 = t.vertex_find(args[0]->as().value); if (!option_fill(n1)) return t.commit(), false; auto n2 = t.vertex_find(args[1]->as().value); if (!option_fill(n2)) return t.commit(), false; auto r = t.edge_insert(n2.get(), n1.get()); - r.property("age", args[2]); - r.property("weight", args[3]); + r.set(prop_age, args[2]); + r.set(prop_weight, args[3]); auto &IS = t.type_find_or_create("IS"); r.edge_type(IS); @@ -173,13 +204,12 @@ auto load_queries(Db &db) DbAccessor t(db); auto &label = t.label_find_or_create("LABEL"); + auto prop_key = + t.vertex_property_family_get("name").get(Type::String).family_key(); - auto &index_record_collection = t.label_find_index(label); - auto accessor = index_record_collection.access(); cout << "VERTICES" << endl; - for (auto &v : accessor) { - cout << v.record->data.props.at("name").as().value << endl; - } + iter::for_all(label.index->for_range_exact(t), + [&](auto a) { cout << a.at(prop_key) << endl; }); // TODO // db.graph.vertices.fileter("LABEL").all(t, handler); diff --git a/include/query_engine/util.hpp b/include/query_engine/util.hpp index 76bf1e5a6..40b5b5a57 100644 --- a/include/query_engine/util.hpp +++ b/include/query_engine/util.hpp @@ -40,5 +40,4 @@ std::string code_line(const std::string &format_str, const Args &... args) { return "\t" + format(format_str, args...) + "\n"; } - } diff --git a/include/storage/edges.hpp b/include/storage/edges.hpp index 660962be8..e980675b6 100644 --- a/include/storage/edges.hpp +++ b/include/storage/edges.hpp @@ -1,13 +1,17 @@ #pragma once +#include #include "data_structures/concurrent/concurrent_map.hpp" #include "mvcc/version_list.hpp" #include "storage/common.hpp" #include "storage/edge_accessor.hpp" +#include "storage/model/properties/property_family.hpp" #include "utils/option.hpp" class Edges { + using prop_familys_t = ConcurrentMap; + public: Option find(DbTransaction &t, const Id &id); @@ -15,7 +19,15 @@ public: Edge::Accessor insert(DbTransaction &t, VertexRecord *from, VertexRecord *to); + // auto property_family_access(); + + PropertyFamily &property_family_find_or_create(const std::string &name); + private: ConcurrentMap edges; + // TODO: Because familys wont be removed this could be done with more + // efficent + // data structure. + prop_familys_t prop_familys; AtomicCounter counter; }; diff --git a/include/storage/indexes/impl/nonunique_unordered_index.hpp b/include/storage/indexes/impl/nonunique_unordered_index.hpp new file mode 100644 index 000000000..01bebb1c5 --- /dev/null +++ b/include/storage/indexes/impl/nonunique_unordered_index.hpp @@ -0,0 +1,38 @@ +#pragma once + +#include "storage/indexes/index_base.hpp" +#include "storage/indexes/index_record.hpp" + +#include "data_structures/concurrent/concurrent_list.hpp" + +template +class NonUniqueUnorderedIndex : public IndexBase +{ +public: + typedef T value_type; + typedef K key_type; + + NonUniqueUnorderedIndex(); + + // Insert's value. + // nonunique => always succeds. + bool insert(IndexRecord &&value) final; + + // Returns iterator which returns valid records in range. + // ordered==None => doesn't guarantee any order of submitting records. + std::unique_ptr> + for_range(DbAccessor &t, Border from = Border(), + Border to = Border()) final; + + // Same as for_range just whit known returned iterator. + auto for_range_exact(DbAccessor &t, Border from = Border(), + Border to = Border()); + + // Removes for all transactions obsolete Records. + // Cleaner has to call this method when he decideds that it is time for + // cleaning. + void clean(DbTransaction &) final; + +private: + List> list; +}; diff --git a/include/storage/indexes/index.hpp b/include/storage/indexes/index.hpp index 4b24e65a3..87007f0a7 100644 --- a/include/storage/indexes/index.hpp +++ b/include/storage/indexes/index.hpp @@ -1,44 +1,44 @@ -#pragma once - -#include - -#include "data_structures/concurrent/concurrent_map.hpp" -#include "storage/indexes/index_record.hpp" -#include "storage/indexes/index_record_collection.hpp" -#include "storage/label/label.hpp" - -template -class Index -{ - public: - using container_t = ConcurrentMap; - - Index() : index(std::make_unique()) {} - - auto update(const Label &label, VertexIndexRecord &&index_record) - { - auto accessor = index->access(); - auto label_ref = label_ref_t(label); - - // create Index Record Collection if it doesn't exist - if (!accessor.contains(label_ref)) { - accessor.insert(label_ref, std::move(VertexIndexRecordCollection())); - } - - // add Vertex Index Record to the Record Collection - auto &record_collection = (*accessor.find(label_ref)).second; - record_collection.add(std::forward(index_record)); - } - - VertexIndexRecordCollection &find(const Label &label) - { - // TODO: accessor should be outside? - // bacause otherwise GC could delete record that has just be returned - auto label_ref = label_ref_t(label); - auto accessor = index->access(); - return (*accessor.find(label_ref)).second; - } - - private: - std::unique_ptr index; -}; +// #pragma once +// +// #include +// +// #include "data_structures/concurrent/concurrent_map.hpp" +// #include "storage/indexes/index_record.hpp" +// #include "storage/indexes/index_record_collection.hpp" +// #include "storage/label/label.hpp" +// +// template +// class Index +// { +// public: +// using container_t = ConcurrentMap; +// +// Index() : index(std::make_unique()) {} +// +// auto update(const Label &label, VertexIndexRecord &&index_record) +// { +// auto accessor = index->access(); +// auto label_ref = label_ref_t(label); +// +// // create Index Record Collection if it doesn't exist +// if (!accessor.contains(label_ref)) { +// accessor.insert(label_ref, std::move(VertexIndexRecordCollection())); +// } +// +// // add Vertex Index Record to the Record Collection +// auto &record_collection = (*accessor.find(label_ref)).second; +// record_collection.add(std::forward(index_record)); +// } +// +// VertexIndexRecordCollection &find(const Label &label) +// { +// // TODO: accessor should be outside? +// // bacause otherwise GC could delete record that has just be returned +// auto label_ref = label_ref_t(label); +// auto accessor = index->access(); +// return (*accessor.find(label_ref)).second; +// } +// +// private: +// std::unique_ptr index; +// }; diff --git a/include/storage/indexes/index_base.hpp b/include/storage/indexes/index_base.hpp new file mode 100644 index 000000000..aa7603848 --- /dev/null +++ b/include/storage/indexes/index_base.hpp @@ -0,0 +1,60 @@ +#pragma once + +// #include "storage/indexes/index_record.hpp" +#include +#include +#include "utils/border.hpp" +#include "utils/iterator/iterator_base.hpp" + +class DbTransaction; +class DbAccessor; + +template +class IndexRecord; + +// Defines ordering of data +enum Order +{ + None = 0, + Ascending = 1, + Descending = 2, +}; + +// Interface for all indexes. +// T type of record. +// K type of key on which records are ordered +template +class IndexBase +{ +public: + typedef T value_type; + typedef K key_type; + + IndexBase(bool unique, Order order) : unique(unique), order(order) {} + + // Insert's value. + // unique => returns false if there is already valid equal value. + // nonunique => always succeds. + virtual bool insert(IndexRecord &&value) = 0; + + // Returns iterator which returns valid records in range. + // order==noe => doesn't guarantee any order of returned records. + // order==Ascending => guarantees order of returnd records will be from + // smallest to largest. + // order==Descending => guarantees order of returned records will be from + // largest to smallest. + // Range must be from<=to + virtual std::unique_ptr> + for_range(DbAccessor &, Border from = Border(), + Border to = Border()) = 0; + + // Removes for all transactions obsolete Records. + // Cleaner has to call this method when he decideds that it is time for + // cleaning. + virtual void clean(DbTransaction &) = 0; + + // Are the records unique + const bool unique; + // Ordering of the records. + const Order order; +}; diff --git a/include/storage/indexes/index_record.hpp b/include/storage/indexes/index_record.hpp index b7e63efaa..70f05af71 100644 --- a/include/storage/indexes/index_record.hpp +++ b/include/storage/indexes/index_record.hpp @@ -1,46 +1,67 @@ #pragma once +#include "database/db_transaction.hpp" #include "mvcc/version_list.hpp" #include "utils/total_ordering.hpp" -template -class IndexRecord : TotalOrdering> +// class DbTransaction; +// namespace tx +// { +// class Transaction; +// } + +// T type of record. +// K key on which record is ordered. +template +class IndexRecord : public TotalOrdering> { public: using vlist_t = mvcc::VersionList; IndexRecord() = default; - IndexRecord(T *record, vlist_t *vlist) : record(record), vlist(vlist) + IndexRecord(K key, T *record, vlist_t *vlist) + : key(std::move(key)), record(record), vlist(vlist) { assert(record != nullptr); assert(vlist != nullptr); } - friend bool operator<(const IndexRecord& lhs, const IndexRecord& rhs) + friend bool operator<(const IndexRecord &lhs, const IndexRecord &rhs) { - return lhs.record < rhs.record; + return lhs.key < rhs.key || + (lhs.key == rhs.key && lhs.vlist == rhs.vlist && + lhs.record < rhs.record); } - friend bool operator==(const IndexRecord& lhs, const IndexRecord& rhs) + friend bool operator==(const IndexRecord &lhs, const IndexRecord &rhs) { - return lhs.record == rhs.record; + return lhs.key == rhs.key && + (lhs.vlist != rhs.vlist || lhs.record == rhs.record); } bool empty() const { return record == nullptr; } - // const typename T::Accessor get() - // { - // // TODO: if somebody wants to read T content - // // const T::Accessor has to be returned from here - // // the problem is that here we don't have pointer to store - // // TODO: figure it out - // } + bool is_valid(tx::Transaction &t) const + { + assert(!empty()); + return record == vlist->find(t); + } -// private: + const auto access(DbTransaction &db) const + { + return T::Accessor::create(record, vlist, db); + } + + const K key; + +private: T *const record{nullptr}; vlist_t *const vlist{nullptr}; }; -using VertexIndexRecord = IndexRecord; -using EdgeIndexRecord = IndexRecord; +template +using VertexIndexRecord = IndexRecord; + +template +using EdgeIndexRecord = IndexRecord; diff --git a/include/storage/indexes/index_record_collection.hpp b/include/storage/indexes/index_record_collection.hpp index 0d06e2a91..53d4a1ba3 100644 --- a/include/storage/indexes/index_record_collection.hpp +++ b/include/storage/indexes/index_record_collection.hpp @@ -1,38 +1,38 @@ -#pragma once - -#include - -#include "data_structures/concurrent/concurrent_set.hpp" -#include "storage/indexes/index_record.hpp" - -template -class IndexRecordCollection -{ -public: - using index_record_t = IndexRecord; - using index_record_collection_t = ConcurrentSet; - - IndexRecordCollection() - : records(std::make_unique()) - { - } - - void add(index_record_t &&record) - { - auto accessor = records->access(); - accessor.insert(std::forward(record)); - } - - auto access() - { - return records->access(); - } - - // TODO: iterator and proxy - -private: - std::unique_ptr records; -}; - -using VertexIndexRecordCollection = IndexRecordCollection; -using EdgeIndexRecordCollection = IndexRecordCollection; +// #pragma once +// +// #include +// +// #include "data_structures/concurrent/concurrent_set.hpp" +// #include "storage/indexes/index_record.hpp" +// +// template +// class IndexRecordCollection +// { +// public: +// using index_record_t = IndexRecord; +// using index_record_collection_t = ConcurrentSet; +// +// IndexRecordCollection() +// : records(std::make_unique()) +// { +// } +// +// void add(index_record_t &&record) +// { +// auto accessor = records->access(); +// accessor.insert(std::forward(record)); +// } +// +// auto access() +// { +// return records->access(); +// } +// +// // TODO: iterator and proxy +// +// private: +// std::unique_ptr records; +// }; +// +// using VertexIndexRecordCollection = IndexRecordCollection; +// using EdgeIndexRecordCollection = IndexRecordCollection; diff --git a/include/storage/indexes/sort_order.hpp b/include/storage/indexes/sort_order.hpp index 82a60f61d..5d9348601 100644 --- a/include/storage/indexes/sort_order.hpp +++ b/include/storage/indexes/sort_order.hpp @@ -3,7 +3,7 @@ template struct Ascending { - constexpr bool operator()(const T& lhs, const T& rhs) const + constexpr bool operator()(const T &lhs, const T &rhs) const { return lhs < rhs; } @@ -12,7 +12,7 @@ struct Ascending template struct Descending { - constexpr bool operator()(const T& lhs, const T& rhs) const + constexpr bool operator()(const T &lhs, const T &rhs) const { return lhs > rhs; } diff --git a/include/storage/label/label.hpp b/include/storage/label/label.hpp index 97430fe5a..bcb478921 100644 --- a/include/storage/label/label.hpp +++ b/include/storage/label/label.hpp @@ -1,27 +1,39 @@ #pragma once -#include #include +#include -#include "utils/total_ordering.hpp" +#include "storage/indexes/impl/nonunique_unordered_index.hpp" +#include "storage/vertex.hpp" +#include "storage/vertex_accessor.hpp" #include "utils/reference_wrapper.hpp" +#include "utils/total_ordering.hpp" +#include "utils/void.hpp" + +using LabelIndexRecord = VertexIndexRecord; class Label : public TotalOrdering(acc)) {} - // Iter(const Iter &other) = delete; - // Iter(Iter &&other) : - // begin(std::move(other.begin)),end(std::move(other.end)) {}; - - auto next() - { - if (begin != acc.end()) { - auto ret = Option(&(*(begin.operator->()))); - begin++; - return ret; - } else { - return Option(); - } - } - -private: - I begin; - A acc; -}; - -// TODO: Join to make functions into one -template -auto make_iter(A &&acc) -{ - return Iter()))), decltype(acc.begin()), - A>(std::move(acc)); -} - -template -auto make_iter_ref(A &acc) -{ - return Iter()))), decltype(acc.begin()), - A &>(acc); -} -} diff --git a/include/utils/iterator/iterator.hpp b/include/utils/iterator/iterator.hpp index e3e8c1cd1..f91d4e9e2 100644 --- a/include/utils/iterator/iterator.hpp +++ b/include/utils/iterator/iterator.hpp @@ -2,6 +2,8 @@ #include "utils/iterator/accessor.hpp" #include "utils/iterator/for_all.hpp" -#include "utils/iterator/iter.hpp" +#include "utils/iterator/func_iterator.hpp" +#include "utils/iterator/iterator_accessor.hpp" +#include "utils/iterator/iterator_base.hpp" #include "utils/iterator/map.hpp" -#include "utils/iterator/wrap.hpp" +#include "utils/iterator/range_iterator.hpp" diff --git a/include/utils/iterator/iterator_accessor.hpp b/include/utils/iterator/iterator_accessor.hpp new file mode 100644 index 000000000..794a9cf4a --- /dev/null +++ b/include/utils/iterator/iterator_accessor.hpp @@ -0,0 +1,59 @@ +#pragma once + +#include "utils/iterator/iterator_base.hpp" +#include "utils/option.hpp" + +namespace iter +{ + +// Class which turns accessor int next() based iterator. +// T - type of return value +// I - iterator type gotten from accessor +// A - accessor type +template +class IteratorAccessor : public IteratorBase +{ +public: + IteratorAccessor() = delete; + + IteratorAccessor(A &&acc) + : begin(std::move(acc.begin())), acc(std::forward(acc)) + { + } + // Iter(const Iter &other) = delete; + // Iter(Iter &&other) : + // begin(std::move(other.begin)),end(std::move(other.end)) {}; + + Option next() final + { + if (begin != acc.end()) { + auto ret = Option(&(*(begin.operator->()))); + begin++; + return ret; + } else { + return Option(); + } + } + +private: + I begin; + A acc; +}; + +// TODO: Join to make functions into one +template +auto make_iter(A &&acc) +{ + // Compiler cant deduce types T and I. decltype are here to help with it. + return IteratorAccessor()))), + decltype(acc.begin()), A>(std::move(acc)); +} + +template +auto make_iter_ref(A &acc) +{ + // Compiler cant deduce types T and I. decltype are here to help with it. + return IteratorAccessor()))), + decltype(acc.begin()), A &>(acc); +} +} diff --git a/include/utils/iterator/iterator_base.hpp b/include/utils/iterator/iterator_base.hpp new file mode 100644 index 000000000..0d805edfa --- /dev/null +++ b/include/utils/iterator/iterator_base.hpp @@ -0,0 +1,12 @@ +#pragma once + +#include "utils/option.hpp" + +// Base iterator for next() kind iterator. +// T - type of return value +template +class IteratorBase +{ +public: + virtual Option next() = 0; +}; diff --git a/include/utils/iterator/map.hpp b/include/utils/iterator/map.hpp index 7bc8ba474..2f4543bcb 100644 --- a/include/utils/iterator/map.hpp +++ b/include/utils/iterator/map.hpp @@ -1,38 +1,47 @@ #pragma once +#include "utils/iterator/iterator_base.hpp" #include "utils/option.hpp" namespace iter { -template -class Map + +// Class which maps values returned by I iterator into value of type T with OP +// function. +// T - type of return value +// I - iterator type +// OP - type of mapper function +template +class Map : public IteratorBase { public: Map() = delete; - template - Map(IT &&iter, OP &&op) : iter(std::move(iter)), op(std::move(op)) - { - } - auto next() + // Map operation is designed to be used in chained calls which operate on a + // iterator. Map will in that usecase receive other iterator by value and + // std::move is a optimization for it. + Map(I &&iter, OP &&op) : iter(std::move(iter)), op(std::move(op)) {} + + Option next() final { auto item = iter.next(); if (item.is_present()) { - return Option(op(item.take())); + return Option(op(item.take())); } else { - return Option(); + return Option(); } } private: I iter; - MapOperator op; + OP op; }; template auto make_map(I &&iter, OP &&op) { + // Compiler cant deduce type T. decltype is here to help with it. return Map(std::move(iter), std::move(op)); } diff --git a/include/utils/iterator/range_iterator.hpp b/include/utils/iterator/range_iterator.hpp new file mode 100644 index 000000000..923e9525c --- /dev/null +++ b/include/utils/iterator/range_iterator.hpp @@ -0,0 +1,74 @@ +#pragma once + +#include "utils/option.hpp" + +namespace iter +{ + +// Class which wraps iterator with next() into c++ iterator. +// T - type of return value +// I - iterator type +template +class RangeIterator +{ + +public: + RangeIterator() : iter(Option()), value(Option()){}; + + RangeIterator(I &&iter) + : value(iter.next()), iter(Option(std::move(iter))) + { + } + + T &operator*() + { + assert(value.is_present()); + return value.get(); + } + + T *operator->() + { + assert(value.is_present()); + return &value.get(); + } + + operator T &() + { + assert(value.is_present()); + return value.get(); + } + + RangeIterator &operator++() + { + assert(iter.is_present()); + value = iter.get().next(); + return (*this); + } + + RangeIterator &operator++(int) { return operator++(); } + + friend bool operator==(const RangeIterator &a, const RangeIterator &b) + { + return a.value.is_present() == b.value.is_present(); + } + + friend bool operator!=(const RangeIterator &a, const RangeIterator &b) + { + return !(a == b); + } + +private: + Option iter; + Option value; +}; + +template +auto make_range_iterator(I &&iter) +{ + // Because function isn't receving or in any way using type T from + // RangeIterator compiler can't deduce it thats way there is decltype in + // construction of RangeIterator. Resoulting type of iter.next().take() is + // T. + return RangeIterator(std::move(iter)); +} +} diff --git a/include/utils/iterator/wrap.hpp b/include/utils/iterator/wrap.hpp deleted file mode 100644 index a39fe6d2c..000000000 --- a/include/utils/iterator/wrap.hpp +++ /dev/null @@ -1,60 +0,0 @@ -#pragma once - -#include "utils/option.hpp" - -namespace iter -{ -template -class Wrap -{ - -public: - Wrap() : iter(Option()), value(Option()){}; - - Wrap(I &&iter) : value(iter.next()), iter(Option(std::move(iter))) {} - - T &operator*() - { - assert(value.is_present()); - return value.get(); - } - - T *operator->() - { - assert(value.is_present()); - return &value.get(); - } - - operator T &() - { - assert(value.is_present()); - return value.get(); - } - - Wrap &operator++() - { - assert(iter.is_present()); - value = iter.get().next(); - return (*this); - } - - Wrap &operator++(int) { return operator++(); } - - friend bool operator==(const Wrap &a, const Wrap &b) - { - return a.value.is_present() == b.value.is_present(); - } - - friend bool operator!=(const Wrap &a, const Wrap &b) { return !(a == b); } - -private: - Option iter; - Option value; -}; - -template -auto make_wrap(I &&iter) -{ - return Wrap(std::move(iter)); -} -} diff --git a/include/utils/option.hpp b/include/utils/option.hpp index 8e1b66d44..a3aae3c5f 100644 --- a/include/utils/option.hpp +++ b/include/utils/option.hpp @@ -1,5 +1,6 @@ #pragma once +#include #include #include @@ -8,6 +9,12 @@ class Option { public: Option() {} + // + // Option(T item) + // { + // new (data._M_addr()) T(std::forward(item)); + // initialized = true; + // } Option(T const &item) { @@ -22,6 +29,9 @@ public: } Option(Option &other) = default; + // Containers from std which have strong exception guarantees wont use move + // constructors and operators wihtout noexcept. "Optimized C++,2016 , Kurt + // Guntheroth, page: 142, title: Moving instances into std::vector" Option(Option &&other) noexcept { if (other.initialized) { @@ -36,7 +46,8 @@ public: if (initialized) get().~T(); } - Option &operator=(Option &&other) + Option &operator=(Option &other) = default; + Option &operator=(Option &&other) { if (initialized) { get().~T(); @@ -60,7 +71,11 @@ public: return *data._M_ptr(); } - const T &get() const noexcept { assert(initialized); } + const T &get() const noexcept + { + assert(initialized); + return *data._M_ptr(); + } T take() { @@ -72,6 +87,9 @@ public: explicit operator bool() const { return initialized; } private: + // Aligned buffer is here to ensure aligment for data of type T. It isn't + // applicable to just put T field because the field has to be able to be + // uninitialized to fulfill the semantics of Option class. __gnu_cxx::__aligned_buffer data; bool initialized = false; }; diff --git a/include/utils/void.hpp b/include/utils/void.hpp new file mode 100644 index 000000000..ff36ff3b1 --- /dev/null +++ b/include/utils/void.hpp @@ -0,0 +1,10 @@ +#pragma once + +#include "utils/total_ordering.hpp" + +class Void : public TotalOrdering +{ + friend bool operator<(const Void &lhs, const Void &rhs) { return false; } + + friend bool operator==(const Void &lhs, const Void &rhs) { return true; } +}; diff --git a/poc/astar.cpp b/poc/astar.cpp index 7e9be0e03..1490efb31 100644 --- a/poc/astar.cpp +++ b/poc/astar.cpp @@ -11,8 +11,15 @@ #include "data_structures/map/rh_hashmap.hpp" #include "database/db.hpp" #include "database/db_accessor.hpp" +#include "storage/edges.cpp" +#include "storage/edges.hpp" +#include "storage/indexes/impl/nonunique_unordered_index.cpp" +#include "storage/model/properties/properties.cpp" +#include "storage/record_accessor.cpp" #include "storage/vertex_accessor.cpp" #include "storage/vertex_accessor.hpp" +#include "storage/vertices.cpp" +#include "storage/vertices.hpp" using namespace std; typedef Vertex::Accessor VertexAccessor; @@ -23,13 +30,19 @@ class Node { public: Node *parent = {nullptr}; + type_key_t tkey; double cost; int depth = {0}; VertexAccessor vacc; - Node(VertexAccessor vacc, double cost) : cost(cost), vacc(vacc) {} - Node(VertexAccessor vacc, double cost, Node *parent) - : cost(cost), vacc(vacc), parent(parent), depth(parent->depth + 1) + Node(VertexAccessor vacc, double cost, type_key_t tkey) + : cost(cost), vacc(vacc), tkey(tkey) + { + } + Node(VertexAccessor vacc, double cost, Node *parent, + type_key_t tkey) + : cost(cost), vacc(vacc), parent(parent), depth(parent->depth + 1), + tkey(tkey) { } @@ -38,7 +51,7 @@ public: auto now = this; double sum = 0; do { - sum += now->vacc.at("score"); + sum += *(now->vacc.at(tkey).get()); now = now->parent; } while (now != nullptr); return sum; @@ -105,10 +118,11 @@ void found_result(Node *res) } } -double calc_heuristic_cost_dummy(Edge::Accessor &edge, Vertex::Accessor &vertex) +double calc_heuristic_cost_dummy(type_key_t tkey, Edge::Accessor &edge, + Vertex::Accessor &vertex) { assert(!vertex.empty()); - return 1 - vertex.at("score"); + return 1 - *vertex.at(tkey).get(); } typedef bool (*EdgeFilter)(DbAccessor &t, Edge::Accessor &, Node *before); @@ -168,11 +182,16 @@ bool vertex_filter_contained(DbAccessor &t, Vertex::Accessor &v, Node *before) // Filtri vracaju true ako element zadovoljava uvjete. auto a_star(Db &db, int64_t sys_id_start, uint max_depth, EdgeFilter e_filter[], VertexFilter v_filter[], - double (*calc_heuristic_cost)(Edge::Accessor &edge, + double (*calc_heuristic_cost)(type_key_t tkey, + Edge::Accessor &edge, Vertex::Accessor &vertex), int limit) { DbAccessor t(db); + type_key_t tkey = t.vertex_property_family_get("score") + .get(Type::Double) + .type_key(); + auto best_found = new std::map[max_depth]; std::vector best; @@ -182,7 +201,7 @@ auto a_star(Db &db, int64_t sys_id_start, uint max_depth, EdgeFilter e_filter[], auto start_vr = t.vertex_find(sys_id_start); assert(start_vr); start_vr.get().fill(); - Node *start = new Node(start_vr.take(), 0); + Node *start = new Node(start_vr.take(), 0, tkey); queue.push(start); int count = 0; do { @@ -213,8 +232,8 @@ auto a_star(Db &db, int64_t sys_id_start, uint max_depth, EdgeFilter e_filter[], if (e_filter[now->depth](t, edge, now)) { VertexAccessor va = edge.to(); if (v_filter[now->depth](t, va, now)) { - auto cost = calc_heuristic_cost(edge, va); - Node *n = new Node(va, now->cost + cost, now); + auto cost = calc_heuristic_cost(tkey, edge, va); + Node *n = new Node(va, now->cost + cost, now, tkey); queue.push(n); } } @@ -346,6 +365,18 @@ int load_csv(Db &db, char *file_path, char *edge_file_path) std::string line; DbAccessor t(db); + auto key_id = + t.vertex_property_family_get("id").get(Type::Int32).family_key(); + auto key_garment_id = t.vertex_property_family_get("garment_id") + .get(Type::Int32) + .family_key(); + auto key_garment_category_id = + t.vertex_property_family_get("garment_category_id") + .get(Type::Int32) + .family_key(); + auto key_score = + t.vertex_property_family_get("score").get(Type::Double).family_key(); + int max_score = 1000000; // VERTEX import @@ -356,14 +387,15 @@ int load_csv(Db &db, char *file_path, char *edge_file_path) } auto vertex_accessor = t.vertex_insert(); - vertex_accessor.property("id", std::make_shared(id)); - vertex_accessor.property("garment_id", std::make_shared(gar_id)); - vertex_accessor.property("garment_category_id", - std::make_shared(cat_id)); + vertex_accessor.set(key_id, std::make_shared(id)); + vertex_accessor.set(key_garment_id, std::make_shared(gar_id)); + vertex_accessor.set(key_garment_category_id, + std::make_shared(cat_id)); std::srand(id ^ 0x7482616); - vertex_accessor.property( - "score", std::make_shared((std::rand() % max_score) / - (max_score + 0.0))); + vertex_accessor.set(key_score, + std::make_shared((std::rand() % max_score) / + (max_score + 0.0))); + for (auto l_name : labels) { auto &label = t.label_find_or_create(l_name); vertex_accessor.add_label(label); @@ -372,6 +404,7 @@ int load_csv(Db &db, char *file_path, char *edge_file_path) return vertex_accessor; }; + // Skip header std::getline(file, line); vector va; @@ -423,39 +456,39 @@ int load_csv(Db &db, char *file_path, char *edge_file_path) void load_graph_dummy(Db &db) { DbAccessor t(db); - auto v = [&](auto id, auto score) { - auto vertex_accessor = t.vertex_insert(); - vertex_accessor.property("id", std::make_shared(id)); - vertex_accessor.property("score", std::make_shared(score)); - return vertex_accessor.id(); - }; - - Id va[] = { - v(0, 0.5), v(1, 1), v(2, 0.3), v(3, 0.15), v(4, 0.8), v(5, 0.8), - }; - - auto e = [&](auto from, auto type, auto to) { - auto v1 = t.vertex_find(va[from]); - - auto v2 = t.vertex_find(va[to]); - - auto edge_accessor = t.edge_insert(v1.get(), v2.get()); - - auto &edge_type = t.type_find_or_create(type); - edge_accessor.edge_type(edge_type); - }; - - e(0, "ok", 3); - e(0, "ok", 2); - e(0, "ok", 4); - e(1, "ok", 3); - e(2, "ok", 1); - e(2, "ok", 4); - e(3, "ok", 4); - e(3, "ok", 5); - e(4, "ok", 0); - e(4, "ok", 1); - e(5, "ok", 2); + // auto v = [&](auto id, auto score) { + // auto vertex_accessor = t.vertex_insert(); + // vertex_accessor.property("id", std::make_shared(id)); + // vertex_accessor.property("score", std::make_shared(score)); + // return vertex_accessor.id(); + // }; + // + // Id va[] = { + // v(0, 0.5), v(1, 1), v(2, 0.3), v(3, 0.15), v(4, 0.8), v(5, 0.8), + // }; + // + // auto e = [&](auto from, auto type, auto to) { + // auto v1 = t.vertex_find(va[from]); + // + // auto v2 = t.vertex_find(va[to]); + // + // auto edge_accessor = t.edge_insert(v1.get(), v2.get()); + // + // auto &edge_type = t.type_find_or_create(type); + // edge_accessor.edge_type(edge_type); + // }; + // + // e(0, "ok", 3); + // e(0, "ok", 2); + // e(0, "ok", 4); + // e(1, "ok", 3); + // e(2, "ok", 1); + // e(2, "ok", 4); + // e(3, "ok", 4); + // e(3, "ok", 5); + // e(4, "ok", 0); + // e(4, "ok", 1); + // e(5, "ok", 2); t.commit(); } diff --git a/src/data_structures/map/rh_common.hpp b/src/data_structures/map/rh_common.hpp index 69593bdb0..9fa396e30 100644 --- a/src/data_structures/map/rh_common.hpp +++ b/src/data_structures/map/rh_common.hpp @@ -187,22 +187,26 @@ public: RhBase() {} - RhBase(const RhBase &other) - { - capacity = other.capacity; - count = other.count; - if (capacity > 0) { - size_t bytes = sizeof(Combined) * capacity; - array = (Combined *)malloc(bytes); - memcpy(array, other.array, bytes); + RhBase(const RhBase &other) { copy_from(other); } - } else { - array = nullptr; - } - } + RhBase(RhBase &&other) { take_from(std::move(other)); } ~RhBase() { this->clear(); } + RhBase &operator=(const RhBase &other) + { + clear(); + copy_from(other); + return *this; + } + + RhBase &operator=(RhBase &&other) + { + clear(); + take_from(std::move(other)); + return *this; + } + Iterator begin() { return Iterator(this); } ConstIterator begin() const { return ConstIterator(this); } @@ -216,6 +220,30 @@ public: ConstIterator cend() const { return ConstIterator(); } protected: + void copy_from(const RhBase &other) + { + capacity = other.capacity; + count = other.count; + if (capacity > 0) { + size_t bytes = sizeof(Combined) * capacity; + array = (Combined *)malloc(bytes); + memcpy(array, other.array, bytes); + + } else { + array = nullptr; + } + } + + void take_from(RhBase &&other) + { + capacity = other.capacity; + count = other.count; + array = other.array; + other.array = nullptr; + other.count = 0; + other.capacity = 0; + } + void init_array(size_t size) { size_t bytes = sizeof(Combined) * size; diff --git a/src/database/db.cpp b/src/database/db.cpp index 9ceb50a77..df9abaa74 100644 --- a/src/database/db.cpp +++ b/src/database/db.cpp @@ -1,4 +1,5 @@ #include "database/db.hpp" +#include "storage/model/properties/property_family.hpp" Db::Db() = default; Db::Db(const std::string &name) : name_(name) {} diff --git a/src/database/db_accessor.cpp b/src/database/db_accessor.cpp index b3972e29a..ca07dc89a 100644 --- a/src/database/db_accessor.cpp +++ b/src/database/db_accessor.cpp @@ -1,36 +1,46 @@ #include "database/db_accessor.hpp" -DbAccessor::DbAccessor(Db &db) : db(DbTransaction(db, db.tx_engine.begin())) {} +#include "database/db.hpp" + +#include "utils/iterator/iterator.hpp" + +DbAccessor::DbAccessor(Db &db) + : db_transaction(DbTransaction(db, db.tx_engine.begin())) +{ +} // VERTEX METHODS auto DbAccessor::vertex_access() { return iter::make_map( - iter::make_iter(this->db.db.graph.vertices.access()), - [&](auto e) -> auto { return Vertex::Accessor(&(e->second), db); }); + iter::make_iter(this->db_transaction.db.graph.vertices.access()), + [&](auto e) -> auto { + return Vertex::Accessor(&(e->second), db_transaction); + }); } Option DbAccessor::vertex_find(const Id &id) { - return this->db.db.graph.vertices.find(db, id); + return this->db_transaction.db.graph.vertices.find(db_transaction, id); } Vertex::Accessor DbAccessor::vertex_insert() { - return this->db.db.graph.vertices.insert(db); + return this->db_transaction.db.graph.vertices.insert(db_transaction); } // EDGE METHODS Option DbAccessor::edge_find(const Id &id) { - return db.db.graph.edges.find(db, id); + return db_transaction.db.graph.edges.find(db_transaction, id); } Edge::Accessor DbAccessor::edge_insert(Vertex::Accessor const &from, Vertex::Accessor const &to) { - auto edge_accessor = db.db.graph.edges.insert(db, from.vlist, to.vlist); + auto edge_accessor = db_transaction.db.graph.edges.insert( + db_transaction, from.vlist, to.vlist); from.update()->data.out.add(edge_accessor.vlist); to.update()->data.in.add(edge_accessor.vlist); return edge_accessor; @@ -39,37 +49,57 @@ Edge::Accessor DbAccessor::edge_insert(Vertex::Accessor const &from, // LABEL METHODS const Label &DbAccessor::label_find_or_create(const std::string &name) { - return db.db.graph.label_store.find_or_create( + return db_transaction.db.graph.label_store.find_or_create( std::forward(name)); } bool DbAccessor::label_contains(const std::string &name) { - return db.db.graph.label_store.contains( + return db_transaction.db.graph.label_store.contains( std::forward(name)); } -VertexIndexRecordCollection &DbAccessor::label_find_index(const Label &label) -{ - return db.db.graph.vertices.find_label_index(label); -} - // TYPE METHODS const EdgeType &DbAccessor::type_find_or_create(const std::string &name) { - return db.db.graph.edge_type_store.find_or_create( + return db_transaction.db.graph.edge_type_store.find_or_create( std::forward(name)); } bool DbAccessor::type_contains(const std::string &name) { - return db.db.graph.edge_type_store.contains( + return db_transaction.db.graph.edge_type_store.contains( std::forward(name)); } +//********************PROPERTY METHODS +// Vertices::prop_familys_t::Accessor +// DbAccessor::vertex_property_family_access() +// { +// return db.db.graph.vertices.property_family_access(); +// } +// +// auto DbAccessor::edge_property_family_access() +// { +// return db.db.graph.edges.property_family_access(); +// } + +auto edge_property_family_access(); + +PropertyFamily &DbAccessor::vertex_property_family_get(const std::string &name) +{ + return db_transaction.db.graph.vertices.property_family_find_or_create( + name); +} + +PropertyFamily &DbAccessor::edge_property_family_get(const std::string &name) +{ + return db_transaction.db.graph.edges.property_family_find_or_create(name); +} + // TRANSACTION METHODS -void DbAccessor::commit() { db.trans.commit(); } -void DbAccessor::abort() { db.trans.abort(); } +void DbAccessor::commit() { db_transaction.trans.commit(); } +void DbAccessor::abort() { db_transaction.trans.abort(); } // // EASE OF USE METHODS // tx::Transaction &DbAccessor::operator*() { return db.trans; } diff --git a/src/database/db_transaction.cpp b/src/database/db_transaction.cpp index 3760ce479..c16be5e5f 100644 --- a/src/database/db_transaction.cpp +++ b/src/database/db_transaction.cpp @@ -1,8 +1,2 @@ -#include "database/db.hpp" -#include "database/db_transaction.hpp" - -void DbTransaction::update_label_index(const Label &label, - VertexIndexRecord &&index_record) -{ - db.graph.vertices.update_label_index(label, std::move(index_record)); -} +// #include "database/db.hpp" +// #include "database/db_transaction.hpp" diff --git a/src/query_engine/util.cpp b/src/query_engine/util.cpp index 9a980826e..2efcaf5f3 100644 --- a/src/query_engine/util.cpp +++ b/src/query_engine/util.cpp @@ -15,7 +15,7 @@ void cout_properties(const Properties &properties) cout << "----" << endl; } -void cout_property(const std::string &key, const Property &property) +void cout_property(const prop_key_t &key, const Property &property) { ConsoleWriter writer; writer.handle(key, property); diff --git a/src/storage/edges.cpp b/src/storage/edges.cpp index 5c142e9a8..3c5a3bfe1 100644 --- a/src/storage/edges.cpp +++ b/src/storage/edges.cpp @@ -1,4 +1,6 @@ #include "storage/edges.hpp" +#include "storage/model/properties/property_family.hpp" +#include "utils/iterator/iterator.hpp" Option Edges::find(DbTransaction &t, const Id &id) { @@ -30,3 +32,25 @@ Edge::Accessor Edges::insert(DbTransaction &t, VertexRecord *from, return Edge::Accessor(edge, &inserted_edge_record->second, t); } + +// auto Edges::property_family_access() +// { // Returnig access directly would allow extern code to remove elements. +// Which +// // would be BAD, VERY BAD. +// return iter::make_iter(prop_familys.access()); +// } + +PropertyFamily &Edges::property_family_find_or_create(const std::string &name) +{ + auto acc = prop_familys.access(); + auto it = acc.find(name); + if (it == acc.end()) { + PropertyFamily *family = new PropertyFamily(name); + auto res = acc.insert(name, family); + if (!res.second) { + delete family; + } + it = res.first; + } + return *(it->second); +} diff --git a/src/storage/indexes/impl/nonunique_unordered_index.cpp b/src/storage/indexes/impl/nonunique_unordered_index.cpp new file mode 100644 index 000000000..7278d3dc4 --- /dev/null +++ b/src/storage/indexes/impl/nonunique_unordered_index.cpp @@ -0,0 +1,61 @@ +#include "storage/indexes/impl/nonunique_unordered_index.hpp" + +#include "database/db_accessor.hpp" +#include "database/db_transaction.hpp" +#include "utils/iterator/iterator.hpp" + +template +NonUniqueUnorderedIndex::NonUniqueUnorderedIndex() + : IndexBase(false, None) +{ +} + +template +bool NonUniqueUnorderedIndex::insert(IndexRecord &&value) +{ + list.begin().push(std::move(value)); + return true; +} + +template +std::unique_ptr> +NonUniqueUnorderedIndex::for_range(DbAccessor &t, Border from, + Border to) +{ + return std::make_unique( + for_range_exact(t, std::move(from), std::move(to))); +} + +template +auto NonUniqueUnorderedIndex::for_range_exact(DbAccessor &t_v, + Border from_v, + Border to_v) +{ + return iter::make_iterator([ + it = list.cbegin(), end = list.cend(), from = from_v, to = to_v, t = t_v + ]() mutable->auto { + while (it != end) { + const IndexRecord &r = *it; + if (from < r.key && to > r.key && + r.is_valid(t.db_transaction.trans)) { + const typename T::Accessor acc = r.access(t.db_transaction); + it++; + return make_option(std::move(acc)); + } + it++; + } + + return Option(); + }); +} + +template +void NonUniqueUnorderedIndex::clean(DbTransaction &) +{ + // TODO: Actual cleaning +} + +#include "storage/vertex.hpp" +// #include "utils/singleton.hpp" +template class NonUniqueUnorderedIndex; diff --git a/src/storage/label/label.cpp b/src/storage/label/label.cpp index 9e0b8228f..b669fcc56 100644 --- a/src/storage/label/label.cpp +++ b/src/storage/label/label.cpp @@ -1,24 +1,29 @@ +// #include "storage/indexes/impl/nonunique_unordered_index.hpp" #include "storage/label/label.hpp" -Label::Label(const std::string& name) : name(name) {} -Label::Label(std::string&& name) : name(std::move(name)) {} +Label::Label(const std::string &name) + : name(name), index(std::unique_ptr(new label_index_t())) +{ +} +Label::Label(std::string &&name) + : name(std::move(name)), + index(std::unique_ptr(new label_index_t())) +{ +} -bool operator<(const Label& lhs, const Label& rhs) +bool operator<(const Label &lhs, const Label &rhs) { return lhs.name < rhs.name; } -bool operator==(const Label& lhs, const Label& rhs) +bool operator==(const Label &lhs, const Label &rhs) { return lhs.name == rhs.name; } -std::ostream& operator<<(std::ostream& stream, const Label& label) +std::ostream &operator<<(std::ostream &stream, const Label &label) { return stream << label.name; } -Label::operator const std::string&() const -{ - return name; -} +Label::operator const std::string &() const { return name; } diff --git a/src/storage/label/label_collection.cpp b/src/storage/label/label_collection.cpp index 3131ce5b2..22a77380a 100644 --- a/src/storage/label/label_collection.cpp +++ b/src/storage/label/label_collection.cpp @@ -1,5 +1,7 @@ #include "storage/label/label_collection.hpp" +#include "storage/label/label.hpp" + auto LabelCollection::begin() { return _labels.begin(); } auto LabelCollection::begin() const { return _labels.begin(); } auto LabelCollection::cbegin() const { return _labels.begin(); } @@ -8,36 +10,30 @@ auto LabelCollection::end() { return _labels.end(); } auto LabelCollection::end() const { return _labels.end(); } auto LabelCollection::cend() const { return _labels.end(); } -bool LabelCollection::add(const Label& label) +bool LabelCollection::add(const Label &label) { return _labels.insert(label_ref_t(label)).second; } -bool LabelCollection::has(const Label& label) const +bool LabelCollection::has(const Label &label) const { return _labels.count(label); } -size_t LabelCollection::count() const { - return _labels.size(); -} +size_t LabelCollection::count() const { return _labels.size(); } -bool LabelCollection::remove(const Label& label) +bool LabelCollection::remove(const Label &label) { auto it = _labels.find(label); - if(it == _labels.end()) - return false; + if (it == _labels.end()) return false; return _labels.erase(it), true; } -void LabelCollection::clear() -{ - _labels.clear(); -} +void LabelCollection::clear() { _labels.clear(); } -const std::set& LabelCollection::operator()() const +const std::set &LabelCollection::operator()() const { return _labels; } diff --git a/src/storage/model/properties/properties.cpp b/src/storage/model/properties/properties.cpp index c4dec594d..0f3110427 100644 --- a/src/storage/model/properties/properties.cpp +++ b/src/storage/model/properties/properties.cpp @@ -1,54 +1,76 @@ #include "storage/model/properties/properties.hpp" #include "storage/model/properties/null.hpp" +#include "storage/model/properties/property_family.hpp" +#include "utils/option.hpp" -const Property& Properties::at(const std::string& key) const +const Property &Properties::at(prop_key_t &key) const { auto it = props.find(key); - if(it == props.end()) - return Property::Null; + if (it == props.end()) return Property::Null; return *it->second.get(); } +template +auto Properties::at(type_key_t &key) const +{ + auto f_key = key.family_key(); + auto it = props.find(f_key); + + if (it == props.end() || it->first.prop_type() != key.prop_type()) + return Optionsecond.get()->template as().value_ref()))>(); + + return make_option(&(it->second.get()->template as().value_ref())); +} + template -void Properties::set(const std::string& key, Args&&... args) +void Properties::set(type_key_t &key, Args &&... args) { auto value = std::make_shared(std::forward(args)...); // try to emplace the item + // TODO: There is uneccesary copying of value here. auto result = props.emplace(std::make_pair(key, value)); - // return if we succedded - if(result.second) - return; - - // the key already exists, replace the value it holds - result.first->second = std::move(value); + if (!result.second) { + // It is necessary to change key because the types from before and now + // could be different. + prop_key_t &key_ref = const_cast(result.first->first); + key_ref = key; + result.first->second = std::move(value); + } } -void Properties::set(const std::string& key, Property::sptr value) +void Properties::set(prop_key_t &key, Property::sptr value) { - props[key] = std::move(value); + // TODO: There is uneccesary copying of value here. + auto result = props.insert(make_pair(key, value)); + if (!result.second) { + // It is necessary to change key because the types from before and now + // could be different. + prop_key_t &key_ref = const_cast(result.first->first); + key_ref = key; + result.first->second = std::move(value); + } } -void Properties::clear(const std::string& key) -{ - props.erase(key); -} +void Properties::clear(prop_key_t &key) { props.erase(key); } // template // void Properties::accept(Handler& handler) const // { // for(auto& kv : props) // handler.handle(kv.first, *kv.second); -// +// // handler.finish(); // } -template<> -inline void Properties::set(const std::string& key) +template <> +inline void Properties::set(type_key_t &key) { - clear(key); + auto fk = key.family_key(); + clear(fk); } diff --git a/src/storage/model/properties/property_family.cpp b/src/storage/model/properties/property_family.cpp new file mode 100644 index 000000000..c6bc34dea --- /dev/null +++ b/src/storage/model/properties/property_family.cpp @@ -0,0 +1,39 @@ +#include "storage/model/properties/property_family.hpp" + +PropertyFamily::PropertyFamily(std::string const &name_v) + : name_v(std::forward(name_v)) +{ +} +PropertyFamily::PropertyFamily(std::string &&name_v) : name_v(std::move(name_v)) +{ +} + +std::string const &PropertyFamily::name() const { return name_v; } + +// Returns type if it exists otherwise creates it. +PropertyFamily::PropertyType &PropertyFamily::get(Type type) +{ + auto acc = types.access(); + auto it = acc.find(type); + if (it == acc.end()) { + auto value = + std::unique_ptr(new PropertyType(*this, type)); + auto res = acc.insert(type, std::move(value)); + it = res.first; + } + return *(it->second); +} + +PropertyFamily::PropertyType::PropertyType(PropertyFamily &family, Type type) + : family(family), type(std::move(type)) +{ +} + +bool PropertyFamily::PropertyType::is(Type &t) const { return type == t; } + +// Returns key ordered on POINTERS to PropertyFamily +PropertyFamily::PropertyType::PropertyFamilyKey +PropertyFamily::PropertyType::family_key() +{ + return PropertyFamilyKey(*this); +} diff --git a/src/storage/record_accessor.cpp b/src/storage/record_accessor.cpp new file mode 100644 index 000000000..21198de4c --- /dev/null +++ b/src/storage/record_accessor.cpp @@ -0,0 +1,8 @@ +#include "storage/record_accessor.hpp" + +template +template +auto RecordAccessor::at(type_key_t &key) const +{ + return properties().template at(key); +} diff --git a/src/storage/vertex_accessor.cpp b/src/storage/vertex_accessor.cpp index 250910689..ab27a5b3a 100644 --- a/src/storage/vertex_accessor.cpp +++ b/src/storage/vertex_accessor.cpp @@ -15,14 +15,20 @@ size_t Vertex::Accessor::in_degree() const size_t Vertex::Accessor::degree() const { return in_degree() + out_degree(); } -void Vertex::Accessor::add_label(const Label &label) +bool Vertex::Accessor::add_label(const Label &label) { // update vertex - this->record->data.labels.add(label); + if (this->record->data.labels.add(label)) { + label.index->insert(create_ir(std::nullptr_t())); + return true; + } + return false; +} - // update index - this->db.update_label_index(label, - VertexIndexRecord(this->record, this->vlist)); +bool Vertex::Accessor::remove_label(const Label &label) +{ + // update vertex + return this->record->data.labels.remove(label); } bool Vertex::Accessor::has_label(const Label &label) const diff --git a/src/storage/vertices.cpp b/src/storage/vertices.cpp index a6cf69c9a..84aaee3e2 100644 --- a/src/storage/vertices.cpp +++ b/src/storage/vertices.cpp @@ -1,4 +1,5 @@ #include "storage/vertices.hpp" +#include "utils/iterator/iterator.hpp" Vertices::vertices_t::Accessor Vertices::access() { return vertices.access(); } @@ -33,13 +34,21 @@ Vertex::Accessor Vertices::insert(DbTransaction &t) return Vertex::Accessor(vertex, &inserted_vertex_record->second, t); } -void Vertices::update_label_index(const Label &label, - VertexIndexRecord &&index_record) -{ - label_index.update(label, std::forward(index_record)); -} +// +// Vertices::prop_familys_t::Accessor Vertices::property_family_access() +// { +// return prop_familys.access(); +// } -VertexIndexRecordCollection &Vertices::find_label_index(const Label &label) +PropertyFamily & +Vertices::property_family_find_or_create(const std::string &name) { - return label_index.find(label); + auto acc = prop_familys.access(); + auto it = acc.find(name); + if (it == acc.end()) { + auto family = std::unique_ptr(new PropertyFamily(name)); + auto res = acc.insert(name, std::move(family)); + it = res.first; + } + return *(it->second); } diff --git a/tests/integration/queries.cpp b/tests/integration/queries.cpp index 81acd19b8..ed940dee1 100644 --- a/tests/integration/queries.cpp +++ b/tests/integration/queries.cpp @@ -1,4 +1,8 @@ #include "query_engine/hardcode/queries.hpp" +#include "storage/edges.cpp" +#include "storage/edges.hpp" +#include "storage/vertices.cpp" +#include "storage/vertices.hpp" #include "utils/assert.hpp" int main(void) @@ -14,11 +18,10 @@ int main(void) "CREATE (n:LABEL {name: \"TEST1\"}) RETURN n", "CREATE (n:LABEL {name: \"TEST2\"}) RETURN n", "CREATE (n:LABEL {name: \"TEST3\"}) RETURN n", - "CREATE (n:ACCOUNT {id: 2322, name: \"TEST\", country: \"Croatia\", created_at: 2352352}) RETURN n" , - "MATCH (n {id: 0}) RETURN n", - "MATCH (n {id: 1}) RETURN n", - "MATCH (n {id: 2}) RETURN n", - "MATCH (n {id: 3}) RETURN n", + "CREATE (n:ACCOUNT {id: 2322, name: \"TEST\", country: \"Croatia\", " + "created_at: 2352352}) RETURN n", + "MATCH (n {id: 0}) RETURN n", "MATCH (n {id: 1}) RETURN n", + "MATCH (n {id: 2}) RETURN n", "MATCH (n {id: 3}) RETURN n", "MATCH (a {id:0}), (p {id: 1}) CREATE (a)-[r:IS]->(p) RETURN r", "MATCH (a {id:1}), (p {id: 2}) CREATE (a)-[r:IS]->(p) RETURN r", "MATCH ()-[r]-() WHERE ID(r)=0 RETURN r", @@ -26,8 +29,7 @@ int main(void) "MATCH (n: {id: 0}) SET n.name = \"TEST100\" RETURN n", "MATCH (n: {id: 1}) SET n.name = \"TEST101\" RETURN n", "MATCH (n: {id: 0}) SET n.name = \"TEST102\" RETURN n", - "MATCH (n:LABEL) RETURN n" - }; + "MATCH (n:LABEL) RETURN n"}; for (auto &query : queries) { auto stripped = stripper.strip(query); diff --git a/tests/manual/queries.cpp b/tests/manual/queries.cpp index fbc0d2336..a65a9e231 100644 --- a/tests/manual/queries.cpp +++ b/tests/manual/queries.cpp @@ -1,6 +1,10 @@ #include #include "query_engine/hardcode/queries.hpp" +#include "storage/edges.cpp" +#include "storage/edges.hpp" +#include "storage/vertices.cpp" +#include "storage/vertices.hpp" using namespace std; diff --git a/tests/unit/concurrent_list.cpp b/tests/unit/concurrent_list.cpp new file mode 100644 index 000000000..54d42a9c8 --- /dev/null +++ b/tests/unit/concurrent_list.cpp @@ -0,0 +1,75 @@ +#define CATCH_CONFIG_MAIN +#include "catch.hpp" + +#include "data_structures/concurrent/concurrent_list.hpp" + +TEST_CASE("Conncurent List insert") +{ + List list; + auto it = list.begin(); + it.push(32); + it.reset(); + REQUIRE(*it == 32); +} + +TEST_CASE("Conncurent List iterate") +{ + List list; + auto it = list.begin(); + it.push(32); + it.push(7); + it.push(9); + it.push(0); + it.reset(); + + REQUIRE(*it == 0); + it++; + REQUIRE(*it == 9); + it++; + REQUIRE(*it == 7); + it++; + REQUIRE(*it == 32); + it++; + REQUIRE(it == list.end()); +} + +TEST_CASE("Conncurent List head remove") +{ + List list; + auto it = list.begin(); + it.push(32); + it.reset(); + + REQUIRE(it.remove()); + REQUIRE(it.is_removed()); + REQUIRE(!it.remove()); + + it.reset(); + REQUIRE(it == list.end()); +} + +TEST_CASE("Conncurent List remove") +{ + List list; + auto it = list.begin(); + it.push(32); + it.push(7); + it.push(9); + it.push(0); + it.reset(); + + it++; + it++; + REQUIRE(it.remove()); + REQUIRE(it.is_removed()); + REQUIRE(!it.remove()); + + it.reset(); + REQUIRE(*it == 0); + it++; + REQUIRE(*it == 9); + it++; + REQUIRE(*it == 32); + it++; + REQUIRE(it == list.end()); +} From cdd7188747293297c07bf4d6c64db0822e609893 Mon Sep 17 00:00:00 2001 From: Kruno Tomola Fabro Date: Thu, 18 Aug 2016 17:43:06 +0100 Subject: [PATCH 4/4] Changes for audit of previous commit. --- .../data_structures/concurrent/concurrent_list.hpp | 3 +-- include/storage/record_accessor.hpp | 7 ++++++- include/storage/vertices.hpp | 5 +---- include/utils/border.hpp | 4 ++-- include/utils/iterator/iterator.hpp | 2 +- .../{func_iterator.hpp => lambda_iterator.hpp} | 12 ++++++------ src/data_structures/map/rh_common.hpp | 4 ++-- src/storage/vertex_accessor.cpp | 2 +- 8 files changed, 20 insertions(+), 19 deletions(-) rename include/utils/iterator/{func_iterator.hpp => lambda_iterator.hpp} (62%) diff --git a/include/data_structures/concurrent/concurrent_list.hpp b/include/data_structures/concurrent/concurrent_list.hpp index 0e364a325..76e547eda 100644 --- a/include/data_structures/concurrent/concurrent_list.hpp +++ b/include/data_structures/concurrent/concurrent_list.hpp @@ -7,7 +7,7 @@ template class List { - +private: template static V load(std::atomic &atomic) { @@ -33,7 +33,6 @@ class List return atomic.exchange(desired, std::memory_order_seq_cst); } -private: class Node { public: diff --git a/include/storage/record_accessor.hpp b/include/storage/record_accessor.hpp index 8928b90ad..4703509cc 100644 --- a/include/storage/record_accessor.hpp +++ b/include/storage/record_accessor.hpp @@ -103,8 +103,13 @@ public: } protected: + IndexRecord create_index_record() + { + return create_index_record(std::nullptr_t()); + } + template - IndexRecord create_ir(K &&key) + IndexRecord create_index_record(K &&key) { return IndexRecord(std::move(key), record, vlist); } diff --git a/include/storage/vertices.hpp b/include/storage/vertices.hpp index 616f63ed9..b3503afd5 100644 --- a/include/storage/vertices.hpp +++ b/include/storage/vertices.hpp @@ -3,10 +3,7 @@ #include #include #include "data_structures/concurrent/concurrent_map.hpp" -// #include "database/db_transaction.hpp" #include "storage/common.hpp" -// #include "storage/indexes/index.hpp" -// #include "storage/indexes/index_record_collection.hpp" #include "storage/model/properties/property_family.hpp" #include "storage/vertex_accessor.hpp" #include "utils/option.hpp" @@ -33,7 +30,7 @@ public: private: vertices_t vertices; - // TODO: Because familys wont be removed this could be done with more + // TODO: Because families wont be removed this could be done with more // efficent // data structure. prop_familys_t prop_familys; diff --git a/include/utils/border.hpp b/include/utils/border.hpp index 94e56a712..641ee1d29 100644 --- a/include/utils/border.hpp +++ b/include/utils/border.hpp @@ -26,14 +26,14 @@ public: Border &operator=(Border &&other) = default; Border &operator=(Border &other) = default; - // true if no border or this>key or this>=key depends on border type. + // true if no border or this > key or this >= key depends on border type. bool operator>(const T &other) const { return !key.is_present() || key.get() > other || (type == Including && key.get() == other); } - // true if no border or this -class FunctionIterator : public IteratorBase +class LambdaIterator : public IteratorBase { public: - FunctionIterator(F &&f) : func(std::move(f)) {} + LambdaIterator(F &&f) : func(std::move(f)) {} Option next() final { return func(); } @@ -19,7 +19,7 @@ private: F func; }; -// Wraps function which returns options as an iterator. +// Wraps lambda which returns options as an iterator. template auto make_iterator(F &&f) { @@ -27,6 +27,6 @@ auto make_iterator(F &&f) // FunctionIterator compiler can't deduce it thats way there is decltype in // construction of FunctionIterator. Resoulting type of iter.next().take() // is T. - return FunctionIterator(std::move(f)); + return LambdaIterator(std::move(f)); } } diff --git a/src/data_structures/map/rh_common.hpp b/src/data_structures/map/rh_common.hpp index 9fa396e30..244e1ec52 100644 --- a/src/data_structures/map/rh_common.hpp +++ b/src/data_structures/map/rh_common.hpp @@ -6,8 +6,8 @@ #include "utils/option_ptr.hpp" // RobinHood base. -// Entrys are POINTERS alligned to 8B. -// Entrys must know thers key. +// Entries are POINTERS alligned to 8B. +// Entries must know thers key. // D must have method K& get_key() // K must be comparable with ==. template diff --git a/src/storage/vertex_accessor.cpp b/src/storage/vertex_accessor.cpp index ab27a5b3a..6ce4e8264 100644 --- a/src/storage/vertex_accessor.cpp +++ b/src/storage/vertex_accessor.cpp @@ -19,7 +19,7 @@ bool Vertex::Accessor::add_label(const Label &label) { // update vertex if (this->record->data.labels.add(label)) { - label.index->insert(create_ir(std::nullptr_t())); + label.index->insert(create_index_record()); return true; } return false;