9f460914ed
Summary: GraphDbAccessor is now constructed only through GraphDb. This allows the concrete GraphDb to instantiate a concrete GraphDbAccessor. This allows us to use virtual calls, so that the implementation may be kept separate. The major downside of doing things this way is heap allocation of GraphDbAccessor. In case it turns out to be a real performance issues, another solution with pointer to static implementation may be used. InsertVertexIntoRemote is now a non-member function, which reduces coupling. It made no sense for it to be member function because it used only the public parts of GraphDbAccessor. Reviewers: msantl, mtomic, mferencevic Reviewed By: msantl Subscribers: pullbot Differential Revision: https://phabricator.memgraph.io/D1504
97 lines
3.0 KiB
C++
97 lines
3.0 KiB
C++
// tests in this suite deal with edge cases in logical operator behavior
|
|
// that's not easily testable with single-phase testing. instead, for
|
|
// easy testing and latter readability they are tested end-to-end.
|
|
|
|
#include <experimental/optional>
|
|
|
|
#include "gmock/gmock.h"
|
|
#include "gtest/gtest.h"
|
|
|
|
#include "communication/result_stream_faker.hpp"
|
|
#include "database/graph_db.hpp"
|
|
#include "database/graph_db_accessor.hpp"
|
|
#include "query/interpreter.hpp"
|
|
|
|
DECLARE_bool(query_cost_planner);
|
|
|
|
class QueryExecution : public testing::Test {
|
|
protected:
|
|
std::experimental::optional<database::SingleNode> db_;
|
|
std::unique_ptr<database::GraphDbAccessor> dba_;
|
|
|
|
void SetUp() {
|
|
db_.emplace();
|
|
dba_ = db_->Access();
|
|
}
|
|
|
|
void TearDown() {
|
|
dba_ = nullptr;
|
|
db_ = std::experimental::nullopt;
|
|
}
|
|
|
|
/** Commits the current transaction and refreshes the dba_
|
|
* variable to hold a new accessor with a new transaction */
|
|
void Commit() {
|
|
dba_->Commit();
|
|
dba_ = db_->Access();
|
|
}
|
|
|
|
/** Executes the query and returns the results.
|
|
* Does NOT commit the transaction */
|
|
auto Execute(const std::string &query) {
|
|
ResultStreamFaker<query::TypedValue> stream;
|
|
auto results = query::Interpreter{*db_}(query, *dba_, {}, false);
|
|
stream.Header(results.header());
|
|
results.PullAll(stream);
|
|
stream.Summary(results.summary());
|
|
return stream.GetResults();
|
|
}
|
|
};
|
|
|
|
TEST_F(QueryExecution, MissingOptionalIntoExpand) {
|
|
// validating bug where expanding from Null (due to a preceeding optional
|
|
// match) exhausts the expansion cursor, even if it's input is still not
|
|
// exhausted
|
|
Execute(
|
|
"CREATE (a:Person {id: 1}), (b:Person "
|
|
"{id:2})-[:Has]->(:Dog)-[:Likes]->(:Food )");
|
|
Commit();
|
|
ASSERT_EQ(Execute("MATCH (n) RETURN n").size(), 4);
|
|
|
|
auto Exec = [this](bool desc, const std::string &edge_pattern) {
|
|
// this test depends on left-to-right query planning
|
|
FLAGS_query_cost_planner = false;
|
|
return Execute(std::string("MATCH (p:Person) WITH p ORDER BY p.id ") +
|
|
(desc ? "DESC " : "") +
|
|
"OPTIONAL MATCH (p)-->(d:Dog) WITH p, d "
|
|
"MATCH (d)" +
|
|
edge_pattern +
|
|
"(f:Food) "
|
|
"RETURN p, d, f")
|
|
.size();
|
|
};
|
|
|
|
std::string expand = "-->";
|
|
std::string variable = "-[*1]->";
|
|
std::string bfs = "-[*bfs..1]->";
|
|
|
|
EXPECT_EQ(Exec(false, expand), 1);
|
|
EXPECT_EQ(Exec(true, expand), 1);
|
|
EXPECT_EQ(Exec(false, variable), 1);
|
|
EXPECT_EQ(Exec(true, bfs), 1);
|
|
EXPECT_EQ(Exec(true, bfs), 1);
|
|
}
|
|
|
|
TEST_F(QueryExecution, EdgeUniquenessInOptional) {
|
|
// Validating that an edge uniqueness check can't fail when the edge is Null
|
|
// due to optonal match. Since edge-uniqueness only happens in one OPTIONAL
|
|
// MATCH, we only need to check that scenario.
|
|
Execute("CREATE (), ()-[:Type]->()");
|
|
Commit();
|
|
ASSERT_EQ(Execute("MATCH (n) RETURN n").size(), 3);
|
|
EXPECT_EQ(Execute("MATCH (n) OPTIONAL MATCH (n)-[r1]->(), (n)-[r2]->() "
|
|
"RETURN n, r1, r2")
|
|
.size(),
|
|
3);
|
|
}
|