From 7bd45f87144c9a2a07b3ddac856732a6177a6797 Mon Sep 17 00:00:00 2001
From: Teon Banek <teon.banek@memgraph.io>
Date: Wed, 11 Sep 2019 16:10:53 +0200
Subject: [PATCH] Make query execution work with storage_v2

Reviewers: mferencevic, ipaljak

Reviewed By: mferencevic

Subscribers: pullbot

Differential Revision: https://phabricator.memgraph.io/D2203
---
 src/CMakeLists.txt                            |  53 ++
 src/database/single_node/dump.cpp             | 101 +++-
 src/database/single_node/dump.hpp             |  16 +-
 src/glue/communication.cpp                    |  24 +
 src/memgraph.cpp                              |  10 +
 src/memgraph_init.cpp                         |  59 +-
 src/memgraph_init.hpp                         |  18 +
 src/query/common.cpp                          |  48 --
 src/query/common.hpp                          |  31 +-
 src/query/context.hpp                         |  14 +-
 src/query/db_accessor.hpp                     |  22 +-
 .../interpret/awesome_memgraph_functions.cpp  |  88 ++-
 .../interpret/awesome_memgraph_functions.hpp  |   9 +-
 src/query/interpret/eval.hpp                  | 144 +++--
 src/query/interpreter.cpp                     | 169 ++++--
 src/query/interpreter.hpp                     |  32 +-
 src/query/path.hpp                            |  17 +-
 src/query/plan/operator.cpp                   | 513 ++++++++++++------
 src/query/plan/operator.lcp                   |  11 +-
 src/query/plan/pretty_print.cpp               |  47 +-
 src/query/plan/pretty_print.hpp               |  41 +-
 src/query/plan/rewrite/index_lookup.hpp       |   6 +-
 src/query/plan/rule_based_planner.hpp         |   6 +-
 src/query/plan/vertex_count_cache.hpp         |  10 +-
 src/query/repl.cpp                            |   5 +-
 src/query/transaction_engine.hpp              |  37 +-
 src/query/typed_value.cpp                     |   4 +-
 src/query/typed_value.hpp                     |   4 +-
 src/storage/common/types/types.hpp            |  11 +
 src/storage/edge_accessor.hpp                 |   5 +
 src/storage/single_node/record_accessor.hpp   |   2 +
 src/storage/v2/edge_accessor.hpp              |   2 +
 src/storage/v2/storage.cpp                    |   5 +
 src/storage/vertex_accessor.hpp               |   5 +
 tests/benchmark/expansion.cpp                 |   6 +-
 tests/benchmark/query/eval.cpp                |   9 +-
 tests/benchmark/query/execution.cpp           |  47 +-
 tests/benchmark/query/planner.cpp             |   9 +-
 tests/manual/interactive_planning.cpp         |  15 +-
 tests/manual/single_query.cpp                 |   5 +-
 .../memgraph_V1/features/memgraph.feature     |   2 +-
 tests/unit/CMakeLists.txt                     |  33 +-
 tests/unit/bfs_common.hpp                     |  44 +-
 tests/unit/bfs_single_node.cpp                |   4 +-
 tests/unit/bolt_encoder.cpp                   |   9 +-
 tests/unit/database_dump.cpp                  |  48 +-
 tests/unit/database_transaction_timeout.cpp   |   6 +-
 tests/unit/interpreter.cpp                    |  47 +-
 tests/unit/plan_pretty_print.cpp              |   3 +-
 tests/unit/query_expression_evaluator.cpp     | 114 ++--
 .../unit/query_plan_accumulate_aggregate.cpp  |  36 +-
 tests/unit/query_plan_bag_semantics.cpp       |  18 +-
 tests/unit/query_plan_checker.hpp             |  18 +-
 tests/unit/query_plan_common.hpp              |   2 +-
 .../query_plan_create_set_remove_delete.cpp   | 166 ++++--
 tests/unit/query_plan_edge_cases.cpp          |   3 +-
 tests/unit/query_plan_match_filter_return.cpp | 189 ++++---
 ...query_plan_v2_create_set_remove_delete.cpp | 119 ++++
 tests/unit/query_variable_start_planner.cpp   |  43 +-
 tests/unit/typed_value.cpp                    |   8 +-
 60 files changed, 1694 insertions(+), 878 deletions(-)
 create mode 100644 tests/unit/query_plan_v2_create_set_remove_delete.cpp

diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index af4c17a43..4fa35d7ac 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -108,6 +108,59 @@ target_compile_definitions(mg-single-node PUBLIC MG_SINGLE_NODE)
 # END Memgraph Single Node
 # ----------------------------------------------------------------------------
 
+# ----------------------------------------------------------------------------
+# Memgraph Single Node v2
+# ----------------------------------------------------------------------------
+set(mg_single_node_v2_sources
+    ${lcp_common_cpp_files}
+    audit/log.cpp
+    database/single_node/dump.cpp
+    glue/auth.cpp
+    glue/communication.cpp
+    query/common.cpp
+    query/frontend/ast/cypher_main_visitor.cpp
+    query/frontend/ast/pretty_print.cpp
+    query/frontend/parsing.cpp
+    query/frontend/semantic/required_privileges.cpp
+    query/frontend/semantic/symbol_generator.cpp
+    query/frontend/stripped.cpp
+    query/interpret/awesome_memgraph_functions.cpp
+    query/interpreter.cpp
+    query/plan/operator.cpp
+    query/plan/preprocess.cpp
+    query/plan/pretty_print.cpp
+    query/plan/profile.cpp
+    query/plan/rewrite/index_lookup.cpp
+    query/plan/rule_based_planner.cpp
+    query/plan/variable_start_planner.cpp
+    query/typed_value.cpp
+    memgraph_init.cpp
+)
+
+set(MG_SINGLE_NODE_V2_LIBS stdc++fs Threads::Threads fmt cppitertools
+    antlr_opencypher_parser_lib dl glog gflags mg-storage-v2
+    mg-utils mg-io mg-requests mg-communication)
+# These are enterprise subsystems
+set(MG_SINGLE_NODE_V2_LIBS ${MG_SINGLE_NODE_V2_LIBS} mg-integrations-kafka mg-auth)
+
+if (USE_LTALLOC)
+  list(APPEND MG_SINGLE_NODE_V2_LIBS ltalloc)
+  # TODO(mferencevic): Enable this when clang is updated on apollo.
+  # set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -flto")
+endif()
+
+add_library(mg-single-node-v2 STATIC ${mg_single_node_v2_sources})
+target_link_libraries(mg-single-node-v2 ${MG_SINGLE_NODE_V2_LIBS})
+add_dependencies(mg-single-node-v2 generate_opencypher_parser)
+add_dependencies(mg-single-node-v2 generate_lcp_common)
+target_compile_definitions(mg-single-node-v2 PUBLIC MG_SINGLE_NODE_V2)
+add_executable(memgraph-v2 memgraph.cpp)
+target_link_libraries(memgraph-v2 mg-single-node-v2 kvstore_lib telemetry_lib)
+
+# ----------------------------------------------------------------------------
+# END Memgraph Single Node v2
+# ----------------------------------------------------------------------------
+
 # ----------------------------------------------------------------------------
 # Memgraph Single Node High Availability
 # ----------------------------------------------------------------------------
diff --git a/src/database/single_node/dump.cpp b/src/database/single_node/dump.cpp
index 50e049bbf..f419c906a 100644
--- a/src/database/single_node/dump.cpp
+++ b/src/database/single_node/dump.cpp
@@ -10,7 +10,7 @@
 
 #include <glog/logging.h>
 
-#include "database/graph_db_accessor.hpp"
+#include "query/exceptions.hpp"
 #include "utils/algorithm.hpp"
 #include "utils/string.hpp"
 
@@ -75,8 +75,12 @@ void DumpPropertyValue(std::ostream *os, const PropertyValue &value) {
   }
 }
 
-void DumpProperties(std::ostream *os, GraphDbAccessor *dba,
+void DumpProperties(std::ostream *os, query::DbAccessor *dba,
+#ifdef MG_SINGLE_NODE_V2
+                    const std::map<storage::Property, PropertyValue> &store,
+#else
                     const PropertyValueStore &store,
+#endif
                     std::optional<uint64_t> property_id = std::nullopt) {
   *os << "{";
   if (property_id) {
@@ -84,89 +88,136 @@ void DumpProperties(std::ostream *os, GraphDbAccessor *dba,
     if (store.size() > 0) *os << ", ";
   }
   utils::PrintIterable(*os, store, ", ", [&dba](auto &os, const auto &kv) {
-    os << dba->PropertyName(kv.first) << ": ";
+    os << dba->PropertyToName(kv.first) << ": ";
     DumpPropertyValue(&os, kv.second);
   });
   *os << "}";
 }
 
-void DumpVertex(std::ostream *os, GraphDbAccessor *dba,
-                const VertexAccessor &vertex) {
+void DumpVertex(std::ostream *os, query::DbAccessor *dba,
+                const query::VertexAccessor &vertex) {
   *os << "CREATE (";
   *os << ":" << kInternalVertexLabel;
-  for (const auto &label : vertex.labels()) {
-    *os << ":" << dba->LabelName(label);
+  auto maybe_labels = vertex.Labels(storage::View::OLD);
+  if (maybe_labels.HasError()) {
+    switch (maybe_labels.GetError()) {
+      case storage::Error::DELETED_OBJECT:
+        throw query::QueryRuntimeException(
+            "Trying to get labels from a deleted node.");
+      case storage::Error::SERIALIZATION_ERROR:
+      case storage::Error::VERTEX_HAS_EDGES:
+        throw query::QueryRuntimeException(
+            "Unexpected error when getting labels.");
+    }
+  }
+  for (const auto &label : *maybe_labels) {
+    *os << ":" << dba->LabelToName(label);
   }
   *os << " ";
-  DumpProperties(os, dba, vertex.Properties(),
+  auto maybe_props = vertex.Properties(storage::View::OLD);
+  if (maybe_props.HasError()) {
+    switch (maybe_props.GetError()) {
+      case storage::Error::DELETED_OBJECT:
+        throw query::QueryRuntimeException(
+            "Trying to get properties from a deleted object.");
+      case storage::Error::SERIALIZATION_ERROR:
+      case storage::Error::VERTEX_HAS_EDGES:
+        throw query::QueryRuntimeException(
+            "Unexpected error when getting properties.");
+    }
+  }
+  DumpProperties(os, dba, *maybe_props,
                  std::optional<uint64_t>(vertex.CypherId()));
   *os << ");";
 }
 
-void DumpEdge(std::ostream *os, GraphDbAccessor *dba,
-              const EdgeAccessor &edge) {
+void DumpEdge(std::ostream *os, query::DbAccessor *dba,
+              const query::EdgeAccessor &edge) {
   *os << "MATCH ";
   *os << "(u:" << kInternalVertexLabel << "), ";
   *os << "(v:" << kInternalVertexLabel << ")";
   *os << " WHERE ";
-  *os << "u." << kInternalPropertyId << " = " << edge.from().CypherId();
+  *os << "u." << kInternalPropertyId << " = " << edge.From().CypherId();
   *os << " AND ";
-  *os << "v." << kInternalPropertyId << " = " << edge.to().CypherId() << " ";
+  *os << "v." << kInternalPropertyId << " = " << edge.To().CypherId() << " ";
   *os << "CREATE (u)-[";
-  *os << ":" << dba->EdgeTypeName(edge.EdgeType());
-  const auto &props = edge.Properties();
-  if (props.size() > 0) {
+  *os << ":" << dba->EdgeTypeToName(edge.EdgeType());
+  auto maybe_props = edge.Properties(storage::View::OLD);
+  if (maybe_props.HasError()) {
+    switch (maybe_props.GetError()) {
+      case storage::Error::DELETED_OBJECT:
+        throw query::QueryRuntimeException(
+            "Trying to get properties from a deleted object.");
+      case storage::Error::SERIALIZATION_ERROR:
+      case storage::Error::VERTEX_HAS_EDGES:
+        throw query::QueryRuntimeException(
+            "Unexpected error when getting properties.");
+    }
+  }
+  if (maybe_props->size() > 0) {
     *os << " ";
-    DumpProperties(os, dba, edge.Properties());
+    DumpProperties(os, dba, *maybe_props);
   }
   *os << "]->(v);";
 }
 
-void DumpIndexKey(std::ostream *os, GraphDbAccessor *dba,
+#ifndef MG_SINGLE_NODE_V2
+void DumpIndexKey(std::ostream *os, query::DbAccessor *dba,
                   const LabelPropertyIndex::Key &key) {
-  *os << "CREATE INDEX ON :" << dba->LabelName(key.label_) << "("
-      << dba->PropertyName(key.property_) << ");";
+  *os << "CREATE INDEX ON :" << dba->LabelToName(key.label_) << "("
+      << dba->PropertyToName(key.property_) << ");";
 }
 
 void DumpUniqueConstraint(
-    std::ostream *os, GraphDbAccessor *dba,
+    std::ostream *os, query::DbAccessor *dba,
     const storage::constraints::ConstraintEntry &constraint) {
-  *os << "CREATE CONSTRAINT ON (u:" << dba->LabelName(constraint.label)
+  *os << "CREATE CONSTRAINT ON (u:" << dba->LabelToName(constraint.label)
       << ") ASSERT ";
   utils::PrintIterable(*os, constraint.properties, ", ",
                        [&dba](auto &os, const auto &property) {
-                         os << "u." << dba->PropertyName(property);
+                         os << "u." << dba->PropertyToName(property);
                        });
   *os << " IS UNIQUE;";
 }
+#endif
 
 }  // namespace
 
-CypherDumpGenerator::CypherDumpGenerator(GraphDbAccessor *dba)
+CypherDumpGenerator::CypherDumpGenerator(query::DbAccessor *dba)
     : dba_(dba),
       created_internal_index_(false),
       cleaned_internal_index_(false),
       cleaned_internal_label_property_(false) {
   CHECK(dba);
+#ifdef MG_SINGLE_NODE_V2
+  throw utils::NotYetImplemented("Dumping indices and constraints");
+#else
   indices_state_.emplace(dba->GetIndicesKeys());
   unique_constraints_state_.emplace(dba->ListUniqueConstraints());
-  vertices_state_.emplace(dba->Vertices(false));
-  edges_state_.emplace(dba->Edges(false));
+#endif
+  vertices_state_.emplace(dba->Vertices(storage::View::OLD));
+  edges_state_.emplace(dba->Edges(storage::View::OLD));
 }
 
 bool CypherDumpGenerator::NextQuery(std::ostream *os) {
+#ifdef MG_SINGLE_NODE_V2
+  if (!vertices_state_->Empty() && !created_internal_index_) {
+#else
   if (!indices_state_->ReachedEnd()) {
     DumpIndexKey(os, dba_, *indices_state_->GetCurrentAndAdvance());
     return true;
   } else if (!vertices_state_->Empty() && !created_internal_index_) {
+#endif
     *os << "CREATE INDEX ON :" << kInternalVertexLabel << "("
         << kInternalPropertyId << ");";
     created_internal_index_ = true;
     return true;
+#ifndef MG_SINGLE_NODE_V2
   } else if (!unique_constraints_state_->ReachedEnd()) {
     DumpUniqueConstraint(os, dba_,
                          *unique_constraints_state_->GetCurrentAndAdvance());
     return true;
+#endif
   } else if (!vertices_state_->ReachedEnd()) {
     DumpVertex(os, dba_, *vertices_state_->GetCurrentAndAdvance());
     return true;
diff --git a/src/database/single_node/dump.hpp b/src/database/single_node/dump.hpp
index 58842d68e..14078c324 100644
--- a/src/database/single_node/dump.hpp
+++ b/src/database/single_node/dump.hpp
@@ -2,8 +2,11 @@
 
 #include <ostream>
 
-#include "database/graph_db_accessor.hpp"
+// TODO: Move this whole file to query folder
+#include "query/db_accessor.hpp"
+#ifndef MG_SINGLE_NODE_V2
 #include "storage/common/constraints/unique_constraints.hpp"
+#endif
 
 namespace database {
 
@@ -14,7 +17,7 @@ namespace database {
 /// queries.
 class CypherDumpGenerator {
  public:
-  explicit CypherDumpGenerator(GraphDbAccessor *dba);
+  explicit CypherDumpGenerator(query::DbAccessor *dba);
 
   CypherDumpGenerator(const CypherDumpGenerator &other) = delete;
   // NOLINTNEXTLINE(performance-noexcept-move-constructor)
@@ -64,20 +67,23 @@ class CypherDumpGenerator {
     bool empty_;
   };
 
-  GraphDbAccessor *dba_;
+  query::DbAccessor *dba_;
 
   bool created_internal_index_;
   bool cleaned_internal_index_;
   bool cleaned_internal_label_property_;
 
+#ifndef MG_SINGLE_NODE_V2
   std::optional<ContainerState<std::vector<LabelPropertyIndex::Key>>>
       indices_state_;
   std::optional<
       ContainerState<std::vector<storage::constraints::ConstraintEntry>>>
       unique_constraints_state_;
-  std::optional<ContainerState<decltype(dba_->Vertices(false))>>
+#endif
+  std::optional<ContainerState<decltype(dba_->Vertices(storage::View::OLD))>>
       vertices_state_;
-  std::optional<ContainerState<decltype(dba_->Edges(false))>> edges_state_;
+  std::optional<ContainerState<decltype(dba_->Edges(storage::View::OLD))>>
+      edges_state_;
 };
 
 }  // namespace database
diff --git a/src/glue/communication.cpp b/src/glue/communication.cpp
index f3cf3b95d..880775bf1 100644
--- a/src/glue/communication.cpp
+++ b/src/glue/communication.cpp
@@ -49,6 +49,30 @@ query::TypedValue ToTypedValue(const Value &value) {
   }
 }
 
+#ifdef MG_SINGLE_NODE_V2
+storage::Result<communication::bolt::Vertex> ToBoltVertex(
+    const query::VertexAccessor &vertex, const storage::Storage &db,
+    storage::View view) {
+  return ToBoltVertex(vertex.impl_, db, view);
+}
+
+storage::Result<communication::bolt::Edge> ToBoltEdge(
+    const query::EdgeAccessor &edge, const storage::Storage &db,
+    storage::View view) {
+  return ToBoltEdge(edge.impl_, db, view);
+}
+#else
+communication::bolt::Vertex ToBoltVertex(const query::VertexAccessor &vertex,
+                                         storage::View view) {
+  return ToBoltVertex(vertex.impl_, view);
+}
+
+communication::bolt::Edge ToBoltEdge(const query::EdgeAccessor &edge,
+                                     storage::View view) {
+  return ToBoltEdge(edge.impl_, view);
+}
+#endif
+
 #ifdef MG_SINGLE_NODE_V2
 storage::Result<Value> ToBoltValue(const query::TypedValue &value,
                                    const storage::Storage &db,
diff --git a/src/memgraph.cpp b/src/memgraph.cpp
index d3e1b8c75..f71d66399 100644
--- a/src/memgraph.cpp
+++ b/src/memgraph.cpp
@@ -10,7 +10,11 @@
 #include <glog/logging.h>
 
 #include "communication/server.hpp"
+#ifdef MG_SINGLE_NODE_V2
+#include "storage/v2/storage.hpp"
+#else
 #include "database/single_node/graph_db.hpp"
+#endif
 #include "integrations/kafka/exceptions.hpp"
 #include "integrations/kafka/streams.hpp"
 #include "memgraph_init.hpp"
@@ -88,7 +92,11 @@ void SingleNodeMain() {
 
   // Main storage and execution engines initialization
 
+#ifdef MG_SINGLE_NODE_V2
+  storage::Storage db;
+#else
   database::GraphDb db;
+#endif
   query::Interpreter interpreter;
   SessionData session_data{&db, &interpreter, &auth, &audit_log};
 
@@ -123,6 +131,7 @@ void SingleNodeMain() {
 
   // Setup telemetry
   std::optional<telemetry::Telemetry> telemetry;
+#ifndef MG_SINGLE_NODE_V2
   if (FLAGS_telemetry_enabled) {
     telemetry.emplace(
         "https://telemetry.memgraph.com/88b5e7e8-746a-11e8-9f85-538a9e9690cc/",
@@ -132,6 +141,7 @@ void SingleNodeMain() {
       return {{"vertices", dba.VerticesCount()}, {"edges", dba.EdgesCount()}};
     });
   }
+#endif
 
   // Handler for regular termination signals
   auto shutdown = [&server] {
diff --git a/src/memgraph_init.cpp b/src/memgraph_init.cpp
index 8df44cb5e..c3c349b5f 100644
--- a/src/memgraph_init.cpp
+++ b/src/memgraph_init.cpp
@@ -29,6 +29,9 @@ BoltSession::BoltSession(SessionData *data,
     : communication::bolt::Session<communication::InputStream,
                                    communication::OutputStream>(input_stream,
                                                                 output_stream),
+#ifdef MG_SINGLE_NODE_V2
+      db_(data->db),
+#endif
       transaction_engine_(data->db, data->interpreter),
 #ifndef MG_SINGLE_NODE_HA
       auth_(data->auth),
@@ -78,12 +81,30 @@ std::vector<std::string> BoltSession::Interpret(
 std::map<std::string, communication::bolt::Value> BoltSession::PullAll(
     TEncoder *encoder) {
   try {
+#ifdef MG_SINGLE_NODE_V2
+    TypedValueResultStream stream(encoder, db_);
+#else
     TypedValueResultStream stream(encoder);
+#endif
     const auto &summary = transaction_engine_.PullAll(&stream);
     std::map<std::string, communication::bolt::Value> decoded_summary;
     for (const auto &kv : summary) {
+#ifdef MG_SINGLE_NODE_V2
+      auto maybe_value = glue::ToBoltValue(kv.second, *db_, storage::View::NEW);
+      if (maybe_value.HasError()) {
+        switch (maybe_value.GetError()) {
+          case storage::Error::DELETED_OBJECT:
+          case storage::Error::SERIALIZATION_ERROR:
+          case storage::Error::VERTEX_HAS_EDGES:
+            throw communication::bolt::ClientError(
+                "Unexpected storage error when streaming summary.");
+        }
+      }
+      decoded_summary.emplace(kv.first, std::move(*maybe_value));
+#else
       decoded_summary.emplace(kv.first,
                               glue::ToBoltValue(kv.second, storage::View::NEW));
+#endif
     }
     return decoded_summary;
   } catch (const query::QueryException &e) {
@@ -106,15 +127,37 @@ bool BoltSession::Authenticate(const std::string &username,
 #endif
 }
 
+#ifdef MG_SINGLE_NODE_V2
+BoltSession::TypedValueResultStream::TypedValueResultStream(
+    TEncoder *encoder, const storage::Storage *db)
+    : encoder_(encoder), db_(db) {}
+#else
 BoltSession::TypedValueResultStream::TypedValueResultStream(TEncoder *encoder)
     : encoder_(encoder) {}
+#endif
 
 void BoltSession::TypedValueResultStream::Result(
     const std::vector<query::TypedValue> &values) {
   std::vector<communication::bolt::Value> decoded_values;
   decoded_values.reserve(values.size());
   for (const auto &v : values) {
+#ifdef MG_SINGLE_NODE_V2
+    auto maybe_value = glue::ToBoltValue(v, *db_, storage::View::NEW);
+    if (maybe_value.HasError()) {
+      switch (maybe_value.GetError()) {
+        case storage::Error::DELETED_OBJECT:
+          throw communication::bolt::ClientError(
+              "Returning a deleted object as a result.");
+        case storage::Error::VERTEX_HAS_EDGES:
+        case storage::Error::SERIALIZATION_ERROR:
+          throw communication::bolt::ClientError(
+              "Unexpected storage error when streaming results.");
+      }
+    }
+    decoded_values.emplace_back(std::move(*maybe_value));
+#else
     decoded_values.push_back(glue::ToBoltValue(v, storage::View::NEW));
+#endif
   }
   encoder_->MessageRecord(decoded_values);
 }
@@ -123,15 +166,29 @@ void KafkaStreamWriter(
     SessionData &session_data, const std::string &query,
     const std::map<std::string, communication::bolt::Value> &params) {
   auto dba = session_data.db->Access();
+  query::DbAccessor execution_dba(&dba);
   KafkaResultStream stream;
   std::map<std::string, PropertyValue> params_pv;
   for (const auto &kv : params)
     params_pv.emplace(kv.first, glue::ToPropertyValue(kv.second));
   try {
-    (*session_data.interpreter)(query, dba, params_pv, false,
+    (*session_data.interpreter)(query, &execution_dba, params_pv, false,
                                 utils::NewDeleteResource())
         .PullAll(stream);
+#ifdef MG_SINGLE_NODE_V2
+    auto maybe_constraint_violation = dba.Commit();
+    if (maybe_constraint_violation.HasError()) {
+      const auto &constraint_violation = maybe_constraint_violation.GetError();
+      auto label_name = dba.LabelToName(constraint_violation.label);
+      auto property_name = dba.PropertyToName(constraint_violation.property);
+      LOG(WARNING) << fmt::format(
+          "[Kafka] query execution failed with an exception: "
+          "Unable to commit due to constraint violation on :{}({}).",
+          label_name, property_name);
+    }
+#else
     dba.Commit();
+#endif
   } catch (const utils::BasicException &e) {
     LOG(WARNING) << "[Kafka] query execution failed with an exception: "
                  << e.what();
diff --git a/src/memgraph_init.hpp b/src/memgraph_init.hpp
index 8f0899279..a5006b019 100644
--- a/src/memgraph_init.hpp
+++ b/src/memgraph_init.hpp
@@ -18,6 +18,12 @@
 #include "query/interpreter.hpp"
 #include "query/transaction_engine.hpp"
 
+#ifdef MG_SINGLE_NODE_V2
+namespace database {
+using GraphDb = storage::Storage;
+}
+#endif
+
 DECLARE_string(durability_directory);
 
 /// Encapsulates Dbms and Interpreter that are passed through the network server
@@ -65,14 +71,26 @@ class BoltSession final
   /// before forwarding the calls to original TEncoder.
   class TypedValueResultStream {
    public:
+#ifdef MG_SINGLE_NODE_V2
+    TypedValueResultStream(TEncoder *encoder, const storage::Storage *db);
+#else
     TypedValueResultStream(TEncoder *encoder);
+#endif
 
     void Result(const std::vector<query::TypedValue> &values);
 
    private:
     TEncoder *encoder_;
+#ifdef MG_SINGLE_NODE_V2
+  // NOTE: Needed only for ToBoltValue conversions
+  const storage::Storage *db_;
+#endif
   };
 
+#ifdef MG_SINGLE_NODE_V2
+  // NOTE: Needed only for ToBoltValue conversions
+  const storage::Storage *db_;
+#endif
   query::TransactionEngine transaction_engine_;
 #ifndef MG_SINGLE_NODE_HA
   auth::Auth *auth_;
diff --git a/src/query/common.cpp b/src/query/common.cpp
index b3cd0626d..ff463ac49 100644
--- a/src/query/common.cpp
+++ b/src/query/common.cpp
@@ -2,39 +2,6 @@
 
 namespace query {
 
-void ReconstructTypedValue(TypedValue &value) {
-  using Type = TypedValue::Type;
-  switch (value.type()) {
-    case Type::Vertex:
-      if (!value.ValueVertex().Reconstruct()) throw ReconstructionException();
-      break;
-    case Type::Edge:
-      if (!value.ValueEdge().Reconstruct()) throw ReconstructionException();
-      break;
-    case Type::List:
-      for (TypedValue &inner_value : value.ValueList())
-        ReconstructTypedValue(inner_value);
-      break;
-    case Type::Map:
-      for (auto &kv : value.ValueMap())
-        ReconstructTypedValue(kv.second);
-      break;
-    case Type::Path:
-      for (auto &vertex : value.ValuePath().vertices()) {
-        if (!vertex.Reconstruct()) throw ReconstructionException();
-      }
-      for (auto &edge : value.ValuePath().edges()) {
-        if (!edge.Reconstruct()) throw ReconstructionException();
-      }
-    case Type::Null:
-    case Type::Bool:
-    case Type::Int:
-    case Type::Double:
-    case Type::String:
-      break;
-  }
-}
-
 namespace impl {
 
 bool TypedValueCompare(const TypedValue &a, const TypedValue &b) {
@@ -81,19 +48,4 @@ bool TypedValueCompare(const TypedValue &a, const TypedValue &b) {
 
 }  // namespace impl
 
-template <typename TAccessor>
-void SwitchAccessor(TAccessor &accessor, storage::View view) {
-  switch (view) {
-    case storage::View::NEW:
-      accessor.SwitchNew();
-      break;
-    case storage::View::OLD:
-      accessor.SwitchOld();
-      break;
-  }
-}
-
-template void SwitchAccessor<>(VertexAccessor &accessor, storage::View view);
-template void SwitchAccessor<>(EdgeAccessor &accessor, storage::View view);
-
 }  // namespace query
diff --git a/src/query/common.hpp b/src/query/common.hpp
index 2312e498a..668b58e87 100644
--- a/src/query/common.hpp
+++ b/src/query/common.hpp
@@ -6,7 +6,7 @@
 
 #include <glog/logging.h>
 
-#include "database/graph_db_accessor.hpp"
+#include "query/db_accessor.hpp"
 #include "query/exceptions.hpp"
 #include "query/frontend/ast/ast.hpp"
 #include "query/frontend/semantic/symbol.hpp"
@@ -16,11 +16,6 @@
 
 namespace query {
 
-/// Recursively reconstruct all the accessors in the given TypedValue.
-///
-/// @throw ReconstructionException if any reconstruction failed.
-void ReconstructTypedValue(TypedValue &value);
-
 namespace impl {
 bool TypedValueCompare(const TypedValue &a, const TypedValue &b);
 }  // namespace impl
@@ -66,10 +61,6 @@ class TypedValueVectorCompare final {
   std::vector<Ordering> ordering_;
 };
 
-/// Switch the given [Vertex/Edge]Accessor to the desired state.
-template <class TAccessor>
-void SwitchAccessor(TAccessor &accessor, storage::View view);
-
 /// Raise QueryRuntimeException if the value for symbol isn't of expected type.
 inline void ExpectType(const Symbol &symbol, const TypedValue &value,
                        TypedValue::Type expected) {
@@ -85,15 +76,23 @@ template <class TRecordAccessor>
 void PropsSetChecked(TRecordAccessor *record, const storage::Property &key,
                      const TypedValue &value) {
   try {
-    record->PropsSet(key, PropertyValue(value));
+    auto maybe_error = record->SetProperty(key, PropertyValue(value));
+    if (maybe_error.HasError()) {
+      switch (maybe_error.GetError()) {
+        case storage::Error::SERIALIZATION_ERROR:
+          throw QueryRuntimeException(
+              "Can't serialize due to concurrent operations.");
+        case storage::Error::DELETED_OBJECT:
+          throw QueryRuntimeException(
+              "Trying to set properties on a deleted object.");
+        case storage::Error::VERTEX_HAS_EDGES:
+          throw QueryRuntimeException(
+              "Unexpected error when setting a property.");
+      }
+    }
   } catch (const TypedValueException &) {
     throw QueryRuntimeException("'{}' cannot be used as a property value.",
                                 value.type());
-  } catch (const RecordDeletedError &) {
-    throw QueryRuntimeException(
-        "Trying to set properties on a deleted graph element.");
-  } catch (const database::ConstraintViolationException &e) {
-    throw QueryRuntimeException(e.what());
   }
 }
 
diff --git a/src/query/context.hpp b/src/query/context.hpp
index 101f6fb8f..da6933f8d 100644
--- a/src/query/context.hpp
+++ b/src/query/context.hpp
@@ -1,6 +1,6 @@
 #pragma once
 
-#include "database/graph_db_accessor.hpp"
+#include "query/common.hpp"
 #include "query/frontend/semantic/symbol_table.hpp"
 #include "query/parameters.hpp"
 #include "query/plan/profile.hpp"
@@ -26,29 +26,27 @@ struct EvaluationContext {
 };
 
 inline std::vector<storage::Property> NamesToProperties(
-    const std::vector<std::string> &property_names,
-    database::GraphDbAccessor *dba) {
+    const std::vector<std::string> &property_names, DbAccessor *dba) {
   std::vector<storage::Property> properties;
   properties.reserve(property_names.size());
   for (const auto &name : property_names) {
-    properties.push_back(dba->Property(name));
+    properties.push_back(dba->NameToProperty(name));
   }
   return properties;
 }
 
 inline std::vector<storage::Label> NamesToLabels(
-    const std::vector<std::string> &label_names,
-    database::GraphDbAccessor *dba) {
+    const std::vector<std::string> &label_names, DbAccessor *dba) {
   std::vector<storage::Label> labels;
   labels.reserve(label_names.size());
   for (const auto &name : label_names) {
-    labels.push_back(dba->Label(name));
+    labels.push_back(dba->NameToLabel(name));
   }
   return labels;
 }
 
 struct ExecutionContext {
-  database::GraphDbAccessor *db_accessor{nullptr};
+  DbAccessor *db_accessor{nullptr};
   SymbolTable symbol_table;
   EvaluationContext evaluation_context;
   bool is_profile_query{false};
diff --git a/src/query/db_accessor.hpp b/src/query/db_accessor.hpp
index c3c2f6ddc..34b29f6fa 100644
--- a/src/query/db_accessor.hpp
+++ b/src/query/db_accessor.hpp
@@ -625,19 +625,20 @@ class DbAccessor final {
 
   bool MustAbort() const { return false; }
 
+  // TODO: Index manipulation should not go through Accessor
   bool CreateIndex(storage::Label label, storage::Property prop) {
-    return accessor_->CreateIndex(label, prop);
+    throw utils::NotYetImplemented("CreateIndex");
   }
 
   bool DropIndex(storage::Label label, storage::Property prop) {
-    return accessor_->DropIndex(label, prop);
+    throw utils::NotYetImplemented("DropIndex");
   }
 
-  // TODO: These should probably be in some kind of StorageInfo class instead of
-  // here.
+  // TODO: Querying information should probably be in some kind of StorageInfo
+  // class instead of here in Accessor.
   bool LabelPropertyIndexExists(storage::Label label,
                                 storage::Property prop) const {
-    return accessor_->LabelPropertyIndexExists(label, prop);
+    throw utils::NotYetImplemented("LabelPropertyIndexExists");
   }
 
   int64_t VerticesCount() const { return accessor_->ApproximateVertexCount(); }
@@ -663,14 +664,15 @@ class DbAccessor final {
     return accessor_->ApproximateVertexCount(label, property, lower, upper);
   }
 
-  auto CreateExistenceConstraint(storage::Label label,
-                                 storage::Property property) {
-    return accessor_->CreateExistenceConstraint(label, property);
+  // TODO: Constraints manipulation should not go through Accessor
+  utils::BasicResult<storage::ExistenceConstraintViolation, bool>
+  CreateExistenceConstraint(storage::Label label, storage::Property property) {
+    throw utils::NotYetImplemented("CreateExistenceConstraint");
   }
 
-  auto DropExistenceConstraint(storage::Label label,
+  bool DropExistenceConstraint(storage::Label label,
                                storage::Property property) {
-    return accessor_->DropExistenceConstraint(label, property);
+    throw utils::NotYetImplemented("DropExistenceConstraint");
   }
 };
 #else
diff --git a/src/query/interpret/awesome_memgraph_functions.cpp b/src/query/interpret/awesome_memgraph_functions.cpp
index 45f5fa637..83ad64cc5 100644
--- a/src/query/interpret/awesome_memgraph_functions.cpp
+++ b/src/query/interpret/awesome_memgraph_functions.cpp
@@ -7,7 +7,10 @@
 #include <functional>
 #include <random>
 
-#include "database/graph_db_accessor.hpp"
+#ifndef MG_SINGLE_NODE_V2
+#include "database/single_node/dump.hpp"
+#endif
+#include "query/db_accessor.hpp"
 #include "query/exceptions.hpp"
 #include "query/typed_value.hpp"
 #include "utils/string.hpp"
@@ -336,7 +339,7 @@ TypedValue EndNode(const TypedValue *args, int64_t nargs,
                    const FunctionContext &ctx) {
   FType<Or<Null, Edge>>("endNode", args, nargs);
   if (args[0].IsNull()) return TypedValue(ctx.memory);
-  return TypedValue(args[0].ValueEdge().to(), ctx.memory);
+  return TypedValue(args[0].ValueEdge().To(), ctx.memory);
 }
 
 TypedValue Head(const TypedValue *args, int64_t nargs,
@@ -363,8 +366,20 @@ TypedValue Properties(const TypedValue *args, int64_t nargs,
   auto *dba = ctx.db_accessor;
   auto get_properties = [&](const auto &record_accessor) {
     TypedValue::TMap properties(ctx.memory);
-    for (const auto &property : record_accessor.Properties()) {
-      properties.emplace(dba->PropertyName(property.first), property.second);
+    auto maybe_props = record_accessor.Properties(ctx.view);
+    if (maybe_props.HasError()) {
+      switch (maybe_props.GetError()) {
+        case storage::Error::DELETED_OBJECT:
+          throw QueryRuntimeException(
+              "Trying to get properties from a deleted object.");
+        case storage::Error::SERIALIZATION_ERROR:
+        case storage::Error::VERTEX_HAS_EDGES:
+          throw QueryRuntimeException(
+              "Unexpected error when getting properties.");
+      }
+    }
+    for (const auto &property : *maybe_props) {
+      properties.emplace(dba->PropertyToName(property.first), property.second);
     }
     return TypedValue(std::move(properties));
   };
@@ -411,17 +426,35 @@ TypedValue StartNode(const TypedValue *args, int64_t nargs,
                      const FunctionContext &ctx) {
   FType<Or<Null, Edge>>("startNode", args, nargs);
   if (args[0].IsNull()) return TypedValue(ctx.memory);
-  return TypedValue(args[0].ValueEdge().from(), ctx.memory);
+  return TypedValue(args[0].ValueEdge().From(), ctx.memory);
 }
 
+namespace {
+
+size_t UnwrapDegreeResult(storage::Result<size_t> maybe_degree) {
+  if (maybe_degree.HasError()) {
+    switch (maybe_degree.GetError()) {
+      case storage::Error::DELETED_OBJECT:
+        throw QueryRuntimeException("Trying to get degree of a deleted node.");
+      case storage::Error::SERIALIZATION_ERROR:
+      case storage::Error::VERTEX_HAS_EDGES:
+        throw QueryRuntimeException(
+            "Unexpected error when getting node degree.");
+    }
+  }
+  return *maybe_degree;
+}
+
+}  // namespace
+
 TypedValue Degree(const TypedValue *args, int64_t nargs,
                   const FunctionContext &ctx) {
   FType<Or<Null, Vertex>>("degree", args, nargs);
   if (args[0].IsNull()) return TypedValue(ctx.memory);
   const auto &vertex = args[0].ValueVertex();
-  return TypedValue(
-      static_cast<int64_t>(vertex.out_degree() + vertex.in_degree()),
-      ctx.memory);
+  size_t out_degree = UnwrapDegreeResult(vertex.OutDegree(ctx.view));
+  size_t in_degree = UnwrapDegreeResult(vertex.InDegree(ctx.view));
+  return TypedValue(static_cast<int64_t>(out_degree + in_degree), ctx.memory);
 }
 
 TypedValue InDegree(const TypedValue *args, int64_t nargs,
@@ -429,7 +462,8 @@ TypedValue InDegree(const TypedValue *args, int64_t nargs,
   FType<Or<Null, Vertex>>("inDegree", args, nargs);
   if (args[0].IsNull()) return TypedValue(ctx.memory);
   const auto &vertex = args[0].ValueVertex();
-  return TypedValue(static_cast<int64_t>(vertex.in_degree()), ctx.memory);
+  size_t in_degree = UnwrapDegreeResult(vertex.InDegree(ctx.view));
+  return TypedValue(static_cast<int64_t>(in_degree), ctx.memory);
 }
 
 TypedValue OutDegree(const TypedValue *args, int64_t nargs,
@@ -437,7 +471,8 @@ TypedValue OutDegree(const TypedValue *args, int64_t nargs,
   FType<Or<Null, Vertex>>("outDegree", args, nargs);
   if (args[0].IsNull()) return TypedValue(ctx.memory);
   const auto &vertex = args[0].ValueVertex();
-  return TypedValue(static_cast<int64_t>(vertex.out_degree()), ctx.memory);
+  size_t out_degree = UnwrapDegreeResult(vertex.OutDegree(ctx.view));
+  return TypedValue(static_cast<int64_t>(out_degree), ctx.memory);
 }
 
 TypedValue ToBoolean(const TypedValue *args, int64_t nargs,
@@ -521,18 +556,30 @@ TypedValue Type(const TypedValue *args, int64_t nargs,
   FType<Or<Null, Edge>>("type", args, nargs);
   auto *dba = ctx.db_accessor;
   if (args[0].IsNull()) return TypedValue(ctx.memory);
-  return TypedValue(dba->EdgeTypeName(args[0].ValueEdge().EdgeType()),
+  return TypedValue(dba->EdgeTypeToName(args[0].ValueEdge().EdgeType()),
                     ctx.memory);
 }
 
+// TODO: How is Keys different from Properties function?
 TypedValue Keys(const TypedValue *args, int64_t nargs,
                 const FunctionContext &ctx) {
   FType<Or<Null, Vertex, Edge>>("keys", args, nargs);
   auto *dba = ctx.db_accessor;
   auto get_keys = [&](const auto &record_accessor) {
     TypedValue::TVector keys(ctx.memory);
-    for (const auto &property : record_accessor.Properties()) {
-      keys.emplace_back(dba->PropertyName(property.first));
+    auto maybe_props = record_accessor.Properties(ctx.view);
+    if (maybe_props.HasError()) {
+      switch (maybe_props.GetError()) {
+        case storage::Error::DELETED_OBJECT:
+          throw QueryRuntimeException(
+              "Trying to get keys from a deleted object.");
+        case storage::Error::SERIALIZATION_ERROR:
+        case storage::Error::VERTEX_HAS_EDGES:
+          throw QueryRuntimeException("Unexpected error when getting keys.");
+      }
+    }
+    for (const auto &property : *maybe_props) {
+      keys.emplace_back(dba->PropertyToName(property.first));
     }
     return TypedValue(std::move(keys));
   };
@@ -554,8 +601,19 @@ TypedValue Labels(const TypedValue *args, int64_t nargs,
   auto *dba = ctx.db_accessor;
   if (args[0].IsNull()) return TypedValue(ctx.memory);
   TypedValue::TVector labels(ctx.memory);
-  for (const auto &label : args[0].ValueVertex().labels()) {
-    labels.emplace_back(dba->LabelName(label));
+  auto maybe_labels = args[0].ValueVertex().Labels(ctx.view);
+  if (maybe_labels.HasError()) {
+    switch (maybe_labels.GetError()) {
+      case storage::Error::DELETED_OBJECT:
+        throw QueryRuntimeException(
+            "Trying to get labels from a deleted node.");
+      case storage::Error::SERIALIZATION_ERROR:
+      case storage::Error::VERTEX_HAS_EDGES:
+        throw QueryRuntimeException("Unexpected error when getting labels.");
+    }
+  }
+  for (const auto &label : *maybe_labels) {
+    labels.emplace_back(dba->LabelToName(label));
   }
   return TypedValue(std::move(labels));
 }
diff --git a/src/query/interpret/awesome_memgraph_functions.hpp b/src/query/interpret/awesome_memgraph_functions.hpp
index e915d8c11..eacea62a9 100644
--- a/src/query/interpret/awesome_memgraph_functions.hpp
+++ b/src/query/interpret/awesome_memgraph_functions.hpp
@@ -5,14 +5,12 @@
 #include <string>
 #include <unordered_map>
 
+#include "storage/v2/view.hpp"
 #include "utils/memory.hpp"
 
-namespace database {
-class GraphDbAccessor;
-}
-
 namespace query {
 
+class DbAccessor;
 class TypedValue;
 
 namespace {
@@ -22,10 +20,11 @@ const char kContains[] = "CONTAINS";
 }  // namespace
 
 struct FunctionContext {
-  database::GraphDbAccessor *db_accessor;
+  DbAccessor *db_accessor;
   utils::MemoryResource *memory;
   int64_t timestamp;
   std::unordered_map<std::string, int64_t> *counters;
+  storage::View view;
 };
 
 /// Return the function implementation with the given name.
diff --git a/src/query/interpret/eval.hpp b/src/query/interpret/eval.hpp
index f06f881e4..1475b2677 100644
--- a/src/query/interpret/eval.hpp
+++ b/src/query/interpret/eval.hpp
@@ -7,9 +7,9 @@
 #include <regex>
 #include <vector>
 
-#include "database/graph_db_accessor.hpp"
 #include "query/common.hpp"
 #include "query/context.hpp"
+#include "query/db_accessor.hpp"
 #include "query/exceptions.hpp"
 #include "query/frontend/ast/ast.hpp"
 #include "query/frontend/semantic/symbol_table.hpp"
@@ -22,8 +22,8 @@ namespace query {
 class ExpressionEvaluator : public ExpressionVisitor<TypedValue> {
  public:
   ExpressionEvaluator(Frame *frame, const SymbolTable &symbol_table,
-                      const EvaluationContext &ctx,
-                      database::GraphDbAccessor *dba, storage::View view)
+                      const EvaluationContext &ctx, DbAccessor *dba,
+                      storage::View view)
       : frame_(frame),
         symbol_table_(&symbol_table),
         ctx_(&ctx),
@@ -42,9 +42,7 @@ class ExpressionEvaluator : public ExpressionVisitor<TypedValue> {
   }
 
   TypedValue Visit(Identifier &ident) override {
-    TypedValue value(frame_->at(symbol_table_->at(ident)), ctx_->memory);
-    SwitchAccessors(value);
-    return value;
+    return TypedValue(frame_->at(symbol_table_->at(ident)), ctx_->memory);
   }
 
 #define BINARY_OPERATOR_VISITOR(OP_NODE, CPP_OP, CYPHER_OP)              \
@@ -203,8 +201,7 @@ class ExpressionEvaluator : public ExpressionVisitor<TypedValue> {
       if (!index.IsString())
         throw QueryRuntimeException(
             "Expected a string as a property name, got {}.", index.type());
-      return TypedValue(lhs.ValueVertex().PropsAt(
-                            dba_->Property(std::string(index.ValueString()))),
+      return TypedValue(GetProperty(lhs.ValueVertex(), index.ValueString()),
                         ctx_->memory);
     }
 
@@ -212,8 +209,7 @@ class ExpressionEvaluator : public ExpressionVisitor<TypedValue> {
       if (!index.IsString())
         throw QueryRuntimeException(
             "Expected a string as a property name, got {}.", index.type());
-      return TypedValue(lhs.ValueEdge().PropsAt(
-                            dba_->Property(std::string(index.ValueString()))),
+      return TypedValue(GetProperty(lhs.ValueEdge(), index.ValueString()),
                         ctx_->memory);
     }
 
@@ -282,12 +278,12 @@ class ExpressionEvaluator : public ExpressionVisitor<TypedValue> {
       case TypedValue::Type::Null:
         return TypedValue(ctx_->memory);
       case TypedValue::Type::Vertex:
-        return TypedValue(expression_result.ValueVertex().PropsAt(
-                              GetProperty(property_lookup.property_)),
+        return TypedValue(GetProperty(expression_result.ValueVertex(),
+                                      property_lookup.property_),
                           ctx_->memory);
       case TypedValue::Type::Edge:
-        return TypedValue(expression_result.ValueEdge().PropsAt(
-                              GetProperty(property_lookup.property_)),
+        return TypedValue(GetProperty(expression_result.ValueEdge(),
+                                      property_lookup.property_),
                           ctx_->memory);
       case TypedValue::Type::Map: {
         // NOTE: Take non-const reference to map, so that we can move out the
@@ -313,7 +309,19 @@ class ExpressionEvaluator : public ExpressionVisitor<TypedValue> {
       case TypedValue::Type::Vertex: {
         const auto &vertex = expression_result.ValueVertex();
         for (const auto &label : labels_test.labels_) {
-          if (!vertex.has_label(GetLabel(label))) {
+          auto has_label = vertex.HasLabel(view_, GetLabel(label));
+          if (has_label.HasError()) {
+            switch (has_label.GetError()) {
+              case storage::Error::DELETED_OBJECT:
+                throw QueryRuntimeException(
+                    "Trying to access labels on a deleted node.");
+              case storage::Error::SERIALIZATION_ERROR:
+              case storage::Error::VERTEX_HAS_EDGES:
+                throw QueryRuntimeException(
+                    "Unexpected error when accessing labels.");
+            }
+          }
+          if (!*has_label) {
             return TypedValue(false, ctx_->memory);
           }
         }
@@ -346,11 +354,7 @@ class ExpressionEvaluator : public ExpressionVisitor<TypedValue> {
   }
 
   TypedValue Visit(Aggregation &aggregation) override {
-    TypedValue value(frame_->at(symbol_table_->at(aggregation)), ctx_->memory);
-    // Aggregation is probably always simple type, but let's switch accessor
-    // just to be sure.
-    SwitchAccessors(value);
-    return value;
+    return TypedValue(frame_->at(symbol_table_->at(aggregation)), ctx_->memory);
   }
 
   TypedValue Visit(Coalesce &coalesce) override {
@@ -372,7 +376,7 @@ class ExpressionEvaluator : public ExpressionVisitor<TypedValue> {
 
   TypedValue Visit(Function &function) override {
     FunctionContext function_ctx{dba_, ctx_->memory, ctx_->timestamp,
-                                 &ctx_->counters};
+                                 &ctx_->counters, view_};
     // Stack allocate evaluated arguments when there's a small number of them.
     if (function.arguments_.size() <= 8) {
       TypedValue arguments[8] = {
@@ -538,74 +542,50 @@ class ExpressionEvaluator : public ExpressionVisitor<TypedValue> {
   }
 
  private:
-  storage::Property GetProperty(PropertyIx prop) {
-    return ctx_->properties[prop.ix];
-  }
-
-  storage::Label GetLabel(LabelIx label) {
-    return ctx_->labels[label.ix];
-  }
-
-  // If the given TypedValue contains accessors, switch them to New or Old,
-  // depending on use_new_ flag.
-  void SwitchAccessors(TypedValue &value) {
-    switch (value.type()) {
-      case TypedValue::Type::Vertex: {
-        auto &vertex = value.ValueVertex();
-        switch (view_) {
-          case storage::View::NEW:
-            vertex.SwitchNew();
-            break;
-          case storage::View::OLD:
-            vertex.SwitchOld();
-            break;
-        }
-        break;
+  template <class TRecordAccessor>
+  PropertyValue GetProperty(const TRecordAccessor &record_accessor,
+                            PropertyIx prop) {
+    auto maybe_prop =
+        record_accessor.GetProperty(view_, ctx_->properties[prop.ix]);
+    if (maybe_prop.HasError()) {
+      switch (maybe_prop.GetError()) {
+        case storage::Error::DELETED_OBJECT:
+          throw QueryRuntimeException(
+              "Trying to get a property from a deleted object.");
+        case storage::Error::SERIALIZATION_ERROR:
+        case storage::Error::VERTEX_HAS_EDGES:
+          throw QueryRuntimeException(
+              "Unexpected error when getting a property.");
       }
-      case TypedValue::Type::Edge: {
-        auto &edge = value.ValueEdge();
-        switch (view_) {
-          case storage::View::NEW:
-            edge.SwitchNew();
-            break;
-          case storage::View::OLD:
-            edge.SwitchOld();
-            break;
-        }
-        break;
-      }
-      case TypedValue::Type::List: {
-        auto &list = value.ValueList();
-        for (auto &list_value : list) SwitchAccessors(list_value);
-        break;
-      }
-      case TypedValue::Type::Map: {
-        auto &map = value.ValueMap();
-        for (auto &kv : map) SwitchAccessors(kv.second);
-        break;
-      }
-      case TypedValue::Type::Path:
-        switch (view_) {
-          case storage::View::NEW:
-            value.ValuePath().SwitchNew();
-            break;
-          case storage::View::OLD:
-            value.ValuePath().SwitchOld();
-            break;
-        }
-      case TypedValue::Type::Null:
-      case TypedValue::Type::Bool:
-      case TypedValue::Type::String:
-      case TypedValue::Type::Int:
-      case TypedValue::Type::Double:
-        break;
     }
+    return *maybe_prop;
   }
 
+  template <class TRecordAccessor>
+  PropertyValue GetProperty(const TRecordAccessor &record_accessor,
+                            const std::string_view &name) {
+    auto maybe_prop =
+        record_accessor.GetProperty(view_, dba_->NameToProperty(name));
+    if (maybe_prop.HasError()) {
+      switch (maybe_prop.GetError()) {
+        case storage::Error::DELETED_OBJECT:
+          throw QueryRuntimeException(
+              "Trying to get a property from a deleted object.");
+        case storage::Error::SERIALIZATION_ERROR:
+        case storage::Error::VERTEX_HAS_EDGES:
+          throw QueryRuntimeException(
+              "Unexpected error when getting a property.");
+      }
+    }
+    return *maybe_prop;
+  }
+
+  storage::Label GetLabel(LabelIx label) { return ctx_->labels[label.ix]; }
+
   Frame *frame_;
   const SymbolTable *symbol_table_;
   const EvaluationContext *ctx_;
-  database::GraphDbAccessor *dba_;
+  DbAccessor *dba_;
   // which switching approach should be used when evaluating
   storage::View view_;
 };
diff --git a/src/query/interpreter.cpp b/src/query/interpreter.cpp
index 2a597572b..e58558784 100644
--- a/src/query/interpreter.cpp
+++ b/src/query/interpreter.cpp
@@ -5,7 +5,7 @@
 #include <glog/logging.h>
 
 #include "auth/auth.hpp"
-#ifdef MG_SINGLE_NODE
+#ifndef MG_SINGLE_NODE_HA
 #include "database/single_node/dump.hpp"
 #endif
 #include "glue/auth.hpp"
@@ -37,12 +37,12 @@ DEFINE_VALIDATED_int32(query_plan_cache_ttl, 60,
 
 namespace query {
 
-#ifdef MG_SINGLE_NODE
+#ifndef MG_SINGLE_NODE_HA
 namespace {
 
 class DumpClosure final {
  public:
-  explicit DumpClosure(database::GraphDbAccessor *dba) : dump_generator_(dba) {}
+  explicit DumpClosure(DbAccessor *dba) : dump_generator_(dba) {}
 
   // Please note that this copy constructor actually moves the other object. We
   // want this because lambdas are not movable, i.e. its move constructor
@@ -96,13 +96,13 @@ class SingleNodeLogicalPlan final : public LogicalPlan {
 Interpreter::CachedPlan::CachedPlan(std::unique_ptr<LogicalPlan> plan)
     : plan_(std::move(plan)) {}
 
-void Interpreter::PrettyPrintPlan(const database::GraphDbAccessor &dba,
+void Interpreter::PrettyPrintPlan(const DbAccessor &dba,
                                   const plan::LogicalOperator *plan_root,
                                   std::ostream *out) {
   plan::PrettyPrint(dba, plan_root, out);
 }
 
-std::string Interpreter::PlanToJson(const database::GraphDbAccessor &dba,
+std::string Interpreter::PlanToJson(const DbAccessor &dba,
                                     const plan::LogicalOperator *plan_root) {
   return plan::PlanToJson(dba, plan_root).dump();
 }
@@ -120,7 +120,7 @@ TypedValue EvaluateOptionalExpression(Expression *expression,
 
 Callback HandleAuthQuery(AuthQuery *auth_query, auth::Auth *auth,
                          const Parameters &parameters,
-                         database::GraphDbAccessor *db_accessor) {
+                         DbAccessor *db_accessor) {
   // Empty frame for evaluation of password expression. This is OK since
   // password should be either null or string literal and it's evaluation
   // should not depend on frame.
@@ -417,7 +417,7 @@ Callback HandleAuthQuery(AuthQuery *auth_query, auth::Auth *auth,
 Callback HandleStreamQuery(StreamQuery *stream_query,
                            integrations::kafka::Streams *streams,
                            const Parameters &parameters,
-                           database::GraphDbAccessor *db_accessor) {
+                           DbAccessor *db_accessor) {
   // Empty frame and symbol table for evaluation of expressions. This is OK
   // since all expressions should be literals or parameter lookups.
   Frame frame(0);
@@ -580,12 +580,12 @@ Callback HandleStreamQuery(StreamQuery *stream_query,
 
 Callback HandleIndexQuery(IndexQuery *index_query,
                           std::function<void()> invalidate_plan_cache,
-                          database::GraphDbAccessor *db_accessor) {
-  auto label = db_accessor->Label(index_query->label_.name);
+                          DbAccessor *db_accessor) {
+  auto label = db_accessor->NameToLabel(index_query->label_.name);
   std::vector<storage::Property> properties;
   properties.reserve(index_query->properties_.size());
   for (const auto &prop : index_query->properties_) {
-    properties.push_back(db_accessor->Property(prop.name));
+    properties.push_back(db_accessor->NameToProperty(prop.name));
   }
 
   if (properties.size() > 1) {
@@ -595,11 +595,15 @@ Callback HandleIndexQuery(IndexQuery *index_query,
   Callback callback;
   switch (index_query->action_) {
     case IndexQuery::Action::CREATE:
-      callback.fn = [label, properties, db_accessor,
-                     invalidate_plan_cache] {
+      callback.fn = [label, properties, db_accessor, invalidate_plan_cache] {
+#ifdef MG_SINGLE_NODE_V2
+        CHECK(properties.size() == 1);
+        db_accessor->CreateIndex(label, properties[0]);
+        invalidate_plan_cache();
+#else
         try {
           CHECK(properties.size() == 1);
-          db_accessor->BuildIndex(label, properties[0]);
+          db_accessor->CreateIndex(label, properties[0]);
           invalidate_plan_cache();
         } catch (const database::ConstraintViolationException &e) {
           throw QueryRuntimeException(e.what());
@@ -608,26 +612,32 @@ Callback HandleIndexQuery(IndexQuery *index_query,
         } catch (const database::TransactionException &e) {
           throw QueryRuntimeException(e.what());
         }
+#endif
         return std::vector<std::vector<TypedValue>>();
       };
       return callback;
     case IndexQuery::Action::DROP:
       callback.fn = [label, properties, db_accessor, invalidate_plan_cache] {
+#ifdef MG_SINGLE_NODE_V2
+        CHECK(properties.size() == 1);
+        db_accessor->DropIndex(label, properties[0]);
+        invalidate_plan_cache();
+#else
         try {
           CHECK(properties.size() == 1);
-          db_accessor->DeleteIndex(label, properties[0]);
+          db_accessor->DropIndex(label, properties[0]);
           invalidate_plan_cache();
         } catch (const database::TransactionException &e) {
           throw QueryRuntimeException(e.what());
         }
+#endif
         return std::vector<std::vector<TypedValue>>();
       };
       return callback;
   }
 }
 
-Callback HandleInfoQuery(InfoQuery *info_query,
-                         database::GraphDbAccessor *db_accessor) {
+Callback HandleInfoQuery(InfoQuery *info_query, DbAccessor *db_accessor) {
   Callback callback;
   switch (info_query->info_type_) {
     case InfoQuery::InfoType::STORAGE:
@@ -662,6 +672,9 @@ Callback HandleInfoQuery(InfoQuery *info_query,
 #endif
       break;
     case InfoQuery::InfoType::INDEX:
+#ifdef MG_SINGLE_NODE_V2
+      throw utils::NotYetImplemented("IndexInfo");
+#else
       callback.header = {"created index"};
       callback.fn = [db_accessor] {
         auto info = db_accessor->IndexInfo();
@@ -673,7 +686,11 @@ Callback HandleInfoQuery(InfoQuery *info_query,
         return results;
       };
       break;
+#endif
     case InfoQuery::InfoType::CONSTRAINT:
+#ifdef MG_SINGLE_NODE_V2
+      throw utils::NotYetImplemented("ConstraintInfo");
+#else
       callback.header = {"constraint type", "label", "properties"};
       callback.fn = [db_accessor] {
         std::vector<std::vector<TypedValue>> results;
@@ -681,11 +698,12 @@ Callback HandleInfoQuery(InfoQuery *info_query,
           std::vector<std::string> property_names(e.properties.size());
           std::transform(e.properties.begin(), e.properties.end(),
                          property_names.begin(), [&db_accessor](const auto &p) {
-                           return db_accessor->PropertyName(p);
+                           return db_accessor->PropertyToName(p);
                          });
 
           std::vector<TypedValue> constraint{
-              TypedValue("unique"), TypedValue(db_accessor->LabelName(e.label)),
+              TypedValue("unique"),
+              TypedValue(db_accessor->LabelToName(e.label)),
               TypedValue(utils::Join(property_names, ","))};
 
           results.emplace_back(constraint);
@@ -693,6 +711,7 @@ Callback HandleInfoQuery(InfoQuery *info_query,
         return results;
       };
       break;
+#endif
     case InfoQuery::InfoType::RAFT:
 #if defined(MG_SINGLE_NODE_HA)
       callback.header = {"info", "value"};
@@ -700,8 +719,9 @@ Callback HandleInfoQuery(InfoQuery *info_query,
         std::vector<std::vector<TypedValue>> results(
             {{TypedValue("is_leader"),
               TypedValue(db_accessor->raft()->IsLeader())},
-             {TypedValue("term_id"), TypedValue(static_cast<int64_t>(
-                                         db_accessor->raft()->TermId()))}});
+             {TypedValue("term_id"),
+              TypedValue(static_cast<int64_t>(
+                  db_accessor->raft()->TermId()))}});
         return results;
       };
       // It is critical to abort this query because it can be executed on
@@ -716,12 +736,13 @@ Callback HandleInfoQuery(InfoQuery *info_query,
 }
 
 Callback HandleConstraintQuery(ConstraintQuery *constraint_query,
-                               database::GraphDbAccessor *db_accessor) {
+                               DbAccessor *db_accessor) {
   std::vector<storage::Property> properties;
-  auto label = db_accessor->Label(constraint_query->constraint_.label.name);
+  auto label =
+      db_accessor->NameToLabel(constraint_query->constraint_.label.name);
   properties.reserve(constraint_query->constraint_.properties.size());
   for (const auto &prop : constraint_query->constraint_.properties) {
-    properties.push_back(db_accessor->Property(prop.name));
+    properties.push_back(db_accessor->NameToProperty(prop.name));
   }
 
   Callback callback;
@@ -731,8 +752,34 @@ Callback HandleConstraintQuery(ConstraintQuery *constraint_query,
         case Constraint::Type::NODE_KEY:
           throw utils::NotYetImplemented("Node key constraints");
         case Constraint::Type::EXISTS:
+#ifdef MG_SINGLE_NODE_V2
+          if (properties.empty() || properties.size() > 1) {
+            throw SyntaxException(
+                "Exactly one property must be used for existence constraints.");
+          }
+          callback.fn = [label, properties, db_accessor] {
+            auto res =
+                db_accessor->CreateExistenceConstraint(label, properties[0]);
+            if (res.HasError()) {
+              auto violation = res.GetError();
+              auto label_name = db_accessor->LabelToName(violation.label);
+              auto property_name =
+                  db_accessor->PropertyToName(violation.property);
+              throw QueryRuntimeException(
+                  "Unable to create a constraint :{}({}), because an existing "
+                  "node violates it.",
+                  label_name, property_name);
+            }
+            return std::vector<std::vector<TypedValue>>();
+          };
+          break;
+#else
           throw utils::NotYetImplemented("Existence constraints");
+#endif
         case Constraint::Type::UNIQUE:
+#ifdef MG_SINGLE_NODE_V2
+          throw utils::NotYetImplemented("Unique constraints");
+#else
           callback.fn = [label, properties, db_accessor] {
             try {
               db_accessor->BuildUniqueConstraint(label, properties);
@@ -746,6 +793,7 @@ Callback HandleConstraintQuery(ConstraintQuery *constraint_query,
             }
           };
           break;
+#endif
       }
     } break;
     case ConstraintQuery::ActionType::DROP: {
@@ -753,8 +801,23 @@ Callback HandleConstraintQuery(ConstraintQuery *constraint_query,
         case Constraint::Type::NODE_KEY:
           throw utils::NotYetImplemented("Node key constraints");
         case Constraint::Type::EXISTS:
+#ifdef MG_SINGLE_NODE_V2
+          if (properties.empty() || properties.size() > 1) {
+            throw SyntaxException(
+                "Exactly one property must be used for existence constraints.");
+          }
+          callback.fn = [label, properties, db_accessor] {
+            db_accessor->DropExistenceConstraint(label, properties[0]);
+            return std::vector<std::vector<TypedValue>>();
+          };
+          break;
+#else
           throw utils::NotYetImplemented("Existence constraints");
+#endif
         case Constraint::Type::UNIQUE:
+#ifdef MG_SINGLE_NODE_V2
+          throw utils::NotYetImplemented("Unique constraints");
+#else
           callback.fn = [label, properties, db_accessor] {
             try {
               db_accessor->DeleteUniqueConstraint(label, properties);
@@ -764,6 +827,7 @@ Callback HandleConstraintQuery(ConstraintQuery *constraint_query,
             }
           };
           break;
+#endif
       }
     } break;
   }
@@ -773,7 +837,7 @@ Callback HandleConstraintQuery(ConstraintQuery *constraint_query,
 Interpreter::Interpreter() : is_tsc_available_(utils::CheckAvailableTSC()) {}
 
 Interpreter::Results Interpreter::operator()(
-    const std::string &query_string, database::GraphDbAccessor &db_accessor,
+    const std::string &query_string, DbAccessor *db_accessor,
     const std::map<std::string, PropertyValue> &params,
     bool in_explicit_transaction, utils::MemoryResource *execution_memory) {
   AstStorage ast_storage;
@@ -781,8 +845,8 @@ Interpreter::Results Interpreter::operator()(
   std::map<std::string, TypedValue> summary;
 
   utils::Timer parsing_timer;
-  auto queries = StripAndParseQuery(query_string, &parameters, &ast_storage,
-                                    &db_accessor, params);
+  auto queries =
+      StripAndParseQuery(query_string, &parameters, &ast_storage, params);
   frontend::StrippedQuery &stripped_query = queries.first;
   ParsedQuery &parsed_query = queries.second;
   auto parsing_time = parsing_timer.Elapsed();
@@ -804,7 +868,7 @@ Interpreter::Results Interpreter::operator()(
 #ifdef MG_SINGLE_NODE_HA
   {
     InfoQuery *info_query = nullptr;
-    if (!db_accessor.raft()->IsLeader() &&
+    if (!db_accessor->raft()->IsLeader() &&
         (!(info_query = utils::Downcast<InfoQuery>(parsed_query.query)) ||
          info_query->info_type_ != InfoQuery::InfoType::RAFT)) {
       throw raft::CantExecuteQueries();
@@ -814,7 +878,7 @@ Interpreter::Results Interpreter::operator()(
 
   if (auto *cypher_query = utils::Downcast<CypherQuery>(parsed_query.query)) {
     plan = CypherQueryToPlan(stripped_query.hash(), cypher_query,
-                             std::move(ast_storage), parameters, &db_accessor);
+                             std::move(ast_storage), parameters, db_accessor);
     auto planning_time = planning_timer.Elapsed();
     summary["planning_time"] = planning_time.count();
     summary["cost_estimate"] = plan->cost();
@@ -831,7 +895,7 @@ Interpreter::Results Interpreter::operator()(
                            .first);
     }
 
-    return Results(&db_accessor, parameters, plan, std::move(output_symbols),
+    return Results(db_accessor, parameters, plan, std::move(output_symbols),
                    std::move(header), std::move(summary),
                    parsed_query.required_privileges, execution_memory);
   }
@@ -866,7 +930,7 @@ Interpreter::Results Interpreter::operator()(
     // and the one that's executed when the query is ran standalone.
     auto queries =
         StripAndParseQuery(query_string.substr(kExplainQueryStart.size()),
-                           &parameters, &ast_storage, &db_accessor, params);
+                           &parameters, &ast_storage, params);
     frontend::StrippedQuery &stripped_query = queries.first;
     ParsedQuery &parsed_query = queries.second;
     auto *cypher_query = utils::Downcast<CypherQuery>(parsed_query.query);
@@ -874,10 +938,10 @@ Interpreter::Results Interpreter::operator()(
         << "Cypher grammar should not allow other queries in EXPLAIN";
     std::shared_ptr<CachedPlan> cypher_query_plan =
         CypherQueryToPlan(stripped_query.hash(), cypher_query,
-                          std::move(ast_storage), parameters, &db_accessor);
+                          std::move(ast_storage), parameters, db_accessor);
 
     std::stringstream printed_plan;
-    PrettyPrintPlan(db_accessor, &cypher_query_plan->plan(), &printed_plan);
+    PrettyPrintPlan(*db_accessor, &cypher_query_plan->plan(), &printed_plan);
 
     std::vector<std::vector<TypedValue>> printed_plan_rows;
     for (const auto &row :
@@ -885,7 +949,7 @@ Interpreter::Results Interpreter::operator()(
       printed_plan_rows.push_back(std::vector<TypedValue>{TypedValue(row)});
     }
 
-    summary["explain"] = PlanToJson(db_accessor, &cypher_query_plan->plan());
+    summary["explain"] = PlanToJson(*db_accessor, &cypher_query_plan->plan());
 
     SymbolTable symbol_table;
     auto query_plan_symbol = symbol_table.CreateSymbol("QUERY PLAN", false);
@@ -902,7 +966,7 @@ Interpreter::Results Interpreter::operator()(
 
     std::vector<std::string> header{query_plan_symbol.name()};
 
-    return Results(&db_accessor, parameters, plan, std::move(output_symbols),
+    return Results(db_accessor, parameters, plan, std::move(output_symbols),
                    std::move(header), std::move(summary),
                    parsed_query.required_privileges, execution_memory);
   }
@@ -926,7 +990,7 @@ Interpreter::Results Interpreter::operator()(
     // "metaqueries" for explain queries
     auto queries =
         StripAndParseQuery(query_string.substr(kProfileQueryStart.size()),
-                           &parameters, &ast_storage, &db_accessor, params);
+                           &parameters, &ast_storage, params);
     frontend::StrippedQuery &stripped_query = queries.first;
     ParsedQuery &parsed_query = queries.second;
     auto *cypher_query = utils::Downcast<CypherQuery>(parsed_query.query);
@@ -934,7 +998,7 @@ Interpreter::Results Interpreter::operator()(
         << "Cypher grammar should not allow other queries in PROFILE";
     auto cypher_query_plan =
         CypherQueryToPlan(stripped_query.hash(), cypher_query,
-                          std::move(ast_storage), parameters, &db_accessor);
+                          std::move(ast_storage), parameters, db_accessor);
 
     // Copy the symbol table and add our own symbols (used by the `OutputTable`
     // operator below)
@@ -984,16 +1048,14 @@ Interpreter::Results Interpreter::operator()(
     auto planning_time = planning_timer.Elapsed();
     summary["planning_time"] = planning_time.count();
 
-    return Results(&db_accessor, parameters, plan, std::move(output_symbols),
+    return Results(db_accessor, parameters, plan, std::move(output_symbols),
                    std::move(header), std::move(summary),
                    parsed_query.required_privileges, execution_memory,
                    /* is_profile_query */ true, /* should_abort_query */ true);
   }
 
   if (auto *dump_query = utils::Downcast<DumpQuery>(parsed_query.query)) {
-#ifdef MG_SINGLE_NODE
-    database::CypherDumpGenerator dump(&db_accessor);
-
+#ifndef MG_SINGLE_NODE_HA
     SymbolTable symbol_table;
     auto query_symbol = symbol_table.CreateSymbol("QUERY", false);
 
@@ -1001,13 +1063,13 @@ Interpreter::Results Interpreter::operator()(
     std::vector<std::string> header = {query_symbol.name()};
 
     auto output_plan = std::make_unique<plan::OutputTableStream>(
-        output_symbols, DumpClosure(&db_accessor));
+        output_symbols, DumpClosure(db_accessor));
     plan = std::make_shared<CachedPlan>(std::make_unique<SingleNodeLogicalPlan>(
         std::move(output_plan), 0.0, AstStorage{}, symbol_table));
 
     summary["planning_time"] = planning_timer.Elapsed().count();
 
-    return Results(&db_accessor, parameters, plan, std::move(output_symbols),
+    return Results(db_accessor, parameters, plan, std::move(output_symbols),
                    std::move(header), std::move(summary),
                    parsed_query.required_privileges, execution_memory,
                    /* is_profile_query */ false,
@@ -1030,7 +1092,7 @@ Interpreter::Results Interpreter::operator()(
       }
     };
     callback =
-        HandleIndexQuery(index_query, invalidate_plan_cache, &db_accessor);
+        HandleIndexQuery(index_query, invalidate_plan_cache, db_accessor);
   } else if (auto *auth_query =
                  utils::Downcast<AuthQuery>(parsed_query.query)) {
 #ifdef MG_SINGLE_NODE_HA
@@ -1041,7 +1103,7 @@ Interpreter::Results Interpreter::operator()(
     if (in_explicit_transaction) {
       throw UserModificationInMulticommandTxException();
     }
-    callback = HandleAuthQuery(auth_query, auth_, parameters, &db_accessor);
+    callback = HandleAuthQuery(auth_query, auth_, parameters, db_accessor);
 #endif
   } else if (auto *stream_query =
                  utils::Downcast<StreamQuery>(parsed_query.query)) {
@@ -1053,14 +1115,14 @@ Interpreter::Results Interpreter::operator()(
       throw StreamClauseInMulticommandTxException();
     }
     callback = HandleStreamQuery(stream_query, kafka_streams_, parameters,
-                                 &db_accessor);
+                                 db_accessor);
 #endif
   } else if (auto *info_query =
                  utils::Downcast<InfoQuery>(parsed_query.query)) {
-    callback = HandleInfoQuery(info_query, &db_accessor);
+    callback = HandleInfoQuery(info_query, db_accessor);
   } else if (auto *constraint_query =
                  utils::Downcast<ConstraintQuery>(parsed_query.query)) {
-    callback = HandleConstraintQuery(constraint_query, &db_accessor);
+    callback = HandleConstraintQuery(constraint_query, db_accessor);
   } else {
     LOG(FATAL) << "Should not get here -- unknown query type!";
   }
@@ -1081,7 +1143,7 @@ Interpreter::Results Interpreter::operator()(
   summary["planning_time"] = planning_time.count();
   summary["cost_estimate"] = 0.0;
 
-  return Results(&db_accessor, parameters, plan, std::move(output_symbols),
+  return Results(db_accessor, parameters, plan, std::move(output_symbols),
                  callback.header, std::move(summary),
                  parsed_query.required_privileges, execution_memory,
                  /* is_profile_query */ false, callback.should_abort_query);
@@ -1089,7 +1151,7 @@ Interpreter::Results Interpreter::operator()(
 
 std::shared_ptr<Interpreter::CachedPlan> Interpreter::CypherQueryToPlan(
     HashType query_hash, CypherQuery *query, AstStorage ast_storage,
-    const Parameters &parameters, database::GraphDbAccessor *db_accessor) {
+    const Parameters &parameters, DbAccessor *db_accessor) {
   auto plan_cache_access = plan_cache_.access();
   auto it = plan_cache_access.find(query_hash);
   if (it != plan_cache_access.end()) {
@@ -1108,8 +1170,7 @@ std::shared_ptr<Interpreter::CachedPlan> Interpreter::CypherQueryToPlan(
 
 Interpreter::ParsedQuery Interpreter::ParseQuery(
     const std::string &stripped_query, const std::string &original_query,
-    const frontend::ParsingContext &context, AstStorage *ast_storage,
-    database::GraphDbAccessor *db_accessor) {
+    const frontend::ParsingContext &context, AstStorage *ast_storage) {
   if (!context.is_query_cached) {
     // Parse original query into antlr4 AST.
     auto parser = [&] {
@@ -1167,7 +1228,7 @@ Interpreter::ParsedQuery Interpreter::ParseQuery(
 std::pair<frontend::StrippedQuery, Interpreter::ParsedQuery>
 Interpreter::StripAndParseQuery(
     const std::string &query_string, Parameters *parameters,
-    AstStorage *ast_storage, database::GraphDbAccessor *db_accessor,
+    AstStorage *ast_storage,
     const std::map<std::string, PropertyValue> &params) {
   frontend::StrippedQuery stripped_query(query_string);
 
@@ -1185,14 +1246,14 @@ Interpreter::StripAndParseQuery(
   parsing_context.is_query_cached = true;
 
   auto parsed_query = ParseQuery(stripped_query.query(), query_string,
-                                 parsing_context, ast_storage, db_accessor);
+                                 parsing_context, ast_storage);
 
   return {std::move(stripped_query), std::move(parsed_query)};
 }
 
 std::unique_ptr<LogicalPlan> Interpreter::MakeLogicalPlan(
     CypherQuery *query, AstStorage ast_storage, const Parameters &parameters,
-    database::GraphDbAccessor *db_accessor) {
+    DbAccessor *db_accessor) {
   auto vertex_counts = plan::MakeVertexCountCache(db_accessor);
 
   auto symbol_table = MakeSymbolTable(query);
diff --git a/src/query/interpreter.hpp b/src/query/interpreter.hpp
index 21e1d8b13..2e941e056 100644
--- a/src/query/interpreter.hpp
+++ b/src/query/interpreter.hpp
@@ -2,9 +2,8 @@
 
 #include <gflags/gflags.h>
 
-#include "database/graph_db.hpp"
-#include "database/graph_db_accessor.hpp"
 #include "query/context.hpp"
+#include "query/db_accessor.hpp"
 #include "query/frontend/ast/ast.hpp"
 #include "query/frontend/ast/cypher_main_visitor.hpp"
 #include "query/frontend/stripped.hpp"
@@ -82,8 +81,7 @@ class Interpreter {
    */
   class Results {
     friend Interpreter;
-    Results(database::GraphDbAccessor *db_accessor,
-            const query::Parameters &parameters,
+    Results(DbAccessor *db_accessor, const query::Parameters &parameters,
             std::shared_ptr<CachedPlan> plan,
             std::vector<Symbol> output_symbols, std::vector<std::string> header,
             std::map<std::string, TypedValue> summary,
@@ -216,8 +214,7 @@ class Interpreter {
    * Generates an Results object for the parameters. The resulting object
    * can be Pulled with its results written to an arbitrary stream.
    */
-  virtual Results operator()(const std::string &query,
-                             database::GraphDbAccessor &db_accessor,
+  virtual Results operator()(const std::string &query, DbAccessor *db_accessor,
                              const std::map<std::string, PropertyValue> &params,
                              bool in_explicit_transaction,
                              utils::MemoryResource *execution_memory);
@@ -228,20 +225,20 @@ class Interpreter {
  protected:
   std::pair<frontend::StrippedQuery, ParsedQuery> StripAndParseQuery(
       const std::string &, Parameters *, AstStorage *ast_storage,
-      database::GraphDbAccessor *,
       const std::map<std::string, PropertyValue> &);
 
   // high level tree -> logical plan
   // AstStorage and SymbolTable may be modified during planning. The created
   // LogicalPlan must take ownership of AstStorage and SymbolTable.
-  virtual std::unique_ptr<LogicalPlan> MakeLogicalPlan(
-      CypherQuery *, AstStorage, const Parameters &,
-      database::GraphDbAccessor *);
+  virtual std::unique_ptr<LogicalPlan> MakeLogicalPlan(CypherQuery *,
+                                                       AstStorage,
+                                                       const Parameters &,
+                                                       DbAccessor *);
 
-  virtual void PrettyPrintPlan(const database::GraphDbAccessor &,
+  virtual void PrettyPrintPlan(const DbAccessor &,
                                const plan::LogicalOperator *, std::ostream *);
 
-  virtual std::string PlanToJson(const database::GraphDbAccessor &,
+  virtual std::string PlanToJson(const DbAccessor &,
                                  const plan::LogicalOperator *);
 
  private:
@@ -290,16 +287,17 @@ class Interpreter {
   bool is_tsc_available_;
 
   // high level tree -> CachedPlan
-  std::shared_ptr<CachedPlan> CypherQueryToPlan(
-      HashType query_hash, CypherQuery *query, AstStorage ast_storage,
-      const Parameters &parameters, database::GraphDbAccessor *db_accessor);
+  std::shared_ptr<CachedPlan> CypherQueryToPlan(HashType query_hash,
+                                                CypherQuery *query,
+                                                AstStorage ast_storage,
+                                                const Parameters &parameters,
+                                                DbAccessor *db_accessor);
 
   // stripped query -> high level tree
   ParsedQuery ParseQuery(const std::string &stripped_query,
                          const std::string &original_query,
                          const frontend::ParsingContext &context,
-                         AstStorage *ast_storage,
-                         database::GraphDbAccessor *db_accessor);
+                         AstStorage *ast_storage);
 };
 
 }  // namespace query
diff --git a/src/query/path.hpp b/src/query/path.hpp
index 89b288c97..fcabc6a74 100644
--- a/src/query/path.hpp
+++ b/src/query/path.hpp
@@ -5,8 +5,7 @@
 
 #include "glog/logging.h"
 
-#include "storage/edge_accessor.hpp"
-#include "storage/vertex_accessor.hpp"
+#include "query/db_accessor.hpp"
 #include "utils/memory.hpp"
 #include "utils/pmr/vector.hpp"
 
@@ -144,7 +143,7 @@ class Path {
         << "Attempting to stream out an invalid path";
     os << path.vertices_[0];
     for (int i = 0; i < static_cast<int>(path.edges_.size()); i++) {
-      bool arrow_to_left = path.vertices_[i] == path.edges_[i].to();
+      bool arrow_to_left = path.vertices_[i] == path.edges_[i].To();
       if (arrow_to_left) os << "<";
       os << "-" << path.edges_[i] << "-";
       if (!arrow_to_left) os << ">";
@@ -154,18 +153,6 @@ class Path {
     return os;
   }
 
-  /// Calls SwitchNew on all the elements of the path.
-  void SwitchNew() {
-    for (auto &v : vertices_) v.SwitchNew();
-    for (auto &e : edges_) e.SwitchNew();
-  }
-
-  /// Calls SwitchNew on all the elements of the path.
-  void SwitchOld() {
-    for (auto &v : vertices_) v.SwitchOld();
-    for (auto &e : edges_) e.SwitchOld();
-  }
-
  private:
   // Contains all the vertices in the path.
   utils::pmr::vector<VertexAccessor> vertices_;
diff --git a/src/query/plan/operator.cpp b/src/query/plan/operator.cpp
index d4935cee0..384b48a5b 100644
--- a/src/query/plan/operator.cpp
+++ b/src/query/plan/operator.cpp
@@ -16,7 +16,6 @@
 #include <cppitertools/chain.hpp>
 #include <cppitertools/imap.hpp>
 
-#include "database/graph_db_accessor.hpp"
 #include "query/context.hpp"
 #include "query/exceptions.hpp"
 #include "query/frontend/ast/ast.hpp"
@@ -123,7 +122,21 @@ VertexAccessor &CreateLocalVertex(const NodeCreationInfo &node_info,
                                   const ExecutionContext &context) {
   auto &dba = *context.db_accessor;
   auto new_node = dba.InsertVertex();
-  for (auto label : node_info.labels) new_node.add_label(label);
+  for (auto label : node_info.labels) {
+    auto maybe_error = new_node.AddLabel(label);
+    if (maybe_error.HasError()) {
+      switch (maybe_error.GetError()) {
+        case storage::Error::SERIALIZATION_ERROR:
+          throw QueryRuntimeException(
+              "Can't serialize due to concurrent operations.");
+        case storage::Error::DELETED_OBJECT:
+          throw QueryRuntimeException(
+              "Trying to set a label on a deleted node.");
+        case storage::Error::VERTEX_HAS_EDGES:
+          throw QueryRuntimeException("Unexpected error when setting a label.");
+      }
+    }
+  }
   // Evaluator should use the latest accessors, as modified in this query, when
   // setting properties on new nodes.
   ExpressionEvaluator evaluator(frame, context.symbol_table,
@@ -200,14 +213,27 @@ CreateExpand::CreateExpandCursor::CreateExpandCursor(const CreateExpand &self,
 
 namespace {
 
-void CreateEdge(const EdgeCreationInfo &edge_info,
-                database::GraphDbAccessor *dba, VertexAccessor *from,
-                VertexAccessor *to, Frame *frame,
+void CreateEdge(const EdgeCreationInfo &edge_info, DbAccessor *dba,
+                VertexAccessor *from, VertexAccessor *to, Frame *frame,
                 ExpressionEvaluator *evaluator) {
-  EdgeAccessor edge = dba->InsertEdge(*from, *to, edge_info.edge_type);
-  for (auto kv : edge_info.properties)
-    PropsSetChecked(&edge, kv.first, kv.second->Accept(*evaluator));
-  (*frame)[edge_info.symbol] = edge;
+  auto maybe_edge = dba->InsertEdge(from, to, edge_info.edge_type);
+  if (maybe_edge.HasValue()) {
+    auto &edge = *maybe_edge;
+    for (auto kv : edge_info.properties)
+      PropsSetChecked(&edge, kv.first, kv.second->Accept(*evaluator));
+    (*frame)[edge_info.symbol] = edge;
+  } else {
+    switch (maybe_edge.GetError()) {
+      case storage::Error::SERIALIZATION_ERROR:
+        throw QueryRuntimeException(
+            "Can't serialize due to concurrent operations.");
+      case storage::Error::DELETED_OBJECT:
+        throw QueryRuntimeException(
+            "Trying to create an edge on a deleted node.");
+      case storage::Error::VERTEX_HAS_EDGES:
+        throw QueryRuntimeException("Unexpected error when creating an edge.");
+    }
+  }
 }
 
 }  // namespace
@@ -224,16 +250,14 @@ bool CreateExpand::CreateExpandCursor::Pull(Frame &frame,
   auto &v1 = vertex_value.ValueVertex();
 
   // Similarly to CreateNode, newly created edges and nodes should use the
-  // latest accesors.
+  // storage::View::NEW.
+  // E.g. we pickup new properties: `CREATE (n {p: 42}) -[:r {ep: n.p}]-> ()`
   ExpressionEvaluator evaluator(&frame, context.symbol_table,
                                 context.evaluation_context, context.db_accessor,
                                 storage::View::NEW);
-  // E.g. we pickup new properties: `CREATE (n {p: 42}) -[:r {ep: n.p}]-> ()`
-  v1.SwitchNew();
 
   // get the destination vertex (possibly an existing node)
   auto &v2 = OtherVertex(frame, context);
-  v2.SwitchNew();
 
   // create an edge between the two nodes
   auto *dba = context.db_accessor;
@@ -274,8 +298,8 @@ VertexAccessor &CreateExpand::CreateExpandCursor::OtherVertex(
 template <class TVerticesFun>
 class ScanAllCursor : public Cursor {
  public:
-  explicit ScanAllCursor(Symbol output_symbol, UniqueCursorPtr &&input_cursor,
-                         TVerticesFun &&get_vertices)
+  explicit ScanAllCursor(Symbol output_symbol, UniqueCursorPtr input_cursor,
+                         TVerticesFun get_vertices)
       : output_symbol_(output_symbol),
         input_cursor_(std::move(input_cursor)),
         get_vertices_(std::move(get_vertices)) {}
@@ -283,7 +307,7 @@ class ScanAllCursor : public Cursor {
   bool Pull(Frame &frame, ExecutionContext &context) override {
     SCOPED_PROFILE_OP("ScanAll");
 
-    if (context.db_accessor->should_abort()) throw HintedAbortError();
+    if (context.db_accessor->MustAbort()) throw HintedAbortError();
 
     while (!vertices_ || vertices_it_.value() == vertices_.value().end()) {
       if (!input_cursor_->Pull(frame, context)) return false;
@@ -298,7 +322,8 @@ class ScanAllCursor : public Cursor {
       vertices_it_.emplace(vertices_.value().begin());
     }
 
-    frame[output_symbol_] = *vertices_it_.value()++;
+    frame[output_symbol_] = *vertices_it_.value();
+    ++vertices_it_.value();
     return true;
   }
 
@@ -331,7 +356,7 @@ ACCEPT_WITH_INPUT(ScanAll)
 UniqueCursorPtr ScanAll::MakeCursor(utils::MemoryResource *mem) const {
   auto vertices = [this](Frame &, ExecutionContext &context) {
     auto *db = context.db_accessor;
-    return std::make_optional(db->Vertices(view_ == storage::View::NEW));
+    return std::make_optional(db->Vertices(view_));
   };
   return MakeUniqueCursorPtr<ScanAllCursor<decltype(vertices)>>(
       mem, output_symbol_, input_->MakeCursor(mem), std::move(vertices));
@@ -353,8 +378,7 @@ ACCEPT_WITH_INPUT(ScanAllByLabel)
 UniqueCursorPtr ScanAllByLabel::MakeCursor(utils::MemoryResource *mem) const {
   auto vertices = [this](Frame &, ExecutionContext &context) {
     auto *db = context.db_accessor;
-    return std::make_optional(
-        db->Vertices(label_, view_ == storage::View::NEW));
+    return std::make_optional(db->Vertices(view_, label_));
   };
   return MakeUniqueCursorPtr<ScanAllCursor<decltype(vertices)>>(
       mem, output_symbol_, input_->MakeCursor(mem), std::move(vertices));
@@ -380,7 +404,7 @@ UniqueCursorPtr ScanAllByLabelPropertyRange::MakeCursor(
     utils::MemoryResource *mem) const {
   auto vertices = [this](Frame &frame, ExecutionContext &context)
       -> std::optional<decltype(context.db_accessor->Vertices(
-          label_, property_, std::nullopt, std::nullopt, false))> {
+          view_, label_, property_, std::nullopt, std::nullopt))> {
     auto *db = context.db_accessor;
     ExpressionEvaluator evaluator(&frame, context.symbol_table,
                                   context.evaluation_context,
@@ -404,9 +428,8 @@ UniqueCursorPtr ScanAllByLabelPropertyRange::MakeCursor(
     // is treated as not satisfying the filter, so return no vertices.
     if (maybe_lower && maybe_lower->value().IsNull()) return std::nullopt;
     if (maybe_upper && maybe_upper->value().IsNull()) return std::nullopt;
-    return std::make_optional(db->Vertices(label_, property_, maybe_lower,
-                                           maybe_upper,
-                                           view_ == storage::View::NEW));
+    return std::make_optional(
+        db->Vertices(view_, label_, property_, maybe_lower, maybe_upper));
   };
   return MakeUniqueCursorPtr<ScanAllCursor<decltype(vertices)>>(
       mem, output_symbol_, input_->MakeCursor(mem), std::move(vertices));
@@ -431,7 +454,7 @@ UniqueCursorPtr ScanAllByLabelPropertyValue::MakeCursor(
     utils::MemoryResource *mem) const {
   auto vertices = [this](Frame &frame, ExecutionContext &context)
       -> std::optional<decltype(context.db_accessor->Vertices(
-          label_, property_, PropertyValue(), false))> {
+          view_, label_, property_, PropertyValue()))> {
     auto *db = context.db_accessor;
     ExpressionEvaluator evaluator(&frame, context.symbol_table,
                                   context.evaluation_context,
@@ -442,9 +465,8 @@ UniqueCursorPtr ScanAllByLabelPropertyValue::MakeCursor(
       throw QueryRuntimeException("'{}' cannot be used as a property value.",
                                   value.type());
     }
-    return std::make_optional(db->Vertices(label_, property_,
-                                           PropertyValue(value),
-                                           view_ == storage::View::NEW));
+    return std::make_optional(
+        db->Vertices(view_, label_, property_, PropertyValue(value)));
   };
   return MakeUniqueCursorPtr<ScanAllCursor<decltype(vertices)>>(
       mem, output_symbol_, input_->MakeCursor(mem), std::move(vertices));
@@ -458,6 +480,23 @@ bool CheckExistingNode(const VertexAccessor &new_node,
   ExpectType(existing_node_sym, existing_node, TypedValue::Type::Vertex);
   return existing_node.ValueVertex() != new_node;
 }
+
+template <class TEdges>
+auto UnwrapEdgesResult(storage::Result<TEdges> &&result) {
+  if (result.HasError()) {
+    switch (result.GetError()) {
+      case storage::Error::DELETED_OBJECT:
+        throw QueryRuntimeException(
+            "Trying to get relationships of a deleted node.");
+      case storage::Error::VERTEX_HAS_EDGES:
+      case storage::Error::SERIALIZATION_ERROR:
+        throw QueryRuntimeException(
+            "Unexpected error when accessing relationships.");
+    }
+  }
+  return std::move(*result);
+}
+
 }  // namespace
 
 Expand::Expand(const std::shared_ptr<LogicalOperator> &input,
@@ -496,10 +535,10 @@ bool Expand::ExpandCursor::Pull(Frame &frame, ExecutionContext &context) {
     if (self_.common_.existing_node) return;
     switch (direction) {
       case EdgeAtom::Direction::IN:
-        frame[self_.common_.node_symbol] = new_edge.from();
+        frame[self_.common_.node_symbol] = new_edge.From();
         break;
       case EdgeAtom::Direction::OUT:
-        frame[self_.common_.node_symbol] = new_edge.to();
+        frame[self_.common_.node_symbol] = new_edge.To();
         break;
       case EdgeAtom::Direction::BOTH:
         LOG(FATAL) << "Must indicate exact expansion direction here";
@@ -507,7 +546,7 @@ bool Expand::ExpandCursor::Pull(Frame &frame, ExecutionContext &context) {
   };
 
   while (true) {
-    if (context.db_accessor->should_abort()) throw HintedAbortError();
+    if (context.db_accessor->MustAbort()) throw HintedAbortError();
     // attempt to get a value from the incoming edges
     if (in_edges_ && *in_edges_it_ != in_edges_->end()) {
       auto edge = *(*in_edges_it_)++;
@@ -523,7 +562,7 @@ bool Expand::ExpandCursor::Pull(Frame &frame, ExecutionContext &context) {
       // we should do only one expansion for cycles, and it was
       // already done in the block above
       if (self_.common_.direction == EdgeAtom::Direction::BOTH &&
-          edge.is_cycle())
+          edge.IsCycle())
         continue;
       frame[self_.common_.edge_symbol] = edge;
       pull_node(edge, EdgeAtom::Direction::OUT);
@@ -560,7 +599,6 @@ bool Expand::ExpandCursor::InitEdges(Frame &frame, ExecutionContext &context) {
 
     ExpectType(self_.input_symbol_, vertex_value, TypedValue::Type::Vertex);
     auto &vertex = vertex_value.ValueVertex();
-    SwitchAccessor(vertex, self_.view_);
 
     auto direction = self_.common_.direction;
     if (direction == EdgeAtom::Direction::IN ||
@@ -571,11 +609,13 @@ bool Expand::ExpandCursor::InitEdges(Frame &frame, ExecutionContext &context) {
         if (!existing_node.IsNull()) {
           ExpectType(self_.common_.node_symbol, existing_node,
                      TypedValue::Type::Vertex);
-          in_edges_.emplace(vertex.in(existing_node.ValueVertex(),
-                                      &self_.common_.edge_types));
+          in_edges_.emplace(UnwrapEdgesResult(
+              vertex.InEdges(self_.view_, self_.common_.edge_types,
+                             existing_node.ValueVertex())));
         }
       } else {
-        in_edges_.emplace(vertex.in(&self_.common_.edge_types));
+        in_edges_.emplace(UnwrapEdgesResult(
+            vertex.InEdges(self_.view_, self_.common_.edge_types)));
       }
       if (in_edges_) {
         in_edges_it_.emplace(in_edges_->begin());
@@ -590,11 +630,13 @@ bool Expand::ExpandCursor::InitEdges(Frame &frame, ExecutionContext &context) {
         if (!existing_node.IsNull()) {
           ExpectType(self_.common_.node_symbol, existing_node,
                      TypedValue::Type::Vertex);
-          out_edges_.emplace(vertex.out(existing_node.ValueVertex(),
-                                        &self_.common_.edge_types));
+          out_edges_.emplace(UnwrapEdgesResult(
+              vertex.OutEdges(self_.view_, self_.common_.edge_types,
+                              existing_node.ValueVertex())));
         }
       } else {
-        out_edges_.emplace(vertex.out(&self_.common_.edge_types));
+        out_edges_.emplace(UnwrapEdgesResult(
+            vertex.OutEdges(self_.view_, self_.common_.edge_types)));
       }
       if (out_edges_) {
         out_edges_it_.emplace(out_edges_->begin());
@@ -645,6 +687,21 @@ std::vector<Symbol> ExpandVariable::ModifiedSymbols(
 }
 
 namespace {
+
+size_t UnwrapDegreeResult(storage::Result<size_t> maybe_degree) {
+  if (maybe_degree.HasError()) {
+    switch (maybe_degree.GetError()) {
+      case storage::Error::DELETED_OBJECT:
+        throw QueryRuntimeException("Trying to get degree of a deleted node.");
+      case storage::Error::SERIALIZATION_ERROR:
+      case storage::Error::VERTEX_HAS_EDGES:
+        throw QueryRuntimeException(
+            "Unexpected error when getting node degree.");
+    }
+  }
+  return *maybe_degree;
+}
+
 /**
  * Helper function that returns an iterable over
  * <EdgeAtom::Direction, EdgeAccessor> pairs
@@ -661,27 +718,30 @@ auto ExpandFromVertex(const VertexAccessor &vertex,
                       const std::vector<storage::EdgeType> &edge_types,
                       utils::MemoryResource *memory) {
   // wraps an EdgeAccessor into a pair <accessor, direction>
-  auto wrapper = [](EdgeAtom::Direction direction, auto &&vertices) {
+  auto wrapper = [](EdgeAtom::Direction direction, auto &&edges) {
     return iter::imap(
-        [direction](const EdgeAccessor &edge) {
+        [direction](const auto &edge) {
           return std::make_pair(edge, direction);
         },
-        std::forward<decltype(vertices)>(vertices));
+        std::forward<decltype(edges)>(edges));
   };
 
-  // prepare a vector of elements we'll pass to the itertools
-  utils::pmr::vector<decltype(wrapper(direction, vertex.in()))> chain_elements(
-      memory);
+  storage::View view = storage::View::OLD;
+  utils::pmr::vector<decltype(
+      wrapper(direction, *vertex.InEdges(view, edge_types)))>
+      chain_elements(memory);
 
-  if (direction != EdgeAtom::Direction::OUT && vertex.in_degree() > 0) {
-    auto edges = vertex.in(&edge_types);
+  size_t in_degree = UnwrapDegreeResult(vertex.InDegree(view));
+  if (direction != EdgeAtom::Direction::OUT && in_degree > 0) {
+    auto edges = UnwrapEdgesResult(vertex.InEdges(view, edge_types));
     if (edges.begin() != edges.end()) {
       chain_elements.emplace_back(
           wrapper(EdgeAtom::Direction::IN, std::move(edges)));
     }
   }
-  if (direction != EdgeAtom::Direction::IN && vertex.out_degree() > 0) {
-    auto edges = vertex.out(&edge_types);
+  size_t out_degree = UnwrapDegreeResult(vertex.OutDegree(view));
+  if (direction != EdgeAtom::Direction::IN && out_degree > 0) {
+    auto edges = UnwrapEdgesResult(vertex.OutEdges(view, edge_types));
     if (edges.begin() != edges.end()) {
       chain_elements.emplace_back(
           wrapper(EdgeAtom::Direction::OUT, std::move(edges)));
@@ -770,7 +830,7 @@ class ExpandVariableCursor : public Cursor {
     // Input Vertex could be null if it is created by a failed optional match.
     // In those cases we skip that input pull and continue with the next.
     while (true) {
-      if (context.db_accessor->should_abort()) throw HintedAbortError();
+      if (context.db_accessor->MustAbort()) throw HintedAbortError();
       if (!input_cursor_->Pull(frame, context)) return false;
       TypedValue &vertex_value = frame[self_.input_symbol_];
 
@@ -779,7 +839,6 @@ class ExpandVariableCursor : public Cursor {
 
       ExpectType(self_.input_symbol_, vertex_value, TypedValue::Type::Vertex);
       auto &vertex = vertex_value.ValueVertex();
-      SwitchAccessor(vertex, storage::View::OLD);
 
       // Evaluate the upper and lower bounds.
       ExpressionEvaluator evaluator(&frame, context.symbol_table,
@@ -798,7 +857,6 @@ class ExpandVariableCursor : public Cursor {
                                         : std::numeric_limits<int64_t>::max();
 
       if (upper_bound_ > 0) {
-        SwitchAccessor(vertex, storage::View::OLD);
         auto *memory = edges_.get_allocator().GetMemoryResource();
         edges_.emplace_back(ExpandFromVertex(vertex, self_.common_.direction,
                                              self_.common_.edge_types, memory));
@@ -852,7 +910,7 @@ class ExpandVariableCursor : public Cursor {
     // existing_node criterions, so expand in a loop until either the input
     // vertex is exhausted or a valid variable-length expansion is available.
     while (true) {
-      if (context.db_accessor->should_abort()) throw HintedAbortError();
+      if (context.db_accessor->MustAbort()) throw HintedAbortError();
       // pop from the stack while there is stuff to pop and the current
       // level is exhausted
       while (!edges_.empty() && edges_it_.back() == edges_.back().end()) {
@@ -883,8 +941,7 @@ class ExpandVariableCursor : public Cursor {
 
       // if we are here, we have a valid stack,
       // get the edge, increase the relevant iterator
-      std::pair<EdgeAccessor, EdgeAtom::Direction> current_edge =
-          *edges_it_.back()++;
+      auto current_edge = *edges_it_.back()++;
 
       // Check edge-uniqueness.
       bool found_existing =
@@ -897,8 +954,8 @@ class ExpandVariableCursor : public Cursor {
       AppendEdge(current_edge.first, &edges_on_frame);
       VertexAccessor current_vertex =
           current_edge.second == EdgeAtom::Direction::IN
-              ? current_edge.first.from()
-              : current_edge.first.to();
+              ? current_edge.first.From()
+              : current_edge.first.To();
 
       if (self_.common_.existing_node &&
           !CheckExistingNode(current_vertex, self_.common_.node_symbol, frame))
@@ -916,7 +973,6 @@ class ExpandVariableCursor : public Cursor {
       // we are doing depth-first search, so place the current
       // edge's expansions onto the stack, if we should continue to expand
       if (upper_bound_ > static_cast<int64_t>(edges_.size())) {
-        SwitchAccessor(current_vertex, storage::View::OLD);
         auto *memory = edges_.get_allocator().GetMemoryResource();
         edges_.emplace_back(ExpandFromVertex(current_vertex,
                                              self_.common_.direction,
@@ -1001,8 +1057,8 @@ class STShortestPathCursor : public query::plan::Cursor {
     while (true) {
       const auto &last_edge = in_edge.at(last_vertex);
       if (!last_edge) break;
-      last_vertex =
-          last_edge->from_is(last_vertex) ? last_edge->to() : last_edge->from();
+      last_vertex = last_edge->From() == last_vertex ? last_edge->To()
+                                                     : last_edge->From();
       result.emplace_back(*last_edge);
     }
     std::reverse(result.begin(), result.end());
@@ -1010,8 +1066,8 @@ class STShortestPathCursor : public query::plan::Cursor {
     while (true) {
       const auto &last_edge = out_edge.at(last_vertex);
       if (!last_edge) break;
-      last_vertex =
-          last_edge->from_is(last_vertex) ? last_edge->to() : last_edge->from();
+      last_vertex = last_edge->From() == last_vertex ? last_edge->To()
+                                                     : last_edge->From();
       result.emplace_back(*last_edge);
     }
     frame->at(self_.common_.edge_symbol) = std::move(result);
@@ -1032,9 +1088,9 @@ class STShortestPathCursor : public query::plan::Cursor {
         "Expansion condition must evaluate to boolean or null");
   }
 
-  bool FindPath(const database::GraphDbAccessor &dba,
-                const VertexAccessor &source, const VertexAccessor &sink,
-                int64_t lower_bound, int64_t upper_bound, Frame *frame,
+  bool FindPath(const DbAccessor &dba, const VertexAccessor &source,
+                const VertexAccessor &sink, int64_t lower_bound,
+                int64_t upper_bound, Frame *frame,
                 ExpressionEvaluator *evaluator) {
     using utils::Contains;
 
@@ -1069,45 +1125,49 @@ class STShortestPathCursor : public query::plan::Cursor {
     out_edge[sink] = std::nullopt;
 
     while (true) {
-      if (dba.should_abort()) throw HintedAbortError();
+      if (dba.MustAbort()) throw HintedAbortError();
       // Top-down step (expansion from the source).
       ++current_length;
       if (current_length > upper_bound) return false;
 
       for (const auto &vertex : source_frontier) {
         if (self_.common_.direction != EdgeAtom::Direction::IN) {
-          for (const auto &edge : vertex.out(&self_.common_.edge_types)) {
-            if (ShouldExpand(edge.to(), edge, frame, evaluator) &&
-                !Contains(in_edge, edge.to())) {
-              in_edge.emplace(edge.to(), edge);
-              if (Contains(out_edge, edge.to())) {
+          auto out_edges = UnwrapEdgesResult(
+              vertex.OutEdges(storage::View::OLD, self_.common_.edge_types));
+          for (const auto &edge : out_edges) {
+            if (ShouldExpand(edge.To(), edge, frame, evaluator) &&
+                !Contains(in_edge, edge.To())) {
+              in_edge.emplace(edge.To(), edge);
+              if (Contains(out_edge, edge.To())) {
                 if (current_length >= lower_bound) {
-                  ReconstructPath(edge.to(), in_edge, out_edge, frame,
+                  ReconstructPath(edge.To(), in_edge, out_edge, frame,
                                   pull_memory);
                   return true;
                 } else {
                   return false;
                 }
               }
-              source_next.push_back(edge.to());
+              source_next.push_back(edge.To());
             }
           }
         }
         if (self_.common_.direction != EdgeAtom::Direction::OUT) {
-          for (const auto &edge : vertex.in(&self_.common_.edge_types)) {
-            if (ShouldExpand(edge.from(), edge, frame, evaluator) &&
-                !Contains(in_edge, edge.from())) {
-              in_edge.emplace(edge.from(), edge);
-              if (Contains(out_edge, edge.from())) {
+          auto in_edges = UnwrapEdgesResult(
+              vertex.InEdges(storage::View::OLD, self_.common_.edge_types));
+          for (const auto &edge : in_edges) {
+            if (ShouldExpand(edge.From(), edge, frame, evaluator) &&
+                !Contains(in_edge, edge.From())) {
+              in_edge.emplace(edge.From(), edge);
+              if (Contains(out_edge, edge.From())) {
                 if (current_length >= lower_bound) {
-                  ReconstructPath(edge.from(), in_edge, out_edge, frame,
+                  ReconstructPath(edge.From(), in_edge, out_edge, frame,
                                   pull_memory);
                   return true;
                 } else {
                   return false;
                 }
               }
-              source_next.push_back(edge.from());
+              source_next.push_back(edge.From());
             }
           }
         }
@@ -1126,38 +1186,42 @@ class STShortestPathCursor : public query::plan::Cursor {
       // reversed.
       for (const auto &vertex : sink_frontier) {
         if (self_.common_.direction != EdgeAtom::Direction::OUT) {
-          for (const auto &edge : vertex.out(&self_.common_.edge_types)) {
+          auto out_edges = UnwrapEdgesResult(
+              vertex.OutEdges(storage::View::OLD, self_.common_.edge_types));
+          for (const auto &edge : out_edges) {
             if (ShouldExpand(vertex, edge, frame, evaluator) &&
-                !Contains(out_edge, edge.to())) {
-              out_edge.emplace(edge.to(), edge);
-              if (Contains(in_edge, edge.to())) {
+                !Contains(out_edge, edge.To())) {
+              out_edge.emplace(edge.To(), edge);
+              if (Contains(in_edge, edge.To())) {
                 if (current_length >= lower_bound) {
-                  ReconstructPath(edge.to(), in_edge, out_edge, frame,
+                  ReconstructPath(edge.To(), in_edge, out_edge, frame,
                                   pull_memory);
                   return true;
                 } else {
                   return false;
                 }
               }
-              sink_next.push_back(edge.to());
+              sink_next.push_back(edge.To());
             }
           }
         }
         if (self_.common_.direction != EdgeAtom::Direction::IN) {
-          for (const auto &edge : vertex.in(&self_.common_.edge_types)) {
+          auto in_edges = UnwrapEdgesResult(
+              vertex.InEdges(storage::View::OLD, self_.common_.edge_types));
+          for (const auto &edge : in_edges) {
             if (ShouldExpand(vertex, edge, frame, evaluator) &&
-                !Contains(out_edge, edge.from())) {
-              out_edge.emplace(edge.from(), edge);
-              if (Contains(in_edge, edge.from())) {
+                !Contains(out_edge, edge.From())) {
+              out_edge.emplace(edge.From(), edge);
+              if (Contains(in_edge, edge.From())) {
                 if (current_length >= lower_bound) {
-                  ReconstructPath(edge.from(), in_edge, out_edge, frame,
+                  ReconstructPath(edge.From(), in_edge, out_edge, frame,
                                   pull_memory);
                   return true;
                 } else {
                   return false;
                 }
               }
-              sink_next.push_back(edge.from());
+              sink_next.push_back(edge.From());
             }
           }
         }
@@ -1225,18 +1289,20 @@ class SingleSourceShortestPathCursor : public query::plan::Cursor {
     // the "where" condition.
     auto expand_from_vertex = [this, &expand_pair](const auto &vertex) {
       if (self_.common_.direction != EdgeAtom::Direction::IN) {
-        for (const EdgeAccessor &edge : vertex.out(&self_.common_.edge_types))
-          expand_pair(edge, edge.to());
+        auto out_edges = UnwrapEdgesResult(
+            vertex.OutEdges(storage::View::OLD, self_.common_.edge_types));
+        for (const auto &edge : out_edges) expand_pair(edge, edge.To());
       }
       if (self_.common_.direction != EdgeAtom::Direction::OUT) {
-        for (const EdgeAccessor &edge : vertex.in(&self_.common_.edge_types))
-          expand_pair(edge, edge.from());
+        auto in_edges = UnwrapEdgesResult(
+            vertex.InEdges(storage::View::OLD, self_.common_.edge_types));
+        for (const auto &edge : in_edges) expand_pair(edge, edge.From());
       }
     };
 
     // do it all in a loop because we skip some elements
     while (true) {
-      if (context.db_accessor->should_abort()) throw HintedAbortError();
+      if (context.db_accessor->MustAbort()) throw HintedAbortError();
       // if we have nothing to visit on the current depth, switch to next
       if (to_visit_current_.empty()) to_visit_current_.swap(to_visit_next_);
 
@@ -1272,8 +1338,7 @@ class SingleSourceShortestPathCursor : public query::plan::Cursor {
       }
 
       // take the next expansion from the queue
-      std::pair<EdgeAccessor, VertexAccessor> expansion =
-          to_visit_current_.back();
+      auto expansion = to_visit_current_.back();
       to_visit_current_.pop_back();
 
       // create the frame value for the edges
@@ -1284,7 +1349,7 @@ class SingleSourceShortestPathCursor : public query::plan::Cursor {
       while (true) {
         const EdgeAccessor &last_edge = edge_list.back().ValueEdge();
         last_vertex =
-            last_edge.from() == last_vertex ? last_edge.to() : last_edge.from();
+            last_edge.From() == last_vertex ? last_edge.To() : last_edge.From();
         // origin_vertex must be in processed
         const auto &previous_edge = processed_.find(last_vertex)->second;
         if (!previous_edge) break;
@@ -1363,9 +1428,6 @@ class ExpandWeightedShortestPathCursor : public query::plan::Cursor {
     auto expand_pair = [this, &evaluator, &frame, &create_state](
                            EdgeAccessor edge, VertexAccessor vertex,
                            double weight, int depth) {
-      SwitchAccessor(edge, storage::View::OLD);
-      SwitchAccessor(vertex, storage::View::OLD);
-
       auto *memory = evaluator.GetMemoryResource();
       if (self_.filter_lambda_.expression) {
         frame[self_.filter_lambda_.inner_edge_symbol] = edge;
@@ -1404,19 +1466,23 @@ class ExpandWeightedShortestPathCursor : public query::plan::Cursor {
     auto expand_from_vertex = [this, &expand_pair](const VertexAccessor &vertex,
                                                    double weight, int depth) {
       if (self_.common_.direction != EdgeAtom::Direction::IN) {
-        for (const EdgeAccessor &edge : vertex.out(&self_.common_.edge_types)) {
-          expand_pair(edge, edge.to(), weight, depth);
+        auto out_edges = UnwrapEdgesResult(
+            vertex.OutEdges(storage::View::OLD, self_.common_.edge_types));
+        for (const auto &edge : out_edges) {
+          expand_pair(edge, edge.To(), weight, depth);
         }
       }
       if (self_.common_.direction != EdgeAtom::Direction::OUT) {
-        for (const EdgeAccessor &edge : vertex.in(&self_.common_.edge_types)) {
-          expand_pair(edge, edge.from(), weight, depth);
+        auto in_edges = UnwrapEdgesResult(
+            vertex.InEdges(storage::View::OLD, self_.common_.edge_types));
+        for (const auto &edge : in_edges) {
+          expand_pair(edge, edge.From(), weight, depth);
         }
       }
     };
 
     while (true) {
-      if (context.db_accessor->should_abort()) throw HintedAbortError();
+      if (context.db_accessor->MustAbort()) throw HintedAbortError();
       if (pq_.empty()) {
         if (!input_cursor_->Pull(frame, context)) return false;
         const auto &vertex_value = frame[self_.input_symbol_];
@@ -1428,7 +1494,6 @@ class ExpandWeightedShortestPathCursor : public query::plan::Cursor {
           // Skip expansion for such nodes.
           if (node.IsNull()) continue;
         }
-        SwitchAccessor(vertex, storage::View::OLD);
         if (self_.upper_bound_) {
           upper_bound_ =
               EvaluateInt(&evaluator, self_.upper_bound_,
@@ -1456,7 +1521,7 @@ class ExpandWeightedShortestPathCursor : public query::plan::Cursor {
       }
 
       while (!pq_.empty()) {
-        if (context.db_accessor->should_abort()) throw HintedAbortError();
+        if (context.db_accessor->MustAbort()) throw HintedAbortError();
         auto current = pq_.top();
         double current_weight = std::get<0>(current);
         int current_depth = std::get<1>(current);
@@ -1492,9 +1557,9 @@ class ExpandWeightedShortestPathCursor : public query::plan::Cursor {
           const auto &previous_edge =
               previous_.find(create_state(last_vertex, last_depth))->second;
           if (!previous_edge) break;
-          last_vertex = previous_edge->from() == last_vertex
-                            ? previous_edge->to()
-                            : previous_edge->from();
+          last_vertex = previous_edge->From() == last_vertex
+                            ? previous_edge->To()
+                            : previous_edge->From();
           last_depth--;
           edge_list.emplace_back(previous_edge.value());
         }
@@ -1658,10 +1723,10 @@ class ConstructNamedPathCursor : public Cursor {
           // vertices.
           const auto &edges = expansion.ValueList();
           for (const auto &edge_value : edges) {
-            const EdgeAccessor &edge = edge_value.ValueEdge();
-            const VertexAccessor &from = edge.from();
+            const auto &edge = edge_value.ValueEdge();
+            const auto &from = edge.From();
             if (path.vertices().back() == from)
-              path.Expand(edge, edge.to());
+              path.Expand(edge, edge.To());
             else
               path.Expand(edge, from);
           }
@@ -1828,23 +1893,57 @@ bool Delete::DeleteCursor::Pull(Frame &frame, ExecutionContext &context) {
   auto &dba = *context.db_accessor;
   // delete edges first
   for (TypedValue &expression_result : expression_results) {
-    if (dba.should_abort()) throw HintedAbortError();
-    if (expression_result.type() == TypedValue::Type::Edge)
-      dba.RemoveEdge(expression_result.ValueEdge());
+    if (dba.MustAbort()) throw HintedAbortError();
+    if (expression_result.type() == TypedValue::Type::Edge) {
+      auto maybe_error = dba.RemoveEdge(&expression_result.ValueEdge());
+      if (maybe_error.HasError()) {
+        switch (maybe_error.GetError()) {
+          case storage::Error::SERIALIZATION_ERROR:
+            throw QueryRuntimeException(
+                "Can't serialize due to concurrent operations.");
+          case storage::Error::DELETED_OBJECT:
+          case storage::Error::VERTEX_HAS_EDGES:
+            throw QueryRuntimeException(
+                "Unexpected error when deleting an edge.");
+        }
+      }
+    }
   }
 
   // delete vertices
   for (TypedValue &expression_result : expression_results) {
-    if (dba.should_abort()) throw HintedAbortError();
+    if (dba.MustAbort()) throw HintedAbortError();
     switch (expression_result.type()) {
       case TypedValue::Type::Vertex: {
-        VertexAccessor &va = expression_result.ValueVertex();
-        va.SwitchNew();  //  necessary because an edge deletion could have
-                         //  updated
-        if (self_.detach_)
-          dba.DetachRemoveVertex(va);
-        else if (!dba.RemoveVertex(va))
-          throw RemoveAttachedVertexException();
+        auto &va = expression_result.ValueVertex();
+        if (self_.detach_) {
+          auto maybe_error = dba.DetachRemoveVertex(&va);
+          if (maybe_error.HasError()) {
+            switch (maybe_error.GetError()) {
+              case storage::Error::SERIALIZATION_ERROR:
+                throw QueryRuntimeException(
+                    "Can't serialize due to concurrent operations.");
+              case storage::Error::DELETED_OBJECT:
+              case storage::Error::VERTEX_HAS_EDGES:
+                throw QueryRuntimeException(
+                    "Unexpected error when deleting a node.");
+            }
+          }
+        } else {
+          auto res = dba.RemoveVertex(&va);
+          if (res.HasError()) {
+            switch (res.GetError()) {
+              case storage::Error::SERIALIZATION_ERROR:
+                throw QueryRuntimeException(
+                    "Can't serialize due to concurrent operations.");
+              case storage::Error::VERTEX_HAS_EDGES:
+                throw RemoveAttachedVertexException();
+              case storage::Error::DELETED_OBJECT:
+                throw QueryRuntimeException(
+                    "Unexpected error when deleting a node.");
+            }
+          }
+        }
         break;
       }
 
@@ -1952,39 +2051,70 @@ namespace {
 /// @tparam TRecordAccessor Either RecordAccessor<Vertex> or
 ///     RecordAccessor<Edge>
 template <typename TRecordAccessor>
-void SetPropertiesOnRecord(database::GraphDbAccessor *dba,
-                           TRecordAccessor *record, const TypedValue &rhs,
-                           SetProperties::Op op) {
-  record->SwitchNew();
+void SetPropertiesOnRecord(DbAccessor *dba, TRecordAccessor *record,
+                           const TypedValue &rhs, SetProperties::Op op) {
   if (op == SetProperties::Op::REPLACE) {
-    try {
-      record->PropsClear();
-    } catch (const RecordDeletedError &) {
-      throw QueryRuntimeException(
-          "Trying to set properties on a deleted graph element.");
+    auto maybe_error = record->ClearProperties();
+    if (maybe_error.HasError()) {
+      switch (maybe_error.GetError()) {
+        case storage::Error::DELETED_OBJECT:
+          throw QueryRuntimeException(
+              "Trying to set properties on a deleted graph element.");
+        case storage::Error::SERIALIZATION_ERROR:
+          throw QueryRuntimeException(
+              "Can't serialize due to concurrent operations.");
+        case storage::Error::VERTEX_HAS_EDGES:
+          throw QueryRuntimeException(
+              "Unexpected error when setting properties.");
+      }
     }
   }
 
+  auto get_props = [](const auto &record) {
+    auto maybe_props = record.Properties(storage::View::NEW);
+    if (maybe_props.HasError()) {
+      switch (maybe_props.GetError()) {
+        case storage::Error::DELETED_OBJECT:
+          throw QueryRuntimeException(
+              "Trying to get properties from a deleted object.");
+        case storage::Error::SERIALIZATION_ERROR:
+        case storage::Error::VERTEX_HAS_EDGES:
+          throw QueryRuntimeException(
+              "Unexpected error when getting properties.");
+      }
+    }
+    return *maybe_props;
+  };
+
   auto set_props = [record](const auto &properties) {
-    try {
-      for (const auto &kv : properties) record->PropsSet(kv.first, kv.second);
-    } catch (const RecordDeletedError &) {
-      throw QueryRuntimeException(
-          "Trying to set properties on a deleted graph element.");
+    for (const auto &kv : properties) {
+      auto maybe_error = record->SetProperty(kv.first, kv.second);
+      if (maybe_error.HasError()) {
+        switch (maybe_error.GetError()) {
+          case storage::Error::DELETED_OBJECT:
+            throw QueryRuntimeException(
+                "Trying to set properties on a deleted graph element.");
+          case storage::Error::SERIALIZATION_ERROR:
+            throw QueryRuntimeException(
+                "Can't serialize due to concurrent operations.");
+          case storage::Error::VERTEX_HAS_EDGES:
+            throw QueryRuntimeException(
+                "Unexpected error when setting properties.");
+        }
+      }
     }
   };
 
   switch (rhs.type()) {
     case TypedValue::Type::Edge:
-      set_props(rhs.ValueEdge().Properties());
+      set_props(get_props(rhs.ValueEdge()));
       break;
     case TypedValue::Type::Vertex:
-      set_props(rhs.ValueVertex().Properties());
+      set_props(get_props(rhs.ValueVertex()));
       break;
     case TypedValue::Type::Map: {
       for (const auto &kv : rhs.ValueMap())
-        PropsSetChecked(record, dba->Property(std::string(kv.first)),
-                        kv.second);
+        PropsSetChecked(record, dba->NameToProperty(kv.first), kv.second);
       break;
     }
     default:
@@ -2012,12 +2142,12 @@ bool SetProperties::SetPropertiesCursor::Pull(Frame &frame,
 
   switch (lhs.type()) {
     case TypedValue::Type::Vertex:
-      SetPropertiesOnRecord(context.db_accessor, &lhs.ValueVertex(), rhs,
-                            self_.op_);
+      SetPropertiesOnRecord(context.db_accessor, &lhs.ValueVertex(),
+                            rhs, self_.op_);
       break;
     case TypedValue::Type::Edge:
-      SetPropertiesOnRecord(context.db_accessor, &lhs.ValueEdge(), rhs,
-                            self_.op_);
+      SetPropertiesOnRecord(context.db_accessor, &lhs.ValueEdge(),
+                            rhs, self_.op_);
       break;
     case TypedValue::Type::Null:
       // Skip setting properties on Null (can occur in optional match).
@@ -2064,11 +2194,20 @@ bool SetLabels::SetLabelsCursor::Pull(Frame &frame, ExecutionContext &context) {
   if (vertex_value.IsNull()) return true;
   ExpectType(self_.input_symbol_, vertex_value, TypedValue::Type::Vertex);
   auto &vertex = vertex_value.ValueVertex();
-  vertex.SwitchNew();
-  try {
-    for (auto label : self_.labels_) vertex.add_label(label);
-  } catch (const RecordDeletedError &) {
-    throw QueryRuntimeException("Trying to set labels on a deleted node.");
+  for (auto label : self_.labels_) {
+    auto maybe_error = vertex.AddLabel(label);
+    if (maybe_error.HasError()) {
+      switch (maybe_error.GetError()) {
+        case storage::Error::SERIALIZATION_ERROR:
+          throw QueryRuntimeException(
+              "Can't serialize due to concurrent operations.");
+        case storage::Error::DELETED_OBJECT:
+          throw QueryRuntimeException(
+              "Trying to set a label on a deleted node.");
+        case storage::Error::VERTEX_HAS_EDGES:
+          throw QueryRuntimeException("Unexpected error when setting a label.");
+      }
+    }
   }
 
   return true;
@@ -2109,22 +2248,29 @@ bool RemoveProperty::RemovePropertyCursor::Pull(Frame &frame,
                                 storage::View::NEW);
   TypedValue lhs = self_.lhs_->expression_->Accept(evaluator);
 
+  auto remove_prop = [property = self_.property_](auto *record) {
+    auto maybe_error = record->RemoveProperty(property);
+    if (maybe_error.HasError()) {
+      switch (maybe_error.GetError()) {
+        case storage::Error::DELETED_OBJECT:
+          throw QueryRuntimeException(
+              "Trying to remove a property on a deleted graph element.");
+        case storage::Error::SERIALIZATION_ERROR:
+          throw QueryRuntimeException(
+              "Can't serialize due to concurrent operations.");
+        case storage::Error::VERTEX_HAS_EDGES:
+          throw QueryRuntimeException(
+              "Unexpected error when removing property.");
+      }
+    }
+  };
+
   switch (lhs.type()) {
     case TypedValue::Type::Vertex:
-      try {
-        lhs.ValueVertex().PropsErase(self_.property_);
-      } catch (const RecordDeletedError &) {
-        throw QueryRuntimeException(
-            "Trying to remove properties from a deleted node.");
-      }
+      remove_prop(&lhs.ValueVertex());
       break;
     case TypedValue::Type::Edge:
-      try {
-        lhs.ValueEdge().PropsErase(self_.property_);
-      } catch (const RecordDeletedError &) {
-        throw QueryRuntimeException(
-            "Trying to remove properties from a deleted edge.");
-      }
+      remove_prop(&lhs.ValueEdge());
       break;
     case TypedValue::Type::Null:
       // Skip removing properties on Null (can occur in optional match).
@@ -2173,11 +2319,21 @@ bool RemoveLabels::RemoveLabelsCursor::Pull(Frame &frame,
   if (vertex_value.IsNull()) return true;
   ExpectType(self_.input_symbol_, vertex_value, TypedValue::Type::Vertex);
   auto &vertex = vertex_value.ValueVertex();
-  vertex.SwitchNew();
-  try {
-    for (auto label : self_.labels_) vertex.remove_label(label);
-  } catch (const RecordDeletedError &) {
-    throw QueryRuntimeException("Trying to remove labels from a deleted node.");
+  for (auto label : self_.labels_) {
+    auto maybe_error = vertex.RemoveLabel(label);
+    if (maybe_error.HasError()) {
+      switch (maybe_error.GetError()) {
+        case storage::Error::SERIALIZATION_ERROR:
+          throw QueryRuntimeException(
+              "Can't serialize due to concurrent operations.");
+        case storage::Error::DELETED_OBJECT:
+          throw QueryRuntimeException(
+              "Trying to remove labels from a deleted node.");
+        case storage::Error::VERTEX_HAS_EDGES:
+          throw QueryRuntimeException(
+              "Unexpected error when removing labels from a node.");
+      }
+    }
   }
 
   return true;
@@ -2291,14 +2447,10 @@ class AccumulateCursor : public Cursor {
       pulled_all_input_ = true;
       cache_it_ = cache_.begin();
 
-      if (self_.advance_command_) {
-        dba.AdvanceCommand();
-        for (auto &row : cache_)
-          for (auto &col : row) query::ReconstructTypedValue(col);
-      }
+      if (self_.advance_command_) dba.AdvanceCommand();
     }
 
-    if (dba.should_abort()) throw HintedAbortError();
+    if (dba.MustAbort()) throw HintedAbortError();
     if (cache_it_ == cache_.end()) return false;
     auto row_it = (cache_it_++)->begin();
     for (const Symbol &symbol : self_.symbols_) frame[symbol] = *row_it++;
@@ -2860,7 +3012,7 @@ class OrderByCursor : public Cursor {
 
     if (cache_it_ == cache_.end()) return false;
 
-    if (context.db_accessor->should_abort()) throw HintedAbortError();
+    if (context.db_accessor->MustAbort()) throw HintedAbortError();
 
     // place the output values on the frame
     DCHECK(self_.output_symbols_.size() == cache_it_->remember.size())
@@ -3091,9 +3243,8 @@ class UnwindCursor : public Cursor {
 
   bool Pull(Frame &frame, ExecutionContext &context) override {
     SCOPED_PROFILE_OP("Unwind");
-
     while (true) {
-      if (context.db_accessor->should_abort()) throw HintedAbortError();
+      if (context.db_accessor->MustAbort()) throw HintedAbortError();
       // if we reached the end of our list of values
       // pull from the input
       if (input_value_it_ == input_value_.end()) {
@@ -3351,7 +3502,7 @@ class CartesianCursor : public Cursor {
       restore_frame(self_.right_symbols_, right_op_frame_);
     }
 
-    if (context.db_accessor->should_abort()) throw HintedAbortError();
+    if (context.db_accessor->MustAbort()) throw HintedAbortError();
 
     restore_frame(self_.left_symbols_, *left_op_frames_it_);
     left_op_frames_it_++;
diff --git a/src/query/plan/operator.lcp b/src/query/plan/operator.lcp
index d899ae1e4..1a3817d59 100644
--- a/src/query/plan/operator.lcp
+++ b/src/query/plan/operator.lcp
@@ -808,11 +808,12 @@ pulled.")
      void Reset() override;
 
     private:
-     using InEdgeT = decltype(std::declval<VertexAccessor>().in());
-     using InEdgeIteratorT = decltype(std::declval<VertexAccessor>().in().begin());
-     using OutEdgeT = decltype(std::declval<VertexAccessor>().out());
-     using OutEdgeIteratorT =
-         decltype(std::declval<VertexAccessor>().out().begin());
+     using InEdgeT = std::remove_reference_t<decltype(
+         *std::declval<VertexAccessor>().InEdges(storage::View::OLD))>;
+     using InEdgeIteratorT = decltype(std::declval<InEdgeT>().begin());
+     using OutEdgeT = std::remove_reference_t<decltype(
+         *std::declval<VertexAccessor>().OutEdges(storage::View::OLD))>;
+     using OutEdgeIteratorT = decltype(std::declval<OutEdgeT>().begin());
 
      const Expand &self_;
      const UniqueCursorPtr input_cursor_;
diff --git a/src/query/plan/pretty_print.cpp b/src/query/plan/pretty_print.cpp
index 53ec14aef..1db9fae5c 100644
--- a/src/query/plan/pretty_print.cpp
+++ b/src/query/plan/pretty_print.cpp
@@ -1,13 +1,12 @@
 #include "query/plan/pretty_print.hpp"
 
-#include "database/graph_db_accessor.hpp"
+#include "query/db_accessor.hpp"
 #include "query/frontend/ast/pretty_print.hpp"
 #include "utils/string.hpp"
 
 namespace query::plan {
 
-PlanPrinter::PlanPrinter(const database::GraphDbAccessor *dba,
-                         std::ostream *out)
+PlanPrinter::PlanPrinter(const DbAccessor *dba, std::ostream *out)
     : dba_(dba), out_(out) {}
 
 #define PRE_VISIT(TOp)                                   \
@@ -24,7 +23,7 @@ bool PlanPrinter::PreVisit(CreateExpand &op) {
         << (op.edge_info_.direction == query::EdgeAtom::Direction::IN ? "<-"
                                                                       : "-")
         << "[" << op.edge_info_.symbol.name() << ":"
-        << dba_->EdgeTypeName(op.edge_info_.edge_type) << "]"
+        << dba_->EdgeTypeToName(op.edge_info_.edge_type) << "]"
         << (op.edge_info_.direction == query::EdgeAtom::Direction::OUT ? "->"
                                                                        : "-")
         << "(" << op.node_info_.symbol.name() << ")";
@@ -46,7 +45,7 @@ bool PlanPrinter::PreVisit(query::plan::ScanAllByLabel &op) {
   WithPrintLn([&](auto &out) {
     out << "* ScanAllByLabel"
         << " (" << op.output_symbol_.name() << " :"
-        << dba_->LabelName(op.label_) << ")";
+        << dba_->LabelToName(op.label_) << ")";
   });
   return true;
 }
@@ -55,8 +54,8 @@ bool PlanPrinter::PreVisit(query::plan::ScanAllByLabelPropertyValue &op) {
   WithPrintLn([&](auto &out) {
     out << "* ScanAllByLabelPropertyValue"
         << " (" << op.output_symbol_.name() << " :"
-        << dba_->LabelName(op.label_) << " {"
-        << dba_->PropertyName(op.property_) << "})";
+        << dba_->LabelToName(op.label_) << " {"
+        << dba_->PropertyToName(op.property_) << "})";
   });
   return true;
 }
@@ -65,8 +64,8 @@ bool PlanPrinter::PreVisit(query::plan::ScanAllByLabelPropertyRange &op) {
   WithPrintLn([&](auto &out) {
     out << "* ScanAllByLabelPropertyRange"
         << " (" << op.output_symbol_.name() << " :"
-        << dba_->LabelName(op.label_) << " {"
-        << dba_->PropertyName(op.property_) << "})";
+        << dba_->LabelToName(op.label_) << " {"
+        << dba_->PropertyToName(op.property_) << "})";
   });
   return true;
 }
@@ -79,7 +78,7 @@ bool PlanPrinter::PreVisit(query::plan::Expand &op) {
           << "[" << op.common_.edge_symbol.name();
     utils::PrintIterable(*out_, op.common_.edge_types, "|",
                          [this](auto &stream, const auto &edge_type) {
-                           stream << ":" << dba_->EdgeTypeName(edge_type);
+                           stream << ":" << dba_->EdgeTypeToName(edge_type);
                          });
     *out_ << "]"
           << (op.common_.direction == query::EdgeAtom::Direction::OUT ? "->"
@@ -97,7 +96,7 @@ bool PlanPrinter::PreVisit(query::plan::ExpandVariable &op) {
           << "[" << op.common_.edge_symbol.name();
     utils::PrintIterable(*out_, op.common_.edge_types, "|",
                          [this](auto &stream, const auto &edge_type) {
-                           stream << ":" << dba_->EdgeTypeName(edge_type);
+                           stream << ":" << dba_->EdgeTypeToName(edge_type);
                          });
     *out_ << "]"
           << (op.common_.direction == query::EdgeAtom::Direction::OUT ? "->"
@@ -223,14 +222,14 @@ void PlanPrinter::Branch(query::plan::LogicalOperator &op,
   --depth_;
 }
 
-void PrettyPrint(const database::GraphDbAccessor &dba,
-                 const LogicalOperator *plan_root, std::ostream *out) {
+void PrettyPrint(const DbAccessor &dba, const LogicalOperator *plan_root,
+                 std::ostream *out) {
   PlanPrinter printer(&dba, out);
   // FIXME(mtomic): We should make visitors that take const arguments.
   const_cast<LogicalOperator *>(plan_root)->Accept(printer);
 }
 
-nlohmann::json PlanToJson(const database::GraphDbAccessor &dba,
+nlohmann::json PlanToJson(const DbAccessor &dba,
                           const LogicalOperator *plan_root) {
   impl::PlanToJsonVisitor visitor(&dba);
   // FIXME(mtomic): We should make visitors that take const arguments.
@@ -310,16 +309,16 @@ json ToJson(const utils::Bound<Expression *> &bound) {
 
 json ToJson(const Symbol &symbol) { return symbol.name(); }
 
-json ToJson(storage::EdgeType edge_type, const database::GraphDbAccessor &dba) {
-  return dba.EdgeTypeName(edge_type);
+json ToJson(storage::EdgeType edge_type, const DbAccessor &dba) {
+  return dba.EdgeTypeToName(edge_type);
 }
 
-json ToJson(storage::Label label, const database::GraphDbAccessor &dba) {
-  return dba.LabelName(label);
+json ToJson(storage::Label label, const DbAccessor &dba) {
+  return dba.LabelToName(label);
 }
 
-json ToJson(storage::Property property, const database::GraphDbAccessor &dba) {
-  return dba.PropertyName(property);
+json ToJson(storage::Property property, const DbAccessor &dba) {
+  return dba.PropertyToName(property);
 }
 
 json ToJson(NamedExpression *nexpr) {
@@ -331,7 +330,7 @@ json ToJson(NamedExpression *nexpr) {
 
 json ToJson(
     const std::vector<std::pair<storage::Property, Expression *>> &properties,
-    const database::GraphDbAccessor &dba) {
+    const DbAccessor &dba) {
   json json;
   for (const auto &prop_pair : properties) {
     json.emplace(ToJson(prop_pair.first, dba), ToJson(prop_pair.second));
@@ -339,8 +338,7 @@ json ToJson(
   return json;
 }
 
-json ToJson(const NodeCreationInfo &node_info,
-            const database::GraphDbAccessor &dba) {
+json ToJson(const NodeCreationInfo &node_info, const DbAccessor &dba) {
   json self;
   self["symbol"] = ToJson(node_info.symbol);
   self["labels"] = ToJson(node_info.labels, dba);
@@ -348,8 +346,7 @@ json ToJson(const NodeCreationInfo &node_info,
   return self;
 }
 
-json ToJson(const EdgeCreationInfo &edge_info,
-            const database::GraphDbAccessor &dba) {
+json ToJson(const EdgeCreationInfo &edge_info, const DbAccessor &dba) {
   json self;
   self["symbol"] = ToJson(edge_info.symbol);
   self["properties"] = ToJson(edge_info.properties, dba);
diff --git a/src/query/plan/pretty_print.hpp b/src/query/plan/pretty_print.hpp
index ef5b2c036..5c3691162 100644
--- a/src/query/plan/pretty_print.hpp
+++ b/src/query/plan/pretty_print.hpp
@@ -7,8 +7,8 @@
 
 #include "query/plan/operator.hpp"
 
-namespace database {
-class GraphDbAccessor;
+namespace query {
+class DbAccessor;
 }
 
 namespace query::plan {
@@ -16,21 +16,21 @@ namespace query::plan {
 class LogicalOperator;
 
 /// Pretty print a `LogicalOperator` plan to a `std::ostream`.
-/// GraphDbAccessor is needed for resolving label and property names.
+/// DbAccessor is needed for resolving label and property names.
 /// Note that `plan_root` isn't modified, but we can't take it as a const
 /// because we don't have support for visiting a const LogicalOperator.
-void PrettyPrint(const database::GraphDbAccessor &dba,
-                 const LogicalOperator *plan_root, std::ostream *out);
+void PrettyPrint(const DbAccessor &dba, const LogicalOperator *plan_root,
+                 std::ostream *out);
 
 /// Overload of `PrettyPrint` which defaults the `std::ostream` to `std::cout`.
-inline void PrettyPrint(const database::GraphDbAccessor &dba,
+inline void PrettyPrint(const DbAccessor &dba,
                         const LogicalOperator *plan_root) {
   PrettyPrint(dba, plan_root, &std::cout);
 }
 
 /// Convert a `LogicalOperator` plan to a JSON representation.
-/// GraphDbAccessor is needed for resolving label and property names.
-nlohmann::json PlanToJson(const database::GraphDbAccessor &dba,
+/// DbAccessor is needed for resolving label and property names.
+nlohmann::json PlanToJson(const DbAccessor &dba,
                           const LogicalOperator *plan_root);
 
 class PlanPrinter : public virtual HierarchicalLogicalOperatorVisitor {
@@ -39,7 +39,7 @@ class PlanPrinter : public virtual HierarchicalLogicalOperatorVisitor {
   using HierarchicalLogicalOperatorVisitor::PreVisit;
   using HierarchicalLogicalOperatorVisitor::Visit;
 
-  PlanPrinter(const database::GraphDbAccessor *dba, std::ostream *out);
+  PlanPrinter(const DbAccessor *dba, std::ostream *out);
 
   bool DefaultPreVisit() override;
 
@@ -101,7 +101,7 @@ class PlanPrinter : public virtual HierarchicalLogicalOperatorVisitor {
   void Branch(LogicalOperator &op, const std::string &branch_name = "");
 
   int64_t depth_{0};
-  const database::GraphDbAccessor *dba_{nullptr};
+  const DbAccessor *dba_{nullptr};
   std::ostream *out_{nullptr};
 };
 
@@ -119,26 +119,21 @@ nlohmann::json ToJson(const utils::Bound<Expression *> &bound);
 
 nlohmann::json ToJson(const Symbol &symbol);
 
-nlohmann::json ToJson(storage::EdgeType edge_type,
-                      const database::GraphDbAccessor &dba);
+nlohmann::json ToJson(storage::EdgeType edge_type, const DbAccessor &dba);
 
-nlohmann::json ToJson(storage::Label label,
-                      const database::GraphDbAccessor &dba);
+nlohmann::json ToJson(storage::Label label, const DbAccessor &dba);
 
-nlohmann::json ToJson(storage::Property property,
-                      const database::GraphDbAccessor &dba);
+nlohmann::json ToJson(storage::Property property, const DbAccessor &dba);
 
 nlohmann::json ToJson(NamedExpression *nexpr);
 
 nlohmann::json ToJson(
     const std::vector<std::pair<storage::Property, Expression *>> &properties,
-    const database::GraphDbAccessor &dba);
+    const DbAccessor &dba);
 
-nlohmann::json ToJson(const NodeCreationInfo &node_info,
-                      const database::GraphDbAccessor &dba);
+nlohmann::json ToJson(const NodeCreationInfo &node_info, const DbAccessor &dba);
 
-nlohmann::json ToJson(const EdgeCreationInfo &edge_info,
-                      const database::GraphDbAccessor &dba);
+nlohmann::json ToJson(const EdgeCreationInfo &edge_info, const DbAccessor &dba);
 
 nlohmann::json ToJson(const Aggregate::Element &elem);
 
@@ -153,7 +148,7 @@ nlohmann::json ToJson(const std::vector<T> &items, Args &&... args) {
 
 class PlanToJsonVisitor : public virtual HierarchicalLogicalOperatorVisitor {
  public:
-  PlanToJsonVisitor(const database::GraphDbAccessor *dba) : dba_(dba) {}
+  explicit PlanToJsonVisitor(const DbAccessor *dba) : dba_(dba) {}
 
   using HierarchicalLogicalOperatorVisitor::PostVisit;
   using HierarchicalLogicalOperatorVisitor::PreVisit;
@@ -204,7 +199,7 @@ class PlanToJsonVisitor : public virtual HierarchicalLogicalOperatorVisitor {
 
  protected:
   nlohmann::json output_;
-  const database::GraphDbAccessor *dba_;
+  const DbAccessor *dba_;
 
   nlohmann::json PopOutput() {
     nlohmann::json tmp;
diff --git a/src/query/plan/rewrite/index_lookup.hpp b/src/query/plan/rewrite/index_lookup.hpp
index 482027ded..eb119ef6a 100644
--- a/src/query/plan/rewrite/index_lookup.hpp
+++ b/src/query/plan/rewrite/index_lookup.hpp
@@ -418,10 +418,12 @@ class IndexLookupRewriter final : public HierarchicalLogicalOperatorVisitor {
     }
   }
 
-  storage::Label GetLabel(LabelIx label) { return db_->Label(label.name); }
+  storage::Label GetLabel(LabelIx label) {
+    return db_->NameToLabel(label.name);
+  }
 
   storage::Property GetProperty(PropertyIx prop) {
-    return db_->Property(prop.name);
+    return db_->NameToProperty(prop.name);
   }
 
   LabelIx FindBestLabelIndex(const std::unordered_set<LabelIx> &labels) {
diff --git a/src/query/plan/rule_based_planner.hpp b/src/query/plan/rule_based_planner.hpp
index 331f975bd..59bc895f4 100644
--- a/src/query/plan/rule_based_planner.hpp
+++ b/src/query/plan/rule_based_planner.hpp
@@ -221,15 +221,15 @@ class RuleBasedPlanner {
   TPlanningContext *context_;
 
   storage::Label GetLabel(LabelIx label) {
-    return context_->db->Label(label.name);
+    return context_->db->NameToLabel(label.name);
   }
 
   storage::Property GetProperty(PropertyIx prop) {
-    return context_->db->Property(prop.name);
+    return context_->db->NameToProperty(prop.name);
   }
 
   storage::EdgeType GetEdgeType(EdgeTypeIx edge_type) {
-    return context_->db->EdgeType(edge_type.name);
+    return context_->db->NameToEdgeType(edge_type.name);
   }
 
   std::unique_ptr<LogicalOperator> GenCreate(
diff --git a/src/query/plan/vertex_count_cache.hpp b/src/query/plan/vertex_count_cache.hpp
index d83c7f5df..dfaf3b479 100644
--- a/src/query/plan/vertex_count_cache.hpp
+++ b/src/query/plan/vertex_count_cache.hpp
@@ -18,9 +18,13 @@ class VertexCountCache {
  public:
   VertexCountCache(TDbAccessor *db) : db_(db) {}
 
-  auto Label(const std::string &name) { return db_->Label(name); }
-  auto Property(const std::string &name) { return db_->Property(name); }
-  auto EdgeType(const std::string &name) { return db_->EdgeType(name); }
+  auto NameToLabel(const std::string &name) { return db_->NameToLabel(name); }
+  auto NameToProperty(const std::string &name) {
+    return db_->NameToProperty(name);
+  }
+  auto NameToEdgeType(const std::string &name) {
+    return db_->NameToEdgeType(name);
+  }
 
   int64_t VerticesCount() {
     if (!vertices_count_) vertices_count_ = db_->VerticesCount();
diff --git a/src/query/repl.cpp b/src/query/repl.cpp
index aa23a27e2..14c856b8b 100644
--- a/src/query/repl.cpp
+++ b/src/query/repl.cpp
@@ -63,8 +63,9 @@ void query::Repl(database::GraphDb *db, query::Interpreter *interpreter) {
     try {
       auto dba = db->Access();
       ResultStreamFaker<query::TypedValue> stream;
-      auto results =
-          (*interpreter)(command, dba, {}, false, utils::NewDeleteResource());
+      DbAccessor execution_dba(&dba);
+      auto results = (*interpreter)(command, &execution_dba, {}, false,
+                                    utils::NewDeleteResource());
       stream.Header(results.header());
       results.PullAll(stream);
       stream.Summary(results.summary());
diff --git a/src/query/transaction_engine.hpp b/src/query/transaction_engine.hpp
index 1519507c5..b286d77de 100644
--- a/src/query/transaction_engine.hpp
+++ b/src/query/transaction_engine.hpp
@@ -14,7 +14,11 @@ static constexpr size_t kExecutionMemoryBlockSize = 1U * 1024U * 1024U;
 
 class TransactionEngine final {
  public:
+#ifdef MG_SINGLE_NODE_V2
+  TransactionEngine(storage::Storage *db, Interpreter *interpreter)
+#else
   TransactionEngine(database::GraphDb *db, Interpreter *interpreter)
+#endif
       : db_(db),
         interpreter_(interpreter),
         execution_memory_(&initial_memory_block_[0],
@@ -72,7 +76,10 @@ class TransactionEngine final {
     if (in_explicit_transaction_ && db_accessor_) AdvanceCommand();
 
     // Create a DB accessor if we don't yet have one.
-    if (!db_accessor_) db_accessor_.emplace(db_->Access());
+    if (!db_accessor_) {
+      db_accessor_.emplace(db_->Access());
+      execution_db_accessor_.emplace(&*db_accessor_);
+    }
 
     // Clear leftover results.
     results_ = std::nullopt;
@@ -80,7 +87,7 @@ class TransactionEngine final {
 
     // Interpret the query and return the headers.
     try {
-      results_.emplace((*interpreter_)(query, *db_accessor_, params,
+      results_.emplace((*interpreter_)(query, &*execution_db_accessor_, params,
                                        in_explicit_transaction_,
                                        &execution_memory_));
       return {std::move(results_->header()), std::move(results_->privileges())};
@@ -129,13 +136,20 @@ class TransactionEngine final {
     in_explicit_transaction_ = false;
     if (!db_accessor_) return;
     db_accessor_->Abort();
+    execution_db_accessor_ = std::nullopt;
     db_accessor_ = std::nullopt;
   }
 
  private:
+#ifdef MG_SINGLE_NODE_V2
+  storage::Storage *db_{nullptr};
+  std::optional<storage::Storage::Accessor> db_accessor_;
+#else
   database::GraphDb *db_{nullptr};
-  Interpreter *interpreter_{nullptr};
   std::optional<database::GraphDbAccessor> db_accessor_;
+#endif
+  std::optional<DbAccessor> execution_db_accessor_;
+  Interpreter *interpreter_{nullptr};
   // The `query::Interpreter::Results` object MUST be destroyed before the
   // `database::GraphDbAccessor` is destroyed because the `Results` object holds
   // references to the `GraphDb` object and will crash the database when
@@ -151,7 +165,24 @@ class TransactionEngine final {
     results_ = std::nullopt;
     execution_memory_.Release();
     if (!db_accessor_) return;
+#ifdef MG_SINGLE_NODE_V2
+    auto maybe_constraint_violation = db_accessor_->Commit();
+    if (maybe_constraint_violation.HasError()) {
+      const auto &constraint_violation = maybe_constraint_violation.GetError();
+      auto label_name = execution_db_accessor_->LabelToName(
+          constraint_violation.label);
+      auto property_name = execution_db_accessor_->PropertyToName(
+          constraint_violation.property);
+      execution_db_accessor_ = std::nullopt;
+      db_accessor_ = std::nullopt;
+      throw QueryException(
+          "Unable to commit due to existence constraint violation on :{}({}).",
+          label_name, property_name);
+    }
+#else
     db_accessor_->Commit();
+#endif
+    execution_db_accessor_ = std::nullopt;
     db_accessor_ = std::nullopt;
   }
 
diff --git a/src/query/typed_value.cpp b/src/query/typed_value.cpp
index 3c97a24f0..dec08b625 100644
--- a/src/query/typed_value.cpp
+++ b/src/query/typed_value.cpp
@@ -904,9 +904,9 @@ size_t TypedValue::Hash::operator()(const TypedValue &value) const {
       return hash;
     }
     case TypedValue::Type::Vertex:
-      return value.ValueVertex().gid().AsUint();
+      return value.ValueVertex().Gid().AsUint();
     case TypedValue::Type::Edge:
-      return value.ValueEdge().gid().AsUint();
+      return value.ValueEdge().Gid().AsUint();
     case TypedValue::Type::Path: {
       const auto &vertices = value.ValuePath().vertices();
       const auto &edges = value.ValuePath().edges();
diff --git a/src/query/typed_value.hpp b/src/query/typed_value.hpp
index d697f03ec..9f52e0387 100644
--- a/src/query/typed_value.hpp
+++ b/src/query/typed_value.hpp
@@ -9,10 +9,8 @@
 #include <utility>
 #include <vector>
 
+#include "query/db_accessor.hpp"
 #include "query/path.hpp"
-#include "storage/common/types/property_value.hpp"
-#include "storage/edge_accessor.hpp"
-#include "storage/vertex_accessor.hpp"
 #include "utils/exceptions.hpp"
 #include "utils/memory.hpp"
 #include "utils/pmr/map.hpp"
diff --git a/src/storage/common/types/types.hpp b/src/storage/common/types/types.hpp
index b86aee9e1..0948e04c8 100644
--- a/src/storage/common/types/types.hpp
+++ b/src/storage/common/types/types.hpp
@@ -1,5 +1,14 @@
 #pragma once
 
+#ifdef MG_SINGLE_NODE_V2
+#include "storage/v2/id_types.hpp"
+namespace storage {
+using EdgeType = EdgeTypeId;
+using Label = LabelId;
+using Property = PropertyId;
+}
+#else
+
 #include <atomic>
 #include <cstdint>
 #include <functional>
@@ -247,3 +256,5 @@ struct hash<storage::Gid> {
   }
 };
 }  // namespace std
+
+#endif
diff --git a/src/storage/edge_accessor.hpp b/src/storage/edge_accessor.hpp
index 9b7cb0a97..92a01dac1 100644
--- a/src/storage/edge_accessor.hpp
+++ b/src/storage/edge_accessor.hpp
@@ -1,5 +1,10 @@
 #pragma once
 
+#ifdef MG_SINGLE_NODE_V2
+#include "storage/v2/edge_accessor.hpp"
+using EdgeAccessor = storage::EdgeAccessor;
+#endif
+
 #ifdef MG_SINGLE_NODE
 #include "storage/single_node/edge_accessor.hpp"
 #endif
diff --git a/src/storage/single_node/record_accessor.hpp b/src/storage/single_node/record_accessor.hpp
index 2c8d9386c..db7986249 100644
--- a/src/storage/single_node/record_accessor.hpp
+++ b/src/storage/single_node/record_accessor.hpp
@@ -64,6 +64,7 @@ class RecordAccessor {
 
   /**
    * Erases the property for the given key.
+   * @throw RecordDeletedError
    * @throw utils::LockTimeoutException
    * @throw SerializationError
    */
@@ -71,6 +72,7 @@ class RecordAccessor {
 
   /**
    * Removes all the properties from this record.
+   * @throw RecordDeletedError
    * @throw utils::LockTimeoutException
    * @throw SerializationError
    */
diff --git a/src/storage/v2/edge_accessor.hpp b/src/storage/v2/edge_accessor.hpp
index fc17152c4..9facf0e7d 100644
--- a/src/storage/v2/edge_accessor.hpp
+++ b/src/storage/v2/edge_accessor.hpp
@@ -47,6 +47,8 @@ class EdgeAccessor final {
 
   Gid Gid() const { return edge_->gid; }
 
+  bool IsCycle() const { return from_vertex_ == to_vertex_; }
+
   bool operator==(const EdgeAccessor &other) const {
     return edge_ == other.edge_ && transaction_ == other.transaction_;
   }
diff --git a/src/storage/v2/storage.cpp b/src/storage/v2/storage.cpp
index 865b2d6c7..f14fd3f64 100644
--- a/src/storage/v2/storage.cpp
+++ b/src/storage/v2/storage.cpp
@@ -2,10 +2,15 @@
 
 #include <memory>
 
+#include <gflags/gflags.h>
 #include <glog/logging.h>
 
 #include "storage/v2/mvcc.hpp"
 
+DEFINE_string(
+    durability_directory, "durability",
+    "Path to directory in which to save snapshots and write-ahead log files.");
+
 namespace storage {
 
 auto AdvanceToVisibleVertex(utils::SkipList<Vertex>::Iterator it,
diff --git a/src/storage/vertex_accessor.hpp b/src/storage/vertex_accessor.hpp
index ef2718fcd..2b33fa41f 100644
--- a/src/storage/vertex_accessor.hpp
+++ b/src/storage/vertex_accessor.hpp
@@ -1,5 +1,10 @@
 #pragma once
 
+#ifdef MG_SINGLE_NODE_V2
+#include "storage/v2/vertex_accessor.hpp"
+using VertexAccessor = storage::VertexAccessor;
+#endif
+
 #ifdef MG_SINGLE_NODE
 #include "storage/single_node/vertex_accessor.hpp"
 #endif
diff --git a/tests/benchmark/expansion.cpp b/tests/benchmark/expansion.cpp
index 9412786a1..64eb2d04d 100644
--- a/tests/benchmark/expansion.cpp
+++ b/tests/benchmark/expansion.cpp
@@ -44,9 +44,10 @@ class ExpansionBenchFixture : public benchmark::Fixture {
 BENCHMARK_DEFINE_F(ExpansionBenchFixture, Match)(benchmark::State &state) {
   auto query = "MATCH (s:Starting) return s";
   auto dba = db_->Access();
+  query::DbAccessor query_dba(&dba);
   while (state.KeepRunning()) {
     ResultStreamFaker<query::TypedValue> results;
-    interpreter()(query, dba, {}, false, utils::NewDeleteResource())
+    interpreter()(query, &query_dba, {}, false, utils::NewDeleteResource())
         .PullAll(results);
   }
 }
@@ -59,9 +60,10 @@ BENCHMARK_REGISTER_F(ExpansionBenchFixture, Match)
 BENCHMARK_DEFINE_F(ExpansionBenchFixture, Expand)(benchmark::State &state) {
   auto query = "MATCH (s:Starting) WITH s MATCH (s)--(d) RETURN count(d)";
   auto dba = db_->Access();
+  query::DbAccessor query_dba(&dba);
   while (state.KeepRunning()) {
     ResultStreamFaker<query::TypedValue> results;
-    interpreter()(query, dba, {}, false, utils::NewDeleteResource())
+    interpreter()(query, &query_dba, {}, false, utils::NewDeleteResource())
         .PullAll(results);
   }
 }
diff --git a/tests/benchmark/query/eval.cpp b/tests/benchmark/query/eval.cpp
index b328aea4a..ccbaf8bbe 100644
--- a/tests/benchmark/query/eval.cpp
+++ b/tests/benchmark/query/eval.cpp
@@ -1,5 +1,6 @@
 #include <benchmark/benchmark.h>
 
+#include "query/db_accessor.hpp"
 #include "query/interpret/eval.hpp"
 #include "query/transaction_engine.hpp"
 
@@ -27,6 +28,7 @@ static void MapLiteral(benchmark::State &state) {
   query::Frame frame(symbol_table.max_position(), memory.get());
   database::GraphDb db;
   auto dba = db.Access();
+  query::DbAccessor execution_dba(&dba);
   std::unordered_map<query::PropertyIx, query::Expression *> elements;
   for (int64_t i = 0; i < state.range(0); ++i) {
     elements.emplace(ast.GetPropertyIx("prop" + std::to_string(i)),
@@ -35,9 +37,9 @@ static void MapLiteral(benchmark::State &state) {
   auto *expr = ast.Create<query::MapLiteral>(elements);
   query::EvaluationContext evaluation_context{memory.get()};
   evaluation_context.properties =
-      query::NamesToProperties(ast.properties_, &dba);
+      query::NamesToProperties(ast.properties_, &execution_dba);
   query::ExpressionEvaluator evaluator(&frame, symbol_table, evaluation_context,
-                                       &dba, storage::View::NEW);
+                                       &execution_dba, storage::View::NEW);
   while (state.KeepRunning()) {
     benchmark::DoNotOptimize(expr->Accept(evaluator));
   }
@@ -67,8 +69,9 @@ static void AdditionOperator(benchmark::State &state) {
         expr, ast.Create<query::PrimitiveLiteral>(i));
   }
   query::EvaluationContext evaluation_context{memory.get()};
+  query::DbAccessor execution_dba(&dba);
   query::ExpressionEvaluator evaluator(&frame, symbol_table, evaluation_context,
-                                       &dba, storage::View::NEW);
+                                       &execution_dba, storage::View::NEW);
   while (state.KeepRunning()) {
     benchmark::DoNotOptimize(expr->Accept(evaluator));
   }
diff --git a/tests/benchmark/query/execution.cpp b/tests/benchmark/query/execution.cpp
index f16899a8a..48b8bfb24 100644
--- a/tests/benchmark/query/execution.cpp
+++ b/tests/benchmark/query/execution.cpp
@@ -108,8 +108,9 @@ static void Distinct(benchmark::State &state) {
   auto query_string = "MATCH (s) RETURN DISTINCT s";
   auto *cypher_query = ParseCypherQuery(query_string, &ast);
   auto symbol_table = query::MakeSymbolTable(cypher_query);
-  auto context =
-      query::plan::MakePlanningContext(&ast, &symbol_table, cypher_query, &dba);
+  query::DbAccessor execution_dba(&dba);
+  auto context = query::plan::MakePlanningContext(&ast, &symbol_table,
+                                                  cypher_query, &execution_dba);
   auto plan_and_cost =
       query::plan::MakeLogicalPlan(&context, parameters, false);
   ResultStreamFaker<query::TypedValue> results;
@@ -117,7 +118,7 @@ static void Distinct(benchmark::State &state) {
   TMemory per_pull_memory;
   query::EvaluationContext evaluation_context{per_pull_memory.get()};
   while (state.KeepRunning()) {
-    query::ExecutionContext execution_context{&dba, symbol_table,
+    query::ExecutionContext execution_context{&execution_dba, symbol_table,
                                               evaluation_context};
     TMemory memory;
     query::Frame frame(symbol_table.max_position(), memory.get());
@@ -171,13 +172,15 @@ static void ExpandVariable(benchmark::State &state) {
   TMemory per_pull_memory;
   query::EvaluationContext evaluation_context{per_pull_memory.get()};
   while (state.KeepRunning()) {
-    query::ExecutionContext execution_context{&dba, symbol_table,
+    query::DbAccessor execution_dba(&dba);
+    query::ExecutionContext execution_context{&execution_dba, symbol_table,
                                               evaluation_context};
     TMemory memory;
     query::Frame frame(symbol_table.max_position(), memory.get());
     auto cursor = expand_variable.MakeCursor(memory.get());
     for (const auto &v : dba.Vertices(dba.Label(kStartLabel), false)) {
-      frame[expand_variable.input_symbol_] = query::TypedValue(v);
+      frame[expand_variable.input_symbol_] =
+          query::TypedValue(query::VertexAccessor(v));
       while (cursor->Pull(frame, execution_context)) per_pull_memory.Reset();
     }
   }
@@ -211,13 +214,15 @@ static void ExpandBfs(benchmark::State &state) {
   TMemory per_pull_memory;
   query::EvaluationContext evaluation_context{per_pull_memory.get()};
   while (state.KeepRunning()) {
-    query::ExecutionContext execution_context{&dba, symbol_table,
+    query::DbAccessor execution_dba(&dba);
+    query::ExecutionContext execution_context{&execution_dba, symbol_table,
                                               evaluation_context};
     TMemory memory;
     query::Frame frame(symbol_table.max_position(), memory.get());
     auto cursor = expand_variable.MakeCursor(memory.get());
     for (const auto &v : dba.Vertices(dba.Label(kStartLabel), false)) {
-      frame[expand_variable.input_symbol_] = query::TypedValue(v);
+      frame[expand_variable.input_symbol_] =
+          query::TypedValue(query::VertexAccessor(v));
       while (cursor->Pull(frame, execution_context)) per_pull_memory.Reset();
     }
   }
@@ -253,15 +258,17 @@ static void ExpandShortest(benchmark::State &state) {
   TMemory per_pull_memory;
   query::EvaluationContext evaluation_context{per_pull_memory.get()};
   while (state.KeepRunning()) {
-    query::ExecutionContext execution_context{&dba, symbol_table,
+    query::DbAccessor execution_dba(&dba);
+    query::ExecutionContext execution_context{&execution_dba, symbol_table,
                                               evaluation_context};
     TMemory memory;
     query::Frame frame(symbol_table.max_position(), memory.get());
     auto cursor = expand_variable.MakeCursor(memory.get());
     for (const auto &v : dba.Vertices(dba.Label(kStartLabel), false)) {
-      frame[expand_variable.input_symbol_] = query::TypedValue(v);
+      frame[expand_variable.input_symbol_] =
+          query::TypedValue(query::VertexAccessor(v));
       for (const auto &dest : dba.Vertices(false)) {
-        frame[dest_symbol] = query::TypedValue(dest);
+        frame[dest_symbol] = query::TypedValue(query::VertexAccessor(dest));
         while (cursor->Pull(frame, execution_context)) per_pull_memory.Reset();
       }
     }
@@ -302,15 +309,17 @@ static void ExpandWeightedShortest(benchmark::State &state) {
   TMemory per_pull_memory;
   query::EvaluationContext evaluation_context{per_pull_memory.get()};
   while (state.KeepRunning()) {
-    query::ExecutionContext execution_context{&dba, symbol_table,
+    query::DbAccessor execution_dba(&dba);
+    query::ExecutionContext execution_context{&execution_dba, symbol_table,
                                               evaluation_context};
     TMemory memory;
     query::Frame frame(symbol_table.max_position(), memory.get());
     auto cursor = expand_variable.MakeCursor(memory.get());
     for (const auto &v : dba.Vertices(dba.Label(kStartLabel), false)) {
-      frame[expand_variable.input_symbol_] = query::TypedValue(v);
+      frame[expand_variable.input_symbol_] =
+          query::TypedValue(query::VertexAccessor(v));
       for (const auto &dest : dba.Vertices(false)) {
-        frame[dest_symbol] = query::TypedValue(dest);
+        frame[dest_symbol] = query::TypedValue(query::VertexAccessor(dest));
         while (cursor->Pull(frame, execution_context)) per_pull_memory.Reset();
       }
     }
@@ -352,7 +361,8 @@ static void Accumulate(benchmark::State &state) {
   TMemory per_pull_memory;
   query::EvaluationContext evaluation_context{per_pull_memory.get()};
   while (state.KeepRunning()) {
-    query::ExecutionContext execution_context{&dba, symbol_table,
+    query::DbAccessor execution_dba(&dba);
+    query::ExecutionContext execution_context{&execution_dba, symbol_table,
                                               evaluation_context};
     TMemory memory;
     query::Frame frame(symbol_table.max_position(), memory.get());
@@ -405,7 +415,8 @@ static void Aggregate(benchmark::State &state) {
   TMemory per_pull_memory;
   query::EvaluationContext evaluation_context{per_pull_memory.get()};
   while (state.KeepRunning()) {
-    query::ExecutionContext execution_context{&dba, symbol_table,
+    query::DbAccessor execution_dba(&dba);
+    query::ExecutionContext execution_context{&execution_dba, symbol_table,
                                               evaluation_context};
     TMemory memory;
     query::Frame frame(symbol_table.max_position(), memory.get());
@@ -459,7 +470,8 @@ static void OrderBy(benchmark::State &state) {
   TMemory per_pull_memory;
   query::EvaluationContext evaluation_context{per_pull_memory.get()};
   while (state.KeepRunning()) {
-    query::ExecutionContext execution_context{&dba, symbol_table,
+    query::DbAccessor execution_dba(&dba);
+    query::ExecutionContext execution_context{&execution_dba, symbol_table,
                                               evaluation_context};
     TMemory memory;
     query::Frame frame(symbol_table.max_position(), memory.get());
@@ -500,7 +512,8 @@ static void Unwind(benchmark::State &state) {
   TMemory per_pull_memory;
   query::EvaluationContext evaluation_context{per_pull_memory.get()};
   while (state.KeepRunning()) {
-    query::ExecutionContext execution_context{&dba, symbol_table,
+    query::DbAccessor execution_dba(&dba);
+    query::ExecutionContext execution_context{&execution_dba, symbol_table,
                                               evaluation_context};
     TMemory memory;
     query::Frame frame(symbol_table.max_position(), memory.get());
diff --git a/tests/benchmark/query/planner.cpp b/tests/benchmark/query/planner.cpp
index 3187138e8..3dbad357b 100644
--- a/tests/benchmark/query/planner.cpp
+++ b/tests/benchmark/query/planner.cpp
@@ -42,8 +42,9 @@ static void BM_PlanChainedMatches(benchmark::State &state) {
     int num_matches = state.range(0);
     auto *query = AddChainedMatches(num_matches, storage);
     auto symbol_table = query::MakeSymbolTable(query);
+    query::DbAccessor exec_dba(&dba);
     auto ctx = query::plan::MakePlanningContext(&storage, &symbol_table, query,
-                                                &dba);
+                                                &exec_dba);
     state.ResumeTiming();
     auto query_parts =
         query::plan::CollectQueryParts(symbol_table, storage, query);
@@ -121,8 +122,9 @@ static void BM_PlanAndEstimateIndexedMatching(benchmark::State &state) {
     auto *query = AddIndexedMatches(index_count, label, prop, storage);
     auto symbol_table = query::MakeSymbolTable(query);
     state.ResumeTiming();
+    query::DbAccessor exec_dba(&dba);
     auto ctx = query::plan::MakePlanningContext(&storage, &symbol_table, query,
-                                                &dba);
+                                                &exec_dba);
     auto query_parts =
         query::plan::CollectQueryParts(symbol_table, storage, query);
     if (query_parts.query_parts.size() == 0) {
@@ -146,7 +148,8 @@ static void BM_PlanAndEstimateIndexedMatchingWithCachedCounts(
   int vertex_count = state.range(1);
   std::tie(label, prop) = CreateIndexedVertices(index_count, vertex_count, db);
   auto dba = db.Access();
-  auto vertex_counts = query::plan::MakeVertexCountCache(&dba);
+  query::DbAccessor exec_dba(&dba);
+  auto vertex_counts = query::plan::MakeVertexCountCache(&exec_dba);
   query::Parameters parameters;
   while (state.KeepRunning()) {
     state.PauseTiming();
diff --git a/tests/manual/interactive_planning.cpp b/tests/manual/interactive_planning.cpp
index 901c28860..b833400ac 100644
--- a/tests/manual/interactive_planning.cpp
+++ b/tests/manual/interactive_planning.cpp
@@ -133,9 +133,9 @@ class InteractiveDbAccessor {
                         Timer &timer)
       : dba_(dba), vertices_count_(vertices_count), timer_(timer) {}
 
-  auto Label(const std::string &name) { return dba_->Label(name); }
-  auto Property(const std::string &name) { return dba_->Property(name); }
-  auto EdgeType(const std::string &name) { return dba_->EdgeType(name); }
+  auto NameToLabel(const std::string &name) { return dba_->Label(name); }
+  auto NameToProperty(const std::string &name) { return dba_->Property(name); }
+  auto NameToEdgeType(const std::string &name) { return dba_->EdgeType(name); }
 
   int64_t VerticesCount() { return vertices_count_; }
 
@@ -368,7 +368,8 @@ DEFCOMMAND(Top) {
   for (int64_t i = 0; i < n_plans; ++i) {
     std::cout << "---- Plan #" << i << " ---- " << std::endl;
     std::cout << "cost: " << plans[i].cost << std::endl;
-    query::plan::PrettyPrint(dba, plans[i].final_plan.get());
+    query::DbAccessor query_dba(&dba);
+    query::plan::PrettyPrint(query_dba, plans[i].final_plan.get());
     std::cout << std::endl;
   }
 }
@@ -381,7 +382,8 @@ DEFCOMMAND(Show) {
   const auto &plan = plans[plan_ix].final_plan;
   auto cost = plans[plan_ix].cost;
   std::cout << "Plan cost: " << cost << std::endl;
-  query::plan::PrettyPrint(dba, plan.get());
+  query::DbAccessor query_dba(&dba);
+  query::plan::PrettyPrint(query_dba, plan.get());
 }
 
 DEFCOMMAND(ShowUnoptimized) {
@@ -390,7 +392,8 @@ DEFCOMMAND(ShowUnoptimized) {
   ss >> plan_ix;
   if (ss.fail() || !ss.eof() || plan_ix >= plans.size()) return;
   const auto &plan = plans[plan_ix].unoptimized_plan;
-  query::plan::PrettyPrint(dba, plan.get());
+  query::DbAccessor query_dba(&dba);
+  query::plan::PrettyPrint(query_dba, plan.get());
 }
 
 DEFCOMMAND(Help);
diff --git a/tests/manual/single_query.cpp b/tests/manual/single_query.cpp
index 49a4f3398..57a753217 100644
--- a/tests/manual/single_query.cpp
+++ b/tests/manual/single_query.cpp
@@ -13,9 +13,10 @@ int main(int argc, char *argv[]) {
   }
   database::GraphDb db;
   auto dba = db.Access();
+  query::DbAccessor query_dba(&dba);
   ResultStreamFaker<query::TypedValue> stream;
-  auto results =
-      query::Interpreter()(argv[1], dba, {}, false, utils::NewDeleteResource());
+  auto results = query::Interpreter()(argv[1], &query_dba, {}, false,
+                                      utils::NewDeleteResource());
   stream.Header(results.header());
   results.PullAll(stream);
   stream.Summary(results.summary());
diff --git a/tests/qa/tests/memgraph_V1/features/memgraph.feature b/tests/qa/tests/memgraph_V1/features/memgraph.feature
index b73640906..8a13a5a74 100644
--- a/tests/qa/tests/memgraph_V1/features/memgraph.feature
+++ b/tests/qa/tests/memgraph_V1/features/memgraph.feature
@@ -31,7 +31,7 @@ Feature: Memgraph only tests (queries in which we choose to be incompatible with
         Given an empty graph
         When executing query:
             """
-            CREATE(a:A), (b:B), (c:C), (a)-[:T]->(b) WITH a DETACH DELETE a WITH a MATCH()-[r:T]->() RETURN r
+            CREATE(a:A), (b:B), (c:C), (a)-[:T]->(b) WITH a DETACH DELETE a WITH a MATCH(a)-[r:T]->() RETURN r
             """
         Then an error should be raised
 
diff --git a/tests/unit/CMakeLists.txt b/tests/unit/CMakeLists.txt
index 199aad11f..b4913029a 100644
--- a/tests/unit/CMakeLists.txt
+++ b/tests/unit/CMakeLists.txt
@@ -6,7 +6,7 @@ function(add_unit_test test_cpp)
   # get exec name (remove extension from the abs path)
   get_filename_component(exec_name ${test_cpp} NAME_WE)
   set(target_name ${test_prefix}${exec_name})
-  add_executable(${target_name} ${test_cpp})
+  add_executable(${target_name} ${test_cpp} ${ARGN})
   # OUTPUT_NAME sets the real name of a target when it is built and can be
   # used to help create two targets of the same name even though CMake
   # requires unique logical target names
@@ -132,6 +132,37 @@ target_link_libraries(${test_prefix}query_plan_bag_semantics mg-single-node kvst
 add_unit_test(query_plan_create_set_remove_delete.cpp)
 target_link_libraries(${test_prefix}query_plan_create_set_remove_delete mg-single-node kvstore_dummy_lib)
 
+# Storage V2 in query execution
+define_add_lcp(add_lcp_query_plan_v2_create_set_remove_delete
+  lcp_query_plan_v2_create_set_remove_delete
+  generated_lcp_query_plan_v2_create_set_remove_delete_files)
+
+add_lcp_query_plan_v2_create_set_remove_delete(
+  ${CMAKE_SOURCE_DIR}/src/query/frontend/ast/ast.lcp)
+add_lcp_query_plan_v2_create_set_remove_delete(
+  ${CMAKE_SOURCE_DIR}/src/query/frontend/semantic/symbol.lcp)
+add_lcp_query_plan_v2_create_set_remove_delete(
+  ${CMAKE_SOURCE_DIR}/src/query/plan/operator.lcp)
+
+add_custom_target(generate_lcp_query_plan_v2_create_set_remove_delete DEPENDS
+  ${generated_lcp_query_plan_v2_create_set_remove_delete_files})
+
+add_unit_test(query_plan_v2_create_set_remove_delete.cpp
+  ${lcp_query_plan_v2_create_set_remove_delete}
+  ${CMAKE_SOURCE_DIR}/src/query/common.cpp
+  # ${CMAKE_SOURCE_DIR}/src/query/frontend/ast/ast.lcp.cpp
+  ${CMAKE_SOURCE_DIR}/src/query/frontend/ast/pretty_print.cpp
+  ${CMAKE_SOURCE_DIR}/src/query/plan/operator.cpp
+  # ${CMAKE_SOURCE_DIR}/src/query/plan/operator.lcp.cpp
+  ${CMAKE_SOURCE_DIR}/src/query/typed_value.cpp)
+target_compile_definitions(${test_prefix}query_plan_v2_create_set_remove_delete PUBLIC MG_SINGLE_NODE_V2)
+target_link_libraries(${test_prefix}query_plan_v2_create_set_remove_delete glog cppitertools)
+target_link_libraries(${test_prefix}query_plan_v2_create_set_remove_delete mg-storage-v2)
+add_dependencies(${test_prefix}query_plan_v2_create_set_remove_delete
+  generate_lcp_query_plan_v2_create_set_remove_delete)
+
+# END Storage V2 in query execution
+
 add_unit_test(query_plan_edge_cases.cpp)
 target_link_libraries(${test_prefix}query_plan_edge_cases mg-single-node kvstore_dummy_lib)
 
diff --git a/tests/unit/bfs_common.hpp b/tests/unit/bfs_common.hpp
index ef1477e2a..38e4fabb4 100644
--- a/tests/unit/bfs_common.hpp
+++ b/tests/unit/bfs_common.hpp
@@ -228,8 +228,8 @@ std::unique_ptr<query::plan::LogicalOperator> YieldVertices(
   std::vector<std::vector<query::TypedValue>> frames;
   frames.push_back(std::vector<query::TypedValue>{query::TypedValue()});
   for (const auto &vertex : vertices) {
-    frames.emplace_back(std::vector<query::TypedValue>{
-        query::TypedValue(VertexAccessor(vertex, *dba))});
+    frames.emplace_back(std::vector<query::TypedValue>{query::TypedValue(
+        query::VertexAccessor(VertexAccessor(vertex, *dba)))});
   }
   return std::make_unique<Yield>(input_op, std::vector<query::Symbol>{symbol},
                                  frames);
@@ -242,12 +242,12 @@ std::unique_ptr<query::plan::LogicalOperator> YieldEntities(
     std::shared_ptr<query::plan::LogicalOperator> input_op) {
   std::vector<std::vector<query::TypedValue>> frames;
   for (const auto &vertex : vertices) {
-    frames.emplace_back(std::vector<query::TypedValue>{
-        query::TypedValue(VertexAccessor(vertex, *dba))});
+    frames.emplace_back(std::vector<query::TypedValue>{query::TypedValue(
+        query::VertexAccessor(VertexAccessor(vertex, *dba)))});
   }
   for (const auto &edge : edges) {
     frames.emplace_back(std::vector<query::TypedValue>{
-        query::TypedValue(EdgeAccessor(edge, *dba))});
+        query::TypedValue(query::EdgeAccessor(EdgeAccessor(edge, *dba)))});
   }
   return std::make_unique<Yield>(input_op, std::vector<query::Symbol>{symbol},
                                  frames);
@@ -259,20 +259,30 @@ auto GetProp(const RecordAccessor<TRecord> &rec, std::string prop,
   return rec.PropsAt(dba->Property(prop));
 }
 
+inline auto GetProp(const query::VertexAccessor &rec, std::string prop,
+                    database::GraphDbAccessor *dba) {
+  return GetProp(rec.impl_, prop, dba);
+}
+
+inline auto GetProp(const query::EdgeAccessor &rec, std::string prop,
+                    database::GraphDbAccessor *dba) {
+  return GetProp(rec.impl_, prop, dba);
+}
+
 // Checks if the given path is actually a path from source to sink and if all
 // of its edges exist in the given edge list.
 template <class TPathAllocator>
-void CheckPath(database::GraphDbAccessor *dba, const VertexAccessor &source,
-               const VertexAccessor &sink,
+void CheckPath(database::GraphDbAccessor *dba, const query::VertexAccessor &source,
+               const query::VertexAccessor &sink,
                const std::vector<query::TypedValue, TPathAllocator> &path,
                const std::vector<std::pair<int, int>> &edges) {
-  VertexAccessor curr = source;
+  auto curr = source;
   for (const auto &edge_tv : path) {
     ASSERT_TRUE(edge_tv.IsEdge());
     auto edge = edge_tv.ValueEdge();
 
-    ASSERT_TRUE(edge.from() == curr || edge.to() == curr);
-    VertexAccessor next = edge.from_is(curr) ? edge.to() : edge.from();
+    ASSERT_TRUE(edge.From() == curr || edge.To() == curr);
+    auto next = edge.From() == curr ? edge.To() : edge.From();
 
     int from = GetProp(curr, "id", dba).ValueInt();
     int to = GetProp(next, "id", dba).ValueInt();
@@ -311,7 +321,8 @@ void BfsTest(Database *db, int lower_bound, int upper_bound,
   auto dba_ptr = db->Access();
   auto &dba = *dba_ptr;
   query::AstStorage storage;
-  query::ExecutionContext context{dba_ptr.get()};
+  query::DbAccessor execution_dba(&dba);
+  query::ExecutionContext context{&execution_dba};
   query::Symbol blocked_sym =
       context.symbol_table.CreateSymbol("blocked", true);
   query::Symbol source_sym = context.symbol_table.CreateSymbol("source", true);
@@ -343,8 +354,7 @@ void BfsTest(Database *db, int lower_bound, int upper_bound,
       // No filter lambda, nothing is ever blocked.
       input_op = std::make_shared<Yield>(
           nullptr, std::vector<query::Symbol>{blocked_sym},
-          std::vector<std::vector<query::TypedValue>>{
-              {query::TypedValue()}});
+          std::vector<std::vector<query::TypedValue>>{{query::TypedValue()}});
       filter_expr = nullptr;
       break;
     case FilterLambdaType::USE_FRAME:
@@ -364,8 +374,8 @@ void BfsTest(Database *db, int lower_bound, int upper_bound,
       // We only block vertex #5 and run BFS.
       input_op = std::make_shared<Yield>(
           nullptr, std::vector<query::Symbol>{blocked_sym},
-          std::vector<std::vector<query::TypedValue>>{
-              {query::TypedValue(VertexAccessor(vertices[5], *dba_ptr))}});
+          std::vector<std::vector<query::TypedValue>>{{query::TypedValue(
+              query::VertexAccessor(VertexAccessor(vertices[5], *dba_ptr)))}});
       filter_expr = NEQ(PROPERTY_LOOKUP(inner_node, PROPERTY_PAIR("id")),
                         PARAMETER_LOOKUP(0));
       context.evaluation_context.parameters.Add(0, PropertyValue(5));
@@ -399,9 +409,9 @@ void BfsTest(Database *db, int lower_bound, int upper_bound,
                                    filter_expr});
 
   context.evaluation_context.properties =
-      query::NamesToProperties(storage.properties_, &dba);
+      query::NamesToProperties(storage.properties_, &execution_dba);
   context.evaluation_context.labels =
-      query::NamesToLabels(storage.labels_, &dba);
+      query::NamesToLabels(storage.labels_, &execution_dba);
   std::vector<std::vector<query::TypedValue>> results;
 
   // An exception should be thrown on one of the pulls.
diff --git a/tests/unit/bfs_single_node.cpp b/tests/unit/bfs_single_node.cpp
index 3b9a13b5c..e0cff0e26 100644
--- a/tests/unit/bfs_single_node.cpp
+++ b/tests/unit/bfs_single_node.cpp
@@ -48,8 +48,8 @@ class SingleNodeDb : public Database {
       int u, v;
       std::string type;
       std::tie(u, v, type) = e;
-      VertexAccessor from(vertex_addr[u], *dba);
-      VertexAccessor to(vertex_addr[v], *dba);
+      ::VertexAccessor from(vertex_addr[u], *dba);
+      ::VertexAccessor to(vertex_addr[v], *dba);
       auto edge = dba->InsertEdge(from, to, dba->EdgeType(type));
       edge.PropsSet(dba->Property("from"), PropertyValue(u));
       edge.PropsSet(dba->Property("to"), PropertyValue(v));
diff --git a/tests/unit/bolt_encoder.cpp b/tests/unit/bolt_encoder.cpp
index 4870708bf..d6d708958 100644
--- a/tests/unit/bolt_encoder.cpp
+++ b/tests/unit/bolt_encoder.cpp
@@ -189,9 +189,12 @@ TEST(BoltEncoder, VertexAndEdge) {
 
   // check everything
   std::vector<Value> vals;
-  vals.push_back(glue::ToBoltValue(query::TypedValue(va1), storage::View::NEW));
-  vals.push_back(glue::ToBoltValue(query::TypedValue(va2), storage::View::NEW));
-  vals.push_back(glue::ToBoltValue(query::TypedValue(ea), storage::View::NEW));
+  vals.push_back(glue::ToBoltValue(
+      query::TypedValue(query::VertexAccessor(va1)), storage::View::NEW));
+  vals.push_back(glue::ToBoltValue(
+      query::TypedValue(query::VertexAccessor(va2)), storage::View::NEW));
+  vals.push_back(glue::ToBoltValue(query::TypedValue(query::EdgeAccessor(ea)),
+                                   storage::View::NEW));
   bolt_encoder.MessageRecord(vals);
 
   // The vertexedge_encoded testdata has hardcoded zeros for IDs,
diff --git a/tests/unit/database_dump.cpp b/tests/unit/database_dump.cpp
index 50f3add6f..13dbf7553 100644
--- a/tests/unit/database_dump.cpp
+++ b/tests/unit/database_dump.cpp
@@ -184,7 +184,8 @@ class DatabaseEnvironment {
 void Execute(GraphDbAccessor *dba, const std::string &query) {
   CHECK(dba);
   ResultStreamFaker<query::TypedValue> results;
-  query::Interpreter()(query, *dba, {}, false, utils::NewDeleteResource())
+  query::DbAccessor query_dba(dba);
+  query::Interpreter()(query, &query_dba, {}, false, utils::NewDeleteResource())
       .PullAll(results);
 }
 
@@ -227,7 +228,8 @@ EdgeAccessor CreateEdge(GraphDbAccessor *dba, VertexAccessor from,
 TEST(DumpTest, EmptyGraph) {
   DatabaseEnvironment db;
   auto dba = db.Access();
-  CypherDumpGenerator dump(&dba);
+  query::DbAccessor query_dba(&dba);
+  CypherDumpGenerator dump(&query_dba);
   EXPECT_EQ(DumpNext(&dump), "");
 }
 
@@ -242,7 +244,8 @@ TEST(DumpTest, SingleVertex) {
 
   {
     auto dba = db.Access();
-    CypherDumpGenerator dump(&dba);
+    query::DbAccessor query_dba(&dba);
+    CypherDumpGenerator dump(&query_dba);
     EXPECT_EQ(DumpNext(&dump), kCreateInternalIndex);
     EXPECT_EQ(DumpNext(&dump), "CREATE (:__mg_vertex__ {__mg_id__: 0});");
     EXPECT_EQ(DumpNext(&dump), kDropInternalIndex);
@@ -262,7 +265,8 @@ TEST(DumpTest, VertexWithSingleLabel) {
 
   {
     auto dba = db.Access();
-    CypherDumpGenerator dump(&dba);
+    query::DbAccessor query_dba(&dba);
+    CypherDumpGenerator dump(&query_dba);
     EXPECT_EQ(DumpNext(&dump), kCreateInternalIndex);
     EXPECT_EQ(DumpNext(&dump),
               "CREATE (:__mg_vertex__:Label1 {__mg_id__: 0});");
@@ -283,7 +287,8 @@ TEST(DumpTest, VertexWithMultipleLabels) {
 
   {
     auto dba = db.Access();
-    CypherDumpGenerator dump(&dba);
+    query::DbAccessor query_dba(&dba);
+    CypherDumpGenerator dump(&query_dba);
     EXPECT_EQ(DumpNext(&dump), kCreateInternalIndex);
     EXPECT_EQ(DumpNext(&dump),
               "CREATE (:__mg_vertex__:Label1:Label2 {__mg_id__: 0});");
@@ -304,7 +309,8 @@ TEST(DumpTest, VertexWithSingleProperty) {
 
   {
     auto dba = db.Access();
-    CypherDumpGenerator dump(&dba);
+    query::DbAccessor query_dba(&dba);
+    CypherDumpGenerator dump(&query_dba);
     EXPECT_EQ(DumpNext(&dump), kCreateInternalIndex);
     EXPECT_EQ(DumpNext(&dump),
               "CREATE (:__mg_vertex__ {__mg_id__: 0, prop: 42});");
@@ -327,7 +333,8 @@ TEST(DumpTest, MultipleVertices) {
 
   {
     auto dba = db.Access();
-    CypherDumpGenerator dump(&dba);
+    query::DbAccessor query_dba(&dba);
+    CypherDumpGenerator dump(&query_dba);
     EXPECT_EQ(DumpNext(&dump), kCreateInternalIndex);
     EXPECT_EQ(DumpNext(&dump), "CREATE (:__mg_vertex__ {__mg_id__: 0});");
     EXPECT_EQ(DumpNext(&dump), "CREATE (:__mg_vertex__ {__mg_id__: 1});");
@@ -351,7 +358,8 @@ TEST(DumpTest, SingleEdge) {
 
   {
     auto dba = db.Access();
-    CypherDumpGenerator dump(&dba);
+    query::DbAccessor query_dba(&dba);
+    CypherDumpGenerator dump(&query_dba);
     EXPECT_EQ(DumpNext(&dump), kCreateInternalIndex);
     EXPECT_EQ(DumpNext(&dump), "CREATE (:__mg_vertex__ {__mg_id__: 0});");
     EXPECT_EQ(DumpNext(&dump), "CREATE (:__mg_vertex__ {__mg_id__: 1});");
@@ -380,7 +388,8 @@ TEST(DumpTest, MultipleEdges) {
 
   {
     auto dba = db.Access();
-    CypherDumpGenerator dump(&dba);
+    query::DbAccessor query_dba(&dba);
+    CypherDumpGenerator dump(&query_dba);
     EXPECT_EQ(DumpNext(&dump), kCreateInternalIndex);
     EXPECT_EQ(DumpNext(&dump), "CREATE (:__mg_vertex__ {__mg_id__: 0});");
     EXPECT_EQ(DumpNext(&dump), "CREATE (:__mg_vertex__ {__mg_id__: 1});");
@@ -413,7 +422,8 @@ TEST(DumpTest, EdgeWithProperties) {
 
   {
     auto dba = db.Access();
-    CypherDumpGenerator dump(&dba);
+    query::DbAccessor query_dba(&dba);
+    CypherDumpGenerator dump(&query_dba);
     EXPECT_EQ(DumpNext(&dump), kCreateInternalIndex);
     EXPECT_EQ(DumpNext(&dump), "CREATE (:__mg_vertex__ {__mg_id__: 0});");
     EXPECT_EQ(DumpNext(&dump), "CREATE (:__mg_vertex__ {__mg_id__: 1});");
@@ -439,7 +449,8 @@ TEST(DumpTest, IndicesKeys) {
 
   {
     auto dba = db.Access();
-    CypherDumpGenerator dump(&dba);
+    query::DbAccessor query_dba(&dba);
+    CypherDumpGenerator dump(&query_dba);
     EXPECT_EQ(DumpNext(&dump), "CREATE INDEX ON :Label1(prop);");
     EXPECT_EQ(DumpNext(&dump), "CREATE INDEX ON :Label2(prop);");
     EXPECT_EQ(DumpNext(&dump), kCreateInternalIndex);
@@ -467,7 +478,8 @@ TEST(DumpTest, UniqueConstraints) {
 
   {
     auto dba = db.Access();
-    CypherDumpGenerator dump(&dba);
+    query::DbAccessor query_dba(&dba);
+    CypherDumpGenerator dump(&query_dba);
     EXPECT_EQ(DumpNext(&dump), kCreateInternalIndex);
     EXPECT_EQ(DumpNext(&dump),
               "CREATE CONSTRAINT ON (u:Label) ASSERT u.prop IS UNIQUE;");
@@ -498,7 +510,8 @@ TEST(DumpTest, CheckStateVertexWithMultipleProperties) {
   DatabaseEnvironment db_dump;
   {
     auto dba = db.Access();
-    CypherDumpGenerator dump(&dba);
+    query::DbAccessor query_dba(&dba);
+    CypherDumpGenerator dump(&query_dba);
     std::string cmd;
     while (!(cmd = DumpNext(&dump)).empty()) {
       auto dba_dump = db_dump.Access();
@@ -540,7 +553,8 @@ TEST(DumpTest, CheckStateSimpleGraph) {
   DatabaseEnvironment db_dump;
   {
     auto dba = db.Access();
-    CypherDumpGenerator dump(&dba);
+    query::DbAccessor query_dba(&dba);
+    CypherDumpGenerator dump(&query_dba);
     std::string cmd;
     while (!(cmd = DumpNext(&dump)).empty()) {
       auto dba_dump = db_dump.Access();
@@ -564,11 +578,11 @@ TEST(DumpTest, ExecuteDumpDatabase) {
 
   {
     auto dba = db.Access();
+    query::DbAccessor query_dba(&dba);
     const std::string query = "DUMP DATABASE";
     ResultStreamFaker<query::TypedValue> stream;
-    auto results =
-        query::Interpreter()(query, dba, {}, false, utils::NewDeleteResource());
-
+    auto results = query::Interpreter()(query, &query_dba, {}, false,
+                                        utils::NewDeleteResource());
     stream.Header(results.header());
     results.PullAll(stream);
     stream.Summary(results.summary());
diff --git a/tests/unit/database_transaction_timeout.cpp b/tests/unit/database_transaction_timeout.cpp
index f824f0ff9..8c2f23ea8 100644
--- a/tests/unit/database_transaction_timeout.cpp
+++ b/tests/unit/database_transaction_timeout.cpp
@@ -12,8 +12,9 @@ TEST(TransactionTimeout, TransactionTimeout) {
   database::GraphDb db;
   query::Interpreter interpreter;
   auto interpret = [&](auto &dba, const std::string &query) {
+    query::DbAccessor query_dba(&dba);
     ResultStreamFaker<query::TypedValue> stream;
-    interpreter(query, dba, {}, false, utils::NewDeleteResource())
+    interpreter(query, &query_dba, {}, false, utils::NewDeleteResource())
         .PullAll(stream);
   };
   {
@@ -23,8 +24,7 @@ TEST(TransactionTimeout, TransactionTimeout) {
   {
     auto dba = db.Access();
     std::this_thread::sleep_for(std::chrono::seconds(5));
-    ASSERT_THROW(interpret(dba, "MATCH (n) RETURN n"),
-                 query::HintedAbortError);
+    ASSERT_THROW(interpret(dba, "MATCH (n) RETURN n"), query::HintedAbortError);
   }
   {
     auto dba = db.Access();
diff --git a/tests/unit/interpreter.cpp b/tests/unit/interpreter.cpp
index d1da8f035..2d51c3406 100644
--- a/tests/unit/interpreter.cpp
+++ b/tests/unit/interpreter.cpp
@@ -20,9 +20,10 @@ class InterpreterTest : public ::testing::Test {
   auto Interpret(const std::string &query,
                  const std::map<std::string, PropertyValue> &params = {}) {
     auto dba = db_.Access();
+    query::DbAccessor query_dba(&dba);
     ResultStreamFaker<query::TypedValue> stream;
-    auto results =
-        interpreter_(query, dba, params, false, utils::NewDeleteResource());
+    auto results = interpreter_(query, &query_dba, params, false,
+                                utils::NewDeleteResource());
     stream.Header(results.header());
     results.PullAll(stream);
     stream.Summary(results.summary());
@@ -207,11 +208,12 @@ TEST_F(InterpreterTest, Bfs) {
   }
 
   auto dba = db_.Access();
+  query::DbAccessor query_dba(&dba);
   ResultStreamFaker<query::TypedValue> stream;
   auto results = interpreter_(
       "MATCH (n {id: 0})-[r *bfs..5 (e, n | n.reachable and "
       "e.reachable)]->(m) RETURN r",
-      dba, {}, false, utils::NewDeleteResource());
+      &query_dba, {}, false, utils::NewDeleteResource());
   stream.Header(results.header());
   results.PullAll(stream);
   stream.Summary(results.summary());
@@ -231,14 +233,14 @@ TEST_F(InterpreterTest, Bfs) {
     EXPECT_EQ(edges.size(), expected_level);
     // Check that starting node is correct.
     EXPECT_EQ(
-        edges[0].from().PropsAt(dba.Property(kId)).ValueInt(),
+        edges[0].impl_.from().PropsAt(dba.Property(kId)).ValueInt(),
         0);
     for (int i = 1; i < static_cast<int>(edges.size()); ++i) {
       // Check that edges form a connected path.
-      EXPECT_EQ(edges[i - 1].to(), edges[i].from());
+      EXPECT_EQ(edges[i - 1].To(), edges[i].From());
     }
     auto matched_id =
-        edges.back().to().PropsAt(dba.Property(kId)).ValueInt();
+        edges.back().impl_.to().PropsAt(dba.Property(kId)).ValueInt();
     // Check that we didn't match that node already.
     EXPECT_TRUE(matched_ids.insert(matched_id).second);
     // Check that shortest path was found.
@@ -254,7 +256,8 @@ TEST_F(InterpreterTest, Bfs) {
 TEST_F(InterpreterTest, CreateIndexInMulticommandTransaction) {
   ResultStreamFaker<query::TypedValue> stream;
   auto dba = db_.Access();
-  ASSERT_THROW(interpreter_("CREATE INDEX ON :X(y)", dba, {}, true,
+  query::DbAccessor query_dba(&dba);
+  ASSERT_THROW(interpreter_("CREATE INDEX ON :X(y)", &query_dba, {}, true,
                             utils::NewDeleteResource())
                    .PullAll(stream),
                query::IndexInMulticommandTxException);
@@ -265,10 +268,11 @@ TEST_F(InterpreterTest, ShortestPath) {
   {
     ResultStreamFaker<query::TypedValue> stream;
     auto dba = db_.Access();
+    query::DbAccessor query_dba(&dba);
     interpreter_(
         "CREATE (n:A {x: 1}), (m:B {x: 2}), (l:C {x: 1}), (n)-[:r1 {w: 1 "
         "}]->(m)-[:r2 {w: 2}]->(l), (n)-[:r3 {w: 4}]->(l)",
-        dba, {}, true, utils::NewDeleteResource())
+        &query_dba, {}, true, utils::NewDeleteResource())
         .PullAll(stream);
 
     dba.Commit();
@@ -276,9 +280,10 @@ TEST_F(InterpreterTest, ShortestPath) {
 
   ResultStreamFaker<query::TypedValue> stream;
   auto dba = db_.Access();
+  query::DbAccessor query_dba(&dba);
   auto results =
       interpreter_("MATCH (n)-[e *wshortest 5 (e, n | e.w) ]->(m) return e",
-                   dba, {}, false, utils::NewDeleteResource());
+                   &query_dba, {}, false, utils::NewDeleteResource());
   stream.Header(results.header());
   results.PullAll(stream);
   stream.Summary(results.summary());
@@ -315,15 +320,17 @@ TEST_F(InterpreterTest, UniqueConstraintTest) {
   ResultStreamFaker<query::TypedValue> stream;
   {
     auto dba = db_.Access();
-    interpreter_("CREATE CONSTRAINT ON (n:A) ASSERT n.a, n.b IS UNIQUE;", dba,
-                 {}, true, utils::NewDeleteResource())
+    query::DbAccessor query_dba(&dba);
+    interpreter_("CREATE CONSTRAINT ON (n:A) ASSERT n.a, n.b IS UNIQUE;",
+                 &query_dba, {}, true, utils::NewDeleteResource())
         .PullAll(stream);
     dba.Commit();
   }
 
   {
     auto dba = db_.Access();
-    interpreter_("CREATE (:A{a:1, b:1})", dba, {}, true,
+    query::DbAccessor query_dba(&dba);
+    interpreter_("CREATE (:A{a:1, b:1})", &query_dba, {}, true,
                  utils::NewDeleteResource())
         .PullAll(stream);
     dba.Commit();
@@ -331,7 +338,8 @@ TEST_F(InterpreterTest, UniqueConstraintTest) {
 
   {
     auto dba = db_.Access();
-    interpreter_("CREATE (:A{a:2, b:2})", dba, {}, true,
+    query::DbAccessor query_dba(&dba);
+    interpreter_("CREATE (:A{a:2, b:2})", &query_dba, {}, true,
                  utils::NewDeleteResource())
         .PullAll(stream);
     dba.Commit();
@@ -339,7 +347,8 @@ TEST_F(InterpreterTest, UniqueConstraintTest) {
 
   {
     auto dba = db_.Access();
-    ASSERT_THROW(interpreter_("CREATE (:A{a:1, b:1})", dba, {}, true,
+    query::DbAccessor query_dba(&dba);
+    ASSERT_THROW(interpreter_("CREATE (:A{a:1, b:1})", &query_dba, {}, true,
                               utils::NewDeleteResource())
                      .PullAll(stream),
                  query::QueryRuntimeException);
@@ -348,10 +357,11 @@ TEST_F(InterpreterTest, UniqueConstraintTest) {
 
   {
     auto dba = db_.Access();
-    interpreter_("MATCH (n:A{a:2, b:2}) SET n.a=1", dba, {}, true,
+    query::DbAccessor query_dba(&dba);
+    interpreter_("MATCH (n:A{a:2, b:2}) SET n.a=1", &query_dba, {}, true,
                  utils::NewDeleteResource())
         .PullAll(stream);
-    interpreter_("CREATE (:A{a:2, b:2})", dba, {}, true,
+    interpreter_("CREATE (:A{a:2, b:2})", &query_dba, {}, true,
                  utils::NewDeleteResource())
         .PullAll(stream);
     dba.Commit();
@@ -359,10 +369,11 @@ TEST_F(InterpreterTest, UniqueConstraintTest) {
 
   {
     auto dba = db_.Access();
-    interpreter_("MATCH (n:A{a:2, b:2}) DETACH DELETE n", dba, {}, true,
+    query::DbAccessor query_dba(&dba);
+    interpreter_("MATCH (n:A{a:2, b:2}) DETACH DELETE n", &query_dba, {}, true,
                  utils::NewDeleteResource())
         .PullAll(stream);
-    interpreter_("CREATE (n:A{a:2, b:2})", dba, {}, true,
+    interpreter_("CREATE (n:A{a:2, b:2})", &query_dba, {}, true,
                  utils::NewDeleteResource())
         .PullAll(stream);
     dba.Commit();
diff --git a/tests/unit/plan_pretty_print.cpp b/tests/unit/plan_pretty_print.cpp
index 989c0cb83..ee0a8972b 100644
--- a/tests/unit/plan_pretty_print.cpp
+++ b/tests/unit/plan_pretty_print.cpp
@@ -39,7 +39,8 @@ class PrintToJsonTest : public ::testing::Test {
   }
 
   void Check(LogicalOperator *root, std::string expected) {
-    EXPECT_EQ(PlanToJson(dba, root), json::parse(expected));
+    query::DbAccessor query_dba(&dba);
+    EXPECT_EQ(PlanToJson(query_dba, root), json::parse(expected));
   }
 };
 
diff --git a/tests/unit/query_expression_evaluator.cpp b/tests/unit/query_expression_evaluator.cpp
index 4c9faddb9..ad615be29 100644
--- a/tests/unit/query_expression_evaluator.cpp
+++ b/tests/unit/query_expression_evaluator.cpp
@@ -38,7 +38,9 @@ class ExpressionEvaluatorTest : public ::testing::Test {
   SymbolTable symbol_table;
 
   Frame frame{128};
-  ExpressionEvaluator eval{&frame, symbol_table, ctx, &dba, storage::View::OLD};
+  query::DbAccessor execution_dba{&dba};
+  ExpressionEvaluator eval{&frame, symbol_table, ctx, &execution_dba,
+                           storage::View::OLD};
 
   Identifier *CreateIdentifierWithValue(std::string name,
                                         const TypedValue &value) {
@@ -51,8 +53,8 @@ class ExpressionEvaluatorTest : public ::testing::Test {
 
   template <class TExpression>
   auto Eval(TExpression *expr) {
-    ctx.properties = NamesToProperties(storage.properties_, &dba);
-    ctx.labels = NamesToLabels(storage.labels_, &dba);
+    ctx.properties = NamesToProperties(storage.properties_, &execution_dba);
+    ctx.labels = NamesToLabels(storage.labels_, &execution_dba);
     auto value = expr->Accept(eval);
     EXPECT_EQ(value.GetMemoryResource(), &mem)
         << "ExpressionEvaluator must use the MemoryResource from "
@@ -421,8 +423,10 @@ TEST_F(ExpressionEvaluatorTest, VertexAndEdgeIndexing) {
   v1.PropsSet(prop, PropertyValue(42));
   e11.PropsSet(prop, PropertyValue(43));
 
-  auto *vertex_id = CreateIdentifierWithValue("v1", TypedValue(v1));
-  auto *edge_id = CreateIdentifierWithValue("e11", TypedValue(e11));
+  auto *vertex_id =
+      CreateIdentifierWithValue("v1", TypedValue(query::VertexAccessor(v1)));
+  auto *edge_id =
+      CreateIdentifierWithValue("e11", TypedValue(query::EdgeAccessor(e11)));
   {
     // Legal indexing.
     auto *op1 = storage.Create<SubscriptOperator>(
@@ -635,7 +639,7 @@ TEST_F(ExpressionEvaluatorTest, LabelsTest) {
   auto *identifier = storage.Create<Identifier>("n");
   auto node_symbol = symbol_table.CreateSymbol("n", true);
   identifier->MapTo(node_symbol);
-  frame[node_symbol] = TypedValue(v1);
+  frame[node_symbol] = TypedValue(query::VertexAccessor(v1));
   {
     auto *op = storage.Create<LabelsTest>(
         identifier, std::vector<LabelIx>{storage.GetLabelIx("DOG"),
@@ -915,7 +919,7 @@ class ExpressionEvaluatorPropertyLookup : public ExpressionEvaluatorTest {
 TEST_F(ExpressionEvaluatorPropertyLookup, Vertex) {
   auto v1 = dba.InsertVertex();
   v1.PropsSet(prop_age.second, PropertyValue(10));
-  frame[symbol] = TypedValue(v1);
+  frame[symbol] = TypedValue(query::VertexAccessor(v1));
   EXPECT_EQ(Value(prop_age).ValueInt(), 10);
   EXPECT_TRUE(Value(prop_height).IsNull());
 }
@@ -925,7 +929,7 @@ TEST_F(ExpressionEvaluatorPropertyLookup, Edge) {
   auto v2 = dba.InsertVertex();
   auto e12 = dba.InsertEdge(v1, v2, dba.EdgeType("edge_type"));
   e12.PropsSet(prop_age.second, PropertyValue(10));
-  frame[symbol] = TypedValue(e12);
+  frame[symbol] = TypedValue(query::EdgeAccessor(e12));
   EXPECT_EQ(Value(prop_age).ValueInt(), 10);
   EXPECT_TRUE(Value(prop_height).IsNull());
 }
@@ -999,10 +1003,10 @@ TEST_F(FunctionTest, EndNode) {
   v1.add_label(dba.Label("label1"));
   auto v2 = dba.InsertVertex();
   v2.add_label(dba.Label("label2"));
-  auto e = dba.InsertEdge(v1, v2, dba.EdgeType("t"));
-  ASSERT_TRUE(EvaluateFunction("ENDNODE", e)
-                  .ValueVertex()
-                  .has_label(dba.Label("label2")));
+  query::EdgeAccessor e(dba.InsertEdge(v1, v2, dba.EdgeType("t")));
+  ASSERT_TRUE(*EvaluateFunction("ENDNODE", e)
+                   .ValueVertex()
+                   .HasLabel(storage::View::NEW, dba.Label("label2")));
   ASSERT_THROW(EvaluateFunction("ENDNODE", 2), QueryRuntimeException);
 }
 
@@ -1035,10 +1039,12 @@ TEST_F(FunctionTest, Properties) {
     return properties;
   };
 
-  ASSERT_THAT(prop_values_to_int(EvaluateFunction("PROPERTIES", v1)),
+  ASSERT_THAT(prop_values_to_int(
+                  EvaluateFunction("PROPERTIES", query::VertexAccessor(v1))),
               UnorderedElementsAre(testing::Pair("height", 5),
                                    testing::Pair("age", 10)));
-  ASSERT_THAT(prop_values_to_int(EvaluateFunction("PROPERTIES", e)),
+  ASSERT_THAT(prop_values_to_int(
+                  EvaluateFunction("PROPERTIES", query::EdgeAccessor(e))),
               UnorderedElementsAre(testing::Pair("height", 3),
                                    testing::Pair("age", 15)));
   ASSERT_THROW(EvaluateFunction("PROPERTIES", 2), QueryRuntimeException);
@@ -1069,11 +1075,12 @@ TEST_F(FunctionTest, Size) {
       3);
   ASSERT_THROW(EvaluateFunction("SIZE", 5), QueryRuntimeException);
 
-  auto v0 = dba.InsertVertex();
+  query::VertexAccessor v0(dba.InsertVertex());
   query::Path path(v0);
   EXPECT_EQ(EvaluateFunction("SIZE", path).ValueInt(), 0);
-  auto v1 = dba.InsertVertex();
-  path.Expand(dba.InsertEdge(v0, v1, dba.EdgeType("type")));
+  query::VertexAccessor v1(dba.InsertVertex());
+  path.Expand(query::EdgeAccessor(
+      dba.InsertEdge(v0.impl_, v1.impl_, dba.EdgeType("type"))));
   path.Expand(v1);
   EXPECT_EQ(EvaluateFunction("SIZE", path).ValueInt(), 1);
 }
@@ -1086,9 +1093,9 @@ TEST_F(FunctionTest, StartNode) {
   auto v2 = dba.InsertVertex();
   v2.add_label(dba.Label("label2"));
   auto e = dba.InsertEdge(v1, v2, dba.EdgeType("t"));
-  ASSERT_TRUE(EvaluateFunction("STARTNODE", e)
-                  .ValueVertex()
-                  .has_label(dba.Label("label1")));
+  ASSERT_TRUE(*EvaluateFunction("STARTNODE", query::EdgeAccessor(e))
+                   .ValueVertex()
+                   .HasLabel(storage::View::NEW, dba.Label("label1")));
   ASSERT_THROW(EvaluateFunction("STARTNODE", 2), QueryRuntimeException);
 }
 
@@ -1100,21 +1107,25 @@ TEST_F(FunctionTest, Degree) {
   auto v3 = dba.InsertVertex();
   auto e12 = dba.InsertEdge(v1, v2, dba.EdgeType("t"));
   dba.InsertEdge(v3, v2, dba.EdgeType("t"));
-  ASSERT_EQ(EvaluateFunction("DEGREE", v1).ValueInt(), 1);
-  ASSERT_EQ(EvaluateFunction("DEGREE", v2).ValueInt(), 2);
-  ASSERT_EQ(EvaluateFunction("DEGREE", v3).ValueInt(), 1);
+  ASSERT_EQ(EvaluateFunction("DEGREE", query::VertexAccessor(v1)).ValueInt(),
+            1);
+  ASSERT_EQ(EvaluateFunction("DEGREE", query::VertexAccessor(v2)).ValueInt(),
+            2);
+  ASSERT_EQ(EvaluateFunction("DEGREE", query::VertexAccessor(v3)).ValueInt(),
+            1);
   ASSERT_THROW(EvaluateFunction("DEGREE", 2), QueryRuntimeException);
-  ASSERT_THROW(EvaluateFunction("DEGREE", e12), QueryRuntimeException);
+  ASSERT_THROW(EvaluateFunction("DEGREE", query::EdgeAccessor(e12)),
+               QueryRuntimeException);
 }
 
 TEST_F(FunctionTest, InDegree) {
   ASSERT_THROW(EvaluateFunction("INDEGREE"), QueryRuntimeException);
   ASSERT_TRUE(EvaluateFunction("INDEGREE", TypedValue()).IsNull());
-  auto v1 = dba.InsertVertex();
-  auto v2 = dba.InsertVertex();
-  auto v3 = dba.InsertVertex();
-  auto e12 = dba.InsertEdge(v1, v2, dba.EdgeType("t"));
-  dba.InsertEdge(v3, v2, dba.EdgeType("t"));
+  query::VertexAccessor v1(dba.InsertVertex());
+  query::VertexAccessor v2(dba.InsertVertex());
+  query::VertexAccessor v3(dba.InsertVertex());
+  query::EdgeAccessor e12(dba.InsertEdge(v1.impl_, v2.impl_, dba.EdgeType("t")));
+  dba.InsertEdge(v3.impl_, v2.impl_, dba.EdgeType("t"));
   ASSERT_EQ(EvaluateFunction("INDEGREE", v1).ValueInt(), 0);
   ASSERT_EQ(EvaluateFunction("INDEGREE", v2).ValueInt(), 2);
   ASSERT_EQ(EvaluateFunction("INDEGREE", v3).ValueInt(), 0);
@@ -1125,11 +1136,12 @@ TEST_F(FunctionTest, InDegree) {
 TEST_F(FunctionTest, OutDegree) {
   ASSERT_THROW(EvaluateFunction("OUTDEGREE"), QueryRuntimeException);
   ASSERT_TRUE(EvaluateFunction("OUTDEGREE", TypedValue()).IsNull());
-  auto v1 = dba.InsertVertex();
-  auto v2 = dba.InsertVertex();
-  auto v3 = dba.InsertVertex();
-  auto e12 = dba.InsertEdge(v1, v2, dba.EdgeType("t"));
-  dba.InsertEdge(v3, v2, dba.EdgeType("t"));
+  query::VertexAccessor v1(dba.InsertVertex());
+  query::VertexAccessor v2(dba.InsertVertex());
+  query::VertexAccessor v3(dba.InsertVertex());
+  query::EdgeAccessor e12(
+      dba.InsertEdge(v1.impl_, v2.impl_, dba.EdgeType("t")));
+  dba.InsertEdge(v3.impl_, v2.impl_, dba.EdgeType("t"));
   ASSERT_EQ(EvaluateFunction("OUTDEGREE", v1).ValueInt(), 1);
   ASSERT_EQ(EvaluateFunction("OUTDEGREE", v2).ValueInt(), 0);
   ASSERT_EQ(EvaluateFunction("OUTDEGREE", v3).ValueInt(), 1);
@@ -1180,7 +1192,7 @@ TEST_F(FunctionTest, Type) {
   v1.add_label(dba.Label("label1"));
   auto v2 = dba.InsertVertex();
   v2.add_label(dba.Label("label2"));
-  auto e = dba.InsertEdge(v1, v2, dba.EdgeType("type1"));
+  query::EdgeAccessor e(dba.InsertEdge(v1, v2, dba.EdgeType("type1")));
   ASSERT_EQ(EvaluateFunction("TYPE", e).ValueString(), "type1");
   ASSERT_THROW(EvaluateFunction("TYPE", 2), QueryRuntimeException);
 }
@@ -1192,7 +1204,8 @@ TEST_F(FunctionTest, Labels) {
   v.add_label(dba.Label("label1"));
   v.add_label(dba.Label("label2"));
   std::vector<std::string> labels;
-  auto _labels = EvaluateFunction("LABELS", v).ValueList();
+  auto _labels =
+      EvaluateFunction("LABELS", query::VertexAccessor(v)).ValueList();
   labels.reserve(_labels.size());
   for (auto label : _labels) {
     labels.emplace_back(label.ValueString());
@@ -1213,19 +1226,21 @@ TEST_F(FunctionTest, NodesRelationships) {
     auto v3 = dba.InsertVertex();
     auto e1 = dba.InsertEdge(v1, v2, dba.EdgeType("Type"));
     auto e2 = dba.InsertEdge(v2, v3, dba.EdgeType("Type"));
-    query::Path path(v1, e1, v2, e2, v3);
+    query::Path path{query::VertexAccessor(v1), query::EdgeAccessor(e1),
+                     query::VertexAccessor(v2), query::EdgeAccessor(e2),
+                     query::VertexAccessor(v3)};
 
     auto _nodes = EvaluateFunction("NODES", path).ValueList();
-    std::vector<VertexAccessor> nodes;
+    std::vector<::VertexAccessor> nodes;
     for (const auto &node : _nodes) {
-      nodes.push_back(node.ValueVertex());
+      nodes.push_back(node.ValueVertex().impl_);
     }
     EXPECT_THAT(nodes, ElementsAre(v1, v2, v3));
 
     auto _edges = EvaluateFunction("RELATIONSHIPS", path).ValueList();
-    std::vector<EdgeAccessor> edges;
+    std::vector<::EdgeAccessor> edges;
     for (const auto &edge : _edges) {
-      edges.push_back(edge.ValueEdge());
+      edges.push_back(edge.ValueEdge().impl_);
     }
     EXPECT_THAT(edges, ElementsAre(e1, e2));
   }
@@ -1273,10 +1288,12 @@ TEST_F(FunctionTest, Keys) {
     }
     return keys;
   };
-  ASSERT_THAT(prop_keys_to_string(EvaluateFunction("KEYS", v1)),
-              UnorderedElementsAre("height", "age"));
-  ASSERT_THAT(prop_keys_to_string(EvaluateFunction("KEYS", e)),
-              UnorderedElementsAre("width", "age"));
+  ASSERT_THAT(
+      prop_keys_to_string(EvaluateFunction("KEYS", query::VertexAccessor(v1))),
+      UnorderedElementsAre("height", "age"));
+  ASSERT_THAT(
+      prop_keys_to_string(EvaluateFunction("KEYS", query::EdgeAccessor(e))),
+      UnorderedElementsAre("width", "age"));
   ASSERT_THROW(EvaluateFunction("KEYS", 2), QueryRuntimeException);
 }
 
@@ -1494,9 +1511,10 @@ TEST_F(FunctionTest, Counter) {
 }
 
 TEST_F(FunctionTest, Id) {
-  auto va = dba.InsertVertex();
-  auto ea = dba.InsertEdge(va, va, dba.EdgeType("edge"));
-  auto vb = dba.InsertVertex();
+  query::VertexAccessor va(dba.InsertVertex());
+  query::EdgeAccessor ea(
+      dba.InsertEdge(va.impl_, va.impl_, dba.EdgeType("edge")));
+  query::VertexAccessor vb(dba.InsertVertex());
   EXPECT_EQ(EvaluateFunction("ID", va).ValueInt(), 0);
   EXPECT_EQ(EvaluateFunction("ID", ea).ValueInt(), 0);
   EXPECT_EQ(EvaluateFunction("ID", vb).ValueInt(), 1);
diff --git a/tests/unit/query_plan_accumulate_aggregate.cpp b/tests/unit/query_plan_accumulate_aggregate.cpp
index 09803e3b9..13a678a76 100644
--- a/tests/unit/query_plan_accumulate_aggregate.cpp
+++ b/tests/unit/query_plan_accumulate_aggregate.cpp
@@ -65,7 +65,8 @@ TEST(QueryPlan, Accumulate) {
     auto m_p_ne =
         NEXPR("m.p", m_p)->MapTo(symbol_table.CreateSymbol("m_p_ne", true));
     auto produce = MakeProduce(last_op, n_p_ne, m_p_ne);
-    auto context = MakeContext(storage, symbol_table, &dba);
+    query::DbAccessor execution_dba(&dba);
+    auto context = MakeContext(storage, symbol_table, &execution_dba);
     auto results = CollectProduce(*produce, &context);
     std::vector<int> results_data;
     for (const auto &row : results)
@@ -95,7 +96,8 @@ TEST(QueryPlan, AccumulateAdvance) {
     auto accumulate = std::make_shared<Accumulate>(
         create, std::vector<Symbol>{node.symbol}, advance);
     auto match = MakeScanAll(storage, symbol_table, "m", accumulate);
-    auto context = MakeContext(storage, symbol_table, &dba);
+    query::DbAccessor execution_dba(&dba);
+    auto context = MakeContext(storage, symbol_table, &execution_dba);
     EXPECT_EQ(advance ? 1 : 0, PullAll(*match.op_, &context));
   };
   check(false);
@@ -182,7 +184,8 @@ class QueryPlanAggregateOps : public ::testing::Test {
     auto produce =
         MakeAggregationProduce(n.op_, symbol_table, storage,
                                aggregation_expressions, ops, group_bys, {});
-    auto context = MakeContext(storage, symbol_table, &dba);
+    query::DbAccessor execution_dba(&dba);
+    auto context = MakeContext(storage, symbol_table, &execution_dba);
     return CollectProduce(*produce, &context);
   }
 };
@@ -327,7 +330,8 @@ TEST(QueryPlan, AggregateGroupByValues) {
       MakeAggregationProduce(n.op_, symbol_table, storage, {n_p},
                              {Aggregation::Op::COUNT}, {n_p}, {n.sym_});
 
-  auto context = MakeContext(storage, symbol_table, &dba);
+  query::DbAccessor execution_dba(&dba);
+  auto context = MakeContext(storage, symbol_table, &execution_dba);
   auto results = CollectProduce(*produce, &context);
   ASSERT_EQ(results.size(), group_by_vals.size() - 2);
   std::unordered_set<TypedValue, TypedValue::Hash, TypedValue::BoolEqual>
@@ -376,7 +380,8 @@ TEST(QueryPlan, AggregateMultipleGroupBy) {
                                         {Aggregation::Op::COUNT},
                                         {n_p1, n_p2, n_p3}, {n.sym_});
 
-  auto context = MakeContext(storage, symbol_table, &dba);
+  query::DbAccessor execution_dba(&dba);
+  auto context = MakeContext(storage, symbol_table, &execution_dba);
   auto results = CollectProduce(*produce, &context);
   EXPECT_EQ(results.size(), 2 * 3 * 5);
 }
@@ -390,7 +395,8 @@ TEST(QueryPlan, AggregateNoInput) {
   auto two = LITERAL(2);
   auto produce = MakeAggregationProduce(nullptr, symbol_table, storage, {two},
                                         {Aggregation::Op::COUNT}, {}, {});
-  auto context = MakeContext(storage, symbol_table, &dba);
+  query::DbAccessor execution_dba(&dba);
+  auto context = MakeContext(storage, symbol_table, &execution_dba);
   auto results = CollectProduce(*produce, &context);
   EXPECT_EQ(1, results.size());
   EXPECT_EQ(1, results[0].size());
@@ -422,7 +428,8 @@ TEST(QueryPlan, AggregateCountEdgeCases) {
   auto count = [&]() {
     auto produce = MakeAggregationProduce(n.op_, symbol_table, storage, {n_p},
                                           {Aggregation::Op::COUNT}, {}, {});
-    auto context = MakeContext(storage, symbol_table, &dba);
+    query::DbAccessor execution_dba(&dba);
+    auto context = MakeContext(storage, symbol_table, &execution_dba);
     auto results = CollectProduce(*produce, &context);
     if (results.size() == 0) return -1L;
     EXPECT_EQ(1, results.size());
@@ -440,8 +447,7 @@ TEST(QueryPlan, AggregateCountEdgeCases) {
   EXPECT_EQ(0, count());
 
   // one vertex, property set
-  for (VertexAccessor va : dba.Vertices(false))
-    va.PropsSet(prop, PropertyValue(42));
+  for (auto va : dba.Vertices(false)) va.PropsSet(prop, PropertyValue(42));
   dba.AdvanceCommand();
   EXPECT_EQ(1, count());
 
@@ -451,8 +457,7 @@ TEST(QueryPlan, AggregateCountEdgeCases) {
   EXPECT_EQ(1, count());
 
   // two vertices, both with property set
-  for (VertexAccessor va : dba.Vertices(false))
-    va.PropsSet(prop, PropertyValue(42));
+  for (auto va : dba.Vertices(false)) va.PropsSet(prop, PropertyValue(42));
   dba.AdvanceCommand();
   EXPECT_EQ(2, count());
 }
@@ -482,7 +487,8 @@ TEST(QueryPlan, AggregateFirstValueTypes) {
   auto aggregate = [&](Expression *expression, Aggregation::Op aggr_op) {
     auto produce = MakeAggregationProduce(n.op_, symbol_table, storage,
                                           {expression}, {aggr_op}, {}, {});
-    auto context = MakeContext(storage, symbol_table, &dba);
+    query::DbAccessor execution_dba(&dba);
+    auto context = MakeContext(storage, symbol_table, &execution_dba);
     CollectProduce(*produce, &context);
   };
 
@@ -538,7 +544,8 @@ TEST(QueryPlan, AggregateTypes) {
   auto aggregate = [&](Expression *expression, Aggregation::Op aggr_op) {
     auto produce = MakeAggregationProduce(n.op_, symbol_table, storage,
                                           {expression}, {aggr_op}, {}, {});
-    auto context = MakeContext(storage, symbol_table, &dba);
+    query::DbAccessor execution_dba(&dba);
+    auto context = MakeContext(storage, symbol_table, &execution_dba);
     CollectProduce(*produce, &context);
   };
 
@@ -596,7 +603,8 @@ TEST(QueryPlan, Unwind) {
                   ->MapTo(symbol_table.CreateSymbol("y_ne", true));
   auto produce = MakeProduce(unwind_1, x_ne, y_ne);
 
-  auto context = MakeContext(storage, symbol_table, &dba);
+  query::DbAccessor execution_dba(&dba);
+  auto context = MakeContext(storage, symbol_table, &execution_dba);
   auto results = CollectProduce(*produce, &context);
   ASSERT_EQ(4, results.size());
   const std::vector<int> expected_x_card{3, 3, 3, 1};
diff --git a/tests/unit/query_plan_bag_semantics.cpp b/tests/unit/query_plan_bag_semantics.cpp
index 83eea0531..9d55905dd 100644
--- a/tests/unit/query_plan_bag_semantics.cpp
+++ b/tests/unit/query_plan_bag_semantics.cpp
@@ -31,7 +31,8 @@ TEST(QueryPlan, Skip) {
   auto n = MakeScanAll(storage, symbol_table, "n1");
   auto skip = std::make_shared<plan::Skip>(n.op_, LITERAL(2));
 
-  auto context = MakeContext(storage, symbol_table, &dba);
+  query::DbAccessor execution_dba(&dba);
+  auto context = MakeContext(storage, symbol_table, &execution_dba);
   EXPECT_EQ(0, PullAll(*skip, &context));
 
   dba.InsertVertex();
@@ -61,7 +62,8 @@ TEST(QueryPlan, Limit) {
   auto n = MakeScanAll(storage, symbol_table, "n1");
   auto skip = std::make_shared<plan::Limit>(n.op_, LITERAL(2));
 
-  auto context = MakeContext(storage, symbol_table, &dba);
+  query::DbAccessor execution_dba(&dba);
+  auto context = MakeContext(storage, symbol_table, &execution_dba);
   EXPECT_EQ(0, PullAll(*skip, &context));
 
   dba.InsertVertex();
@@ -100,7 +102,8 @@ TEST(QueryPlan, CreateLimit) {
   auto c = std::make_shared<CreateNode>(n.op_, m);
   auto skip = std::make_shared<plan::Limit>(c, LITERAL(1));
 
-  auto context = MakeContext(storage, symbol_table, &dba);
+  query::DbAccessor execution_dba(&dba);
+  auto context = MakeContext(storage, symbol_table, &execution_dba);
   EXPECT_EQ(1, PullAll(*skip, &context));
   dba.AdvanceCommand();
   EXPECT_EQ(3, CountIterable(dba.Vertices(false)));
@@ -171,7 +174,8 @@ TEST(QueryPlan, OrderBy) {
     auto n_p_ne =
         NEXPR("n.p", n_p)->MapTo(symbol_table.CreateSymbol("n.p", true));
     auto produce = MakeProduce(order_by, n_p_ne);
-    auto context = MakeContext(storage, symbol_table, &dba);
+    query::DbAccessor execution_dba(&dba);
+    auto context = MakeContext(storage, symbol_table, &execution_dba);
     auto results = CollectProduce(*produce, &context);
     ASSERT_EQ(values.size(), results.size());
     for (int j = 0; j < results.size(); ++j)
@@ -223,7 +227,8 @@ TEST(QueryPlan, OrderByMultiple) {
   auto n_p2_ne =
       NEXPR("n.p2", n_p2)->MapTo(symbol_table.CreateSymbol("n.p2", true));
   auto produce = MakeProduce(order_by, n_p1_ne, n_p2_ne);
-  auto context = MakeContext(storage, symbol_table, &dba);
+  query::DbAccessor execution_dba(&dba);
+  auto context = MakeContext(storage, symbol_table, &execution_dba);
   auto results = CollectProduce(*produce, &context);
   ASSERT_EQ(N * N, results.size());
   for (int j = 0; j < N * N; ++j) {
@@ -277,7 +282,8 @@ TEST(QueryPlan, OrderByExceptions) {
     auto order_by = std::make_shared<plan::OrderBy>(
         n.op_, std::vector<SortItem>{{Ordering::ASC, n_p}},
         std::vector<Symbol>{});
-    auto context = MakeContext(storage, symbol_table, &dba);
+    query::DbAccessor execution_dba(&dba);
+    auto context = MakeContext(storage, symbol_table, &execution_dba);
     EXPECT_THROW(PullAll(*order_by, &context), QueryRuntimeException);
   }
 }
diff --git a/tests/unit/query_plan_checker.hpp b/tests/unit/query_plan_checker.hpp
index 7977e79a6..6b4f5081d 100644
--- a/tests/unit/query_plan_checker.hpp
+++ b/tests/unit/query_plan_checker.hpp
@@ -392,33 +392,43 @@ class FakeDbAccessor {
     label_property_index_.emplace_back(label, property, count);
   }
 
-  storage::Label Label(const std::string &name) {
+  storage::Label NameToLabel(const std::string &name) {
     auto found = labels_.find(name);
     if (found != labels_.end()) return found->second;
     return labels_.emplace(name, storage::Label(labels_.size())).first->second;
   }
 
-  storage::EdgeType EdgeType(const std::string &name) {
+  storage::Label Label(const std::string &name) { return NameToLabel(name); }
+
+  storage::EdgeType NameToEdgeType(const std::string &name) {
     auto found = edge_types_.find(name);
     if (found != edge_types_.end()) return found->second;
     return edge_types_.emplace(name, storage::EdgeType(edge_types_.size()))
         .first->second;
   }
 
-  storage::Property Property(const std::string &name) {
+  storage::Property NameToProperty(const std::string &name) {
     auto found = properties_.find(name);
     if (found != properties_.end()) return found->second;
     return properties_.emplace(name, storage::Property(properties_.size()))
         .first->second;
   }
 
-  std::string PropertyName(storage::Property property) const {
+  storage::Property Property(const std::string &name) {
+    return NameToProperty(name);
+  }
+
+  std::string PropertyToName(storage::Property property) const {
     for (const auto &kv : properties_) {
       if (kv.second == property) return kv.first;
     }
     LOG(FATAL) << "Unable to find property name";
   }
 
+  std::string PropertyName(storage::Property property) const {
+    return PropertyToName(property);
+  }
+
  private:
   std::unordered_map<std::string, storage::Label> labels_;
   std::unordered_map<std::string, storage::EdgeType> edge_types_;
diff --git a/tests/unit/query_plan_common.hpp b/tests/unit/query_plan_common.hpp
index c3d229efb..e98bd6086 100644
--- a/tests/unit/query_plan_common.hpp
+++ b/tests/unit/query_plan_common.hpp
@@ -19,7 +19,7 @@ using Bound = ScanAllByLabelPropertyRange::Bound;
 
 ExecutionContext MakeContext(const AstStorage &storage,
                              const SymbolTable &symbol_table,
-                             database::GraphDbAccessor *dba) {
+                             query::DbAccessor *dba) {
   ExecutionContext context{dba};
   context.symbol_table = symbol_table;
   context.evaluation_context.properties =
diff --git a/tests/unit/query_plan_create_set_remove_delete.cpp b/tests/unit/query_plan_create_set_remove_delete.cpp
index 4a73a4c84..8fc8f2e30 100644
--- a/tests/unit/query_plan_create_set_remove_delete.cpp
+++ b/tests/unit/query_plan_create_set_remove_delete.cpp
@@ -32,13 +32,14 @@ TEST(QueryPlan, CreateNodeWithAttributes) {
   node.properties.emplace_back(property.second, LITERAL(42));
 
   auto create = std::make_shared<CreateNode>(nullptr, node);
-  auto context = MakeContext(storage, symbol_table, &dba);
+  query::DbAccessor execution_dba(&dba);
+  auto context = MakeContext(storage, symbol_table, &execution_dba);
   PullAll(*create, &context);
   dba.AdvanceCommand();
 
   // count the number of vertices
   int vertex_count = 0;
-  for (VertexAccessor vertex : dba.Vertices(false)) {
+  for (auto vertex : dba.Vertices(false)) {
     vertex_count++;
     EXPECT_EQ(vertex.labels().size(), 1);
     EXPECT_EQ(*vertex.labels().begin(), label);
@@ -77,13 +78,15 @@ TEST(QueryPlan, CreateReturn) {
           ->MapTo(symbol_table.CreateSymbol("named_expr_n_p", true));
 
   auto produce = MakeProduce(create, named_expr_n, named_expr_n_p);
-  auto context = MakeContext(storage, symbol_table, &dba);
+  query::DbAccessor execution_dba(&dba);
+  auto context = MakeContext(storage, symbol_table, &execution_dba);
   auto results = CollectProduce(*produce, &context);
   EXPECT_EQ(1, results.size());
   EXPECT_EQ(2, results[0].size());
   EXPECT_EQ(TypedValue::Type::Vertex, results[0][0].type());
-  EXPECT_EQ(1, results[0][0].ValueVertex().labels().size());
-  EXPECT_EQ(label, results[0][0].ValueVertex().labels()[0]);
+  auto maybe_labels = results[0][0].ValueVertex().Labels(storage::View::OLD);
+  EXPECT_EQ(1, maybe_labels->size());
+  EXPECT_EQ(label, (*maybe_labels)[0]);
   EXPECT_EQ(TypedValue::Type::Int, results[0][1].type());
   EXPECT_EQ(42, results[0][1].ValueInt());
 
@@ -128,7 +131,8 @@ TEST(QueryPlan, CreateExpand) {
     auto create_op = std::make_shared<CreateNode>(nullptr, n);
     auto create_expand =
         std::make_shared<CreateExpand>(m, r, create_op, n.symbol, cycle);
-    auto context = MakeContext(storage, symbol_table, &dba);
+    query::DbAccessor execution_dba(&dba);
+    auto context = MakeContext(storage, symbol_table, &execution_dba);
     PullAll(*create_expand, &context);
     dba.AdvanceCommand();
 
@@ -141,7 +145,7 @@ TEST(QueryPlan, CreateExpand) {
   test_create_path(false, 2, 1);
   test_create_path(true, 1, 1);
 
-  for (VertexAccessor vertex : dba.Vertices(false)) {
+  for (auto vertex : dba.Vertices(false)) {
     EXPECT_EQ(vertex.labels().size(), 1);
     storage::Label label = vertex.labels()[0];
     if (label == label_node_1) {
@@ -155,7 +159,7 @@ TEST(QueryPlan, CreateExpand) {
       FAIL();
     }
 
-    for (EdgeAccessor edge : dba.Edges(false)) {
+    for (auto edge : dba.Edges(false)) {
       EXPECT_EQ(edge.EdgeType(), edge_type);
       EXPECT_EQ(edge.PropsAt(property.second).ValueInt(), 3);
     }
@@ -184,7 +188,8 @@ TEST(QueryPlan, MatchCreateNode) {
   auto create_node = std::make_shared<CreateNode>(n_scan_all.op_, m);
 
   EXPECT_EQ(CountIterable(dba.Vertices(false)), 3);
-  auto context = MakeContext(storage, symbol_table, &dba);
+  query::DbAccessor execution_dba(&dba);
+  auto context = MakeContext(storage, symbol_table, &execution_dba);
   PullAll(*create_node, &context);
   dba.AdvanceCommand();
   EXPECT_EQ(CountIterable(dba.Vertices(false)), 6);
@@ -227,7 +232,8 @@ TEST(QueryPlan, MatchCreateExpand) {
 
     auto create_expand = std::make_shared<CreateExpand>(m, r, n_scan_all.op_,
                                                         n_scan_all.sym_, cycle);
-    auto context = MakeContext(storage, symbol_table, &dba);
+    query::DbAccessor execution_dba(&dba);
+    auto context = MakeContext(storage, symbol_table, &execution_dba);
     PullAll(*create_expand, &context);
     dba.AdvanceCommand();
 
@@ -246,7 +252,7 @@ TEST(QueryPlan, Delete) {
   auto dba = db.Access();
 
   // make a fully-connected (one-direction, no cycles) with 4 nodes
-  std::vector<VertexAccessor> vertices;
+  std::vector<::VertexAccessor> vertices;
   for (int i = 0; i < 4; ++i) vertices.push_back(dba.InsertVertex());
   auto type = dba.EdgeType("type");
   for (int j = 0; j < 4; ++j)
@@ -266,7 +272,8 @@ TEST(QueryPlan, Delete) {
     auto n_get = storage.Create<Identifier>("n")->MapTo(n.sym_);
     auto delete_op = std::make_shared<plan::Delete>(
         n.op_, std::vector<Expression *>{n_get}, false);
-    auto context = MakeContext(storage, symbol_table, &dba);
+    query::DbAccessor execution_dba(&dba);
+    auto context = MakeContext(storage, symbol_table, &execution_dba);
     EXPECT_THROW(PullAll(*delete_op, &context), QueryRuntimeException);
     dba.AdvanceCommand();
     EXPECT_EQ(4, CountIterable(dba.Vertices(false)));
@@ -280,7 +287,8 @@ TEST(QueryPlan, Delete) {
     auto delete_op = std::make_shared<plan::Delete>(
         n.op_, std::vector<Expression *>{n_get}, true);
     Frame frame(symbol_table.max_position());
-    auto context = MakeContext(storage, symbol_table, &dba);
+    query::DbAccessor execution_dba(&dba);
+    auto context = MakeContext(storage, symbol_table, &execution_dba);
     delete_op->MakeCursor(utils::NewDeleteResource())->Pull(frame, context);
     dba.AdvanceCommand();
     EXPECT_EQ(3, CountIterable(dba.Vertices(false)));
@@ -296,7 +304,8 @@ TEST(QueryPlan, Delete) {
     auto r_get = storage.Create<Identifier>("r")->MapTo(r_m.edge_sym_);
     auto delete_op = std::make_shared<plan::Delete>(
         r_m.op_, std::vector<Expression *>{r_get}, false);
-    auto context = MakeContext(storage, symbol_table, &dba);
+    query::DbAccessor execution_dba(&dba);
+    auto context = MakeContext(storage, symbol_table, &execution_dba);
     PullAll(*delete_op, &context);
     dba.AdvanceCommand();
     EXPECT_EQ(3, CountIterable(dba.Vertices(false)));
@@ -309,7 +318,8 @@ TEST(QueryPlan, Delete) {
     auto n_get = storage.Create<Identifier>("n")->MapTo(n.sym_);
     auto delete_op = std::make_shared<plan::Delete>(
         n.op_, std::vector<Expression *>{n_get}, false);
-    auto context = MakeContext(storage, symbol_table, &dba);
+    query::DbAccessor execution_dba(&dba);
+    auto context = MakeContext(storage, symbol_table, &execution_dba);
     PullAll(*delete_op, &context);
     dba.AdvanceCommand();
     EXPECT_EQ(0, CountIterable(dba.Vertices(false)));
@@ -355,7 +365,8 @@ TEST(QueryPlan, DeleteTwiceDeleteBlockingEdge) {
 
     auto delete_op = std::make_shared<plan::Delete>(
         r_m.op_, std::vector<Expression *>{n_get, r_get, m_get}, detach);
-    auto context = MakeContext(storage, symbol_table, &dba);
+    query::DbAccessor execution_dba(&dba);
+    auto context = MakeContext(storage, symbol_table, &execution_dba);
     EXPECT_EQ(2, PullAll(*delete_op, &context));
     dba.AdvanceCommand();
     EXPECT_EQ(0, CountIterable(dba.Vertices(false)));
@@ -395,7 +406,8 @@ TEST(QueryPlan, DeleteReturn) {
                  ->MapTo(symbol_table.CreateSymbol("bla", true));
   auto produce = MakeProduce(delete_op, n_p);
 
-  auto context = MakeContext(storage, symbol_table, &dba);
+  query::DbAccessor execution_dba(&dba);
+  auto context = MakeContext(storage, symbol_table, &execution_dba);
   auto results = CollectProduce(*produce, &context);
   EXPECT_EQ(4, results.size());
   dba.AdvanceCommand();
@@ -412,7 +424,8 @@ TEST(QueryPlan, DeleteNull) {
   auto once = std::make_shared<Once>();
   auto delete_op = std::make_shared<plan::Delete>(
       once, std::vector<Expression *>{LITERAL(TypedValue())}, false);
-  auto context = MakeContext(storage, symbol_table, &dba);
+  query::DbAccessor execution_dba(&dba);
+  auto context = MakeContext(storage, symbol_table, &execution_dba);
   EXPECT_EQ(1, PullAll(*delete_op, &context));
 }
 
@@ -420,16 +433,9 @@ TEST(QueryPlan, DeleteAdvance) {
   // test queries on empty DB:
   // CREATE (n)
   // MATCH (n) DELETE n WITH n ...
-  // this fails due to us advancing the command
-  // when processing the WITH clause
-  //
-  // note that Neo does not fail when the deleted
-  // record is not used in subsequent clauses, but
-  // we are not yet compatible with that
+  // this fails only if the deleted record `n` is actually used in subsequent
+  // clauses, which is compatible with Neo's behavior.
   database::GraphDb db;
-  auto dba = db.Access();
-  dba.InsertVertex();
-  dba.AdvanceCommand();
 
   AstStorage storage;
   SymbolTable symbol_table;
@@ -440,8 +446,28 @@ TEST(QueryPlan, DeleteAdvance) {
       n.op_, std::vector<Expression *>{n_get}, false);
   auto advance = std::make_shared<Accumulate>(
       delete_op, std::vector<Symbol>{n.sym_}, true);
-  auto context = MakeContext(storage, symbol_table, &dba);
-  EXPECT_THROW(PullAll(*advance, &context), ReconstructionException);
+  auto res_sym = symbol_table.CreateSymbol("res", true);
+  {
+    auto dba = db.Access();
+    dba.InsertVertex();
+    dba.AdvanceCommand();
+    query::DbAccessor execution_dba(&dba);
+    auto produce =
+        MakeProduce(advance, NEXPR("res", LITERAL(42))->MapTo(res_sym));
+    auto context = MakeContext(storage, symbol_table, &execution_dba);
+    EXPECT_EQ(1, PullAll(*produce, &context));
+    dba.Abort();
+  }
+  {
+    auto dba = db.Access();
+    dba.InsertVertex();
+    dba.AdvanceCommand();
+    query::DbAccessor execution_dba(&dba);
+    auto n_prop = PROPERTY_LOOKUP(n_get, dba.Property("prop"));
+    auto produce = MakeProduce(advance, NEXPR("res", n_prop)->MapTo(res_sym));
+    auto context = MakeContext(storage, symbol_table, &execution_dba);
+    EXPECT_THROW(PullAll(*produce, &context), ReconstructionException);
+  }
 }
 
 TEST(QueryPlan, SetProperty) {
@@ -480,16 +506,17 @@ TEST(QueryPlan, SetProperty) {
   auto r_p = PROPERTY_LOOKUP(IDENT("r")->MapTo(r_m.edge_sym_), prop1);
   auto set_r_p =
       std::make_shared<plan::SetProperty>(set_n_p, prop1, r_p, literal);
-  auto context = MakeContext(storage, symbol_table, &dba);
+  query::DbAccessor execution_dba(&dba);
+  auto context = MakeContext(storage, symbol_table, &execution_dba);
   EXPECT_EQ(2, PullAll(*set_r_p, &context));
   dba.AdvanceCommand();
 
   EXPECT_EQ(CountIterable(dba.Edges(false)), 2);
-  for (EdgeAccessor edge : dba.Edges(false)) {
+  for (auto edge : dba.Edges(false)) {
     ASSERT_EQ(edge.PropsAt(prop1).type(), PropertyValue::Type::Int);
     EXPECT_EQ(edge.PropsAt(prop1).ValueInt(), 42);
-    VertexAccessor from = edge.from();
-    VertexAccessor to = edge.to();
+    auto from = edge.from();
+    auto to = edge.to();
     ASSERT_EQ(from.PropsAt(prop1).type(), PropertyValue::Type::Int);
     EXPECT_EQ(from.PropsAt(prop1).ValueInt(), 42);
     ASSERT_EQ(to.PropsAt(prop1).type(), PropertyValue::Type::Null);
@@ -532,13 +559,14 @@ TEST(QueryPlan, SetProperties) {
         std::make_shared<plan::SetProperties>(r_m.op_, n.sym_, r_ident, op);
     auto set_m_to_r = std::make_shared<plan::SetProperties>(
         set_r_to_n, r_m.edge_sym_, m_ident, op);
-    auto context = MakeContext(storage, symbol_table, &dba);
+    query::DbAccessor execution_dba(&dba);
+    auto context = MakeContext(storage, symbol_table, &execution_dba);
     EXPECT_EQ(1, PullAll(*set_m_to_r, &context));
     dba.AdvanceCommand();
 
     EXPECT_EQ(CountIterable(dba.Edges(false)), 1);
-    for (EdgeAccessor edge : dba.Edges(false)) {
-      VertexAccessor from = edge.from();
+    for (auto edge : dba.Edges(false)) {
+      auto from = edge.from();
       EXPECT_EQ(from.Properties().size(), update ? 2 : 1);
       if (update) {
         ASSERT_EQ(from.PropsAt(prop_a).type(), PropertyValue::Type::Int);
@@ -555,7 +583,7 @@ TEST(QueryPlan, SetProperties) {
       ASSERT_EQ(edge.PropsAt(prop_c).type(), PropertyValue::Type::Int);
       EXPECT_EQ(edge.PropsAt(prop_c).ValueInt(), 2);
 
-      VertexAccessor to = edge.to();
+      auto to = edge.to();
       EXPECT_EQ(to.Properties().size(), 1);
       ASSERT_EQ(to.PropsAt(prop_c).type(), PropertyValue::Type::Int);
       EXPECT_EQ(to.PropsAt(prop_c).ValueInt(), 2);
@@ -583,10 +611,11 @@ TEST(QueryPlan, SetLabels) {
   auto n = MakeScanAll(storage, symbol_table, "n");
   auto label_set = std::make_shared<plan::SetLabels>(
       n.op_, n.sym_, std::vector<storage::Label>{label2, label3});
-  auto context = MakeContext(storage, symbol_table, &dba);
+  query::DbAccessor execution_dba(&dba);
+  auto context = MakeContext(storage, symbol_table, &execution_dba);
   EXPECT_EQ(2, PullAll(*label_set, &context));
 
-  for (VertexAccessor vertex : dba.Vertices(false)) {
+  for (auto vertex : dba.Vertices(false)) {
     vertex.SwitchNew();
     EXPECT_EQ(3, vertex.labels().size());
     EXPECT_TRUE(vertex.has_label(label2));
@@ -631,15 +660,16 @@ TEST(QueryPlan, RemoveProperty) {
 
   auto r_p = PROPERTY_LOOKUP(IDENT("r")->MapTo(r_m.edge_sym_), prop1);
   auto set_r_p = std::make_shared<plan::RemoveProperty>(set_n_p, prop1, r_p);
-  auto context = MakeContext(storage, symbol_table, &dba);
+  query::DbAccessor execution_dba(&dba);
+  auto context = MakeContext(storage, symbol_table, &execution_dba);
   EXPECT_EQ(2, PullAll(*set_r_p, &context));
   dba.AdvanceCommand();
 
   EXPECT_EQ(CountIterable(dba.Edges(false)), 2);
-  for (EdgeAccessor edge : dba.Edges(false)) {
+  for (auto edge : dba.Edges(false)) {
     EXPECT_EQ(edge.PropsAt(prop1).type(), PropertyValue::Type::Null);
-    VertexAccessor from = edge.from();
-    VertexAccessor to = edge.to();
+    auto from = edge.from();
+    auto to = edge.to();
     EXPECT_EQ(from.PropsAt(prop1).type(), PropertyValue::Type::Null);
     EXPECT_EQ(from.PropsAt(prop2).type(), PropertyValue::Type::Int);
     EXPECT_EQ(to.PropsAt(prop1).type(), PropertyValue::Type::Int);
@@ -668,10 +698,11 @@ TEST(QueryPlan, RemoveLabels) {
   auto n = MakeScanAll(storage, symbol_table, "n");
   auto label_remove = std::make_shared<plan::RemoveLabels>(
       n.op_, n.sym_, std::vector<storage::Label>{label1, label2});
-  auto context = MakeContext(storage, symbol_table, &dba);
+  query::DbAccessor execution_dba(&dba);
+  auto context = MakeContext(storage, symbol_table, &execution_dba);
   EXPECT_EQ(2, PullAll(*label_remove, &context));
 
-  for (VertexAccessor vertex : dba.Vertices(false)) {
+  for (auto vertex : dba.Vertices(false)) {
     vertex.SwitchNew();
     EXPECT_EQ(1, vertex.labels().size());
     EXPECT_FALSE(vertex.has_label(label1));
@@ -713,7 +744,8 @@ TEST(QueryPlan, NodeFilterSet) {
   auto add = ADD(set_prop, LITERAL(1));
   auto set = std::make_shared<plan::SetProperty>(node_filter, prop.second,
                                                  set_prop, add);
-  auto context = MakeContext(storage, symbol_table, &dba);
+  query::DbAccessor execution_dba(&dba);
+  auto context = MakeContext(storage, symbol_table, &execution_dba);
   EXPECT_EQ(2, PullAll(*set, &context));
   dba.AdvanceCommand();
   v1.Reconstruct();
@@ -752,7 +784,8 @@ TEST(QueryPlan, FilterRemove) {
   auto rem_prop = PROPERTY_LOOKUP(IDENT("n")->MapTo(scan_all.sym_), prop);
   auto rem =
       std::make_shared<plan::RemoveProperty>(filter, prop.second, rem_prop);
-  auto context = MakeContext(storage, symbol_table, &dba);
+  query::DbAccessor execution_dba(&dba);
+  auto context = MakeContext(storage, symbol_table, &execution_dba);
   EXPECT_EQ(2, PullAll(*rem, &context));
   dba.AdvanceCommand();
   v1.Reconstruct();
@@ -776,7 +809,8 @@ TEST(QueryPlan, SetRemove) {
       scan_all.op_, scan_all.sym_, std::vector<storage::Label>{label1, label2});
   auto rem = std::make_shared<plan::RemoveLabels>(
       set, scan_all.sym_, std::vector<storage::Label>{label1, label2});
-  auto context = MakeContext(storage, symbol_table, &dba);
+  query::DbAccessor execution_dba(&dba);
+  auto context = MakeContext(storage, symbol_table, &execution_dba);
   EXPECT_EQ(1, PullAll(*rem, &context));
   dba.AdvanceCommand();
   v.Reconstruct();
@@ -819,7 +853,8 @@ TEST(QueryPlan, Merge) {
       std::make_shared<Once>(), prop.second, n_p, LITERAL(2));
 
   auto merge = std::make_shared<plan::Merge>(n.op_, m_set, n_set);
-  auto context = MakeContext(storage, symbol_table, &dba);
+  query::DbAccessor execution_dba(&dba);
+  auto context = MakeContext(storage, symbol_table, &execution_dba);
   ASSERT_EQ(3, PullAll(*merge, &context));
   dba.AdvanceCommand();
   v1.Reconstruct();
@@ -848,7 +883,8 @@ TEST(QueryPlan, MergeNoInput) {
   auto merge = std::make_shared<plan::Merge>(nullptr, create, create);
 
   EXPECT_EQ(0, CountIterable(dba.Vertices(false)));
-  auto context = MakeContext(storage, symbol_table, &dba);
+  query::DbAccessor execution_dba(&dba);
+  auto context = MakeContext(storage, symbol_table, &execution_dba);
   EXPECT_EQ(1, PullAll(*merge, &context));
   dba.AdvanceCommand();
   EXPECT_EQ(1, CountIterable(dba.Vertices(false)));
@@ -867,7 +903,8 @@ TEST(QueryPlan, SetPropertyOnNull) {
   auto once = std::make_shared<Once>();
   auto set_op =
       std::make_shared<plan::SetProperty>(once, prop.second, n_prop, literal);
-  auto context = MakeContext(storage, symbol_table, &dba);
+  query::DbAccessor execution_dba(&dba);
+  auto context = MakeContext(storage, symbol_table, &execution_dba);
   EXPECT_EQ(1, PullAll(*set_op, &context));
 }
 
@@ -884,7 +921,8 @@ TEST(QueryPlan, SetPropertiesOnNull) {
   auto set_op = std::make_shared<plan::SetProperties>(
       optional, n.sym_, n_ident, plan::SetProperties::Op::REPLACE);
   EXPECT_EQ(0, CountIterable(dba.Vertices(false)));
-  auto context = MakeContext(storage, symbol_table, &dba);
+  query::DbAccessor execution_dba(&dba);
+  auto context = MakeContext(storage, symbol_table, &execution_dba);
   EXPECT_EQ(1, PullAll(*set_op, &context));
 }
 
@@ -901,7 +939,8 @@ TEST(QueryPlan, SetLabelsOnNull) {
   auto set_op = std::make_shared<plan::SetLabels>(
       optional, n.sym_, std::vector<storage::Label>{label});
   EXPECT_EQ(0, CountIterable(dba.Vertices(false)));
-  auto context = MakeContext(storage, symbol_table, &dba);
+  query::DbAccessor execution_dba(&dba);
+  auto context = MakeContext(storage, symbol_table, &execution_dba);
   EXPECT_EQ(1, PullAll(*set_op, &context));
 }
 
@@ -917,7 +956,8 @@ TEST(QueryPlan, RemovePropertyOnNull) {
   auto once = std::make_shared<Once>();
   auto remove_op =
       std::make_shared<plan::RemoveProperty>(once, prop.second, n_prop);
-  auto context = MakeContext(storage, symbol_table, &dba);
+  query::DbAccessor execution_dba(&dba);
+  auto context = MakeContext(storage, symbol_table, &execution_dba);
   EXPECT_EQ(1, PullAll(*remove_op, &context));
 }
 
@@ -934,7 +974,8 @@ TEST(QueryPlan, RemoveLabelsOnNull) {
   auto remove_op = std::make_shared<plan::RemoveLabels>(
       optional, n.sym_, std::vector<storage::Label>{label});
   EXPECT_EQ(0, CountIterable(dba.Vertices(false)));
-  auto context = MakeContext(storage, symbol_table, &dba);
+  query::DbAccessor execution_dba(&dba);
+  auto context = MakeContext(storage, symbol_table, &execution_dba);
   EXPECT_EQ(1, PullAll(*remove_op, &context));
 }
 
@@ -956,7 +997,8 @@ TEST(QueryPlan, DeleteSetProperty) {
   auto n_prop = PROPERTY_LOOKUP(IDENT("n")->MapTo(n.sym_), prop);
   auto set_op = std::make_shared<plan::SetProperty>(delete_op, prop.second,
                                                     n_prop, LITERAL(42));
-  auto context = MakeContext(storage, symbol_table, &dba);
+  query::DbAccessor execution_dba(&dba);
+  auto context = MakeContext(storage, symbol_table, &execution_dba);
   EXPECT_THROW(PullAll(*set_op, &context), QueryRuntimeException);
 }
 
@@ -982,7 +1024,8 @@ TEST(QueryPlan, DeleteSetPropertiesFromMap) {
        {plan::SetProperties::Op::REPLACE, plan::SetProperties::Op::UPDATE}) {
     auto set_op =
         std::make_shared<plan::SetProperties>(delete_op, n.sym_, rhs, op_type);
-    auto context = MakeContext(storage, symbol_table, &dba);
+    query::DbAccessor execution_dba(&dba);
+    auto context = MakeContext(storage, symbol_table, &execution_dba);
     EXPECT_THROW(PullAll(*set_op, &context), QueryRuntimeException);
   }
 }
@@ -1009,7 +1052,8 @@ TEST(QueryPlan, DeleteSetPropertiesFromVertex) {
        {plan::SetProperties::Op::REPLACE, plan::SetProperties::Op::UPDATE}) {
     auto set_op =
         std::make_shared<plan::SetProperties>(delete_op, n.sym_, rhs, op_type);
-    auto context = MakeContext(storage, symbol_table, &dba);
+    query::DbAccessor execution_dba(&dba);
+    auto context = MakeContext(storage, symbol_table, &execution_dba);
     EXPECT_THROW(PullAll(*set_op, &context), QueryRuntimeException);
   }
 }
@@ -1030,7 +1074,8 @@ TEST(QueryPlan, DeleteRemoveLabels) {
       n.op_, std::vector<Expression *>{n_get}, false);
   std::vector<storage::Label> labels{dba.Label("label")};
   auto rem_op = std::make_shared<plan::RemoveLabels>(delete_op, n.sym_, labels);
-  auto context = MakeContext(storage, symbol_table, &dba);
+  query::DbAccessor execution_dba(&dba);
+  auto context = MakeContext(storage, symbol_table, &execution_dba);
   EXPECT_THROW(PullAll(*rem_op, &context), QueryRuntimeException);
 }
 
@@ -1052,6 +1097,7 @@ TEST(QueryPlan, DeleteRemoveProperty) {
   auto n_prop = PROPERTY_LOOKUP(IDENT("n")->MapTo(n.sym_), prop);
   auto rem_op =
       std::make_shared<plan::RemoveProperty>(delete_op, prop.second, n_prop);
-  auto context = MakeContext(storage, symbol_table, &dba);
+  query::DbAccessor execution_dba(&dba);
+  auto context = MakeContext(storage, symbol_table, &execution_dba);
   EXPECT_THROW(PullAll(*rem_op, &context), QueryRuntimeException);
 }
diff --git a/tests/unit/query_plan_edge_cases.cpp b/tests/unit/query_plan_edge_cases.cpp
index d6953743b..92f2b3ef9 100644
--- a/tests/unit/query_plan_edge_cases.cpp
+++ b/tests/unit/query_plan_edge_cases.cpp
@@ -39,8 +39,9 @@ class QueryExecution : public testing::Test {
   /** Executes the query and returns the results.
    * Does NOT commit the transaction */
   auto Execute(const std::string &query) {
+    query::DbAccessor query_dba(&*dba_);
     ResultStreamFaker<query::TypedValue> stream;
-    auto results = query::Interpreter()(query, *dba_, {}, false,
+    auto results = query::Interpreter()(query, &query_dba, {}, false,
                                         utils::NewDeleteResource());
     stream.Header(results.header());
     results.PullAll(stream);
diff --git a/tests/unit/query_plan_match_filter_return.cpp b/tests/unit/query_plan_match_filter_return.cpp
index 5f48b56f3..f82dd85b0 100644
--- a/tests/unit/query_plan_match_filter_return.cpp
+++ b/tests/unit/query_plan_match_filter_return.cpp
@@ -37,7 +37,8 @@ class MatchReturnFixture : public testing::Test {
 
   std::vector<Path> PathResults(std::shared_ptr<Produce> &op) {
     std::vector<Path> res;
-    auto context = MakeContext(storage, symbol_table, &dba_);
+    query::DbAccessor execution_dba(&dba_);
+    auto context = MakeContext(storage, symbol_table, &execution_dba);
     for (const auto &row : CollectProduce(*op, &context))
       res.emplace_back(row[0].ValuePath());
     return res;
@@ -55,7 +56,8 @@ TEST_F(MatchReturnFixture, MatchReturn) {
         NEXPR("n", IDENT("n")->MapTo(scan_all.sym_))
             ->MapTo(symbol_table.CreateSymbol("named_expression_1", true));
     auto produce = MakeProduce(scan_all.op_, output);
-    auto context = MakeContext(storage, symbol_table, &dba_);
+    query::DbAccessor execution_dba(&dba_);
+    auto context = MakeContext(storage, symbol_table, &execution_dba);
     return PullAll(*produce, &context);
   };
 
@@ -83,7 +85,8 @@ TEST_F(MatchReturnFixture, MatchReturnPath) {
   auto results = PathResults(produce);
   ASSERT_EQ(results.size(), 2);
   std::vector<query::Path> expected_paths;
-  for (const auto &v : dba_.Vertices(false)) expected_paths.emplace_back(v);
+  for (const auto &v : dba_.Vertices(false))
+    expected_paths.emplace_back(query::VertexAccessor(v));
   ASSERT_EQ(expected_paths.size(), 2);
   EXPECT_TRUE(std::is_permutation(expected_paths.begin(), expected_paths.end(),
                                   results.begin()));
@@ -109,7 +112,8 @@ TEST(QueryPlan, MatchReturnCartesian) {
       NEXPR("m", IDENT("m")->MapTo(m.sym_))
           ->MapTo(symbol_table.CreateSymbol("named_expression_2", true));
   auto produce = MakeProduce(m.op_, return_n, return_m);
-  auto context = MakeContext(storage, symbol_table, &dba);
+  query::DbAccessor execution_dba(&dba);
+  auto context = MakeContext(storage, symbol_table, &execution_dba);
   auto results = CollectProduce(*produce, &context);
   EXPECT_EQ(results.size(), 4);
   // ensure the result ordering is OK:
@@ -134,7 +138,8 @@ TEST(QueryPlan, StandaloneReturn) {
   auto produce = MakeProduce(std::shared_ptr<LogicalOperator>(nullptr), output);
   output->MapTo(symbol_table.CreateSymbol("named_expression_1", true));
 
-  auto context = MakeContext(storage, symbol_table, &dba);
+  query::DbAccessor execution_dba(&dba);
+  auto context = MakeContext(storage, symbol_table, &execution_dba);
   auto results = CollectProduce(*produce, &context);
   EXPECT_EQ(results.size(), 1);
   EXPECT_EQ(results[0].size(), 1);
@@ -187,7 +192,8 @@ TEST(QueryPlan, NodeFilterLabelsAndProperties) {
           ->MapTo(symbol_table.CreateSymbol("named_expression_1", true));
   auto produce = MakeProduce(node_filter, output);
 
-  auto context = MakeContext(storage, symbol_table, &dba);
+  query::DbAccessor execution_dba(&dba);
+  auto context = MakeContext(storage, symbol_table, &execution_dba);
   EXPECT_EQ(1, PullAll(*produce, &context));
 
   //  test that filtering works with old records
@@ -242,7 +248,8 @@ TEST(QueryPlan, NodeFilterMultipleLabels) {
           ->MapTo(symbol_table.CreateSymbol("named_expression_1", true));
   auto produce = MakeProduce(node_filter, output);
 
-  auto context = MakeContext(storage, symbol_table, &dba);
+  query::DbAccessor execution_dba(&dba);
+  auto context = MakeContext(storage, symbol_table, &execution_dba);
   auto results = CollectProduce(*produce, &context);
   EXPECT_EQ(results.size(), 2);
 }
@@ -257,8 +264,8 @@ TEST(QueryPlan, Cartesian) {
     return vertex;
   };
 
-  std::vector<VertexAccessor> vertices{add_vertex("v1"), add_vertex("v2"),
-                                       add_vertex("v3")};
+  std::vector<::VertexAccessor> vertices{add_vertex("v1"), add_vertex("v2"),
+                                         add_vertex("v3")};
   dba.AdvanceCommand();
 
   AstStorage storage;
@@ -280,13 +287,14 @@ TEST(QueryPlan, Cartesian) {
 
   auto produce = MakeProduce(cartesian_op, return_n, return_m);
 
-  auto context = MakeContext(storage, symbol_table, &dba);
+  query::DbAccessor execution_dba(&dba);
+  auto context = MakeContext(storage, symbol_table, &execution_dba);
   auto results = CollectProduce(*produce, &context);
   EXPECT_EQ(results.size(), 9);
   for (int i = 0; i < 3; ++i) {
     for (int j = 0; j < 3; ++j) {
-      EXPECT_EQ(results[3 * i + j][0].ValueVertex(), vertices[j]);
-      EXPECT_EQ(results[3 * i + j][1].ValueVertex(), vertices[i]);
+      EXPECT_EQ(results[3 * i + j][0].ValueVertex().impl_, vertices[j]);
+      EXPECT_EQ(results[3 * i + j][1].ValueVertex().impl_, vertices[i]);
     }
   }
 }
@@ -313,7 +321,8 @@ TEST(QueryPlan, CartesianEmptySet) {
       std::make_shared<Cartesian>(n.op_, left_symbols, m.op_, right_symbols);
 
   auto produce = MakeProduce(cartesian_op, return_n, return_m);
-  auto context = MakeContext(storage, symbol_table, &dba);
+  query::DbAccessor execution_dba(&dba);
+  auto context = MakeContext(storage, symbol_table, &execution_dba);
   auto results = CollectProduce(*produce, &context);
   EXPECT_EQ(results.size(), 0);
 }
@@ -327,8 +336,8 @@ TEST(QueryPlan, CartesianThreeWay) {
     return vertex;
   };
 
-  std::vector<VertexAccessor> vertices{add_vertex("v1"), add_vertex("v2"),
-                                       add_vertex("v3")};
+  std::vector<::VertexAccessor> vertices{add_vertex("v1"), add_vertex("v2"),
+                                         add_vertex("v3")};
   dba.AdvanceCommand();
 
   AstStorage storage;
@@ -358,16 +367,17 @@ TEST(QueryPlan, CartesianThreeWay) {
                                                     l.op_, l_symbols);
 
   auto produce = MakeProduce(cartesian_op_2, return_n, return_m, return_l);
-  auto context = MakeContext(storage, symbol_table, &dba);
+  query::DbAccessor execution_dba(&dba);
+  auto context = MakeContext(storage, symbol_table, &execution_dba);
   auto results = CollectProduce(*produce, &context);
   EXPECT_EQ(results.size(), 27);
   int id = 0;
   for (int i = 0; i < 3; ++i) {
     for (int j = 0; j < 3; ++j) {
       for (int k = 0; k < 3; ++k) {
-        EXPECT_EQ(results[id][0].ValueVertex(), vertices[k]);
-        EXPECT_EQ(results[id][1].ValueVertex(), vertices[j]);
-        EXPECT_EQ(results[id][2].ValueVertex(), vertices[i]);
+        EXPECT_EQ(results[id][0].ValueVertex().impl_, vertices[k]);
+        EXPECT_EQ(results[id][1].ValueVertex().impl_, vertices[j]);
+        EXPECT_EQ(results[id][2].ValueVertex().impl_, vertices[i]);
         ++id;
       }
     }
@@ -382,12 +392,12 @@ class ExpandFixture : public testing::Test {
   SymbolTable symbol_table;
 
   // make a V-graph (v3)<-[r2]-(v1)-[r1]->(v2)
-  VertexAccessor v1 = dba_.InsertVertex();
-  VertexAccessor v2 = dba_.InsertVertex();
-  VertexAccessor v3 = dba_.InsertVertex();
+  ::VertexAccessor v1 = dba_.InsertVertex();
+  ::VertexAccessor v2 = dba_.InsertVertex();
+  ::VertexAccessor v3 = dba_.InsertVertex();
   storage::EdgeType edge_type = dba_.EdgeType("Edge");
-  EdgeAccessor r1 = dba_.InsertEdge(v1, v2, edge_type);
-  EdgeAccessor r2 = dba_.InsertEdge(v1, v3, edge_type);
+  ::EdgeAccessor r1 = dba_.InsertEdge(v1, v2, edge_type);
+  ::EdgeAccessor r2 = dba_.InsertEdge(v1, v3, edge_type);
 
   void SetUp() override {
     v1.add_label(dba_.Label("l1"));
@@ -409,7 +419,8 @@ TEST_F(ExpandFixture, Expand) {
         NEXPR("m", IDENT("m")->MapTo(r_m.node_sym_))
             ->MapTo(symbol_table.CreateSymbol("named_expression_1", true));
     auto produce = MakeProduce(r_m.op_, output);
-    auto context = MakeContext(storage, symbol_table, &dba_);
+    query::DbAccessor execution_dba(&dba_);
+    auto context = MakeContext(storage, symbol_table, &execution_dba);
     return PullAll(*produce, &context);
   };
 
@@ -445,9 +456,13 @@ TEST_F(ExpandFixture, ExpandPath) {
           ->MapTo(symbol_table.CreateSymbol("named_expression_1", true));
   auto produce = MakeProduce(path, output);
 
-  std::vector<query::Path> expected_paths{query::Path(v1, r2, v3),
-                                          query::Path(v1, r1, v2)};
-  auto context = MakeContext(storage, symbol_table, &dba_);
+  std::vector<query::Path> expected_paths{
+      query::Path(query::VertexAccessor(v1), query::EdgeAccessor(r2),
+                  query::VertexAccessor(v3)),
+      query::Path(query::VertexAccessor(v1), query::EdgeAccessor(r1),
+                  query::VertexAccessor(v2))};
+  query::DbAccessor execution_dba(&dba_);
+  auto context = MakeContext(storage, symbol_table, &execution_dba);
   auto results = CollectProduce(*produce, &context);
   ASSERT_EQ(results.size(), 2);
   std::vector<query::Path> results_paths;
@@ -491,11 +506,11 @@ class QueryPlanExpandVariable : public testing::Test {
   void SetUp() {
     // create the graph
     int chain_length = 3;
-    std::vector<VertexAccessor> layer;
+    std::vector<::VertexAccessor> layer;
     for (int from_layer_ind = -1; from_layer_ind < chain_length - 1;
          from_layer_ind++) {
-      std::vector<VertexAccessor> new_layer{dba_.InsertVertex(),
-                                            dba_.InsertVertex()};
+      std::vector<::VertexAccessor> new_layer{dba_.InsertVertex(),
+                                              dba_.InsertVertex()};
       auto label = dba_.Label(std::to_string(from_layer_ind + 1));
       labels.push_back(label);
       for (size_t v_to_ind = 0; v_to_ind < new_layer.size(); v_to_ind++) {
@@ -582,7 +597,8 @@ class QueryPlanExpandVariable : public testing::Test {
   auto GetListResults(std::shared_ptr<LogicalOperator> input_op, Symbol symbol) {
     Frame frame(symbol_table.max_position());
     auto cursor = input_op->MakeCursor(utils::NewDeleteResource());
-    auto context = MakeContext(storage, symbol_table, &dba_);
+    query::DbAccessor execution_dba(&dba_);
+    auto context = MakeContext(storage, symbol_table, &execution_dba);
     std::vector<utils::pmr::vector<TypedValue>> results;
     while (cursor->Pull(frame, context))
       results.emplace_back(frame[symbol].ValueList());
@@ -595,7 +611,8 @@ class QueryPlanExpandVariable : public testing::Test {
   auto GetPathResults(std::shared_ptr<LogicalOperator> input_op, Symbol symbol) {
     Frame frame(symbol_table.max_position());
     auto cursor = input_op->MakeCursor(utils::NewDeleteResource());
-    auto context = MakeContext(storage, symbol_table, &dba_);
+    query::DbAccessor execution_dba(&dba_);
+    auto context = MakeContext(storage, symbol_table, &execution_dba);
     std::vector<Path> results;
     while (cursor->Pull(frame, context))
       results.emplace_back(frame[symbol].ValuePath());
@@ -776,7 +793,10 @@ TEST_F(QueryPlanExpandVariable, NamedPath) {
   for (const auto &v : dba_.Vertices(labels[0], false))
     for (const auto &e1 : v.out())
       for (const auto &e2 : e1.to().out())
-        expected_paths.emplace_back(v, e1, e1.to(), e2, e2.to());
+        expected_paths.emplace_back(
+            query::VertexAccessor(v), query::EdgeAccessor(e1),
+            query::VertexAccessor(e1.to()), query::EdgeAccessor(e2),
+            query::VertexAccessor(e2.to()));
   ASSERT_EQ(expected_paths.size(), 8);
 
   auto results = GetPathResults(create_path, path_symbol);
@@ -798,8 +818,8 @@ struct hash<std::pair<int, int>> {
 class QueryPlanExpandWeightedShortestPath : public testing::Test {
  public:
   struct ResultType {
-    std::vector<EdgeAccessor> path;
-    VertexAccessor vertex;
+    std::vector<::EdgeAccessor> path;
+    ::VertexAccessor vertex;
     double total_weight;
   };
 
@@ -813,10 +833,10 @@ class QueryPlanExpandWeightedShortestPath : public testing::Test {
 
   // make 5 vertices because we'll need to compare against them exactly
   // v[0] has `prop` with the value 0
-  std::vector<VertexAccessor> v;
+  std::vector<::VertexAccessor> v;
 
   // make some edges too, in a map (from, to) vertex indices
-  std::unordered_map<std::pair<int, int>, EdgeAccessor> e;
+  std::unordered_map<std::pair<int, int>, ::EdgeAccessor> e;
 
   AstStorage storage;
   SymbolTable symbol_table;
@@ -837,7 +857,7 @@ class QueryPlanExpandWeightedShortestPath : public testing::Test {
     }
 
     auto add_edge = [&](int from, int to, double weight) {
-      EdgeAccessor edge = dba.InsertEdge(v[from], v[to], edge_type);
+      auto edge = dba.InsertEdge(v[from], v[to], edge_type);
       edge.PropsSet(prop.second, PropertyValue(weight));
       e.emplace(std::make_pair(from, to), edge);
     };
@@ -894,13 +914,14 @@ class QueryPlanExpandWeightedShortestPath : public testing::Test {
     Frame frame(symbol_table.max_position());
     auto cursor = last_op->MakeCursor(utils::NewDeleteResource());
     std::vector<ResultType> results;
-    auto context = MakeContext(storage, symbol_table, &dba);
+    query::DbAccessor execution_dba(&dba);
+    auto context = MakeContext(storage, symbol_table, &execution_dba);
     while (cursor->Pull(frame, context)) {
-      results.push_back(ResultType{std::vector<EdgeAccessor>(),
-                                   frame[node_sym].ValueVertex(),
+      results.push_back(ResultType{std::vector<::EdgeAccessor>(),
+                                   frame[node_sym].ValueVertex().impl_,
                                    frame[total_weight].ValueDouble()});
       for (const TypedValue &edge : frame[edge_list_sym].ValueList())
-        results.back().path.emplace_back(edge.ValueEdge());
+        results.back().path.emplace_back(edge.ValueEdge().impl_);
     }
 
     return results;
@@ -1168,13 +1189,14 @@ TEST(QueryPlan, ExpandOptional) {
   auto m_ne = NEXPR("m", IDENT("m")->MapTo(r_m.node_sym_))
                   ->MapTo(symbol_table.CreateSymbol("m", true));
   auto produce = MakeProduce(optional, n_ne, r_ne, m_ne);
-  auto context = MakeContext(storage, symbol_table, &dba);
+  query::DbAccessor execution_dba(&dba);
+  auto context = MakeContext(storage, symbol_table, &execution_dba);
   auto results = CollectProduce(*produce, &context);
   ASSERT_EQ(4, results.size());
   int v1_is_n_count = 0;
   for (auto &row : results) {
     ASSERT_EQ(row[0].type(), TypedValue::Type::Vertex);
-    VertexAccessor &va = row[0].ValueVertex();
+    auto &va = row[0].ValueVertex().impl_;
     auto va_p = va.PropsAt(prop);
     ASSERT_EQ(va_p.type(), PropertyValue::Type::Int);
     if (va_p.ValueInt() == 1) {
@@ -1204,7 +1226,8 @@ TEST(QueryPlan, OptionalMatchEmptyDB) {
   auto optional = std::make_shared<plan::Optional>(nullptr, n.op_,
                                                    std::vector<Symbol>{n.sym_});
   auto produce = MakeProduce(optional, n_ne);
-  auto context = MakeContext(storage, symbol_table, &dba);
+  query::DbAccessor execution_dba(&dba);
+  auto context = MakeContext(storage, symbol_table, &execution_dba);
   auto results = CollectProduce(*produce, &context);
   ASSERT_EQ(1, results.size());
   EXPECT_EQ(results[0][0].type(), TypedValue::Type::Null);
@@ -1232,7 +1255,8 @@ TEST(QueryPlan, OptionalMatchEmptyDBExpandFromNode) {
   auto m_ne = NEXPR("m", IDENT("m")->MapTo(r_m.node_sym_))
                   ->MapTo(symbol_table.CreateSymbol("m", true));
   auto produce = MakeProduce(r_m.op_, m_ne);
-  auto context = MakeContext(storage, symbol_table, &dba);
+  query::DbAccessor execution_dba(&dba);
+  auto context = MakeContext(storage, symbol_table, &execution_dba);
   auto results = CollectProduce(*produce, &context);
   EXPECT_EQ(0, results.size());
 }
@@ -1280,7 +1304,8 @@ TEST(QueryPlan, OptionalMatchThenExpandToMissingNode) {
   auto m_ne = NEXPR("m", IDENT("m")->MapTo(m.sym_))
                   ->MapTo(symbol_table.CreateSymbol("m", true));
   auto produce = MakeProduce(expand, m_ne);
-  auto context = MakeContext(storage, symbol_table, &dba);
+  query::DbAccessor execution_dba(&dba);
+  auto context = MakeContext(storage, symbol_table, &execution_dba);
   auto results = CollectProduce(*produce, &context);
   EXPECT_EQ(0, results.size());
 }
@@ -1316,7 +1341,8 @@ TEST(QueryPlan, ExpandExistingNode) {
         NEXPR("n", IDENT("n")->MapTo(n.sym_))
             ->MapTo(symbol_table.CreateSymbol("named_expression_1", true));
     auto produce = MakeProduce(r_n.op_, output);
-    auto context = MakeContext(storage, symbol_table, &dba);
+    query::DbAccessor execution_dba(&dba);
+    auto context = MakeContext(storage, symbol_table, &execution_dba);
     auto results = CollectProduce(*produce, &context);
     EXPECT_EQ(results.size(), expected_result_count);
   };
@@ -1342,7 +1368,8 @@ TEST(QueryPlan, ExpandBothCycleEdgeCase) {
   auto r_ =
       MakeExpand(storage, symbol_table, n.op_, n.sym_, "r",
                  EdgeAtom::Direction::BOTH, {}, "_", false, storage::View::OLD);
-  auto context = MakeContext(storage, symbol_table, &dba);
+  query::DbAccessor execution_dba(&dba);
+  auto context = MakeContext(storage, symbol_table, &execution_dba);
   EXPECT_EQ(1, PullAll(*r_.op_, &context));
 }
 
@@ -1357,10 +1384,10 @@ TEST(QueryPlan, EdgeFilter) {
   std::vector<storage::EdgeType> edge_types;
   for (int j = 0; j < 2; ++j)
     edge_types.push_back(dba.EdgeType("et" + std::to_string(j)));
-  std::vector<VertexAccessor> vertices;
+  std::vector<::VertexAccessor> vertices;
   for (int i = 0; i < 7; ++i) vertices.push_back(dba.InsertVertex());
   auto prop = PROPERTY_PAIR("property");
-  std::vector<EdgeAccessor> edges;
+  std::vector<::EdgeAccessor> edges;
   for (int i = 0; i < 6; ++i) {
     edges.push_back(
         dba.InsertEdge(vertices[0], vertices[i + 1], edge_types[i % 2]));
@@ -1403,7 +1430,8 @@ TEST(QueryPlan, EdgeFilter) {
         NEXPR("m", IDENT("m")->MapTo(r_m.node_sym_))
             ->MapTo(symbol_table.CreateSymbol("named_expression_1", true));
     auto produce = MakeProduce(edge_filter, output);
-    auto context = MakeContext(storage, symbol_table, &dba);
+    query::DbAccessor execution_dba(&dba);
+    auto context = MakeContext(storage, symbol_table, &execution_dba);
     return PullAll(*produce, &context);
   };
 
@@ -1443,7 +1471,8 @@ TEST(QueryPlan, EdgeFilterMultipleTypes) {
       NEXPR("m", IDENT("m")->MapTo(r_m.node_sym_))
           ->MapTo(symbol_table.CreateSymbol("named_expression_1", true));
   auto produce = MakeProduce(r_m.op_, output);
-  auto context = MakeContext(storage, symbol_table, &dba);
+  query::DbAccessor execution_dba(&dba);
+  auto context = MakeContext(storage, symbol_table, &execution_dba);
   auto results = CollectProduce(*produce, &context);
   EXPECT_EQ(results.size(), 2);
 }
@@ -1470,7 +1499,8 @@ TEST(QueryPlan, Filter) {
       NEXPR("x", IDENT("n")->MapTo(n.sym_))
           ->MapTo(symbol_table.CreateSymbol("named_expression_1", true));
   auto produce = MakeProduce(f, output);
-  auto context = MakeContext(storage, symbol_table, &dba);
+  query::DbAccessor execution_dba(&dba);
+  auto context = MakeContext(storage, symbol_table, &execution_dba);
   EXPECT_EQ(CollectProduce(*produce, &context).size(), 2);
 }
 
@@ -1502,7 +1532,8 @@ TEST(QueryPlan, EdgeUniquenessFilter) {
     if (edge_uniqueness)
       last_op = std::make_shared<EdgeUniquenessFilter>(
           last_op, r2_n3.edge_sym_, std::vector<Symbol>{r1_n2.edge_sym_});
-    auto context = MakeContext(storage, symbol_table, &dba);
+    query::DbAccessor execution_dba(&dba);
+    auto context = MakeContext(storage, symbol_table, &execution_dba);
     return PullAll(*last_op, &context);
   };
 
@@ -1535,7 +1566,8 @@ TEST(QueryPlan, Distinct) {
     auto x_ne = NEXPR("x", x_expr);
     x_ne->MapTo(symbol_table.CreateSymbol("x_ne", true));
     auto produce = MakeProduce(distinct, x_ne);
-    auto context = MakeContext(storage, symbol_table, &dba);
+    query::DbAccessor execution_dba(&dba);
+    auto context = MakeContext(storage, symbol_table, &execution_dba);
     auto results = CollectProduce(*produce, &context);
     ASSERT_EQ(output.size(), results.size());
     auto output_it = output.begin();
@@ -1582,12 +1614,13 @@ TEST(QueryPlan, ScanAllByLabel) {
   auto output = NEXPR("n", IDENT("n")->MapTo(scan_all_by_label.sym_))
                     ->MapTo(symbol_table.CreateSymbol("n", true));
   auto produce = MakeProduce(scan_all_by_label.op_, output);
-  auto context = MakeContext(storage, symbol_table, &dba);
+  query::DbAccessor execution_dba(&dba);
+  auto context = MakeContext(storage, symbol_table, &execution_dba);
   auto results = CollectProduce(*produce, &context);
   ASSERT_EQ(results.size(), 1);
   auto result_row = results[0];
   ASSERT_EQ(result_row.size(), 1);
-  EXPECT_EQ(result_row[0].ValueVertex(), labeled_vertex);
+  EXPECT_EQ(result_row[0].ValueVertex().impl_, labeled_vertex);
 }
 
 TEST(QueryPlan, ScanAllByLabelProperty) {
@@ -1636,12 +1669,14 @@ TEST(QueryPlan, ScanAllByLabelProperty) {
     auto output = NEXPR("n", IDENT("n")->MapTo(scan_all.sym_))
                       ->MapTo(symbol_table.CreateSymbol("n", true));
     auto produce = MakeProduce(scan_all.op_, output);
-    auto context = MakeContext(storage, symbol_table, &dba);
+    query::DbAccessor execution_dba(&dba);
+    auto context = MakeContext(storage, symbol_table, &execution_dba);
     auto results = CollectProduce(*produce, &context);
     ASSERT_EQ(results.size(), expected.size());
     for (size_t i = 0; i < expected.size(); i++) {
       TypedValue equal =
-          TypedValue(results[i][0].ValueVertex().PropsAt(prop)) == expected[i];
+          TypedValue(results[i][0].ValueVertex().impl_.PropsAt(prop)) ==
+          expected[i];
       ASSERT_EQ(equal.type(), TypedValue::Type::Bool);
       EXPECT_TRUE(equal.ValueBool());
     }
@@ -1712,12 +1747,13 @@ TEST(QueryPlan, ScanAllByLabelPropertyEqualityNoError) {
   auto output = NEXPR("n", IDENT("n")->MapTo(scan_all.sym_))
                     ->MapTo(symbol_table.CreateSymbol("n", true));
   auto produce = MakeProduce(scan_all.op_, output);
-  auto context = MakeContext(storage, symbol_table, &dba);
+  query::DbAccessor execution_dba(&dba);
+  auto context = MakeContext(storage, symbol_table, &execution_dba);
   auto results = CollectProduce(*produce, &context);
   ASSERT_EQ(results.size(), 1);
   const auto &row = results[0];
   ASSERT_EQ(row.size(), 1);
-  auto vertex = row[0].ValueVertex();
+  auto vertex = row[0].ValueVertex().impl_;
   TypedValue value(vertex.PropsAt(prop));
   TypedValue::BoolEqual eq;
   EXPECT_TRUE(eq(value, TypedValue(42)));
@@ -1747,7 +1783,8 @@ TEST(QueryPlan, ScanAllByLabelPropertyValueError) {
   ident_m->MapTo(scan_all.sym_);
   auto scan_index = MakeScanAllByLabelPropertyValue(
       storage, symbol_table, "n", label, prop, "prop", ident_m, scan_all.op_);
-  auto context = MakeContext(storage, symbol_table, &dba);
+  query::DbAccessor execution_dba(&dba);
+  auto context = MakeContext(storage, symbol_table, &execution_dba);
   EXPECT_THROW(PullAll(*scan_index.op_, &context), QueryRuntimeException);
 }
 
@@ -1778,7 +1815,8 @@ TEST(QueryPlan, ScanAllByLabelPropertyRangeError) {
     auto scan_index = MakeScanAllByLabelPropertyRange(
         storage, symbol_table, "n", label, prop, "prop",
         Bound{ident_m, Bound::Type::INCLUSIVE}, std::nullopt, scan_all.op_);
-    auto context = MakeContext(storage, symbol_table, &dba);
+    query::DbAccessor execution_dba(&dba);
+    auto context = MakeContext(storage, symbol_table, &execution_dba);
     EXPECT_THROW(PullAll(*scan_index.op_, &context), QueryRuntimeException);
   }
   {
@@ -1786,7 +1824,8 @@ TEST(QueryPlan, ScanAllByLabelPropertyRangeError) {
     auto scan_index = MakeScanAllByLabelPropertyRange(
         storage, symbol_table, "n", label, prop, "prop", std::nullopt,
         Bound{ident_m, Bound::Type::INCLUSIVE}, scan_all.op_);
-    auto context = MakeContext(storage, symbol_table, &dba);
+    query::DbAccessor execution_dba(&dba);
+    auto context = MakeContext(storage, symbol_table, &execution_dba);
     EXPECT_THROW(PullAll(*scan_index.op_, &context), QueryRuntimeException);
   }
   {
@@ -1795,7 +1834,8 @@ TEST(QueryPlan, ScanAllByLabelPropertyRangeError) {
         storage, symbol_table, "n", label, prop, "prop",
         Bound{ident_m, Bound::Type::INCLUSIVE},
         Bound{ident_m, Bound::Type::INCLUSIVE}, scan_all.op_);
-    auto context = MakeContext(storage, symbol_table, &dba);
+    query::DbAccessor execution_dba(&dba);
+    auto context = MakeContext(storage, symbol_table, &execution_dba);
     EXPECT_THROW(PullAll(*scan_index.op_, &context), QueryRuntimeException);
   }
 }
@@ -1829,7 +1869,8 @@ TEST(QueryPlan, ScanAllByLabelPropertyEqualNull) {
   auto output = NEXPR("n", IDENT("n")->MapTo(scan_all.sym_))
                     ->MapTo(symbol_table.CreateSymbol("n", true));
   auto produce = MakeProduce(scan_all.op_, output);
-  auto context = MakeContext(storage, symbol_table, &dba);
+  query::DbAccessor execution_dba(&dba);
+  auto context = MakeContext(storage, symbol_table, &execution_dba);
   auto results = CollectProduce(*produce, &context);
   EXPECT_EQ(results.size(), 0);
 }
@@ -1864,7 +1905,8 @@ TEST(QueryPlan, ScanAllByLabelPropertyRangeNull) {
   auto output = NEXPR("n", IDENT("n")->MapTo(scan_all.sym_))
                     ->MapTo(symbol_table.CreateSymbol("n", true));
   auto produce = MakeProduce(scan_all.op_, output);
-  auto context = MakeContext(storage, symbol_table, &dba);
+  query::DbAccessor execution_dba(&dba);
+  auto context = MakeContext(storage, symbol_table, &execution_dba);
   auto results = CollectProduce(*produce, &context);
   EXPECT_EQ(results.size(), 0);
 }
@@ -1898,7 +1940,8 @@ TEST(QueryPlan, ScanAllByLabelPropertyNoValueInIndexContinuation) {
   auto scan_all = MakeScanAllByLabelPropertyValue(
       storage, symbol_table, "n", label, prop, "prop", x_expr, unwind);
 
-  auto context = MakeContext(storage, symbol_table, &dba);
+  query::DbAccessor execution_dba(&dba);
+  auto context = MakeContext(storage, symbol_table, &execution_dba);
   EXPECT_EQ(PullAll(*scan_all.op_, &context), 1);
 }
 
@@ -1939,7 +1982,8 @@ TEST(QueryPlan, ScanAllEqualsScanAllByLabelProperty) {
         NEXPR("n", IDENT("n")->MapTo(scan_all_by_label_property_value.sym_))
             ->MapTo(symbol_table.CreateSymbol("named_expression_1", true));
     auto produce = MakeProduce(scan_all_by_label_property_value.op_, output);
-    auto context = MakeContext(storage, symbol_table, &dba);
+    query::DbAccessor execution_dba(&dba);
+    auto context = MakeContext(storage, symbol_table, &execution_dba);
     EXPECT_EQ(PullAll(*produce, &context), prop_count);
   };
 
@@ -1957,7 +2001,8 @@ TEST(QueryPlan, ScanAllEqualsScanAllByLabelProperty) {
         NEXPR("n", IDENT("n")->MapTo(scan_all.sym_))
             ->MapTo(symbol_table.CreateSymbol("named_expression_1", true));
     auto produce = MakeProduce(filter, output);
-    auto context = MakeContext(storage, symbol_table, &dba);
+    query::DbAccessor execution_dba(&dba);
+    auto context = MakeContext(storage, symbol_table, &execution_dba);
     EXPECT_EQ(PullAll(*produce, &context), prop_count);
   };
 
diff --git a/tests/unit/query_plan_v2_create_set_remove_delete.cpp b/tests/unit/query_plan_v2_create_set_remove_delete.cpp
new file mode 100644
index 000000000..2975d5be0
--- /dev/null
+++ b/tests/unit/query_plan_v2_create_set_remove_delete.cpp
@@ -0,0 +1,119 @@
+#include <gtest/gtest.h>
+
+#include "query_plan_common.hpp"
+
+#include "query/frontend/semantic/symbol_table.hpp"
+#include "query/plan/operator.hpp"
+#include "storage/v2/storage.hpp"
+
+TEST(QueryPlan, CreateNodeWithAttributes) {
+  storage::Storage db;
+  auto dba = db.Access();
+
+  auto label = storage::Label::FromInt(42);
+  auto property = storage::Property::FromInt(1);
+
+  query::AstStorage ast;
+  query::SymbolTable symbol_table;
+
+  query::plan::NodeCreationInfo node;
+  node.symbol = symbol_table.CreateSymbol("n", true);
+  node.labels.emplace_back(label);
+  node.properties.emplace_back(property, ast.Create<PrimitiveLiteral>(42));
+
+  query::plan::CreateNode create_node(nullptr, node);
+  DbAccessor execution_dba(&dba);
+  auto context = MakeContext(ast, symbol_table, &execution_dba);
+  Frame frame(context.symbol_table.max_position());
+  auto cursor = create_node.MakeCursor(utils::NewDeleteResource());
+  int count = 0;
+  while (cursor->Pull(frame, context)) {
+    ++count;
+    const auto &node_value = frame[node.symbol];
+    EXPECT_EQ(node_value.type(), TypedValue::Type::Vertex);
+    const auto &v = node_value.ValueVertex();
+    EXPECT_TRUE(*v.HasLabel(storage::View::NEW, label));
+    EXPECT_EQ(v.GetProperty(storage::View::NEW, property)->ValueInt(), 42);
+    EXPECT_EQ(CountIterable(*v.InEdges(storage::View::NEW)), 0);
+    EXPECT_EQ(CountIterable(*v.OutEdges(storage::View::NEW)), 0);
+    // Invokes LOG(FATAL) instead of erroring out.
+    // EXPECT_TRUE(v.HasLabel(label, storage::View::OLD).IsError());
+  }
+  EXPECT_EQ(count, 1);
+}
+
+TEST(QueryPlan, ScanAllEmpty) {
+  query::AstStorage ast;
+  query::SymbolTable symbol_table;
+  storage::Storage db;
+  auto dba = db.Access();
+  DbAccessor execution_dba(&dba);
+  auto node_symbol = symbol_table.CreateSymbol("n", true);
+  {
+    query::plan::ScanAll scan_all(nullptr, node_symbol, storage::View::OLD);
+    auto context = MakeContext(ast, symbol_table, &execution_dba);
+    Frame frame(context.symbol_table.max_position());
+    auto cursor = scan_all.MakeCursor(utils::NewDeleteResource());
+    int count = 0;
+    while (cursor->Pull(frame, context)) ++count;
+    EXPECT_EQ(count, 0);
+  }
+  {
+    query::plan::ScanAll scan_all(nullptr, node_symbol, storage::View::NEW);
+    auto context = MakeContext(ast, symbol_table, &execution_dba);
+    Frame frame(context.symbol_table.max_position());
+    auto cursor = scan_all.MakeCursor(utils::NewDeleteResource());
+    int count = 0;
+    while (cursor->Pull(frame, context)) ++count;
+    EXPECT_EQ(count, 0);
+  }
+}
+
+TEST(QueryPlan, ScanAll) {
+  storage::Storage db;
+  {
+    auto dba = db.Access();
+    for (int i = 0; i < 42; ++i) dba.CreateVertex();
+    EXPECT_FALSE(dba.Commit().HasError());
+  }
+  query::AstStorage ast;
+  query::SymbolTable symbol_table;
+  auto dba = db.Access();
+  DbAccessor execution_dba(&dba);
+  auto node_symbol = symbol_table.CreateSymbol("n", true);
+  query::plan::ScanAll scan_all(nullptr, node_symbol);
+  auto context = MakeContext(ast, symbol_table, &execution_dba);
+  Frame frame(context.symbol_table.max_position());
+  auto cursor = scan_all.MakeCursor(utils::NewDeleteResource());
+  int count = 0;
+  while (cursor->Pull(frame, context)) ++count;
+  EXPECT_EQ(count, 42);
+}
+
+TEST(QueryPlan, ScanAllByLabel) {
+  storage::Storage db;
+  auto label = db.NameToLabel("label");
+  {
+    auto dba = db.Access();
+    // Add some unlabeled vertices
+    for (int i = 0; i < 12; ++i) dba.CreateVertex();
+    // Add labeled vertices
+    for (int i = 0; i < 42; ++i) {
+      auto v = dba.CreateVertex();
+      ASSERT_TRUE(v.AddLabel(label).HasValue());
+    }
+    EXPECT_FALSE(dba.Commit().HasError());
+  }
+  auto dba = db.Access();
+  query::AstStorage ast;
+  query::SymbolTable symbol_table;
+  auto node_symbol = symbol_table.CreateSymbol("n", true);
+  DbAccessor execution_dba(&dba);
+  query::plan::ScanAllByLabel scan_all(nullptr, node_symbol, label);
+  auto context = MakeContext(ast, symbol_table, &execution_dba);
+  Frame frame(context.symbol_table.max_position());
+  auto cursor = scan_all.MakeCursor(utils::NewDeleteResource());
+  int count = 0;
+  while (cursor->Pull(frame, context)) ++count;
+  EXPECT_EQ(count, 42);
+}
diff --git a/tests/unit/query_variable_start_planner.cpp b/tests/unit/query_variable_start_planner.cpp
index 050a32e5e..af2d74e18 100644
--- a/tests/unit/query_variable_start_planner.cpp
+++ b/tests/unit/query_variable_start_planner.cpp
@@ -61,8 +61,9 @@ void CheckPlansProduce(
     database::GraphDbAccessor *dba,
     std::function<void(const std::vector<std::vector<TypedValue>> &)> check) {
   auto symbol_table = query::MakeSymbolTable(query);
+  query::DbAccessor execution_dba(dba);
   auto planning_context =
-      MakePlanningContext(&storage, &symbol_table, query, dba);
+      MakePlanningContext(&storage, &symbol_table, query, &execution_dba);
   auto query_parts = CollectQueryParts(symbol_table, storage, query);
   EXPECT_TRUE(query_parts.query_parts.size() > 0);
   auto single_query_parts = query_parts.query_parts.at(0).single_query_parts;
@@ -72,7 +73,7 @@ void CheckPlansProduce(
   for (const auto &plan : plans) {
     auto *produce = dynamic_cast<Produce *>(plan.get());
     ASSERT_TRUE(produce);
-    auto context = MakeContext(storage, symbol_table, dba);
+    auto context = MakeContext(storage, symbol_table, &execution_dba);
     auto results = CollectProduce(*produce, &context);
     check(results);
   }
@@ -94,7 +95,7 @@ TEST(TestVariableStartPlanner, MatchReturn) {
   // We have 2 nodes `n` and `m` from which we could start, so expect 2 plans.
   CheckPlansProduce(2, query, storage, &dba, [&](const auto &results) {
     // We expect to produce only a single (v1) node.
-    AssertRows(results, {{TypedValue(v1)}});
+    AssertRows(results, {{TypedValue(query::VertexAccessor(v1))}});
   });
 }
 
@@ -118,7 +119,7 @@ TEST(TestVariableStartPlanner, MatchTripletPatternReturn) {
     // We have 3 nodes: `n`, `m` and `l` from which we could start.
     CheckPlansProduce(3, query, storage, &dba, [&](const auto &results) {
       // We expect to produce only a single (v1) node.
-      AssertRows(results, {{TypedValue(v1)}});
+      AssertRows(results, {{TypedValue(query::VertexAccessor(v1))}});
     });
   }
   {
@@ -129,7 +130,7 @@ TEST(TestVariableStartPlanner, MatchTripletPatternReturn) {
               PATTERN(NODE("m"), EDGE("e", Direction::OUT), NODE("l"))),
         RETURN("n")));
     CheckPlansProduce(3, query, storage, &dba, [&](const auto &results) {
-      AssertRows(results, {{TypedValue(v1)}});
+      AssertRows(results, {{TypedValue(query::VertexAccessor(v1))}});
     });
   }
 }
@@ -156,8 +157,10 @@ TEST(TestVariableStartPlanner, MatchOptionalMatchReturn) {
     // We expect to produce 2 rows:
     //   * (v1), (v3)
     //   * (v2), null
-    AssertRows(results, {{TypedValue(v1), TypedValue(v3)},
-                         {TypedValue(v2), TypedValue()}});
+    AssertRows(results,
+               {{TypedValue(query::VertexAccessor(v1)),
+                 TypedValue(query::VertexAccessor(v3))},
+                {TypedValue(query::VertexAccessor(v2)), TypedValue()}});
   });
 }
 
@@ -165,11 +168,11 @@ TEST(TestVariableStartPlanner, MatchOptionalMatchMergeReturn) {
   database::GraphDb db;
   auto dba = db.Access();
   // Graph (v1) -[:r]-> (v2)
-  auto v1 = dba.InsertVertex();
-  auto v2 = dba.InsertVertex();
+  query::VertexAccessor v1(dba.InsertVertex());
+  query::VertexAccessor v2(dba.InsertVertex());
   auto r_type_name = "r";
   auto r_type = dba.EdgeType(r_type_name);
-  dba.InsertEdge(v1, v2, r_type);
+  dba.InsertEdge(v1.impl_, v2.impl_, r_type);
   dba.AdvanceCommand();
   // Test MATCH (n) -[r]-> (m) OPTIONAL MATCH (m) -[e]-> (l)
   //      MERGE (u) -[q:r]-> (v) RETURN n, m, l, u, v
@@ -193,9 +196,9 @@ TEST(TestVariableStartPlanner, MatchWithMatchReturn) {
   database::GraphDb db;
   auto dba = db.Access();
   // Graph (v1) -[:r]-> (v2)
-  auto v1 = dba.InsertVertex();
-  auto v2 = dba.InsertVertex();
-  dba.InsertEdge(v1, v2, dba.EdgeType("r"));
+  query::VertexAccessor v1(dba.InsertVertex());
+  query::VertexAccessor v2(dba.InsertVertex());
+  dba.InsertEdge(v1.impl_, v2.impl_, dba.EdgeType("r"));
   dba.AdvanceCommand();
   // Test MATCH (n) -[r]-> (m) WITH n MATCH (m) -[r]-> (l) RETURN n, m, l
   AstStorage storage;
@@ -219,8 +222,8 @@ TEST(TestVariableStartPlanner, MatchVariableExpand) {
   auto v1 = dba.InsertVertex();
   auto v2 = dba.InsertVertex();
   auto v3 = dba.InsertVertex();
-  auto r1 = dba.InsertEdge(v1, v2, dba.EdgeType("r1"));
-  auto r2 = dba.InsertEdge(v2, v3, dba.EdgeType("r2"));
+  query::EdgeAccessor r1(dba.InsertEdge(v1, v2, dba.EdgeType("r1")));
+  query::EdgeAccessor r2(dba.InsertEdge(v2, v3, dba.EdgeType("r2")));
   dba.AdvanceCommand();
   // Test MATCH (n) -[r*]-> (m) RETURN r
   AstStorage storage;
@@ -249,8 +252,8 @@ TEST(TestVariableStartPlanner, MatchVariableExpandReferenceNode) {
   v2.PropsSet(id, PropertyValue(2));
   auto v3 = dba.InsertVertex();
   v3.PropsSet(id, PropertyValue(3));
-  auto r1 = dba.InsertEdge(v1, v2, dba.EdgeType("r1"));
-  auto r2 = dba.InsertEdge(v2, v3, dba.EdgeType("r2"));
+  query::EdgeAccessor r1(dba.InsertEdge(v1, v2, dba.EdgeType("r1")));
+  query::EdgeAccessor r2(dba.InsertEdge(v2, v3, dba.EdgeType("r2")));
   dba.AdvanceCommand();
   // Test MATCH (n) -[r*..n.id]-> (m) RETURN r
   AstStorage storage;
@@ -277,8 +280,8 @@ TEST(TestVariableStartPlanner, MatchVariableExpandBoth) {
   v1.PropsSet(id, PropertyValue(1));
   auto v2 = dba.InsertVertex();
   auto v3 = dba.InsertVertex();
-  auto r1 = dba.InsertEdge(v1, v2, dba.EdgeType("r1"));
-  auto r2 = dba.InsertEdge(v2, v3, dba.EdgeType("r2"));
+  query::EdgeAccessor r1(dba.InsertEdge(v1, v2, dba.EdgeType("r1")));
+  query::EdgeAccessor r2(dba.InsertEdge(v2, v3, dba.EdgeType("r2")));
   dba.AdvanceCommand();
   // Test MATCH (n {id:1}) -[r*]- (m) RETURN r
   AstStorage storage;
@@ -308,7 +311,7 @@ TEST(TestVariableStartPlanner, MatchBfs) {
   v2.PropsSet(id, PropertyValue(2));
   auto v3 = dba.InsertVertex();
   v3.PropsSet(id, PropertyValue(3));
-  auto r1 = dba.InsertEdge(v1, v2, dba.EdgeType("r1"));
+  query::EdgeAccessor r1(dba.InsertEdge(v1, v2, dba.EdgeType("r1")));
   dba.InsertEdge(v2, v3, dba.EdgeType("r2"));
   dba.AdvanceCommand();
   // Test MATCH (n) -[r *bfs..10](r, n | n.id <> 3)]-> (m) RETURN r
diff --git a/tests/unit/typed_value.cpp b/tests/unit/typed_value.cpp
index 4630cc879..69f9dd49e 100644
--- a/tests/unit/typed_value.cpp
+++ b/tests/unit/typed_value.cpp
@@ -37,9 +37,11 @@ class AllTypesFixture : public testing::Test {
                                           {"d", TypedValue(0.5)},
                                           {"e", TypedValue()}});
     auto vertex = dba_.InsertVertex();
-    values_.emplace_back(vertex);
-    values_.emplace_back(dba_.InsertEdge(vertex, vertex, dba_.EdgeType("et")));
-    values_.emplace_back(query::Path(dba_.InsertVertex()));
+    values_.emplace_back(query::VertexAccessor(vertex));
+    values_.emplace_back(query::EdgeAccessor(
+        dba_.InsertEdge(vertex, vertex, dba_.EdgeType("et"))));
+    values_.emplace_back(
+        query::Path(query::VertexAccessor(dba_.InsertVertex())));
   }
 };