change from edge draft

This commit is contained in:
imilinovic
2023-09-03 19:08:05 +02:00
parent 9661c52179
commit 5933096113
8 changed files with 164 additions and 0 deletions

View File

@@ -427,6 +427,10 @@ inline mgp_edge_type edge_get_type(mgp_edge *e) { return MgInvoke<mgp_edge_type>
inline mgp_vertex *edge_get_from(mgp_edge *e) { return MgInvoke<mgp_vertex *>(mgp_edge_get_from, e); }
inline void edge_change_from(mgp_edge *e, mgp_vertex *new_from) {
return MgInvokeVoid(mgp_edge_change_from, e, new_from);
}
inline mgp_vertex *edge_get_to(mgp_edge *e) { return MgInvoke<mgp_vertex *>(mgp_edge_get_to, e); }
inline mgp_value *edge_get_property(mgp_edge *e, const char *property_name, mgp_memory *memory) {

View File

@@ -792,6 +792,8 @@ enum mgp_error mgp_edge_get_type(struct mgp_edge *e, struct mgp_edge_type *resul
/// Current implementation always returns without errors.
enum mgp_error mgp_edge_get_from(struct mgp_edge *e, struct mgp_vertex **result);
enum mgp_error mgp_edge_change_from(struct mgp_edge *e, struct mgp_vertex *new_from);
/// Get the destination vertex of the given edge.
/// Resulting vertex is valid until the edge is valid and it must not be used afterwards.
/// Current implementation always returns without errors.

View File

@@ -250,6 +250,8 @@ class Graph {
void DetachDeleteNode(const Node &node);
/// @brief Creates a relationship of type `type` between nodes `from` and `to` and adds it to the graph.
Relationship CreateRelationship(const Node &from, const Node &to, const std::string_view type);
/// @brief Changes a relationship from node.
void ChangeRelationshipFrom(Relationship &relationship, const Node &new_from);
/// @brief Deletes a relationship from the graph.
void DeleteRelationship(const Relationship &relationship);
@@ -797,6 +799,8 @@ class Relationship {
/// @brief Returns the relationships source node.
Node From() const;
/// @brief todo
void ChangeFrom(Node *new_from);
/// @brief Returns the relationships destination node.
Node To() const;
@@ -1979,6 +1983,10 @@ inline Relationship Graph::CreateRelationship(const Node &from, const Node &to,
return relationship;
}
inline void Graph::ChangeRelationshipFrom(Relationship &relationship, const Node &new_from) {
mgp::edge_change_from(relationship.ptr_, new_from.ptr_);
}
inline void Graph::DeleteRelationship(const Relationship &relationship) {
mgp::graph_delete_edge(graph_, relationship.ptr_);
}

View File

@@ -76,6 +76,18 @@ SubgraphDbAccessor::DetachRemoveVertex( // NOLINT(readability-convert-member-fu
"Vertex holds only partial information about edges. Cannot detach delete safely while using projected graph."};
}
storage::Result<void> SubgraphDbAccessor::ChangeEdgeFrom(EdgeAccessor *edge, SubgraphVertexAccessor *new_from) {
VertexAccessor *new_from_impl = &new_from->impl_;
if (!this->graph_->ContainsVertex(*new_from_impl)) {
throw std::logic_error{"Projected graph must contain the new vertex!"};
}
auto result = db_accessor_.ChangeEdgeFrom(edge, new_from_impl);
if (result.HasError()) {
return result;
}
return {};
}
storage::Result<std::optional<VertexAccessor>> SubgraphDbAccessor::RemoveVertex(
SubgraphVertexAccessor *subgraphvertex_accessor) {
VertexAccessor *vertex_accessor = &subgraphvertex_accessor->impl_;

View File

@@ -19,6 +19,7 @@
#include "query/exceptions.hpp"
#include "storage/v2/edge_accessor.hpp"
#include "storage/v2/id_types.hpp"
#include "storage/v2/inmemory/storage.hpp"
#include "storage/v2/property_value.hpp"
#include "storage/v2/result.hpp"
#include "storage/v2/storage_mode.hpp"
@@ -370,6 +371,13 @@ class DbAccessor final {
return EdgeAccessor(*maybe_edge);
}
storage::Result<void> ChangeEdgeFrom(EdgeAccessor *edge, VertexAccessor *new_from) {
auto result = static_cast<storage::InMemoryStorage::InMemoryAccessor *>(accessor_)->ChangeEdgeFrom(
&edge->impl_, &new_from->impl_);
if (result.HasError()) return storage::Result<void>(result.GetError());
return {};
}
storage::Result<std::optional<EdgeAccessor>> RemoveEdge(EdgeAccessor *edge) {
auto res = accessor_->DeleteEdge(&edge->impl_);
if (res.HasError()) {
@@ -539,6 +547,8 @@ class SubgraphDbAccessor final {
storage::Result<EdgeAccessor> InsertEdge(SubgraphVertexAccessor *from, SubgraphVertexAccessor *to,
const storage::EdgeTypeId &edge_type);
storage::Result<void> ChangeEdgeFrom(EdgeAccessor *edge, SubgraphVertexAccessor *new_from);
storage::Result<std::optional<std::pair<VertexAccessor, std::vector<EdgeAccessor>>>> DetachRemoveVertex(
SubgraphVertexAccessor *vertex_accessor);

View File

@@ -2209,6 +2209,33 @@ mgp_error mgp_edge_get_from(mgp_edge *e, mgp_vertex **result) {
return mgp_error::MGP_ERROR_NO_ERROR;
}
mgp_error mgp_edge_change_from(struct mgp_edge *e, struct mgp_vertex *new_from) {
return WrapExceptions([&]() -> void {
auto result = std::visit(
memgraph::utils::Overloaded{[&](memgraph::query::DbAccessor *accessor) {
return accessor->ChangeEdgeFrom(
&e->impl, &std::get<memgraph::query::VertexAccessor>(new_from->impl));
},
[&](memgraph::query::SubgraphDbAccessor *accessor) {
return accessor->ChangeEdgeFrom(
&e->impl, &std::get<memgraph::query::SubgraphVertexAccessor>(new_from->impl));
}},
new_from->graph->impl);
if (result.HasError()) {
switch (result.GetError()) {
case memgraph::storage::Error::NONEXISTENT_OBJECT:
LOG_FATAL("Query modules shouldn't have access to nonexistent objects when removing an edge!");
case memgraph::storage::Error::DELETED_OBJECT:
case memgraph::storage::Error::PROPERTIES_DISABLED:
case memgraph::storage::Error::VERTEX_HAS_EDGES:
LOG_FATAL("Unexpected error when removing an edge.");
case memgraph::storage::Error::SERIALIZATION_ERROR:
throw SerializationException{"Cannot serialize removing an edge."};
}
}
});
}
mgp_error mgp_edge_get_to(mgp_edge *e, mgp_vertex **result) {
*result = &e->to;
return mgp_error::MGP_ERROR_NO_ERROR;

View File

@@ -10,6 +10,7 @@
// licenses/APL.txt.
#include "storage/v2/inmemory/storage.hpp"
#include <optional>
#include "storage/v2/durability/durability.hpp"
#include "storage/v2/durability/snapshot.hpp"
@@ -493,6 +494,103 @@ Result<EdgeAccessor> InMemoryStorage::InMemoryAccessor::CreateEdgeEx(VertexAcces
&storage_->constraints_, config_);
}
Result<void> InMemoryStorage::InMemoryAccessor::ChangeEdgeFrom(EdgeAccessor *edge, VertexAccessor *new_from) {
MG_ASSERT(edge->transaction_ == new_from->transaction_,
"EdgeAccessor must be from the same transaction as the storage "
"accessor when deleting an edge!");
MG_ASSERT(edge->transaction_ == &transaction_,
"EdgeAccessor must be from the same transaction as the storage "
"accessor when deleting an edge!");
auto *old_from_vertex = edge->from_vertex_;
auto *new_from_vertex = new_from->vertex_;
auto *to_vertex = edge->to_vertex_;
auto edge_ref = edge->edge_;
auto edge_type = edge->edge_type_;
std::unique_lock<utils::RWSpinLock> guard;
if (config_.properties_on_edges) {
auto *edge_ptr = edge_ref.ptr;
guard = std::unique_lock{edge_ptr->lock};
if (!PrepareForWrite(&transaction_, edge_ptr)) return Error::SERIALIZATION_ERROR;
if (edge_ptr->deleted) return Error::DELETED_OBJECT;
}
std::unique_lock<utils::RWSpinLock> guard_old_from(old_from_vertex->lock, std::defer_lock);
std::unique_lock<utils::RWSpinLock> guard_new_from(new_from_vertex->lock, std::defer_lock);
std::unique_lock<utils::RWSpinLock> guard_to(to_vertex->lock, std::defer_lock);
std::vector<memgraph::storage::Vertex *> vertices{old_from_vertex, new_from_vertex, to_vertex};
sort(vertices.begin(), vertices.end(), [](auto x, auto y) { return x->gid < y->gid; });
// TODO ADD CHECKING IF IDS ARE SAME
for (auto *vertex : vertices) {
if (vertex == old_from_vertex) {
guard_old_from.lock();
} else if (vertex == new_from_vertex) {
guard_new_from.lock();
} else if (vertex == to_vertex) {
guard_to.lock();
} else {
return Error::NONEXISTENT_OBJECT;
}
}
if (!PrepareForWrite(&transaction_, old_from_vertex)) return Error::SERIALIZATION_ERROR;
MG_ASSERT(!old_from_vertex->deleted, "Invalid database state!");
if (!PrepareForWrite(&transaction_, new_from_vertex)) return Error::SERIALIZATION_ERROR;
MG_ASSERT(!new_from_vertex->deleted, "Invalid database state!");
if (to_vertex != old_from_vertex && to_vertex != new_from_vertex) {
if (!PrepareForWrite(&transaction_, to_vertex)) return Error::SERIALIZATION_ERROR;
MG_ASSERT(!to_vertex->deleted, "Invalid database state!");
}
auto delete_edge_from_storage = [&edge_type, &edge_ref, this](auto *vertex, auto *edges) {
std::tuple<EdgeTypeId, Vertex *, EdgeRef> link(edge_type, vertex, edge_ref);
auto it = std::find(edges->begin(), edges->end(), link);
if (config_.properties_on_edges) {
MG_ASSERT(it != edges->end(), "Invalid database state!");
} else if (it == edges->end()) {
return false;
}
std::swap(*it, *edges->rbegin());
edges->pop_back();
return true;
};
auto op1 = delete_edge_from_storage(to_vertex, &old_from_vertex->out_edges);
auto op2 = delete_edge_from_storage(old_from_vertex, &to_vertex->in_edges);
if (config_.properties_on_edges) {
MG_ASSERT((op1 && op2), "Invalid database state!");
} else {
MG_ASSERT((op1 && op2) || (!op1 && !op2), "Invalid database state!");
if (!op1 && !op2) {
// The edge is already deleted.
return Error::DELETED_OBJECT;
}
}
CreateAndLinkDelta(&transaction_, old_from_vertex, Delta::AddOutEdgeTag(), edge_type, to_vertex, edge_ref);
CreateAndLinkDelta(&transaction_, to_vertex, Delta::AddInEdgeTag(), edge_type, old_from_vertex, edge_ref);
CreateAndLinkDelta(&transaction_, new_from_vertex, Delta::RemoveOutEdgeTag(), edge_type, to_vertex, edge_ref);
new_from_vertex->out_edges.emplace_back(edge_type, to_vertex, edge_ref);
CreateAndLinkDelta(&transaction_, to_vertex, Delta::RemoveInEdgeTag(), edge_type, new_from_vertex, edge_ref);
to_vertex->in_edges.emplace_back(edge_type, new_from_vertex, edge_ref);
transaction_.manyDeltasCache.Invalidate(new_from_vertex, edge_type, EdgeDirection::OUT);
transaction_.manyDeltasCache.Invalidate(old_from_vertex, edge_type, EdgeDirection::OUT);
transaction_.manyDeltasCache.Invalidate(to_vertex, edge_type, EdgeDirection::IN);
return {};
}
Result<std::optional<EdgeAccessor>> InMemoryStorage::InMemoryAccessor::DeleteEdge(EdgeAccessor *edge) {
MG_ASSERT(edge->transaction_ == &transaction_,
"EdgeAccessor must be from the same transaction as the storage "

View File

@@ -215,6 +215,9 @@ class InMemoryStorage final : public Storage {
/// @throw std::bad_alloc
Result<EdgeAccessor> CreateEdge(VertexAccessor *from, VertexAccessor *to, EdgeTypeId edge_type) override;
/// Change edge from
Result<void> ChangeEdgeFrom(EdgeAccessor *edge, VertexAccessor *new_from);
/// Accessor to the deleted edge if a deletion took place, std::nullopt otherwise
/// @throw std::bad_alloc
Result<std::optional<EdgeAccessor>> DeleteEdge(EdgeAccessor *edge) override;