Initial version of mvcc_gc.
Summary: Tested MVCC garbage collector. Also refactors graph_db forward declarations of vertex and edge which were causing issues. Reviewers: mislav.bradac, dtomicevic, florijan, teon.banek, buda Reviewed By: teon.banek, buda Subscribers: pullbot Differential Revision: https://phabricator.memgraph.io/D177
This commit is contained in:
@@ -14,8 +14,9 @@ template_cpp_path: "./template/plan_template_cpp"
|
||||
# path to the folder with snapshots
|
||||
snapshots_path: "snapshots"
|
||||
|
||||
# leaning cycle interval
|
||||
cleaning_cycle_sec: "300"
|
||||
# cleaning cycle interval
|
||||
# if set to -1 the GC will not run
|
||||
cleaning_cycle_sec: "30"
|
||||
|
||||
# snapshot cycle interval
|
||||
snapshot_cycle_sec: "60"
|
||||
|
||||
@@ -1,10 +1,22 @@
|
||||
|
||||
#include "database/graph_db.hpp"
|
||||
#include <storage/edge.hpp>
|
||||
#include "config/config.hpp"
|
||||
#include "database/creation_exception.hpp"
|
||||
#include "logging/logger.hpp"
|
||||
#include "storage/edge.hpp"
|
||||
#include "storage/garbage_collector.hpp"
|
||||
//#include "snapshot/snapshoter.hpp"
|
||||
|
||||
GraphDb::GraphDb(const std::string &name, bool import_snapshot) : name_(name) {
|
||||
const int DEFAULT_CLEANING_CYCLE_SEC = 30; // 30 seconds
|
||||
|
||||
GraphDb::GraphDb(const std::string &name, bool import_snapshot)
|
||||
: name_(name),
|
||||
gc_vertices_(&vertices_, &tx_engine),
|
||||
gc_edges_(&edges_, &tx_engine) {
|
||||
const std::string timeStr = CONFIG(config::CLEANING_CYCLE_SEC);
|
||||
int pause = DEFAULT_CLEANING_CYCLE_SEC;
|
||||
if (!timeStr.empty()) pause = stoll(timeStr);
|
||||
this->gc_edges_.Run(std::chrono::seconds(pause));
|
||||
this->gc_vertices_.Run(std::chrono::seconds(pause));
|
||||
// if (import_snapshot)
|
||||
// snap_engine.import();
|
||||
}
|
||||
|
||||
@@ -1,22 +1,22 @@
|
||||
#pragma once
|
||||
|
||||
#include <thread>
|
||||
|
||||
#include "data_structures/concurrent/concurrent_set.hpp"
|
||||
#include "data_structures/concurrent/skiplist.hpp"
|
||||
#include "database/graph_db_datatypes.hpp"
|
||||
#include "mvcc/version_list.hpp"
|
||||
#include "storage/edge.hpp"
|
||||
#include "storage/garbage_collector.hpp"
|
||||
#include "storage/unique_object_store.hpp"
|
||||
#include "storage/vertex.hpp"
|
||||
#include "transactions/engine.hpp"
|
||||
#include "utils/pass_key.hpp"
|
||||
|
||||
// forward declaring Edge and Vertex because they use
|
||||
// GraphDb::Label etc., and therefore include this header
|
||||
class Vertex;
|
||||
class Edge;
|
||||
|
||||
// TODO: Maybe split this in another layer between Db and Dbms. Where the new
|
||||
// layer would hold SnapshotEngine and his kind of concept objects. Some
|
||||
// guidelines would be: retain objects which are necessary to implement querys
|
||||
// in Db, the rest can be moved to the new layer.
|
||||
|
||||
/**
|
||||
* Main class which represents Database concept in code.
|
||||
* This class is essentially a data structure. It exposes
|
||||
@@ -25,11 +25,6 @@ class Edge;
|
||||
*/
|
||||
class GraphDb {
|
||||
public:
|
||||
// definitions for what data types are used for a Label, Property, EdgeType
|
||||
using Label = std::string *;
|
||||
using EdgeType = std::string *;
|
||||
using Property = std::string *;
|
||||
|
||||
/**
|
||||
* Construct database with a custom name.
|
||||
*
|
||||
@@ -61,8 +56,11 @@ class GraphDb {
|
||||
// main storage for the graph
|
||||
SkipList<mvcc::VersionList<Vertex> *> vertices_;
|
||||
SkipList<mvcc::VersionList<Edge> *> edges_;
|
||||
GarbageCollector<Vertex> gc_vertices_;
|
||||
GarbageCollector<Edge> gc_edges_;
|
||||
|
||||
// unique object stores
|
||||
// TODO this should be also garbage collected
|
||||
ConcurrentSet<std::string> labels_;
|
||||
ConcurrentSet<std::string> edge_types_;
|
||||
ConcurrentSet<std::string> properties_;
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
#include "storage/vertex_accessor.hpp"
|
||||
#include "utils/assert.hpp"
|
||||
|
||||
GraphDbAccessor::GraphDbAccessor(GraphDb& db)
|
||||
GraphDbAccessor::GraphDbAccessor(GraphDb &db)
|
||||
: db_(db), transaction_(db.tx_engine.begin()) {}
|
||||
|
||||
GraphDbAccessor::~GraphDbAccessor() {
|
||||
@@ -16,7 +16,7 @@ GraphDbAccessor::~GraphDbAccessor() {
|
||||
}
|
||||
}
|
||||
|
||||
const std::string& GraphDbAccessor::name() const { return db_.name_; }
|
||||
const std::string &GraphDbAccessor::name() const { return db_.name_; }
|
||||
|
||||
void GraphDbAccessor::advance_command() {
|
||||
transaction_->engine.advance(transaction_->id);
|
||||
@@ -38,8 +38,8 @@ void GraphDbAccessor::abort() {
|
||||
|
||||
VertexAccessor GraphDbAccessor::insert_vertex() {
|
||||
// create a vertex
|
||||
auto vertex_vlist = new mvcc::VersionList<Vertex>();
|
||||
Vertex* vertex = vertex_vlist->insert(*transaction_);
|
||||
Vertex *vertex = nullptr;
|
||||
auto vertex_vlist = new mvcc::VersionList<Vertex>(*transaction_, vertex);
|
||||
|
||||
// insert the newly created record into the main storage
|
||||
// TODO make the number of tries configurable
|
||||
@@ -52,7 +52,7 @@ VertexAccessor GraphDbAccessor::insert_vertex() {
|
||||
throw CreationException("Unable to create a Vertex after 5 attempts");
|
||||
}
|
||||
|
||||
bool GraphDbAccessor::remove_vertex(VertexAccessor& vertex_accessor) {
|
||||
bool GraphDbAccessor::remove_vertex(VertexAccessor &vertex_accessor) {
|
||||
// TODO consider if this works well with MVCC
|
||||
if (vertex_accessor.out_degree() > 0 || vertex_accessor.in_degree() > 0)
|
||||
return false;
|
||||
@@ -61,7 +61,7 @@ bool GraphDbAccessor::remove_vertex(VertexAccessor& vertex_accessor) {
|
||||
return true;
|
||||
}
|
||||
|
||||
void GraphDbAccessor::detach_remove_vertex(VertexAccessor& vertex_accessor) {
|
||||
void GraphDbAccessor::detach_remove_vertex(VertexAccessor &vertex_accessor) {
|
||||
// removing edges via accessors is both safe
|
||||
// and it should remove all the pointers in the relevant
|
||||
// vertices (including this one)
|
||||
@@ -70,13 +70,13 @@ void GraphDbAccessor::detach_remove_vertex(VertexAccessor& vertex_accessor) {
|
||||
for (auto edge_accessor : vertex_accessor.out()) remove_edge(edge_accessor);
|
||||
}
|
||||
|
||||
EdgeAccessor GraphDbAccessor::insert_edge(VertexAccessor& from,
|
||||
VertexAccessor& to,
|
||||
GraphDb::EdgeType edge_type) {
|
||||
EdgeAccessor GraphDbAccessor::insert_edge(VertexAccessor &from,
|
||||
VertexAccessor &to,
|
||||
GraphDbTypes::EdgeType edge_type) {
|
||||
// create an edge
|
||||
auto edge_vlist = new mvcc::VersionList<Edge>();
|
||||
Edge* edge =
|
||||
edge_vlist->insert(*transaction_, *from.vlist_, *to.vlist_, edge_type);
|
||||
Edge *edge = nullptr;
|
||||
auto edge_vlist = new mvcc::VersionList<Edge>(
|
||||
*transaction_, edge, *from.vlist_, *to.vlist_, edge_type);
|
||||
|
||||
// set the vertex connections to this edge
|
||||
from.update().out_.emplace_back(edge_vlist);
|
||||
@@ -97,43 +97,45 @@ EdgeAccessor GraphDbAccessor::insert_edge(VertexAccessor& from,
|
||||
* Removes the given edge pointer from a vector of pointers.
|
||||
* Does NOT maintain edge pointer ordering (for efficiency).
|
||||
*/
|
||||
void swap_out_edge(std::vector<mvcc::VersionList<Edge>*>& edges,
|
||||
mvcc::VersionList<Edge>* edge) {
|
||||
void swap_out_edge(std::vector<mvcc::VersionList<Edge> *> &edges,
|
||||
mvcc::VersionList<Edge> *edge) {
|
||||
auto found = std::find(edges.begin(), edges.end(), edge);
|
||||
debug_assert(found != edges.end(), "Edge doesn't exist.");
|
||||
std::swap(*found, edges.back());
|
||||
edges.pop_back();
|
||||
}
|
||||
|
||||
void GraphDbAccessor::remove_edge(EdgeAccessor& edge_accessor) {
|
||||
void GraphDbAccessor::remove_edge(EdgeAccessor &edge_accessor) {
|
||||
swap_out_edge(edge_accessor.from().update().out_, edge_accessor.vlist_);
|
||||
swap_out_edge(edge_accessor.to().update().in_, edge_accessor.vlist_);
|
||||
edge_accessor.vlist_->remove(&edge_accessor.update(), *transaction_);
|
||||
}
|
||||
|
||||
GraphDb::Label GraphDbAccessor::label(const std::string& label_name) {
|
||||
GraphDbTypes::Label GraphDbAccessor::label(const std::string &label_name) {
|
||||
return &(*db_.labels_.access().insert(label_name).first);
|
||||
}
|
||||
|
||||
std::string& GraphDbAccessor::label_name(const GraphDb::Label label) const {
|
||||
std::string &GraphDbAccessor::label_name(
|
||||
const GraphDbTypes::Label label) const {
|
||||
return *label;
|
||||
}
|
||||
|
||||
GraphDb::EdgeType GraphDbAccessor::edge_type(
|
||||
const std::string& edge_type_name) {
|
||||
GraphDbTypes::EdgeType GraphDbAccessor::edge_type(
|
||||
const std::string &edge_type_name) {
|
||||
return &(*db_.edge_types_.access().insert(edge_type_name).first);
|
||||
}
|
||||
|
||||
std::string& GraphDbAccessor::edge_type_name(
|
||||
const GraphDb::EdgeType edge_type) const {
|
||||
std::string &GraphDbAccessor::edge_type_name(
|
||||
const GraphDbTypes::EdgeType edge_type) const {
|
||||
return *edge_type;
|
||||
}
|
||||
|
||||
GraphDb::Property GraphDbAccessor::property(const std::string& property_name) {
|
||||
GraphDbTypes::Property GraphDbAccessor::property(
|
||||
const std::string &property_name) {
|
||||
return &(*db_.properties_.access().insert(property_name).first);
|
||||
}
|
||||
|
||||
std::string& GraphDbAccessor::property_name(
|
||||
const GraphDb::Property property) const {
|
||||
std::string &GraphDbAccessor::property_name(
|
||||
const GraphDbTypes::Property property) const {
|
||||
return *property;
|
||||
}
|
||||
|
||||
@@ -97,7 +97,7 @@ class GraphDbAccessor {
|
||||
* @return An accessor to the edge.
|
||||
*/
|
||||
EdgeAccessor insert_edge(VertexAccessor& from, VertexAccessor& to,
|
||||
GraphDb::EdgeType type);
|
||||
GraphDbTypes::EdgeType type);
|
||||
|
||||
/**
|
||||
* Removes an edge from the graph.
|
||||
@@ -126,7 +126,7 @@ class GraphDbAccessor {
|
||||
* Obtains the Label for the label's name.
|
||||
* @return See above.
|
||||
*/
|
||||
GraphDb::Label label(const std::string& label_name);
|
||||
GraphDbTypes::Label label(const std::string& label_name);
|
||||
|
||||
/**
|
||||
* Obtains the label name (a string) for the given label.
|
||||
@@ -134,13 +134,13 @@ class GraphDbAccessor {
|
||||
* @param label a Label.
|
||||
* @return See above.
|
||||
*/
|
||||
std::string& label_name(const GraphDb::Label label) const;
|
||||
std::string& label_name(const GraphDbTypes::Label label) const;
|
||||
|
||||
/**
|
||||
* Obtains the EdgeType for it's name.
|
||||
* @return See above.
|
||||
*/
|
||||
GraphDb::EdgeType edge_type(const std::string& edge_type_name);
|
||||
GraphDbTypes::EdgeType edge_type(const std::string& edge_type_name);
|
||||
|
||||
/**
|
||||
* Obtains the edge type name (a string) for the given edge type.
|
||||
@@ -148,13 +148,13 @@ class GraphDbAccessor {
|
||||
* @param edge_type an EdgeType.
|
||||
* @return See above.
|
||||
*/
|
||||
std::string& edge_type_name(const GraphDb::EdgeType edge_type) const;
|
||||
std::string& edge_type_name(const GraphDbTypes::EdgeType edge_type) const;
|
||||
|
||||
/**
|
||||
* Obtains the Property for it's name.
|
||||
* @return See above.
|
||||
*/
|
||||
GraphDb::Property property(const std::string& property_name);
|
||||
GraphDbTypes::Property property(const std::string& property_name);
|
||||
|
||||
/**
|
||||
* Obtains the property name (a string) for the given property.
|
||||
@@ -162,7 +162,7 @@ class GraphDbAccessor {
|
||||
* @param property a Property.
|
||||
* @return See above.
|
||||
*/
|
||||
std::string& property_name(const GraphDb::Property property) const;
|
||||
std::string& property_name(const GraphDbTypes::Property property) const;
|
||||
|
||||
/**
|
||||
* Advances transaction's command id by 1.
|
||||
|
||||
10
src/database/graph_db_datatypes.hpp
Normal file
10
src/database/graph_db_datatypes.hpp
Normal file
@@ -0,0 +1,10 @@
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace GraphDbTypes {
|
||||
// definitions for what data types are used for a Label, Property, EdgeType
|
||||
using Label = std::string *;
|
||||
using EdgeType = std::string *;
|
||||
using Property = std::string *;
|
||||
};
|
||||
@@ -13,15 +13,15 @@ class Logger {
|
||||
Message(Timestamp timestamp, std::string location, std::string message)
|
||||
: timestamp(timestamp), location(location), message(message) {}
|
||||
|
||||
const Timestamp& when() const override { return timestamp; }
|
||||
const Timestamp &when() const override { return timestamp; }
|
||||
|
||||
const std::string& where() const override { return location; }
|
||||
const std::string &where() const override { return location; }
|
||||
|
||||
unsigned level() const override { return Level::level; }
|
||||
|
||||
const std::string& level_str() const override { return Level::text; }
|
||||
const std::string &level_str() const override { return Level::text; }
|
||||
|
||||
const std::string& text() const override { return message; }
|
||||
const std::string &text() const override { return message; }
|
||||
|
||||
private:
|
||||
Timestamp timestamp;
|
||||
@@ -32,10 +32,10 @@ class Logger {
|
||||
public:
|
||||
Logger() = default;
|
||||
|
||||
Logger(Log* log, const std::string& name) : log(log), name(name) {}
|
||||
Logger(Log *log, const std::string &name) : log(log), name(name) {}
|
||||
|
||||
template <class Level, class... Args>
|
||||
void emit(Args&&... args) {
|
||||
void emit(Args &&... args) {
|
||||
debug_assert(log != nullptr, "Log object has to be defined.");
|
||||
|
||||
auto message = std::make_unique<Message<Level>>(
|
||||
@@ -44,8 +44,14 @@ class Logger {
|
||||
log->emit(std::move(message));
|
||||
}
|
||||
|
||||
/**
|
||||
*@brief Return if the logger is initialized.
|
||||
*@return true if initialized, false otherwise.
|
||||
*/
|
||||
bool Initialized() { return log != nullptr; }
|
||||
|
||||
template <class... Args>
|
||||
void trace(Args&&... args) {
|
||||
void trace(Args &&... args) {
|
||||
#ifndef NDEBUG
|
||||
#ifndef LOG_NO_TRACE
|
||||
emit<Trace>(std::forward<Args>(args)...);
|
||||
@@ -54,7 +60,7 @@ class Logger {
|
||||
}
|
||||
|
||||
template <class... Args>
|
||||
void debug(Args&&... args) {
|
||||
void debug(Args &&... args) {
|
||||
#ifndef NDEBUG
|
||||
#ifndef LOG_NO_DEBUG
|
||||
emit<Debug>(std::forward<Args>(args)...);
|
||||
@@ -63,27 +69,27 @@ class Logger {
|
||||
}
|
||||
|
||||
template <class... Args>
|
||||
void info(Args&&... args) {
|
||||
void info(Args &&... args) {
|
||||
#ifndef LOG_NO_INFO
|
||||
emit<Info>(std::forward<Args>(args)...);
|
||||
#endif
|
||||
}
|
||||
|
||||
template <class... Args>
|
||||
void warn(Args&&... args) {
|
||||
void warn(Args &&... args) {
|
||||
#ifndef LOG_NO_WARN
|
||||
emit<Warn>(std::forward<Args>(args)...);
|
||||
#endif
|
||||
}
|
||||
|
||||
template <class... Args>
|
||||
void error(Args&&... args) {
|
||||
void error(Args &&... args) {
|
||||
#ifndef LOG_NO_ERROR
|
||||
emit<Error>(std::forward<Args>(args)...);
|
||||
#endif
|
||||
}
|
||||
|
||||
private:
|
||||
Log* log;
|
||||
Log *log;
|
||||
std::string name;
|
||||
};
|
||||
|
||||
@@ -12,16 +12,24 @@ namespace mvcc {
|
||||
|
||||
template <class T>
|
||||
class VersionList {
|
||||
// TODO what is this Accessor? Dead code?
|
||||
friend class Accessor;
|
||||
|
||||
public:
|
||||
using uptr = std::unique_ptr<VersionList<T>>;
|
||||
using item_t = T;
|
||||
|
||||
VersionList() = default;
|
||||
/* @brief Constructor that is used to insert one item into VersionList.
|
||||
@param t - transaction
|
||||
@param T item - item which will point to new and only entry in
|
||||
version_list.
|
||||
@param args - args forwarded to constructor of item T.
|
||||
*/
|
||||
template <typename... Args>
|
||||
VersionList(tx::Transaction &t, T *&item, Args &&... args) {
|
||||
item = insert(t, std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
VersionList() = delete;
|
||||
VersionList(const VersionList &) = delete;
|
||||
VersionList &operator=(const VersionList &) = delete;
|
||||
|
||||
/* @brief Move constructs the version list
|
||||
* Note: use only at the beginning of the "other's" lifecycle since this
|
||||
@@ -48,59 +56,58 @@ class VersionList {
|
||||
return stream;
|
||||
}
|
||||
|
||||
auto gc_lock_acquire() { return std::unique_lock<RecordLock>(lock); }
|
||||
|
||||
// Frees all records which are deleted by transaction older than given id.
|
||||
// EXPECTS THAT THERE IS NO ACTIVE TRANSACTION WITH ID LESS THAN GIVEN ID.
|
||||
// EXPECTS THAT THERE WON'T BE SIMULATAIUS CALLS FROM DIFFERENT THREADS OF
|
||||
// THIS METHOD.
|
||||
// True if this whole version list isn't needed any more. There is still
|
||||
// possibilty that someone is reading it at this moment but he cant change
|
||||
// it or get anything from it.
|
||||
// TODO: Validate this method
|
||||
bool gc_deleted(const Id &id) {
|
||||
auto r = head.load(std::memory_order_seq_cst);
|
||||
T *bef = nullptr;
|
||||
/**
|
||||
* This method is NOT thread-safe. This should never be called with a
|
||||
* transaction id newer than the oldest active transaction id.
|
||||
* Garbage collect (delete) all records which are no longer visible for any
|
||||
* transaction with an id greater or equal to id.
|
||||
* @param id - transaction id from which to start garbage collection
|
||||
* @return true - If version list is empty after garbage collection.
|
||||
*/
|
||||
bool GcDeleted(const Id &id) {
|
||||
auto newest_deleted_record = head.load(std::memory_order_seq_cst);
|
||||
T *oldest_not_deleted_record = nullptr;
|
||||
|
||||
// nullptr
|
||||
// |
|
||||
// [v1] ...
|
||||
// |
|
||||
// [v2] <------+
|
||||
// [v2] <------+ newest_deleted_record
|
||||
// | |
|
||||
// [v3] <------+
|
||||
// [v3] <------+ oldest_not_deleted_record
|
||||
// | | Jump backwards until you find a first old deleted
|
||||
// [VerList] ----+ version, or you reach the end of the list
|
||||
// [VerList] ----+ record, or you reach the end of the list
|
||||
//
|
||||
while (r != nullptr && !r->is_deleted_before(id)) {
|
||||
bef = r;
|
||||
r = r->next(std::memory_order_seq_cst);
|
||||
while (newest_deleted_record != nullptr &&
|
||||
!newest_deleted_record->is_deleted_before(id)) {
|
||||
oldest_not_deleted_record = newest_deleted_record;
|
||||
newest_deleted_record =
|
||||
newest_deleted_record->next(std::memory_order_seq_cst);
|
||||
}
|
||||
|
||||
if (bef == nullptr) {
|
||||
// if r==nullptr he is needed and it is expecting insert.
|
||||
// if r!=nullptr vertex has been explicitly deleted. It can't be
|
||||
// updated because for update, visible record is needed and at this
|
||||
// point whe know that there is no visible record for any
|
||||
// transaction. Also it cant be inserted because head isn't nullptr.
|
||||
// Remove also requires visible record. Find wont return any record
|
||||
// because none is visible.
|
||||
return r != nullptr;
|
||||
} else {
|
||||
if (r != nullptr) {
|
||||
// Bef is possible visible to some transaction but r is not and
|
||||
// the implementation of this version list guarantees that
|
||||
// record r and older records aren't accessed.
|
||||
bef->next(nullptr, std::memory_order_seq_cst);
|
||||
delete r; // THIS IS ISSUE IF MULTIPLE THREADS TRY TO DO THIS
|
||||
}
|
||||
|
||||
return false;
|
||||
if (oldest_not_deleted_record == nullptr) {
|
||||
// This can happen only if the head already points to a deleted record or
|
||||
// the version list is empty. This means that the version_list is ready
|
||||
// for complete destruction.
|
||||
if (newest_deleted_record != nullptr) delete newest_deleted_record;
|
||||
head.store(nullptr, std::memory_order_seq_cst);
|
||||
return true;
|
||||
}
|
||||
// oldest_not_deleted_record might be visible to some transaction but
|
||||
// newest_deleted_record is not.
|
||||
oldest_not_deleted_record->next(
|
||||
nullptr, std::memory_order_seq_cst); // No transaction will look
|
||||
// further than this record and
|
||||
// that's why it's safe to set
|
||||
// next to nullptr.
|
||||
// Call destructor which will clean everything older than this record since
|
||||
// they are called recursively.
|
||||
if (newest_deleted_record != nullptr)
|
||||
delete newest_deleted_record; // THIS IS ISSUE IF MULTIPLE THREADS TRY TO
|
||||
// DO THIS
|
||||
return false;
|
||||
}
|
||||
|
||||
void vacuum() {}
|
||||
|
||||
T *find(const tx::Transaction &t) const {
|
||||
auto r = head.load(std::memory_order_seq_cst);
|
||||
|
||||
@@ -120,25 +127,6 @@ class VersionList {
|
||||
return r;
|
||||
}
|
||||
|
||||
/**
|
||||
* @Args forwarded to the constructor of T
|
||||
*/
|
||||
template <typename... Args>
|
||||
T *insert(tx::Transaction &t, Args &&... args) {
|
||||
debug_assert(head == nullptr, "Head is not nullptr on creation.");
|
||||
|
||||
// create a first version of the record
|
||||
// TODO replace 'new' with something better
|
||||
auto v1 = new T(std::forward<Args>(args)...);
|
||||
|
||||
// mark the record as created by the transaction t
|
||||
v1->mark_created(t);
|
||||
|
||||
head.store(v1, std::memory_order_seq_cst);
|
||||
|
||||
return v1;
|
||||
}
|
||||
|
||||
T *update(tx::Transaction &t) {
|
||||
debug_assert(head != nullptr, "Head is nullptr on update.");
|
||||
auto record = find(t);
|
||||
@@ -202,6 +190,27 @@ class VersionList {
|
||||
throw SerializationError();
|
||||
}
|
||||
|
||||
/**
|
||||
* This is private because this should only be called from the constructor.
|
||||
* Otherwise head might be nullptr while version_list exists and it could
|
||||
* interfere with GC.
|
||||
* @tparam Args forwarded to the constructor of T
|
||||
*/
|
||||
template <typename... Args>
|
||||
T *insert(tx::Transaction &t, Args &&... args) {
|
||||
debug_assert(head == nullptr, "Head is not nullptr on creation.");
|
||||
|
||||
// create a first version of the record
|
||||
// TODO replace 'new' with something better
|
||||
auto v1 = new T(std::forward<Args>(args)...);
|
||||
|
||||
// mark the record as created by the transaction t
|
||||
v1->mark_created(t);
|
||||
|
||||
head.store(v1, std::memory_order_seq_cst);
|
||||
return v1;
|
||||
}
|
||||
|
||||
std::atomic<T *> head{nullptr};
|
||||
RecordLock lock;
|
||||
};
|
||||
|
||||
@@ -347,16 +347,18 @@ class PropertyLookup : public Expression {
|
||||
}
|
||||
|
||||
Expression *expression_ = nullptr;
|
||||
GraphDb::Property property_ = nullptr;
|
||||
GraphDbTypes::Property property_ = nullptr;
|
||||
// TODO potential problem: property lookups are allowed on both map literals
|
||||
// and records, but map literals have strings as keys and records have
|
||||
// GraphDb::Property
|
||||
// GraphDbTypes::Property
|
||||
//
|
||||
// possible solution: store both string and GraphDb::Property here and choose
|
||||
// possible solution: store both string and GraphDbTypes::Property here and
|
||||
// choose
|
||||
// between the two depending on Expression result
|
||||
|
||||
protected:
|
||||
PropertyLookup(int uid, Expression *expression, GraphDb::Property property)
|
||||
PropertyLookup(int uid, Expression *expression,
|
||||
GraphDbTypes::Property property)
|
||||
: Expression(uid), expression_(expression), property_(property) {}
|
||||
};
|
||||
|
||||
@@ -402,9 +404,9 @@ class NodeAtom : public PatternAtom {
|
||||
visitor.PostVisit(*this);
|
||||
}
|
||||
|
||||
std::vector<GraphDb::Label> labels_;
|
||||
std::vector<GraphDbTypes::Label> labels_;
|
||||
// TODO: change to unordered_map
|
||||
std::map<GraphDb::Property, Expression *> properties_;
|
||||
std::map<GraphDbTypes::Property, Expression *> properties_;
|
||||
|
||||
protected:
|
||||
using PatternAtom::PatternAtom;
|
||||
@@ -426,9 +428,9 @@ class EdgeAtom : public PatternAtom {
|
||||
}
|
||||
|
||||
Direction direction_ = Direction::BOTH;
|
||||
std::vector<GraphDb::EdgeType> edge_types_;
|
||||
std::vector<GraphDbTypes::EdgeType> edge_types_;
|
||||
// TODO: change to unordered_map
|
||||
std::map<GraphDb::Property, Expression *> properties_;
|
||||
std::map<GraphDbTypes::Property, Expression *> properties_;
|
||||
|
||||
protected:
|
||||
using PatternAtom::PatternAtom;
|
||||
@@ -620,12 +622,12 @@ class SetLabels : public Clause {
|
||||
visitor.PostVisit(*this);
|
||||
}
|
||||
Identifier *identifier_ = nullptr;
|
||||
std::vector<GraphDb::Label> labels_;
|
||||
std::vector<GraphDbTypes::Label> labels_;
|
||||
|
||||
protected:
|
||||
SetLabels(int uid) : Clause(uid) {}
|
||||
SetLabels(int uid, Identifier *identifier,
|
||||
const std::vector<GraphDb::Label> &labels)
|
||||
const std::vector<GraphDbTypes::Label> &labels)
|
||||
: Clause(uid), identifier_(identifier), labels_(labels) {}
|
||||
};
|
||||
|
||||
|
||||
@@ -148,19 +148,20 @@ antlrcpp::Any CypherMainVisitor::visitNodePattern(
|
||||
}
|
||||
if (ctx->nodeLabels()) {
|
||||
node->labels_ =
|
||||
ctx->nodeLabels()->accept(this).as<std::vector<GraphDb::Label>>();
|
||||
ctx->nodeLabels()->accept(this).as<std::vector<GraphDbTypes::Label>>();
|
||||
}
|
||||
if (ctx->properties()) {
|
||||
node->properties_ = ctx->properties()
|
||||
->accept(this)
|
||||
.as<std::map<GraphDb::Property, Expression *>>();
|
||||
node->properties_ =
|
||||
ctx->properties()
|
||||
->accept(this)
|
||||
.as<std::map<GraphDbTypes::Property, Expression *>>();
|
||||
}
|
||||
return node;
|
||||
}
|
||||
|
||||
antlrcpp::Any CypherMainVisitor::visitNodeLabels(
|
||||
CypherParser::NodeLabelsContext *ctx) {
|
||||
std::vector<GraphDb::Label> labels;
|
||||
std::vector<GraphDbTypes::Label> labels;
|
||||
for (auto *node_label : ctx->nodeLabel()) {
|
||||
labels.push_back(ctx_.db_accessor_.label(node_label->accept(this)));
|
||||
}
|
||||
@@ -182,7 +183,7 @@ antlrcpp::Any CypherMainVisitor::visitProperties(
|
||||
|
||||
antlrcpp::Any CypherMainVisitor::visitMapLiteral(
|
||||
CypherParser::MapLiteralContext *ctx) {
|
||||
std::map<GraphDb::Property, Expression *> map;
|
||||
std::map<GraphDbTypes::Property, Expression *> map;
|
||||
for (int i = 0; i < (int)ctx->propertyKeyName().size(); ++i) {
|
||||
map[ctx->propertyKeyName()[i]->accept(this)] =
|
||||
ctx->expression()[i]->accept(this);
|
||||
@@ -267,13 +268,14 @@ antlrcpp::Any CypherMainVisitor::visitRelationshipPattern(
|
||||
edge->edge_types_ = ctx->relationshipDetail()
|
||||
->relationshipTypes()
|
||||
->accept(this)
|
||||
.as<std::vector<GraphDb::EdgeType>>();
|
||||
.as<std::vector<GraphDbTypes::EdgeType>>();
|
||||
}
|
||||
if (ctx->relationshipDetail()->properties()) {
|
||||
edge->properties_ = ctx->relationshipDetail()
|
||||
->properties()
|
||||
->accept(this)
|
||||
.as<std::map<GraphDb::Property, Expression *>>();
|
||||
edge->properties_ =
|
||||
ctx->relationshipDetail()
|
||||
->properties()
|
||||
->accept(this)
|
||||
.as<std::map<GraphDbTypes::Property, Expression *>>();
|
||||
}
|
||||
if (ctx->relationshipDetail()->rangeLiteral()) {
|
||||
// TODO: implement other clauses.
|
||||
@@ -311,7 +313,7 @@ antlrcpp::Any CypherMainVisitor::visitRelationshipDetail(
|
||||
|
||||
antlrcpp::Any CypherMainVisitor::visitRelationshipTypes(
|
||||
CypherParser::RelationshipTypesContext *ctx) {
|
||||
std::vector<GraphDb::EdgeType> types;
|
||||
std::vector<GraphDbTypes::EdgeType> types;
|
||||
for (auto *edge_type : ctx->relTypeName()) {
|
||||
types.push_back(ctx_.db_accessor_.edge_type(edge_type->accept(this)));
|
||||
}
|
||||
@@ -738,7 +740,7 @@ antlrcpp::Any CypherMainVisitor::visitSetItem(
|
||||
set_labels->identifier_ = storage_.Create<Identifier>(
|
||||
ctx->variable()->accept(this).as<std::string>());
|
||||
set_labels->labels_ =
|
||||
ctx->nodeLabels()->accept(this).as<std::vector<GraphDb::Label>>();
|
||||
ctx->nodeLabels()->accept(this).as<std::vector<GraphDbTypes::Label>>();
|
||||
return static_cast<Clause *>(set_labels);
|
||||
}
|
||||
|
||||
|
||||
@@ -181,22 +181,22 @@ class CypherMainVisitor : public antlropencypher::CypherBaseVisitor {
|
||||
CypherParser::NodePatternContext *ctx) override;
|
||||
|
||||
/**
|
||||
* @return vector<GraphDb::Label>
|
||||
* @return vector<GraphDbTypes::Label>
|
||||
*/
|
||||
antlrcpp::Any visitNodeLabels(CypherParser::NodeLabelsContext *ctx) override;
|
||||
|
||||
/**
|
||||
* @return unordered_map<GraphDb::Property, Expression*>
|
||||
* @return unordered_map<GraphDbTypes::Property, Expression*>
|
||||
*/
|
||||
antlrcpp::Any visitProperties(CypherParser::PropertiesContext *ctx) override;
|
||||
|
||||
/**
|
||||
* @return unordered_map<GraphDb::Property, Expression*>
|
||||
* @return unordered_map<GraphDbTypes::Property, Expression*>
|
||||
*/
|
||||
antlrcpp::Any visitMapLiteral(CypherParser::MapLiteralContext *ctx) override;
|
||||
|
||||
/**
|
||||
* @return GraphDb::Property
|
||||
* @return GraphDbTypes::Property
|
||||
*/
|
||||
antlrcpp::Any visitPropertyKeyName(
|
||||
CypherParser::PropertyKeyNameContext *ctx) override;
|
||||
@@ -244,7 +244,7 @@ class CypherMainVisitor : public antlropencypher::CypherBaseVisitor {
|
||||
CypherParser::RelationshipDetailContext *ctx) override;
|
||||
|
||||
/**
|
||||
* @return vector<GraphDb::EdgeType>
|
||||
* @return vector<GraphDbTypes::EdgeType>
|
||||
*/
|
||||
antlrcpp::Any visitRelationshipTypes(
|
||||
CypherParser::RelationshipTypesContext *ctx) override;
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
#include <vector>
|
||||
|
||||
#include "database/graph_db_accessor.hpp"
|
||||
#include "database/graph_db_datatypes.hpp"
|
||||
#include "query/frontend/ast/ast.hpp"
|
||||
#include "query/frontend/interpret/interpret.hpp"
|
||||
#include "query/frontend/semantic/symbol_table.hpp"
|
||||
@@ -593,7 +594,7 @@ class EdgeFilter : public LogicalOperator {
|
||||
SymbolTable &symbol_table) {
|
||||
// edge type filtering - logical OR
|
||||
const auto &types = self_.edge_atom_->edge_types_;
|
||||
GraphDb::EdgeType type = edge.edge_type();
|
||||
GraphDbTypes::EdgeType type = edge.edge_type();
|
||||
if (!std::any_of(types.begin(), types.end(),
|
||||
[type](auto t) { return t == type; }))
|
||||
return false;
|
||||
@@ -999,7 +1000,7 @@ class SetLabels : public LogicalOperator {
|
||||
public:
|
||||
SetLabels(const std::shared_ptr<LogicalOperator> input,
|
||||
const Symbol input_symbol,
|
||||
const std::vector<GraphDb::Label> &labels)
|
||||
const std::vector<GraphDbTypes::Label> &labels)
|
||||
: input_(input), input_symbol_(input_symbol), labels_(labels) {}
|
||||
|
||||
void Accept(LogicalOperatorVisitor &visitor) override {
|
||||
@@ -1019,8 +1020,7 @@ class SetLabels : public LogicalOperator {
|
||||
|
||||
TypedValue vertex_value = frame[self_.input_symbol_];
|
||||
VertexAccessor vertex = vertex_value.Value<VertexAccessor>();
|
||||
for (auto label : self_.labels_)
|
||||
vertex.add_label(label);
|
||||
for (auto label : self_.labels_) vertex.add_label(label);
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -1038,7 +1038,7 @@ class SetLabels : public LogicalOperator {
|
||||
private:
|
||||
std::shared_ptr<LogicalOperator> input_;
|
||||
const Symbol input_symbol_;
|
||||
std::vector<GraphDb::Label> labels_;
|
||||
std::vector<GraphDbTypes::Label> labels_;
|
||||
};
|
||||
|
||||
} // namespace plan
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
#include "database/graph_db.hpp"
|
||||
#include "database/graph_db_datatypes.hpp"
|
||||
#include "mvcc/record.hpp"
|
||||
#include "mvcc/version_list.hpp"
|
||||
#include "storage/property_value_store.hpp"
|
||||
@@ -10,12 +10,12 @@ class Vertex;
|
||||
|
||||
class Edge : public mvcc::Record<Edge> {
|
||||
public:
|
||||
Edge(mvcc::VersionList<Vertex>& from, mvcc::VersionList<Vertex>& to,
|
||||
GraphDb::EdgeType edge_type)
|
||||
Edge(mvcc::VersionList<Vertex> &from, mvcc::VersionList<Vertex> &to,
|
||||
GraphDbTypes::EdgeType edge_type)
|
||||
: from_(from), to_(to), edge_type_(edge_type) {}
|
||||
|
||||
mvcc::VersionList<Vertex>& from_;
|
||||
mvcc::VersionList<Vertex>& to_;
|
||||
GraphDb::EdgeType edge_type_;
|
||||
PropertyValueStore<GraphDb::Property> properties_;
|
||||
mvcc::VersionList<Vertex> &from_;
|
||||
mvcc::VersionList<Vertex> &to_;
|
||||
GraphDbTypes::EdgeType edge_type_;
|
||||
PropertyValueStore<GraphDbTypes::Property> properties_;
|
||||
};
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
#include "storage/edge_accessor.hpp"
|
||||
#include "storage/vertex_accessor.hpp"
|
||||
|
||||
void EdgeAccessor::set_edge_type(GraphDb::EdgeType edge_type) {
|
||||
void EdgeAccessor::set_edge_type(GraphDbTypes::EdgeType edge_type) {
|
||||
update().edge_type_ = edge_type;
|
||||
}
|
||||
|
||||
GraphDb::EdgeType EdgeAccessor::edge_type() const { return view().edge_type_; }
|
||||
GraphDbTypes::EdgeType EdgeAccessor::edge_type() const { return view().edge_type_; }
|
||||
|
||||
VertexAccessor EdgeAccessor::from() const {
|
||||
return VertexAccessor(view().from_, db_accessor());
|
||||
|
||||
@@ -25,13 +25,13 @@ class EdgeAccessor : public RecordAccessor<Edge> {
|
||||
* Sets a new edge type.
|
||||
* @param edge_type The new type.
|
||||
*/
|
||||
void set_edge_type(GraphDb::EdgeType edge_type);
|
||||
void set_edge_type(GraphDbTypes::EdgeType edge_type);
|
||||
|
||||
/**
|
||||
* Returns the edge type.
|
||||
* @return
|
||||
*/
|
||||
GraphDb::EdgeType edge_type() const;
|
||||
GraphDbTypes::EdgeType edge_type() const;
|
||||
|
||||
/**
|
||||
* Returns an accessor to the originating Vertex of this edge.
|
||||
|
||||
82
src/storage/garbage_collector.hpp
Normal file
82
src/storage/garbage_collector.hpp
Normal file
@@ -0,0 +1,82 @@
|
||||
#pragma once
|
||||
|
||||
#include <chrono>
|
||||
#include <condition_variable>
|
||||
#include <thread>
|
||||
|
||||
#include "config/config.hpp"
|
||||
#include "data_structures/concurrent/skiplist.hpp"
|
||||
#include "logging/loggable.hpp"
|
||||
#include "mvcc/id.hpp"
|
||||
#include "mvcc/version_list.hpp"
|
||||
#include "transactions/engine.hpp"
|
||||
|
||||
/**
|
||||
@template T type of underlying record in mvcc
|
||||
*/
|
||||
|
||||
template <typename T>
|
||||
class GarbageCollector : public Loggable {
|
||||
public:
|
||||
GarbageCollector(SkipList<mvcc::VersionList<T> *> *skiplist,
|
||||
tx::Engine *engine)
|
||||
: Loggable("MvccGc"), skiplist_(skiplist), engine_(engine) {
|
||||
permanent_assert(skiplist != nullptr, "Skiplist can't be nullptr.");
|
||||
permanent_assert(engine != nullptr, "Engine can't be nullptr.");
|
||||
};
|
||||
|
||||
~GarbageCollector() {
|
||||
destruction_.store(true);
|
||||
condition_variable_.notify_one();
|
||||
if (run_thread_.joinable()) run_thread_.join();
|
||||
}
|
||||
|
||||
/**
|
||||
*@brief - Runs garbage collector. Starts a new thread which garbage collects
|
||||
*in the background.
|
||||
*@param pause - How long to sleep between successive garbage collector
|
||||
*thread. If this parameter is -1 the GC will not run.
|
||||
*/
|
||||
void Run(const std::chrono::seconds &pause) {
|
||||
if (pause == std::chrono::seconds(-1)) return;
|
||||
// Invoke new thread which will do the GC work.
|
||||
run_thread_ = std::thread([this, pause]() {
|
||||
for (;;) {
|
||||
// If the whole class is being destructed we should end this thread.
|
||||
if (this->destruction_.load(std::memory_order_seq_cst)) break;
|
||||
|
||||
auto accessor = this->skiplist_->access();
|
||||
uint64_t count = 0;
|
||||
// Acquire id of either the oldest active transaction, or the id of a
|
||||
// transaction that will be assigned next. We should make sure that we
|
||||
// get count before we ask for active transactions since some
|
||||
// transaction could possibly increase the count while we ask for
|
||||
// oldest_active transaction.
|
||||
const auto next_id = engine_->count() + 1;
|
||||
const auto id = this->engine_->oldest_active().get_or(next_id);
|
||||
if (logger.Initialized())
|
||||
logger.trace("Gc started cleaning everything deleted before {}", id);
|
||||
for (auto x : accessor) {
|
||||
// If the mvcc is empty, i.e. there is nothing else to be read from it
|
||||
// we can delete it.
|
||||
if (x->GcDeleted(id)) count += accessor.remove(x);
|
||||
}
|
||||
if (logger.Initialized()) logger.trace("Destroyed: {}", count);
|
||||
|
||||
std::unique_lock<std::mutex> lk(mutex_);
|
||||
condition_variable_.wait_for(lk, std::chrono::seconds(pause), [&] {
|
||||
return this->destruction_ == true;
|
||||
});
|
||||
lk.unlock();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private:
|
||||
SkipList<mvcc::VersionList<T> *> *skiplist_{nullptr}; // Not owned.
|
||||
tx::Engine *engine_{nullptr}; // Not owned.
|
||||
std::thread run_thread_;
|
||||
std::atomic<bool> destruction_;
|
||||
std::mutex mutex_;
|
||||
std::condition_variable condition_variable_;
|
||||
};
|
||||
@@ -22,12 +22,12 @@ RecordAccessor<TRecord>::RecordAccessor(mvcc::VersionList<TRecord> &vlist,
|
||||
|
||||
template <typename TRecord>
|
||||
const PropertyValue &RecordAccessor<TRecord>::PropsAt(
|
||||
GraphDb::Property key) const {
|
||||
GraphDbTypes::Property key) const {
|
||||
return view().properties_.at(key);
|
||||
}
|
||||
|
||||
template <typename TRecord>
|
||||
size_t RecordAccessor<TRecord>::PropsErase(GraphDb::Property key) {
|
||||
size_t RecordAccessor<TRecord>::PropsErase(GraphDbTypes::Property key) {
|
||||
return update().properties_.erase(key);
|
||||
}
|
||||
|
||||
@@ -37,14 +37,15 @@ void RecordAccessor<TRecord>::PropsClear() {
|
||||
}
|
||||
|
||||
template <typename TRecord>
|
||||
const PropertyValueStore<GraphDb::Property>
|
||||
const PropertyValueStore<GraphDbTypes::Property>
|
||||
&RecordAccessor<TRecord>::Properties() const {
|
||||
return view().properties_;
|
||||
}
|
||||
|
||||
template <typename TRecord>
|
||||
void RecordAccessor<TRecord>::PropertiesAccept(
|
||||
std::function<void(const GraphDb::Property key, const PropertyValue &prop)>
|
||||
std::function<void(const GraphDbTypes::Property key,
|
||||
const PropertyValue &prop)>
|
||||
handler,
|
||||
std::function<void()> finish) const {
|
||||
view().properties_.Accept(handler, finish);
|
||||
|
||||
@@ -62,7 +62,7 @@ class RecordAccessor {
|
||||
* @param key
|
||||
* @return
|
||||
*/
|
||||
const PropertyValue& PropsAt(GraphDb::Property key) const;
|
||||
const PropertyValue& PropsAt(GraphDbTypes::Property key) const;
|
||||
|
||||
/**
|
||||
* Sets a value on the record for the given property.
|
||||
@@ -72,7 +72,7 @@ class RecordAccessor {
|
||||
* @param value The value to set.
|
||||
*/
|
||||
template <typename TValue>
|
||||
void PropsSet(GraphDb::Property key, TValue value) {
|
||||
void PropsSet(GraphDbTypes::Property key, TValue value) {
|
||||
update().properties_.set(key, value);
|
||||
}
|
||||
|
||||
@@ -82,7 +82,7 @@ class RecordAccessor {
|
||||
* @param key
|
||||
* @return
|
||||
*/
|
||||
size_t PropsErase(GraphDb::Property key);
|
||||
size_t PropsErase(GraphDbTypes::Property key);
|
||||
|
||||
/**
|
||||
* Removes all the properties from this record.
|
||||
@@ -93,9 +93,9 @@ class RecordAccessor {
|
||||
* Returns the properties of this record.
|
||||
* @return
|
||||
*/
|
||||
const PropertyValueStore<GraphDb::Property>& Properties() const;
|
||||
const PropertyValueStore<GraphDbTypes::Property>& Properties() const;
|
||||
|
||||
void PropertiesAccept(std::function<void(const GraphDb::Property key,
|
||||
void PropertiesAccept(std::function<void(const GraphDbTypes::Property key,
|
||||
const PropertyValue& prop)>
|
||||
handler,
|
||||
std::function<void()> finish = {}) const;
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include "database/graph_db.hpp"
|
||||
#include "database/graph_db_datatypes.hpp"
|
||||
#include "mvcc/record.hpp"
|
||||
#include "mvcc/version_list.hpp"
|
||||
#include "storage/property_value_store.hpp"
|
||||
@@ -12,8 +12,8 @@ class Edge;
|
||||
|
||||
class Vertex : public mvcc::Record<Vertex> {
|
||||
public:
|
||||
std::vector<mvcc::VersionList<Edge>*> out_;
|
||||
std::vector<mvcc::VersionList<Edge>*> in_;
|
||||
std::vector<GraphDb::Label> labels_;
|
||||
PropertyValueStore<GraphDb::Property> properties_;
|
||||
std::vector<mvcc::VersionList<Edge> *> out_;
|
||||
std::vector<mvcc::VersionList<Edge> *> in_;
|
||||
std::vector<GraphDbTypes::Label> labels_;
|
||||
PropertyValueStore<GraphDbTypes::Property> properties_;
|
||||
};
|
||||
|
||||
@@ -8,7 +8,7 @@ size_t VertexAccessor::out_degree() const { return view().out_.size(); }
|
||||
|
||||
size_t VertexAccessor::in_degree() const { return view().in_.size(); }
|
||||
|
||||
bool VertexAccessor::add_label(GraphDb::Label label) {
|
||||
bool VertexAccessor::add_label(GraphDbTypes::Label label) {
|
||||
auto &labels_view = view().labels_;
|
||||
auto found = std::find(labels_view.begin(), labels_view.end(), label);
|
||||
if (found != labels_view.end()) return false;
|
||||
@@ -18,7 +18,7 @@ bool VertexAccessor::add_label(GraphDb::Label label) {
|
||||
return true;
|
||||
}
|
||||
|
||||
size_t VertexAccessor::remove_label(GraphDb::Label label) {
|
||||
size_t VertexAccessor::remove_label(GraphDbTypes::Label label) {
|
||||
auto &labels = update().labels_;
|
||||
auto found = std::find(labels.begin(), labels.end(), label);
|
||||
if (found == labels.end()) return 0;
|
||||
@@ -28,11 +28,11 @@ size_t VertexAccessor::remove_label(GraphDb::Label label) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
bool VertexAccessor::has_label(GraphDb::Label label) const {
|
||||
bool VertexAccessor::has_label(GraphDbTypes::Label label) const {
|
||||
auto &labels = this->view().labels_;
|
||||
return std::find(labels.begin(), labels.end(), label) != labels.end();
|
||||
}
|
||||
|
||||
const std::vector<GraphDb::Label> &VertexAccessor::labels() const {
|
||||
const std::vector<GraphDbTypes::Label> &VertexAccessor::labels() const {
|
||||
return this->view().labels_;
|
||||
}
|
||||
|
||||
@@ -41,27 +41,27 @@ class VertexAccessor : public RecordAccessor<Vertex> {
|
||||
* @param label A label.
|
||||
* @return If or not a new Label was set on this Vertex.
|
||||
*/
|
||||
bool add_label(GraphDb::Label label);
|
||||
bool add_label(GraphDbTypes::Label label);
|
||||
|
||||
/**
|
||||
* Removes a label from the Vertex.
|
||||
* @param label The label to remove.
|
||||
* @return The number of removed labels (can be 0 or 1).
|
||||
*/
|
||||
size_t remove_label(GraphDb::Label label);
|
||||
size_t remove_label(GraphDbTypes::Label label);
|
||||
|
||||
/**
|
||||
* Indicates if the Vertex has the given label.
|
||||
* @param label A label.
|
||||
* @return
|
||||
*/
|
||||
bool has_label(GraphDb::Label label) const;
|
||||
bool has_label(GraphDbTypes::Label label) const;
|
||||
|
||||
/**
|
||||
* Returns all the Labels of the Vertex.
|
||||
* @return
|
||||
*/
|
||||
const std::vector<GraphDb::Label>& labels() const;
|
||||
const std::vector<GraphDbTypes::Label>& labels() const;
|
||||
|
||||
/**
|
||||
* Returns EdgeAccessors for all incoming edges.
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
#include <vector>
|
||||
|
||||
#include "database/graph_db_accessor.hpp"
|
||||
#include "database/graph_db_datatypes.hpp"
|
||||
#include "storage/property_value.hpp"
|
||||
#include "storage/vertex_accessor.hpp"
|
||||
#include "utils/assert.hpp"
|
||||
@@ -44,7 +45,7 @@ class RandomGraphGenerator {
|
||||
void AddVertices(uint count, std::vector<std::string> label_names) {
|
||||
permanent_assert(!did_commit_, "Already committed");
|
||||
|
||||
std::vector<GraphDb::Label> labels;
|
||||
std::vector<GraphDbTypes::Label> labels;
|
||||
for (const auto &label_name : label_names)
|
||||
labels.push_back(dba_.label(label_name));
|
||||
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
#include "storage/vertex_accessor.hpp"
|
||||
|
||||
void write_properties(std::ostream &os, const GraphDbAccessor &access,
|
||||
const PropertyValueStore<GraphDb::Property> &properties) {
|
||||
const PropertyValueStore<GraphDbTypes::Property> &properties) {
|
||||
if (properties.size() > 0) {
|
||||
os << "{";
|
||||
for (auto x : properties) {
|
||||
@@ -22,7 +22,7 @@ void write_properties(std::ostream &os, const GraphDbAccessor &access,
|
||||
|
||||
std::ostream &operator<<(std::ostream &os, const VertexAccessor &vertex) {
|
||||
if (vertex.labels().size() > 0) {
|
||||
for (GraphDb::Label label : vertex.labels()) {
|
||||
for (GraphDbTypes::Label label : vertex.labels()) {
|
||||
os << vertex.db_accessor().property_name(label) << ", ";
|
||||
}
|
||||
os << "\n";
|
||||
|
||||
@@ -380,7 +380,7 @@ TEST(CypherMainVisitorTest, NodePattern) {
|
||||
ast_generator.db_accessor_->label("label1"),
|
||||
ast_generator.db_accessor_->label("label2"),
|
||||
ast_generator.db_accessor_->label("label3")));
|
||||
std::unordered_map<GraphDb::Property, int64_t> properties;
|
||||
std::unordered_map<GraphDbTypes::Property, int64_t> properties;
|
||||
for (auto x : node->properties_) {
|
||||
auto *literal = dynamic_cast<Literal *>(x.second);
|
||||
ASSERT_TRUE(literal);
|
||||
@@ -457,7 +457,7 @@ TEST(CypherMainVisitorTest, RelationshipPatternDetails) {
|
||||
edge->edge_types_,
|
||||
UnorderedElementsAre(ast_generator.db_accessor_->edge_type("type1"),
|
||||
ast_generator.db_accessor_->edge_type("type2")));
|
||||
std::unordered_map<GraphDb::Property, int64_t> properties;
|
||||
std::unordered_map<GraphDbTypes::Property, int64_t> properties;
|
||||
for (auto x : edge->properties_) {
|
||||
auto *literal = dynamic_cast<Literal *>(x.second);
|
||||
ASSERT_TRUE(literal);
|
||||
|
||||
@@ -263,7 +263,7 @@ TEST(GraphDbAccessorTest, Labels) {
|
||||
Dbms dbms;
|
||||
auto dba1 = dbms.active();
|
||||
|
||||
GraphDb::Label label_friend = dba1->label("friend");
|
||||
GraphDbTypes::Label label_friend = dba1->label("friend");
|
||||
EXPECT_EQ(label_friend, dba1->label("friend"));
|
||||
EXPECT_NE(label_friend, dba1->label("friend2"));
|
||||
EXPECT_EQ(dba1->label_name(label_friend), "friend");
|
||||
@@ -277,7 +277,7 @@ TEST(GraphDbAccessorTest, EdgeTypes) {
|
||||
Dbms dbms;
|
||||
auto dba1 = dbms.active();
|
||||
|
||||
GraphDb::EdgeType edge_type = dba1->edge_type("likes");
|
||||
GraphDbTypes::EdgeType edge_type = dba1->edge_type("likes");
|
||||
EXPECT_EQ(edge_type, dba1->edge_type("likes"));
|
||||
EXPECT_NE(edge_type, dba1->edge_type("hates"));
|
||||
EXPECT_EQ(dba1->edge_type_name(edge_type), "likes");
|
||||
@@ -291,7 +291,7 @@ TEST(GraphDbAccessorTest, Properties) {
|
||||
Dbms dbms;
|
||||
auto dba1 = dbms.active();
|
||||
|
||||
GraphDb::EdgeType prop = dba1->property("name");
|
||||
GraphDbTypes::EdgeType prop = dba1->property("name");
|
||||
EXPECT_EQ(prop, dba1->property("name"));
|
||||
EXPECT_NE(prop, dba1->property("surname"));
|
||||
EXPECT_EQ(dba1->property_name(prop), "name");
|
||||
|
||||
@@ -185,8 +185,8 @@ TEST(Interpreter, NodeFilterLabelsAndProperties) {
|
||||
auto dba = dbms.active();
|
||||
|
||||
// add a few nodes to the database
|
||||
GraphDb::Label label = dba->label("Label");
|
||||
GraphDb::Property property = dba->property("Property");
|
||||
GraphDbTypes::Label label = dba->label("Label");
|
||||
GraphDbTypes::Property property = dba->property("Property");
|
||||
auto v1 = dba->insert_vertex();
|
||||
auto v2 = dba->insert_vertex();
|
||||
auto v3 = dba->insert_vertex();
|
||||
@@ -231,9 +231,9 @@ TEST(Interpreter, NodeFilterMultipleLabels) {
|
||||
auto dba = dbms.active();
|
||||
|
||||
// add a few nodes to the database
|
||||
GraphDb::Label label1 = dba->label("label1");
|
||||
GraphDb::Label label2 = dba->label("label2");
|
||||
GraphDb::Label label3 = dba->label("label3");
|
||||
GraphDbTypes::Label label1 = dba->label("label1");
|
||||
GraphDbTypes::Label label2 = dba->label("label2");
|
||||
GraphDbTypes::Label label3 = dba->label("label3");
|
||||
// the test will look for nodes that have label1 and label2
|
||||
dba->insert_vertex(); // NOT accepted
|
||||
dba->insert_vertex().add_label(label1); // NOT accepted
|
||||
@@ -278,8 +278,8 @@ TEST(Interpreter, CreateNodeWithAttributes) {
|
||||
Dbms dbms;
|
||||
auto dba = dbms.active();
|
||||
|
||||
GraphDb::Label label = dba->label("Person");
|
||||
GraphDb::Property property = dba->label("age");
|
||||
GraphDbTypes::Label label = dba->label("Person");
|
||||
GraphDbTypes::Property property = dba->label("age");
|
||||
|
||||
AstTreeStorage storage;
|
||||
SymbolTable symbol_table;
|
||||
@@ -312,8 +312,8 @@ TEST(Interpreter, CreateReturn) {
|
||||
Dbms dbms;
|
||||
auto dba = dbms.active();
|
||||
|
||||
GraphDb::Label label = dba->label("Person");
|
||||
GraphDb::Property property = dba->label("age");
|
||||
GraphDbTypes::Label label = dba->label("Person");
|
||||
GraphDbTypes::Property property = dba->label("age");
|
||||
|
||||
AstTreeStorage storage;
|
||||
SymbolTable symbol_table;
|
||||
@@ -354,10 +354,10 @@ TEST(Interpreter, CreateExpand) {
|
||||
Dbms dbms;
|
||||
auto dba = dbms.active();
|
||||
|
||||
GraphDb::Label label_node_1 = dba->label("Node1");
|
||||
GraphDb::Label label_node_2 = dba->label("Node2");
|
||||
GraphDb::Property property = dba->label("prop");
|
||||
GraphDb::EdgeType edge_type = dba->label("edge_type");
|
||||
GraphDbTypes::Label label_node_1 = dba->label("Node1");
|
||||
GraphDbTypes::Label label_node_2 = dba->label("Node2");
|
||||
GraphDbTypes::Property property = dba->label("prop");
|
||||
GraphDbTypes::EdgeType edge_type = dba->label("edge_type");
|
||||
|
||||
SymbolTable symbol_table;
|
||||
AstTreeStorage storage;
|
||||
@@ -404,7 +404,7 @@ TEST(Interpreter, CreateExpand) {
|
||||
|
||||
for (VertexAccessor vertex : dba->vertices()) {
|
||||
EXPECT_EQ(vertex.labels().size(), 1);
|
||||
GraphDb::Label label = vertex.labels()[0];
|
||||
GraphDbTypes::Label label = vertex.labels()[0];
|
||||
if (label == label_node_1) {
|
||||
// node created by first op
|
||||
EXPECT_EQ(vertex.PropsAt(property).Value<int64_t>(), 1);
|
||||
@@ -460,10 +460,10 @@ TEST(Interpreter, MatchCreateExpand) {
|
||||
dba->insert_vertex();
|
||||
dba->advance_command();
|
||||
|
||||
// GraphDb::Label label_node_1 = dba->label("Node1");
|
||||
// GraphDb::Label label_node_2 = dba->label("Node2");
|
||||
// GraphDb::Property property = dba->label("prop");
|
||||
GraphDb::EdgeType edge_type = dba->label("edge_type");
|
||||
// GraphDbTypes::Label label_node_1 = dba->label("Node1");
|
||||
// GraphDbTypes::Label label_node_2 = dba->label("Node2");
|
||||
// GraphDbTypes::Property property = dba->label("prop");
|
||||
GraphDbTypes::EdgeType edge_type = dba->label("edge_type");
|
||||
|
||||
SymbolTable symbol_table;
|
||||
AstTreeStorage storage;
|
||||
@@ -507,11 +507,11 @@ TEST(Interpreter, Expand) {
|
||||
|
||||
// make a V-graph (v3)<-[r2]-(v1)-[r1]->(v2)
|
||||
auto v1 = dba->insert_vertex();
|
||||
v1.add_label((GraphDb::Label)1);
|
||||
v1.add_label((GraphDbTypes::Label)1);
|
||||
auto v2 = dba->insert_vertex();
|
||||
v2.add_label((GraphDb::Label)2);
|
||||
v2.add_label((GraphDbTypes::Label)2);
|
||||
auto v3 = dba->insert_vertex();
|
||||
v3.add_label((GraphDb::Label)3);
|
||||
v3.add_label((GraphDbTypes::Label)3);
|
||||
auto edge_type = dba->edge_type("Edge");
|
||||
dba->insert_edge(v1, v2, edge_type);
|
||||
dba->insert_edge(v1, v3, edge_type);
|
||||
@@ -585,11 +585,11 @@ TEST(Interpreter, ExpandEdgeCycle) {
|
||||
|
||||
// make a V-graph (v3)<-[r2]-(v1)-[r1]->(v2)
|
||||
auto v1 = dba->insert_vertex();
|
||||
v1.add_label((GraphDb::Label)1);
|
||||
v1.add_label((GraphDbTypes::Label)1);
|
||||
auto v2 = dba->insert_vertex();
|
||||
v2.add_label((GraphDb::Label)2);
|
||||
v2.add_label((GraphDbTypes::Label)2);
|
||||
auto v3 = dba->insert_vertex();
|
||||
v3.add_label((GraphDb::Label)3);
|
||||
v3.add_label((GraphDbTypes::Label)3);
|
||||
auto edge_type = dba->edge_type("Edge");
|
||||
dba->insert_edge(v1, v2, edge_type);
|
||||
dba->insert_edge(v1, v3, edge_type);
|
||||
@@ -631,12 +631,12 @@ TEST(Interpreter, EdgeFilter) {
|
||||
// where only one edge will qualify
|
||||
// and there are all combinations of
|
||||
// (edge_type yes|no) * (property yes|absent|no)
|
||||
std::vector<GraphDb::EdgeType> edge_types;
|
||||
std::vector<GraphDbTypes::EdgeType> edge_types;
|
||||
for (int j = 0; j < 2; ++j)
|
||||
edge_types.push_back(dba->edge_type("et" + std::to_string(j)));
|
||||
std::vector<VertexAccessor> vertices;
|
||||
for (int i = 0; i < 7; ++i) vertices.push_back(dba->insert_vertex());
|
||||
GraphDb::Property prop = dba->property("prop");
|
||||
GraphDbTypes::Property prop = dba->property("prop");
|
||||
std::vector<EdgeAccessor> edges;
|
||||
for (int i = 0; i < 6; ++i) {
|
||||
edges.push_back(
|
||||
@@ -835,7 +835,7 @@ TEST(Interpreter, Filter) {
|
||||
auto dba = dbms.active();
|
||||
|
||||
// add a 6 nodes with property 'prop', 2 have true as value
|
||||
GraphDb::Property property = dba->property("Property");
|
||||
GraphDbTypes::Property property = dba->property("Property");
|
||||
for (int i = 0; i < 6; ++i)
|
||||
dba->insert_vertex().PropsSet(property, i % 3 == 0);
|
||||
dba->insert_vertex(); // prop not set, gives NULL
|
||||
@@ -995,7 +995,7 @@ TEST(Interpreter, SetLabels) {
|
||||
|
||||
auto n = MakeScanAll(storage, symbol_table, "n");
|
||||
auto label_set = std::make_shared<plan::SetLabels>(
|
||||
n.op_, n.sym_, std::vector<GraphDb::Label>{label2, label3});
|
||||
n.op_, n.sym_, std::vector<GraphDbTypes::Label>{label2, label3});
|
||||
EXPECT_EQ(2, PullAll(label_set, *dba, symbol_table));
|
||||
|
||||
for (VertexAccessor vertex : dba->vertices()) {
|
||||
|
||||
@@ -10,9 +10,9 @@ class Prop : public mvcc::Record<Prop> {};
|
||||
|
||||
TEST(MVCC, Case1Test3) {
|
||||
tx::Engine engine;
|
||||
mvcc::VersionList<Prop> version_list;
|
||||
auto t1 = engine.begin();
|
||||
version_list.insert(*t1);
|
||||
Prop *prop;
|
||||
mvcc::VersionList<Prop> version_list(*t1, prop);
|
||||
t1->commit();
|
||||
|
||||
auto t2 = engine.begin();
|
||||
@@ -28,14 +28,18 @@ TEST(MVCC, Case1Test3) {
|
||||
|
||||
TEST(MVCC, InSnapshot) {
|
||||
tx::Engine engine;
|
||||
mvcc::VersionList<Prop> version_list;
|
||||
Prop *prop;
|
||||
auto t1 = engine.begin();
|
||||
auto v1 = version_list.insert(*t1);
|
||||
version_list.update(*t1); // expire old record and create new
|
||||
auto t2 = engine.begin(); // t1 is in snapshot of t2
|
||||
mvcc::VersionList<Prop> version_list(*t1, prop);
|
||||
t1->commit();
|
||||
|
||||
EXPECT_THROW(version_list.update(v1, *t2), SerializationError);
|
||||
auto t2 = engine.begin();
|
||||
version_list.update(*t2);
|
||||
auto t3 = engine.begin(); // t2 is in snapshot of t3
|
||||
auto v = version_list.find(*t3);
|
||||
t2->commit();
|
||||
|
||||
EXPECT_THROW(version_list.update(v, *t3), SerializationError);
|
||||
}
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
|
||||
122
tests/unit/mvcc_gc.cpp
Normal file
122
tests/unit/mvcc_gc.cpp
Normal file
@@ -0,0 +1,122 @@
|
||||
#include "gmock/gmock.h"
|
||||
#include "gtest/gtest.h"
|
||||
|
||||
#include <chrono>
|
||||
#include <memory>
|
||||
|
||||
#include "config/config.hpp"
|
||||
#include "data_structures/concurrent/skiplist.hpp"
|
||||
#include "logging/logger.hpp"
|
||||
#include "logging/streams/stdout.hpp"
|
||||
#include "mvcc/record.hpp"
|
||||
#include "mvcc/version_list.hpp"
|
||||
#include "storage/garbage_collector.hpp"
|
||||
#include "storage/vertex.hpp"
|
||||
#include "transactions/engine.hpp"
|
||||
|
||||
/**
|
||||
* Class which takes an atomic variable to count number of destructor calls (to
|
||||
* test if GC is actually deleting records).
|
||||
*/
|
||||
class Prop : public mvcc::Record<Prop> {
|
||||
public:
|
||||
Prop(std::atomic<int> &count) : count_(count) {}
|
||||
~Prop() { ++count_; }
|
||||
|
||||
private:
|
||||
std::atomic<int> &count_;
|
||||
};
|
||||
|
||||
/**
|
||||
* Test will the mvcc gc delete records inside the version list because they
|
||||
* are not longer visible.
|
||||
*/
|
||||
TEST(VersionList, GcDeleted) {
|
||||
const int UPDATES = 10;
|
||||
tx::Engine engine;
|
||||
std::vector<uint64_t> ids;
|
||||
auto t1 = engine.begin();
|
||||
std::atomic<int> count{0};
|
||||
Prop *prop;
|
||||
mvcc::VersionList<Prop> version_list(*t1, prop, count);
|
||||
ids.push_back(t1->id);
|
||||
t1->commit();
|
||||
|
||||
for (int i = 0; i < UPDATES; ++i) {
|
||||
auto t2 = engine.begin();
|
||||
ids.push_back(t2->id);
|
||||
prop = version_list.update(prop, *t2);
|
||||
t2->commit();
|
||||
}
|
||||
|
||||
EXPECT_EQ(version_list.GcDeleted(ids[0]), false);
|
||||
EXPECT_EQ(count, 0);
|
||||
EXPECT_EQ(version_list.GcDeleted(ids.back() + 1), false);
|
||||
EXPECT_EQ(count, UPDATES);
|
||||
|
||||
auto tl = engine.begin();
|
||||
version_list.remove(*tl);
|
||||
EXPECT_EQ(version_list.GcDeleted(tl->id + 1), true);
|
||||
EXPECT_EQ(count, UPDATES + 1);
|
||||
tl->commit();
|
||||
}
|
||||
|
||||
/**
|
||||
* Test integration of garbage collector with MVCC GC. Delete mvcc's which are
|
||||
* empty (not visible from any future transaction) from the skiplist.
|
||||
*/
|
||||
TEST(GarbageCollector, WaitAndClean) {
|
||||
SkipList<mvcc::VersionList<Prop> *> skiplist;
|
||||
tx::Engine engine;
|
||||
GarbageCollector<Prop> gc(&skiplist, &engine);
|
||||
gc.Run(std::chrono::seconds(1));
|
||||
|
||||
auto t1 = engine.begin();
|
||||
Prop *prop;
|
||||
std::atomic<int> count;
|
||||
auto vl = new mvcc::VersionList<Prop>(*t1, prop, count);
|
||||
|
||||
auto access = skiplist.access();
|
||||
access.insert(vl);
|
||||
t1->commit();
|
||||
auto t2 = engine.begin();
|
||||
EXPECT_EQ(vl->remove(*t2), true);
|
||||
t2->commit();
|
||||
|
||||
std::this_thread::sleep_for(std::chrono::seconds(3));
|
||||
EXPECT_EQ(access.size(), (size_t)0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Same as above, but, the GarbageCollector will never be run because the time
|
||||
* between garbage collections is set to -1.
|
||||
*/
|
||||
TEST(GarbageCollector, WaitAndDontClean) {
|
||||
SkipList<mvcc::VersionList<Prop> *> skiplist;
|
||||
tx::Engine engine;
|
||||
GarbageCollector<Prop> gc(&skiplist, &engine);
|
||||
gc.Run(std::chrono::seconds(-1)); // Never run GC. This test is identical to
|
||||
// the top one except GC is never run.
|
||||
|
||||
auto t1 = engine.begin();
|
||||
Prop *prop;
|
||||
std::atomic<int> count;
|
||||
auto vl = new mvcc::VersionList<Prop>(*t1, prop, count);
|
||||
|
||||
auto access = skiplist.access();
|
||||
access.insert(vl);
|
||||
t1->commit();
|
||||
auto t2 = engine.begin();
|
||||
EXPECT_EQ(vl->remove(*t2), true);
|
||||
t2->commit();
|
||||
|
||||
std::this_thread::sleep_for(std::chrono::seconds(3));
|
||||
EXPECT_EQ(access.size(), (size_t)1);
|
||||
}
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
::logging::init_async();
|
||||
::logging::log->pipe(std::make_unique<Stdout>());
|
||||
::testing::InitGoogleTest(&argc, argv);
|
||||
return RUN_ALL_TESTS();
|
||||
}
|
||||
@@ -1,3 +1,5 @@
|
||||
#include "database/graph_db_datatypes.hpp"
|
||||
|
||||
namespace query {
|
||||
|
||||
namespace test_common {
|
||||
@@ -8,7 +10,7 @@ namespace test_common {
|
||||
/// Name is used to create the Identifier which is used for property lookup.
|
||||
///
|
||||
auto GetPropertyLookup(AstTreeStorage &storage, const std::string &name,
|
||||
GraphDb::Property property) {
|
||||
GraphDbTypes::Property property) {
|
||||
return storage.Create<PropertyLookup>(storage.Create<Identifier>(name),
|
||||
property);
|
||||
}
|
||||
@@ -19,7 +21,7 @@ auto GetPropertyLookup(AstTreeStorage &storage, const std::string &name,
|
||||
/// Name is used to create the Identifier which is assigned to the edge.
|
||||
///
|
||||
auto GetEdge(AstTreeStorage &storage, const std::string &name,
|
||||
GraphDb::EdgeType edge_type = nullptr,
|
||||
GraphDbTypes::EdgeType edge_type = nullptr,
|
||||
EdgeAtom::Direction dir = EdgeAtom::Direction::BOTH) {
|
||||
auto edge = storage.Create<EdgeAtom>(storage.Create<Identifier>(name), dir);
|
||||
if (edge_type) edge->edge_types_.emplace_back(edge_type);
|
||||
@@ -40,7 +42,7 @@ auto GetEdge(AstTreeStorage &storage, const std::string &name,
|
||||
/// Name is used to create the Identifier which is assigned to the node.
|
||||
///
|
||||
auto GetNode(AstTreeStorage &storage, const std::string &name,
|
||||
GraphDb::Label label = nullptr) {
|
||||
GraphDbTypes::Label label = nullptr) {
|
||||
auto node = storage.Create<NodeAtom>(storage.Create<Identifier>(name));
|
||||
if (label) node->labels_.emplace_back(label);
|
||||
return node;
|
||||
@@ -156,7 +158,7 @@ auto GetSet(AstTreeStorage &storage, const std::string &name, Expression *expr,
|
||||
/// Create a set labels clause for given identifier name and labels.
|
||||
///
|
||||
auto GetSet(AstTreeStorage &storage, const std::string &name,
|
||||
std::vector<GraphDb::Label> labels) {
|
||||
std::vector<GraphDbTypes::Label> labels) {
|
||||
return storage.Create<SetLabels>(storage.Create<Identifier>(name), labels);
|
||||
}
|
||||
|
||||
|
||||
@@ -85,8 +85,8 @@ TEST(RecordAccessor, VertexLabels) {
|
||||
|
||||
EXPECT_EQ(v1.labels().size(), 0);
|
||||
|
||||
GraphDb::Label l1 = dba->label("label1");
|
||||
GraphDb::Label l2 = dba->label("label2");
|
||||
GraphDbTypes::Label l1 = dba->label("label1");
|
||||
GraphDbTypes::Label l2 = dba->label("label2");
|
||||
|
||||
// adding labels
|
||||
EXPECT_FALSE(v1.has_label(l1));
|
||||
@@ -106,7 +106,7 @@ TEST(RecordAccessor, VertexLabels) {
|
||||
EXPECT_EQ(labels.size(), 2);
|
||||
|
||||
// removing labels
|
||||
GraphDb::Label l3 = dba->label("label3");
|
||||
GraphDbTypes::Label l3 = dba->label("label3");
|
||||
EXPECT_EQ(v1.remove_label(l3), 0);
|
||||
EXPECT_EQ(labels.size(), 2);
|
||||
|
||||
@@ -124,8 +124,8 @@ TEST(RecordAccessor, EdgeType) {
|
||||
auto v1 = dba->insert_vertex();
|
||||
auto v2 = dba->insert_vertex();
|
||||
|
||||
GraphDb::EdgeType likes = dba->edge_type("likes");
|
||||
GraphDb::EdgeType hates = dba->edge_type("hates");
|
||||
GraphDbTypes::EdgeType likes = dba->edge_type("likes");
|
||||
GraphDbTypes::EdgeType hates = dba->edge_type("hates");
|
||||
|
||||
auto edge = dba->insert_edge(v1, v2, likes);
|
||||
EXPECT_EQ(edge.edge_type(), likes);
|
||||
|
||||
Reference in New Issue
Block a user