Make query execution work with storage_v2

Reviewers: mferencevic, ipaljak

Reviewed By: mferencevic

Subscribers: pullbot

Differential Revision: https://phabricator.memgraph.io/D2203
This commit is contained in:
Teon Banek 2019-09-11 16:10:53 +02:00
parent 68f19df305
commit 7bd45f8714
60 changed files with 1694 additions and 878 deletions

View File

@ -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
# ----------------------------------------------------------------------------

View File

@ -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;

View File

@ -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

View File

@ -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,

View File

@ -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] {

View File

@ -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();

View File

@ -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_;

View File

@ -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

View File

@ -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());
}
}

View File

@ -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};

View File

@ -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

View File

@ -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));
}

View File

@ -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.

View File

@ -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_;
};

View File

@ -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);

View File

@ -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

View File

@ -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_;

View File

@ -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_++;

View File

@ -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_;

View File

@ -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);

View File

@ -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;

View File

@ -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) {

View File

@ -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(

View File

@ -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();

View File

@ -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());

View File

@ -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;
}

View File

@ -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();

View File

@ -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"

View File

@ -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

View File

@ -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

View File

@ -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
*/

View File

@ -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_;
}

View File

@ -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,

View File

@ -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

View File

@ -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);
}
}

View File

@ -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));
}

View File

@ -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());

View File

@ -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();

View File

@ -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);

View File

@ -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());

View File

@ -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

View File

@ -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)

View File

@ -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.

View File

@ -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));

View File

@ -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,

View File

@ -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());

View File

@ -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();

View File

@ -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();

View File

@ -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));
}
};

View File

@ -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);

View File

@ -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};

View File

@ -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);
}
}

View File

@ -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_;

View File

@ -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 =

View File

@ -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);
}

View File

@ -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);

View File

@ -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);
};

View File

@ -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);
}

View File

@ -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

View File

@ -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())));
}
};