2017-08-04 15:44:51 +08:00
|
|
|
// 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.
|
|
|
|
|
2017-10-30 17:43:25 +08:00
|
|
|
#include <experimental/optional>
|
|
|
|
|
2017-08-04 15:44:51 +08:00
|
|
|
#include "gmock/gmock.h"
|
|
|
|
#include "gtest/gtest.h"
|
|
|
|
|
|
|
|
#include "communication/result_stream_faker.hpp"
|
2018-01-12 22:17:04 +08:00
|
|
|
#include "database/graph_db.hpp"
|
|
|
|
#include "database/graph_db_accessor.hpp"
|
2017-08-04 15:44:51 +08:00
|
|
|
#include "query/interpreter.hpp"
|
|
|
|
|
2017-12-22 20:39:31 +08:00
|
|
|
DECLARE_bool(query_cost_planner);
|
|
|
|
|
2017-08-04 15:44:51 +08:00
|
|
|
class QueryExecution : public testing::Test {
|
|
|
|
protected:
|
2018-01-12 22:17:04 +08:00
|
|
|
std::experimental::optional<database::SingleNode> db_;
|
2018-07-26 15:08:21 +08:00
|
|
|
std::unique_ptr<database::GraphDbAccessor> dba_;
|
2017-10-30 17:43:25 +08:00
|
|
|
|
|
|
|
void SetUp() {
|
|
|
|
db_.emplace();
|
2018-07-26 15:08:21 +08:00
|
|
|
dba_ = db_->Access();
|
2017-10-30 17:43:25 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
void TearDown() {
|
2018-07-26 15:08:21 +08:00
|
|
|
dba_ = nullptr;
|
2017-10-30 17:43:25 +08:00
|
|
|
db_ = std::experimental::nullopt;
|
|
|
|
}
|
2017-08-04 15:44:51 +08:00
|
|
|
|
2017-10-30 17:43:25 +08:00
|
|
|
/** Commits the current transaction and refreshes the dba_
|
2017-08-04 15:44:51 +08:00
|
|
|
* variable to hold a new accessor with a new transaction */
|
|
|
|
void Commit() {
|
2017-10-30 17:43:25 +08:00
|
|
|
dba_->Commit();
|
2018-07-26 15:08:21 +08:00
|
|
|
dba_ = db_->Access();
|
2017-08-04 15:44:51 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
/** Executes the query and returns the results.
|
|
|
|
* Does NOT commit the transaction */
|
|
|
|
auto Execute(const std::string &query) {
|
2018-07-18 16:40:06 +08:00
|
|
|
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();
|
2017-08-04 15:44:51 +08:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
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);
|
|
|
|
|
2017-09-18 20:30:07 +08:00
|
|
|
auto Exec = [this](bool desc, const std::string &edge_pattern) {
|
2017-08-04 15:44:51 +08:00
|
|
|
// 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 "
|
2017-10-10 00:09:28 +08:00
|
|
|
"MATCH (d)" +
|
|
|
|
edge_pattern +
|
2017-09-18 20:30:07 +08:00
|
|
|
"(f:Food) "
|
2017-08-04 15:44:51 +08:00
|
|
|
"RETURN p, d, f")
|
|
|
|
.size();
|
|
|
|
};
|
|
|
|
|
2017-09-18 20:30:07 +08:00
|
|
|
std::string expand = "-->";
|
|
|
|
std::string variable = "-[*1]->";
|
2017-09-27 20:57:41 +08:00
|
|
|
std::string bfs = "-[*bfs..1]->";
|
2017-08-04 15:44:51 +08:00
|
|
|
|
2017-09-18 20:30:07 +08:00
|
|
|
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);
|
2017-08-04 15:44:51 +08:00
|
|
|
}
|