Merge branch 'T1191-MG-implement-create-expand-with-multiframe' into T1189-MG-implement-create-node-cursor-mf
This commit is contained in:
commit
c283c6e6ea
@ -168,7 +168,7 @@ class ValidFramesModifier {
|
||||
Iterator &operator++() {
|
||||
do {
|
||||
ptr_++;
|
||||
} while (*this != iterator_wrapper_->end() && ptr_->IsValid());
|
||||
} while (*this != iterator_wrapper_->end() && !ptr_->IsValid());
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
@ -2538,9 +2538,9 @@ class DistributedCreateExpandCursor : public Cursor {
|
||||
|
||||
std::vector<msgs::NewExpand> ExpandCreationInfoToRequests(MultiFrame &multi_frame, ExecutionContext &context) const {
|
||||
std::vector<msgs::NewExpand> edge_requests;
|
||||
auto reader = multi_frame.GetValidFramesModifier();
|
||||
auto frames_modifier = multi_frame.GetValidFramesModifier();
|
||||
|
||||
for (auto &frame : reader) {
|
||||
for (auto &frame : frames_modifier) {
|
||||
const auto &edge_info = self_.edge_info_;
|
||||
msgs::NewExpand request{.id = {context.edge_ids_alloc->AllocateId()}};
|
||||
ExpressionEvaluator evaluator(&frame, context.symbol_table, context.evaluation_context, nullptr,
|
||||
@ -2559,35 +2559,22 @@ class DistributedCreateExpandCursor : public Cursor {
|
||||
request.properties.emplace_back(property_id, storage::v3::TypedValueToValue(value));
|
||||
}
|
||||
}
|
||||
// src, dest
|
||||
|
||||
TypedValue &v1_value = frame[self_.input_symbol_];
|
||||
const auto &v1 = v1_value.ValueVertex();
|
||||
const auto &v2 = OtherVertex(frame);
|
||||
msgs::Edge edge{.src = request.src_vertex,
|
||||
.dst = request.dest_vertex,
|
||||
.properties = request.properties,
|
||||
.id = request.id,
|
||||
.type = request.type};
|
||||
frame[self_.edge_info_.symbol] = TypedValue(accessors::EdgeAccessor(std::move(edge), context.request_router));
|
||||
|
||||
// Set src and dest vertices
|
||||
// TODO(jbajic) Currently we are only handling scenario where vertices
|
||||
// are matched
|
||||
const auto set_vertex = [](const auto &vertex, auto &vertex_id) {
|
||||
vertex_id.first = vertex.PrimaryLabel();
|
||||
vertex_id.second = vertex.GetVertex().id.second;
|
||||
};
|
||||
|
||||
std::invoke([&]() {
|
||||
switch (edge_info.direction) {
|
||||
case EdgeAtom::Direction::IN: {
|
||||
set_vertex(v2, request.src_vertex);
|
||||
set_vertex(v1, request.dest_vertex);
|
||||
break;
|
||||
}
|
||||
case EdgeAtom::Direction::OUT: {
|
||||
set_vertex(v1, request.src_vertex);
|
||||
set_vertex(v2, request.dest_vertex);
|
||||
break;
|
||||
}
|
||||
case EdgeAtom::Direction::BOTH:
|
||||
LOG_FATAL("Must indicate exact expansion direction here");
|
||||
}
|
||||
});
|
||||
request.src_vertex = v1.Id();
|
||||
request.dest_vertex = v2.Id();
|
||||
|
||||
edge_requests.push_back(std::move(request));
|
||||
}
|
||||
|
@ -9,8 +9,6 @@ set(storage_v3_src_files
|
||||
temporal.cpp
|
||||
edge_accessor.cpp
|
||||
indices.cpp
|
||||
key_store.cpp
|
||||
lexicographically_ordered_vertex.cpp
|
||||
property_store.cpp
|
||||
vertex_accessor.cpp
|
||||
schemas.cpp
|
||||
@ -19,6 +17,7 @@ set(storage_v3_src_files
|
||||
shard_rsm.cpp
|
||||
bindings/typed_value.cpp
|
||||
expr.cpp
|
||||
vertex.cpp
|
||||
request_helper.cpp)
|
||||
|
||||
# ######################
|
||||
|
@ -11,17 +11,18 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <memory>
|
||||
#include "storage/v3/edge_ref.hpp"
|
||||
#include "storage/v3/id_types.hpp"
|
||||
#include "storage/v3/property_value.hpp"
|
||||
#include "storage/v3/vertex.hpp"
|
||||
#include "storage/v3/vertex_id.hpp"
|
||||
#include "utils/logging.hpp"
|
||||
|
||||
namespace memgraph::storage::v3 {
|
||||
|
||||
// Forward declarations because we only store pointers here.
|
||||
struct Vertex;
|
||||
struct Edge;
|
||||
struct Delta;
|
||||
struct CommitInfo;
|
||||
@ -129,7 +130,7 @@ inline bool operator==(const PreviousPtr::Pointer &a, const PreviousPtr::Pointer
|
||||
inline bool operator!=(const PreviousPtr::Pointer &a, const PreviousPtr::Pointer &b) { return !(a == b); }
|
||||
|
||||
struct Delta {
|
||||
enum class Action {
|
||||
enum class Action : uint8_t {
|
||||
// Used for both Vertex and Edge
|
||||
DELETE_OBJECT,
|
||||
RECREATE_OBJECT,
|
||||
|
@ -21,7 +21,7 @@
|
||||
|
||||
namespace memgraph::storage::v3 {
|
||||
|
||||
struct Vertex;
|
||||
using EdgeContainer = std::map<Gid, Edge>;
|
||||
|
||||
struct Edge {
|
||||
Edge(Gid gid, Delta *delta) : gid(gid), deleted(false), delta(delta) {
|
||||
|
@ -25,7 +25,6 @@
|
||||
|
||||
namespace memgraph::storage::v3 {
|
||||
|
||||
struct Vertex;
|
||||
class VertexAccessor;
|
||||
struct Indices;
|
||||
|
||||
|
@ -11,12 +11,16 @@
|
||||
|
||||
#include "indices.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
#include <functional>
|
||||
#include <limits>
|
||||
|
||||
#include "storage/v3/delta.hpp"
|
||||
#include "storage/v3/id_types.hpp"
|
||||
#include "storage/v3/mvcc.hpp"
|
||||
#include "storage/v3/property_value.hpp"
|
||||
#include "storage/v3/schemas.hpp"
|
||||
#include "storage/v3/vertex.hpp"
|
||||
#include "utils/bound.hpp"
|
||||
#include "utils/logging.hpp"
|
||||
#include "utils/memory_tracker.hpp"
|
||||
@ -53,9 +57,9 @@ bool AnyVersionHasLabel(const Vertex &vertex, LabelId label, uint64_t timestamp)
|
||||
bool deleted{false};
|
||||
const Delta *delta{nullptr};
|
||||
{
|
||||
has_label = utils::Contains(vertex.labels, label);
|
||||
deleted = vertex.deleted;
|
||||
delta = vertex.delta;
|
||||
has_label = utils::Contains(vertex.second.labels, label);
|
||||
deleted = vertex.second.deleted;
|
||||
delta = vertex.second.delta;
|
||||
}
|
||||
if (!deleted && has_label) {
|
||||
return true;
|
||||
@ -105,10 +109,10 @@ bool AnyVersionHasLabelProperty(const Vertex &vertex, LabelId label, PropertyId
|
||||
bool deleted{false};
|
||||
const Delta *delta{nullptr};
|
||||
{
|
||||
has_label = utils::Contains(vertex.labels, label);
|
||||
current_value_equal_to_value = vertex.properties.IsPropertyEqual(key, value);
|
||||
deleted = vertex.deleted;
|
||||
delta = vertex.delta;
|
||||
has_label = utils::Contains(vertex.second.labels, label);
|
||||
current_value_equal_to_value = vertex.second.properties.IsPropertyEqual(key, value);
|
||||
deleted = vertex.second.deleted;
|
||||
delta = vertex.second.delta;
|
||||
}
|
||||
|
||||
if (!deleted && has_label && current_value_equal_to_value) {
|
||||
@ -163,9 +167,9 @@ bool CurrentVersionHasLabel(const Vertex &vertex, LabelId label, Transaction *tr
|
||||
bool has_label{false};
|
||||
const Delta *delta{nullptr};
|
||||
{
|
||||
deleted = vertex.deleted;
|
||||
has_label = utils::Contains(vertex.labels, label);
|
||||
delta = vertex.delta;
|
||||
deleted = vertex.second.deleted;
|
||||
has_label = utils::Contains(vertex.second.labels, label);
|
||||
delta = vertex.second.delta;
|
||||
}
|
||||
ApplyDeltasForRead(transaction, delta, view, [&deleted, &has_label, label](const Delta &delta) {
|
||||
switch (delta.action) {
|
||||
@ -214,10 +218,10 @@ bool CurrentVersionHasLabelProperty(const Vertex &vertex, LabelId label, Propert
|
||||
bool current_value_equal_to_value = value.IsNull();
|
||||
const Delta *delta{nullptr};
|
||||
{
|
||||
deleted = vertex.deleted;
|
||||
has_label = utils::Contains(vertex.labels, label);
|
||||
current_value_equal_to_value = vertex.properties.IsPropertyEqual(key, value);
|
||||
delta = vertex.delta;
|
||||
deleted = vertex.second.deleted;
|
||||
has_label = utils::Contains(vertex.second.labels, label);
|
||||
current_value_equal_to_value = vertex.second.properties.IsPropertyEqual(key, value);
|
||||
delta = vertex.second.delta;
|
||||
}
|
||||
ApplyDeltasForRead(transaction, delta, view,
|
||||
[&deleted, &has_label, ¤t_value_equal_to_value, key, label, &value](const Delta &delta) {
|
||||
@ -265,11 +269,10 @@ bool CurrentVersionHasLabelProperty(const Vertex &vertex, LabelId label, Propert
|
||||
void LabelIndex::UpdateOnAddLabel(LabelId label, Vertex *vertex, const Transaction &tx) {
|
||||
auto it = index_.find(label);
|
||||
if (it == index_.end()) return;
|
||||
auto acc = it->second.access();
|
||||
acc.insert(Entry{vertex, tx.start_timestamp.logical_id});
|
||||
it->second.insert(Entry{vertex, tx.start_timestamp.logical_id});
|
||||
}
|
||||
|
||||
bool LabelIndex::CreateIndex(LabelId label, VerticesSkipList::Accessor vertices) {
|
||||
bool LabelIndex::CreateIndex(LabelId label, VertexContainer &vertices) {
|
||||
utils::MemoryTracker::OutOfMemoryExceptionEnabler oom_exception;
|
||||
auto [it, emplaced] = index_.emplace(std::piecewise_construct, std::forward_as_tuple(label), std::forward_as_tuple());
|
||||
if (!emplaced) {
|
||||
@ -277,13 +280,11 @@ bool LabelIndex::CreateIndex(LabelId label, VerticesSkipList::Accessor vertices)
|
||||
return false;
|
||||
}
|
||||
try {
|
||||
auto acc = it->second.access();
|
||||
for (auto &lgo_vertex : vertices) {
|
||||
auto &vertex = lgo_vertex.vertex;
|
||||
if (vertex.deleted || !utils::Contains(vertex.labels, label)) {
|
||||
for (auto &vertex : vertices) {
|
||||
if (vertex.second.deleted || !VertexHasLabel(vertex, label)) {
|
||||
continue;
|
||||
}
|
||||
acc.insert(Entry{&vertex, 0});
|
||||
it->second.insert(Entry{&vertex, 0});
|
||||
}
|
||||
} catch (const utils::OutOfMemoryException &) {
|
||||
utils::MemoryTracker::OutOfMemoryExceptionBlocker oom_exception_blocker;
|
||||
@ -304,7 +305,7 @@ std::vector<LabelId> LabelIndex::ListIndices() const {
|
||||
|
||||
void LabelIndex::RemoveObsoleteEntries(const uint64_t clean_up_before_timestamp) {
|
||||
for (auto &label_storage : index_) {
|
||||
auto vertices_acc = label_storage.second.access();
|
||||
auto &vertices_acc = label_storage.second;
|
||||
for (auto it = vertices_acc.begin(); it != vertices_acc.end();) {
|
||||
auto next_it = it;
|
||||
++next_it;
|
||||
@ -316,7 +317,7 @@ void LabelIndex::RemoveObsoleteEntries(const uint64_t clean_up_before_timestamp)
|
||||
|
||||
if ((next_it != vertices_acc.end() && it->vertex == next_it->vertex) ||
|
||||
!AnyVersionHasLabel(*it->vertex, label_storage.first, clean_up_before_timestamp)) {
|
||||
vertices_acc.remove(*it);
|
||||
vertices_acc.erase(*it);
|
||||
}
|
||||
|
||||
it = next_it;
|
||||
@ -324,7 +325,7 @@ void LabelIndex::RemoveObsoleteEntries(const uint64_t clean_up_before_timestamp)
|
||||
}
|
||||
}
|
||||
|
||||
LabelIndex::Iterable::Iterator::Iterator(Iterable *self, utils::SkipList<Entry>::Iterator index_iterator)
|
||||
LabelIndex::Iterable::Iterator::Iterator(Iterable *self, LabelIndexContainer::iterator index_iterator)
|
||||
: self_(self),
|
||||
index_iterator_(index_iterator),
|
||||
current_vertex_accessor_(nullptr, nullptr, nullptr, self_->config_, *self_->vertex_validator_),
|
||||
@ -339,7 +340,7 @@ LabelIndex::Iterable::Iterator &LabelIndex::Iterable::Iterator::operator++() {
|
||||
}
|
||||
|
||||
void LabelIndex::Iterable::Iterator::AdvanceUntilValid() {
|
||||
for (; index_iterator_ != self_->index_accessor_.end(); ++index_iterator_) {
|
||||
for (; index_iterator_ != self_->index_container_->end(); ++index_iterator_) {
|
||||
if (index_iterator_->vertex == current_vertex_) {
|
||||
continue;
|
||||
}
|
||||
@ -352,10 +353,9 @@ void LabelIndex::Iterable::Iterator::AdvanceUntilValid() {
|
||||
}
|
||||
}
|
||||
|
||||
LabelIndex::Iterable::Iterable(utils::SkipList<Entry>::Accessor index_accessor, LabelId label, View view,
|
||||
Transaction *transaction, Indices *indices, Config::Items config,
|
||||
const VertexValidator &vertex_validator)
|
||||
: index_accessor_(std::move(index_accessor)),
|
||||
LabelIndex::Iterable::Iterable(LabelIndexContainer &index_container, LabelId label, View view, Transaction *transaction,
|
||||
Indices *indices, Config::Items config, const VertexValidator &vertex_validator)
|
||||
: index_container_(&index_container),
|
||||
label_(label),
|
||||
view_(view),
|
||||
transaction_(transaction),
|
||||
@ -363,12 +363,6 @@ LabelIndex::Iterable::Iterable(utils::SkipList<Entry>::Accessor index_accessor,
|
||||
config_(config),
|
||||
vertex_validator_(&vertex_validator) {}
|
||||
|
||||
void LabelIndex::RunGC() {
|
||||
for (auto &index_entry : index_) {
|
||||
index_entry.second.run_gc();
|
||||
}
|
||||
}
|
||||
|
||||
bool LabelPropertyIndex::Entry::operator<(const Entry &rhs) const {
|
||||
if (value < rhs.value) {
|
||||
return true;
|
||||
@ -388,14 +382,13 @@ bool LabelPropertyIndex::Entry::operator<(const PropertyValue &rhs) const { retu
|
||||
bool LabelPropertyIndex::Entry::operator==(const PropertyValue &rhs) const { return value == rhs; }
|
||||
|
||||
void LabelPropertyIndex::UpdateOnAddLabel(LabelId label, Vertex *vertex, const Transaction &tx) {
|
||||
for (auto &[label_prop, storage] : index_) {
|
||||
for (auto &[label_prop, index] : index_) {
|
||||
if (label_prop.first != label) {
|
||||
continue;
|
||||
}
|
||||
auto prop_value = vertex->properties.GetProperty(label_prop.second);
|
||||
auto prop_value = vertex->second.properties.GetProperty(label_prop.second);
|
||||
if (!prop_value.IsNull()) {
|
||||
auto acc = storage.access();
|
||||
acc.insert(Entry{std::move(prop_value), vertex, tx.start_timestamp.logical_id});
|
||||
index.emplace(Entry{prop_value, vertex, tx.start_timestamp.logical_id});
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -405,18 +398,17 @@ void LabelPropertyIndex::UpdateOnSetProperty(PropertyId property, const Property
|
||||
if (value.IsNull()) {
|
||||
return;
|
||||
}
|
||||
for (auto &[label_prop, storage] : index_) {
|
||||
for (auto &[label_prop, index] : index_) {
|
||||
if (label_prop.second != property) {
|
||||
continue;
|
||||
}
|
||||
if (utils::Contains(vertex->labels, label_prop.first)) {
|
||||
auto acc = storage.access();
|
||||
acc.insert(Entry{value, vertex, tx.start_timestamp.logical_id});
|
||||
if (VertexHasLabel(*vertex, label_prop.first)) {
|
||||
index.emplace(Entry{value, vertex, tx.start_timestamp.logical_id});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool LabelPropertyIndex::CreateIndex(LabelId label, PropertyId property, VerticesSkipList::Accessor vertices) {
|
||||
bool LabelPropertyIndex::CreateIndex(LabelId label, PropertyId property, VertexContainer &vertices) {
|
||||
utils::MemoryTracker::OutOfMemoryExceptionEnabler oom_exception;
|
||||
auto [it, emplaced] =
|
||||
index_.emplace(std::piecewise_construct, std::forward_as_tuple(label, property), std::forward_as_tuple());
|
||||
@ -425,17 +417,15 @@ bool LabelPropertyIndex::CreateIndex(LabelId label, PropertyId property, Vertice
|
||||
return false;
|
||||
}
|
||||
try {
|
||||
auto acc = it->second.access();
|
||||
for (auto &lgo_vertex : vertices) {
|
||||
auto &vertex = lgo_vertex.vertex;
|
||||
if (vertex.deleted || !utils::Contains(vertex.labels, label)) {
|
||||
for (auto &vertex : vertices) {
|
||||
if (vertex.second.deleted || !VertexHasLabel(vertex, label)) {
|
||||
continue;
|
||||
}
|
||||
auto value = vertex.properties.GetProperty(property);
|
||||
auto value = vertex.second.properties.GetProperty(property);
|
||||
if (value.IsNull()) {
|
||||
continue;
|
||||
}
|
||||
acc.insert(Entry{std::move(value), &vertex, 0});
|
||||
it->second.emplace(Entry{value, &vertex, 0});
|
||||
}
|
||||
} catch (const utils::OutOfMemoryException &) {
|
||||
utils::MemoryTracker::OutOfMemoryExceptionBlocker oom_exception_blocker;
|
||||
@ -456,8 +446,7 @@ std::vector<std::pair<LabelId, PropertyId>> LabelPropertyIndex::ListIndices() co
|
||||
|
||||
void LabelPropertyIndex::RemoveObsoleteEntries(const uint64_t clean_up_before_timestamp) {
|
||||
for (auto &[label_property, index] : index_) {
|
||||
auto index_acc = index.access();
|
||||
for (auto it = index_acc.begin(); it != index_acc.end();) {
|
||||
for (auto it = index.begin(); it != index.end();) {
|
||||
auto next_it = it;
|
||||
++next_it;
|
||||
|
||||
@ -466,17 +455,17 @@ void LabelPropertyIndex::RemoveObsoleteEntries(const uint64_t clean_up_before_ti
|
||||
continue;
|
||||
}
|
||||
|
||||
if ((next_it != index_acc.end() && it->vertex == next_it->vertex && it->value == next_it->value) ||
|
||||
if ((next_it != index.end() && it->vertex == next_it->vertex && it->value == next_it->value) ||
|
||||
!AnyVersionHasLabelProperty(*it->vertex, label_property.first, label_property.second, it->value,
|
||||
clean_up_before_timestamp)) {
|
||||
index_acc.remove(*it);
|
||||
index.erase(it);
|
||||
}
|
||||
it = next_it;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
LabelPropertyIndex::Iterable::Iterator::Iterator(Iterable *self, utils::SkipList<Entry>::Iterator index_iterator)
|
||||
LabelPropertyIndex::Iterable::Iterator::Iterator(Iterable *self, LabelPropertyIndexContainer::iterator index_iterator)
|
||||
: self_(self),
|
||||
index_iterator_(index_iterator),
|
||||
current_vertex_accessor_(nullptr, nullptr, nullptr, self_->config_, *self_->vertex_validator_),
|
||||
@ -491,7 +480,7 @@ LabelPropertyIndex::Iterable::Iterator &LabelPropertyIndex::Iterable::Iterator::
|
||||
}
|
||||
|
||||
void LabelPropertyIndex::Iterable::Iterator::AdvanceUntilValid() {
|
||||
for (; index_iterator_ != self_->index_accessor_.end(); ++index_iterator_) {
|
||||
for (; index_iterator_ != self_->index_container_->end(); ++index_iterator_) {
|
||||
if (index_iterator_->vertex == current_vertex_) {
|
||||
continue;
|
||||
}
|
||||
@ -506,11 +495,11 @@ void LabelPropertyIndex::Iterable::Iterator::AdvanceUntilValid() {
|
||||
}
|
||||
if (self_->upper_bound_) {
|
||||
if (self_->upper_bound_->value() < index_iterator_->value) {
|
||||
index_iterator_ = self_->index_accessor_.end();
|
||||
index_iterator_ = self_->index_container_->end();
|
||||
break;
|
||||
}
|
||||
if (!self_->upper_bound_->IsInclusive() && index_iterator_->value == self_->upper_bound_->value()) {
|
||||
index_iterator_ = self_->index_accessor_.end();
|
||||
index_iterator_ = self_->index_container_->end();
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -537,13 +526,12 @@ const PropertyValue kSmallestMap = PropertyValue(std::map<std::string, PropertyV
|
||||
const PropertyValue kSmallestTemporalData =
|
||||
PropertyValue(TemporalData{static_cast<TemporalType>(0), std::numeric_limits<int64_t>::min()});
|
||||
|
||||
LabelPropertyIndex::Iterable::Iterable(utils::SkipList<Entry>::Accessor index_accessor, LabelId label,
|
||||
PropertyId property,
|
||||
LabelPropertyIndex::Iterable::Iterable(LabelPropertyIndexContainer &index_container, LabelId label, PropertyId property,
|
||||
const std::optional<utils::Bound<PropertyValue>> &lower_bound,
|
||||
const std::optional<utils::Bound<PropertyValue>> &upper_bound, View view,
|
||||
Transaction *transaction, Indices *indices, Config::Items config,
|
||||
const VertexValidator &vertex_validator)
|
||||
: index_accessor_(std::move(index_accessor)),
|
||||
: index_container_(&index_container),
|
||||
label_(label),
|
||||
property_(property),
|
||||
lower_bound_(lower_bound),
|
||||
@ -651,49 +639,55 @@ LabelPropertyIndex::Iterable::Iterator LabelPropertyIndex::Iterable::begin() {
|
||||
// If the bounds are set and don't have comparable types we don't yield any
|
||||
// items from the index.
|
||||
if (!bounds_valid_) {
|
||||
return {this, index_accessor_.end()};
|
||||
return {this, index_container_->end()};
|
||||
}
|
||||
auto index_iterator = index_accessor_.begin();
|
||||
if (lower_bound_) {
|
||||
index_iterator = index_accessor_.find_equal_or_greater(lower_bound_->value());
|
||||
return {this, std::ranges::lower_bound(*index_container_, lower_bound_->value(), std::less{}, &Entry::value)};
|
||||
}
|
||||
return {this, index_iterator};
|
||||
return {this, index_container_->begin()};
|
||||
}
|
||||
|
||||
LabelPropertyIndex::Iterable::Iterator LabelPropertyIndex::Iterable::end() { return {this, index_accessor_.end()}; }
|
||||
LabelPropertyIndex::Iterable::Iterator LabelPropertyIndex::Iterable::end() { return {this, index_container_->end()}; }
|
||||
|
||||
int64_t LabelPropertyIndex::ApproximateVertexCount(LabelId label, PropertyId property,
|
||||
const PropertyValue &value) const {
|
||||
int64_t LabelPropertyIndex::VertexCount(LabelId label, PropertyId property, const PropertyValue &value) const {
|
||||
auto it = index_.find({label, property});
|
||||
MG_ASSERT(it != index_.end(), "Index for label {} and property {} doesn't exist", label.AsUint(), property.AsUint());
|
||||
auto acc = it->second.access();
|
||||
if (!value.IsNull()) {
|
||||
return static_cast<int64_t>(
|
||||
acc.estimate_count(value, static_cast<int>(utils::SkipListLayerForCountEstimation(acc.size()))));
|
||||
}
|
||||
// The value `Null` won't ever appear in the index because it indicates that
|
||||
// the property shouldn't exist. Instead, this value is used as an indicator
|
||||
// to estimate the average number of equal elements in the list (for any
|
||||
// given value).
|
||||
return static_cast<int64_t>(acc.estimate_average_number_of_equals(
|
||||
[](const auto &first, const auto &second) { return first.value == second.value; },
|
||||
static_cast<int>(utils::SkipListLayerForAverageEqualsEstimation(acc.size()))));
|
||||
}
|
||||
MG_ASSERT(!value.IsNull(), "Null is not supported!");
|
||||
|
||||
int64_t LabelPropertyIndex::ApproximateVertexCount(LabelId label, PropertyId property,
|
||||
const std::optional<utils::Bound<PropertyValue>> &lower,
|
||||
const std::optional<utils::Bound<PropertyValue>> &upper) const {
|
||||
auto it = index_.find({label, property});
|
||||
MG_ASSERT(it != index_.end(), "Index for label {} and property {} doesn't exist", label.AsUint(), property.AsUint());
|
||||
auto acc = it->second.access();
|
||||
// TODO(jbajic) This can be improved by exiting early
|
||||
auto start_it = std::ranges::lower_bound(it->second, value, std::less{}, &Entry::value);
|
||||
return static_cast<int64_t>(
|
||||
acc.estimate_range_count(lower, upper, static_cast<int>(utils::SkipListLayerForCountEstimation(acc.size()))));
|
||||
std::ranges::count_if(start_it, it->second.end(), [&value](const auto &elem) { return elem.value == value; }));
|
||||
}
|
||||
|
||||
void LabelPropertyIndex::RunGC() {
|
||||
for (auto &index_entry : index_) {
|
||||
index_entry.second.run_gc();
|
||||
}
|
||||
int64_t LabelPropertyIndex::VertexCount(LabelId label, PropertyId property,
|
||||
const std::optional<utils::Bound<PropertyValue>> &lower,
|
||||
const std::optional<utils::Bound<PropertyValue>> &upper) const {
|
||||
auto it = index_.find({label, property});
|
||||
MG_ASSERT(it != index_.end(), "Index for label {} and property {} doesn't exist", label.AsUint(), property.AsUint());
|
||||
const auto lower_it = std::invoke(
|
||||
[&index = it->second](const auto value, const auto def) {
|
||||
if (value) {
|
||||
if (value->IsInclusive()) {
|
||||
return std::ranges::lower_bound(index, value->value(), std::less{}, &Entry::value);
|
||||
}
|
||||
return std::ranges::upper_bound(index, value->value(), std::less{}, &Entry::value);
|
||||
}
|
||||
return def;
|
||||
},
|
||||
lower, it->second.begin());
|
||||
const auto upper_it = std::invoke(
|
||||
[&index = it->second](const auto value, const auto def) {
|
||||
if (value) {
|
||||
if (value->IsInclusive()) {
|
||||
return std::ranges::upper_bound(index, value->value(), std::less{}, &Entry::value);
|
||||
}
|
||||
return std::ranges::lower_bound(index, value->value(), std::less{}, &Entry::value);
|
||||
}
|
||||
return def;
|
||||
},
|
||||
upper, it->second.end());
|
||||
return static_cast<int64_t>(std::distance(lower_it, upper_it));
|
||||
}
|
||||
|
||||
void RemoveObsoleteEntries(Indices *indices, const uint64_t clean_up_before_timestamp) {
|
||||
|
@ -13,6 +13,7 @@
|
||||
|
||||
#include <cstdint>
|
||||
#include <optional>
|
||||
#include <set>
|
||||
#include <tuple>
|
||||
#include <utility>
|
||||
|
||||
@ -20,7 +21,6 @@
|
||||
#include "storage/v3/property_value.hpp"
|
||||
#include "storage/v3/transaction.hpp"
|
||||
#include "storage/v3/vertex_accessor.hpp"
|
||||
#include "storage/v3/vertices_skip_list.hpp"
|
||||
#include "utils/bound.hpp"
|
||||
#include "utils/logging.hpp"
|
||||
#include "utils/skip_list.hpp"
|
||||
@ -30,7 +30,6 @@ namespace memgraph::storage::v3 {
|
||||
struct Indices;
|
||||
|
||||
class LabelIndex {
|
||||
private:
|
||||
struct Entry {
|
||||
Vertex *vertex;
|
||||
uint64_t timestamp;
|
||||
@ -41,17 +40,9 @@ class LabelIndex {
|
||||
bool operator==(const Entry &rhs) const { return vertex == rhs.vertex && timestamp == rhs.timestamp; }
|
||||
};
|
||||
|
||||
struct LabelStorage {
|
||||
LabelId label;
|
||||
utils::SkipList<Entry> vertices;
|
||||
|
||||
bool operator<(const LabelStorage &rhs) const { return label < rhs.label; }
|
||||
bool operator<(LabelId rhs) const { return label < rhs; }
|
||||
bool operator==(const LabelStorage &rhs) const { return label == rhs.label; }
|
||||
bool operator==(LabelId rhs) const { return label == rhs; }
|
||||
};
|
||||
|
||||
public:
|
||||
using LabelIndexContainer = std::set<Entry>;
|
||||
|
||||
LabelIndex(Indices *indices, Config::Items config, const VertexValidator &vertex_validator)
|
||||
: indices_(indices), config_(config), vertex_validator_{&vertex_validator} {}
|
||||
|
||||
@ -59,7 +50,7 @@ class LabelIndex {
|
||||
void UpdateOnAddLabel(LabelId label, Vertex *vertex, const Transaction &tx);
|
||||
|
||||
/// @throw std::bad_alloc
|
||||
bool CreateIndex(LabelId label, VerticesSkipList::Accessor vertices);
|
||||
bool CreateIndex(LabelId label, VertexContainer &vertices);
|
||||
|
||||
/// Returns false if there was no index to drop
|
||||
bool DropIndex(LabelId label) { return index_.erase(label) > 0; }
|
||||
@ -72,12 +63,12 @@ class LabelIndex {
|
||||
|
||||
class Iterable {
|
||||
public:
|
||||
Iterable(utils::SkipList<Entry>::Accessor index_accessor, LabelId label, View view, Transaction *transaction,
|
||||
Indices *indices, Config::Items config, const VertexValidator &vertex_validator);
|
||||
Iterable(LabelIndexContainer &index_container, LabelId label, View view, Transaction *transaction, Indices *indices,
|
||||
Config::Items config, const VertexValidator &vertex_validator);
|
||||
|
||||
class Iterator {
|
||||
public:
|
||||
Iterator(Iterable *self, utils::SkipList<Entry>::Iterator index_iterator);
|
||||
Iterator(Iterable *self, LabelIndexContainer::iterator index_iterator);
|
||||
|
||||
VertexAccessor operator*() const { return current_vertex_accessor_; }
|
||||
|
||||
@ -90,16 +81,16 @@ class LabelIndex {
|
||||
void AdvanceUntilValid();
|
||||
|
||||
Iterable *self_;
|
||||
utils::SkipList<Entry>::Iterator index_iterator_;
|
||||
LabelIndexContainer::iterator index_iterator_;
|
||||
VertexAccessor current_vertex_accessor_;
|
||||
Vertex *current_vertex_;
|
||||
};
|
||||
|
||||
Iterator begin() { return {this, index_accessor_.begin()}; }
|
||||
Iterator end() { return {this, index_accessor_.end()}; }
|
||||
Iterator begin() { return {this, index_container_->begin()}; }
|
||||
Iterator end() { return {this, index_container_->end()}; }
|
||||
|
||||
private:
|
||||
utils::SkipList<Entry>::Accessor index_accessor_;
|
||||
LabelIndexContainer *index_container_;
|
||||
LabelId label_;
|
||||
View view_;
|
||||
Transaction *transaction_;
|
||||
@ -112,7 +103,7 @@ class LabelIndex {
|
||||
Iterable Vertices(LabelId label, View view, Transaction *transaction) {
|
||||
auto it = index_.find(label);
|
||||
MG_ASSERT(it != index_.end(), "Index for label {} doesn't exist", label.AsUint());
|
||||
return {it->second.access(), label, view, transaction, indices_, config_, *vertex_validator_};
|
||||
return {it->second, label, view, transaction, indices_, config_, *vertex_validator_};
|
||||
}
|
||||
|
||||
int64_t ApproximateVertexCount(LabelId label) {
|
||||
@ -123,17 +114,14 @@ class LabelIndex {
|
||||
|
||||
void Clear() { index_.clear(); }
|
||||
|
||||
void RunGC();
|
||||
|
||||
private:
|
||||
std::map<LabelId, utils::SkipList<Entry>> index_;
|
||||
std::map<LabelId, LabelIndexContainer> index_;
|
||||
Indices *indices_;
|
||||
Config::Items config_;
|
||||
const VertexValidator *vertex_validator_;
|
||||
};
|
||||
|
||||
class LabelPropertyIndex {
|
||||
private:
|
||||
struct Entry {
|
||||
PropertyValue value;
|
||||
Vertex *vertex;
|
||||
@ -147,6 +135,8 @@ class LabelPropertyIndex {
|
||||
};
|
||||
|
||||
public:
|
||||
using LabelPropertyIndexContainer = std::set<Entry>;
|
||||
|
||||
LabelPropertyIndex(Indices *indices, Config::Items config, const VertexValidator &vertex_validator)
|
||||
: indices_(indices), config_(config), vertex_validator_{&vertex_validator} {}
|
||||
|
||||
@ -157,7 +147,7 @@ class LabelPropertyIndex {
|
||||
void UpdateOnSetProperty(PropertyId property, const PropertyValue &value, Vertex *vertex, const Transaction &tx);
|
||||
|
||||
/// @throw std::bad_alloc
|
||||
bool CreateIndex(LabelId label, PropertyId property, VerticesSkipList::Accessor vertices);
|
||||
bool CreateIndex(LabelId label, PropertyId property, VertexContainer &vertices);
|
||||
|
||||
bool DropIndex(LabelId label, PropertyId property) { return index_.erase({label, property}) > 0; }
|
||||
|
||||
@ -169,14 +159,14 @@ class LabelPropertyIndex {
|
||||
|
||||
class Iterable {
|
||||
public:
|
||||
Iterable(utils::SkipList<Entry>::Accessor index_accessor, LabelId label, PropertyId property,
|
||||
Iterable(LabelPropertyIndexContainer &index_container, LabelId label, PropertyId property,
|
||||
const std::optional<utils::Bound<PropertyValue>> &lower_bound,
|
||||
const std::optional<utils::Bound<PropertyValue>> &upper_bound, View view, Transaction *transaction,
|
||||
Indices *indices, Config::Items config, const VertexValidator &vertex_validator);
|
||||
|
||||
class Iterator {
|
||||
public:
|
||||
Iterator(Iterable *self, utils::SkipList<Entry>::Iterator index_iterator);
|
||||
Iterator(Iterable *self, LabelPropertyIndexContainer::iterator index_iterator);
|
||||
|
||||
VertexAccessor operator*() const { return current_vertex_accessor_; }
|
||||
|
||||
@ -189,7 +179,7 @@ class LabelPropertyIndex {
|
||||
void AdvanceUntilValid();
|
||||
|
||||
Iterable *self_;
|
||||
utils::SkipList<Entry>::Iterator index_iterator_;
|
||||
LabelPropertyIndexContainer::iterator index_iterator_;
|
||||
VertexAccessor current_vertex_accessor_;
|
||||
Vertex *current_vertex_;
|
||||
};
|
||||
@ -198,7 +188,7 @@ class LabelPropertyIndex {
|
||||
Iterator end();
|
||||
|
||||
private:
|
||||
utils::SkipList<Entry>::Accessor index_accessor_;
|
||||
LabelPropertyIndexContainer *index_container_;
|
||||
LabelId label_;
|
||||
PropertyId property_;
|
||||
std::optional<utils::Bound<PropertyValue>> lower_bound_;
|
||||
@ -217,11 +207,11 @@ class LabelPropertyIndex {
|
||||
auto it = index_.find({label, property});
|
||||
MG_ASSERT(it != index_.end(), "Index for label {} and property {} doesn't exist", label.AsUint(),
|
||||
property.AsUint());
|
||||
return {it->second.access(), label, property, lower_bound, upper_bound, view,
|
||||
transaction, indices_, config_, *vertex_validator_};
|
||||
return {it->second, label, property, lower_bound, upper_bound,
|
||||
view, transaction, indices_, config_, *vertex_validator_};
|
||||
}
|
||||
|
||||
int64_t ApproximateVertexCount(LabelId label, PropertyId property) const {
|
||||
int64_t VertexCount(LabelId label, PropertyId property) const {
|
||||
auto it = index_.find({label, property});
|
||||
MG_ASSERT(it != index_.end(), "Index for label {} and property {} doesn't exist", label.AsUint(),
|
||||
property.AsUint());
|
||||
@ -232,18 +222,15 @@ class LabelPropertyIndex {
|
||||
/// an estimated count of nodes which have their property's value set to
|
||||
/// `value`. If the `value` specified is `Null`, then an average number of
|
||||
/// equal elements is returned.
|
||||
int64_t ApproximateVertexCount(LabelId label, PropertyId property, const PropertyValue &value) const;
|
||||
int64_t VertexCount(LabelId label, PropertyId property, const PropertyValue &value) const;
|
||||
|
||||
int64_t ApproximateVertexCount(LabelId label, PropertyId property,
|
||||
const std::optional<utils::Bound<PropertyValue>> &lower,
|
||||
const std::optional<utils::Bound<PropertyValue>> &upper) const;
|
||||
int64_t VertexCount(LabelId label, PropertyId property, const std::optional<utils::Bound<PropertyValue>> &lower,
|
||||
const std::optional<utils::Bound<PropertyValue>> &upper) const;
|
||||
|
||||
void Clear() { index_.clear(); }
|
||||
|
||||
void RunGC();
|
||||
|
||||
private:
|
||||
std::map<std::pair<LabelId, PropertyId>, utils::SkipList<Entry>> index_;
|
||||
std::map<std::pair<LabelId, PropertyId>, LabelPropertyIndexContainer> index_;
|
||||
Indices *indices_;
|
||||
Config::Items config_;
|
||||
const VertexValidator *vertex_validator_;
|
||||
|
@ -1,43 +0,0 @@
|
||||
// Copyright 2022 Memgraph Ltd.
|
||||
//
|
||||
// Use of this software is governed by the Business Source License
|
||||
// included in the file licenses/BSL.txt; by using this file, you agree to be bound by the terms of the Business Source
|
||||
// License, and you may not use this file except in compliance with the Business Source License.
|
||||
//
|
||||
// As of the Change Date specified in that file, in accordance with
|
||||
// the Business Source License, use of this software will be governed
|
||||
// by the Apache License, Version 2.0, included in the file
|
||||
// licenses/APL.txt.
|
||||
|
||||
#include <algorithm>
|
||||
#include <iterator>
|
||||
#include <ranges>
|
||||
|
||||
#include "storage/v3/id_types.hpp"
|
||||
#include "storage/v3/key_store.hpp"
|
||||
#include "storage/v3/property_value.hpp"
|
||||
|
||||
namespace memgraph::storage::v3 {
|
||||
|
||||
KeyStore::KeyStore(const PrimaryKey &key_values) {
|
||||
for (auto i = 0; i < key_values.size(); ++i) {
|
||||
MG_ASSERT(!key_values[i].IsNull());
|
||||
store_.SetProperty(PropertyId::FromInt(i), key_values[i]);
|
||||
}
|
||||
}
|
||||
|
||||
PropertyValue KeyStore::GetKey(const size_t index) const { return store_.GetProperty(PropertyId::FromUint(index)); }
|
||||
|
||||
PropertyValue KeyStore::GetKey(const PropertyId property_id) const { return store_.GetProperty(property_id); }
|
||||
|
||||
PrimaryKey KeyStore::Keys() const {
|
||||
auto keys_map = store_.Properties();
|
||||
PrimaryKey keys;
|
||||
keys.reserve(keys_map.size());
|
||||
std::ranges::transform(
|
||||
keys_map, std::back_inserter(keys),
|
||||
[](std::pair<const PropertyId, PropertyValue> &id_and_value) { return std::move(id_and_value.second); });
|
||||
return keys;
|
||||
}
|
||||
|
||||
} // namespace memgraph::storage::v3
|
@ -11,12 +11,6 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <algorithm>
|
||||
#include <compare>
|
||||
#include <functional>
|
||||
|
||||
#include "storage/v3/id_types.hpp"
|
||||
#include "storage/v3/property_store.hpp"
|
||||
#include "storage/v3/property_value.hpp"
|
||||
|
||||
namespace memgraph::storage::v3 {
|
||||
@ -24,39 +18,4 @@ namespace memgraph::storage::v3 {
|
||||
// Primary key is a collection of primary properties.
|
||||
using PrimaryKey = std::vector<PropertyValue>;
|
||||
|
||||
class KeyStore {
|
||||
public:
|
||||
explicit KeyStore(const PrimaryKey &key_values);
|
||||
|
||||
KeyStore(const KeyStore &) = delete;
|
||||
KeyStore(KeyStore &&other) noexcept = default;
|
||||
KeyStore &operator=(const KeyStore &) = delete;
|
||||
KeyStore &operator=(KeyStore &&other) noexcept = default;
|
||||
|
||||
~KeyStore() = default;
|
||||
|
||||
PropertyValue GetKey(size_t index) const;
|
||||
|
||||
PropertyValue GetKey(PropertyId property) const;
|
||||
|
||||
PrimaryKey Keys() const;
|
||||
|
||||
friend bool operator<(const KeyStore &lhs, const KeyStore &rhs) {
|
||||
return std::ranges::lexicographical_compare(lhs.Keys(), rhs.Keys(), std::less<PropertyValue>{});
|
||||
}
|
||||
|
||||
friend bool operator==(const KeyStore &lhs, const KeyStore &rhs) {
|
||||
return std::ranges::equal(lhs.Keys(), rhs.Keys());
|
||||
}
|
||||
|
||||
friend bool operator<(const KeyStore &lhs, const PrimaryKey &rhs) {
|
||||
return std::ranges::lexicographical_compare(lhs.Keys(), rhs, std::less<PropertyValue>{});
|
||||
}
|
||||
|
||||
friend bool operator==(const KeyStore &lhs, const PrimaryKey &rhs) { return std::ranges::equal(lhs.Keys(), rhs); }
|
||||
|
||||
private:
|
||||
PropertyStore store_;
|
||||
};
|
||||
|
||||
} // namespace memgraph::storage::v3
|
||||
|
@ -1,14 +0,0 @@
|
||||
// Copyright 2022 Memgraph Ltd.
|
||||
//
|
||||
// Use of this software is governed by the Business Source License
|
||||
// included in the file licenses/BSL.txt; by using this file, you agree to be bound by the terms of the Business Source
|
||||
// License, and you may not use this file except in compliance with the Business Source License.
|
||||
//
|
||||
// As of the Change Date specified in that file, in accordance with
|
||||
// the Business Source License, use of this software will be governed
|
||||
// by the Apache License, Version 2.0, included in the file
|
||||
// licenses/APL.txt.
|
||||
|
||||
#include "storage/v3/lexicographically_ordered_vertex.hpp"
|
||||
|
||||
namespace memgraph::storage::v3 {} // namespace memgraph::storage::v3
|
@ -1,42 +0,0 @@
|
||||
// Copyright 2022 Memgraph Ltd.
|
||||
//
|
||||
// Use of this software is governed by the Business Source License
|
||||
// included in the file licenses/BSL.txt; by using this file, you agree to be bound by the terms of the Business Source
|
||||
// License, and you may not use this file except in compliance with the Business Source License.
|
||||
//
|
||||
// As of the Change Date specified in that file, in accordance with
|
||||
// the Business Source License, use of this software will be governed
|
||||
// by the Apache License, Version 2.0, included in the file
|
||||
// licenses/APL.txt.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <concepts>
|
||||
#include <type_traits>
|
||||
|
||||
#include "storage/v3/vertex.hpp"
|
||||
#include "utils/concepts.hpp"
|
||||
|
||||
namespace memgraph::storage::v3 {
|
||||
|
||||
struct LexicographicallyOrderedVertex {
|
||||
Vertex vertex;
|
||||
|
||||
friend bool operator==(const LexicographicallyOrderedVertex &lhs, const LexicographicallyOrderedVertex &rhs) {
|
||||
return lhs.vertex.keys == rhs.vertex.keys;
|
||||
}
|
||||
|
||||
friend bool operator<(const LexicographicallyOrderedVertex &lhs, const LexicographicallyOrderedVertex &rhs) {
|
||||
return lhs.vertex.keys < rhs.vertex.keys;
|
||||
}
|
||||
|
||||
// TODO(antaljanosbenjamin): maybe it worth to overload this for std::array to avoid heap construction of the vector
|
||||
friend bool operator==(const LexicographicallyOrderedVertex &lhs, const std::vector<PropertyValue> &rhs) {
|
||||
return lhs.vertex.keys == rhs;
|
||||
}
|
||||
|
||||
friend bool operator<(const LexicographicallyOrderedVertex &lhs, const std::vector<PropertyValue> &rhs) {
|
||||
return lhs.vertex.keys < rhs;
|
||||
}
|
||||
};
|
||||
} // namespace memgraph::storage::v3
|
@ -11,12 +11,21 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <type_traits>
|
||||
|
||||
#include "storage/v3/edge.hpp"
|
||||
#include "storage/v3/property_value.hpp"
|
||||
#include "storage/v3/transaction.hpp"
|
||||
#include "storage/v3/vertex.hpp"
|
||||
#include "storage/v3/view.hpp"
|
||||
#include "utils/concepts.hpp"
|
||||
|
||||
namespace memgraph::storage::v3 {
|
||||
|
||||
inline VertexData *GetDeltaHolder(Vertex *vertex) { return &vertex->second; }
|
||||
|
||||
inline Edge *GetDeltaHolder(Edge *edge) { return edge; }
|
||||
|
||||
/// This function iterates through the undo buffers from an object (starting
|
||||
/// from the supplied delta) and determines what deltas should be applied to get
|
||||
/// the currently visible version of the object. When the function finds a delta
|
||||
@ -77,10 +86,12 @@ inline void ApplyDeltasForRead(Transaction *transaction, const Delta *delta, Vie
|
||||
/// transaction) and returns a `bool` value indicating whether the caller can
|
||||
/// proceed with a write operation.
|
||||
template <typename TObj>
|
||||
requires utils::SameAsAnyOf<TObj, Edge, Vertex>
|
||||
inline bool PrepareForWrite(Transaction *transaction, TObj *object) {
|
||||
if (object->delta == nullptr) return true;
|
||||
auto *delta_holder = GetDeltaHolder(object);
|
||||
if (delta_holder->delta == nullptr) return true;
|
||||
|
||||
const auto &delta_commit_info = *object->delta->commit_info;
|
||||
const auto &delta_commit_info = *delta_holder->delta->commit_info;
|
||||
if (delta_commit_info.start_or_commit_timestamp == transaction->commit_info->start_or_commit_timestamp ||
|
||||
(delta_commit_info.is_locally_committed &&
|
||||
delta_commit_info.start_or_commit_timestamp < transaction->start_timestamp)) {
|
||||
@ -105,9 +116,11 @@ inline Delta *CreateDeleteObjectDelta(Transaction *transaction) {
|
||||
/// the delta into the object's delta list.
|
||||
/// @throw std::bad_alloc
|
||||
template <typename TObj, class... Args>
|
||||
requires utils::SameAsAnyOf<TObj, Edge, Vertex>
|
||||
inline void CreateAndLinkDelta(Transaction *transaction, TObj *object, Args &&...args) {
|
||||
auto delta = &transaction->deltas.emplace_back(std::forward<Args>(args)..., transaction->commit_info.get(),
|
||||
transaction->command_id);
|
||||
auto *delta_holder = GetDeltaHolder(object);
|
||||
|
||||
// The operations are written in such order so that both `next` and `prev`
|
||||
// chains are valid at all times. The chains must be valid at all times
|
||||
@ -118,21 +131,21 @@ inline void CreateAndLinkDelta(Transaction *transaction, TObj *object, Args &&..
|
||||
// TODO(antaljanosbenjamin): clang-tidy detects (in my opinion a false positive) issue in
|
||||
// `Shard::Accessor::CreateEdge`.
|
||||
// NOLINTNEXTLINE(clang-analyzer-core.NullDereference)
|
||||
delta->next = object->delta;
|
||||
delta->next = delta_holder->delta;
|
||||
// 2. We need to set the previous delta of the new delta to the object.
|
||||
delta->prev.Set(object);
|
||||
// 3. We need to set the previous delta of the existing delta to the new
|
||||
// delta. After this point the garbage collector will be able to see the new
|
||||
// delta but won't modify it until we are done with all of our modifications.
|
||||
if (object->delta) {
|
||||
object->delta->prev.Set(delta);
|
||||
if (delta_holder->delta) {
|
||||
delta_holder->delta->prev.Set(delta);
|
||||
}
|
||||
// 4. Finally, we need to set the object's delta to the new delta. The garbage
|
||||
// collector and other transactions will acquire the object lock to read the
|
||||
// delta from the object. Because the lock is held during the whole time this
|
||||
// modification is being done, everybody else will wait until we are fully
|
||||
// done with our modification before they read the object's delta value.
|
||||
object->delta = delta;
|
||||
delta_holder->delta = delta;
|
||||
}
|
||||
|
||||
} // namespace memgraph::storage::v3
|
||||
|
@ -38,7 +38,7 @@
|
||||
#include "storage/v3/transaction.hpp"
|
||||
#include "storage/v3/vertex.hpp"
|
||||
#include "storage/v3/vertex_accessor.hpp"
|
||||
#include "storage/v3/vertices_skip_list.hpp"
|
||||
#include "storage/v3/view.hpp"
|
||||
#include "utils/exceptions.hpp"
|
||||
#include "utils/file.hpp"
|
||||
#include "utils/logging.hpp"
|
||||
@ -75,11 +75,11 @@ uint64_t GetCleanupBeforeTimestamp(const std::map<uint64_t, std::unique_ptr<Tran
|
||||
|
||||
} // namespace
|
||||
|
||||
auto AdvanceToVisibleVertex(VerticesSkipList::Iterator it, VerticesSkipList::Iterator end,
|
||||
auto AdvanceToVisibleVertex(VertexContainer::iterator it, VertexContainer::iterator end,
|
||||
std::optional<VertexAccessor> *vertex, Transaction *tx, View view, Indices *indices,
|
||||
Config::Items config, const VertexValidator &vertex_validator) {
|
||||
while (it != end) {
|
||||
*vertex = VertexAccessor::Create(&it->vertex, tx, indices, config, vertex_validator, view);
|
||||
*vertex = VertexAccessor::Create(&*it, tx, indices, config, vertex_validator, view);
|
||||
if (!*vertex) {
|
||||
++it;
|
||||
continue;
|
||||
@ -89,17 +89,17 @@ auto AdvanceToVisibleVertex(VerticesSkipList::Iterator it, VerticesSkipList::Ite
|
||||
return it;
|
||||
}
|
||||
|
||||
AllVerticesIterable::Iterator::Iterator(AllVerticesIterable *self, VerticesSkipList::Iterator it)
|
||||
AllVerticesIterable::Iterator::Iterator(AllVerticesIterable *self, VertexContainer::iterator it)
|
||||
: self_(self),
|
||||
it_(AdvanceToVisibleVertex(it, self->vertices_accessor_.end(), &self->vertex_, self->transaction_, self->view_,
|
||||
it_(AdvanceToVisibleVertex(it, self->vertices_accessor_->end(), &self->vertex_, self->transaction_, self->view_,
|
||||
self->indices_, self->config_, *self_->vertex_validator_)) {}
|
||||
|
||||
VertexAccessor AllVerticesIterable::Iterator::operator*() const { return *self_->vertex_; }
|
||||
|
||||
AllVerticesIterable::Iterator &AllVerticesIterable::Iterator::operator++() {
|
||||
++it_;
|
||||
it_ = AdvanceToVisibleVertex(it_, self_->vertices_accessor_.end(), &self_->vertex_, self_->transaction_, self_->view_,
|
||||
self_->indices_, self_->config_, *self_->vertex_validator_);
|
||||
it_ = AdvanceToVisibleVertex(it_, self_->vertices_accessor_->end(), &self_->vertex_, self_->transaction_,
|
||||
self_->view_, self_->indices_, self_->config_, *self_->vertex_validator_);
|
||||
return *this;
|
||||
}
|
||||
|
||||
@ -346,7 +346,7 @@ Shard::Accessor::Accessor(Shard &shard, Transaction &transaction)
|
||||
: shard_(&shard), transaction_(&transaction), config_(shard_->config_.items) {}
|
||||
|
||||
ShardResult<VertexAccessor> Shard::Accessor::CreateVertexAndValidate(
|
||||
const std::vector<LabelId> &labels, const std::vector<PropertyValue> &primary_properties,
|
||||
const std::vector<LabelId> &labels, const PrimaryKey &primary_properties,
|
||||
const std::vector<std::pair<PropertyId, PropertyValue>> &properties) {
|
||||
OOMExceptionEnabler oom_exception;
|
||||
const auto schema = shard_->GetSchema(shard_->primary_label_)->second;
|
||||
@ -357,16 +357,15 @@ ShardResult<VertexAccessor> Shard::Accessor::CreateVertexAndValidate(
|
||||
return {std::move(maybe_schema_violation.GetError())};
|
||||
}
|
||||
|
||||
auto acc = shard_->vertices_.access();
|
||||
auto *delta = CreateDeleteObjectDelta(transaction_);
|
||||
auto [it, inserted] = acc.insert({Vertex{delta, primary_properties}});
|
||||
delta->prev.Set(&it->vertex);
|
||||
auto [it, inserted] = shard_->vertices_.emplace(primary_properties, VertexData{delta});
|
||||
delta->prev.Set(&*it);
|
||||
|
||||
VertexAccessor vertex_acc{&it->vertex, transaction_, &shard_->indices_, config_, shard_->vertex_validator_};
|
||||
VertexAccessor vertex_acc{&*it, transaction_, &shard_->indices_, config_, shard_->vertex_validator_};
|
||||
if (!inserted) {
|
||||
return SHARD_ERROR(ErrorCode::VERTEX_ALREADY_INSERTED);
|
||||
}
|
||||
MG_ASSERT(it != acc.end(), "Invalid Vertex accessor!");
|
||||
MG_ASSERT(it != shard_->vertices_.end(), "Invalid Vertex accessor!");
|
||||
|
||||
// TODO(jbajic) Improve, maybe delay index update
|
||||
for (const auto &[property_id, property_value] : properties) {
|
||||
@ -386,13 +385,11 @@ ShardResult<VertexAccessor> Shard::Accessor::CreateVertexAndValidate(
|
||||
}
|
||||
|
||||
std::optional<VertexAccessor> Shard::Accessor::FindVertex(std::vector<PropertyValue> primary_key, View view) {
|
||||
auto acc = shard_->vertices_.access();
|
||||
// Later on use label space
|
||||
auto it = acc.find(primary_key);
|
||||
if (it == acc.end()) {
|
||||
auto it = shard_->vertices_.find(primary_key);
|
||||
if (it == shard_->vertices_.end()) {
|
||||
return std::nullopt;
|
||||
}
|
||||
return VertexAccessor::Create(&it->vertex, transaction_, &shard_->indices_, config_, shard_->vertex_validator_, view);
|
||||
return VertexAccessor::Create(&*it, transaction_, &shard_->indices_, config_, shard_->vertex_validator_, view);
|
||||
}
|
||||
|
||||
ShardResult<std::optional<VertexAccessor>> Shard::Accessor::DeleteVertex(VertexAccessor *vertex) {
|
||||
@ -403,14 +400,15 @@ ShardResult<std::optional<VertexAccessor>> Shard::Accessor::DeleteVertex(VertexA
|
||||
|
||||
if (!PrepareForWrite(transaction_, vertex_ptr)) return SHARD_ERROR(ErrorCode::SERIALIZATION_ERROR);
|
||||
|
||||
if (vertex_ptr->deleted) {
|
||||
if (vertex_ptr->second.deleted) {
|
||||
return std::optional<VertexAccessor>{};
|
||||
}
|
||||
|
||||
if (!vertex_ptr->in_edges.empty() || !vertex_ptr->out_edges.empty()) return SHARD_ERROR(ErrorCode::VERTEX_HAS_EDGES);
|
||||
if (!vertex_ptr->second.in_edges.empty() || !vertex_ptr->second.out_edges.empty())
|
||||
return SHARD_ERROR(ErrorCode::VERTEX_HAS_EDGES);
|
||||
|
||||
CreateAndLinkDelta(transaction_, vertex_ptr, Delta::RecreateObjectTag());
|
||||
vertex_ptr->deleted = true;
|
||||
vertex_ptr->second.deleted = true;
|
||||
|
||||
return std::make_optional<VertexAccessor>(vertex_ptr, transaction_, &shard_->indices_, config_,
|
||||
shard_->vertex_validator_, true);
|
||||
@ -425,20 +423,20 @@ ShardResult<std::optional<std::pair<VertexAccessor, std::vector<EdgeAccessor>>>>
|
||||
"accessor when deleting a vertex!");
|
||||
auto *vertex_ptr = vertex->vertex_;
|
||||
|
||||
std::vector<Vertex::EdgeLink> in_edges;
|
||||
std::vector<Vertex::EdgeLink> out_edges;
|
||||
std::vector<VertexData::EdgeLink> in_edges;
|
||||
std::vector<VertexData::EdgeLink> out_edges;
|
||||
|
||||
{
|
||||
if (!PrepareForWrite(transaction_, vertex_ptr)) return SHARD_ERROR(ErrorCode::SERIALIZATION_ERROR);
|
||||
|
||||
if (vertex_ptr->deleted) return std::optional<ReturnType>{};
|
||||
if (vertex_ptr->second.deleted) return std::optional<ReturnType>{};
|
||||
|
||||
in_edges = vertex_ptr->in_edges;
|
||||
out_edges = vertex_ptr->out_edges;
|
||||
in_edges = vertex_ptr->second.in_edges;
|
||||
out_edges = vertex_ptr->second.out_edges;
|
||||
}
|
||||
|
||||
std::vector<EdgeAccessor> deleted_edges;
|
||||
const VertexId vertex_id{shard_->primary_label_, vertex_ptr->keys.Keys()};
|
||||
const VertexId vertex_id{shard_->primary_label_, *vertex->PrimaryKey(View::OLD)}; // TODO Replace
|
||||
for (const auto &item : in_edges) {
|
||||
auto [edge_type, from_vertex, edge] = item;
|
||||
EdgeAccessor e(edge, edge_type, from_vertex, vertex_id, transaction_, &shard_->indices_, config_);
|
||||
@ -472,10 +470,10 @@ ShardResult<std::optional<std::pair<VertexAccessor, std::vector<EdgeAccessor>>>>
|
||||
|
||||
if (!PrepareForWrite(transaction_, vertex_ptr)) return SHARD_ERROR(ErrorCode::SERIALIZATION_ERROR);
|
||||
|
||||
MG_ASSERT(!vertex_ptr->deleted, "Invalid database state!");
|
||||
MG_ASSERT(!vertex_ptr->second.deleted, "Invalid database state!");
|
||||
|
||||
CreateAndLinkDelta(transaction_, vertex_ptr, Delta::RecreateObjectTag());
|
||||
vertex_ptr->deleted = true;
|
||||
vertex_ptr->second.deleted = true;
|
||||
|
||||
return std::make_optional<ReturnType>(
|
||||
VertexAccessor{vertex_ptr, transaction_, &shard_->indices_, config_, shard_->vertex_validator_, true},
|
||||
@ -488,51 +486,50 @@ ShardResult<EdgeAccessor> Shard::Accessor::CreateEdge(VertexId from_vertex_id, V
|
||||
Vertex *from_vertex{nullptr};
|
||||
Vertex *to_vertex{nullptr};
|
||||
|
||||
auto acc = shard_->vertices_.access();
|
||||
auto &vertices = shard_->vertices_;
|
||||
|
||||
const auto from_is_local = shard_->IsVertexBelongToShard(from_vertex_id);
|
||||
const auto to_is_local = shard_->IsVertexBelongToShard(to_vertex_id);
|
||||
MG_ASSERT(from_is_local || to_is_local, "Trying to create an edge without having a local vertex");
|
||||
|
||||
if (from_is_local) {
|
||||
auto it = acc.find(from_vertex_id.primary_key);
|
||||
MG_ASSERT(it != acc.end(), "Cannot find local vertex");
|
||||
from_vertex = &it->vertex;
|
||||
auto it = vertices.find(from_vertex_id.primary_key);
|
||||
MG_ASSERT(it != vertices.end(), "Cannot find local vertex");
|
||||
from_vertex = &*it;
|
||||
}
|
||||
|
||||
if (to_is_local) {
|
||||
auto it = acc.find(to_vertex_id.primary_key);
|
||||
MG_ASSERT(it != acc.end(), "Cannot find local vertex");
|
||||
to_vertex = &it->vertex;
|
||||
auto it = vertices.find(to_vertex_id.primary_key);
|
||||
MG_ASSERT(it != vertices.end(), "Cannot find local vertex");
|
||||
to_vertex = &*it;
|
||||
}
|
||||
|
||||
if (from_is_local) {
|
||||
if (!PrepareForWrite(transaction_, from_vertex)) return SHARD_ERROR(ErrorCode::SERIALIZATION_ERROR);
|
||||
if (from_vertex->deleted) return SHARD_ERROR(ErrorCode::DELETED_OBJECT);
|
||||
if (from_vertex->second.deleted) return SHARD_ERROR(ErrorCode::DELETED_OBJECT);
|
||||
}
|
||||
if (to_is_local && to_vertex != from_vertex) {
|
||||
if (!PrepareForWrite(transaction_, to_vertex)) return SHARD_ERROR(ErrorCode::SERIALIZATION_ERROR);
|
||||
if (to_vertex->deleted) return SHARD_ERROR(ErrorCode::DELETED_OBJECT);
|
||||
if (to_vertex->second.deleted) return SHARD_ERROR(ErrorCode::DELETED_OBJECT);
|
||||
}
|
||||
|
||||
EdgeRef edge(gid);
|
||||
if (config_.properties_on_edges) {
|
||||
auto acc = shard_->edges_.access();
|
||||
auto *delta = CreateDeleteObjectDelta(transaction_);
|
||||
auto [it, inserted] = acc.insert(Edge(gid, delta));
|
||||
auto [it, inserted] = shard_->edges_.emplace(gid, Edge{gid, delta});
|
||||
MG_ASSERT(inserted, "The edge must be inserted here!");
|
||||
MG_ASSERT(it != acc.end(), "Invalid Edge accessor!");
|
||||
edge = EdgeRef(&*it);
|
||||
delta->prev.Set(&*it);
|
||||
MG_ASSERT(it != shard_->edges_.end(), "Invalid Edge accessor!");
|
||||
edge = EdgeRef(&it->second);
|
||||
delta->prev.Set(&it->second);
|
||||
}
|
||||
|
||||
if (from_is_local) {
|
||||
CreateAndLinkDelta(transaction_, from_vertex, Delta::RemoveOutEdgeTag(), edge_type, to_vertex_id, edge);
|
||||
from_vertex->out_edges.emplace_back(edge_type, to_vertex_id, edge);
|
||||
from_vertex->second.out_edges.emplace_back(edge_type, to_vertex_id, edge);
|
||||
}
|
||||
if (to_is_local) {
|
||||
CreateAndLinkDelta(transaction_, to_vertex, Delta::RemoveInEdgeTag(), edge_type, from_vertex_id, edge);
|
||||
to_vertex->in_edges.emplace_back(edge_type, from_vertex_id, edge);
|
||||
to_vertex->second.in_edges.emplace_back(edge_type, from_vertex_id, edge);
|
||||
}
|
||||
// Increment edge count.
|
||||
++shard_->edge_count_;
|
||||
@ -543,24 +540,24 @@ ShardResult<EdgeAccessor> Shard::Accessor::CreateEdge(VertexId from_vertex_id, V
|
||||
|
||||
ShardResult<std::optional<EdgeAccessor>> Shard::Accessor::DeleteEdge(VertexId from_vertex_id, VertexId to_vertex_id,
|
||||
const Gid edge_id) {
|
||||
Vertex *from_vertex{nullptr};
|
||||
Vertex *to_vertex{nullptr};
|
||||
VertexContainer::value_type *from_vertex{nullptr};
|
||||
VertexContainer::value_type *to_vertex{nullptr};
|
||||
|
||||
auto acc = shard_->vertices_.access();
|
||||
auto &vertices = shard_->vertices_;
|
||||
|
||||
const auto from_is_local = shard_->IsVertexBelongToShard(from_vertex_id);
|
||||
const auto to_is_local = shard_->IsVertexBelongToShard(to_vertex_id);
|
||||
|
||||
if (from_is_local) {
|
||||
auto it = acc.find(from_vertex_id.primary_key);
|
||||
MG_ASSERT(it != acc.end(), "Cannot find local vertex");
|
||||
from_vertex = &it->vertex;
|
||||
auto it = vertices.find(from_vertex_id.primary_key);
|
||||
MG_ASSERT(it != vertices.end(), "Cannot find local vertex");
|
||||
from_vertex = &*it;
|
||||
}
|
||||
|
||||
if (to_is_local) {
|
||||
auto it = acc.find(to_vertex_id.primary_key);
|
||||
MG_ASSERT(it != acc.end(), "Cannot find local vertex");
|
||||
to_vertex = &it->vertex;
|
||||
auto it = vertices.find(to_vertex_id.primary_key);
|
||||
MG_ASSERT(it != vertices.end(), "Cannot find local vertex");
|
||||
to_vertex = &*it;
|
||||
}
|
||||
|
||||
MG_ASSERT(from_is_local || to_is_local, "Trying to delete an edge without having a local vertex");
|
||||
@ -569,29 +566,28 @@ ShardResult<std::optional<EdgeAccessor>> Shard::Accessor::DeleteEdge(VertexId fr
|
||||
if (!PrepareForWrite(transaction_, from_vertex)) {
|
||||
return SHARD_ERROR(ErrorCode::SERIALIZATION_ERROR);
|
||||
}
|
||||
MG_ASSERT(!from_vertex->deleted, "Invalid database state!");
|
||||
MG_ASSERT(!from_vertex->second.deleted, "Invalid database state!");
|
||||
}
|
||||
if (to_is_local && to_vertex != from_vertex) {
|
||||
if (!PrepareForWrite(transaction_, to_vertex)) {
|
||||
return SHARD_ERROR(ErrorCode::SERIALIZATION_ERROR);
|
||||
}
|
||||
MG_ASSERT(!to_vertex->deleted, "Invalid database state!");
|
||||
MG_ASSERT(!to_vertex->second.deleted, "Invalid database state!");
|
||||
}
|
||||
|
||||
const auto edge_ref = std::invoke([edge_id, this]() -> EdgeRef {
|
||||
if (!config_.properties_on_edges) {
|
||||
return EdgeRef(edge_id);
|
||||
}
|
||||
auto edge_acc = shard_->edges_.access();
|
||||
auto res = edge_acc.find(edge_id);
|
||||
MG_ASSERT(res != edge_acc.end(), "Cannot find edge");
|
||||
return EdgeRef(&*res);
|
||||
auto res = shard_->edges_.find(edge_id);
|
||||
MG_ASSERT(res != shard_->edges_.end(), "Cannot find edge");
|
||||
return EdgeRef(&res->second);
|
||||
});
|
||||
|
||||
std::optional<EdgeTypeId> edge_type{};
|
||||
auto delete_edge_from_storage = [&edge_type, &edge_ref, this](std::vector<Vertex::EdgeLink> &edges) mutable {
|
||||
auto delete_edge_from_storage = [&edge_type, &edge_ref, this](std::vector<VertexData::EdgeLink> &edges) mutable {
|
||||
auto it = std::find_if(edges.begin(), edges.end(),
|
||||
[&edge_ref](const Vertex::EdgeLink &link) { return std::get<2>(link) == edge_ref; });
|
||||
[&edge_ref](const VertexData::EdgeLink &link) { return std::get<2>(link) == edge_ref; });
|
||||
if (config_.properties_on_edges) {
|
||||
MG_ASSERT(it != edges.end(), "Invalid database state!");
|
||||
} else if (it == edges.end()) {
|
||||
@ -603,8 +599,8 @@ ShardResult<std::optional<EdgeAccessor>> Shard::Accessor::DeleteEdge(VertexId fr
|
||||
return true;
|
||||
};
|
||||
// NOLINTNEXTLINE(clang-analyzer-core.NonNullParamChecker)
|
||||
auto success_on_to = to_is_local ? delete_edge_from_storage(to_vertex->in_edges) : false;
|
||||
auto success_on_from = from_is_local ? delete_edge_from_storage(from_vertex->out_edges) : false;
|
||||
auto success_on_to = to_is_local ? delete_edge_from_storage(to_vertex->second.in_edges) : false;
|
||||
auto success_on_from = from_is_local ? delete_edge_from_storage(from_vertex->second.out_edges) : false;
|
||||
|
||||
if (config_.properties_on_edges) {
|
||||
// Because of the check above, we are sure that the vertex exists.
|
||||
@ -630,10 +626,10 @@ ShardResult<std::optional<EdgeAccessor>> Shard::Accessor::DeleteEdge(VertexId fr
|
||||
MG_ASSERT(edge_type.has_value(), "Edge type is not determined");
|
||||
|
||||
if (from_is_local) {
|
||||
CreateAndLinkDelta(transaction_, from_vertex, Delta::AddOutEdgeTag(), *edge_type, to_vertex_id, edge_ref);
|
||||
CreateAndLinkDelta(transaction_, &*from_vertex, Delta::AddOutEdgeTag(), *edge_type, to_vertex_id, edge_ref);
|
||||
}
|
||||
if (to_is_local) {
|
||||
CreateAndLinkDelta(transaction_, to_vertex, Delta::AddInEdgeTag(), *edge_type, from_vertex_id, edge_ref);
|
||||
CreateAndLinkDelta(transaction_, &*to_vertex, Delta::AddInEdgeTag(), *edge_type, from_vertex_id, edge_ref);
|
||||
}
|
||||
|
||||
// Decrement edge count.
|
||||
@ -695,41 +691,41 @@ void Shard::Accessor::Abort() {
|
||||
auto prev = delta.prev.Get();
|
||||
switch (prev.type) {
|
||||
case PreviousPtr::Type::VERTEX: {
|
||||
auto *vertex = prev.vertex;
|
||||
Delta *current = vertex->delta;
|
||||
auto &[pk, vertex] = *prev.vertex;
|
||||
Delta *current = vertex.delta;
|
||||
while (current != nullptr && current->commit_info->start_or_commit_timestamp == transaction_->start_timestamp) {
|
||||
switch (current->action) {
|
||||
case Delta::Action::REMOVE_LABEL: {
|
||||
auto it = std::find(vertex->labels.begin(), vertex->labels.end(), current->label);
|
||||
MG_ASSERT(it != vertex->labels.end(), "Invalid database state!");
|
||||
std::swap(*it, *vertex->labels.rbegin());
|
||||
vertex->labels.pop_back();
|
||||
auto it = std::find(vertex.labels.begin(), vertex.labels.end(), current->label);
|
||||
MG_ASSERT(it != vertex.labels.end(), "Invalid database state!");
|
||||
std::swap(*it, *vertex.labels.rbegin());
|
||||
vertex.labels.pop_back();
|
||||
break;
|
||||
}
|
||||
case Delta::Action::ADD_LABEL: {
|
||||
auto it = std::find(vertex->labels.begin(), vertex->labels.end(), current->label);
|
||||
MG_ASSERT(it == vertex->labels.end(), "Invalid database state!");
|
||||
vertex->labels.push_back(current->label);
|
||||
auto it = std::find(vertex.labels.begin(), vertex.labels.end(), current->label);
|
||||
MG_ASSERT(it == vertex.labels.end(), "Invalid database state!");
|
||||
vertex.labels.push_back(current->label);
|
||||
break;
|
||||
}
|
||||
case Delta::Action::SET_PROPERTY: {
|
||||
vertex->properties.SetProperty(current->property.key, current->property.value);
|
||||
vertex.properties.SetProperty(current->property.key, current->property.value);
|
||||
break;
|
||||
}
|
||||
case Delta::Action::ADD_IN_EDGE: {
|
||||
Vertex::EdgeLink link{current->vertex_edge.edge_type, current->vertex_edge.vertex_id,
|
||||
current->vertex_edge.edge};
|
||||
auto it = std::find(vertex->in_edges.begin(), vertex->in_edges.end(), link);
|
||||
MG_ASSERT(it == vertex->in_edges.end(), "Invalid database state!");
|
||||
vertex->in_edges.push_back(link);
|
||||
VertexData::EdgeLink link{current->vertex_edge.edge_type, current->vertex_edge.vertex_id,
|
||||
current->vertex_edge.edge};
|
||||
auto it = std::find(vertex.in_edges.begin(), vertex.in_edges.end(), link);
|
||||
MG_ASSERT(it == vertex.in_edges.end(), "Invalid database state!");
|
||||
vertex.in_edges.push_back(link);
|
||||
break;
|
||||
}
|
||||
case Delta::Action::ADD_OUT_EDGE: {
|
||||
Vertex::EdgeLink link{current->vertex_edge.edge_type, current->vertex_edge.vertex_id,
|
||||
current->vertex_edge.edge};
|
||||
auto it = std::find(vertex->out_edges.begin(), vertex->out_edges.end(), link);
|
||||
MG_ASSERT(it == vertex->out_edges.end(), "Invalid database state!");
|
||||
vertex->out_edges.push_back(link);
|
||||
VertexData::EdgeLink link{current->vertex_edge.edge_type, current->vertex_edge.vertex_id,
|
||||
current->vertex_edge.edge};
|
||||
auto it = std::find(vertex.out_edges.begin(), vertex.out_edges.end(), link);
|
||||
MG_ASSERT(it == vertex.out_edges.end(), "Invalid database state!");
|
||||
vertex.out_edges.push_back(link);
|
||||
// Increment edge count. We only increment the count here because
|
||||
// the information in `ADD_IN_EDGE` and `Edge/RECREATE_OBJECT` is
|
||||
// redundant. Also, `Edge/RECREATE_OBJECT` isn't available when
|
||||
@ -738,21 +734,21 @@ void Shard::Accessor::Abort() {
|
||||
break;
|
||||
}
|
||||
case Delta::Action::REMOVE_IN_EDGE: {
|
||||
Vertex::EdgeLink link{current->vertex_edge.edge_type, current->vertex_edge.vertex_id,
|
||||
current->vertex_edge.edge};
|
||||
auto it = std::find(vertex->in_edges.begin(), vertex->in_edges.end(), link);
|
||||
MG_ASSERT(it != vertex->in_edges.end(), "Invalid database state!");
|
||||
std::swap(*it, *vertex->in_edges.rbegin());
|
||||
vertex->in_edges.pop_back();
|
||||
VertexData::EdgeLink link{current->vertex_edge.edge_type, current->vertex_edge.vertex_id,
|
||||
current->vertex_edge.edge};
|
||||
auto it = std::find(vertex.in_edges.begin(), vertex.in_edges.end(), link);
|
||||
MG_ASSERT(it != vertex.in_edges.end(), "Invalid database state!");
|
||||
std::swap(*it, *vertex.in_edges.rbegin());
|
||||
vertex.in_edges.pop_back();
|
||||
break;
|
||||
}
|
||||
case Delta::Action::REMOVE_OUT_EDGE: {
|
||||
Vertex::EdgeLink link{current->vertex_edge.edge_type, current->vertex_edge.vertex_id,
|
||||
current->vertex_edge.edge};
|
||||
auto it = std::find(vertex->out_edges.begin(), vertex->out_edges.end(), link);
|
||||
MG_ASSERT(it != vertex->out_edges.end(), "Invalid database state!");
|
||||
std::swap(*it, *vertex->out_edges.rbegin());
|
||||
vertex->out_edges.pop_back();
|
||||
VertexData::EdgeLink link{current->vertex_edge.edge_type, current->vertex_edge.vertex_id,
|
||||
current->vertex_edge.edge};
|
||||
auto it = std::find(vertex.out_edges.begin(), vertex.out_edges.end(), link);
|
||||
MG_ASSERT(it != vertex.out_edges.end(), "Invalid database state!");
|
||||
std::swap(*it, *vertex.out_edges.rbegin());
|
||||
vertex.out_edges.pop_back();
|
||||
// Decrement edge count. We only decrement the count here because
|
||||
// the information in `REMOVE_IN_EDGE` and `Edge/DELETE_OBJECT` is
|
||||
// redundant. Also, `Edge/DELETE_OBJECT` isn't available when edge
|
||||
@ -761,20 +757,20 @@ void Shard::Accessor::Abort() {
|
||||
break;
|
||||
}
|
||||
case Delta::Action::DELETE_OBJECT: {
|
||||
vertex->deleted = true;
|
||||
shard_->deleted_vertices_.push_back(vertex->keys.Keys());
|
||||
vertex.deleted = true;
|
||||
shard_->deleted_vertices_.push_back(&pk);
|
||||
break;
|
||||
}
|
||||
case Delta::Action::RECREATE_OBJECT: {
|
||||
vertex->deleted = false;
|
||||
vertex.deleted = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
current = current->next;
|
||||
}
|
||||
vertex->delta = current;
|
||||
vertex.delta = current;
|
||||
if (current != nullptr) {
|
||||
current->prev.Set(vertex);
|
||||
current->prev.Set(prev.vertex);
|
||||
}
|
||||
|
||||
break;
|
||||
@ -851,10 +847,7 @@ const std::string &Shard::EdgeTypeToName(EdgeTypeId edge_type) const {
|
||||
|
||||
bool Shard::CreateIndex(LabelId label, const std::optional<uint64_t> /*desired_commit_timestamp*/) {
|
||||
// TODO(jbajic) response should be different when label == primary_label
|
||||
if (label == primary_label_ || !indices_.label_index.CreateIndex(label, vertices_.access())) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
return !(label == primary_label_ || !indices_.label_index.CreateIndex(label, vertices_));
|
||||
}
|
||||
|
||||
bool Shard::CreateIndex(LabelId label, PropertyId property,
|
||||
@ -865,7 +858,7 @@ bool Shard::CreateIndex(LabelId label, PropertyId property,
|
||||
// Index already exists on primary key
|
||||
return false;
|
||||
}
|
||||
if (!indices_.label_property_index.CreateIndex(label, property, vertices_.access())) {
|
||||
if (!indices_.label_property_index.CreateIndex(label, property, vertices_)) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
@ -972,10 +965,12 @@ void Shard::CollectGarbage(const io::Time current_time) {
|
||||
auto prev = delta.prev.Get();
|
||||
switch (prev.type) {
|
||||
case PreviousPtr::Type::VERTEX: {
|
||||
Vertex *vertex = prev.vertex;
|
||||
vertex->delta = nullptr;
|
||||
if (vertex->deleted) {
|
||||
deleted_vertices_.push_back(vertex->keys.Keys());
|
||||
// Here we need to get pk from prev pointer, and that is why change
|
||||
// to the PrevPtr so it points to pair of pk and vertex
|
||||
auto &[pk, vertex] = *prev.vertex;
|
||||
vertex.delta = nullptr;
|
||||
if (vertex.deleted) {
|
||||
deleted_vertices_.push_back(&pk);
|
||||
}
|
||||
break;
|
||||
}
|
||||
@ -1022,15 +1017,13 @@ void Shard::CollectGarbage(const io::Time current_time) {
|
||||
RemoveObsoleteEntries(&indices_, clean_up_before_timestamp);
|
||||
}
|
||||
|
||||
auto vertex_acc = vertices_.access();
|
||||
for (const auto &vertex : deleted_vertices_) {
|
||||
MG_ASSERT(vertex_acc.remove(vertex), "Invalid database state!");
|
||||
for (const auto *vertex : deleted_vertices_) {
|
||||
MG_ASSERT(vertices_.erase(*vertex), "Invalid database state!");
|
||||
}
|
||||
deleted_vertices_.clear();
|
||||
{
|
||||
auto edge_acc = edges_.access();
|
||||
for (auto edge : deleted_edges_) {
|
||||
MG_ASSERT(edge_acc.remove(edge), "Invalid database state!");
|
||||
MG_ASSERT(edges_.erase(edge), "Invalid database state!");
|
||||
}
|
||||
}
|
||||
deleted_edges_.clear();
|
||||
|
@ -31,7 +31,6 @@
|
||||
#include "storage/v3/indices.hpp"
|
||||
#include "storage/v3/isolation_level.hpp"
|
||||
#include "storage/v3/key_store.hpp"
|
||||
#include "storage/v3/lexicographically_ordered_vertex.hpp"
|
||||
#include "storage/v3/mvcc.hpp"
|
||||
#include "storage/v3/name_id_mapper.hpp"
|
||||
#include "storage/v3/property_value.hpp"
|
||||
@ -42,7 +41,6 @@
|
||||
#include "storage/v3/vertex.hpp"
|
||||
#include "storage/v3/vertex_accessor.hpp"
|
||||
#include "storage/v3/vertex_id.hpp"
|
||||
#include "storage/v3/vertices_skip_list.hpp"
|
||||
#include "storage/v3/view.hpp"
|
||||
#include "utils/exceptions.hpp"
|
||||
#include "utils/file_locker.hpp"
|
||||
@ -65,7 +63,7 @@ namespace memgraph::storage::v3 {
|
||||
/// An instance of this will be usually be wrapped inside VerticesIterable for
|
||||
/// generic, public use.
|
||||
class AllVerticesIterable final {
|
||||
VerticesSkipList::Accessor vertices_accessor_;
|
||||
VertexContainer *vertices_accessor_;
|
||||
Transaction *transaction_;
|
||||
View view_;
|
||||
Indices *indices_;
|
||||
@ -76,10 +74,10 @@ class AllVerticesIterable final {
|
||||
public:
|
||||
class Iterator final {
|
||||
AllVerticesIterable *self_;
|
||||
VerticesSkipList::Iterator it_;
|
||||
VertexContainer::iterator it_;
|
||||
|
||||
public:
|
||||
Iterator(AllVerticesIterable *self, VerticesSkipList::Iterator it);
|
||||
Iterator(AllVerticesIterable *self, VertexContainer::iterator it);
|
||||
|
||||
VertexAccessor operator*() const;
|
||||
|
||||
@ -90,17 +88,17 @@ class AllVerticesIterable final {
|
||||
bool operator!=(const Iterator &other) const { return !(*this == other); }
|
||||
};
|
||||
|
||||
AllVerticesIterable(VerticesSkipList::Accessor vertices_accessor, Transaction *transaction, View view,
|
||||
Indices *indices, Config::Items config, const VertexValidator &vertex_validator)
|
||||
: vertices_accessor_(std::move(vertices_accessor)),
|
||||
AllVerticesIterable(VertexContainer &vertices_accessor, Transaction *transaction, View view, Indices *indices,
|
||||
Config::Items config, const VertexValidator &vertex_validator)
|
||||
: vertices_accessor_(&vertices_accessor),
|
||||
transaction_(transaction),
|
||||
view_(view),
|
||||
indices_(indices),
|
||||
config_(config),
|
||||
vertex_validator_{&vertex_validator} {}
|
||||
|
||||
Iterator begin() { return {this, vertices_accessor_.begin()}; }
|
||||
Iterator end() { return {this, vertices_accessor_.end()}; }
|
||||
Iterator begin() { return {this, vertices_accessor_->begin()}; }
|
||||
Iterator end() { return {this, vertices_accessor_->end()}; }
|
||||
};
|
||||
|
||||
/// Generic access to different kinds of vertex iterations.
|
||||
@ -213,7 +211,7 @@ class Shard final {
|
||||
std::optional<VertexAccessor> FindVertex(std::vector<PropertyValue> primary_key, View view);
|
||||
|
||||
VerticesIterable Vertices(View view) {
|
||||
return VerticesIterable(AllVerticesIterable(shard_->vertices_.access(), transaction_, view, &shard_->indices_,
|
||||
return VerticesIterable(AllVerticesIterable(shard_->vertices_, transaction_, view, &shard_->indices_,
|
||||
shard_->config_.items, shard_->vertex_validator_));
|
||||
}
|
||||
|
||||
@ -236,18 +234,17 @@ class Shard final {
|
||||
int64_t ApproximateVertexCount(LabelId label) const {
|
||||
return shard_->indices_.label_index.ApproximateVertexCount(label);
|
||||
}
|
||||
|
||||
/// Return approximate number of vertices with the given label and property.
|
||||
/// Note that this is always an over-estimate and never an under-estimate.
|
||||
int64_t ApproximateVertexCount(LabelId label, PropertyId property) const {
|
||||
return shard_->indices_.label_property_index.ApproximateVertexCount(label, property);
|
||||
return shard_->indices_.label_property_index.VertexCount(label, property);
|
||||
}
|
||||
|
||||
/// Return approximate number of vertices with the given label and the given
|
||||
/// value for the given property. Note that this is always an over-estimate
|
||||
/// and never an under-estimate.
|
||||
/// value for the given property.
|
||||
/// Note that this is always an over-estimate and never an under-estimate.
|
||||
int64_t ApproximateVertexCount(LabelId label, PropertyId property, const PropertyValue &value) const {
|
||||
return shard_->indices_.label_property_index.ApproximateVertexCount(label, property, value);
|
||||
return shard_->indices_.label_property_index.VertexCount(label, property, value);
|
||||
}
|
||||
|
||||
/// Return approximate number of vertices with the given label and value for
|
||||
@ -256,7 +253,7 @@ class Shard final {
|
||||
int64_t ApproximateVertexCount(LabelId label, PropertyId property,
|
||||
const std::optional<utils::Bound<PropertyValue>> &lower,
|
||||
const std::optional<utils::Bound<PropertyValue>> &upper) const {
|
||||
return shard_->indices_.label_property_index.ApproximateVertexCount(label, property, lower, upper);
|
||||
return shard_->indices_.label_property_index.VertexCount(label, property, lower, upper);
|
||||
}
|
||||
|
||||
/// @return Accessor to the deleted vertex if a deletion took place, std::nullopt otherwise
|
||||
@ -309,9 +306,6 @@ class Shard final {
|
||||
void Abort();
|
||||
|
||||
private:
|
||||
/// @throw std::bad_alloc
|
||||
VertexAccessor CreateVertex(Gid gid, LabelId primary_label);
|
||||
|
||||
Shard *shard_;
|
||||
Transaction *transaction_;
|
||||
Config::Items config_;
|
||||
@ -377,8 +371,8 @@ class Shard final {
|
||||
// The shard's range is [min, max)
|
||||
PrimaryKey min_primary_key_;
|
||||
std::optional<PrimaryKey> max_primary_key_;
|
||||
VerticesSkipList vertices_;
|
||||
utils::SkipList<Edge> edges_;
|
||||
VertexContainer vertices_;
|
||||
EdgeContainer edges_;
|
||||
// Even though the edge count is already kept in the `edges_` SkipList, the
|
||||
// list is used only when properties are enabled for edges. Because of that we
|
||||
// keep a separate count of edges that is always updated.
|
||||
@ -396,7 +390,7 @@ class Shard final {
|
||||
|
||||
// Vertices that are logically deleted but still have to be removed from
|
||||
// indices before removing them from the main storage.
|
||||
std::list<PrimaryKey> deleted_vertices_;
|
||||
std::list<const PrimaryKey *> deleted_vertices_;
|
||||
|
||||
// Edges that are logically deleted and wait to be removed from the main
|
||||
// storage.
|
||||
|
@ -9,11 +9,22 @@
|
||||
// by the Apache License, Version 2.0, included in the file
|
||||
// licenses/APL.txt.
|
||||
|
||||
#pragma once
|
||||
#include "storage/v3/vertex.hpp"
|
||||
|
||||
#include "storage/v3/lexicographically_ordered_vertex.hpp"
|
||||
#include "utils/skip_list.hpp"
|
||||
#include <limits>
|
||||
#include <tuple>
|
||||
#include <type_traits>
|
||||
#include <vector>
|
||||
|
||||
#include "storage/v3/delta.hpp"
|
||||
|
||||
namespace memgraph::storage::v3 {
|
||||
using VerticesSkipList = utils::SkipList<LexicographicallyOrderedVertex>;
|
||||
|
||||
VertexData::VertexData(Delta *delta) : delta{delta} {
|
||||
MG_ASSERT(delta == nullptr || delta->action == Delta::Action::DELETE_OBJECT,
|
||||
"Vertex must be created with an initial DELETE_OBJECT delta!");
|
||||
}
|
||||
|
||||
bool VertexHasLabel(const Vertex &vertex, const LabelId label) { return utils::Contains(vertex.second.labels, label); }
|
||||
|
||||
} // namespace memgraph::storage::v3
|
@ -16,7 +16,6 @@
|
||||
#include <type_traits>
|
||||
#include <vector>
|
||||
|
||||
#include "storage/v3/delta.hpp"
|
||||
#include "storage/v3/edge_ref.hpp"
|
||||
#include "storage/v3/id_types.hpp"
|
||||
#include "storage/v3/key_store.hpp"
|
||||
@ -28,17 +27,12 @@
|
||||
|
||||
namespace memgraph::storage::v3 {
|
||||
|
||||
struct Vertex {
|
||||
struct Delta;
|
||||
|
||||
struct VertexData {
|
||||
using EdgeLink = std::tuple<EdgeTypeId, VertexId, EdgeRef>;
|
||||
|
||||
Vertex(Delta *delta, const std::vector<PropertyValue> &primary_properties) : keys{primary_properties}, delta{delta} {
|
||||
MG_ASSERT(delta == nullptr || delta->action == Delta::Action::DELETE_OBJECT,
|
||||
"Vertex must be created with an initial DELETE_OBJECT delta!");
|
||||
}
|
||||
|
||||
friend bool operator==(const Vertex &vertex, const PrimaryKey &primary_key) { return vertex.keys == primary_key; }
|
||||
|
||||
KeyStore keys;
|
||||
explicit VertexData(Delta *delta);
|
||||
|
||||
std::vector<LabelId> labels;
|
||||
PropertyStore properties;
|
||||
@ -52,8 +46,11 @@ struct Vertex {
|
||||
Delta *delta;
|
||||
};
|
||||
|
||||
static_assert(alignof(Vertex) >= 8, "The Vertex should be aligned to at least 8!");
|
||||
static_assert(alignof(VertexData) >= 8, "The Vertex should be aligned to at least 8!");
|
||||
|
||||
inline bool VertexHasLabel(const Vertex &vertex, const LabelId label) { return utils::Contains(vertex.labels, label); }
|
||||
using VertexContainer = std::map<PrimaryKey, VertexData>;
|
||||
using Vertex = VertexContainer::value_type;
|
||||
|
||||
bool VertexHasLabel(const Vertex &vertex, LabelId label);
|
||||
|
||||
} // namespace memgraph::storage::v3
|
||||
|
@ -36,8 +36,8 @@ std::pair<bool, bool> IsVisible(Vertex *vertex, Transaction *transaction, View v
|
||||
bool deleted = false;
|
||||
Delta *delta = nullptr;
|
||||
{
|
||||
deleted = vertex->deleted;
|
||||
delta = vertex->delta;
|
||||
deleted = vertex->second.deleted;
|
||||
delta = vertex->second.delta;
|
||||
}
|
||||
ApplyDeltasForRead(transaction, delta, view, [&](const Delta &delta) {
|
||||
switch (delta.action) {
|
||||
@ -85,13 +85,14 @@ ShardResult<bool> VertexAccessor::AddLabel(LabelId label) {
|
||||
|
||||
if (!PrepareForWrite(transaction_, vertex_)) return SHARD_ERROR(ErrorCode::SERIALIZATION_ERROR);
|
||||
|
||||
if (vertex_->deleted) return SHARD_ERROR(ErrorCode::DELETED_OBJECT);
|
||||
if (vertex_->second.deleted) return SHARD_ERROR(ErrorCode::DELETED_OBJECT);
|
||||
|
||||
if (std::find(vertex_->labels.begin(), vertex_->labels.end(), label) != vertex_->labels.end()) return false;
|
||||
if (std::find(vertex_->second.labels.begin(), vertex_->second.labels.end(), label) != vertex_->second.labels.end())
|
||||
return false;
|
||||
|
||||
CreateAndLinkDelta(transaction_, vertex_, Delta::RemoveLabelTag(), label);
|
||||
|
||||
vertex_->labels.push_back(label);
|
||||
vertex_->second.labels.push_back(label);
|
||||
|
||||
UpdateOnAddLabel(indices_, label, vertex_, *transaction_);
|
||||
|
||||
@ -106,13 +107,14 @@ ShardResult<bool> VertexAccessor::AddLabelAndValidate(LabelId label) {
|
||||
|
||||
if (!PrepareForWrite(transaction_, vertex_)) return SHARD_ERROR(ErrorCode::SERIALIZATION_ERROR);
|
||||
|
||||
if (vertex_->deleted) return SHARD_ERROR(ErrorCode::DELETED_OBJECT);
|
||||
if (vertex_->second.deleted) return SHARD_ERROR(ErrorCode::DELETED_OBJECT);
|
||||
|
||||
if (std::find(vertex_->labels.begin(), vertex_->labels.end(), label) != vertex_->labels.end()) return false;
|
||||
if (std::find(vertex_->second.labels.begin(), vertex_->second.labels.end(), label) != vertex_->second.labels.end())
|
||||
return false;
|
||||
|
||||
CreateAndLinkDelta(transaction_, vertex_, Delta::RemoveLabelTag(), label);
|
||||
|
||||
vertex_->labels.push_back(label);
|
||||
vertex_->second.labels.push_back(label);
|
||||
|
||||
UpdateOnAddLabel(indices_, label, vertex_, *transaction_);
|
||||
|
||||
@ -122,15 +124,15 @@ ShardResult<bool> VertexAccessor::AddLabelAndValidate(LabelId label) {
|
||||
ShardResult<bool> VertexAccessor::RemoveLabel(LabelId label) {
|
||||
if (!PrepareForWrite(transaction_, vertex_)) return SHARD_ERROR(ErrorCode::SERIALIZATION_ERROR);
|
||||
|
||||
if (vertex_->deleted) return SHARD_ERROR(ErrorCode::DELETED_OBJECT);
|
||||
if (vertex_->second.deleted) return SHARD_ERROR(ErrorCode::DELETED_OBJECT);
|
||||
|
||||
auto it = std::find(vertex_->labels.begin(), vertex_->labels.end(), label);
|
||||
if (it == vertex_->labels.end()) return false;
|
||||
auto it = std::find(vertex_->second.labels.begin(), vertex_->second.labels.end(), label);
|
||||
if (it == vertex_->second.labels.end()) return false;
|
||||
|
||||
CreateAndLinkDelta(transaction_, vertex_, Delta::AddLabelTag(), label);
|
||||
|
||||
std::swap(*it, *vertex_->labels.rbegin());
|
||||
vertex_->labels.pop_back();
|
||||
std::swap(*it, *vertex_->second.labels.rbegin());
|
||||
vertex_->second.labels.pop_back();
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -142,15 +144,15 @@ ShardResult<bool> VertexAccessor::RemoveLabelAndValidate(LabelId label) {
|
||||
|
||||
if (!PrepareForWrite(transaction_, vertex_)) return SHARD_ERROR(ErrorCode::SERIALIZATION_ERROR);
|
||||
|
||||
if (vertex_->deleted) return SHARD_ERROR(ErrorCode::DELETED_OBJECT);
|
||||
if (vertex_->second.deleted) return SHARD_ERROR(ErrorCode::DELETED_OBJECT);
|
||||
|
||||
auto it = std::find(vertex_->labels.begin(), vertex_->labels.end(), label);
|
||||
if (it == vertex_->labels.end()) return false;
|
||||
auto it = std::find(vertex_->second.labels.begin(), vertex_->second.labels.end(), label);
|
||||
if (it == vertex_->second.labels.end()) return false;
|
||||
|
||||
CreateAndLinkDelta(transaction_, vertex_, Delta::AddLabelTag(), label);
|
||||
|
||||
std::swap(*it, *vertex_->labels.rbegin());
|
||||
vertex_->labels.pop_back();
|
||||
std::swap(*it, *vertex_->second.labels.rbegin());
|
||||
vertex_->second.labels.pop_back();
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -162,9 +164,9 @@ ShardResult<bool> VertexAccessor::HasLabel(LabelId label, View view) const {
|
||||
bool has_label = false;
|
||||
Delta *delta = nullptr;
|
||||
{
|
||||
deleted = vertex_->deleted;
|
||||
deleted = vertex_->second.deleted;
|
||||
has_label = label == vertex_validator_->primary_label_ || VertexHasLabel(*vertex_, label);
|
||||
delta = vertex_->delta;
|
||||
delta = vertex_->second.delta;
|
||||
}
|
||||
ApplyDeltasForRead(transaction_, delta, view, [&exists, &deleted, &has_label, label](const Delta &delta) {
|
||||
switch (delta.action) {
|
||||
@ -215,14 +217,14 @@ ShardResult<PrimaryKey> VertexAccessor::PrimaryKey(const View view) const {
|
||||
if (const auto result = CheckVertexExistence(view); result.HasError()) {
|
||||
return result.GetError();
|
||||
}
|
||||
return vertex_->keys.Keys();
|
||||
return vertex_->first;
|
||||
}
|
||||
|
||||
ShardResult<VertexId> VertexAccessor::Id(View view) const {
|
||||
if (const auto result = CheckVertexExistence(view); result.HasError()) {
|
||||
return result.GetError();
|
||||
}
|
||||
return VertexId{vertex_validator_->primary_label_, vertex_->keys.Keys()};
|
||||
return VertexId{vertex_validator_->primary_label_, vertex_->first};
|
||||
};
|
||||
|
||||
ShardResult<std::vector<LabelId>> VertexAccessor::Labels(View view) const {
|
||||
@ -231,9 +233,9 @@ ShardResult<std::vector<LabelId>> VertexAccessor::Labels(View view) const {
|
||||
std::vector<LabelId> labels;
|
||||
Delta *delta = nullptr;
|
||||
{
|
||||
deleted = vertex_->deleted;
|
||||
labels = vertex_->labels;
|
||||
delta = vertex_->delta;
|
||||
deleted = vertex_->second.deleted;
|
||||
labels = vertex_->second.labels;
|
||||
delta = vertex_->second.delta;
|
||||
}
|
||||
ApplyDeltasForRead(transaction_, delta, view, [&exists, &deleted, &labels](const Delta &delta) {
|
||||
switch (delta.action) {
|
||||
@ -278,9 +280,9 @@ ShardResult<PropertyValue> VertexAccessor::SetProperty(PropertyId property, cons
|
||||
|
||||
if (!PrepareForWrite(transaction_, vertex_)) return SHARD_ERROR(ErrorCode::SERIALIZATION_ERROR);
|
||||
|
||||
if (vertex_->deleted) return SHARD_ERROR(ErrorCode::DELETED_OBJECT);
|
||||
if (vertex_->second.deleted) return SHARD_ERROR(ErrorCode::DELETED_OBJECT);
|
||||
|
||||
auto current_value = vertex_->properties.GetProperty(property);
|
||||
auto current_value = vertex_->second.properties.GetProperty(property);
|
||||
// We could skip setting the value if the previous one is the same to the new
|
||||
// one. This would save some memory as a delta would not be created as well as
|
||||
// avoid copying the value. The reason we are not doing that is because the
|
||||
@ -288,7 +290,7 @@ ShardResult<PropertyValue> VertexAccessor::SetProperty(PropertyId property, cons
|
||||
// "modify in-place". Additionally, the created delta will make other
|
||||
// transactions get a SERIALIZATION_ERROR.
|
||||
CreateAndLinkDelta(transaction_, vertex_, Delta::SetPropertyTag(), property, current_value);
|
||||
vertex_->properties.SetProperty(property, value);
|
||||
vertex_->second.properties.SetProperty(property, value);
|
||||
|
||||
UpdateOnSetProperty(indices_, property, value, vertex_, *transaction_);
|
||||
|
||||
@ -300,8 +302,8 @@ ShardResult<void> VertexAccessor::CheckVertexExistence(View view) const {
|
||||
bool deleted = false;
|
||||
Delta *delta = nullptr;
|
||||
{
|
||||
deleted = vertex_->deleted;
|
||||
delta = vertex_->delta;
|
||||
deleted = vertex_->second.deleted;
|
||||
delta = vertex_->second.delta;
|
||||
}
|
||||
ApplyDeltasForRead(transaction_, delta, view, [&exists, &deleted](const Delta &delta) {
|
||||
switch (delta.action) {
|
||||
@ -343,11 +345,11 @@ ShardResult<PropertyValue> VertexAccessor::SetPropertyAndValidate(PropertyId pro
|
||||
return SHARD_ERROR(ErrorCode::SERIALIZATION_ERROR);
|
||||
}
|
||||
|
||||
if (vertex_->deleted) {
|
||||
if (vertex_->second.deleted) {
|
||||
return SHARD_ERROR(ErrorCode::DELETED_OBJECT);
|
||||
}
|
||||
|
||||
auto current_value = vertex_->properties.GetProperty(property);
|
||||
auto current_value = vertex_->second.properties.GetProperty(property);
|
||||
// We could skip setting the value if the previous one is the same to the new
|
||||
// one. This would save some memory as a delta would not be created as well as
|
||||
// avoid copying the value. The reason we are not doing that is because the
|
||||
@ -355,7 +357,7 @@ ShardResult<PropertyValue> VertexAccessor::SetPropertyAndValidate(PropertyId pro
|
||||
// "modify in-place". Additionally, the created delta will make other
|
||||
// transactions get a SERIALIZATION_ERROR.
|
||||
CreateAndLinkDelta(transaction_, vertex_, Delta::SetPropertyTag(), property, current_value);
|
||||
vertex_->properties.SetProperty(property, value);
|
||||
vertex_->second.properties.SetProperty(property, value);
|
||||
|
||||
UpdateOnSetProperty(indices_, property, value, vertex_, *transaction_);
|
||||
|
||||
@ -365,15 +367,15 @@ ShardResult<PropertyValue> VertexAccessor::SetPropertyAndValidate(PropertyId pro
|
||||
ShardResult<std::map<PropertyId, PropertyValue>> VertexAccessor::ClearProperties() {
|
||||
if (!PrepareForWrite(transaction_, vertex_)) return SHARD_ERROR(ErrorCode::SERIALIZATION_ERROR);
|
||||
|
||||
if (vertex_->deleted) return SHARD_ERROR(ErrorCode::DELETED_OBJECT);
|
||||
if (vertex_->second.deleted) return SHARD_ERROR(ErrorCode::DELETED_OBJECT);
|
||||
|
||||
auto properties = vertex_->properties.Properties();
|
||||
auto properties = vertex_->second.properties.Properties();
|
||||
for (const auto &property : properties) {
|
||||
CreateAndLinkDelta(transaction_, vertex_, Delta::SetPropertyTag(), property.first, property.second);
|
||||
UpdateOnSetProperty(indices_, property.first, PropertyValue(), vertex_, *transaction_);
|
||||
}
|
||||
|
||||
vertex_->properties.ClearProperties();
|
||||
vertex_->second.properties.ClearProperties();
|
||||
|
||||
return std::move(properties);
|
||||
}
|
||||
@ -394,18 +396,13 @@ PropertyValue VertexAccessor::GetPropertyValue(PropertyId property, View view) c
|
||||
return value;
|
||||
}
|
||||
// Find PropertyId index in keystore
|
||||
size_t property_index{0};
|
||||
for (; property_index < schema->second.size(); ++property_index) {
|
||||
for (size_t property_index{0}; property_index < schema->second.size(); ++property_index) {
|
||||
if (schema->second[property_index].property_id == property) {
|
||||
break;
|
||||
return vertex_->first[property_index];
|
||||
}
|
||||
}
|
||||
|
||||
value = vertex_->keys.GetKey(property_index);
|
||||
if (value.IsNull()) {
|
||||
value = vertex_->properties.GetProperty(property);
|
||||
}
|
||||
return value;
|
||||
return value = vertex_->second.properties.GetProperty(property);
|
||||
}
|
||||
|
||||
ShardResult<PropertyValue> VertexAccessor::GetProperty(PropertyId property, View view) const {
|
||||
@ -414,9 +411,9 @@ ShardResult<PropertyValue> VertexAccessor::GetProperty(PropertyId property, View
|
||||
PropertyValue value;
|
||||
Delta *delta = nullptr;
|
||||
{
|
||||
deleted = vertex_->deleted;
|
||||
deleted = vertex_->second.deleted;
|
||||
value = GetPropertyValue(property, view);
|
||||
delta = vertex_->delta;
|
||||
delta = vertex_->second.delta;
|
||||
}
|
||||
ApplyDeltasForRead(transaction_, delta, view, [&exists, &deleted, &value, property](const Delta &delta) {
|
||||
switch (delta.action) {
|
||||
@ -443,8 +440,12 @@ ShardResult<PropertyValue> VertexAccessor::GetProperty(PropertyId property, View
|
||||
break;
|
||||
}
|
||||
});
|
||||
if (!exists) return SHARD_ERROR(ErrorCode::NONEXISTENT_OBJECT);
|
||||
if (!for_deleted_ && deleted) return SHARD_ERROR(ErrorCode::DELETED_OBJECT);
|
||||
if (!exists) {
|
||||
return SHARD_ERROR(ErrorCode::NONEXISTENT_OBJECT);
|
||||
}
|
||||
if (!for_deleted_ && deleted) {
|
||||
return SHARD_ERROR(ErrorCode::DELETED_OBJECT);
|
||||
}
|
||||
return std::move(value);
|
||||
}
|
||||
|
||||
@ -454,10 +455,10 @@ ShardResult<std::map<PropertyId, PropertyValue>> VertexAccessor::Properties(View
|
||||
std::map<PropertyId, PropertyValue> properties;
|
||||
Delta *delta = nullptr;
|
||||
{
|
||||
deleted = vertex_->deleted;
|
||||
deleted = vertex_->second.deleted;
|
||||
// TODO(antaljanosbenjamin): Should this also return the primary key?
|
||||
properties = vertex_->properties.Properties();
|
||||
delta = vertex_->delta;
|
||||
properties = vertex_->second.properties.Properties();
|
||||
delta = vertex_->second.delta;
|
||||
}
|
||||
ApplyDeltasForRead(transaction_, delta, view, [&exists, &deleted, &properties](const Delta &delta) {
|
||||
switch (delta.action) {
|
||||
@ -502,14 +503,14 @@ ShardResult<std::vector<EdgeAccessor>> VertexAccessor::InEdges(View view, const
|
||||
const VertexId *destination_id) const {
|
||||
bool exists = true;
|
||||
bool deleted = false;
|
||||
std::vector<Vertex::EdgeLink> in_edges;
|
||||
std::vector<VertexData::EdgeLink> in_edges;
|
||||
Delta *delta = nullptr;
|
||||
{
|
||||
deleted = vertex_->deleted;
|
||||
deleted = vertex_->second.deleted;
|
||||
if (edge_types.empty() && nullptr == destination_id) {
|
||||
in_edges = vertex_->in_edges;
|
||||
in_edges = vertex_->second.in_edges;
|
||||
} else {
|
||||
for (const auto &item : vertex_->in_edges) {
|
||||
for (const auto &item : vertex_->second.in_edges) {
|
||||
const auto &[edge_type, from_vertex, edge] = item;
|
||||
if (nullptr != destination_id && from_vertex != *destination_id) {
|
||||
continue;
|
||||
@ -519,7 +520,7 @@ ShardResult<std::vector<EdgeAccessor>> VertexAccessor::InEdges(View view, const
|
||||
in_edges.push_back(item);
|
||||
}
|
||||
}
|
||||
delta = vertex_->delta;
|
||||
delta = vertex_->second.delta;
|
||||
}
|
||||
ApplyDeltasForRead(
|
||||
transaction_, delta, view, [&exists, &deleted, &in_edges, &edge_types, destination_id](const Delta &delta) {
|
||||
@ -530,7 +531,7 @@ ShardResult<std::vector<EdgeAccessor>> VertexAccessor::InEdges(View view, const
|
||||
std::find(edge_types.begin(), edge_types.end(), delta.vertex_edge.edge_type) == edge_types.end())
|
||||
break;
|
||||
// Add the edge because we don't see the removal.
|
||||
Vertex::EdgeLink link{delta.vertex_edge.edge_type, delta.vertex_edge.vertex_id, delta.vertex_edge.edge};
|
||||
VertexData::EdgeLink link{delta.vertex_edge.edge_type, delta.vertex_edge.vertex_id, delta.vertex_edge.edge};
|
||||
auto it = std::find(in_edges.begin(), in_edges.end(), link);
|
||||
MG_ASSERT(it == in_edges.end(), "Invalid database state!");
|
||||
in_edges.push_back(link);
|
||||
@ -542,7 +543,7 @@ ShardResult<std::vector<EdgeAccessor>> VertexAccessor::InEdges(View view, const
|
||||
std::find(edge_types.begin(), edge_types.end(), delta.vertex_edge.edge_type) == edge_types.end())
|
||||
break;
|
||||
// Remove the label because we don't see the addition.
|
||||
Vertex::EdgeLink link{delta.vertex_edge.edge_type, delta.vertex_edge.vertex_id, delta.vertex_edge.edge};
|
||||
VertexData::EdgeLink link{delta.vertex_edge.edge_type, delta.vertex_edge.vertex_id, delta.vertex_edge.edge};
|
||||
auto it = std::find(in_edges.begin(), in_edges.end(), link);
|
||||
MG_ASSERT(it != in_edges.end(), "Invalid database state!");
|
||||
std::swap(*it, *in_edges.rbegin());
|
||||
@ -572,7 +573,7 @@ ShardResult<std::vector<EdgeAccessor>> VertexAccessor::InEdges(View view, const
|
||||
return ret;
|
||||
}
|
||||
ret.reserve(in_edges.size());
|
||||
const auto id = VertexId{vertex_validator_->primary_label_, vertex_->keys.Keys()};
|
||||
const auto id = VertexId{vertex_validator_->primary_label_, vertex_->first};
|
||||
for (const auto &item : in_edges) {
|
||||
const auto &[edge_type, from_vertex, edge] = item;
|
||||
ret.emplace_back(edge, edge_type, from_vertex, id, transaction_, indices_, config_);
|
||||
@ -584,14 +585,14 @@ ShardResult<std::vector<EdgeAccessor>> VertexAccessor::OutEdges(View view, const
|
||||
const VertexId *destination_id) const {
|
||||
bool exists = true;
|
||||
bool deleted = false;
|
||||
std::vector<Vertex::EdgeLink> out_edges;
|
||||
std::vector<VertexData::EdgeLink> out_edges;
|
||||
Delta *delta = nullptr;
|
||||
{
|
||||
deleted = vertex_->deleted;
|
||||
deleted = vertex_->second.deleted;
|
||||
if (edge_types.empty() && nullptr == destination_id) {
|
||||
out_edges = vertex_->out_edges;
|
||||
out_edges = vertex_->second.out_edges;
|
||||
} else {
|
||||
for (const auto &item : vertex_->out_edges) {
|
||||
for (const auto &item : vertex_->second.out_edges) {
|
||||
const auto &[edge_type, to_vertex, edge] = item;
|
||||
if (nullptr != destination_id && to_vertex != *destination_id) continue;
|
||||
if (!edge_types.empty() && std::find(edge_types.begin(), edge_types.end(), edge_type) == edge_types.end())
|
||||
@ -599,7 +600,7 @@ ShardResult<std::vector<EdgeAccessor>> VertexAccessor::OutEdges(View view, const
|
||||
out_edges.push_back(item);
|
||||
}
|
||||
}
|
||||
delta = vertex_->delta;
|
||||
delta = vertex_->second.delta;
|
||||
}
|
||||
ApplyDeltasForRead(
|
||||
transaction_, delta, view, [&exists, &deleted, &out_edges, &edge_types, destination_id](const Delta &delta) {
|
||||
@ -610,7 +611,7 @@ ShardResult<std::vector<EdgeAccessor>> VertexAccessor::OutEdges(View view, const
|
||||
std::find(edge_types.begin(), edge_types.end(), delta.vertex_edge.edge_type) == edge_types.end())
|
||||
break;
|
||||
// Add the edge because we don't see the removal.
|
||||
Vertex::EdgeLink link{delta.vertex_edge.edge_type, delta.vertex_edge.vertex_id, delta.vertex_edge.edge};
|
||||
VertexData::EdgeLink link{delta.vertex_edge.edge_type, delta.vertex_edge.vertex_id, delta.vertex_edge.edge};
|
||||
auto it = std::find(out_edges.begin(), out_edges.end(), link);
|
||||
MG_ASSERT(it == out_edges.end(), "Invalid database state!");
|
||||
out_edges.push_back(link);
|
||||
@ -622,7 +623,7 @@ ShardResult<std::vector<EdgeAccessor>> VertexAccessor::OutEdges(View view, const
|
||||
std::find(edge_types.begin(), edge_types.end(), delta.vertex_edge.edge_type) == edge_types.end())
|
||||
break;
|
||||
// Remove the label because we don't see the addition.
|
||||
Vertex::EdgeLink link{delta.vertex_edge.edge_type, delta.vertex_edge.vertex_id, delta.vertex_edge.edge};
|
||||
VertexData::EdgeLink link{delta.vertex_edge.edge_type, delta.vertex_edge.vertex_id, delta.vertex_edge.edge};
|
||||
auto it = std::find(out_edges.begin(), out_edges.end(), link);
|
||||
MG_ASSERT(it != out_edges.end(), "Invalid database state!");
|
||||
std::swap(*it, *out_edges.rbegin());
|
||||
@ -652,7 +653,7 @@ ShardResult<std::vector<EdgeAccessor>> VertexAccessor::OutEdges(View view, const
|
||||
return ret;
|
||||
}
|
||||
ret.reserve(out_edges.size());
|
||||
const auto id = VertexId{vertex_validator_->primary_label_, vertex_->keys.Keys()};
|
||||
const auto id = VertexId{vertex_validator_->primary_label_, vertex_->first};
|
||||
for (const auto &item : out_edges) {
|
||||
const auto &[edge_type, to_vertex, edge] = item;
|
||||
ret.emplace_back(edge, edge_type, id, to_vertex, transaction_, indices_, config_);
|
||||
@ -666,9 +667,9 @@ ShardResult<size_t> VertexAccessor::InDegree(View view) const {
|
||||
size_t degree = 0;
|
||||
Delta *delta = nullptr;
|
||||
{
|
||||
deleted = vertex_->deleted;
|
||||
degree = vertex_->in_edges.size();
|
||||
delta = vertex_->delta;
|
||||
deleted = vertex_->second.deleted;
|
||||
degree = vertex_->second.in_edges.size();
|
||||
delta = vertex_->second.delta;
|
||||
}
|
||||
ApplyDeltasForRead(transaction_, delta, view, [&exists, &deleted, °ree](const Delta &delta) {
|
||||
switch (delta.action) {
|
||||
@ -703,9 +704,9 @@ ShardResult<size_t> VertexAccessor::OutDegree(View view) const {
|
||||
size_t degree = 0;
|
||||
Delta *delta = nullptr;
|
||||
{
|
||||
deleted = vertex_->deleted;
|
||||
degree = vertex_->out_edges.size();
|
||||
delta = vertex_->delta;
|
||||
deleted = vertex_->second.deleted;
|
||||
degree = vertex_->second.out_edges.size();
|
||||
delta = vertex_->second.delta;
|
||||
}
|
||||
ApplyDeltasForRead(transaction_, delta, view, [&exists, &deleted, °ree](const Delta &delta) {
|
||||
switch (delta.action) {
|
||||
|
@ -17,9 +17,9 @@
|
||||
|
||||
#include "coordinator/hybrid_logical_clock.hpp"
|
||||
#include "storage/v3/key_store.hpp"
|
||||
#include "storage/v3/lexicographically_ordered_vertex.hpp"
|
||||
#include "storage/v3/mvcc.hpp"
|
||||
#include "storage/v3/transaction.hpp"
|
||||
#include "storage/v3/vertex.hpp"
|
||||
#include "utils/skip_list.hpp"
|
||||
|
||||
namespace memgraph::benchmark {
|
||||
@ -40,9 +40,7 @@ inline void PrepareData(std::map<TKey, TValue> &std_map, const int64_t num_eleme
|
||||
storage::v3::Transaction transaction{start_timestamp, storage::v3::IsolationLevel::SNAPSHOT_ISOLATION};
|
||||
auto *delta = storage::v3::CreateDeleteObjectDelta(&transaction);
|
||||
for (auto i{0}; i < num_elements; ++i) {
|
||||
std_map.insert({storage::v3::PrimaryKey{storage::v3::PropertyValue{i}},
|
||||
storage::v3::LexicographicallyOrderedVertex{storage::v3::Vertex{
|
||||
delta, std::vector<storage::v3::PropertyValue>{storage::v3::PropertyValue{true}}}}});
|
||||
std_map.insert({storage::v3::PrimaryKey{storage::v3::PropertyValue{i}}, storage::v3::VertexData{delta}});
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -25,7 +25,6 @@
|
||||
|
||||
#include "data_structures_common.hpp"
|
||||
#include "storage/v3/key_store.hpp"
|
||||
#include "storage/v3/lexicographically_ordered_vertex.hpp"
|
||||
#include "storage/v3/mvcc.hpp"
|
||||
#include "storage/v3/property_value.hpp"
|
||||
#include "storage/v3/transaction.hpp"
|
||||
@ -57,7 +56,7 @@ static void BM_BenchmarkContainsSkipList(::benchmark::State &state) {
|
||||
}
|
||||
|
||||
static void BM_BenchmarkContainsStdMap(::benchmark::State &state) {
|
||||
std::map<storage::v3::PrimaryKey, storage::v3::LexicographicallyOrderedVertex> std_map;
|
||||
std::map<storage::v3::PrimaryKey, storage::v3::VertexData> std_map;
|
||||
PrepareData(std_map, state.range(0));
|
||||
|
||||
// So we can also have elements that does don't exist
|
||||
|
@ -25,7 +25,6 @@
|
||||
|
||||
#include "data_structures_common.hpp"
|
||||
#include "storage/v3/key_store.hpp"
|
||||
#include "storage/v3/lexicographically_ordered_vertex.hpp"
|
||||
#include "storage/v3/mvcc.hpp"
|
||||
#include "storage/v3/property_value.hpp"
|
||||
#include "storage/v3/transaction.hpp"
|
||||
@ -57,7 +56,7 @@ static void BM_BenchmarkFindSkipList(::benchmark::State &state) {
|
||||
}
|
||||
|
||||
static void BM_BenchmarkFindStdMap(::benchmark::State &state) {
|
||||
std::map<storage::v3::PrimaryKey, storage::v3::LexicographicallyOrderedVertex> std_map;
|
||||
std::map<storage::v3::PrimaryKey, storage::v3::VertexData> std_map;
|
||||
PrepareData(std_map, state.range(0));
|
||||
// So we can also have elements that does don't exist
|
||||
std::mt19937 i_generator(std::random_device{}());
|
||||
|
@ -24,7 +24,6 @@
|
||||
#include <gflags/gflags.h>
|
||||
|
||||
#include "storage/v3/key_store.hpp"
|
||||
#include "storage/v3/lexicographically_ordered_vertex.hpp"
|
||||
#include "storage/v3/mvcc.hpp"
|
||||
#include "storage/v3/property_value.hpp"
|
||||
#include "storage/v3/transaction.hpp"
|
||||
@ -50,16 +49,14 @@ static void BM_BenchmarkInsertSkipList(::benchmark::State &state) {
|
||||
}
|
||||
|
||||
static void BM_BenchmarkInsertStdMap(::benchmark::State &state) {
|
||||
std::map<storage::v3::PrimaryKey, storage::v3::LexicographicallyOrderedVertex> std_map;
|
||||
std::map<storage::v3::PrimaryKey, storage::v3::VertexData> std_map;
|
||||
coordinator::Hlc start_timestamp;
|
||||
storage::v3::Transaction transaction{start_timestamp, storage::v3::IsolationLevel::SNAPSHOT_ISOLATION};
|
||||
auto *delta = storage::v3::CreateDeleteObjectDelta(&transaction);
|
||||
|
||||
for (auto _ : state) {
|
||||
for (auto i{0}; i < state.range(0); ++i) {
|
||||
std_map.insert({storage::v3::PrimaryKey{storage::v3::PropertyValue{i}},
|
||||
storage::v3::LexicographicallyOrderedVertex{storage::v3::Vertex{
|
||||
delta, std::vector<storage::v3::PropertyValue>{storage::v3::PropertyValue{true}}}}});
|
||||
std_map.insert({storage::v3::PrimaryKey{storage::v3::PropertyValue{i}}, storage::v3::VertexData{delta}});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -25,7 +25,6 @@
|
||||
|
||||
#include "data_structures_common.hpp"
|
||||
#include "storage/v3/key_store.hpp"
|
||||
#include "storage/v3/lexicographically_ordered_vertex.hpp"
|
||||
#include "storage/v3/mvcc.hpp"
|
||||
#include "storage/v3/property_value.hpp"
|
||||
#include "storage/v3/transaction.hpp"
|
||||
@ -58,7 +57,7 @@ static void BM_BenchmarkRemoveSkipList(::benchmark::State &state) {
|
||||
}
|
||||
|
||||
static void BM_BenchmarkRemoveStdMap(::benchmark::State &state) {
|
||||
std::map<storage::v3::PrimaryKey, storage::v3::LexicographicallyOrderedVertex> std_map;
|
||||
std::map<storage::v3::PrimaryKey, storage::v3::VertexData> std_map;
|
||||
PrepareData(std_map, state.range(0));
|
||||
|
||||
// So we can also have elements that does don't exist
|
||||
|
@ -328,9 +328,6 @@ target_link_libraries(${test_prefix}query_v2_dummy_test mg-query-v2)
|
||||
add_unit_test(storage_v3_property_store.cpp)
|
||||
target_link_libraries(${test_prefix}storage_v3_property_store mg-storage-v3 fmt)
|
||||
|
||||
add_unit_test(storage_v3_key_store.cpp)
|
||||
target_link_libraries(${test_prefix}storage_v3_key_store mg-storage-v3 rapidcheck rapidcheck_gtest)
|
||||
|
||||
add_unit_test(storage_v3_indices.cpp)
|
||||
target_link_libraries(${test_prefix}storage_v3_indices mg-storage-v3)
|
||||
|
||||
|
@ -73,10 +73,10 @@ inline expr::ExecutionContext MakeContext(const expr::AstStorage &storage, const
|
||||
return context;
|
||||
}
|
||||
|
||||
inline MockedLogicalOperator &BaseToMock(plan::LogicalOperator *op) {
|
||||
return *static_cast<MockedLogicalOperator *>(op);
|
||||
inline MockedLogicalOperator &BaseToMock(plan::LogicalOperator &op) {
|
||||
return dynamic_cast<MockedLogicalOperator &>(op);
|
||||
}
|
||||
|
||||
inline MockedCursor &BaseToMock(plan::Cursor *cursor) { return *static_cast<MockedCursor *>(cursor); }
|
||||
inline MockedCursor &BaseToMock(plan::Cursor &cursor) { return dynamic_cast<MockedCursor &>(cursor); }
|
||||
|
||||
} // namespace memgraph::query::v2
|
||||
|
@ -26,12 +26,11 @@
|
||||
namespace memgraph::query::v2 {
|
||||
|
||||
MultiFrame CreateMultiFrame(const size_t max_pos, const Symbol &src, const Symbol &dst, MockedRequestRouter *router) {
|
||||
static constexpr size_t frame_size = 100;
|
||||
MultiFrame multi_frame(max_pos, frame_size, utils::NewDeleteResource());
|
||||
static constexpr size_t number_of_frames = 100;
|
||||
MultiFrame multi_frame(max_pos, number_of_frames, utils::NewDeleteResource());
|
||||
auto frames_populator = multi_frame.GetInvalidFramesPopulator();
|
||||
size_t i = 0;
|
||||
for (auto &frame : frames_populator) {
|
||||
frame.MakeValid();
|
||||
auto &src_acc = frame.at(src);
|
||||
auto &dst_acc = frame.at(dst);
|
||||
auto v1 = msgs::Vertex{.id = {{msgs::LabelId::FromUint(1)}, {msgs::Value(static_cast<int64_t>(i++))}}};
|
||||
@ -41,6 +40,8 @@ MultiFrame CreateMultiFrame(const size_t max_pos, const Symbol &src, const Symbo
|
||||
dst_acc = TypedValue(query::v2::accessors::VertexAccessor(v2, mp, router));
|
||||
}
|
||||
|
||||
multi_frame.MakeAllFramesInvalid();
|
||||
|
||||
return multi_frame;
|
||||
}
|
||||
|
||||
@ -55,25 +56,39 @@ TEST(CreateExpandTest, Cursor) {
|
||||
plan::EdgeCreationInfo edge;
|
||||
edge.edge_type = msgs::EdgeTypeId::FromUint(1);
|
||||
edge.direction = EdgeAtom::Direction::IN;
|
||||
edge.symbol = symbol_table.CreateSymbol("e", true);
|
||||
auto id_alloc = IdAllocator(0, 100);
|
||||
|
||||
const auto &src = symbol_table.CreateSymbol("n", true);
|
||||
node.symbol = symbol_table.CreateSymbol("u", true);
|
||||
|
||||
auto once_cur = plan::MakeUniqueCursorPtr<MockedCursor>(utils::NewDeleteResource());
|
||||
EXPECT_CALL(BaseToMock(once_cur.get()), PullMultiple(_, _)).Times(1);
|
||||
|
||||
std::shared_ptr<plan::LogicalOperator> once_op = std::make_shared<MockedLogicalOperator>();
|
||||
EXPECT_CALL(BaseToMock(once_op.get()), MakeCursor(_)).Times(1).WillOnce(Return(std::move(once_cur)));
|
||||
auto once_op = std::make_shared<plan::Once>();
|
||||
auto once_cur = once_op->MakeCursor(utils::NewDeleteResource());
|
||||
|
||||
auto create_expand = plan::CreateExpand(node, edge, once_op, src, true);
|
||||
auto cursor = create_expand.MakeCursor(utils::NewDeleteResource());
|
||||
|
||||
MockedRequestRouter router;
|
||||
EXPECT_CALL(router, CreateExpand(_)).Times(1).WillOnce(Return(std::vector<msgs::CreateExpandResponse>{}));
|
||||
EXPECT_CALL(router, CreateExpand(_))
|
||||
.Times(1)
|
||||
.WillOnce(Return(std::vector<msgs::CreateExpandResponse>{msgs::CreateExpandResponse{}}));
|
||||
auto context = MakeContext(ast, symbol_table, &router, &id_alloc);
|
||||
auto multi_frame = CreateMultiFrame(context.symbol_table.max_position(), src, node.symbol, &router);
|
||||
cursor->PullMultiple(multi_frame, context);
|
||||
|
||||
auto frames = multi_frame.GetValidFramesReader();
|
||||
auto number_of_valid_frames = 0;
|
||||
for (auto &frame : frames) {
|
||||
++number_of_valid_frames;
|
||||
EXPECT_EQ(frame[edge.symbol].IsEdge(), true);
|
||||
const auto &e = frame[edge.symbol].ValueEdge();
|
||||
EXPECT_EQ(e.EdgeType(), edge.edge_type);
|
||||
}
|
||||
EXPECT_EQ(number_of_valid_frames, 1);
|
||||
|
||||
auto invalid_frames = multi_frame.GetInvalidFramesPopulator();
|
||||
auto number_of_invalid_frames = std::distance(invalid_frames.begin(), invalid_frames.end());
|
||||
EXPECT_EQ(number_of_invalid_frames, 99);
|
||||
}
|
||||
|
||||
} // namespace memgraph::query::v2
|
||||
|
@ -1,46 +0,0 @@
|
||||
// Copyright 2022 Memgraph Ltd.
|
||||
//
|
||||
// Use of this software is governed by the Business Source License
|
||||
// included in the file licenses/BSL.txt; by using this file, you agree to be bound by the terms of the Business Source
|
||||
// License, and you may not use this file except in compliance with the Business Source License.
|
||||
//
|
||||
// As of the Change Date specified in that file, in accordance with
|
||||
// the Business Source License, use of this software will be governed
|
||||
// by the Apache License, Version 2.0, included in the file
|
||||
// licenses/APL.txt.
|
||||
|
||||
#include <algorithm>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
/**
|
||||
* gtest/gtest.h must be included before rapidcheck/gtest.h!
|
||||
*/
|
||||
#include <gtest/gtest.h>
|
||||
#include <rapidcheck.h>
|
||||
#include <rapidcheck/gtest.h>
|
||||
|
||||
#include "storage/v3/id_types.hpp"
|
||||
#include "storage/v3/key_store.hpp"
|
||||
#include "storage/v3/property_value.hpp"
|
||||
|
||||
namespace memgraph::storage::v3::test {
|
||||
|
||||
RC_GTEST_PROP(KeyStore, KeyStore, (std::vector<std::string> values)) {
|
||||
RC_PRE(!values.empty());
|
||||
|
||||
std::vector<PropertyValue> property_values;
|
||||
property_values.reserve(values.size());
|
||||
std::transform(values.begin(), values.end(), std::back_inserter(property_values),
|
||||
[](std::string &value) { return PropertyValue{std::move(value)}; });
|
||||
|
||||
KeyStore key_store{property_values};
|
||||
|
||||
const auto keys = key_store.Keys();
|
||||
RC_ASSERT(keys.size() == property_values.size());
|
||||
for (int i = 0; i < keys.size(); ++i) {
|
||||
RC_ASSERT(keys[i] == property_values[i]);
|
||||
RC_ASSERT(key_store.GetKey(i) == property_values[i]);
|
||||
}
|
||||
}
|
||||
} // namespace memgraph::storage::v3::test
|
Loading…
Reference in New Issue
Block a user