From 85b01f84973298f50c5e03de4f47d9933838b250 Mon Sep 17 00:00:00 2001 From: Teon Banek Date: Fri, 6 Sep 2019 15:29:26 +0200 Subject: [PATCH] Add query/db_accessor.hpp which wraps storage implementation Reviewers: mferencevic, ipaljak Reviewed By: mferencevic Subscribers: pullbot Differential Revision: https://phabricator.memgraph.io/D2361 --- src/query/db_accessor.hpp | 910 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 910 insertions(+) create mode 100644 src/query/db_accessor.hpp diff --git a/src/query/db_accessor.hpp b/src/query/db_accessor.hpp new file mode 100644 index 000000000..c3c2f6ddc --- /dev/null +++ b/src/query/db_accessor.hpp @@ -0,0 +1,910 @@ +#pragma once + +#include + +#include +#include + +#include "query/exceptions.hpp" +#include "storage/common/types/property_value.hpp" +#include "storage/common/types/types.hpp" +#include "storage/edge_accessor.hpp" +#include "storage/v2/result.hpp" +#include "storage/v2/view.hpp" +#include "storage/vertex_accessor.hpp" +#include "utils/bound.hpp" +#include "utils/exceptions.hpp" + +#ifdef MG_SINGLE_NODE_V2 +#include "storage/v2/storage.hpp" +#else +#include "database/graph_db_accessor.hpp" +#endif + +namespace query { + +class VertexAccessor; + +#ifdef MG_SINGLE_NODE_V2 +class EdgeAccessor final { + public: + storage::EdgeAccessor impl_; + + public: + explicit EdgeAccessor(storage::EdgeAccessor impl) : impl_(std::move(impl)) {} + + storage::EdgeType EdgeType() const { return impl_.EdgeType(); } + + auto Properties(storage::View view) const { return impl_.Properties(view); } + + storage::Result GetProperty(storage::View view, + storage::Property key) const { + return impl_.GetProperty(key, view); + } + + storage::Result SetProperty(storage::Property key, + const PropertyValue &value) { + return impl_.SetProperty(key, value); + } + + storage::Result RemoveProperty(storage::Property key) { + return SetProperty(key, PropertyValue()); + } + + utils::BasicResult ClearProperties() { + throw utils::NotYetImplemented("ClearProperties"); + } + + VertexAccessor To() const; + + VertexAccessor From() const; + + bool IsCycle() const; + + int64_t CypherId() const { return impl_.Gid().AsInt(); } + + auto Gid() const { return impl_.Gid(); } + + bool operator==(const EdgeAccessor &e) const { return impl_ == e.impl_; } + + bool operator!=(const EdgeAccessor &e) const { return !(*this == e); } + + friend std::ostream &operator<<(std::ostream &s, const EdgeAccessor &e) { + throw utils::NotYetImplemented("operator<<"); + } +}; + +class VertexAccessor final { + public: + storage::VertexAccessor impl_; + + static EdgeAccessor MakeEdgeAccessor(const storage::EdgeAccessor impl) { + return EdgeAccessor(impl); + } + + public: + explicit VertexAccessor(storage::VertexAccessor impl) + : impl_(std::move(impl)) {} + + auto Labels(storage::View view) const { return impl_.Labels(view); } + + storage::Result AddLabel(storage::Label label) { + return impl_.AddLabel(label); + } + + storage::Result RemoveLabel(storage::Label label) { + return impl_.RemoveLabel(label); + } + + storage::Result HasLabel(storage::View view, + storage::Label label) const { + return impl_.HasLabel(label, view); + } + + auto Properties(storage::View view) const { return impl_.Properties(view); } + + storage::Result GetProperty(storage::View view, + storage::Property key) const { + return impl_.GetProperty(key, view); + } + + storage::Result SetProperty(storage::Property key, + const PropertyValue &value) { + return impl_.SetProperty(key, value); + } + + storage::Result RemoveProperty(storage::Property key) { + return SetProperty(key, PropertyValue()); + } + + utils::BasicResult ClearProperties() { + throw utils::NotYetImplemented("ClearProperties"); + } + + auto InEdges(storage::View view, + const std::vector &edge_types) const + -> storage::Result { + auto maybe_edges = impl_.InEdges(edge_types, view); + if (maybe_edges.HasError()) return maybe_edges.GetError(); + return iter::imap(MakeEdgeAccessor, std::move(*maybe_edges)); + } + + auto InEdges(storage::View view) const { return InEdges(view, {}); } + + auto InEdges(storage::View view, + const std::vector &edge_types, + const VertexAccessor &dest) const + -> storage::Result { + throw utils::NotYetImplemented("InEdges with set destination"); + } + + auto OutEdges(storage::View view, + const std::vector &edge_types) const + -> storage::Result { + auto maybe_edges = impl_.OutEdges(edge_types, view); + if (maybe_edges.HasError()) return maybe_edges.GetError(); + return iter::imap(MakeEdgeAccessor, std::move(*maybe_edges)); + } + + auto OutEdges(storage::View view) const { return OutEdges(view, {}); } + + auto OutEdges(storage::View view, + const std::vector &edge_types, + const VertexAccessor &dest) const + -> storage::Result { + throw utils::NotYetImplemented("OutEdges with set destination"); + } + + storage::Result InDegree(storage::View view) const { + return impl_.InDegree(view); + } + + storage::Result OutDegree(storage::View view) const { + return impl_.OutDegree(view); + } + + int64_t CypherId() const { return impl_.Gid().AsInt(); } + + auto Gid() const { return impl_.Gid(); } + + bool operator==(const VertexAccessor &v) const { return impl_ == v.impl_; } + + bool operator!=(const VertexAccessor &v) const { return !(*this == v); } + + friend std::ostream &operator<<(std::ostream &s, const VertexAccessor &v) { + throw utils::NotYetImplemented("operator<<"); + } +}; + +inline VertexAccessor EdgeAccessor::To() const { + return VertexAccessor(impl_.ToVertex()); +} + +inline VertexAccessor EdgeAccessor::From() const { + return VertexAccessor(impl_.FromVertex()); +} + +inline bool EdgeAccessor::IsCycle() const { return To() == From(); } +#else + +class EdgeAccessor final { + public: + ::EdgeAccessor impl_; + + private: + void SwitchToView(storage::View view) const { + if (!const_cast<::EdgeAccessor &>(impl_).Reconstruct()) + throw ReconstructionException(); + switch (view) { + case storage::View::OLD: + const_cast<::EdgeAccessor &>(impl_).SwitchOld(); + break; + case storage::View::NEW: + const_cast<::EdgeAccessor &>(impl_).SwitchNew(); + break; + } + } + + public: + explicit EdgeAccessor(::EdgeAccessor impl) : impl_(impl) {} + + storage::EdgeType EdgeType() const { return impl_.EdgeType(); } + + storage::Result Properties(storage::View view) const { + SwitchToView(view); + return impl_.Properties(); + } + + storage::Result GetProperty(storage::View view, + storage::Property key) const { + SwitchToView(view); + return impl_.PropsAt(key); + } + + storage::Result SetProperty(storage::Property key, + const PropertyValue &value) { + SwitchToView(storage::View::NEW); + try { + impl_.PropsSet(key, value); + return true; + } catch (const RecordDeletedError &) { + return storage::Error::DELETED_OBJECT; + } catch (const mvcc::SerializationError &) { + return storage::Error::SERIALIZATION_ERROR; + } catch (const database::ConstraintViolationException &e) { + throw QueryRuntimeException(e.what()); + } + } + + storage::Result RemoveProperty(storage::Property key) { + SwitchToView(storage::View::NEW); + try { + impl_.PropsErase(key); + return true; + } catch (const RecordDeletedError &) { + return storage::Error::DELETED_OBJECT; + } catch (const mvcc::SerializationError &) { + return storage::Error::SERIALIZATION_ERROR; + } catch (const database::ConstraintViolationException &e) { + throw QueryRuntimeException(e.what()); + } + } + + utils::BasicResult ClearProperties() { + SwitchToView(storage::View::NEW); + try { + impl_.PropsClear(); + return {}; + } catch (const RecordDeletedError &) { + return storage::Error::DELETED_OBJECT; + } catch (const mvcc::SerializationError &) { + return storage::Error::SERIALIZATION_ERROR; + } catch (const database::ConstraintViolationException &e) { + throw QueryRuntimeException(e.what()); + } + } + + VertexAccessor To() const; + + VertexAccessor From() const; + + bool IsCycle() const; + + int64_t CypherId() const { return impl_.CypherId(); } + + auto Gid() const { return impl_.gid(); } + + bool operator==(const EdgeAccessor &e) const { return impl_ == e.impl_; } + + bool operator!=(const EdgeAccessor &e) const { return !(*this == e); } + + friend std::ostream &operator<<(std::ostream &s, const EdgeAccessor &e) { + return s << e.impl_; + } +}; + +class VertexAccessor final { + public: + ::VertexAccessor impl_; + + private: + void SwitchToView(storage::View view) const { + if (!const_cast<::VertexAccessor &>(impl_).Reconstruct()) + throw ReconstructionException(); + switch (view) { + case storage::View::OLD: + const_cast<::VertexAccessor &>(impl_).SwitchOld(); + break; + case storage::View::NEW: + const_cast<::VertexAccessor &>(impl_).SwitchNew(); + break; + } + } + + static EdgeAccessor MakeEdgeAccessor(const ::EdgeAccessor impl) { + return EdgeAccessor(impl); + } + + public: + explicit VertexAccessor(::VertexAccessor impl) : impl_(impl) {} + + storage::Result> Labels( + storage::View view) const { + SwitchToView(view); + return impl_.labels(); + } + + storage::Result AddLabel(storage::Label label) { + SwitchToView(storage::View::NEW); + try { + impl_.add_label(label); + return true; + } catch (const RecordDeletedError &) { + return storage::Error::DELETED_OBJECT; + } catch (const mvcc::SerializationError &) { + return storage::Error::SERIALIZATION_ERROR; + } catch (const database::ConstraintViolationException &e) { + throw QueryRuntimeException(e.what()); + } + } + + storage::Result RemoveLabel(storage::Label label) { + SwitchToView(storage::View::NEW); + try { + impl_.remove_label(label); + return true; + } catch (const RecordDeletedError &) { + return storage::Error::DELETED_OBJECT; + } catch (const mvcc::SerializationError &) { + return storage::Error::SERIALIZATION_ERROR; + } + } + + storage::Result HasLabel(storage::View view, + storage::Label label) const { + SwitchToView(view); + return impl_.has_label(label); + } + + storage::Result Properties(storage::View view) const { + SwitchToView(view); + return impl_.Properties(); + } + + storage::Result GetProperty(storage::View view, + storage::Property key) const { + SwitchToView(view); + return impl_.PropsAt(key); + } + + storage::Result SetProperty(storage::Property key, + const PropertyValue &value) { + SwitchToView(storage::View::NEW); + try { + impl_.PropsSet(key, value); + return true; + } catch (const RecordDeletedError &) { + return storage::Error::DELETED_OBJECT; + } catch (const mvcc::SerializationError &) { + return storage::Error::SERIALIZATION_ERROR; + } catch (const database::ConstraintViolationException &e) { + throw QueryRuntimeException(e.what()); + } + } + + storage::Result RemoveProperty(storage::Property key) { + SwitchToView(storage::View::NEW); + try { + impl_.PropsErase(key); + return true; + } catch (const RecordDeletedError &) { + return storage::Error::DELETED_OBJECT; + } catch (const mvcc::SerializationError &) { + return storage::Error::SERIALIZATION_ERROR; + } catch (const database::ConstraintViolationException &e) { + throw QueryRuntimeException(e.what()); + } + } + + utils::BasicResult ClearProperties() { + SwitchToView(storage::View::NEW); + try { + impl_.PropsClear(); + return {}; + } catch (const RecordDeletedError &) { + return storage::Error::DELETED_OBJECT; + } catch (const mvcc::SerializationError &) { + return storage::Error::SERIALIZATION_ERROR; + } catch (const database::ConstraintViolationException &e) { + throw QueryRuntimeException(e.what()); + } + } + + auto InEdges(storage::View view) const + -> storage::Result { + SwitchToView(view); + return iter::imap(MakeEdgeAccessor, impl_.in()); + } + + auto InEdges(storage::View view, + const std::vector &edge_types) const + -> storage::Result { + SwitchToView(view); + return iter::imap(MakeEdgeAccessor, impl_.in(&edge_types)); + } + + auto InEdges(storage::View view, + const std::vector &edge_types, + const VertexAccessor &dest) const + -> storage::Result { + SwitchToView(view); + return iter::imap(MakeEdgeAccessor, impl_.in(dest.impl_, &edge_types)); + } + + auto OutEdges(storage::View view) const + -> storage::Result { + SwitchToView(view); + return iter::imap(MakeEdgeAccessor, impl_.out()); + } + + auto OutEdges(storage::View view, + const std::vector &edge_types) const + -> storage::Result { + SwitchToView(view); + return iter::imap(MakeEdgeAccessor, impl_.out(&edge_types)); + } + + auto OutEdges(storage::View view, + const std::vector &edge_types, + const VertexAccessor &dest) const + -> storage::Result { + SwitchToView(view); + return iter::imap(MakeEdgeAccessor, impl_.out(dest.impl_, &edge_types)); + } + + storage::Result InDegree(storage::View view) const { + SwitchToView(view); + return impl_.in_degree(); + } + + storage::Result OutDegree(storage::View view) const { + SwitchToView(view); + return impl_.out_degree(); + } + + int64_t CypherId() const { return impl_.CypherId(); } + + auto Gid() const { return impl_.gid(); } + + bool operator==(const VertexAccessor &v) const { return impl_ == v.impl_; } + + bool operator!=(const VertexAccessor &v) const { return !(*this == v); } + + friend std::ostream &operator<<(std::ostream &s, const VertexAccessor &v) { + return s << v.impl_; + } +}; + +inline VertexAccessor EdgeAccessor::To() const { + return VertexAccessor(impl_.to()); +} + +inline VertexAccessor EdgeAccessor::From() const { + return VertexAccessor(impl_.from()); +} + +inline bool EdgeAccessor::IsCycle() const { return To() == From(); } +#endif + +#ifdef MG_SINGLE_NODE_V2 +class DbAccessor final { + storage::Storage::Accessor *accessor_; + + class VerticesIterable final { + storage::VerticesIterable iterable_; + + public: + class Iterator final { + storage::VerticesIterable::Iterator it_; + + public: + explicit Iterator(storage::VerticesIterable::Iterator it) : it_(it) {} + + VertexAccessor operator*() const { return VertexAccessor(*it_); } + + Iterator &operator++() { + ++it_; + return *this; + } + + bool operator==(const Iterator &other) const { return it_ == other.it_; } + + bool operator!=(const Iterator &other) const { return !(other == *this); } + }; + + explicit VerticesIterable(storage::VerticesIterable iterable) + : iterable_(std::move(iterable)) {} + + Iterator begin() { return Iterator(iterable_.begin()); } + + Iterator end() { return Iterator(iterable_.end()); } + }; + + class EdgesIterable final { + public: + class Iterator final { + public: + EdgeAccessor operator*() const { + throw utils::NotYetImplemented("operator*"); + } + + Iterator &operator++() { throw utils::NotYetImplemented("operator++"); } + + bool operator==(const Iterator &other) const { + throw utils::NotYetImplemented("operator=="); + } + + bool operator!=(const Iterator &other) const { return !(other == *this); } + }; + + Iterator begin() { throw utils::NotYetImplemented("begin"); } + Iterator end() { throw utils::NotYetImplemented("end"); } + }; + + public: + explicit DbAccessor(storage::Storage::Accessor *accessor) + : accessor_(accessor) {} + + VerticesIterable Vertices(storage::View view) { + return VerticesIterable(accessor_->Vertices(view)); + } + + VerticesIterable Vertices(storage::View view, storage::Label label) { + return VerticesIterable(accessor_->Vertices(label, view)); + } + + VerticesIterable Vertices(storage::View view, storage::Label label, + storage::Property property, + const PropertyValue &value) { + return VerticesIterable(accessor_->Vertices(label, property, value, view)); + } + + VerticesIterable Vertices( + storage::View view, storage::Label label, storage::Property property, + const std::optional> &lower, + const std::optional> &upper) { + return VerticesIterable( + accessor_->Vertices(label, property, lower, upper, view)); + } + + EdgesIterable Edges(storage::View view) { + throw utils::NotYetImplemented("Edges"); + } + + VertexAccessor InsertVertex() { + return VertexAccessor(accessor_->CreateVertex()); + } + + storage::Result InsertEdge(VertexAccessor *from, + VertexAccessor *to, + const storage::EdgeType &edge_type) { + auto maybe_edge = + accessor_->CreateEdge(&from->impl_, &to->impl_, edge_type); + if (maybe_edge.HasError()) + return storage::Result(maybe_edge.GetError()); + return EdgeAccessor(std::move(*maybe_edge)); + } + + storage::Result RemoveEdge(EdgeAccessor *edge) { + return accessor_->DeleteEdge(&edge->impl_); + } + + storage::Result DetachRemoveVertex(VertexAccessor *vertex_accessor) { + return accessor_->DetachDeleteVertex(&vertex_accessor->impl_); + } + + storage::Result RemoveVertex(VertexAccessor *vertex_accessor) { + return accessor_->DeleteVertex(&vertex_accessor->impl_); + } + + storage::Property NameToProperty(const std::string_view &name) { + // TODO: New storage should work with string_view to avoid needless + // allocation. + return accessor_->NameToProperty(std::string(name)); + } + + storage::Label NameToLabel(const std::string_view &name) { + return accessor_->NameToLabel(std::string(name)); + } + + storage::EdgeType NameToEdgeType(const std::string_view &name) { + return accessor_->NameToEdgeType(std::string(name)); + } + + const std::string &PropertyToName(storage::Property prop) const { + return accessor_->PropertyToName(prop); + } + + const std::string &LabelToName(storage::Label label) const { + return accessor_->LabelToName(label); + } + + const std::string &EdgeTypeToName(storage::EdgeType type) const { + return accessor_->EdgeTypeToName(type); + } + + void AdvanceCommand() { accessor_->AdvanceCommand(); } + + bool MustAbort() const { return false; } + + bool CreateIndex(storage::Label label, storage::Property prop) { + return accessor_->CreateIndex(label, prop); + } + + bool DropIndex(storage::Label label, storage::Property prop) { + return accessor_->DropIndex(label, prop); + } + + // TODO: These should probably be in some kind of StorageInfo class instead of + // here. + bool LabelPropertyIndexExists(storage::Label label, + storage::Property prop) const { + return accessor_->LabelPropertyIndexExists(label, prop); + } + + int64_t VerticesCount() const { return accessor_->ApproximateVertexCount(); } + + int64_t VerticesCount(storage::Label label) const { + return accessor_->ApproximateVertexCount(label); + } + + int64_t VerticesCount(storage::Label label, + storage::Property property) const { + return accessor_->ApproximateVertexCount(label, property); + } + + int64_t VerticesCount(storage::Label label, storage::Property property, + const PropertyValue &value) const { + return accessor_->ApproximateVertexCount(label, property, value); + } + + int64_t VerticesCount( + storage::Label label, storage::Property property, + const std::optional> &lower, + const std::optional> &upper) const { + return accessor_->ApproximateVertexCount(label, property, lower, upper); + } + + auto CreateExistenceConstraint(storage::Label label, + storage::Property property) { + return accessor_->CreateExistenceConstraint(label, property); + } + + auto DropExistenceConstraint(storage::Label label, + storage::Property property) { + return accessor_->DropExistenceConstraint(label, property); + } +}; +#else +class DbAccessor final { + database::GraphDbAccessor *dba_; + + template + class VerticesIterable final { + TVertices vertices_; + + class Iterator final { + decltype(vertices_.begin()) it_; + + public: + explicit Iterator(decltype(it_) it) : it_(std::move(it)) {} + + VertexAccessor operator*() { return VertexAccessor(*it_); } + + Iterator &operator++() { + ++it_; + return *this; + } + + bool operator==(const Iterator &other) const { return other.it_ == it_; } + + bool operator!=(const Iterator &other) const { return !(other == *this); } + }; + + public: + explicit VerticesIterable(TVertices vertices) + : vertices_(std::move(vertices)) {} + + Iterator begin() { return Iterator(vertices_.begin()); } + + Iterator end() { return Iterator(vertices_.end()); } + }; + + public: + explicit DbAccessor(database::GraphDbAccessor *dba) : dba_(dba) {} + + VertexAccessor InsertVertex() { return VertexAccessor(dba_->InsertVertex()); } + + storage::Result InsertEdge(VertexAccessor *from, + VertexAccessor *to, + const storage::EdgeType &edge_type) { + try { + return EdgeAccessor(dba_->InsertEdge(from->impl_, to->impl_, edge_type)); + } catch (const mvcc::SerializationError &) { + return storage::Error::SERIALIZATION_ERROR; + } catch (const RecordDeletedError &) { + return storage::Error::DELETED_OBJECT; + } + } + + storage::Result RemoveEdge(EdgeAccessor *edge) { + try { + dba_->RemoveEdge(edge->impl_); + return true; + } catch (const mvcc::SerializationError &) { + return storage::Error::SERIALIZATION_ERROR; + } + } + + storage::Result DetachRemoveVertex(VertexAccessor *vertex_accessor) { + try { + dba_->DetachRemoveVertex(vertex_accessor->impl_); + return true; + } catch (const mvcc::SerializationError &) { + return storage::Error::SERIALIZATION_ERROR; + } + } + + storage::Result RemoveVertex(VertexAccessor *vertex_accessor) { + try { + if (!dba_->RemoveVertex(vertex_accessor->impl_)) + return storage::Error::VERTEX_HAS_EDGES; + return true; + } catch (const mvcc::SerializationError &) { + return storage::Error::SERIALIZATION_ERROR; + } + } + + auto Vertices(storage::View view) { + auto vertices = dba_->Vertices(view == storage::View::NEW); + return VerticesIterable(std::move(vertices)); + } + + auto Vertices(storage::View view, storage::Label label) { + auto vertices = dba_->Vertices(label, view == storage::View::NEW); + return VerticesIterable(std::move(vertices)); + } + + auto Vertices(storage::View view, storage::Label label, + storage::Property property, const PropertyValue &value) { + auto vertices = + dba_->Vertices(label, property, value, view == storage::View::NEW); + return VerticesIterable(std::move(vertices)); + } + + auto Vertices(storage::View view, storage::Label label, + storage::Property property, + const std::optional> &lower, + const std::optional> &upper) { + auto vertices = dba_->Vertices(label, property, lower, upper, + view == storage::View::NEW); + return VerticesIterable(std::move(vertices)); + } + + auto Edges(storage::View view) { + // TODO: Exceptions? + return iter::imap([](const auto &e) { return EdgeAccessor(e); }, + dba_->Edges(view == storage::View::NEW)); + } + + storage::Property NameToProperty(const std::string_view &name) { + return dba_->Property(std::string(name)); + } + + storage::Label NameToLabel(const std::string_view &name) { + return dba_->Label(std::string(name)); + } + + storage::EdgeType NameToEdgeType(const std::string_view &name) { + return dba_->EdgeType(std::string(name)); + } + + const std::string &PropertyToName(storage::Property prop) const { + return dba_->PropertyName(prop); + } + + const std::string &LabelToName(storage::Label label) const { + return dba_->LabelName(label); + } + + const std::string &EdgeTypeToName(storage::EdgeType type) const { + return dba_->EdgeTypeName(type); + } + + void AdvanceCommand() { dba_->AdvanceCommand(); } + + bool MustAbort() const { return dba_->should_abort(); } + + bool CreateIndex(storage::Label label, storage::Property prop) { + try { + dba_->BuildIndex(label, prop); + return true; + } catch (const database::IndexExistsException &) { + return false; + } catch (const database::TransactionException &) { + // TODO: What do we do with this? This cannot happen in v2 + throw; + } + } + + bool DropIndex(storage::Label label, storage::Property prop) { + try { + dba_->DeleteIndex(label, prop); + return true; + } catch (const database::TransactionException &) { + // TODO: What do we do with this? This cannot happen in v2 + throw; + } + } + + bool LabelPropertyIndexExists(storage::Label label, + storage::Property prop) const { + return dba_->LabelPropertyIndexExists(label, prop); + } + + int64_t VerticesCount() const { return dba_->VerticesCount(); } + + int64_t VerticesCount(storage::Label label) const { + return dba_->VerticesCount(label); + } + + int64_t VerticesCount(storage::Label label, + storage::Property property) const { + return dba_->VerticesCount(label, property); + } + + int64_t VerticesCount(storage::Label label, storage::Property property, + const PropertyValue &value) const { + return dba_->VerticesCount(label, property, value); + } + + int64_t VerticesCount( + storage::Label label, storage::Property property, + const std::optional> &lower, + const std::optional> &upper) const { + return dba_->VerticesCount(label, property, lower, upper); + } + + auto StorageInfo() const { return dba_->StorageInfo(); } + + auto IndexInfo() const { return dba_->IndexInfo(); } + + auto GetIndicesKeys() const { return dba_->GetIndicesKeys(); } + + auto ListUniqueConstraints() const { return dba_->ListUniqueConstraints(); } + + void BuildUniqueConstraint(storage::Label label, + const std::vector &properties) { + // TODO: Exceptions? + return dba_->BuildUniqueConstraint(label, properties); + } + + void DeleteUniqueConstraint( + storage::Label label, const std::vector &properties) { + // TODO: Exceptions? + return dba_->DeleteUniqueConstraint(label, properties); + } + +#ifdef MG_SINGLE_NODE_HA + auto raft() { return dba_->raft(); } +#endif +}; +#endif + +} // namespace query + +namespace std { + +template <> +struct hash { + size_t operator()(const query::VertexAccessor &v) const { + return std::hash{}(v.impl_); + } +}; + +template <> +struct hash { + size_t operator()(const query::EdgeAccessor &e) const { + return std::hash{}(e.impl_); + } +}; + +} // namespace std