From b93bab375ed71a6f61c805d2fa9e18538f5c0957 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Dominik=20Tomic=CC=8Cevic=CC=81?=
 <dominik.tomicevic@gmail.com>
Date: Sat, 2 Jan 2016 12:20:51 +0100
Subject: [PATCH] record accessor, indexes and cursors

---
 data_structures/skiplist/skiplist.hpp         |  5 +
 data_structures/skiplist/skiplistset.hpp      |  5 +
 mvcc/version_list.hpp                         | 82 +++++++++------
 storage/cursor.hpp                            | 99 +++++++++++++++++++
 storage/indexes/index.hpp                     | 25 +++++
 storage/indexes/index_key.hpp                 | 24 +++++
 storage/indexes/keys/non_unique_key.hpp       | 16 +++
 storage/indexes/keys/unique_key.hpp           | 40 ++++++++
 storage/indexes/sort_order.hpp                | 19 ++++
 storage/label_store.hpp                       | 24 +++++
 {database => storage}/locking/lock_status.hpp |  0
 {database => storage}/locking/record_lock.hpp |  0
 storage/model/label.hpp                       | 17 ++--
 storage/model/label_list.hpp                  |  7 +-
 storage/model/properties/properties.hpp       |  7 +-
 storage/model/record.hpp                      | 24 -----
 storage/record_accessor.hpp                   | 61 ++++++++++++
 storage/vertex.hpp                            |  2 +
 storage/vertex_accessor.hpp                   | 11 +++
 storage/vertices.hpp                          | 57 +----------
 20 files changed, 397 insertions(+), 128 deletions(-)
 create mode 100644 storage/cursor.hpp
 create mode 100644 storage/indexes/index.hpp
 create mode 100644 storage/indexes/index_key.hpp
 create mode 100644 storage/indexes/keys/non_unique_key.hpp
 create mode 100644 storage/indexes/keys/unique_key.hpp
 create mode 100644 storage/indexes/sort_order.hpp
 create mode 100644 storage/label_store.hpp
 rename {database => storage}/locking/lock_status.hpp (100%)
 rename {database => storage}/locking/record_lock.hpp (100%)
 delete mode 100644 storage/model/record.hpp
 create mode 100644 storage/record_accessor.hpp
 create mode 100644 storage/vertex_accessor.hpp

diff --git a/data_structures/skiplist/skiplist.hpp b/data_structures/skiplist/skiplist.hpp
index 2446ab00f..6dce15f12 100644
--- a/data_structures/skiplist/skiplist.hpp
+++ b/data_structures/skiplist/skiplist.hpp
@@ -402,6 +402,11 @@ public:
         return Accessor(this);
     }
 
+    Accessor access() const
+    {
+        return Accessor(this);
+    }
+
 private:
     using guard_t = std::unique_lock<lock_t>;
 
diff --git a/data_structures/skiplist/skiplistset.hpp b/data_structures/skiplist/skiplistset.hpp
index abf3f298d..bf77607d5 100644
--- a/data_structures/skiplist/skiplistset.hpp
+++ b/data_structures/skiplist/skiplistset.hpp
@@ -224,6 +224,11 @@ public:
         return Accessor(std::move(data.access()));
     }
 
+    Accessor access() const
+    {
+        return Accessor(std::move(data.access()));
+    }
+
 private:
     SkipList<Key, T> data;
 };
diff --git a/mvcc/version_list.hpp b/mvcc/version_list.hpp
index 8971aa757..b6f44dd0d 100644
--- a/mvcc/version_list.hpp
+++ b/mvcc/version_list.hpp
@@ -5,7 +5,7 @@
 
 #include "memory/lazy_gc.hpp"
 #include "mvcc/serialization_error.hpp"
-#include "database/locking/record_lock.hpp"
+#include "storage/locking/record_lock.hpp"
 
 namespace mvcc
 {
@@ -22,16 +22,16 @@ public:
     {
         friend class VersionList<T>;
 
-        Accessor(tx::Transaction& transaction, VersionList<T>& record)
-            : transaction(transaction), record(record)
+        Accessor(tx::Transaction& transaction, VersionList<T>& vlist)
+            : transaction(transaction), vlist(vlist)
         {
-            record.add_ref();
+            vlist.add_ref();
         }
 
     public:
         ~Accessor()
         {
-            record.release_ref();
+            vlist.release_ref();
         }
 
         Accessor(const Accessor&) = default;
@@ -42,27 +42,42 @@ public:
 
         T* insert()
         {
-            return record.insert(transaction);
+            return vlist.insert(transaction);
         }
 
         const T* find() const
         {
-            return record.find(transaction);
+            return vlist.find(transaction);
         }
 
         T* update()
         {
-            return record.update(transaction);
+            return vlist.update(transaction);
+        }
+
+        T* update(T* record)
+        {
+            return vlist.update(record, transaction);
         }
 
         bool remove()
         {
-            return record.remove(transaction);
+            return vlist.remove(transaction);
+        }
+
+        bool remove(T* record)
+        {
+            return vlist.remove(record, transaction);
+        }
+
+        const Id& id() const
+        {
+            return vlist.id;
         }
 
     private:
         tx::Transaction& transaction;
-        VersionList<T>& record;
+        VersionList<T>& vlist;
     };
 
     VersionList() = default;
@@ -117,6 +132,9 @@ public:
 private:
     std::atomic<T*> head {nullptr};
     RecordLock lock;
+
+    Id id;
+
     //static Recycler recycler;
 
     T* find(const tx::Transaction& t)
@@ -139,9 +157,10 @@ private:
         return r;
     }
 
-    T* insert(tx::Transaction& t)
+    T* insert(tx::Transaction& t, const Id& id)
     {
         assert(head == nullptr);
+        this->id = id;
 
         // create a first version of the record
         // TODO replace 'new' with something better
@@ -158,12 +177,20 @@ private:
     T* update(tx::Transaction& t)
     {
         assert(head != nullptr);
-        auto record = lock_and_validate(t);
+        auto record = find(t);
 
         // check if we found any visible records
         if(!record)
             return nullptr;
 
+        return update(record, t);
+    }
+
+    T* update(T* record, tx::Transaction& t)
+    {
+        assert(record != nullptr);
+        lock_and_validate(record, t);
+
         auto updated = new T();
         updated->data = record->data;
 
@@ -179,15 +206,23 @@ private:
     bool remove(tx::Transaction& t)
     {
         assert(head != nullptr);
-        auto record = lock_and_validate(t);
+        auto record = find(t);
 
         if(!record)
             return false;
 
-        return record->mark_deleted(t), true;
+        lock_and_validate(record, t);
+        return remove(record, t), true;
     }
 
-    T* lock_and_validate(T* record, tx::Transaction& t)
+    void remove(T* record, tx::Transaction& t)
+    {
+        assert(record != nullptr);
+        lock_and_validate(record, t);
+        record->mark_deleted(t);
+    }
+
+    void lock_and_validate(T* record, tx::Transaction& t)
     {
         assert(record != nullptr);
         assert(record == find(t));
@@ -198,27 +233,12 @@ private:
         // if the record hasn't been deleted yet or the deleting transaction
         // has aborted, it's ok to modify it
         if(!record->tx.exp() || record->hints.load().exp.is_aborted())
-            return record;
+            return;
 
         // if it committed, then we have a serialization conflict
         assert(record->hints.load().exp.is_committed());
         throw SerializationError();
     }
-
-    T* lock_and_validate(tx::Transaction& t)
-    {
-        // find the visible record version to delete
-        auto record = find(t);
-        return record == nullptr ? nullptr : lock_and_validate(record, t);
-    }
 };
 
 }
-
-// these are convenient typedefs to use in other contexes to make the code a
-// bit clearer
-class Vertex;
-class Edge;
-
-using VertexRecord = mvcc::VersionList<Vertex>;
-using EdgeRecord = mvcc::VersionList<Edge>;
diff --git a/storage/cursor.hpp b/storage/cursor.hpp
new file mode 100644
index 000000000..6960e9357
--- /dev/null
+++ b/storage/cursor.hpp
@@ -0,0 +1,99 @@
+#pragma once
+
+#include "transactions/transaction.hpp"
+#include "mvcc/version_list.hpp"
+#include "storage/model/properties/property.hpp"
+
+#include "storage/vertex.hpp"
+
+template <class T, class Accessor, class It, class Derived>
+class RecordCursor : public Crtp<Derived>
+{
+public:
+    RecordCursor(Accessor&& accessor, It item, tx::Transaction& t)
+        : accessor(accessor), item(item), t(t)
+    {
+
+    }
+
+    // |------------------------- data operations -------------------------|
+
+    const Property* property(const std::string& key) const
+    {
+        return record->props.at(key);
+    }
+
+    template <class V, class... Args>
+    void property(const std::string& key, Args&&... args)
+    {
+        record->props.template set<V>(key, std::forward<Args>(args)...);
+    }
+
+    // |------------------------ record operations ------------------------|
+
+    Derived& update()
+    {
+        record = item->update(t);
+        return this->derived();
+    }
+
+    Derived& remove()
+    {
+        item->remove(t);
+        return this->derived();
+    }
+
+    // |-------------------------- iterator impl --------------------------|
+
+    Derived& operator++()
+    {
+        ++item;
+    }
+
+    Derived& operator++(int)
+    {
+        return operator++();
+    }
+
+    friend constexpr bool operator==(const Derived& a, const Derived& b)
+    {
+        return a.record == b.record;
+    }
+
+    friend constexpr bool operator!=(const Derived& a, const Derived& b)
+    {
+        return !(a == b);
+    }
+
+    const T& operator*() const
+    {
+        assert(record != nullptr);
+        return *record;
+    }
+
+    const T* operator->() const
+    {
+        assert(record != nullptr);
+        return record;
+    }
+
+    operator const T&() const
+    {
+        return *record;
+    }
+
+protected:
+    Accessor accessor;
+    tx::Transaction& t;
+    It item;
+
+    T* record;
+};
+
+template <class It, class Accessor>
+class Vertex::Cursor
+    : public RecordCursor<Vertex, Accessor, It, Cursor<It, Accessor>>
+{
+public:
+
+};
diff --git a/storage/indexes/index.hpp b/storage/indexes/index.hpp
new file mode 100644
index 000000000..14189637f
--- /dev/null
+++ b/storage/indexes/index.hpp
@@ -0,0 +1,25 @@
+#pragma once
+
+#include "data_structures/skiplist/skiplist.hpp"
+#include "keys/unique_key.hpp"
+
+#include "storage/cursor.hpp"
+
+template <class Key, class Cursor, class Item, class SortOrder>
+class Index
+{
+    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;
+
+public:
+    Cursor insert(const K& key, Item* item, tx::Transaction& t)
+    {
+
+    }
+
+
+private:
+    skiplist_t skiplist;
+};
diff --git a/storage/indexes/index_key.hpp b/storage/indexes/index_key.hpp
new file mode 100644
index 000000000..6bb7689ec
--- /dev/null
+++ b/storage/indexes/index_key.hpp
@@ -0,0 +1,24 @@
+#pragma once
+
+#include "utils/total_ordering.hpp"
+
+#include "ordering.hpp"
+#include "storage/model/properties/properties.hpp"
+#include "storage/model/properties/property.hpp"
+
+template <class Ordering>
+class UniqueIndexKey
+{
+public:
+    namespace
+
+private:
+    Property& key;
+};
+
+template <class Orderingt>
+class IndexKey
+{
+public:
+
+};
diff --git a/storage/indexes/keys/non_unique_key.hpp b/storage/indexes/keys/non_unique_key.hpp
new file mode 100644
index 000000000..dd64b0264
--- /dev/null
+++ b/storage/indexes/keys/non_unique_key.hpp
@@ -0,0 +1,16 @@
+#pragma once
+
+#include "unique_key.hpp"
+
+template <class K, class T, class SortOrder>
+class NonUniqueKey
+{
+public:
+    NonUniqueKey(const K& key, const T&)
+    {
+
+    }
+
+private:
+    intptr_t x;
+};
diff --git a/storage/indexes/keys/unique_key.hpp b/storage/indexes/keys/unique_key.hpp
new file mode 100644
index 000000000..2917b195d
--- /dev/null
+++ b/storage/indexes/keys/unique_key.hpp
@@ -0,0 +1,40 @@
+#pragma once
+
+#include "utils/total_ordering.hpp"
+
+#include "storage/indexes/sort_order.hpp"
+#include "storage/model/properties/properties.hpp"
+#include "storage/model/properties/property.hpp"
+
+template <class K, class SortOrder>
+class UniqueKey : public TotalOrdering<UniqueKey<K, SortOrder>>
+{
+public:
+    using type = UniqueKey<K, SortOrder>;
+    using key_t = K;
+
+    UniqueKey(const K& key) : key(key) {}
+
+    friend constexpr bool operator<(const type& lhs, const type& rhs)
+    {
+        return sort_order(lhs.key, rhs.key);
+    }
+
+    friend constexpr bool operator==(const type& lhs, const type& rhs)
+    {
+        return lhs.key == rhs.key;
+    }
+
+    operator const K&() const { return key; }
+
+private:
+    static constexpr SortOrder sort_order = SortOrder();
+    const K& key;
+};
+
+template <class K>
+using UniqueKeyAsc = UniqueKey<K, Ascending<K>>;
+
+template <class K>
+using UniqueKeyDesc = UniqueKey<K, Descending<K>>;
+
diff --git a/storage/indexes/sort_order.hpp b/storage/indexes/sort_order.hpp
new file mode 100644
index 000000000..c30ee02eb
--- /dev/null
+++ b/storage/indexes/sort_order.hpp
@@ -0,0 +1,19 @@
+#pragma once
+
+template <class T>
+class Ascending
+{
+    constexpr bool operator()(const T& lhs, const T& rhs) const
+    {
+        return lhs < rhs;
+    }
+};
+
+template <class T>
+class Descending
+{
+    constexpr bool operator()(const T& lhs, const T& rhs) const
+    {
+        return lhs > rhs;
+    }
+};
diff --git a/storage/label_store.hpp b/storage/label_store.hpp
new file mode 100644
index 000000000..f40e37a8b
--- /dev/null
+++ b/storage/label_store.hpp
@@ -0,0 +1,24 @@
+#pragma once
+
+#include "model/label.hpp"
+#include "data_structures/skiplist/skiplistset.hpp"
+
+class LabelStore
+{
+public:
+
+    const Label& find_or_create(const std::string& name)
+    {
+        auto accessor = labels.access();
+        return accessor.insert(Label(name)).first;
+    }
+
+    bool contains(const std::string& name) const
+    {
+        auto accessor = labels.access();
+        return accessor.find(Label(name)) != accessor.end();
+    }
+
+private:
+    SkipListSet<Label> labels;
+};
diff --git a/database/locking/lock_status.hpp b/storage/locking/lock_status.hpp
similarity index 100%
rename from database/locking/lock_status.hpp
rename to storage/locking/lock_status.hpp
diff --git a/database/locking/record_lock.hpp b/storage/locking/record_lock.hpp
similarity index 100%
rename from database/locking/record_lock.hpp
rename to storage/locking/record_lock.hpp
diff --git a/storage/model/label.hpp b/storage/model/label.hpp
index 46ffdcb83..3a4f2b02e 100644
--- a/storage/model/label.hpp
+++ b/storage/model/label.hpp
@@ -7,29 +7,32 @@
 class Label : public TotalOrdering<Label>
 {
 public:
-    Label(const std::string& id) : id(id) {}
-    Label(std::string&& id) : id(std::move(id)) {}
+    Label(const std::string& name) : name(name) {}
+    Label(std::string&& name) : name(std::move(name)) {}
+
+    Label(const Label&) = default;
+    Label(Label&&) = default;
 
     friend bool operator<(const Label& lhs, const Label& rhs)
     {
-        return lhs.id < rhs.id;
+        return lhs.name < rhs.name;
     }
 
     friend bool operator==(const Label& lhs, const Label& rhs)
     {
-        return lhs.id == rhs.id;
+        return lhs.name == rhs.name;
     }
 
     friend std::ostream& operator<<(std::ostream& stream, const Label& label)
     {
-        return stream << label.id;
+        return stream << label.name;
     }
 
     operator const std::string&() const
     {
-        return id;
+        return name;
     }
 
 private:
-    std::string id;
+    std::string name;
 };
diff --git a/storage/model/label_list.hpp b/storage/model/label_list.hpp
index f72dfe824..a9d123d8c 100644
--- a/storage/model/label_list.hpp
+++ b/storage/model/label_list.hpp
@@ -14,11 +14,6 @@ public:
     auto end() const { return labels.end(); }
     auto cend() const { return labels.end(); }
 
-    bool add(Label&& label)
-    {
-        return labels.insert(std::move(label)).second;
-    }
-
     bool add(const Label& label)
     {
         return labels.insert(label).second;
@@ -50,5 +45,5 @@ public:
     }
 
 private:
-    std::set<Label> labels;
+    std::set<const Label&> labels;
 };
diff --git a/storage/model/properties/properties.hpp b/storage/model/properties/properties.hpp
index 2d8c328b7..d3255e5c6 100644
--- a/storage/model/properties/properties.hpp
+++ b/storage/model/properties/properties.hpp
@@ -9,12 +9,7 @@ class Properties
     using props_t = std::map<std::string, Property::sptr>;
 
 public:
-    props_t::iterator find(const std::string& key)
-    {
-        return props.find(key);
-    }
-
-    Property* at(const std::string& key)
+    const Property* at(const std::string& key) const
     {
         auto it = props.find(key);
         return it == props.end() ? nullptr : it->second.get();
diff --git a/storage/model/record.hpp b/storage/model/record.hpp
deleted file mode 100644
index 7c9c128ac..000000000
--- a/storage/model/record.hpp
+++ /dev/null
@@ -1,24 +0,0 @@
-#pragma once
-
-#include <ostream>
-#include <mutex>
-#include <set>
-
-#include "utils/crtp.hpp"
-
-#include "threading/sync/spinlock.hpp"
-
-#include "mvcc/mvcc.hpp"
-
-#include "properties/properties.hpp"
-
-template <class Derived>
-class Record : public Crtp<Derived>, public mvcc::Mvcc<Derived>
-{
-public:
-    // a record contains a key value map containing data
-    Properties properties;
-
-    // each record can have one or more distinct labels.
-    // std::set<uint16_t> labels;
-};
diff --git a/storage/record_accessor.hpp b/storage/record_accessor.hpp
new file mode 100644
index 000000000..567fc5669
--- /dev/null
+++ b/storage/record_accessor.hpp
@@ -0,0 +1,61 @@
+#pragma once
+
+#include "transactions/transaction.hpp"
+#include "mvcc/version_list.hpp"
+#include "storage/model/properties/property.hpp"
+
+template <class T, class Store, class Derived>
+class RecordAccessor
+{
+protected:
+    using vlist_t = mvcc::VersionList<T>;
+
+public:
+    RecordAccessor() = default;
+
+    RecordAccessor(T* record, vlist_t* vlist, Store* store)
+        : record(record), vlist(vlist), store(store) {}
+
+    const Id& id() const
+    {
+        auto accessor = vlist->access();
+        return accessor.id();
+    }
+
+    bool empty() const
+    {
+        return record == nullptr;
+    }
+
+    Derived update(tx::Transaction& t) const
+    {
+        assert(!empty());
+
+        auto accessor = vlist->access();
+        return Derived(accessor->update(t), vlist, store);
+    }
+
+    bool remove(tx::Transaction& t) const
+    {
+        assert(!empty());
+
+        auto accessor = vlist->access();
+        return accessor->remove(t);
+    }
+
+    const Property* property(const std::string& key) const
+    {
+        return record->props.at(key);
+    }
+
+    template <class V, class... Args>
+    void property(const std::string& key, Args&&... args)
+    {
+        record->props.template set<V>(key, std::forward<Args>(args)...);
+    }
+
+protected:
+    T* const record {nullptr};
+    vlist_t* const vlist {nullptr};
+    Store* const store {nullptr};
+};
diff --git a/storage/vertex.hpp b/storage/vertex.hpp
index c1b3d5075..812b79060 100644
--- a/storage/vertex.hpp
+++ b/storage/vertex.hpp
@@ -7,6 +7,8 @@
 class Vertex : public mvcc::Record<Vertex>
 {
 public:
+    class Accessor;
+
     Vertex() = default;
     Vertex(const VertexModel& data) : data(data) {}
     Vertex(VertexModel&& data) : data(std::move(data)) {}
diff --git a/storage/vertex_accessor.hpp b/storage/vertex_accessor.hpp
new file mode 100644
index 000000000..b02c4baaa
--- /dev/null
+++ b/storage/vertex_accessor.hpp
@@ -0,0 +1,11 @@
+#pragma once
+
+#include "record_accessor.hpp"
+#include "vertex.hpp"
+#include "vertices.hpp"
+
+class Vertex::Accessor : public RecordAccessor<Vertex, Vertices, Accessor>
+{
+public:
+    using RecordAccessor::RecordAccessor;
+};
diff --git a/storage/vertices.hpp b/storage/vertices.hpp
index c4b862259..3df3bfc45 100644
--- a/storage/vertices.hpp
+++ b/storage/vertices.hpp
@@ -26,60 +26,9 @@ public:
         return vertex;
     }
 
-    VertexProxy insert(tx::Transaction& transaction)
-    {
-        // get next vertex id
-        auto next = counter.next(std::memory_order_acquire);
-
-        // create new vertex record
-        VertexRecord vertex_record;
-
-        // 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_accessor = inserted_vertex_record->second.access(transaction);
-        auto vertex = vertex_accessor.insert();
-
-        VertexProxy vertex_proxy(next, vertex, this, &inserted_vertex_record->second);
-        return vertex_proxy;
-    }
-
-    Vertex* update(tx::Transaction& transaction, const Id& id)
-    {
-        // find vertex record
-        auto vertices_accessor = vertices.access();
-        auto vertex_record = vertices_accessor.find(id);
-
-        if (vertex_record == vertices_accessor.end())
-            return nullptr;
-
-        // get vertex that is going to be updated
-        auto vertex_accessor = vertex_record->second.access(transaction);
-        auto vertex = vertex_accessor.update();
-
-        return vertex;
-    }
-
-    bool remove(tx::Transaction& transaction, const Id& id)
-    {
-        // find vertex record
-        auto vertices_accessor = vertices.access();
-        auto vertex_record = vertices_accessor.find(id);
-
-        if (vertex_record == vertices_accessor.end())
-            return false;
-
-        // mark vertex record via the vertex accessor as deleted
-        // return boolean result true if vertex could be removed
-        // or false if vertex couldn't be removed
-        auto vertex_accessor = vertex_record->second.access(transaction);
-        return vertex_accessor.remove();
-    }
-
 private:
-    SkipList<uint64_t, VertexRecord> vertices;
+    Indexes indexes;
+
+    SkipList<uint64_t, VersionList<Vertex>> vertices;
     AtomicCounter<uint64_t> counter;
 };