change from edge draft
This commit is contained in:
@@ -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) {
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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 relationship’s source node.
|
||||
Node From() const;
|
||||
/// @brief todo
|
||||
void ChangeFrom(Node *new_from);
|
||||
/// @brief Returns the relationship’s 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_);
|
||||
}
|
||||
|
||||
@@ -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_;
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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 "
|
||||
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user