memgraph/tests/unit/query_plan_edge_cases.cpp
florijan 03f1547a8d Invalidate distributed plan caches
Summary:
 - Remove caches on workers as a result of plan expiration or race
   during insertion.
 - Extract caching functionality into a class.
 - Minor refactor of Interpreter::operator()
 - New RPC and test for it.
 - Rename ConsumePlanRes to DispatchPlanRes for consistency, remove
   return value as it's always true and never used.
 - Interpreter is now constructed with a `GraphDb` reference. At the
   moment only for reaching the `distributed::PlanDispatcher`, but in
   the future we should probably use that primarily for planning.

I added a function to `PlanConsumer` that is only used for testing.
I prefer not doing this, but I felt this needed testing. I can remove
it now if you like.

Reviewers: teon.banek, msantl

Reviewed By: teon.banek

Subscribers: pullbot

Differential Revision: https://phabricator.memgraph.io/D1292
2018-03-13 14:59:51 +01:00

94 lines
2.9 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::experimental::optional<database::GraphDbAccessor> dba_;
void SetUp() {
db_.emplace();
dba_.emplace(*db_);
}
void TearDown() {
dba_ = std::experimental::nullopt;
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_.emplace(*db_);
}
/** Executes the query and returns the results.
* Does NOT commit the transaction */
auto Execute(const std::string &query) {
ResultStreamFaker results;
query::Interpreter{*db_}(query, *dba_, {}, false).PullAll(results);
return results.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);
}