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:
parent
68f19df305
commit
7bd45f8714
@ -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
|
||||
# ----------------------------------------------------------------------------
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
|
@ -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,
|
||||
|
@ -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] {
|
||||
|
@ -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> ¶ms) {
|
||||
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();
|
||||
|
@ -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_;
|
||||
|
@ -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
|
||||
|
@ -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());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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};
|
||||
|
@ -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
|
||||
|
@ -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));
|
||||
}
|
||||
|
@ -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.
|
||||
|
@ -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_;
|
||||
};
|
||||
|
@ -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 ¶meters,
|
||||
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 ¶meters,
|
||||
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> ¶ms,
|
||||
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, ¶meters, &ast_storage,
|
||||
&db_accessor, params);
|
||||
auto queries =
|
||||
StripAndParseQuery(query_string, ¶meters, &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()),
|
||||
¶meters, &ast_storage, &db_accessor, params);
|
||||
¶meters, &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()),
|
||||
¶meters, &ast_storage, &db_accessor, params);
|
||||
¶meters, &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 ¶meters, database::GraphDbAccessor *db_accessor) {
|
||||
const Parameters ¶meters, 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> ¶ms) {
|
||||
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 ¶meters,
|
||||
database::GraphDbAccessor *db_accessor) {
|
||||
DbAccessor *db_accessor) {
|
||||
auto vertex_counts = plan::MakeVertexCountCache(db_accessor);
|
||||
|
||||
auto symbol_table = MakeSymbolTable(query);
|
||||
|
@ -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 ¶meters,
|
||||
Results(DbAccessor *db_accessor, const query::Parameters ¶meters,
|
||||
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> ¶ms,
|
||||
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 ¶meters, database::GraphDbAccessor *db_accessor);
|
||||
std::shared_ptr<CachedPlan> CypherQueryToPlan(HashType query_hash,
|
||||
CypherQuery *query,
|
||||
AstStorage ast_storage,
|
||||
const Parameters ¶meters,
|
||||
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
|
||||
|
@ -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_;
|
||||
|
@ -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_++;
|
||||
|
@ -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_;
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
|
@ -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) {
|
||||
|
@ -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(
|
||||
|
@ -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();
|
||||
|
@ -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());
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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();
|
||||
|
@ -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"
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
*/
|
||||
|
@ -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_;
|
||||
}
|
||||
|
@ -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,
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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));
|
||||
}
|
||||
|
@ -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());
|
||||
|
@ -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();
|
||||
|
@ -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);
|
||||
|
@ -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());
|
||||
|
@ -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
|
||||
|
||||
|
@ -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)
|
||||
|
||||
|
@ -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.
|
||||
|
@ -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));
|
||||
|
@ -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,
|
||||
|
@ -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());
|
||||
|
@ -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();
|
||||
|
@ -20,9 +20,10 @@ class InterpreterTest : public ::testing::Test {
|
||||
auto Interpret(const std::string &query,
|
||||
const std::map<std::string, PropertyValue> ¶ms = {}) {
|
||||
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();
|
||||
|
@ -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));
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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};
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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_;
|
||||
|
@ -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 =
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
};
|
||||
|
||||
|
119
tests/unit/query_plan_v2_create_set_remove_delete.cpp
Normal file
119
tests/unit/query_plan_v2_create_set_remove_delete.cpp
Normal 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);
|
||||
}
|
@ -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
|
||||
|
@ -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())));
|
||||
}
|
||||
};
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user