record accessor, indexes and cursors
This commit is contained in:
parent
c495872b22
commit
b93bab375e
@ -402,6 +402,11 @@ public:
|
||||
return Accessor(this);
|
||||
}
|
||||
|
||||
Accessor access() const
|
||||
{
|
||||
return Accessor(this);
|
||||
}
|
||||
|
||||
private:
|
||||
using guard_t = std::unique_lock<lock_t>;
|
||||
|
||||
|
@ -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;
|
||||
};
|
||||
|
@ -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>;
|
||||
|
99
storage/cursor.hpp
Normal file
99
storage/cursor.hpp
Normal file
@ -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:
|
||||
|
||||
};
|
25
storage/indexes/index.hpp
Normal file
25
storage/indexes/index.hpp
Normal file
@ -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;
|
||||
};
|
24
storage/indexes/index_key.hpp
Normal file
24
storage/indexes/index_key.hpp
Normal file
@ -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:
|
||||
|
||||
};
|
16
storage/indexes/keys/non_unique_key.hpp
Normal file
16
storage/indexes/keys/non_unique_key.hpp
Normal file
@ -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;
|
||||
};
|
40
storage/indexes/keys/unique_key.hpp
Normal file
40
storage/indexes/keys/unique_key.hpp
Normal file
@ -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>>;
|
||||
|
19
storage/indexes/sort_order.hpp
Normal file
19
storage/indexes/sort_order.hpp
Normal file
@ -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;
|
||||
}
|
||||
};
|
24
storage/label_store.hpp
Normal file
24
storage/label_store.hpp
Normal file
@ -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;
|
||||
};
|
@ -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;
|
||||
};
|
||||
|
@ -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;
|
||||
};
|
||||
|
@ -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();
|
||||
|
@ -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;
|
||||
};
|
61
storage/record_accessor.hpp
Normal file
61
storage/record_accessor.hpp
Normal file
@ -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};
|
||||
};
|
@ -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)) {}
|
||||
|
11
storage/vertex_accessor.hpp
Normal file
11
storage/vertex_accessor.hpp
Normal file
@ -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;
|
||||
};
|
@ -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;
|
||||
};
|
||||
|
Loading…
Reference in New Issue
Block a user