Add NONEXISTENT_OBJECT error to storage v2
Summary: Previously, when accessing the labels/properties/edges of a vertex/edge that was just created the NEW view would correctly display the change, but the OLD view would be invalid and would crash the database. With this change the OLD view of a freshly created vertex/edge won't cause a crash, but will instead report an error. Reviewers: teon.banek Reviewed By: teon.banek Subscribers: pullbot Differential Revision: https://phabricator.memgraph.io/D2549
This commit is contained in:
parent
40ae31e9a0
commit
d17d463884
@ -98,6 +98,7 @@ std::map<std::string, communication::bolt::Value> BoltSession::PullAll(
|
||||
case storage::Error::SERIALIZATION_ERROR:
|
||||
case storage::Error::VERTEX_HAS_EDGES:
|
||||
case storage::Error::PROPERTIES_DISABLED:
|
||||
case storage::Error::NONEXISTENT_OBJECT:
|
||||
throw communication::bolt::ClientError(
|
||||
"Unexpected storage error when streaming summary.");
|
||||
}
|
||||
@ -150,6 +151,9 @@ void BoltSession::TypedValueResultStream::Result(
|
||||
case storage::Error::DELETED_OBJECT:
|
||||
throw communication::bolt::ClientError(
|
||||
"Returning a deleted object as a result.");
|
||||
case storage::Error::NONEXISTENT_OBJECT:
|
||||
throw communication::bolt::ClientError(
|
||||
"Returning a nonexistent object as a result.");
|
||||
case storage::Error::VERTEX_HAS_EDGES:
|
||||
case storage::Error::SERIALIZATION_ERROR:
|
||||
case storage::Error::PROPERTIES_DISABLED:
|
||||
|
@ -89,6 +89,7 @@ void PropsSetChecked(TRecordAccessor *record, const storage::Property &key,
|
||||
throw QueryRuntimeException(
|
||||
"Can't set property because properties are disabled.");
|
||||
case storage::Error::VERTEX_HAS_EDGES:
|
||||
case storage::Error::NONEXISTENT_OBJECT:
|
||||
throw QueryRuntimeException(
|
||||
"Unexpected error when setting a property.");
|
||||
}
|
||||
|
@ -104,6 +104,9 @@ void DumpVertex(std::ostream *os, query::DbAccessor *dba,
|
||||
case storage::Error::DELETED_OBJECT:
|
||||
throw query::QueryRuntimeException(
|
||||
"Trying to get labels from a deleted node.");
|
||||
case storage::Error::NONEXISTENT_OBJECT:
|
||||
throw query::QueryRuntimeException(
|
||||
"Trying to get labels from a node that doesn't exist.");
|
||||
case storage::Error::SERIALIZATION_ERROR:
|
||||
case storage::Error::VERTEX_HAS_EDGES:
|
||||
case storage::Error::PROPERTIES_DISABLED:
|
||||
@ -121,6 +124,9 @@ void DumpVertex(std::ostream *os, query::DbAccessor *dba,
|
||||
case storage::Error::DELETED_OBJECT:
|
||||
throw query::QueryRuntimeException(
|
||||
"Trying to get properties from a deleted object.");
|
||||
case storage::Error::NONEXISTENT_OBJECT:
|
||||
throw query::QueryRuntimeException(
|
||||
"Trying to get properties from a node that doesn't exist.");
|
||||
case storage::Error::SERIALIZATION_ERROR:
|
||||
case storage::Error::VERTEX_HAS_EDGES:
|
||||
case storage::Error::PROPERTIES_DISABLED:
|
||||
@ -150,6 +156,9 @@ void DumpEdge(std::ostream *os, query::DbAccessor *dba,
|
||||
case storage::Error::DELETED_OBJECT:
|
||||
throw query::QueryRuntimeException(
|
||||
"Trying to get properties from a deleted object.");
|
||||
case storage::Error::NONEXISTENT_OBJECT:
|
||||
throw query::QueryRuntimeException(
|
||||
"Trying to get properties from an edge that doesn't exist.");
|
||||
case storage::Error::SERIALIZATION_ERROR:
|
||||
case storage::Error::VERTEX_HAS_EDGES:
|
||||
case storage::Error::PROPERTIES_DISABLED:
|
||||
|
@ -369,6 +369,9 @@ TypedValue Properties(const TypedValue *args, int64_t nargs,
|
||||
case storage::Error::DELETED_OBJECT:
|
||||
throw QueryRuntimeException(
|
||||
"Trying to get properties from a deleted object.");
|
||||
case storage::Error::NONEXISTENT_OBJECT:
|
||||
throw query::QueryRuntimeException(
|
||||
"Trying to get properties from an object that doesn't exist.");
|
||||
case storage::Error::SERIALIZATION_ERROR:
|
||||
case storage::Error::VERTEX_HAS_EDGES:
|
||||
case storage::Error::PROPERTIES_DISABLED:
|
||||
@ -434,6 +437,9 @@ size_t UnwrapDegreeResult(storage::Result<size_t> maybe_degree) {
|
||||
switch (maybe_degree.GetError()) {
|
||||
case storage::Error::DELETED_OBJECT:
|
||||
throw QueryRuntimeException("Trying to get degree of a deleted node.");
|
||||
case storage::Error::NONEXISTENT_OBJECT:
|
||||
throw query::QueryRuntimeException(
|
||||
"Trying to get degree of a node that doesn't exist.");
|
||||
case storage::Error::SERIALIZATION_ERROR:
|
||||
case storage::Error::VERTEX_HAS_EDGES:
|
||||
case storage::Error::PROPERTIES_DISABLED:
|
||||
@ -572,6 +578,9 @@ TypedValue Keys(const TypedValue *args, int64_t nargs,
|
||||
case storage::Error::DELETED_OBJECT:
|
||||
throw QueryRuntimeException(
|
||||
"Trying to get keys from a deleted object.");
|
||||
case storage::Error::NONEXISTENT_OBJECT:
|
||||
throw query::QueryRuntimeException(
|
||||
"Trying to get keys from an object that doesn't exist.");
|
||||
case storage::Error::SERIALIZATION_ERROR:
|
||||
case storage::Error::VERTEX_HAS_EDGES:
|
||||
case storage::Error::PROPERTIES_DISABLED:
|
||||
@ -607,6 +616,9 @@ TypedValue Labels(const TypedValue *args, int64_t nargs,
|
||||
case storage::Error::DELETED_OBJECT:
|
||||
throw QueryRuntimeException(
|
||||
"Trying to get labels from a deleted node.");
|
||||
case storage::Error::NONEXISTENT_OBJECT:
|
||||
throw query::QueryRuntimeException(
|
||||
"Trying to get labels from a node that doesn't exist.");
|
||||
case storage::Error::SERIALIZATION_ERROR:
|
||||
case storage::Error::VERTEX_HAS_EDGES:
|
||||
case storage::Error::PROPERTIES_DISABLED:
|
||||
|
@ -315,6 +315,9 @@ class ExpressionEvaluator : public ExpressionVisitor<TypedValue> {
|
||||
case storage::Error::DELETED_OBJECT:
|
||||
throw QueryRuntimeException(
|
||||
"Trying to access labels on a deleted node.");
|
||||
case storage::Error::NONEXISTENT_OBJECT:
|
||||
throw query::QueryRuntimeException(
|
||||
"Trying to access labels from a node that doesn't exist.");
|
||||
case storage::Error::SERIALIZATION_ERROR:
|
||||
case storage::Error::VERTEX_HAS_EDGES:
|
||||
case storage::Error::PROPERTIES_DISABLED:
|
||||
@ -553,6 +556,9 @@ class ExpressionEvaluator : public ExpressionVisitor<TypedValue> {
|
||||
case storage::Error::DELETED_OBJECT:
|
||||
throw QueryRuntimeException(
|
||||
"Trying to get a property from a deleted object.");
|
||||
case storage::Error::NONEXISTENT_OBJECT:
|
||||
throw query::QueryRuntimeException(
|
||||
"Trying to get a property from an object that doesn't exist.");
|
||||
case storage::Error::SERIALIZATION_ERROR:
|
||||
case storage::Error::VERTEX_HAS_EDGES:
|
||||
case storage::Error::PROPERTIES_DISABLED:
|
||||
@ -573,6 +579,9 @@ class ExpressionEvaluator : public ExpressionVisitor<TypedValue> {
|
||||
case storage::Error::DELETED_OBJECT:
|
||||
throw QueryRuntimeException(
|
||||
"Trying to get a property from a deleted object.");
|
||||
case storage::Error::NONEXISTENT_OBJECT:
|
||||
throw query::QueryRuntimeException(
|
||||
"Trying to get a property from an object that doesn't exist.");
|
||||
case storage::Error::SERIALIZATION_ERROR:
|
||||
case storage::Error::VERTEX_HAS_EDGES:
|
||||
case storage::Error::PROPERTIES_DISABLED:
|
||||
|
@ -137,6 +137,7 @@ VertexAccessor &CreateLocalVertex(const NodeCreationInfo &node_info,
|
||||
"Trying to set a label on a deleted node.");
|
||||
case storage::Error::VERTEX_HAS_EDGES:
|
||||
case storage::Error::PROPERTIES_DISABLED:
|
||||
case storage::Error::NONEXISTENT_OBJECT:
|
||||
throw QueryRuntimeException("Unexpected error when setting a label.");
|
||||
}
|
||||
}
|
||||
@ -236,6 +237,7 @@ void CreateEdge(const EdgeCreationInfo &edge_info, DbAccessor *dba,
|
||||
"Trying to create an edge on a deleted node.");
|
||||
case storage::Error::VERTEX_HAS_EDGES:
|
||||
case storage::Error::PROPERTIES_DISABLED:
|
||||
case storage::Error::NONEXISTENT_OBJECT:
|
||||
throw QueryRuntimeException("Unexpected error when creating an edge.");
|
||||
}
|
||||
}
|
||||
@ -493,6 +495,9 @@ auto UnwrapEdgesResult(storage::Result<TEdges> &&result) {
|
||||
case storage::Error::DELETED_OBJECT:
|
||||
throw QueryRuntimeException(
|
||||
"Trying to get relationships of a deleted node.");
|
||||
case storage::Error::NONEXISTENT_OBJECT:
|
||||
throw query::QueryRuntimeException(
|
||||
"Trying to get relationships from a node that doesn't exist.");
|
||||
case storage::Error::VERTEX_HAS_EDGES:
|
||||
case storage::Error::SERIALIZATION_ERROR:
|
||||
case storage::Error::PROPERTIES_DISABLED:
|
||||
@ -699,6 +704,9 @@ size_t UnwrapDegreeResult(storage::Result<size_t> maybe_degree) {
|
||||
switch (maybe_degree.GetError()) {
|
||||
case storage::Error::DELETED_OBJECT:
|
||||
throw QueryRuntimeException("Trying to get degree of a deleted node.");
|
||||
case storage::Error::NONEXISTENT_OBJECT:
|
||||
throw query::QueryRuntimeException(
|
||||
"Trying to get degree from a node that doesn't exist.");
|
||||
case storage::Error::SERIALIZATION_ERROR:
|
||||
case storage::Error::VERTEX_HAS_EDGES:
|
||||
case storage::Error::PROPERTIES_DISABLED:
|
||||
@ -1911,6 +1919,7 @@ bool Delete::DeleteCursor::Pull(Frame &frame, ExecutionContext &context) {
|
||||
case storage::Error::DELETED_OBJECT:
|
||||
case storage::Error::VERTEX_HAS_EDGES:
|
||||
case storage::Error::PROPERTIES_DISABLED:
|
||||
case storage::Error::NONEXISTENT_OBJECT:
|
||||
throw QueryRuntimeException(
|
||||
"Unexpected error when deleting an edge.");
|
||||
}
|
||||
@ -1934,6 +1943,7 @@ bool Delete::DeleteCursor::Pull(Frame &frame, ExecutionContext &context) {
|
||||
case storage::Error::DELETED_OBJECT:
|
||||
case storage::Error::VERTEX_HAS_EDGES:
|
||||
case storage::Error::PROPERTIES_DISABLED:
|
||||
case storage::Error::NONEXISTENT_OBJECT:
|
||||
throw QueryRuntimeException(
|
||||
"Unexpected error when deleting a node.");
|
||||
}
|
||||
@ -1949,6 +1959,7 @@ bool Delete::DeleteCursor::Pull(Frame &frame, ExecutionContext &context) {
|
||||
throw RemoveAttachedVertexException();
|
||||
case storage::Error::DELETED_OBJECT:
|
||||
case storage::Error::PROPERTIES_DISABLED:
|
||||
case storage::Error::NONEXISTENT_OBJECT:
|
||||
throw QueryRuntimeException(
|
||||
"Unexpected error when deleting a node.");
|
||||
}
|
||||
@ -2077,6 +2088,7 @@ void SetPropertiesOnRecord(DbAccessor *dba, TRecordAccessor *record,
|
||||
throw QueryRuntimeException(
|
||||
"Can't set property because properties are disabled.");
|
||||
case storage::Error::VERTEX_HAS_EDGES:
|
||||
case storage::Error::NONEXISTENT_OBJECT:
|
||||
throw QueryRuntimeException(
|
||||
"Unexpected error when setting properties.");
|
||||
}
|
||||
@ -2090,6 +2102,9 @@ void SetPropertiesOnRecord(DbAccessor *dba, TRecordAccessor *record,
|
||||
case storage::Error::DELETED_OBJECT:
|
||||
throw QueryRuntimeException(
|
||||
"Trying to get properties from a deleted object.");
|
||||
case storage::Error::NONEXISTENT_OBJECT:
|
||||
throw query::QueryRuntimeException(
|
||||
"Trying to get properties from an object that doesn't exist.");
|
||||
case storage::Error::SERIALIZATION_ERROR:
|
||||
case storage::Error::VERTEX_HAS_EDGES:
|
||||
case storage::Error::PROPERTIES_DISABLED:
|
||||
@ -2115,6 +2130,7 @@ void SetPropertiesOnRecord(DbAccessor *dba, TRecordAccessor *record,
|
||||
throw QueryRuntimeException(
|
||||
"Can't set property because properties are disabled.");
|
||||
case storage::Error::VERTEX_HAS_EDGES:
|
||||
case storage::Error::NONEXISTENT_OBJECT:
|
||||
throw QueryRuntimeException(
|
||||
"Unexpected error when setting properties.");
|
||||
}
|
||||
@ -2223,6 +2239,7 @@ bool SetLabels::SetLabelsCursor::Pull(Frame &frame, ExecutionContext &context) {
|
||||
"Trying to set a label on a deleted node.");
|
||||
case storage::Error::VERTEX_HAS_EDGES:
|
||||
case storage::Error::PROPERTIES_DISABLED:
|
||||
case storage::Error::NONEXISTENT_OBJECT:
|
||||
throw QueryRuntimeException("Unexpected error when setting a label.");
|
||||
}
|
||||
}
|
||||
@ -2280,6 +2297,7 @@ bool RemoveProperty::RemovePropertyCursor::Pull(Frame &frame,
|
||||
throw QueryRuntimeException(
|
||||
"Can't remove property because properties are disabled.");
|
||||
case storage::Error::VERTEX_HAS_EDGES:
|
||||
case storage::Error::NONEXISTENT_OBJECT:
|
||||
throw QueryRuntimeException(
|
||||
"Unexpected error when removing property.");
|
||||
}
|
||||
@ -2352,6 +2370,7 @@ bool RemoveLabels::RemoveLabelsCursor::Pull(Frame &frame,
|
||||
"Trying to remove labels from a deleted node.");
|
||||
case storage::Error::VERTEX_HAS_EDGES:
|
||||
case storage::Error::PROPERTIES_DISABLED:
|
||||
case storage::Error::NONEXISTENT_OBJECT:
|
||||
throw QueryRuntimeException(
|
||||
"Unexpected error when removing labels from a node.");
|
||||
}
|
||||
|
@ -870,7 +870,8 @@ size_t mgp_vertex_labels_count(const mgp_vertex *v) {
|
||||
if (maybe_labels.HasError()) {
|
||||
switch (maybe_labels.GetError()) {
|
||||
case storage::Error::DELETED_OBJECT:
|
||||
// Treat deleted vertex as having no labels.
|
||||
case storage::Error::NONEXISTENT_OBJECT:
|
||||
// Treat deleted/nonexistent vertex as having no labels.
|
||||
return 0;
|
||||
case storage::Error::PROPERTIES_DISABLED:
|
||||
case storage::Error::VERTEX_HAS_EDGES:
|
||||
@ -888,6 +889,7 @@ mgp_label mgp_vertex_label_at(const mgp_vertex *v, size_t i) {
|
||||
if (maybe_labels.HasError()) {
|
||||
switch (maybe_labels.GetError()) {
|
||||
case storage::Error::DELETED_OBJECT:
|
||||
case storage::Error::NONEXISTENT_OBJECT:
|
||||
return mgp_label{nullptr};
|
||||
case storage::Error::PROPERTIES_DISABLED:
|
||||
case storage::Error::VERTEX_HAS_EDGES:
|
||||
@ -925,6 +927,7 @@ int mgp_vertex_has_label_named(const mgp_vertex *v, const char *name) {
|
||||
if (maybe_has_label.HasError()) {
|
||||
switch (maybe_has_label.GetError()) {
|
||||
case storage::Error::DELETED_OBJECT:
|
||||
case storage::Error::NONEXISTENT_OBJECT:
|
||||
return 0;
|
||||
case storage::Error::PROPERTIES_DISABLED:
|
||||
case storage::Error::VERTEX_HAS_EDGES:
|
||||
@ -948,7 +951,8 @@ mgp_value *mgp_vertex_get_property(const mgp_vertex *v, const char *name,
|
||||
if (maybe_prop.HasError()) {
|
||||
switch (maybe_prop.GetError()) {
|
||||
case storage::Error::DELETED_OBJECT:
|
||||
// Treat deleted vertex as having no properties.
|
||||
case storage::Error::NONEXISTENT_OBJECT:
|
||||
// Treat deleted/nonexistent vertex as having no properties.
|
||||
return new_mgp_object<mgp_value>(memory);
|
||||
case storage::Error::PROPERTIES_DISABLED:
|
||||
case storage::Error::VERTEX_HAS_EDGES:
|
||||
@ -976,7 +980,8 @@ mgp_properties_iterator *mgp_vertex_iter_properties(const mgp_vertex *v,
|
||||
if (maybe_props.HasError()) {
|
||||
switch (maybe_props.GetError()) {
|
||||
case storage::Error::DELETED_OBJECT:
|
||||
// Treat deleted vertex as having no properties.
|
||||
case storage::Error::NONEXISTENT_OBJECT:
|
||||
// Treat deleted/nonexistent vertex as having no properties.
|
||||
return new_mgp_object<mgp_properties_iterator>(memory, v->graph);
|
||||
case storage::Error::PROPERTIES_DISABLED:
|
||||
case storage::Error::VERTEX_HAS_EDGES:
|
||||
@ -1007,7 +1012,8 @@ mgp_edges_iterator *mgp_vertex_iter_in_edges(const mgp_vertex *v,
|
||||
if (maybe_edges.HasError()) {
|
||||
switch (maybe_edges.GetError()) {
|
||||
case storage::Error::DELETED_OBJECT:
|
||||
// Treat deleted vertex as having no edges.
|
||||
case storage::Error::NONEXISTENT_OBJECT:
|
||||
// Treat deleted/nonexistent vertex as having no edges.
|
||||
return it;
|
||||
case storage::Error::PROPERTIES_DISABLED:
|
||||
case storage::Error::VERTEX_HAS_EDGES:
|
||||
@ -1039,7 +1045,8 @@ mgp_edges_iterator *mgp_vertex_iter_out_edges(const mgp_vertex *v,
|
||||
if (maybe_edges.HasError()) {
|
||||
switch (maybe_edges.GetError()) {
|
||||
case storage::Error::DELETED_OBJECT:
|
||||
// Treat deleted vertex as having no edges.
|
||||
case storage::Error::NONEXISTENT_OBJECT:
|
||||
// Treat deleted/nonexistent vertex as having no edges.
|
||||
return it;
|
||||
case storage::Error::PROPERTIES_DISABLED:
|
||||
case storage::Error::VERTEX_HAS_EDGES:
|
||||
@ -1125,7 +1132,8 @@ mgp_value *mgp_edge_get_property(const mgp_edge *e, const char *name,
|
||||
if (maybe_prop.HasError()) {
|
||||
switch (maybe_prop.GetError()) {
|
||||
case storage::Error::DELETED_OBJECT:
|
||||
// Treat deleted edge as having no properties.
|
||||
case storage::Error::NONEXISTENT_OBJECT:
|
||||
// Treat deleted/nonexistent edge as having no properties.
|
||||
return new_mgp_object<mgp_value>(memory);
|
||||
case storage::Error::PROPERTIES_DISABLED:
|
||||
case storage::Error::VERTEX_HAS_EDGES:
|
||||
@ -1154,7 +1162,8 @@ mgp_properties_iterator *mgp_edge_iter_properties(const mgp_edge *e,
|
||||
if (maybe_props.HasError()) {
|
||||
switch (maybe_props.GetError()) {
|
||||
case storage::Error::DELETED_OBJECT:
|
||||
// Treat deleted edge as having no properties.
|
||||
case storage::Error::NONEXISTENT_OBJECT:
|
||||
// Treat deleted/nonexistent edge as having no properties.
|
||||
return new_mgp_object<mgp_properties_iterator>(memory, e->from.graph);
|
||||
case storage::Error::PROPERTIES_DISABLED:
|
||||
case storage::Error::VERTEX_HAS_EDGES:
|
||||
|
@ -79,6 +79,7 @@ Result<bool> EdgeAccessor::ClearProperties() {
|
||||
Result<PropertyValue> EdgeAccessor::GetProperty(PropertyId property,
|
||||
View view) const {
|
||||
if (!config_.properties_on_edges) return PropertyValue();
|
||||
bool exists = true;
|
||||
bool deleted = false;
|
||||
PropertyValue value;
|
||||
Delta *delta = nullptr;
|
||||
@ -92,7 +93,7 @@ Result<PropertyValue> EdgeAccessor::GetProperty(PropertyId property,
|
||||
delta = edge_.ptr->delta;
|
||||
}
|
||||
ApplyDeltasForRead(transaction_, delta, view,
|
||||
[&deleted, &value, property](const Delta &delta) {
|
||||
[&exists, &deleted, &value, property](const Delta &delta) {
|
||||
switch (delta.action) {
|
||||
case Delta::Action::SET_PROPERTY: {
|
||||
if (delta.property.key == property) {
|
||||
@ -101,7 +102,7 @@ Result<PropertyValue> EdgeAccessor::GetProperty(PropertyId property,
|
||||
break;
|
||||
}
|
||||
case Delta::Action::DELETE_OBJECT: {
|
||||
LOG(FATAL) << "Invalid accessor!";
|
||||
exists = false;
|
||||
break;
|
||||
}
|
||||
case Delta::Action::RECREATE_OBJECT: {
|
||||
@ -117,6 +118,7 @@ Result<PropertyValue> EdgeAccessor::GetProperty(PropertyId property,
|
||||
break;
|
||||
}
|
||||
});
|
||||
if (!exists) return Error::NONEXISTENT_OBJECT;
|
||||
if (deleted) return Error::DELETED_OBJECT;
|
||||
return std::move(value);
|
||||
}
|
||||
@ -125,8 +127,9 @@ Result<std::map<PropertyId, PropertyValue>> EdgeAccessor::Properties(
|
||||
View view) const {
|
||||
if (!config_.properties_on_edges)
|
||||
return std::map<PropertyId, PropertyValue>{};
|
||||
std::map<PropertyId, PropertyValue> properties;
|
||||
bool exists = true;
|
||||
bool deleted = false;
|
||||
std::map<PropertyId, PropertyValue> properties;
|
||||
Delta *delta = nullptr;
|
||||
{
|
||||
std::lock_guard<utils::SpinLock> guard(edge_.ptr->lock);
|
||||
@ -134,44 +137,44 @@ Result<std::map<PropertyId, PropertyValue>> EdgeAccessor::Properties(
|
||||
properties = edge_.ptr->properties;
|
||||
delta = edge_.ptr->delta;
|
||||
}
|
||||
ApplyDeltasForRead(
|
||||
transaction_, delta, view, [&deleted, &properties](const Delta &delta) {
|
||||
switch (delta.action) {
|
||||
case Delta::Action::SET_PROPERTY: {
|
||||
auto it = properties.find(delta.property.key);
|
||||
if (it != properties.end()) {
|
||||
if (delta.property.value.IsNull()) {
|
||||
// remove the property
|
||||
properties.erase(it);
|
||||
} else {
|
||||
// set the value
|
||||
it->second = delta.property.value;
|
||||
}
|
||||
} else if (!delta.property.value.IsNull()) {
|
||||
properties.emplace(delta.property.key, delta.property.value);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case Delta::Action::DELETE_OBJECT: {
|
||||
LOG(FATAL) << "Invalid accessor!";
|
||||
break;
|
||||
}
|
||||
case Delta::Action::RECREATE_OBJECT: {
|
||||
deleted = false;
|
||||
break;
|
||||
}
|
||||
case Delta::Action::ADD_LABEL:
|
||||
case Delta::Action::REMOVE_LABEL:
|
||||
case Delta::Action::ADD_IN_EDGE:
|
||||
case Delta::Action::ADD_OUT_EDGE:
|
||||
case Delta::Action::REMOVE_IN_EDGE:
|
||||
case Delta::Action::REMOVE_OUT_EDGE:
|
||||
break;
|
||||
}
|
||||
});
|
||||
if (deleted) {
|
||||
return Error::DELETED_OBJECT;
|
||||
}
|
||||
ApplyDeltasForRead(transaction_, delta, view,
|
||||
[&exists, &deleted, &properties](const Delta &delta) {
|
||||
switch (delta.action) {
|
||||
case Delta::Action::SET_PROPERTY: {
|
||||
auto it = properties.find(delta.property.key);
|
||||
if (it != properties.end()) {
|
||||
if (delta.property.value.IsNull()) {
|
||||
// remove the property
|
||||
properties.erase(it);
|
||||
} else {
|
||||
// set the value
|
||||
it->second = delta.property.value;
|
||||
}
|
||||
} else if (!delta.property.value.IsNull()) {
|
||||
properties.emplace(delta.property.key,
|
||||
delta.property.value);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case Delta::Action::DELETE_OBJECT: {
|
||||
exists = false;
|
||||
break;
|
||||
}
|
||||
case Delta::Action::RECREATE_OBJECT: {
|
||||
deleted = false;
|
||||
break;
|
||||
}
|
||||
case Delta::Action::ADD_LABEL:
|
||||
case Delta::Action::REMOVE_LABEL:
|
||||
case Delta::Action::ADD_IN_EDGE:
|
||||
case Delta::Action::ADD_OUT_EDGE:
|
||||
case Delta::Action::REMOVE_IN_EDGE:
|
||||
case Delta::Action::REMOVE_OUT_EDGE:
|
||||
break;
|
||||
}
|
||||
});
|
||||
if (!exists) return Error::NONEXISTENT_OBJECT;
|
||||
if (deleted) return Error::DELETED_OBJECT;
|
||||
return std::move(properties);
|
||||
}
|
||||
|
||||
|
@ -10,6 +10,7 @@ static_assert(std::is_same_v<uint8_t, unsigned char>);
|
||||
|
||||
enum class Error : uint8_t {
|
||||
SERIALIZATION_ERROR,
|
||||
NONEXISTENT_OBJECT,
|
||||
DELETED_OBJECT,
|
||||
VERTEX_HAS_EDGES,
|
||||
PROPERTIES_DISABLED,
|
||||
|
@ -86,6 +86,7 @@ Result<bool> VertexAccessor::RemoveLabel(LabelId label) {
|
||||
}
|
||||
|
||||
Result<bool> VertexAccessor::HasLabel(LabelId label, View view) const {
|
||||
bool exists = true;
|
||||
bool deleted = false;
|
||||
bool has_label = false;
|
||||
Delta *delta = nullptr;
|
||||
@ -96,44 +97,47 @@ Result<bool> VertexAccessor::HasLabel(LabelId label, View view) const {
|
||||
label) != vertex_->labels.end();
|
||||
delta = vertex_->delta;
|
||||
}
|
||||
ApplyDeltasForRead(transaction_, delta, view,
|
||||
[&deleted, &has_label, label](const Delta &delta) {
|
||||
switch (delta.action) {
|
||||
case Delta::Action::REMOVE_LABEL: {
|
||||
if (delta.label == label) {
|
||||
CHECK(has_label) << "Invalid database state!";
|
||||
has_label = false;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case Delta::Action::ADD_LABEL: {
|
||||
if (delta.label == label) {
|
||||
CHECK(!has_label) << "Invalid database state!";
|
||||
has_label = true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case Delta::Action::DELETE_OBJECT: {
|
||||
LOG(FATAL) << "Invalid accessor!";
|
||||
break;
|
||||
}
|
||||
case Delta::Action::RECREATE_OBJECT: {
|
||||
deleted = false;
|
||||
break;
|
||||
}
|
||||
case Delta::Action::SET_PROPERTY:
|
||||
case Delta::Action::ADD_IN_EDGE:
|
||||
case Delta::Action::ADD_OUT_EDGE:
|
||||
case Delta::Action::REMOVE_IN_EDGE:
|
||||
case Delta::Action::REMOVE_OUT_EDGE:
|
||||
break;
|
||||
}
|
||||
});
|
||||
ApplyDeltasForRead(
|
||||
transaction_, delta, view,
|
||||
[&exists, &deleted, &has_label, label](const Delta &delta) {
|
||||
switch (delta.action) {
|
||||
case Delta::Action::REMOVE_LABEL: {
|
||||
if (delta.label == label) {
|
||||
CHECK(has_label) << "Invalid database state!";
|
||||
has_label = false;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case Delta::Action::ADD_LABEL: {
|
||||
if (delta.label == label) {
|
||||
CHECK(!has_label) << "Invalid database state!";
|
||||
has_label = true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case Delta::Action::DELETE_OBJECT: {
|
||||
exists = false;
|
||||
break;
|
||||
}
|
||||
case Delta::Action::RECREATE_OBJECT: {
|
||||
deleted = false;
|
||||
break;
|
||||
}
|
||||
case Delta::Action::SET_PROPERTY:
|
||||
case Delta::Action::ADD_IN_EDGE:
|
||||
case Delta::Action::ADD_OUT_EDGE:
|
||||
case Delta::Action::REMOVE_IN_EDGE:
|
||||
case Delta::Action::REMOVE_OUT_EDGE:
|
||||
break;
|
||||
}
|
||||
});
|
||||
if (!exists) return Error::NONEXISTENT_OBJECT;
|
||||
if (deleted) return Error::DELETED_OBJECT;
|
||||
return has_label;
|
||||
}
|
||||
|
||||
Result<std::vector<LabelId>> VertexAccessor::Labels(View view) const {
|
||||
bool exists = true;
|
||||
bool deleted = false;
|
||||
std::vector<LabelId> labels;
|
||||
Delta *delta = nullptr;
|
||||
@ -144,7 +148,8 @@ Result<std::vector<LabelId>> VertexAccessor::Labels(View view) const {
|
||||
delta = vertex_->delta;
|
||||
}
|
||||
ApplyDeltasForRead(
|
||||
transaction_, delta, view, [&deleted, &labels](const Delta &delta) {
|
||||
transaction_, delta, view,
|
||||
[&exists, &deleted, &labels](const Delta &delta) {
|
||||
switch (delta.action) {
|
||||
case Delta::Action::REMOVE_LABEL: {
|
||||
// Remove the label because we don't see the addition.
|
||||
@ -162,7 +167,7 @@ Result<std::vector<LabelId>> VertexAccessor::Labels(View view) const {
|
||||
break;
|
||||
}
|
||||
case Delta::Action::DELETE_OBJECT: {
|
||||
LOG(FATAL) << "Invalid accessor!";
|
||||
exists = false;
|
||||
break;
|
||||
}
|
||||
case Delta::Action::RECREATE_OBJECT: {
|
||||
@ -177,6 +182,7 @@ Result<std::vector<LabelId>> VertexAccessor::Labels(View view) const {
|
||||
break;
|
||||
}
|
||||
});
|
||||
if (!exists) return Error::NONEXISTENT_OBJECT;
|
||||
if (deleted) return Error::DELETED_OBJECT;
|
||||
return std::move(labels);
|
||||
}
|
||||
@ -244,6 +250,7 @@ Result<bool> VertexAccessor::ClearProperties() {
|
||||
|
||||
Result<PropertyValue> VertexAccessor::GetProperty(PropertyId property,
|
||||
View view) const {
|
||||
bool exists = true;
|
||||
bool deleted = false;
|
||||
PropertyValue value;
|
||||
Delta *delta = nullptr;
|
||||
@ -257,7 +264,7 @@ Result<PropertyValue> VertexAccessor::GetProperty(PropertyId property,
|
||||
delta = vertex_->delta;
|
||||
}
|
||||
ApplyDeltasForRead(transaction_, delta, view,
|
||||
[&deleted, &value, property](const Delta &delta) {
|
||||
[&exists, &deleted, &value, property](const Delta &delta) {
|
||||
switch (delta.action) {
|
||||
case Delta::Action::SET_PROPERTY: {
|
||||
if (delta.property.key == property) {
|
||||
@ -266,7 +273,7 @@ Result<PropertyValue> VertexAccessor::GetProperty(PropertyId property,
|
||||
break;
|
||||
}
|
||||
case Delta::Action::DELETE_OBJECT: {
|
||||
LOG(FATAL) << "Invalid accessor!";
|
||||
exists = false;
|
||||
break;
|
||||
}
|
||||
case Delta::Action::RECREATE_OBJECT: {
|
||||
@ -282,14 +289,16 @@ Result<PropertyValue> VertexAccessor::GetProperty(PropertyId property,
|
||||
break;
|
||||
}
|
||||
});
|
||||
if (!exists) return Error::NONEXISTENT_OBJECT;
|
||||
if (deleted) return Error::DELETED_OBJECT;
|
||||
return std::move(value);
|
||||
}
|
||||
|
||||
Result<std::map<PropertyId, PropertyValue>> VertexAccessor::Properties(
|
||||
View view) const {
|
||||
std::map<PropertyId, PropertyValue> properties;
|
||||
bool exists = true;
|
||||
bool deleted = false;
|
||||
std::map<PropertyId, PropertyValue> properties;
|
||||
Delta *delta = nullptr;
|
||||
{
|
||||
std::lock_guard<utils::SpinLock> guard(vertex_->lock);
|
||||
@ -297,51 +306,52 @@ Result<std::map<PropertyId, PropertyValue>> VertexAccessor::Properties(
|
||||
properties = vertex_->properties;
|
||||
delta = vertex_->delta;
|
||||
}
|
||||
ApplyDeltasForRead(
|
||||
transaction_, delta, view, [&deleted, &properties](const Delta &delta) {
|
||||
switch (delta.action) {
|
||||
case Delta::Action::SET_PROPERTY: {
|
||||
auto it = properties.find(delta.property.key);
|
||||
if (it != properties.end()) {
|
||||
if (delta.property.value.IsNull()) {
|
||||
// remove the property
|
||||
properties.erase(it);
|
||||
} else {
|
||||
// set the value
|
||||
it->second = delta.property.value;
|
||||
}
|
||||
} else if (!delta.property.value.IsNull()) {
|
||||
properties.emplace(delta.property.key, delta.property.value);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case Delta::Action::DELETE_OBJECT: {
|
||||
LOG(FATAL) << "Invalid accessor!";
|
||||
break;
|
||||
}
|
||||
case Delta::Action::RECREATE_OBJECT: {
|
||||
deleted = false;
|
||||
break;
|
||||
}
|
||||
case Delta::Action::ADD_LABEL:
|
||||
case Delta::Action::REMOVE_LABEL:
|
||||
case Delta::Action::ADD_IN_EDGE:
|
||||
case Delta::Action::ADD_OUT_EDGE:
|
||||
case Delta::Action::REMOVE_IN_EDGE:
|
||||
case Delta::Action::REMOVE_OUT_EDGE:
|
||||
break;
|
||||
}
|
||||
});
|
||||
if (deleted) {
|
||||
return Error::DELETED_OBJECT;
|
||||
}
|
||||
ApplyDeltasForRead(transaction_, delta, view,
|
||||
[&exists, &deleted, &properties](const Delta &delta) {
|
||||
switch (delta.action) {
|
||||
case Delta::Action::SET_PROPERTY: {
|
||||
auto it = properties.find(delta.property.key);
|
||||
if (it != properties.end()) {
|
||||
if (delta.property.value.IsNull()) {
|
||||
// remove the property
|
||||
properties.erase(it);
|
||||
} else {
|
||||
// set the value
|
||||
it->second = delta.property.value;
|
||||
}
|
||||
} else if (!delta.property.value.IsNull()) {
|
||||
properties.emplace(delta.property.key,
|
||||
delta.property.value);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case Delta::Action::DELETE_OBJECT: {
|
||||
exists = false;
|
||||
break;
|
||||
}
|
||||
case Delta::Action::RECREATE_OBJECT: {
|
||||
deleted = false;
|
||||
break;
|
||||
}
|
||||
case Delta::Action::ADD_LABEL:
|
||||
case Delta::Action::REMOVE_LABEL:
|
||||
case Delta::Action::ADD_IN_EDGE:
|
||||
case Delta::Action::ADD_OUT_EDGE:
|
||||
case Delta::Action::REMOVE_IN_EDGE:
|
||||
case Delta::Action::REMOVE_OUT_EDGE:
|
||||
break;
|
||||
}
|
||||
});
|
||||
if (!exists) return Error::NONEXISTENT_OBJECT;
|
||||
if (deleted) return Error::DELETED_OBJECT;
|
||||
return std::move(properties);
|
||||
}
|
||||
|
||||
Result<std::vector<EdgeAccessor>> VertexAccessor::InEdges(
|
||||
const std::vector<EdgeTypeId> &edge_types, View view) const {
|
||||
std::vector<std::tuple<EdgeTypeId, Vertex *, EdgeRef>> in_edges;
|
||||
bool exists = true;
|
||||
bool deleted = false;
|
||||
std::vector<std::tuple<EdgeTypeId, Vertex *, EdgeRef>> in_edges;
|
||||
Delta *delta = nullptr;
|
||||
{
|
||||
std::lock_guard<utils::SpinLock> guard(vertex_->lock);
|
||||
@ -350,7 +360,8 @@ Result<std::vector<EdgeAccessor>> VertexAccessor::InEdges(
|
||||
delta = vertex_->delta;
|
||||
}
|
||||
ApplyDeltasForRead(
|
||||
transaction_, delta, view, [&deleted, &in_edges](const Delta &delta) {
|
||||
transaction_, delta, view,
|
||||
[&exists, &deleted, &in_edges](const Delta &delta) {
|
||||
switch (delta.action) {
|
||||
case Delta::Action::ADD_IN_EDGE: {
|
||||
// Add the edge because we don't see the removal.
|
||||
@ -374,7 +385,7 @@ Result<std::vector<EdgeAccessor>> VertexAccessor::InEdges(
|
||||
break;
|
||||
}
|
||||
case Delta::Action::DELETE_OBJECT: {
|
||||
LOG(FATAL) << "Invalid accessor!";
|
||||
exists = false;
|
||||
break;
|
||||
}
|
||||
case Delta::Action::RECREATE_OBJECT: {
|
||||
@ -389,9 +400,8 @@ Result<std::vector<EdgeAccessor>> VertexAccessor::InEdges(
|
||||
break;
|
||||
}
|
||||
});
|
||||
if (deleted) {
|
||||
return Error::DELETED_OBJECT;
|
||||
}
|
||||
if (!exists) return Error::NONEXISTENT_OBJECT;
|
||||
if (deleted) return Error::DELETED_OBJECT;
|
||||
std::vector<EdgeAccessor> ret;
|
||||
ret.reserve(in_edges.size());
|
||||
for (const auto &item : in_edges) {
|
||||
@ -407,8 +417,9 @@ Result<std::vector<EdgeAccessor>> VertexAccessor::InEdges(
|
||||
|
||||
Result<std::vector<EdgeAccessor>> VertexAccessor::OutEdges(
|
||||
const std::vector<EdgeTypeId> &edge_types, View view) const {
|
||||
std::vector<std::tuple<EdgeTypeId, Vertex *, EdgeRef>> out_edges;
|
||||
bool exists = true;
|
||||
bool deleted = false;
|
||||
std::vector<std::tuple<EdgeTypeId, Vertex *, EdgeRef>> out_edges;
|
||||
Delta *delta = nullptr;
|
||||
{
|
||||
std::lock_guard<utils::SpinLock> guard(vertex_->lock);
|
||||
@ -417,7 +428,8 @@ Result<std::vector<EdgeAccessor>> VertexAccessor::OutEdges(
|
||||
delta = vertex_->delta;
|
||||
}
|
||||
ApplyDeltasForRead(
|
||||
transaction_, delta, view, [&deleted, &out_edges](const Delta &delta) {
|
||||
transaction_, delta, view,
|
||||
[&exists, &deleted, &out_edges](const Delta &delta) {
|
||||
switch (delta.action) {
|
||||
case Delta::Action::ADD_OUT_EDGE: {
|
||||
// Add the edge because we don't see the removal.
|
||||
@ -441,7 +453,7 @@ Result<std::vector<EdgeAccessor>> VertexAccessor::OutEdges(
|
||||
break;
|
||||
}
|
||||
case Delta::Action::DELETE_OBJECT: {
|
||||
LOG(FATAL) << "Invalid accessor!";
|
||||
exists = false;
|
||||
break;
|
||||
}
|
||||
case Delta::Action::RECREATE_OBJECT: {
|
||||
@ -456,9 +468,8 @@ Result<std::vector<EdgeAccessor>> VertexAccessor::OutEdges(
|
||||
break;
|
||||
}
|
||||
});
|
||||
if (deleted) {
|
||||
return Error::DELETED_OBJECT;
|
||||
}
|
||||
if (!exists) return Error::NONEXISTENT_OBJECT;
|
||||
if (deleted) return Error::DELETED_OBJECT;
|
||||
std::vector<EdgeAccessor> ret;
|
||||
ret.reserve(out_edges.size());
|
||||
for (const auto &item : out_edges) {
|
||||
@ -473,8 +484,9 @@ Result<std::vector<EdgeAccessor>> VertexAccessor::OutEdges(
|
||||
}
|
||||
|
||||
Result<size_t> VertexAccessor::InDegree(View view) const {
|
||||
size_t degree = 0;
|
||||
bool exists = true;
|
||||
bool deleted = false;
|
||||
size_t degree = 0;
|
||||
Delta *delta = nullptr;
|
||||
{
|
||||
std::lock_guard<utils::SpinLock> guard(vertex_->lock);
|
||||
@ -483,7 +495,7 @@ Result<size_t> VertexAccessor::InDegree(View view) const {
|
||||
delta = vertex_->delta;
|
||||
}
|
||||
ApplyDeltasForRead(transaction_, delta, view,
|
||||
[&deleted, °ree](const Delta &delta) {
|
||||
[&exists, &deleted, °ree](const Delta &delta) {
|
||||
switch (delta.action) {
|
||||
case Delta::Action::ADD_IN_EDGE:
|
||||
++degree;
|
||||
@ -492,7 +504,7 @@ Result<size_t> VertexAccessor::InDegree(View view) const {
|
||||
--degree;
|
||||
break;
|
||||
case Delta::Action::DELETE_OBJECT:
|
||||
LOG(FATAL) << "Invalid accessor!";
|
||||
exists = false;
|
||||
break;
|
||||
case Delta::Action::RECREATE_OBJECT:
|
||||
deleted = false;
|
||||
@ -505,13 +517,15 @@ Result<size_t> VertexAccessor::InDegree(View view) const {
|
||||
break;
|
||||
}
|
||||
});
|
||||
if (!exists) return Error::NONEXISTENT_OBJECT;
|
||||
if (deleted) return Error::DELETED_OBJECT;
|
||||
return degree;
|
||||
}
|
||||
|
||||
Result<size_t> VertexAccessor::OutDegree(View view) const {
|
||||
size_t degree = 0;
|
||||
bool exists = true;
|
||||
bool deleted = false;
|
||||
size_t degree = 0;
|
||||
Delta *delta = nullptr;
|
||||
{
|
||||
std::lock_guard<utils::SpinLock> guard(vertex_->lock);
|
||||
@ -520,7 +534,7 @@ Result<size_t> VertexAccessor::OutDegree(View view) const {
|
||||
delta = vertex_->delta;
|
||||
}
|
||||
ApplyDeltasForRead(transaction_, delta, view,
|
||||
[&deleted, °ree](const Delta &delta) {
|
||||
[&exists, &deleted, °ree](const Delta &delta) {
|
||||
switch (delta.action) {
|
||||
case Delta::Action::ADD_OUT_EDGE:
|
||||
++degree;
|
||||
@ -529,7 +543,7 @@ Result<size_t> VertexAccessor::OutDegree(View view) const {
|
||||
--degree;
|
||||
break;
|
||||
case Delta::Action::DELETE_OBJECT:
|
||||
LOG(FATAL) << "Invalid accessor!";
|
||||
exists = false;
|
||||
break;
|
||||
case Delta::Action::RECREATE_OBJECT:
|
||||
deleted = false;
|
||||
@ -542,6 +556,7 @@ Result<size_t> VertexAccessor::OutDegree(View view) const {
|
||||
break;
|
||||
}
|
||||
});
|
||||
if (!exists) return Error::NONEXISTENT_OBJECT;
|
||||
if (deleted) return Error::DELETED_OBJECT;
|
||||
return degree;
|
||||
}
|
||||
|
@ -2256,3 +2256,80 @@ TEST(StorageV2, VertexPropertyClear) {
|
||||
acc.Abort();
|
||||
}
|
||||
}
|
||||
|
||||
TEST(StorageV2, VertexNonexistentLabelPropertyEdgeAPI) {
|
||||
storage::Storage store;
|
||||
|
||||
auto label = store.NameToLabel("label");
|
||||
auto property = store.NameToProperty("property");
|
||||
|
||||
auto acc = store.Access();
|
||||
auto vertex = acc.CreateVertex();
|
||||
|
||||
// Check state before (OLD view).
|
||||
ASSERT_EQ(vertex.Labels(storage::View::OLD).GetError(),
|
||||
storage::Error::NONEXISTENT_OBJECT);
|
||||
ASSERT_EQ(vertex.HasLabel(label, storage::View::OLD).GetError(),
|
||||
storage::Error::NONEXISTENT_OBJECT);
|
||||
ASSERT_EQ(vertex.Properties(storage::View::OLD).GetError(),
|
||||
storage::Error::NONEXISTENT_OBJECT);
|
||||
ASSERT_EQ(vertex.GetProperty(property, storage::View::OLD).GetError(),
|
||||
storage::Error::NONEXISTENT_OBJECT);
|
||||
ASSERT_EQ(vertex.InEdges({}, storage::View::OLD).GetError(),
|
||||
storage::Error::NONEXISTENT_OBJECT);
|
||||
ASSERT_EQ(vertex.OutEdges({}, storage::View::OLD).GetError(),
|
||||
storage::Error::NONEXISTENT_OBJECT);
|
||||
ASSERT_EQ(vertex.InDegree(storage::View::OLD).GetError(),
|
||||
storage::Error::NONEXISTENT_OBJECT);
|
||||
ASSERT_EQ(vertex.OutDegree(storage::View::OLD).GetError(),
|
||||
storage::Error::NONEXISTENT_OBJECT);
|
||||
|
||||
// Check state before (NEW view).
|
||||
ASSERT_EQ(vertex.Labels(storage::View::NEW)->size(), 0);
|
||||
ASSERT_EQ(*vertex.HasLabel(label, storage::View::NEW), false);
|
||||
ASSERT_EQ(vertex.Properties(storage::View::NEW)->size(), 0);
|
||||
ASSERT_EQ(*vertex.GetProperty(property, storage::View::NEW),
|
||||
storage::PropertyValue());
|
||||
ASSERT_EQ(vertex.InEdges({}, storage::View::NEW)->size(), 0);
|
||||
ASSERT_EQ(vertex.OutEdges({}, storage::View::NEW)->size(), 0);
|
||||
ASSERT_EQ(*vertex.InDegree(storage::View::NEW), 0);
|
||||
ASSERT_EQ(*vertex.OutDegree(storage::View::NEW), 0);
|
||||
|
||||
// Modify vertex.
|
||||
ASSERT_TRUE(vertex.AddLabel(label).HasValue());
|
||||
ASSERT_TRUE(
|
||||
vertex.SetProperty(property, storage::PropertyValue("value")).HasValue());
|
||||
ASSERT_TRUE(
|
||||
acc.CreateEdge(&vertex, &vertex, acc.NameToEdgeType("edge")).HasValue());
|
||||
|
||||
// Check state after (OLD view).
|
||||
ASSERT_EQ(vertex.Labels(storage::View::OLD).GetError(),
|
||||
storage::Error::NONEXISTENT_OBJECT);
|
||||
ASSERT_EQ(vertex.HasLabel(label, storage::View::OLD).GetError(),
|
||||
storage::Error::NONEXISTENT_OBJECT);
|
||||
ASSERT_EQ(vertex.Properties(storage::View::OLD).GetError(),
|
||||
storage::Error::NONEXISTENT_OBJECT);
|
||||
ASSERT_EQ(vertex.GetProperty(property, storage::View::OLD).GetError(),
|
||||
storage::Error::NONEXISTENT_OBJECT);
|
||||
ASSERT_EQ(vertex.InEdges({}, storage::View::OLD).GetError(),
|
||||
storage::Error::NONEXISTENT_OBJECT);
|
||||
ASSERT_EQ(vertex.OutEdges({}, storage::View::OLD).GetError(),
|
||||
storage::Error::NONEXISTENT_OBJECT);
|
||||
ASSERT_EQ(vertex.InDegree(storage::View::OLD).GetError(),
|
||||
storage::Error::NONEXISTENT_OBJECT);
|
||||
ASSERT_EQ(vertex.OutDegree(storage::View::OLD).GetError(),
|
||||
storage::Error::NONEXISTENT_OBJECT);
|
||||
|
||||
// Check state after (NEW view).
|
||||
ASSERT_EQ(vertex.Labels(storage::View::NEW)->size(), 1);
|
||||
ASSERT_EQ(*vertex.HasLabel(label, storage::View::NEW), true);
|
||||
ASSERT_EQ(vertex.Properties(storage::View::NEW)->size(), 1);
|
||||
ASSERT_EQ(*vertex.GetProperty(property, storage::View::NEW),
|
||||
storage::PropertyValue("value"));
|
||||
ASSERT_EQ(vertex.InEdges({}, storage::View::NEW)->size(), 1);
|
||||
ASSERT_EQ(vertex.OutEdges({}, storage::View::NEW)->size(), 1);
|
||||
ASSERT_EQ(*vertex.InDegree(storage::View::NEW), 1);
|
||||
ASSERT_EQ(*vertex.OutDegree(storage::View::NEW), 1);
|
||||
|
||||
ASSERT_FALSE(acc.Commit().HasError());
|
||||
}
|
||||
|
@ -5325,3 +5325,42 @@ TEST(StorageWithoutProperties, EdgePropertyClear) {
|
||||
acc.Abort();
|
||||
}
|
||||
}
|
||||
|
||||
TEST(StorageWithProperties, EdgeNonexistentPropertyAPI) {
|
||||
storage::Storage store({.items = {.properties_on_edges = true}});
|
||||
|
||||
auto property = store.NameToProperty("property");
|
||||
|
||||
auto acc = store.Access();
|
||||
auto vertex = acc.CreateVertex();
|
||||
auto edge = acc.CreateEdge(&vertex, &vertex, acc.NameToEdgeType("edge"));
|
||||
ASSERT_TRUE(edge.HasValue());
|
||||
|
||||
// Check state before (OLD view).
|
||||
ASSERT_EQ(edge->Properties(storage::View::OLD).GetError(),
|
||||
storage::Error::NONEXISTENT_OBJECT);
|
||||
ASSERT_EQ(edge->GetProperty(property, storage::View::OLD).GetError(),
|
||||
storage::Error::NONEXISTENT_OBJECT);
|
||||
|
||||
// Check state before (NEW view).
|
||||
ASSERT_EQ(edge->Properties(storage::View::NEW)->size(), 0);
|
||||
ASSERT_EQ(*edge->GetProperty(property, storage::View::NEW),
|
||||
storage::PropertyValue());
|
||||
|
||||
// Modify edge.
|
||||
ASSERT_TRUE(
|
||||
edge->SetProperty(property, storage::PropertyValue("value")).HasValue());
|
||||
|
||||
// Check state after (OLD view).
|
||||
ASSERT_EQ(edge->Properties(storage::View::OLD).GetError(),
|
||||
storage::Error::NONEXISTENT_OBJECT);
|
||||
ASSERT_EQ(edge->GetProperty(property, storage::View::OLD).GetError(),
|
||||
storage::Error::NONEXISTENT_OBJECT);
|
||||
|
||||
// Check state after (NEW view).
|
||||
ASSERT_EQ(edge->Properties(storage::View::NEW)->size(), 1);
|
||||
ASSERT_EQ(*edge->GetProperty(property, storage::View::NEW),
|
||||
storage::PropertyValue("value"));
|
||||
|
||||
ASSERT_FALSE(acc.Commit().HasError());
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user