From b94cae12d1e3fbaf9254ea52a5ee218ecb3e9126 Mon Sep 17 00:00:00 2001 From: Marko Budiselic <mbudiselicbuda@gmail.com> Date: Tue, 5 Jul 2016 04:01:22 +0100 Subject: [PATCH] some new header + source files (vertices, vertex_accessor, properties), first version of Index works, skiplist<T> + concurrent_set + concurrent_map (still has to be finished) --- CMakeLists.txt | 24 +- include/storage/locking/record_lock.hpp | 20 + include/storage/model/properties/bool.hpp | 27 + include/storage/model/properties/handler.hpp | 41 + include/storage/model/properties/null.hpp | 31 + .../storage/model/properties/properties.hpp | 33 + .../storage/model/properties/property.hpp | 29 +- include/storage/model/properties/string.hpp | 29 + include/storage/vertex_accessor.hpp | 25 + include/storage/vertices.hpp | 26 + include/template_engine/engine.hpp | 18 + {src => include}/transactions/transaction.hpp | 27 +- .../concurrent/concurrent_map.hpp | 9 + .../concurrent/concurrent_set.hpp | 8 + .../{skiplist => concurrent}/skiplist.hpp | 182 ++--- src/data_structures/skiplist/skiplist_gc.hpp | 3 + src/data_structures/skiplist/skiplist_map.hpp | 709 ++++++++++++++++++ src/data_structures/skiplist/skiplistset.hpp | 6 +- src/query_engine/main_queries.cpp | 185 ++--- src/storage/common.hpp | 4 +- src/storage/edges.hpp | 3 +- src/storage/indexes/index.hpp | 55 +- src/storage/indexes/index_record.hpp | 46 ++ .../indexes/index_record_collection.hpp | 38 + src/storage/indexes/{ => keys}/index_key.hpp | 6 - src/storage/label_store.hpp | 4 +- src/storage/locking/record_lock.cpp | 27 + src/storage/locking/record_lock.hpp | 42 -- src/storage/model/label.hpp | 4 + src/storage/model/label_collection.hpp | 34 +- src/storage/model/properties/all.hpp | 27 - src/storage/model/properties/bool.cpp | 46 ++ src/storage/model/properties/bool.hpp | 57 -- src/storage/model/properties/null.cpp | 30 + src/storage/model/properties/null.hpp | 51 -- src/storage/model/properties/number.hpp | 14 +- src/storage/model/properties/properties.cpp | 54 ++ src/storage/model/properties/properties.hpp | 69 -- src/storage/model/properties/property.cpp | 13 + src/storage/model/properties/string.cpp | 36 + src/storage/model/properties/string.hpp | 48 -- .../properties/traversers/consolewriter.hpp | 45 +- .../properties/traversers/jsonwriter.hpp | 71 +- src/storage/model/property_model.hpp | 2 +- src/storage/vertex_accessor.cpp | 38 + src/storage/vertex_accessor.hpp | 37 - src/storage/vertices.cpp | 50 ++ src/storage/vertices.hpp | 54 -- src/template_engine/engine.cpp | 21 + src/template_engine/engine.hpp | 31 - src/transactions/commit_log.hpp | 61 +- src/transactions/engine.hpp | 14 +- src/transactions/transaction.cpp | 20 + src/transactions/transaction_store.hpp | 2 +- src/utils/reference_wrapper.hpp | 40 + tests/CMakeLists.txt | 14 +- .../unit/{skiplist.cpp => concurrent_map.cpp} | 4 +- tests/unit/concurrent_set.cpp | 58 ++ tests/unit/{db_index.cpp => db_index.cpp.old} | 0 tests/unit/template_engine.cpp | 13 + 60 files changed, 1864 insertions(+), 851 deletions(-) create mode 100644 include/storage/locking/record_lock.hpp create mode 100644 include/storage/model/properties/bool.hpp create mode 100644 include/storage/model/properties/handler.hpp create mode 100644 include/storage/model/properties/null.hpp create mode 100644 include/storage/model/properties/properties.hpp rename {src => include}/storage/model/properties/property.hpp (76%) create mode 100644 include/storage/model/properties/string.hpp create mode 100644 include/storage/vertex_accessor.hpp create mode 100644 include/storage/vertices.hpp create mode 100644 include/template_engine/engine.hpp rename {src => include}/transactions/transaction.hpp (52%) create mode 100644 src/data_structures/concurrent/concurrent_map.hpp create mode 100644 src/data_structures/concurrent/concurrent_set.hpp rename src/data_structures/{skiplist => concurrent}/skiplist.hpp (78%) create mode 100644 src/data_structures/skiplist/skiplist_map.hpp create mode 100644 src/storage/indexes/index_record.hpp create mode 100644 src/storage/indexes/index_record_collection.hpp rename src/storage/indexes/{ => keys}/index_key.hpp (79%) create mode 100644 src/storage/locking/record_lock.cpp delete mode 100644 src/storage/locking/record_lock.hpp delete mode 100644 src/storage/model/properties/all.hpp create mode 100644 src/storage/model/properties/bool.cpp delete mode 100644 src/storage/model/properties/bool.hpp create mode 100644 src/storage/model/properties/null.cpp delete mode 100644 src/storage/model/properties/null.hpp create mode 100644 src/storage/model/properties/properties.cpp delete mode 100644 src/storage/model/properties/properties.hpp create mode 100644 src/storage/model/properties/property.cpp create mode 100644 src/storage/model/properties/string.cpp delete mode 100644 src/storage/model/properties/string.hpp create mode 100644 src/storage/vertex_accessor.cpp delete mode 100644 src/storage/vertex_accessor.hpp create mode 100644 src/storage/vertices.cpp delete mode 100644 src/storage/vertices.hpp create mode 100644 src/template_engine/engine.cpp delete mode 100644 src/template_engine/engine.hpp create mode 100644 src/transactions/transaction.cpp create mode 100644 src/utils/reference_wrapper.hpp rename tests/unit/{skiplist.cpp => concurrent_map.cpp} (94%) create mode 100644 tests/unit/concurrent_set.cpp rename tests/unit/{db_index.cpp => db_index.cpp.old} (100%) create mode 100644 tests/unit/template_engine.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 506dfe91b..1f32bb0e6 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -142,6 +142,7 @@ if(NDEBUG) endif() # includes +include_directories(${CMAKE_SOURCE_DIR}/include) include_directories(${src_dir}) include_directories(${build_include_dir}) include_directories(${fmt_source_dir}) @@ -178,12 +179,25 @@ EXECUTE_PROCESS( # target_link_libraries(query_engine cypher_lib) # target_link_libraries(query_engine ${fmt_static_lib}) -# query hasher executable -add_executable(query_hasher src/query_engine/main_query_hasher.cpp) -target_link_libraries(query_hasher ${fmt_static_lib}) +# # query hasher executable +# add_executable(query_hasher src/query_engine/main_query_hasher.cpp) +# target_link_libraries(query_hasher ${fmt_static_lib}) -# # hard coded implementation of queries -add_executable(queries src/query_engine/main_queries.cpp) +# hard coded implementation of queries +add_executable( + queries + src/query_engine/main_queries.cpp + src/storage/vertices.cpp + src/storage/model/properties/property.cpp + src/storage/model/properties/null.cpp + src/storage/model/properties/bool.cpp + src/storage/model/properties/string.cpp + src/storage/model/properties/properties.cpp + src/storage/locking/record_lock.cpp + src/storage/vertices.cpp + src/storage/vertex_accessor.cpp + src/transactions/transaction.cpp +) target_link_libraries(queries ${fmt_static_lib}) # tests diff --git a/include/storage/locking/record_lock.hpp b/include/storage/locking/record_lock.hpp new file mode 100644 index 000000000..840697244 --- /dev/null +++ b/include/storage/locking/record_lock.hpp @@ -0,0 +1,20 @@ +#pragma once + +#include "threading/sync/futex.hpp" +#include "storage/locking/lock_status.hpp" +#include "mvcc/id.hpp" + +class RecordLock +{ + static constexpr struct timespec timeout {20, 0}; + static constexpr Id INVALID = Id(); + +public: + LockStatus lock(const Id& id); + void lock(); + void unlock(); + +private: + Futex mutex; + Id owner; +}; diff --git a/include/storage/model/properties/bool.hpp b/include/storage/model/properties/bool.hpp new file mode 100644 index 000000000..c551ee3e6 --- /dev/null +++ b/include/storage/model/properties/bool.hpp @@ -0,0 +1,27 @@ +#pragma once + +#include "storage/model/properties/property.hpp" + +class Bool : public Property +{ +public: + static constexpr Flags type = Flags::Bool; + + Bool(bool value); + Bool(const Bool& other) = default; + + bool value() const; + + explicit operator bool() const; + + bool operator==(const Property& other) const override; + + bool operator==(const Bool& other) const; + + bool operator==(bool v) const; + + std::ostream& print(std::ostream& stream) const override; + + friend std::ostream& operator<<(std::ostream& stream, const Bool& prop); +}; + diff --git a/include/storage/model/properties/handler.hpp b/include/storage/model/properties/handler.hpp new file mode 100644 index 000000000..9940f1216 --- /dev/null +++ b/include/storage/model/properties/handler.hpp @@ -0,0 +1,41 @@ +#pragma once + +#include "storage/model/properties/property.hpp" + +#include "storage/model/properties/bool.hpp" +#include "storage/model/properties/double.hpp" +#include "storage/model/properties/float.hpp" +#include "storage/model/properties/int32.hpp" +#include "storage/model/properties/int64.hpp" +#include "storage/model/properties/string.hpp" + +template <class Handler> +void accept(Property &property, Handler &h) +{ + switch (property.flags) { + + case Property::Flags::True: + return h.handle(static_cast<Bool &>(property)); + + case Property::Flags::False: + return h.handle(static_cast<Bool &>(property)); + + case Property::Flags::String: + return h.handle(static_cast<String &>(property)); + + case Property::Flags::Int32: + return h.handle(static_cast<Int32 &>(property)); + + case Property::Flags::Int64: + return h.handle(static_cast<Int64 &>(property)); + + case Property::Flags::Float: + return h.handle(static_cast<Float &>(property)); + + case Property::Flags::Double: + return h.handle(static_cast<Double &>(property)); + + default: + return; + } +} diff --git a/include/storage/model/properties/null.hpp b/include/storage/model/properties/null.hpp new file mode 100644 index 000000000..9a450f5ae --- /dev/null +++ b/include/storage/model/properties/null.hpp @@ -0,0 +1,31 @@ +#pragma once + +#include "storage/model/properties/property.hpp" + +class Null : public Property +{ +public: + friend class Property; + + static constexpr Flags type = Flags::Null; + + Null(const Null&) = delete; + Null(Null&&) = delete; + + Null operator=(const Null&) = delete; + + bool operator==(const Property& other) const override; + + bool operator==(const Null&) const; + + explicit operator bool(); + + friend std::ostream& operator<<(std::ostream& stream, const Null&); + + std::ostream& print(std::ostream& stream) const override; + +private: + // the constructor for null is private, it can be constructed only as a + // value inside the Property class, Property::Null + Null(); +}; diff --git a/include/storage/model/properties/properties.hpp b/include/storage/model/properties/properties.hpp new file mode 100644 index 000000000..029265bd3 --- /dev/null +++ b/include/storage/model/properties/properties.hpp @@ -0,0 +1,33 @@ +#pragma once + +#include <map> + +#include "storage/model/properties/property.hpp" + +class Properties +{ +public: + using sptr = std::shared_ptr<Properties>; + + const Property& at(const std::string& key) const; + + template <class T, class... Args> + void set(const std::string& key, Args&&... args); + + void set(const std::string& key, Property::sptr value); + + void clear(const std::string& key); + + template <class Handler> + void accept(Handler& handler) const + { + for(auto& kv : props) + handler.handle(kv.first, *kv.second); + + handler.finish(); + } + +private: + using props_t = std::map<std::string, Property::sptr>; + props_t props; +}; diff --git a/src/storage/model/properties/property.hpp b/include/storage/model/properties/property.hpp similarity index 76% rename from src/storage/model/properties/property.hpp rename to include/storage/model/properties/property.hpp index bb2a76ae8..28b8423ee 100644 --- a/src/storage/model/properties/property.hpp +++ b/include/storage/model/properties/property.hpp @@ -61,14 +61,11 @@ public: static const Null Null; - Property(Flags flags) : flags(flags) {} + Property(Flags flags); virtual bool operator==(const Property& other) const = 0; - bool operator!=(const Property& other) const - { - return !operator==(other); - } + bool operator!=(const Property& other) const; template <class T> bool is() const @@ -92,13 +89,23 @@ public: virtual std::ostream& print(std::ostream& stream) const = 0; - friend std::ostream& operator<<(std::ostream& stream, const Property& prop) - { - return prop.print(stream); - } + friend std::ostream& operator<<(std::ostream& stream, const Property& prop); - template <class Handler> - void accept(Handler& handler); +// template <class Handler> +// void accept(Handler& h) +// { +// switch(flags) +// { +// case Flags::True: return h.handle(static_cast<Bool&>(*this)); +// case Flags::False: return h.handle(static_cast<Bool&>(*this)); +// case Flags::String: return h.handle(static_cast<String&>(*this)); +// case Flags::Int32: return h.handle(static_cast<Int32&>(*this)); +// case Flags::Int64: return h.handle(static_cast<Int64&>(*this)); +// case Flags::Float: return h.handle(static_cast<Float&>(*this)); +// case Flags::Double: return h.handle(static_cast<Double&>(*this)); +// default: return; +// } +// } const Flags flags; }; diff --git a/include/storage/model/properties/string.hpp b/include/storage/model/properties/string.hpp new file mode 100644 index 000000000..6230c9d98 --- /dev/null +++ b/include/storage/model/properties/string.hpp @@ -0,0 +1,29 @@ +#pragma once + +#include "storage/model/properties/property.hpp" + +class String : public Property +{ +public: + static constexpr Flags type = Flags::String; + + String(const String&) = default; + String(String&&) = default; + + String(const std::string& value); + String(std::string&& value); + + operator const std::string&() const; + + bool operator==(const Property& other) const override; + + bool operator==(const String& other) const; + + bool operator==(const std::string& other) const; + + friend std::ostream& operator<<(std::ostream& stream, const String& prop); + + std::ostream& print(std::ostream& stream) const override; + + std::string value; +}; diff --git a/include/storage/vertex_accessor.hpp b/include/storage/vertex_accessor.hpp new file mode 100644 index 000000000..6c02f2595 --- /dev/null +++ b/include/storage/vertex_accessor.hpp @@ -0,0 +1,25 @@ +#pragma once + +#include "storage/record_accessor.hpp" +#include "storage/vertex.hpp" + +class Vertices; + +class Vertex::Accessor + : public RecordAccessor<Vertex, Vertices, Vertex::Accessor> +{ +public: + using RecordAccessor::RecordAccessor; + + size_t out_degree() const; + + size_t in_degree() const; + + size_t degree() const; + + void add_label(const Label &label); + + bool has_label(const Label &label) const; + + const std::set<label_ref_t>& labels() const; +}; diff --git a/include/storage/vertices.hpp b/include/storage/vertices.hpp new file mode 100644 index 000000000..03af92d1f --- /dev/null +++ b/include/storage/vertices.hpp @@ -0,0 +1,26 @@ +#pragma once + +#include "data_structures/concurrent/concurrent_map.hpp" +#include "storage/common.hpp" +#include "storage/indexes/index.hpp" +#include "storage/indexes/index_record_collection.hpp" +#include "storage/vertex_accessor.hpp" + +class Vertices +{ +public: + const Vertex::Accessor find(tx::Transaction &t, const Id &id); + + Vertex::Accessor insert(tx::Transaction &t); + + void update_label_index(const Label &label, + VertexIndexRecord &&index_record); + + VertexIndexRecordCollection& find_label_index(const Label& label); + +private: + Index<label_ref_t, VertexIndexRecordCollection> label_index; + + ConcurrentMap<uint64_t, VertexRecord> vertices; + AtomicCounter<uint64_t> counter; +}; diff --git a/include/template_engine/engine.hpp b/include/template_engine/engine.hpp new file mode 100644 index 000000000..1df56f802 --- /dev/null +++ b/include/template_engine/engine.hpp @@ -0,0 +1,18 @@ +#pragma once + +#include <string> +#include <unordered_map> + +namespace template_engine +{ + +using std::string; +using data = std::unordered_map<string, string>; + +class TemplateEngine +{ +public: + string render(const string& form, const data& partials); +}; + +} diff --git a/src/transactions/transaction.hpp b/include/transactions/transaction.hpp similarity index 52% rename from src/transactions/transaction.hpp rename to include/transactions/transaction.hpp index dde533062..f287282df 100644 --- a/src/transactions/transaction.hpp +++ b/include/transactions/transaction.hpp @@ -1,13 +1,13 @@ #pragma once -#include <cstdlib> #include <cstdint> +#include <cstdlib> #include <vector> #include "mvcc/id.hpp" -#include "snapshot.hpp" -#include "lock_store.hpp" #include "storage/locking/record_lock.hpp" +#include "transactions/lock_store.hpp" +#include "transactions/snapshot.hpp" namespace tx { @@ -19,35 +19,24 @@ class Transaction friend class Engine; public: - Transaction(const Id& id, const Snapshot<Id>& snapshot, Engine& engine) - : id(id), cid(1), snapshot(snapshot), engine(engine) {} - - Transaction(const Transaction&) = delete; - Transaction(Transaction&&) = delete; + Transaction(const Id &id, const Snapshot<Id> &snapshot, Engine &engine); + Transaction(const Transaction &) = delete; + Transaction(Transaction &&) = delete; // index of this transaction const Id id; - // index of the current command in the current transaction; uint8_t cid; - // a snapshot of currently active transactions const Snapshot<Id> snapshot; - void take_lock(RecordLock& lock) - { - locks.take(&lock, id); - } - - // convenience methods which call the corresponging methods in the - // transaction engine + void take_lock(RecordLock &lock); void commit(); void abort(); - Engine& engine; + Engine &engine; private: LockStore<RecordLock> locks; }; - } diff --git a/src/data_structures/concurrent/concurrent_map.hpp b/src/data_structures/concurrent/concurrent_map.hpp new file mode 100644 index 000000000..a9e04a495 --- /dev/null +++ b/src/data_structures/concurrent/concurrent_map.hpp @@ -0,0 +1,9 @@ +#pragma once + +#include "data_structures/skiplist/skiplist_map.hpp" + +template<class K, class T> +class ConcurrentMap : public SkipListMap<K, T> +{ + // TODO implement with SkipList +}; diff --git a/src/data_structures/concurrent/concurrent_set.hpp b/src/data_structures/concurrent/concurrent_set.hpp new file mode 100644 index 000000000..35993f328 --- /dev/null +++ b/src/data_structures/concurrent/concurrent_set.hpp @@ -0,0 +1,8 @@ +#pragma once + +#include "data_structures/concurrent/skiplist.hpp" + +template<class T> +class ConcurrentSet : public SkipList<T> +{ +}; diff --git a/src/data_structures/skiplist/skiplist.hpp b/src/data_structures/concurrent/skiplist.hpp similarity index 78% rename from src/data_structures/skiplist/skiplist.hpp rename to src/data_structures/concurrent/skiplist.hpp index 26b0341c3..abad9828d 100644 --- a/src/data_structures/skiplist/skiplist.hpp +++ b/src/data_structures/concurrent/skiplist.hpp @@ -4,12 +4,13 @@ #include <memory> #include <cassert> +#include "utils/random/fast_binomial.hpp" +#include "utils/placeholder.hpp" + #include "threading/sync/lockable.hpp" #include "threading/sync/spinlock.hpp" -#include "utils/random/fast_binomial.hpp" -#include "utils/placeholder.hpp" -#include "skiplist_gc.hpp" +#include "data_structures/skiplist/skiplist_gc.hpp" /* @brief Concurrent lock-based skiplist with fine grained locking * @@ -45,40 +46,40 @@ * * The implementation has an interface which closely resembles the functions * with arguments and returned types frequently used by the STL. - * + * * Example usage: - * Skiplist<K, T> skiplist; + * Skiplist<T> skiplist; * * { * auto accessor = skiplist.access(); * - * // inserts <key1, value1> into the skiplist and returns + * // inserts item into the skiplist and returns * // <iterator, bool> pair. iterator points to the newly created * // node and the boolean member evaluates to true denoting that the * // insertion was successful - * accessor.insert_unique(key1, value1); + * accessor.insert(item1); * - * // nothing gets inserted because key1 already exist in the skiplist + * // nothing gets inserted because item1 already exist in the skiplist * // returned iterator points to the existing element and the return * // boolean evaluates to false denoting the failed insertion - * accessor.insert_unique(key1, value1); + * accessor.insert(item1); * - * // returns an iterator to the element pair <key1, value1> - * auto it = accessor.find(key1); + * // returns an iterator to the element item1 + * auto it = accessor.find(item1); * * // returns an empty iterator. it == accessor.end() - * auto it = accessor.find(key2); + * auto it = accessor.find(item2); * - * // iterate over all key value pairs + * // iterate over all items * for(auto it = accessor.begin(); it != accessor.end(); ++it) - * cout << it->first << " " << it->second; + * cout << *it << endl; * * // range based for loops also work * for(auto& e : accessor) - * cout << e.first << " " << e.second; + * cout << e << endl; * - * accessor.remove(key1); // returns true - * accessor.remove(key1); // returns false because key1 doesn't exist + * accessor.remove(item1); // returns true + * accessor.remove(item1); // returns false because key1 doesn't exist * } * * // accessor out of scope, garbage collection might occur @@ -86,14 +87,13 @@ * For detailed operations available, please refer to the Accessor class * inside the public section of the SkipList class. * - * @tparam K Type to use as the key - * @tparam T Type to use as the value + * @tparam T Type to use as the item * @tparam H Maximum node height. Determines the effective number of nodes * the skiplist can hold in order to preserve it's log2 properties * @tparam lock_t Lock type used when locking is needed during the creation * and deletion of nodes. */ -template <class K, class T, size_t H=32, class lock_t=SpinLock> +template <class T, size_t H=32, class lock_t=SpinLock> class SkipList : private Lockable<lock_t> { public: @@ -101,8 +101,6 @@ public: // with p(k) = (1/2)^k for all k from the interval static thread_local FastBinomial<H> rnd; - using value_type = std::pair<const K, T>; - /* @brief Wrapper class for flags used in the implementation * * MARKED flag is used to logically delete a node. @@ -119,22 +117,22 @@ public: bool is_marked() const { - return flags.load(std::memory_order_acquire) & MARKED; + return flags.load() & MARKED; } void set_marked() { - flags.fetch_or(MARKED, std::memory_order_release); + flags.fetch_or(MARKED); } bool is_fully_linked() const { - return flags.load(std::memory_order_acquire) & FULLY_LINKED; + return flags.load() & FULLY_LINKED; } void set_fully_linked() { - flags.fetch_or(FULLY_LINKED, std::memory_order_release); + flags.fetch_or(FULLY_LINKED); } private: @@ -149,27 +147,12 @@ public: const uint8_t height; Flags flags; - const K& key() const - { - return kv_pair().first; - } - T& value() - { - return kv_pair().second; - } - - const T& value() const - { - return kv_pair().second; - } - - value_type& kv_pair() { return data.get(); } - const value_type& kv_pair() const + const T& value() const { return data.get(); } @@ -181,23 +164,18 @@ public: return new (allocate(height)) Node(height); } - static Node* create(const K& key, const T& item, uint8_t height) + static Node* create(const T& item, uint8_t height) { - return create({key, item}, height); + return create(item, height); } - static Node* create(const K& key, T&& item, uint8_t height) - { - return create({key, std::forward<T>(item)}, height); - } - - static Node* create(value_type&& data, uint8_t height) + static Node* create(T&& item, uint8_t height) { auto node = allocate(height); // we have raw memory and we need to construct an object // of type Node on it - return new (node) Node(std::forward<value_type>(data), height); + return new (node) Node(std::forward<T>(item), height); } static void destroy(Node* node) @@ -208,12 +186,12 @@ public: Node* forward(size_t level) const { - return tower[level].load(std::memory_order_acquire); + return tower[level].load(); } void forward(size_t level, Node* next) { - tower[level].store(next, std::memory_order_release); + tower[level].store(next); } private: @@ -226,9 +204,9 @@ public: new (&tower[i]) std::atomic<Node*> {nullptr}; } - Node(value_type&& data, uint8_t height) : Node(height) + Node(T&& data, uint8_t height) : Node(height) { - this->data.set(std::forward<value_type>(data)); + this->data.set(std::forward<T>(data)); } ~Node() @@ -252,8 +230,7 @@ public: return node; } - Placeholder<value_type> data; - + Placeholder<T> data; // this creates an array of the size zero. we can't put any sensible // value here since we don't know what size it will be untill the @@ -276,22 +253,22 @@ public: IteratorBase() = default; IteratorBase(const IteratorBase&) = default; - value_type& operator*() + T& operator*() { assert(node != nullptr); - return node->kv_pair(); + return node->value(); } - value_type* operator->() + T* operator->() { assert(node != nullptr); - return &node->kv_pair(); + return &node->value(); } - operator value_type&() + operator T&() { assert(node != nullptr); - return node->kv_pair(); + return node->value(); } It& operator++() @@ -326,19 +303,19 @@ public: ConstIterator() = default; ConstIterator(const ConstIterator&) = default; - const value_type& operator*() + const T& operator*() { return IteratorBase<ConstIterator>::operator*(); } - const value_type* operator->() + const T* operator->() { return IteratorBase<ConstIterator>::operator->(); } - operator const value_type&() + operator const T&() { - return IteratorBase<ConstIterator>::operator value_type&(); + return IteratorBase<ConstIterator>::operator T&(); } }; @@ -413,29 +390,34 @@ public: return skiplist->cend(); } - std::pair<Iterator, bool> insert_unique(const K& key, const T& item) + std::pair<Iterator, bool> insert(const T& item) { - return skiplist->insert({key, item}, preds, succs); + return skiplist->insert(item, preds, succs); } - std::pair<Iterator, bool> insert_unique(const K& key, T&& item) + std::pair<Iterator, bool> insert(T&& item) { - return skiplist->insert({key, std::forward<T>(item)}, preds, succs); + return skiplist->insert(std::forward<T>(item), preds, succs); } - ConstIterator find(const K& key) const + ConstIterator find(const T& item) const { - return static_cast<const SkipList&>(*skiplist).find(key); + return static_cast<const SkipList&>(*skiplist).find(item); } - Iterator find(const K& key) + Iterator find(const T& item) { - return skiplist->find(key); + return skiplist->find(item); } - bool remove(const K& key) + bool contains(const T& item) const { - return skiplist->remove(key, preds, succs); + return this->find(item) != this->end(); + } + + bool remove(const T& item) + { + return skiplist->remove(item, preds, succs); } size_t size() const @@ -496,28 +478,28 @@ private: return count.load(); } - bool greater(const K& key, const Node* const node) + bool greater(const T& item, const Node* const node) { - return node && key > node->key(); + return node && item > node->value(); } - bool less(const K& key, const Node* const node) + bool less(const T& item, const Node* const node) { - return (node == nullptr) || key < node->key(); + return (node == nullptr) || item < node->value(); } - ConstIterator find(const K& key) const + ConstIterator find(const T& item) const { - return const_cast<SkipList*>(this)->find_node<ConstIterator>(key); + return const_cast<SkipList*>(this)->find_node<ConstIterator>(item); } - Iterator find(const K& key) + Iterator find(const T& item) { - return find_node<Iterator>(key); + return find_node<Iterator>(item); } template <class It> - It find_node(const K& key) + It find_node(const T& item) { Node* node, *pred = header; int h = static_cast<int>(pred->height) - 1; @@ -525,7 +507,7 @@ private: while(true) { // try to descend down first the next key on this layer overshoots - for(; h >= 0 && less(key, node = pred->forward(h)); --h) {} + for(; h >= 0 && less(item, node = pred->forward(h)); --h) {} // if we overshoot at every layer, item doesn't exist if(h < 0) @@ -533,16 +515,16 @@ private: // the item is farther to the right, continue going right as long // as the key is greater than the current node's key - while(greater(key, node)) + while(greater(item, node)) pred = node, node = node->forward(h); // check if we have a hit. if not, we need to descend down again - if(!less(key, node) && !node->flags.is_marked()) + if(!less(item, node) && !node->flags.is_marked()) return It(node); } } - int find_path(Node* from, int start, const K& key, + int find_path(Node* from, int start, const T& item, Node* preds[], Node* succs[]) { int level_found = -1; @@ -552,10 +534,10 @@ private: { Node* node = pred->forward(level); - while(greater(key, node)) + while(greater(item, node)) pred = node, node = pred->forward(level); - if(level_found == -1 && !less(key, node)) + if(level_found == -1 && !less(item, node)) level_found = level; preds[level] = pred; @@ -589,11 +571,12 @@ private: } std::pair<Iterator, bool> - insert(value_type&& data, Node* preds[], Node* succs[]) + insert(T&& data, Node* preds[], Node* succs[]) { while(true) { - auto level = find_path(header, H - 1, data.first, preds, succs); + // TODO: before here was data.first + auto level = find_path(header, H - 1, data, preds, succs); if(level != -1) { @@ -618,7 +601,7 @@ private: continue; // you have the locks, create a new node - auto new_node = Node::create(std::forward<value_type>(data), height); + auto new_node = Node::create(std::forward<T>(data), height); // link the predecessors and successors, e.g. // @@ -646,7 +629,7 @@ private: && !node->flags.is_marked(); } - bool remove(const K& key, Node* preds[], Node* succs[]) + bool remove(const T& item, Node* preds[], Node* succs[]) { Node* node = nullptr; guard_t node_guard; @@ -655,7 +638,7 @@ private: while(true) { - auto level = find_path(header, H - 1, key, preds, succs); + auto level = find_path(header, H - 1, item, preds, succs); if(!marked && (level == -1 || !ok_delete(succs[level], level))) return false; @@ -695,6 +678,5 @@ private: SkiplistGC<Node> gc; }; -template <class K, class T, size_t H, class lock_t> -thread_local FastBinomial<H> SkipList<K, T, H, lock_t>::rnd; - +template <class T, size_t H, class lock_t> +thread_local FastBinomial<H> SkipList<T, H, lock_t>::rnd; diff --git a/src/data_structures/skiplist/skiplist_gc.hpp b/src/data_structures/skiplist/skiplist_gc.hpp index 86e597325..c2ac53fd0 100644 --- a/src/data_structures/skiplist/skiplist_gc.hpp +++ b/src/data_structures/skiplist/skiplist_gc.hpp @@ -1,5 +1,8 @@ #pragma once +// TODO: remove from here and from the project +#include <iostream> + #include "memory/freelist.hpp" #include "memory/lazy_gc.hpp" #include "threading/sync/spinlock.hpp" diff --git a/src/data_structures/skiplist/skiplist_map.hpp b/src/data_structures/skiplist/skiplist_map.hpp new file mode 100644 index 000000000..c67a8f652 --- /dev/null +++ b/src/data_structures/skiplist/skiplist_map.hpp @@ -0,0 +1,709 @@ +#pragma once + +#include <algorithm> +#include <memory> +#include <cassert> + +#include "threading/sync/lockable.hpp" +#include "threading/sync/spinlock.hpp" + +#include "utils/random/fast_binomial.hpp" +#include "utils/placeholder.hpp" +#include "skiplist_gc.hpp" + +/* @brief Concurrent lock-based skiplist with fine grained locking + * + * From Wikipedia: + * "A skip list is a data structure that allows fast search within an + * ordered sequence of elements. Fast search is made possible by + * maintaining a linked hierarchy of subsequences, each skipping over + * fewer elements. Searching starts in the sparsest subsequence until + * two consecutive elements have been found, one smaller and one + * larger than or equal to the element searched for." + * + * [_]---------------->[+]----------->[_] + * [_]->[+]----------->[+]------>[+]->[_] + * [_]->[+]------>[+]->[+]------>[+]->[_] + * [_]->[+]->[+]->[+]->[+]->[+]->[+]->[_] + * head 1 2 4 5 8 9 nil + * + * The logarithmic properties are maintained by randomizing the height for + * every new node using the binomial distribution + * p(k) = (1/2)^k for k in [1...H]. + * + * The implementation is based on the work described in the paper + * "A Provably Correct Scalable Concurrent Skip List" + * URL: https://www.cs.tau.ac.il/~shanir/nir-pubs-web/Papers/OPODIS2006-BA.pdf + * + * The proposed implementation is in Java so the authors don't worry about + * garbage collection, but obviously we have to. This implementation uses + * lazy garbage collection. When all clients stop using the skiplist, we can + * be sure that all logically removed nodes are not visible to anyone so + * we can safely remove them. The idea of counting active clients implies + * the use of a intermediary structure (called Accessor) when accessing the + * skiplist. + * + * The implementation has an interface which closely resembles the functions + * with arguments and returned types frequently used by the STL. + * + * Example usage: + * Skiplist<K, T> skiplist; + * + * { + * auto accessor = skiplist.access(); + * + * // inserts <key1, value1> into the skiplist and returns + * // <iterator, bool> pair. iterator points to the newly created + * // node and the boolean member evaluates to true denoting that the + * // insertion was successful + * accessor.insert_unique(key1, value1); + * + * // nothing gets inserted because key1 already exist in the skiplist + * // returned iterator points to the existing element and the return + * // boolean evaluates to false denoting the failed insertion + * accessor.insert_unique(key1, value1); + * + * // returns an iterator to the element pair <key1, value1> + * auto it = accessor.find(key1); + * + * // returns an empty iterator. it == accessor.end() + * auto it = accessor.find(key2); + * + * // iterate over all key value pairs + * for(auto it = accessor.begin(); it != accessor.end(); ++it) + * cout << it->first << " " << it->second; + * + * // range based for loops also work + * for(auto& e : accessor) + * cout << e.first << " " << e.second; + * + * accessor.remove(key1); // returns true + * accessor.remove(key1); // returns false because key1 doesn't exist + * } + * + * // accessor out of scope, garbage collection might occur + * + * For detailed operations available, please refer to the Accessor class + * inside the public section of the SkipList class. + * + * @tparam K Type to use as the key + * @tparam T Type to use as the value + * @tparam H Maximum node height. Determines the effective number of nodes + * the skiplist can hold in order to preserve it's log2 properties + * @tparam lock_t Lock type used when locking is needed during the creation + * and deletion of nodes. + */ +template <class K, class T, size_t H=32, class lock_t=SpinLock> +class SkipListMap : private Lockable<lock_t> +{ +public: + // computes the height for the new node from the interval [1...H] + // with p(k) = (1/2)^k for all k from the interval + static thread_local FastBinomial<H> rnd; + + using value_type = std::pair<const K, T>; + + /* @brief Wrapper class for flags used in the implementation + * + * MARKED flag is used to logically delete a node. + * FULLY_LINKED is used to mark the node as fully inserted, i.e. linked + * at all layers in the skiplist up to the node height + */ + struct Flags + { + enum node_flags : uint8_t + { + MARKED = 0x01, + FULLY_LINKED = 0x10, + }; + + bool is_marked() const + { + return flags.load(std::memory_order_acquire) & MARKED; + } + + void set_marked() + { + flags.fetch_or(MARKED, std::memory_order_release); + } + + bool is_fully_linked() const + { + return flags.load(std::memory_order_acquire) & FULLY_LINKED; + } + + void set_fully_linked() + { + flags.fetch_or(FULLY_LINKED, std::memory_order_release); + } + + private: + std::atomic<uint8_t> flags {0}; + }; + + class Node : Lockable<lock_t> + { + public: + friend class SkipListMap; + + const uint8_t height; + Flags flags; + + const K& key() const + { + return kv_pair().first; + } + + T& value() + { + return kv_pair().second; + } + + const T& value() const + { + return kv_pair().second; + } + + value_type& kv_pair() + { + return data.get(); + } + + const value_type& kv_pair() const + { + return data.get(); + } + + static Node* sentinel(uint8_t height) + { + // we have raw memory and we need to construct an object + // of type Node on it + return new (allocate(height)) Node(height); + } + + static Node* create(const K& key, const T& item, uint8_t height) + { + return create({key, item}, height); + } + + static Node* create(const K& key, T&& item, uint8_t height) + { + return create({key, std::forward<T>(item)}, height); + } + + static Node* create(value_type&& data, uint8_t height) + { + auto node = allocate(height); + + // we have raw memory and we need to construct an object + // of type Node on it + return new (node) Node(std::forward<value_type>(data), height); + } + + static void destroy(Node* node) + { + node->~Node(); + free(node); + } + + Node* forward(size_t level) const + { + return tower[level].load(std::memory_order_acquire); + } + + void forward(size_t level, Node* next) + { + tower[level].store(next, std::memory_order_release); + } + + private: + Node(uint8_t height) : height(height) + { + // here we assume, that the memory for N towers (N = height) has + // been allocated right after the Node structure so we need to + // initialize that memory + for(auto i = 0; i < height; ++i) + new (&tower[i]) std::atomic<Node*> {nullptr}; + } + + Node(value_type&& data, uint8_t height) : Node(height) + { + this->data.set(std::forward<value_type>(data)); + } + + ~Node() + { + for(auto i = 0; i < height; ++i) + tower[i].~atomic(); + } + + static Node* allocate(uint8_t height) + { + // [ Node ][Node*][Node*][Node*]...[Node*] + // | | | | | + // | 0 1 2 height-1 + // |----------------||-----------------------------| + // space for Node space for tower pointers + // structure right after the Node + // structure + auto size = sizeof(Node) + height * sizeof(std::atomic<Node*>); + auto node = static_cast<Node*>(std::malloc(size)); + + return node; + } + + Placeholder<value_type> data; + + // this creates an array of the size zero. we can't put any sensible + // value here since we don't know what size it will be untill the + // node is allocated. we could make it a Node** but then we would + // have two memory allocations, one for node and one for the forward + // list. this way we avoid expensive malloc/free calls and also cache + // thrashing when following a pointer on the heap + std::atomic<Node*> tower[0]; + }; + +public: + template <class It> + class IteratorBase : public Crtp<It> + { + protected: + IteratorBase(Node* node) : node(node) {} + + Node* node {nullptr}; + public: + IteratorBase() = default; + IteratorBase(const IteratorBase&) = default; + + value_type& operator*() + { + assert(node != nullptr); + return node->kv_pair(); + } + + value_type* operator->() + { + assert(node != nullptr); + return &node->kv_pair(); + } + + operator value_type&() + { + assert(node != nullptr); + return node->kv_pair(); + } + + It& operator++() + { + assert(node != nullptr); + node = node->forward(0); + return this->derived(); + } + + It& operator++(int) + { + return operator++(); + } + + friend bool operator==(const It& a, const It& b) + { + return a.node == b.node; + } + + friend bool operator!=(const It& a, const It& b) + { + return !(a == b); + } + }; + + class ConstIterator : public IteratorBase<ConstIterator> + { + friend class SkipListMap; + ConstIterator(Node* node) : IteratorBase<ConstIterator>(node) {} + + public: + ConstIterator() = default; + ConstIterator(const ConstIterator&) = default; + + const value_type& operator*() + { + return IteratorBase<ConstIterator>::operator*(); + } + + const value_type* operator->() + { + return IteratorBase<ConstIterator>::operator->(); + } + + operator const value_type&() + { + return IteratorBase<ConstIterator>::operator value_type&(); + } + }; + + class Iterator : public IteratorBase<Iterator> + { + friend class SkipListMap; + Iterator(Node* node) : IteratorBase<Iterator>(node) {} + + public: + Iterator() = default; + Iterator(const Iterator&) = default; + }; + + SkipListMap() : header(Node::sentinel(H)) {} + + friend class Accessor; + + class Accessor + { + friend class SkipListMap; + + Accessor(SkipListMap* skiplist) : skiplist(skiplist) + { + assert(skiplist != nullptr); + + skiplist->gc.add_ref(); + } + + public: + Accessor(const Accessor&) = delete; + + Accessor(Accessor&& other) : skiplist(other.skiplist) + { + other.skiplist = nullptr; + } + + ~Accessor() + { + if(skiplist == nullptr) + return; + + skiplist->gc.release_ref(); + } + + Iterator begin() + { + return skiplist->begin(); + } + + ConstIterator begin() const + { + return skiplist->cbegin(); + } + + ConstIterator cbegin() const + { + return skiplist->cbegin(); + } + + Iterator end() + { + return skiplist->end(); + } + + ConstIterator end() const + { + return skiplist->cend(); + } + + ConstIterator cend() const + { + return skiplist->cend(); + } + + std::pair<Iterator, bool> insert_unique(const K& key, const T& item) + { + return skiplist->insert({key, item}, preds, succs); + } + + std::pair<Iterator, bool> insert_unique(const K& key, T&& item) + { + return skiplist->insert({key, std::forward<T>(item)}, preds, succs); + } + + ConstIterator find(const K& key) const + { + return static_cast<const SkipListMap&>(*skiplist).find(key); + } + + Iterator find(const K& key) + { + return skiplist->find(key); + } + + bool contains(const K& key) const + { + return skiplist->contains(key); + } + + bool remove(const K& key) + { + return skiplist->remove(key, preds, succs); + } + + size_t size() const + { + return skiplist->size(); + } + + private: + SkipListMap* skiplist; + Node* preds[H], *succs[H]; + }; + + Accessor access() + { + return Accessor(this); + } + + const Accessor access() const + { + return Accessor(this); + } + +private: + using guard_t = std::unique_lock<lock_t>; + + Iterator begin() + { + return Iterator(header->forward(0)); + } + + ConstIterator begin() const + { + return ConstIterator(header->forward(0)); + } + + ConstIterator cbegin() const + { + return ConstIterator(header->forward(0)); + } + + Iterator end() + { + return Iterator(); + } + + ConstIterator end() const + { + return ConstIterator(); + } + + ConstIterator cend() const + { + return ConstIterator(); + } + + size_t size() const + { + return count.load(); + } + + bool greater(const K& key, const Node* const node) + { + return node && key > node->key(); + } + + bool less(const K& key, const Node* const node) + { + return (node == nullptr) || key < node->key(); + } + + ConstIterator find(const K& key) const + { + return const_cast<SkipListMap*>(this)->find_node<ConstIterator>(key); + } + + Iterator find(const K& key) + { + return find_node<Iterator>(key); + } + + bool contains(const K& key) + { + return this->find(key) != this->end(); + } + + template <class It> + It find_node(const K& key) + { + Node* node, *pred = header; + int h = static_cast<int>(pred->height) - 1; + + while(true) + { + // try to descend down first the next key on this layer overshoots + for(; h >= 0 && less(key, node = pred->forward(h)); --h) {} + + // if we overshoot at every layer, item doesn't exist + if(h < 0) + return It(); + + // the item is farther to the right, continue going right as long + // as the key is greater than the current node's key + while(greater(key, node)) + pred = node, node = node->forward(h); + + // check if we have a hit. if not, we need to descend down again + if(!less(key, node) && !node->flags.is_marked()) + return It(node); + } + } + + int find_path(Node* from, int start, const K& key, + Node* preds[], Node* succs[]) + { + int level_found = -1; + Node* pred = from; + + for(int level = start; level >= 0; --level) + { + Node* node = pred->forward(level); + + while(greater(key, node)) + pred = node, node = pred->forward(level); + + if(level_found == -1 && !less(key, node)) + level_found = level; + + preds[level] = pred; + succs[level] = node; + } + + return level_found; + } + + template <bool ADDING> + bool lock_nodes(uint8_t height, guard_t guards[], + Node* preds[], Node* succs[]) + { + Node *prepred, *pred, *succ = nullptr; + bool valid = true; + + for(int level = 0; valid && level < height; ++level) + { + pred = preds[level], succ = succs[level]; + + if(pred != prepred) + guards[level] = pred->acquire_unique(), prepred = pred; + + valid = !pred->flags.is_marked() && pred->forward(level) == succ; + + if(ADDING) + valid = valid && (succ == nullptr || !succ->flags.is_marked()); + } + + return valid; + } + + std::pair<Iterator, bool> + insert(value_type&& data, Node* preds[], Node* succs[]) + { + while(true) + { + auto level = find_path(header, H - 1, data.first, preds, succs); + + if(level != -1) + { + auto found = succs[level]; + + if(found->flags.is_marked()) + continue; + + while(!found->flags.is_fully_linked()) + usleep(250); + + return {Iterator {succs[level]}, false}; + } + + auto height = rnd(); + guard_t guards[H]; + + // try to acquire the locks for predecessors up to the height of + // the new node. release the locks and try again if someone else + // has the locks + if(!lock_nodes<true>(height, guards, preds, succs)) + continue; + + // you have the locks, create a new node + auto new_node = Node::create(std::forward<value_type>(data), height); + + // link the predecessors and successors, e.g. + // + // 4 HEAD ... P ------------------------> S ... NULL + // 3 HEAD ... ... P -----> NEW ---------> S ... NULL + // 2 HEAD ... ... P -----> NEW -----> S ... ... NULL + // 1 HEAD ... ... ... P -> NEW -> S ... ... ... NULL + for(uint8_t level = 0; level < height; ++level) + { + new_node->forward(level, succs[level]); + preds[level]->forward(level, new_node); + } + + new_node->flags.set_fully_linked(); + count.fetch_add(1); + + return {Iterator {new_node}, true}; + } + } + + bool ok_delete(Node* node, int level) + { + return node->flags.is_fully_linked() + && node->height - 1 == level + && !node->flags.is_marked(); + } + + bool remove(const K& key, Node* preds[], Node* succs[]) + { + Node* node = nullptr; + guard_t node_guard; + bool marked = false; + int height = 0; + + while(true) + { + auto level = find_path(header, H - 1, key, preds, succs); + + if(!marked && (level == -1 || !ok_delete(succs[level], level))) + return false; + + if(!marked) + { + node = succs[level]; + height = node->height; + node_guard = node->acquire_unique(); + + if(node->flags.is_marked()) + return false; + + node->flags.set_marked(); + marked = true; + } + + guard_t guards[H]; + + if(!lock_nodes<false>(height, guards, preds, succs)) + continue; + + for(int level = height - 1; level >= 0; --level) + preds[level]->forward(level, node->forward(level)); + + // TODO: review and test + gc.collect(node); + + count.fetch_sub(1); + return true; + } + } + + // number of elements + std::atomic<size_t> count {0}; + Node* header; + SkiplistGC<Node> gc; +}; + +template <class K, class T, size_t H, class lock_t> +thread_local FastBinomial<H> SkipListMap<K, T, H, lock_t>::rnd; + diff --git a/src/data_structures/skiplist/skiplistset.hpp b/src/data_structures/skiplist/skiplistset.hpp index bf77607d5..4baf95a58 100644 --- a/src/data_structures/skiplist/skiplistset.hpp +++ b/src/data_structures/skiplist/skiplistset.hpp @@ -2,7 +2,7 @@ #include <cassert> -#include "skiplist.hpp" +#include "data_structures/skiplist/skiplist_map.hpp" #include "utils/total_ordering.hpp" template <class T, size_t H=32, class lock_t=SpinLock> @@ -46,7 +46,7 @@ class SkipListSet const T* item {nullptr}; }; - using skiplist_t = SkipList<Key, T, H, lock_t>; + using skiplist_t = SkipListMap<Key, T, H, lock_t>; using iter_t = typename skiplist_t::Iterator; using const_iter_t = typename skiplist_t::ConstIterator; @@ -230,5 +230,5 @@ public: } private: - SkipList<Key, T> data; + SkipListMap<Key, T> data; }; diff --git a/src/query_engine/main_queries.cpp b/src/query_engine/main_queries.cpp index 1c70a1ad0..0ca3152fb 100644 --- a/src/query_engine/main_queries.cpp +++ b/src/query_engine/main_queries.cpp @@ -1,116 +1,123 @@ #include <iostream> +#include "cypher/common.hpp" #include "database/db.hpp" #include "query_stripper.hpp" #include "storage/model/properties/property.hpp" -#include "utils/command_line/arguments.hpp" -#include "cypher/common.hpp" -#include "utils/time/timer.hpp" #include "storage/model/properties/traversers/consolewriter.hpp" +#include "utils/command_line/arguments.hpp" +#include "utils/time/timer.hpp" using namespace std; // -- // DESCRIPTION: create account -// FULL: CREATE (n:ACCOUNT {id: 50, name: "Nikola", country: "Croatia", created_at: 14634563}) RETURN n +// FULL: CREATE (n:ACCOUNT {id: 50, name: "Nikola", country: "Croatia", +// created_at: 14634563}) RETURN n // STRIPPED: CREATE(n:ACCOUNT{id:0,name:1,country:2,created_at:3})RETURNn // HASH: 10597108978382323595 // STATUS: DONE // -- // DESCRIPTION: create personnel -// FULL: CREATE (n:PERSONNEL {id: 23, role: "CTO", created_at: 1235345}) RETURN n +// FULL: CREATE (n:PERSONNEL {id: 23, role: "CTO", created_at: 1235345}) +// RETURN n // STRIPPED: CREATE(n:PERSONNEL{id:0,role:1,created_at:2})RETURNn -// HASH: 4037885257628527960 +// HASH: 4037885257628527960 // STATUS: TODO // -- // DESCRIPTION: create edge between ACCOUNT node and PERSONNEL node (IS type) -// FULL: MATCH (a:ACCOUNT {id:50}), (p:PERSONNEL {id: 23}) CREATE (a)-[:IS]->(p) +// FULL: MATCH (a:ACCOUNT {id:50}), (p:PERSONNEL {id: 23}) CREATE +// (a)-[:IS]->(p) // STRIPPED: MATCH(a:ACCOUNT{id:0}),(p:PERSONNEL{id:1})CREATE(a)-[:IS]->(p) -// HASH: 16888190822925675190 +// HASH: 16888190822925675190 // STATUS: TODO // -- // DESCRIPTION: find ACCOUNT node, PERSONNEL node and edge between them -// FULL: MATCH (a:ACCOUNT {id:50})-[r:IS]->(p:PERSONNEL {id: 23}) RETURN a,r,p +// FULL: MATCH (a:ACCOUNT {id:50})-[r:IS]->(p:PERSONNEL {id: 23}) RETURN +// a,r,p // STRIPPED: MATCH(a:ACCOUNT{id:0})-[r:IS]->(p:PERSONNEL{id:1})RETURNa,r,p -// HASH: 9672752533852902744 +// HASH: 9672752533852902744 // STATUS: TODO // -- // DESCRIPTION: create OPPORTUNITY -// FULL: -// STRIPPED: -// HASH: +// FULL: +// STRIPPED: +// HASH: // STATUS: TODO // -- // DESCRIPTION: create PERSONNEL-[:CREATED]->OPPORTUNITY -// FULL: -// STRIPPED: -// HASH: +// FULL: +// STRIPPED: +// HASH: // STATUS: TODO // -- // DESCRIPTION: create COMPANY -// FULL: -// STRIPPED: -// HASH: +// FULL: +// STRIPPED: +// HASH: // STATUS: TODO // -- // DESCRIPTION: create OPPORTUNITY-[:MATCH]->COMPANY -// FULL: -// STRIPPED: -// HASH: +// FULL: +// STRIPPED: +// HASH: // STATUS: TODO // -- // DESCRIPTION: create an edge between two nodes that are found by the ID -// FULL: MATCH (a {id:0}), (p {id: 1}) CREATE (a)-[r:IS]->(p) RETURN r +// FULL: MATCH (a {id:0}), (p {id: 1}) CREATE (a)-[r:IS]->(p) RETURN r // STRIPPED: MATCH(a{id:0}),(p{id:1})CREATE(a)-[r:IS]->(p)RETURNr -// HASH: 7939106225150551899 +// HASH: 7939106225150551899 // STATUS: DONE // -- // DESCRIPTION: fine node by the ID // FULL: MATCH (n {id: 0}) RETURN n // STRIPPED: MATCH(n{id:0})RETURNn -// HASH: 11198568396549106428 +// HASH: 11198568396549106428 // STATUS: DONE // -- // DESCRIPTION: find edge by the ID // FULL: MATCH ()-[r]-() WHERE ID(r)=0 RETURN r // STRIPPED: MATCH()-[r]-()WHEREID(r)=0RETURNr -// HASH: 8320600413058284114 +// HASH: 8320600413058284114 // STATUS: DONE // -- // DESCRIPTION: update node that is found by the ID -// FULL: MATCH (n: {id: 0}) SET n.name = "TEST100" RETURN n -// STRIPPED: MATCH(n:{id:0})SETn.name=1RETURNn -// HASH: 6813335159006269041 +// FULL: MATCH (n: {id: 0}) SET n.name = "TEST100" RETURN n +// STRIPPED: MATCH(n:{id:0})SETn.name=1RETURNn +// HASH: 6813335159006269041 // STATUS: DONE // -- // DESCRIPTION: find shortest path between two nodes specified by ID -// FULL: -// STRIPPED: -// HASH: +// FULL: +// STRIPPED: +// HASH: +// STATUS: TODO +// -- +// DESCRIPTION: find all nodes by label name +// FULL: MATCH (n:LABEL) RETURN n +// STRIPPPED: MATCH(n:LABEL)RETURNn +// HASH: 4857652843629217005 // STATUS: TODO // -- // TODO: Labels and Types have to be extracted from a query during the // query compile time -void cout_properties(const Properties& properties) +void cout_properties(const Properties &properties) { ConsoleWriter writer; properties.accept(writer); } -int main(int argc, char** argv) +int main(int argc, char **argv) { Db db; - std::map<uint64_t, std::function<bool(const properties_t&)>> queries; + std::map<uint64_t, std::function<bool(const properties_t &)>> queries; - auto create_labeled_and_named_node = [&db](const properties_t& args) - { - auto& t = db.tx_engine.begin(); + 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); - vertex_accessor.property( - "name", args[0] - ); - auto label = db.graph.label_store.find_or_create("LABEL"); + vertex_accessor.property("name", args[0]); + auto &label = db.graph.label_store.find_or_create("LABEL"); vertex_accessor.add_label(label); cout_properties(vertex_accessor.properties()); t.commit(); @@ -118,25 +125,16 @@ int main(int argc, char** argv) return true; }; - auto create_account = [&db](const properties_t& args) - { - auto& t = db.tx_engine.begin(); + auto create_account = [&db](const properties_t &args) { + auto &t = db.tx_engine.begin(); auto vertex_accessor = db.graph.vertices.insert(t); // this can be a loop - 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.property("id", args[0]); + vertex_accessor.property("name", args[1]); + vertex_accessor.property("country", args[2]); + vertex_accessor.property("created_at", args[3]); // here is the problem because LABEL can't be stripped out - auto label = db.graph.label_store.find_or_create("ACCOUNT"); + auto &label = db.graph.label_store.find_or_create("ACCOUNT"); vertex_accessor.add_label(label); cout_properties(vertex_accessor.properties()); t.commit(); @@ -144,35 +142,37 @@ int main(int argc, char** argv) return true; }; - auto find_node_by_internal_id = [&db](const properties_t& args) - { - auto& t = db.tx_engine.begin(); - auto id = static_cast<Int32&>(*args[0]); + auto find_node_by_internal_id = [&db](const properties_t &args) { + auto &t = db.tx_engine.begin(); + auto id = static_cast<Int32 &>(*args[0]); auto vertex_accessor = db.graph.vertices.find(t, Id(id.value)); if (!vertex_accessor) { cout << "vertex doesn't exist" << endl; t.commit(); - return false; + return false; } cout_properties(vertex_accessor.properties()); + + cout << "LABELS:" << endl; + for (auto label_ref : vertex_accessor.labels()) { + cout << label_ref.get() << endl; + } + t.commit(); return true; }; - auto create_edge = [&db](const properties_t& args) - { - auto& t = db.tx_engine.begin(); + auto create_edge = [&db](const properties_t &args) { + auto &t = db.tx_engine.begin(); auto v1 = db.graph.vertices.find(t, args[0]->as<Int32>().value); - if (!v1) - return t.commit(), false; + if (!v1) return t.commit(), false; auto v2 = db.graph.vertices.find(t, args[1]->as<Int32>().value); - if (!v2) - return t.commit(), false; + if (!v2) return t.commit(), false; auto e = db.graph.edges.insert(t); @@ -192,12 +192,10 @@ int main(int argc, char** argv) return true; }; - auto find_edge_by_internal_id = [&db](const properties_t& args) - { - auto& t = db.tx_engine.begin(); + auto find_edge_by_internal_id = [&db](const properties_t &args) { + auto &t = db.tx_engine.begin(); auto e = db.graph.edges.find(t, args[0]->as<Int32>().value); - if (!e) - return t.commit(), false; + if (!e) return t.commit(), false; // print edge type and properties cout << "EDGE_TYPE: " << e.edge_type() << endl; @@ -215,14 +213,12 @@ int main(int argc, char** argv) return true; }; - auto update_node = [&db](const properties_t& args) - { - auto& t = db.tx_engine.begin(); + auto update_node = [&db](const properties_t &args) { + auto &t = db.tx_engine.begin(); auto v = db.graph.vertices.find(t, args[0]->as<Int32>().value); - if (!v) - return t.commit(), false; - + if (!v) return t.commit(), false; + v.property("name", args[1]); cout_properties(v.properties()); @@ -231,18 +227,33 @@ int main(int argc, char** argv) return true; }; + auto find_by_label = [&db](const properties_t &args) + { + auto &t = db.tx_engine.begin(); + auto &label = db.graph.label_store.find_or_create("LABEL"); + auto &index_record_collection = + db.graph.vertices.find_label_index(label); + auto accessor = index_record_collection.access(); + cout << "VERTICES" << endl; + for (auto& v : accessor) { + cout << v.record->data.props.at("name").as<String>().value << endl; + } + return true; + }; + queries[10597108978382323595u] = create_account; - queries[5397556489557792025u] = create_labeled_and_named_node; - queries[7939106225150551899u] = create_edge; + queries[5397556489557792025u] = create_labeled_and_named_node; + queries[7939106225150551899u] = create_edge; queries[11198568396549106428u] = find_node_by_internal_id; - queries[8320600413058284114u] = find_edge_by_internal_id; - queries[6813335159006269041u] = update_node; + queries[8320600413058284114u] = find_edge_by_internal_id; + queries[6813335159006269041u] = update_node; + queries[4857652843629217005u] = find_by_label; // auto arguments = all_arguments(argc, argv); // auto input_query = extract_query(arguments); auto stripper = make_query_stripper(TK_INT, TK_FLOAT, TK_STR, TK_BOOL); // auto stripped = stripper.strip(input_query); - // + // // auto time = timer<ms>([&stripped, &queries]() { // for (int i = 0; i < 1000000; ++i) { // queries[stripped.hash](stripped.arguments); @@ -264,7 +275,7 @@ int main(int argc, char** argv) cout << "unsupported query" << endl; continue; } - + auto result = queries[stripped.hash](stripped.arguments); cout << "RETURN: " << result << endl; diff --git a/src/storage/common.hpp b/src/storage/common.hpp index 4601ec474..8e29ab91e 100644 --- a/src/storage/common.hpp +++ b/src/storage/common.hpp @@ -1,6 +1,6 @@ #pragma once -#include "transactions/transaction.hpp" +#include "data_structures/concurrent/concurrent_map.hpp" #include "mvcc/version_list.hpp" -#include "data_structures/skiplist/skiplist.hpp" +#include "transactions/transaction.hpp" #include "utils/counters/atomic_counter.hpp" diff --git a/src/storage/edges.hpp b/src/storage/edges.hpp index 21c8ec081..23871419d 100644 --- a/src/storage/edges.hpp +++ b/src/storage/edges.hpp @@ -2,6 +2,7 @@ #include "common.hpp" #include "edge_accessor.hpp" +#include "data_structures/concurrent/concurrent_map.hpp" class Edges { @@ -46,6 +47,6 @@ public: } private: - SkipList<uint64_t, EdgeRecord> edges; + ConcurrentMap<uint64_t, EdgeRecord> edges; AtomicCounter<uint64_t> counter; }; diff --git a/src/storage/indexes/index.hpp b/src/storage/indexes/index.hpp index acb038552..9dd98dba7 100644 --- a/src/storage/indexes/index.hpp +++ b/src/storage/indexes/index.hpp @@ -1,48 +1,45 @@ #pragma once -#include "data_structures/skiplist/skiplist.hpp" -#include "keys/unique_key.hpp" +#include <memory> -#include "storage/cursor.hpp" +#include "data_structures/concurrent/concurrent_map.hpp" +#include "storage/indexes/index_record.hpp" +#include "storage/indexes/index_record_collection.hpp" +#include "storage/model/label.hpp" template <class Key, class Item> class Index { public: - using skiplist_t = SkipList<Key, Item*>; - using iterator_t = typename skiplist_t::Iterator; - using accessor_t = typename skiplist_t::Accessor; - using K = typename Key::key_t; - using cursor_t = Cursor<accessor_t, iterator_t, K>; + using container_t = ConcurrentMap<Key, Item>; - // cursor_t insert(const K& key, Item* item, tx::Transaction& t) - auto insert(const K& key, Item* item) + Index() : index(std::make_unique<container_t>()) {} + + auto update(const Label &label, VertexIndexRecord &&index_record) { - // Item has to be some kind of container for the real data like Vertex, - // the container has to be transactionally aware - // in other words index or something that wraps index has to be - // transactionally aware + auto accessor = index->access(); + auto label_ref = label_ref_t(label); - auto accessor = skiplist.access(); - auto result = accessor.insert_unique(key, item); + // create Index Record Collection if it doesn't exist + if (!accessor.contains(label_ref)) { + accessor.insert_unique(label_ref, + std::move(VertexIndexRecordCollection())); + } - // TODO: handle existing insert - - return result; + // add Vertex Index Record to the Record Collection + auto &record_collection = (*accessor.find(label_ref)).second; + record_collection.add(std::forward<VertexIndexRecord>(index_record)); } - auto remove(const K& key) + VertexIndexRecordCollection& find(const Label& label) { - auto accessor = skiplist.access(); - return accessor.remove(key); + // 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; } - auto find(const K& key) - { - auto accessor = skiplist.access(); - return accessor.find(key); - } - private: - skiplist_t skiplist; + std::unique_ptr<container_t> index; }; diff --git a/src/storage/indexes/index_record.hpp b/src/storage/indexes/index_record.hpp new file mode 100644 index 000000000..b7e63efaa --- /dev/null +++ b/src/storage/indexes/index_record.hpp @@ -0,0 +1,46 @@ +#pragma once + +#include "mvcc/version_list.hpp" +#include "utils/total_ordering.hpp" + +template <class T> +class IndexRecord : TotalOrdering<IndexRecord<T>> +{ +public: + using vlist_t = mvcc::VersionList<T>; + + IndexRecord() = default; + + IndexRecord(T *record, vlist_t *vlist) : record(record), vlist(vlist) + { + assert(record != nullptr); + assert(vlist != nullptr); + } + + friend bool operator<(const IndexRecord& lhs, const IndexRecord& rhs) + { + return lhs.record < rhs.record; + } + + friend bool operator==(const IndexRecord& lhs, const IndexRecord& rhs) + { + return 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 + // } + +// private: + T *const record{nullptr}; + vlist_t *const vlist{nullptr}; +}; + +using VertexIndexRecord = IndexRecord<Vertex>; +using EdgeIndexRecord = IndexRecord<Edge>; diff --git a/src/storage/indexes/index_record_collection.hpp b/src/storage/indexes/index_record_collection.hpp new file mode 100644 index 000000000..0d06e2a91 --- /dev/null +++ b/src/storage/indexes/index_record_collection.hpp @@ -0,0 +1,38 @@ +#pragma once + +#include <memory> + +#include "data_structures/concurrent/concurrent_set.hpp" +#include "storage/indexes/index_record.hpp" + +template <class T> +class IndexRecordCollection +{ +public: + using index_record_t = IndexRecord<T>; + using index_record_collection_t = ConcurrentSet<index_record_t>; + + IndexRecordCollection() + : records(std::make_unique<index_record_collection_t>()) + { + } + + void add(index_record_t &&record) + { + auto accessor = records->access(); + accessor.insert(std::forward<index_record_t>(record)); + } + + auto access() + { + return records->access(); + } + + // TODO: iterator and proxy + +private: + std::unique_ptr<index_record_collection_t> records; +}; + +using VertexIndexRecordCollection = IndexRecordCollection<Vertex>; +using EdgeIndexRecordCollection = IndexRecordCollection<Edge>; diff --git a/src/storage/indexes/index_key.hpp b/src/storage/indexes/keys/index_key.hpp similarity index 79% rename from src/storage/indexes/index_key.hpp rename to src/storage/indexes/keys/index_key.hpp index 68beba984..b2555a782 100644 --- a/src/storage/indexes/index_key.hpp +++ b/src/storage/indexes/keys/index_key.hpp @@ -2,7 +2,6 @@ #include "utils/total_ordering.hpp" -#include "ordering.hpp" #include "storage/model/properties/properties.hpp" #include "storage/model/properties/property.hpp" @@ -10,15 +9,10 @@ template <class Ordering> class UniqueIndexKey { public: - namespace - -private: - Property& key; }; template <class Ordering> class IndexKey { public: - }; diff --git a/src/storage/label_store.hpp b/src/storage/label_store.hpp index 13de699bb..cc81056f4 100644 --- a/src/storage/label_store.hpp +++ b/src/storage/label_store.hpp @@ -3,7 +3,7 @@ #include <stdexcept> #include "model/label.hpp" -#include "data_structures/skiplist/skiplistset.hpp" +#include "data_structures/concurrent/concurrent_set.hpp" class LabelStore { @@ -25,5 +25,5 @@ public: // return { Label, is_found } private: - SkipListSet<Label> labels; + ConcurrentSet<Label> labels; }; diff --git a/src/storage/locking/record_lock.cpp b/src/storage/locking/record_lock.cpp new file mode 100644 index 000000000..9ca8f2f6a --- /dev/null +++ b/src/storage/locking/record_lock.cpp @@ -0,0 +1,27 @@ +#include "storage/locking/record_lock.hpp" + +void RecordLock::lock() +{ + mutex.lock(&timeout); +} + +LockStatus RecordLock::lock(const Id& id) +{ + if(mutex.try_lock()) + return owner = id, LockStatus::Acquired; + + if(owner == id) + return LockStatus::AlreadyHeld; + + return mutex.lock(&timeout), LockStatus::Acquired; +} + +void RecordLock::unlock() +{ + owner = INVALID; + mutex.unlock(); +} + +constexpr struct timespec RecordLock::timeout; +constexpr Id RecordLock::INVALID; + diff --git a/src/storage/locking/record_lock.hpp b/src/storage/locking/record_lock.hpp deleted file mode 100644 index d9b562967..000000000 --- a/src/storage/locking/record_lock.hpp +++ /dev/null @@ -1,42 +0,0 @@ -#pragma once - -#include "threading/sync/futex.hpp" -#include "lock_status.hpp" -#include "mvcc/id.hpp" - -class RecordLock -{ - static constexpr struct timespec timeout {20, 0}; - static constexpr Id INVALID = Id(); - -public: - void lock() - { - mutex.lock(&timeout); - } - - LockStatus lock(const Id& id) - { - if(mutex.try_lock()) - return owner = id, LockStatus::Acquired; - - if(owner == id) - return LockStatus::AlreadyHeld; - - return mutex.lock(&timeout), LockStatus::Acquired; - } - - void unlock() - { - owner = INVALID; - mutex.unlock(); - } - -private: - Futex mutex; - Id owner; -}; - -constexpr struct timespec RecordLock::timeout; -constexpr Id RecordLock::INVALID; - diff --git a/src/storage/model/label.hpp b/src/storage/model/label.hpp index 3a4f2b02e..500cd4fb8 100644 --- a/src/storage/model/label.hpp +++ b/src/storage/model/label.hpp @@ -2,7 +2,9 @@ #include <stdint.h> #include <ostream> + #include "utils/total_ordering.hpp" +#include "utils/reference_wrapper.hpp" class Label : public TotalOrdering<Label> { @@ -36,3 +38,5 @@ public: private: std::string name; }; + +using label_ref_t = ReferenceWrapper<const Label>; diff --git a/src/storage/model/label_collection.hpp b/src/storage/model/label_collection.hpp index eec389d8c..e89c9da85 100644 --- a/src/storage/model/label_collection.hpp +++ b/src/storage/model/label_collection.hpp @@ -1,48 +1,54 @@ #pragma once #include <set> + #include "label.hpp" class LabelCollection { public: - auto begin() { return labels.begin(); } - auto begin() const { return labels.begin(); } - auto cbegin() const { return labels.begin(); } + auto begin() { return _labels.begin(); } + auto begin() const { return _labels.begin(); } + auto cbegin() const { return _labels.begin(); } - auto end() { return labels.end(); } - auto end() const { return labels.end(); } - auto cend() const { return labels.end(); } + auto end() { return _labels.end(); } + auto end() const { return _labels.end(); } + auto cend() const { return _labels.end(); } bool add(const Label& label) { - return labels.insert(std::cref(label)).second; + return _labels.insert(label_ref_t(label)).second; } bool has(const Label& label) const { - return labels.count(label); + return _labels.count(label); } size_t count() const { - return labels.size(); + return _labels.size(); } bool remove(const Label& label) { - auto it = labels.find(label); + auto it = _labels.find(label); - if(it == labels.end()) + if(it == _labels.end()) return false; - return labels.erase(it), true; + return _labels.erase(it), true; } void clear() { - labels.clear(); + _labels.clear(); + } + + const std::set<label_ref_t>& operator()() const + { + return _labels; } private: - std::set<std::reference_wrapper<const Label>> labels; + std::set<label_ref_t> _labels; }; diff --git a/src/storage/model/properties/all.hpp b/src/storage/model/properties/all.hpp deleted file mode 100644 index 9a9eaebdd..000000000 --- a/src/storage/model/properties/all.hpp +++ /dev/null @@ -1,27 +0,0 @@ -#pragma once - -#include "property.hpp" - -#include "null.hpp" -#include "bool.hpp" -#include "string.hpp" -#include "int32.hpp" -#include "int64.hpp" -#include "float.hpp" -#include "double.hpp" - -template <class Handler> -void Property::accept(Handler& h) -{ - switch(flags) - { - case Flags::True: return h.handle(static_cast<Bool&>(*this)); - case Flags::False: return h.handle(static_cast<Bool&>(*this)); - case Flags::String: return h.handle(static_cast<String&>(*this)); - case Flags::Int32: return h.handle(static_cast<Int32&>(*this)); - case Flags::Int64: return h.handle(static_cast<Int64&>(*this)); - case Flags::Float: return h.handle(static_cast<Float&>(*this)); - case Flags::Double: return h.handle(static_cast<Double&>(*this)); - default: return; - } -} diff --git a/src/storage/model/properties/bool.cpp b/src/storage/model/properties/bool.cpp new file mode 100644 index 000000000..83c8cf152 --- /dev/null +++ b/src/storage/model/properties/bool.cpp @@ -0,0 +1,46 @@ +#include "storage/model/properties/bool.hpp" + +Bool::Bool(bool value) : Property(value ? Flags::True : Flags::False) {} + +bool Bool::value() const +{ + // true when the subtraction of True from flags is equal to zero + + // True 0000 0000 0000 0000 0000 0000 0000 0011 + // False 0000 0000 0000 0000 0000 0000 0000 0101 + // + // True - True = 0 + // False - True != 0 + + return (underlying_cast(flags) - underlying_cast(Flags::True)) == 0; +} + +Bool::operator bool() const +{ + return value(); +} + +bool Bool::operator==(const Property& other) const +{ + return other.is<Bool>() && operator==(other.as<Bool>()); +} + +bool Bool::operator==(const Bool& other) const +{ + return other.flags == flags; +} + +bool Bool::operator==(bool v) const +{ + return value() == v; +} + +std::ostream& Bool::print(std::ostream& stream) const +{ + return operator<<(stream, *this); +} + +std::ostream& operator<<(std::ostream& stream, const Bool& prop) +{ + return stream << prop.value(); +} diff --git a/src/storage/model/properties/bool.hpp b/src/storage/model/properties/bool.hpp deleted file mode 100644 index 69d68287b..000000000 --- a/src/storage/model/properties/bool.hpp +++ /dev/null @@ -1,57 +0,0 @@ -#pragma once - -#include "property.hpp" - -class Bool : public Property -{ -public: - static constexpr Flags type = Flags::Bool; - - Bool(bool value) : Property(value ? Flags::True : Flags::False) {} - - Bool(const Bool& other) = default; - - bool value() const - { - // true when the subtraction of True from flags is equal to zero - - // True 0000 0000 0000 0000 0000 0000 0000 0011 - // False 0000 0000 0000 0000 0000 0000 0000 0101 - // - // True - True = 0 - // False - True != 0 - - return (underlying_cast(flags) - underlying_cast(Flags::True)) == 0; - } - - explicit operator bool() const - { - return value(); - } - - bool operator==(const Property& other) const override - { - return other.is<Bool>() && operator==(other.as<Bool>()); - } - - bool operator==(const Bool& other) const - { - return other.flags == flags; - } - - bool operator==(bool v) const - { - return value() == v; - } - - std::ostream& print(std::ostream& stream) const override - { - return operator<<(stream, *this); - } - - friend std::ostream& operator<<(std::ostream& stream, const Bool& prop) - { - return stream << prop.value(); - } -}; - diff --git a/src/storage/model/properties/null.cpp b/src/storage/model/properties/null.cpp new file mode 100644 index 000000000..e70f10f55 --- /dev/null +++ b/src/storage/model/properties/null.cpp @@ -0,0 +1,30 @@ +#include "storage/model/properties/null.hpp" + +bool Null::operator==(const Property& other) const +{ + return other.is<Null>(); +} + +bool Null::operator==(const Null&) const +{ + return true; +} + +Null::operator bool() +{ + return false; +} + +std::ostream& operator<<(std::ostream& stream, const Null&) +{ + return stream << "NULL"; +} + +std::ostream& Null::print(std::ostream& stream) const +{ + return operator<<(stream, *this); +} + +Null::Null() : Property(Flags::Null) {} + +const Null Property::Null; diff --git a/src/storage/model/properties/null.hpp b/src/storage/model/properties/null.hpp deleted file mode 100644 index c09171ba6..000000000 --- a/src/storage/model/properties/null.hpp +++ /dev/null @@ -1,51 +0,0 @@ -#pragma once - -#include "property.hpp" - -class Null : public Property -{ -public: - friend class Property; - - static constexpr Flags type = Flags::Null; - - Null(const Null&) = delete; - Null(Null&&) = delete; - - Null operator=(const Null&) = delete; - - bool operator==(const Property& other) const override - { - return other.is<Null>(); - } - - bool operator==(const Null&) const - { - return true; - } - - explicit operator bool() - { - return false; - } - - friend std::ostream& operator<<(std::ostream& stream, const Null&) - { - return stream << "NULL"; - } - - std::ostream& print(std::ostream& stream) const override - { - return operator<<(stream, *this); - } - -private: - // the constructor for null is private, it can be constructed only as a - // value inside the Property class, Property::Null - Null() : Property(Flags::Null) {} -}; - -// Null is a const singleton declared in class Property -// it can be used as a type by using Null or as a value by using Property::Null -const Null Property::Null; - diff --git a/src/storage/model/properties/number.hpp b/src/storage/model/properties/number.hpp index 41a710487..1cc988818 100644 --- a/src/storage/model/properties/number.hpp +++ b/src/storage/model/properties/number.hpp @@ -1,9 +1,9 @@ #pragma once -#include "property.hpp" +#include "storage/model/properties/property.hpp" +#include "utils/math_operations.hpp" #include "utils/total_ordering.hpp" #include "utils/unary_negation.hpp" -#include "utils/math_operations.hpp" template <class Derived> class Number : public Property, @@ -14,27 +14,27 @@ class Number : public Property, public: using Property::Property; - bool operator==(const Property& other) const override + bool operator==(const Property &other) const override { return other.is<Derived>() && this->derived() == other.as<Derived>(); } - friend bool operator==(const Derived& lhs, const Derived& rhs) + friend bool operator==(const Derived &lhs, const Derived &rhs) { return lhs.value == rhs.value; } - friend bool operator<(const Derived& lhs, const Derived& rhs) + friend bool operator<(const Derived &lhs, const Derived &rhs) { return lhs.value == rhs.value; } - friend std::ostream& operator<<(std::ostream& s, const Derived& number) + friend std::ostream &operator<<(std::ostream &s, const Derived &number) { return s << number.value; } - std::ostream& print(std::ostream& stream) const override + std::ostream &print(std::ostream &stream) const override { return operator<<(stream, this->derived()); } diff --git a/src/storage/model/properties/properties.cpp b/src/storage/model/properties/properties.cpp new file mode 100644 index 000000000..c4dec594d --- /dev/null +++ b/src/storage/model/properties/properties.cpp @@ -0,0 +1,54 @@ +#include "storage/model/properties/properties.hpp" + +#include "storage/model/properties/null.hpp" + +const Property& Properties::at(const std::string& key) const +{ + auto it = props.find(key); + + if(it == props.end()) + return Property::Null; + + return *it->second.get(); +} + +template <class T, class... Args> +void Properties::set(const std::string& key, Args&&... args) +{ + auto value = std::make_shared<T>(std::forward<Args>(args)...); + + // try to emplace the item + 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); +} + +void Properties::set(const std::string& key, Property::sptr value) +{ + props[key] = std::move(value); +} + +void Properties::clear(const std::string& key) +{ + props.erase(key); +} + +// template <class Handler> +// void Properties::accept(Handler& handler) const +// { +// for(auto& kv : props) +// handler.handle(kv.first, *kv.second); +// +// handler.finish(); +// } + +template<> +inline void Properties::set<Null>(const std::string& key) +{ + clear(key); +} diff --git a/src/storage/model/properties/properties.hpp b/src/storage/model/properties/properties.hpp deleted file mode 100644 index 37a088ee4..000000000 --- a/src/storage/model/properties/properties.hpp +++ /dev/null @@ -1,69 +0,0 @@ -#pragma once - -#include <map> - -#include "property.hpp" -#include "all.hpp" - -class Properties -{ -public: - using sptr = std::shared_ptr<Properties>; - - const Property& at(const std::string& key) const - { - auto it = props.find(key); - - if(it == props.end()) - return Property::Null; - - return *it->second.get(); - } - - template <class T, class... Args> - void set(const std::string& key, Args&&... args) - { - auto value = std::make_shared<T>(std::forward<Args>(args)...); - - // try to emplace the item - 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); - } - - void set(const std::string& key, Property::sptr value) - { - props[key] = std::move(value); - } - - void clear(const std::string& key) - { - props.erase(key); - } - - template <class Handler> - void accept(Handler& handler) const - { - for(auto& kv : props) - handler.handle(kv.first, *kv.second); - - handler.finish(); - } - -private: - using props_t = std::map<std::string, Property::sptr>; - - props_t props; -}; - -template<> -void Properties::set<Null>(const std::string& key) -{ - clear(key); -} - diff --git a/src/storage/model/properties/property.cpp b/src/storage/model/properties/property.cpp new file mode 100644 index 000000000..80b1830f5 --- /dev/null +++ b/src/storage/model/properties/property.cpp @@ -0,0 +1,13 @@ +#include "storage/model/properties/property.hpp" + +Property::Property(Flags flags) : flags(flags) {} + +bool Property::operator!=(const Property& other) const +{ + return !operator==(other); +} + +std::ostream& operator<<(std::ostream& stream, const Property& prop) +{ + return prop.print(stream); +} diff --git a/src/storage/model/properties/string.cpp b/src/storage/model/properties/string.cpp new file mode 100644 index 000000000..31881bdb1 --- /dev/null +++ b/src/storage/model/properties/string.cpp @@ -0,0 +1,36 @@ +#include "storage/model/properties/string.hpp" + +String::String(const std::string& value) : Property(Flags::String), value(value) {} +String::String(std::string&& value) : Property(Flags::String), value(value) {} + + +String::operator const std::string&() const +{ + return value; +} + +bool String::operator==(const Property& other) const +{ + return other.is<String>() && operator==(other.as<String>()); +} + +bool String::operator==(const String& other) const +{ + return value == other.value; +} + +bool String::operator==(const std::string& other) const +{ + return value == other; +} + +std::ostream& operator<<(std::ostream& stream, const String& prop) +{ + return stream << prop.value; +} + +std::ostream& String::print(std::ostream& stream) const +{ + return operator<<(stream, *this); +} + diff --git a/src/storage/model/properties/string.hpp b/src/storage/model/properties/string.hpp deleted file mode 100644 index 38e2699b7..000000000 --- a/src/storage/model/properties/string.hpp +++ /dev/null @@ -1,48 +0,0 @@ -#pragma once - -#include "property.hpp" - -class String : public Property -{ -public: - static constexpr Flags type = Flags::String; - - String(const std::string& value) : Property(Flags::String), value(value) {} - - String(std::string&& value) : Property(Flags::String), value(value) {} - - String(const String&) = default; - String(String&&) = default; - - operator const std::string&() const - { - return value; - } - - bool operator==(const Property& other) const override - { - return other.is<String>() && operator==(other.as<String>()); - } - - bool operator==(const String& other) const - { - return value == other.value; - } - - bool operator==(const std::string& other) const - { - return value == other; - } - - friend std::ostream& operator<<(std::ostream& stream, const String& prop) - { - return stream << prop.value; - } - - std::ostream& print(std::ostream& stream) const override - { - return operator<<(stream, *this); - } - - std::string value; -}; diff --git a/src/storage/model/properties/traversers/consolewriter.hpp b/src/storage/model/properties/traversers/consolewriter.hpp index 4502d4be1..562c89000 100644 --- a/src/storage/model/properties/traversers/consolewriter.hpp +++ b/src/storage/model/properties/traversers/consolewriter.hpp @@ -2,8 +2,8 @@ #include <iostream> -#include "../properties.hpp" -#include "../all.hpp" +#include "storage/model/properties/properties.hpp" +#include "storage/model/properties/handler.hpp" using std::cout; using std::endl; @@ -13,47 +13,28 @@ class ConsoleWriter public: ConsoleWriter() {} - void handle(const std::string& key, Property& value) + void handle(const std::string &key, Property &value) { cout << "KEY: " << key << "; VALUE: "; - value.accept(*this); + accept(value, *this); + + // value.accept(*this); cout << endl; } - void handle(Bool& b) - { - cout << b.value(); - } + void handle(Bool &b) { cout << b.value(); } - void handle(String& s) - { - cout << s.value; - } + void handle(String &s) { cout << s.value; } - void handle(Int32& int32) - { - cout << int32.value; - } + void handle(Int32 &int32) { cout << int32.value; } - void handle(Int64& int64) - { - cout << int64.value; - } + void handle(Int64 &int64) { cout << int64.value; } - void handle(Float& f) - { - cout << f.value; - } + void handle(Float &f) { cout << f.value; } - void handle(Double& d) - { - cout << d.value; - } + void handle(Double &d) { cout << d.value; } - void finish() - { - } + void finish() {} }; - diff --git a/src/storage/model/properties/traversers/jsonwriter.hpp b/src/storage/model/properties/traversers/jsonwriter.hpp index b4d03ec23..79059f448 100644 --- a/src/storage/model/properties/traversers/jsonwriter.hpp +++ b/src/storage/model/properties/traversers/jsonwriter.hpp @@ -1,94 +1,67 @@ #pragma once -#include "../properties.hpp" -#include "../all.hpp" +#include "storage/model/properties/all.hpp" +#include "storage/model/properties/properties.hpp" +#include "storage/model/properties/handler.hpp" template <class Buffer> struct JsonWriter { public: - JsonWriter(Buffer& buffer) : buffer(buffer) - { - buffer << '{'; - }; + JsonWriter(Buffer &buffer) : buffer(buffer) { buffer << '{'; }; - void handle(const std::string& key, Property& value) + void handle(const std::string &key, Property &value) { - if(!first) - buffer << ','; + if (!first) buffer << ','; - if(first) - first = false; + if (first) first = false; buffer << '"' << key << "\":"; - value.accept(*this); + // value.accept(*this); + accept(value, *this); } - void handle(Bool& b) - { - buffer << (b.value() ? "true" : "false"); - } + void handle(Bool &b) { buffer << (b.value() ? "true" : "false"); } - void handle(String& s) - { - buffer << '"' << s.value << '"'; - } + void handle(String &s) { buffer << '"' << s.value << '"'; } - void handle(Int32& int32) - { - buffer << std::to_string(int32.value); - } + void handle(Int32 &int32) { buffer << std::to_string(int32.value); } - void handle(Int64& int64) - { - buffer << std::to_string(int64.value); - } + void handle(Int64 &int64) { buffer << std::to_string(int64.value); } - void handle(Float& f) - { - buffer << std::to_string(f.value); - } + void handle(Float &f) { buffer << std::to_string(f.value); } - void handle(Double& d) - { - buffer << std::to_string(d.value); - } + void handle(Double &d) { buffer << std::to_string(d.value); } - void finish() - { - buffer << '}'; - } + void finish() { buffer << '}'; } private: - bool first {true}; - Buffer& buffer; + bool first{true}; + Buffer &buffer; }; class StringBuffer { public: - StringBuffer& operator<<(const std::string& str) + StringBuffer &operator<<(const std::string &str) { data += str; return *this; } - StringBuffer& operator<<(const char* str) + StringBuffer &operator<<(const char *str) { data += str; return *this; } - StringBuffer& operator<<(char c) + StringBuffer &operator<<(char c) { data += c; return *this; } - std::string& str() - { - return data; - } + std::string &str() { return data; } private: std::string data; diff --git a/src/storage/model/property_model.hpp b/src/storage/model/property_model.hpp index a03ac6294..d6c7bb193 100644 --- a/src/storage/model/property_model.hpp +++ b/src/storage/model/property_model.hpp @@ -1,6 +1,6 @@ #pragma once -#include "properties/properties.hpp" +#include "storage/model/properties/properties.hpp" class PropertyModel { diff --git a/src/storage/vertex_accessor.cpp b/src/storage/vertex_accessor.cpp new file mode 100644 index 000000000..f6237c216 --- /dev/null +++ b/src/storage/vertex_accessor.cpp @@ -0,0 +1,38 @@ +#include "storage/vertex_accessor.hpp" + +#include "storage/vertices.hpp" + +size_t Vertex::Accessor::out_degree() const +{ + return this->record->data.out.degree(); +} + +size_t Vertex::Accessor::in_degree() const +{ + return this->record->data.in.degree(); +} + +size_t Vertex::Accessor::degree() const +{ + return in_degree() + out_degree(); +} + +void Vertex::Accessor::add_label(const Label &label) +{ + // update vertex + this->record->data.labels.add(label); + + // update index + this->store->update_label_index( + label, VertexIndexRecord(this->record, this->vlist)); +} + +bool Vertex::Accessor::has_label(const Label &label) const +{ + return this->record->data.labels.has(label); +} + +const std::set<label_ref_t>& Vertex::Accessor::labels() const +{ + return this->record->data.labels(); +} diff --git a/src/storage/vertex_accessor.hpp b/src/storage/vertex_accessor.hpp deleted file mode 100644 index 0bb3d6f85..000000000 --- a/src/storage/vertex_accessor.hpp +++ /dev/null @@ -1,37 +0,0 @@ -#pragma once - -#include "storage/vertex.hpp" -#include "storage/record_accessor.hpp" - -class Vertices; - -class Vertex::Accessor : public RecordAccessor<Vertex, Vertices, Vertex::Accessor> -{ -public: - using RecordAccessor::RecordAccessor; - - size_t out_degree() const - { - return this->record->data.out.degree(); - } - - size_t in_degree() const - { - return this->record->data.in.degree(); - } - - size_t degree() const - { - return in_degree() + out_degree(); - } - - bool add_label(const Label& label) - { - return this->record->data.labels.add(label); - } - - bool has_label(const Label& label) const - { - return this->record->data.labels.has(label); - } -}; diff --git a/src/storage/vertices.cpp b/src/storage/vertices.cpp new file mode 100644 index 000000000..d879a5e17 --- /dev/null +++ b/src/storage/vertices.cpp @@ -0,0 +1,50 @@ +#include "storage/vertices.hpp" + +const Vertex::Accessor Vertices::find(tx::Transaction &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(); + + // find vertex + 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) +{ + // get next vertex id + auto next = counter.next(); + + // create new vertex record + VertexRecord vertex_record(next); + // vertex_record.id(next); + + // insert the new vertex record into the vertex store + auto vertices_accessor = vertices.access(); + auto result = + vertices_accessor.insert_unique(next, std::move(vertex_record)); + + // create new vertex + auto inserted_vertex_record = result.first; + auto vertex = inserted_vertex_record->second.insert(t); + + return Vertex::Accessor(vertex, &inserted_vertex_record->second, this); +} + +void Vertices::update_label_index(const Label &label, + VertexIndexRecord &&index_record) +{ + label_index.update(label, + std::forward<VertexIndexRecord>(index_record)); +} + +VertexIndexRecordCollection& Vertices::find_label_index(const Label& label) +{ + return label_index.find(label); +} diff --git a/src/storage/vertices.hpp b/src/storage/vertices.hpp deleted file mode 100644 index 16fc040e6..000000000 --- a/src/storage/vertices.hpp +++ /dev/null @@ -1,54 +0,0 @@ -#pragma once - -#include "common.hpp" -#include "storage/vertex_accessor.hpp" - -class Vertices -{ -public: - Vertex::Accessor find(tx::Transaction& 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(); - - // find vertex - auto vertex = vertices_iterator->second.find(t); - - if (vertex == nullptr) - return Vertex::Accessor(); - - return Vertex::Accessor(vertex, &vertices_iterator->second, this); - } - - Vertex::Accessor insert(tx::Transaction& t) - { - // get next vertex id - auto next = counter.next(std::memory_order_acquire); - - // create new vertex record - VertexRecord vertex_record(next); - // vertex_record.id(next); - - // insert the new vertex record into the vertex store - auto vertices_accessor = vertices.access(); - auto result = vertices_accessor.insert_unique( - next, - std::move(vertex_record) - ); - - // create new vertex - auto inserted_vertex_record = result.first; - auto vertex = inserted_vertex_record->second.insert(t); - - return Vertex::Accessor(vertex, &inserted_vertex_record->second, this); - } - -private: - // Indexes indexes; - - SkipList<uint64_t, VertexRecord> vertices; - AtomicCounter<uint64_t> counter; -}; diff --git a/src/template_engine/engine.cpp b/src/template_engine/engine.cpp new file mode 100644 index 000000000..7b0e4ca9b --- /dev/null +++ b/src/template_engine/engine.cpp @@ -0,0 +1,21 @@ +#include "template_engine/engine.hpp" + +#include "utils/string/replace.hpp" + +namespace template_engine +{ + +string TemplateEngine::render(const string& form, const data& partials) +{ + // TODO more optimal implementation + // another option is something like https://github.com/no1msd/mstch + // but it has to be wrapped + string rendered = form; + for (auto partial : partials) { + string key = "{{" + partial.first + "}}"; + rendered = utils::replace(rendered, key, partial.second); + } + return rendered; +} + +} diff --git a/src/template_engine/engine.hpp b/src/template_engine/engine.hpp deleted file mode 100644 index 83e0f1716..000000000 --- a/src/template_engine/engine.hpp +++ /dev/null @@ -1,31 +0,0 @@ -#pragma once - -#include <string> -#include <unordered_map> - -#include "utils/string/replace.hpp" - -namespace template_engine -{ - -using std::string; -using data = std::unordered_map<string, string>; - -class TemplateEngine -{ -public: - string render(const string& form, const data& partials) - { - // TODO more optimal implementation - // another option is something like https://github.com/no1msd/mstch - // but it has to be wrapped - string rendered = form; - for (auto partial : partials) { - string key = "{{" + partial.first + "}}"; - rendered = utils::replace(rendered, key, partial.second); - } - return rendered; - } -}; - -} diff --git a/src/transactions/commit_log.hpp b/src/transactions/commit_log.hpp index be11524df..9f0608668 100644 --- a/src/transactions/commit_log.hpp +++ b/src/transactions/commit_log.hpp @@ -1,7 +1,7 @@ #pragma once -#include "mvcc/id.hpp" #include "data_structures/bitset/dynamic_bitset.hpp" +#include "mvcc/id.hpp" namespace tx { @@ -13,72 +13,41 @@ public: { enum Status { - ACTIVE = 0, // 00 + ACTIVE = 0, // 00 COMMITTED = 1, // 01 - ABORTED = 2, // 10 + ABORTED = 2, // 10 }; - bool is_active() const - { - return flags & ACTIVE; - } + bool is_active() const { return flags & ACTIVE; } - bool is_committed() const - { - return flags & COMMITTED; - } + bool is_committed() const { return flags & COMMITTED; } - bool is_aborted() const - { - return flags & ABORTED; - } + bool is_aborted() const { return flags & ABORTED; } - operator uint8_t() const - { - return flags; - } + operator uint8_t() const { return flags; } uint8_t flags; }; CommitLog() = default; - CommitLog(CommitLog&) = delete; - CommitLog(CommitLog&&) = delete; + CommitLog(CommitLog &) = delete; + CommitLog(CommitLog &&) = delete; CommitLog operator=(CommitLog) = delete; - Info fetch_info(const Id& id) - { - return Info { log.at(2 * id, 2) }; - } + Info fetch_info(const Id &id) { return Info{log.at(2 * id, 2)}; } - bool is_active(const Id& id) - { - return fetch_info(id).is_active(); - } + bool is_active(const Id &id) { return fetch_info(id).is_active(); } - bool is_committed(const Id& id) - { - return fetch_info(id).is_committed(); - } + bool is_committed(const Id &id) { return fetch_info(id).is_committed(); } - void set_committed(const Id& id) - { - log.set(2 * id); - } + void set_committed(const Id &id) { log.set(2 * id); } - bool is_aborted(const Id& id) - { - return fetch_info(id).is_aborted(); - } + bool is_aborted(const Id &id) { return fetch_info(id).is_aborted(); } - void set_aborted(const Id& id) - { - log.set(2 * id + 1); - } + void set_aborted(const Id &id) { log.set(2 * id + 1); } private: DynamicBitset<uint8_t, 32768> log; }; - } diff --git a/src/transactions/engine.hpp b/src/transactions/engine.hpp index cf9174130..e18927cf5 100644 --- a/src/transactions/engine.hpp +++ b/src/transactions/engine.hpp @@ -3,8 +3,8 @@ #include <atomic> #include <vector> -#include "transaction.hpp" -#include "transaction_store.hpp" +#include "transactions/transaction.hpp" +#include "transactions/transaction_store.hpp" #include "commit_log.hpp" #include "utils/counters/simple_counter.hpp" @@ -107,14 +107,4 @@ private: TransactionStore<uint64_t> store; }; -void Transaction::commit() -{ - engine.commit(*this); -} - -void Transaction::abort() -{ - engine.abort(*this); -} - } diff --git a/src/transactions/transaction.cpp b/src/transactions/transaction.cpp new file mode 100644 index 000000000..402c0c519 --- /dev/null +++ b/src/transactions/transaction.cpp @@ -0,0 +1,20 @@ +#include "transactions/transaction.hpp" + +#include "transactions/engine.hpp" + +namespace tx +{ + +Transaction::Transaction(const Id &id, const Snapshot<Id> &snapshot, + Engine &engine) + : id(id), cid(1), snapshot(snapshot), engine(engine) +{ +} + +void Transaction::take_lock(RecordLock &lock) { locks.take(&lock, id); } + +void Transaction::commit() { engine.commit(*this); } + +void Transaction::abort() { engine.abort(*this); } + +} diff --git a/src/transactions/transaction_store.hpp b/src/transactions/transaction_store.hpp index b97c38d8d..e00014536 100644 --- a/src/transactions/transaction_store.hpp +++ b/src/transactions/transaction_store.hpp @@ -3,7 +3,7 @@ #include <map> #include <memory> -#include "transaction.hpp" +#include "transactions/transaction.hpp" namespace tx { diff --git a/src/utils/reference_wrapper.hpp b/src/utils/reference_wrapper.hpp new file mode 100644 index 000000000..3aaedc04e --- /dev/null +++ b/src/utils/reference_wrapper.hpp @@ -0,0 +1,40 @@ +#pragma once + +#include <memory> + +// ReferenceWrapper was created because std::reference_wrapper +// wasn't copyable +// Implementation has been taken from: +// http://en.cppreference.com/w/cpp/utility/functional/reference_wrapper +// TODO: once the c++ implementation will have proper implementation +// this class should be deleted and replaced with std::reference_wrapper + +template <class T> +class ReferenceWrapper +{ +public: + // types + typedef T type; + + // construct/copy/destroy + ReferenceWrapper(T &ref) noexcept : _ptr(std::addressof(ref)) {} + ReferenceWrapper(T &&) = delete; + ReferenceWrapper(const ReferenceWrapper &) noexcept = default; + + // assignment + ReferenceWrapper &operator=(const ReferenceWrapper &x) noexcept = default; + + // access + operator T &() const noexcept { return *_ptr; } + T &get() const noexcept { return *_ptr; } + + // template <class... ArgTypes> + // typename std::result_of<T &(ArgTypes &&...)>::type + // operator()(ArgTypes &&... args) const + // { + // return std::invoke(get(), std::forward<ArgTypes>(args)...); + // } + +private: + T *_ptr; +}; diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 0c4b8f3f4..4a3d0086e 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -2,6 +2,8 @@ cmake_minimum_required(VERSION 3.1) project(memgraph_tests) +set(src_dir ${CMAKE_SOURCE_DIR}/src) + include_directories(${catch_source_dir}/include) ## UNIT TESTS @@ -16,13 +18,13 @@ message(STATUS "Available unit tests are: ${unit_test_names}") file(COPY ${CMAKE_SOURCE_DIR}/tests/data DESTINATION ${CMAKE_BINARY_DIR}/tests) -set(chosen_test "concurrent_skiplist") + # set(chosen_test "concurrent_skiplist") # build unit tests foreach(test ${unit_test_names}) set(test_name unit_${test}) - if (${chosen_test} STREQUAL ${test_name}) - add_executable(${test_name} unit/${test}.cpp) + # if (${chosen_test} STREQUAL ${test_name}) + add_executable(${test_name} unit/${test}.cpp ${src_dir}/template_engine/engine.cpp) # TODO: separate dependencies target_link_libraries(${test_name} stdc++fs) target_link_libraries(${test_name} cypher_lib) @@ -30,7 +32,7 @@ foreach(test ${unit_test_names}) target_link_libraries(${test_name} ${fmt_static_lib}) add_test(NAME ${test_name} COMMAND ${test_name}) set_property(TARGET ${test_name} PROPERTY CXX_STANDARD 14) - endif(${chosen_test} STREQUAL ${test_name}) + # endif(${chosen_test} STREQUAL ${test_name}) endforeach() ## CONCURRENCY TESTS @@ -45,10 +47,10 @@ message(STATUS "Available concurrency tests are: ${concurrency_test_names}") # build concurrency tests foreach(test ${concurrency_test_names}) set(test_name concurrent_${test}) - if (${chosen_test} STREQUAL ${test_name}) + # if (${chosen_test} STREQUAL ${test_name}) add_executable(${test_name} concurrent/${test}.cpp) target_link_libraries(${test_name} Threads::Threads) add_test(NAME ${test_name} COMMAND ${test_name}) set_property(TARGET ${test_name} PROPERTY CXX_STANDARD 14) - endif(${chosen_test} STREQUAL ${test_name}) + # endif(${chosen_test} STREQUAL ${test_name}) endforeach() diff --git a/tests/unit/skiplist.cpp b/tests/unit/concurrent_map.cpp similarity index 94% rename from tests/unit/skiplist.cpp rename to tests/unit/concurrent_map.cpp index 1fcf37e27..5667fccb0 100644 --- a/tests/unit/skiplist.cpp +++ b/tests/unit/concurrent_map.cpp @@ -1,12 +1,12 @@ #include <iostream> -#include "data_structures/skiplist/skiplist.hpp" +#include "data_structures/concurrent/concurrent_map.hpp" #include "utils/assert.hpp" using std::cout; using std::endl; -using skiplist_t = SkipList<int, int>; +using skiplist_t = ConcurrentMap<int, int>; void print_skiplist(const skiplist_t::Accessor &skiplist) { diff --git a/tests/unit/concurrent_set.cpp b/tests/unit/concurrent_set.cpp new file mode 100644 index 000000000..cd0601b21 --- /dev/null +++ b/tests/unit/concurrent_set.cpp @@ -0,0 +1,58 @@ +#include <iostream> + +#include "data_structures/concurrent/concurrent_set.hpp" +#include "utils/assert.hpp" + +using std::cout; +using std::endl; + +void print_skiplist(const ConcurrentSet<int>::Accessor &skiplist) +{ + cout << "---- skiplist set now has: "; + + for (auto &item : skiplist) + cout << item << ", "; + + cout << "----" << endl; +} + +int main(void) +{ + ConcurrentSet<int> set; + + auto accessor = set.access(); + + cout << std::boolalpha; + + permanent_assert(accessor.insert(1).second == true, + "added non-existing 1? (true)"); + + permanent_assert(accessor.insert(1).second == false, + "added already existing 1? (false)"); + + permanent_assert(accessor.insert(2).second == true, + "added non-existing 2? (true)"); + + permanent_assert(accessor.find(3) == accessor.end(), + "item 3 doesn't exist? (true)"); + + permanent_assert(accessor.contains(3) == false, "item 3 exists? (false)"); + + permanent_assert(accessor.find(2) != accessor.end(), + "item 2 exists? (true)"); + + permanent_assert(*accessor.find(2) == 2, "find item 2"); + + permanent_assert(accessor.remove(1) == true, "removed existing 1? (true)"); + + permanent_assert(accessor.remove(3) == false, + "try to remove non existing element"); + + permanent_assert(accessor.insert(1).second == true, "add 1 again"); + + permanent_assert(accessor.insert(4).second == true, "add 4"); + + print_skiplist(accessor); + + return 0; +} diff --git a/tests/unit/db_index.cpp b/tests/unit/db_index.cpp.old similarity index 100% rename from tests/unit/db_index.cpp rename to tests/unit/db_index.cpp.old diff --git a/tests/unit/template_engine.cpp b/tests/unit/template_engine.cpp new file mode 100644 index 000000000..16072ca1d --- /dev/null +++ b/tests/unit/template_engine.cpp @@ -0,0 +1,13 @@ +#define CATCH_CONFIG_MAIN +#include "catch.hpp" + +#include "template_engine/engine.hpp" + +TEST_CASE("Template Engine - basic placeholder replacement") +{ + template_engine::TemplateEngine engine; + auto rendered = + engine.render("{{one}} {{two}}", {{"one", "two"}, {"two", "one"}}); + + REQUIRE(rendered == "two one"); +}