Query - Logical - Delete op added and tested. Minor refactors.

Reviewers: buda, teon.banek, mislav.bradac

Reviewed By: teon.banek

Subscribers: pullbot

Differential Revision: https://phabricator.memgraph.io/D184
This commit is contained in:
florijan 2017-03-27 13:09:14 +02:00
parent eca90c43b3
commit 1280e77fd3
11 changed files with 538 additions and 262 deletions

View File

@ -49,6 +49,15 @@ class TypeMismatchError : public SemanticException {
name, datum, expected)) {}
};
/**
* An exception for an illegal operation that can not be detected
* before the query starts executing over data.
*/
class QueryRuntimeException : public BasicException {
public:
using BasicException::BasicException;
};
class CppCodeGeneratorException : public StacktraceException {
public:
using StacktraceException::StacktraceException;

View File

@ -1,6 +1,7 @@
#pragma once
#include <memory>
#include <query/exceptions.hpp>
#include <sstream>
#include <vector>
@ -12,6 +13,7 @@
#include "utils/visitor/visitor.hpp"
namespace query {
namespace plan {
class Cursor {
public:
@ -27,10 +29,11 @@ class NodeFilter;
class EdgeFilter;
class Filter;
class Produce;
class Delete;
using LogicalOperatorVisitor =
::utils::Visitor<CreateNode, CreateExpand, ScanAll, Expand, NodeFilter,
EdgeFilter, Filter, Produce>;
EdgeFilter, Filter, Produce, Delete>;
class LogicalOperator : public ::utils::Visitable<LogicalOperatorVisitor> {
public:
@ -55,7 +58,7 @@ class CreateNode : public LogicalOperator {
*
* @param node_atom
* @param input Optional. If nullptr, then a single node will be
* created (a single successfull Pull from this Op's Cursor).
* created (a single successful Pull from this Op's Cursor).
* If a valid input, then a node will be created for each
* successful pull from the given input.
*/
@ -687,10 +690,10 @@ class Produce : public LogicalOperator {
class ProduceCursor : public Cursor {
public:
ProduceCursor(Produce &self, GraphDbAccessor &db)
: self_(self), self_cursor_(self_.input_->MakeCursor(db)) {}
: self_(self), input_cursor_(self_.input_->MakeCursor(db)) {}
bool Pull(Frame &frame, SymbolTable &symbol_table) override {
ExpressionEvaluator evaluator(frame, symbol_table);
if (self_cursor_->Pull(frame, symbol_table)) {
if (input_cursor_->Pull(frame, symbol_table)) {
for (auto named_expr : self_.named_expressions_) {
named_expr->Accept(evaluator);
}
@ -701,11 +704,88 @@ class Produce : public LogicalOperator {
private:
Produce &self_;
std::unique_ptr<Cursor> self_cursor_;
std::unique_ptr<Cursor> input_cursor_;
};
private:
std::shared_ptr<LogicalOperator> input_;
std::vector<NamedExpression *> named_expressions_;
};
}
/**
* Operator for deleting vertices and edges.
* Has a flag for using DETACH DELETE when deleting
* vertices.
*/
class Delete : public LogicalOperator {
public:
Delete(const std::shared_ptr<LogicalOperator> &input_,
const std::vector<Expression *> &expressions, bool detach_)
: input_(input_), expressions_(expressions), detach_(detach_) {}
void Accept(LogicalOperatorVisitor &visitor) override {
visitor.Visit(*this);
input_->Accept(visitor);
visitor.PostVisit(*this);
}
private:
class DeleteCursor : public Cursor {
public:
DeleteCursor(Delete &self, GraphDbAccessor &db)
: self_(self), db_(db), input_cursor_(self_.input_->MakeCursor(db)) {}
bool Pull(Frame &frame, SymbolTable &symbol_table) override {
if (!input_cursor_->Pull(frame, symbol_table)) return false;
ExpressionEvaluator evaluator(frame, symbol_table);
for (Expression *expression : self_.expressions_) {
expression->Accept(evaluator);
TypedValue value = evaluator.PopBack();
switch (value.type()) {
case TypedValue::Type::Null:
// if we got a Null, that's OK, probably it's an OPTIONAL MATCH
return true;
case TypedValue::Type::Vertex:
if (self_.detach_)
db_.detach_remove_vertex(value.Value<VertexAccessor>());
else if (!db_.remove_vertex(value.Value<VertexAccessor>()))
throw query::QueryRuntimeException(
"Failed to remove vertex because of it's existing "
"connections. Consider using DETACH DELETE.");
break;
case TypedValue::Type::Edge:
db_.remove_edge(value.Value<EdgeAccessor>());
break;
case TypedValue::Type::Path:
// TODO consider path deletion
default:
throw TypedValueException("Can only delete edges and vertices");
}
}
return true;
}
private:
Delete &self_;
GraphDbAccessor &db_;
std::unique_ptr<Cursor> input_cursor_;
};
public:
std::unique_ptr<Cursor> MakeCursor(GraphDbAccessor &db) override {
return std::make_unique<DeleteCursor>(*this, db);
}
private:
std::shared_ptr<LogicalOperator> input_;
std::vector<Expression *> expressions_;
// if the vertex should be detached before deletion
// if not detached, and has connections, an error is raised
// ignored when deleting edges
bool detach_;
};
} // namespace plan
} // namespace query

View File

@ -6,6 +6,7 @@
#include "utils/exceptions/not_yet_implemented.hpp"
namespace query {
namespace plan {
namespace {
@ -58,7 +59,7 @@ auto ReducePattern(
}
auto GenCreateForPattern(Pattern &pattern, LogicalOperator *input_op,
const SymbolTable &symbol_table,
const query::SymbolTable &symbol_table,
std::unordered_set<int> bound_symbols) {
auto base = [&](NodeAtom *node) -> LogicalOperator * {
if (BindSymbol(bound_symbols, symbol_table.at(*node->identifier_)))
@ -89,7 +90,7 @@ auto GenCreateForPattern(Pattern &pattern, LogicalOperator *input_op,
}
auto GenCreate(Create &create, LogicalOperator *input_op,
const SymbolTable &symbol_table,
const query::SymbolTable &symbol_table,
std::unordered_set<int> bound_symbols) {
auto last_op = input_op;
for (auto pattern : create.patterns_) {
@ -100,7 +101,7 @@ auto GenCreate(Create &create, LogicalOperator *input_op,
}
auto GenMatch(Match &match, LogicalOperator *input_op,
const SymbolTable &symbol_table,
const query::SymbolTable &symbol_table,
std::unordered_set<int> &bound_symbols) {
auto base = [&](NodeAtom *node) {
if (input_op) {
@ -166,7 +167,7 @@ auto GenReturn(Return &ret, LogicalOperator *input_op) {
} // namespace
std::unique_ptr<LogicalOperator> MakeLogicalPlan(
Query &query, const SymbolTable &symbol_table) {
query::Query &query, const query::SymbolTable &symbol_table) {
// TODO: Extract functions and state into a class with methods. Possibly a
// visitor or similar to avoid all those dynamic casts.
LogicalOperator *input_op = nullptr;
@ -190,4 +191,5 @@ std::unique_ptr<LogicalOperator> MakeLogicalPlan(
return std::unique_ptr<LogicalOperator>(input_op);
}
} // namespace plan
} // namespace query

View File

@ -9,9 +9,12 @@ namespace query {
class Query;
class SymbolTable;
namespace plan {
// Returns the root of LogicalOperator tree. The tree is constructed by
// traversing the given AST Query node. SymbolTable is used to determine inputs
// and outputs of certain operators.
std::unique_ptr<LogicalOperator>
MakeLogicalPlan(Query &query, const SymbolTable &symbol_table);
std::unique_ptr<LogicalOperator> MakeLogicalPlan(
query::Query &query, const query::SymbolTable &symbol_table);
}
}

View File

@ -15,7 +15,6 @@ namespace query {
template <typename Stream>
void Interpret(const std::string &query, GraphDbAccessor &db_accessor,
Stream &stream) {
clock_t start_time = clock();
Config config;
@ -27,7 +26,7 @@ void Interpret(const std::string &query, GraphDbAccessor &db_accessor,
auto low_level_tree = parser.tree();
// AST -> high level tree
query::frontend::CypherMainVisitor visitor(ctx);
frontend::CypherMainVisitor visitor(ctx);
visitor.visit(low_level_tree);
auto high_level_tree = visitor.query();
@ -37,12 +36,12 @@ void Interpret(const std::string &query, GraphDbAccessor &db_accessor,
high_level_tree->Accept(symbol_generator);
// high level tree -> logical plan
auto logical_plan = MakeLogicalPlan(*high_level_tree, symbol_table);
auto logical_plan = plan::MakeLogicalPlan(*high_level_tree, symbol_table);
// generate frame based on symbol table max_position
Frame frame(symbol_table.max_position());
if (auto produce = dynamic_cast<Produce *>(logical_plan.get())) {
if (auto produce = dynamic_cast<plan::Produce *>(logical_plan.get())) {
// top level node in the operator tree is a produce (return)
// so stream out results
@ -64,24 +63,23 @@ void Interpret(const std::string &query, GraphDbAccessor &db_accessor,
for (auto &symbol : symbols) values.emplace_back(frame[symbol]);
stream.Result(values);
}
summary["type"] = "r";
} else if (auto create = dynamic_cast<CreateNode *>(logical_plan.get())) {
auto cursor = create->MakeCursor(db_accessor);
while (cursor->Pull(frame, symbol_table)) {
} else if (dynamic_cast<plan::CreateNode *>(logical_plan.get()) ||
dynamic_cast<plan::CreateExpand *>(logical_plan.get()) ||
dynamic_cast<Delete *>(logical_plan.get())) {
auto cursor = logical_plan.get()->MakeCursor(db_accessor);
while (cursor->Pull(frame, symbol_table))
continue;
}
} else if (auto create = dynamic_cast<CreateExpand *>(logical_plan.get())) {
auto cursor = create->MakeCursor(db_accessor);
while (cursor->Pull(frame, symbol_table)) {
continue;
}
}
clock_t end_time = clock();
double time_second = double(end_time - start_time) / CLOCKS_PER_SEC;
summary["query_time_sec"] = TypedValue(time_second);
// TODO set summary['type'] based on transaction metadata
// the type can't be determined based only on top level LogicalOp
// (for example MATCH DELETE RETURN will have Produce as it's top)
// for now always se "rw" because something must be set, but it doesn't
// have to be correct (for Bolt clients)
summary["type"] = "rw";
stream.Summary(summary);
}
}

View File

@ -1,5 +1,6 @@
#pragma once
#include <cppitertools/reversed.hpp>
#include "cppitertools/imap.hpp"
/**
@ -16,5 +17,9 @@ template <typename TAccessor, typename TIterable>
auto make_accessor_iterator(const TIterable &records, GraphDbAccessor &db_accessor) {
return iter::imap([&db_accessor](auto vlist) {
return TAccessor(*vlist, db_accessor);
}, records);
// note that here we iterate over records in REVERSED order
// this is necessary for DETACH DELETE (see GraphDbAccessor)
// which deletes items from relationship collections in a
// vertex accessor
}, iter::reversed(records));
}

View File

@ -162,52 +162,98 @@ TEST(GraphDbAccessorTest, DetachRemoveVertex) {
Dbms dbms;
auto dba = dbms.active();
// setup (v1) - [:likes] -> (v2) <- [:hates] - (v3)
// setup (v1)- []->(v2)<-[]-(v3)<-[]-(v4)
auto va1 = dba->insert_vertex();
auto va2 = dba->insert_vertex();
auto va3 = dba->insert_vertex();
dba->insert_edge(va1, va2, dba->edge_type("likes"));
dba->insert_edge(va1, va3, dba->edge_type("likes"));
auto va4 = dba->insert_vertex();
auto edge_type = dba->edge_type("type");
dba->insert_edge(va1, va2, edge_type);
dba->insert_edge(va1, va3, edge_type);
dba->insert_edge(va4, va3, edge_type);
dba->advance_command();
// ensure that plain remove does NOT work
EXPECT_EQ(CountVertices(*dba), 3);
EXPECT_EQ(CountEdges(*dba), 2);
EXPECT_EQ(CountVertices(*dba), 4);
EXPECT_EQ(CountEdges(*dba), 3);
EXPECT_FALSE(dba->remove_vertex(va1));
EXPECT_FALSE(dba->remove_vertex(va2));
EXPECT_FALSE(dba->remove_vertex(va3));
EXPECT_EQ(CountVertices(*dba), 3);
EXPECT_EQ(CountEdges(*dba), 2);
EXPECT_EQ(CountVertices(*dba), 4);
EXPECT_EQ(CountEdges(*dba), 3);
// make a new transaction because at the moment deletions
// in the same transaction are not visible
// DETACH REMOVE V3
// new situation: (v1) - [:likes] -> (v2)
dba->detach_remove_vertex(va3);
dba->advance_command();
EXPECT_EQ(CountVertices(*dba), 3);
EXPECT_EQ(CountEdges(*dba), 1);
EXPECT_TRUE(dba->remove_vertex(va4));
dba->advance_command();
EXPECT_EQ(CountVertices(*dba), 2);
EXPECT_EQ(CountEdges(*dba), 1);
for (auto va : dba->vertices()) EXPECT_FALSE(dba->remove_vertex(va));
dba->advance_command();
EXPECT_EQ(CountVertices(*dba), 2);
EXPECT_EQ(CountEdges(*dba), 1);
for (auto va : dba->vertices()) {
EXPECT_FALSE(dba->remove_vertex(va));
dba->detach_remove_vertex(va);
break;
}
dba->advance_command();
EXPECT_EQ(CountVertices(*dba), 1);
EXPECT_EQ(CountEdges(*dba), 0);
// remove the last vertex, it has no connections
// so that should work
for (auto va : dba->vertices()) EXPECT_TRUE(dba->remove_vertex(va));
dba->advance_command();
EXPECT_EQ(CountVertices(*dba), 0);
EXPECT_EQ(CountEdges(*dba), 0);
}
TEST(GraphDbAccessorTest, DetachRemoveVertexMultiple) {
// This test checks that we can detach remove the
// same vertex / edge multiple times
Dbms dbms;
auto dba = dbms.active();
// setup: make a fully connected N graph
// with cycles too!
int N = 7;
std::vector<VertexAccessor> vertices;
auto edge_type = dba->edge_type("edge");
for (int i = 0; i < N; ++i)
vertices.emplace_back(dba->insert_vertex());
for (int j = 0; j < N; ++j)
for (int k = 0; k < N; ++k)
dba->insert_edge(vertices[j], vertices[k], edge_type);
dba->advance_command();
EXPECT_EQ(CountVertices(*dba), N);
EXPECT_EQ(CountEdges(*dba), N * N);
// detach delete one edge
dba->detach_remove_vertex(vertices[0]);
dba->advance_command();
EXPECT_EQ(CountVertices(*dba), N - 1);
EXPECT_EQ(CountEdges(*dba), (N - 1) * (N - 1));
// detach delete two neighboring edges
dba->detach_remove_vertex(vertices[1]);
dba->detach_remove_vertex(vertices[2]);
dba->advance_command();
EXPECT_EQ(CountVertices(*dba), N - 3);
EXPECT_EQ(CountEdges(*dba), (N - 3) * (N - 3));
// detach delete everything, buwahahahaha
for (int l = 3; l < N ; ++l)
dba->detach_remove_vertex(vertices[l]);
dba->advance_command();
EXPECT_EQ(CountVertices(*dba), 0);
EXPECT_EQ(CountEdges(*dba), 0);
@ -257,5 +303,6 @@ TEST(GraphDbAccessorTest, Properties) {
int main(int argc, char **argv) {
::testing::InitGoogleTest(&argc, argv);
// ::testing::GTEST_FLAG(filter) = "*.DetachRemoveVertex";
return RUN_ALL_TESTS();
}

View File

@ -15,12 +15,11 @@
#include "query/context.hpp"
#include "query/frontend/interpret/interpret.hpp"
#include "query/frontend/logical/planner.hpp"
#include "query/frontend/opencypher/parser.hpp"
#include "query/frontend/semantic/symbol_generator.hpp"
#include "query_common.hpp"
using namespace query;
using namespace query::plan;
/**
* Helper function that collects all the results from the given
@ -63,10 +62,10 @@ auto CollectProduce(std::shared_ptr<Produce> produce, SymbolTable &symbol_table,
return stream;
}
void ExecuteCreate(std::shared_ptr<LogicalOperator> create, GraphDbAccessor &db,
SymbolTable symbol_table) {
void PullAll(std::shared_ptr<LogicalOperator> logical_op, GraphDbAccessor &db,
SymbolTable symbol_table) {
Frame frame(symbol_table.max_position());
auto cursor = create->MakeCursor(db);
auto cursor = logical_op->MakeCursor(db);
while (cursor->Pull(frame, symbol_table)) {
continue;
}
@ -256,7 +255,7 @@ TEST(Interpreter, CreateNodeWithAttributes) {
node->properties_[property] = LITERAL(42);
auto create = std::make_shared<CreateNode>(node, nullptr);
ExecuteCreate(create, *dba, symbol_table);
PullAll(create, *dba, symbol_table);
dba->advance_command();
// count the number of vertices
@ -356,7 +355,7 @@ TEST(Interpreter, CreateExpand) {
auto create_op = std::make_shared<CreateNode>(n, nullptr);
auto create_expand =
std::make_shared<CreateExpand>(m, r, create_op, n_sym, cycle);
ExecuteCreate(create_expand, *dba, symbol_table);
PullAll(create_expand, *dba, symbol_table);
dba->advance_command();
EXPECT_EQ(CountIterable(dba->vertices()) - before_v,
@ -412,7 +411,7 @@ TEST(Interpreter, MatchCreateNode) {
auto create_node = std::make_shared<CreateNode>(m, std::get<1>(n_scan_all));
EXPECT_EQ(CountIterable(dba->vertices()), 3);
ExecuteCreate(create_node, *dba, symbol_table);
PullAll(create_node, *dba, symbol_table);
dba->advance_command();
EXPECT_EQ(CountIterable(dba->vertices()), 6);
}
@ -457,7 +456,7 @@ TEST(Interpreter, MatchCreateExpand) {
auto create_expand = std::make_shared<CreateExpand>(
m, r, std::get<1>(n_scan_all), n_sym, cycle);
ExecuteCreate(create_expand, *dba, symbol_table);
PullAll(create_expand, *dba, symbol_table);
dba->advance_command();
EXPECT_EQ(CountIterable(dba->vertices()) - before_v,
@ -687,221 +686,117 @@ TEST(Interpreter, EdgeFilterMultipleTypes) {
EXPECT_EQ(result.GetResults().size(), 2);
}
struct NoContextExpressionEvaluator {
NoContextExpressionEvaluator() {}
Frame frame{0};
TEST(Interpreter, Delete) {
Dbms dbms;
auto dba = dbms.active();
// make a fully-connected (one-direction, no cycles) with 4 nodes
std::vector<VertexAccessor> vertices;
for (int i = 0; i < 4; ++i) vertices.push_back(dba->insert_vertex());
auto type = dba->edge_type("type");
for (int j = 0; j < 4; ++j)
for (int k = j + 1; k < 4; ++k)
dba->insert_edge(vertices[j], vertices[k], type);
dba->advance_command();
EXPECT_EQ(4, CountIterable(dba->vertices()));
EXPECT_EQ(6, CountIterable(dba->edges()));
AstTreeStorage storage;
SymbolTable symbol_table;
ExpressionEvaluator eval{frame, symbol_table};
};
TEST(ExpressionEvaluator, OrOperator) {
AstTreeStorage storage;
NoContextExpressionEvaluator eval;
auto *op = storage.Create<OrOperator>(storage.Create<Literal>(true),
storage.Create<Literal>(false));
op->Accept(eval.eval);
ASSERT_EQ(eval.eval.PopBack().Value<bool>(), true);
op = storage.Create<OrOperator>(storage.Create<Literal>(true),
storage.Create<Literal>(true));
op->Accept(eval.eval);
ASSERT_EQ(eval.eval.PopBack().Value<bool>(), true);
// attempt to delete a vertex, and fail
{
auto n = MakeScanAll(storage, symbol_table, "n");
auto n_get = storage.Create<Identifier>("n");
symbol_table[*n_get] = std::get<2>(n);
auto delete_op = std::make_shared<plan::Delete>(
std::get<1>(n), std::vector<Expression *>{n_get}, false);
EXPECT_THROW(PullAll(delete_op, *dba, symbol_table), QueryRuntimeException);
dba->advance_command();
EXPECT_EQ(4, CountIterable(dba->vertices()));
EXPECT_EQ(6, CountIterable(dba->edges()));
}
// detach delete a single vertex
{
auto n = MakeScanAll(storage, symbol_table, "n");
auto n_get = storage.Create<Identifier>("n");
symbol_table[*n_get] = std::get<2>(n);
auto delete_op = std::make_shared<plan::Delete>(
std::get<1>(n), std::vector<Expression *>{n_get}, true);
Frame frame(symbol_table.max_position());
delete_op->MakeCursor(*dba)->Pull(frame, symbol_table);
dba->advance_command();
EXPECT_EQ(3, CountIterable(dba->vertices()));
EXPECT_EQ(3, CountIterable(dba->edges()));
}
// delete all remaining edges
{
auto n = MakeScanAll(storage, symbol_table, "n");
auto r_m = MakeExpand(storage, symbol_table, std::get<1>(n), std::get<2>(n),
"r", EdgeAtom::Direction::RIGHT, false, "m", false);
auto r_get = storage.Create<Identifier>("r");
symbol_table[*r_get] = std::get<1>(r_m);
auto delete_op = std::make_shared<plan::Delete>(
std::get<4>(r_m), std::vector<Expression *>{r_get}, false);
PullAll(delete_op, *dba, symbol_table);
dba->advance_command();
EXPECT_EQ(3, CountIterable(dba->vertices()));
EXPECT_EQ(0, CountIterable(dba->edges()));
}
// delete all remaining vertices
{
auto n = MakeScanAll(storage, symbol_table, "n");
auto n_get = storage.Create<Identifier>("n");
symbol_table[*n_get] = std::get<2>(n);
auto delete_op = std::make_shared<plan::Delete>(
std::get<1>(n), std::vector<Expression *>{n_get}, false);
PullAll(delete_op, *dba, symbol_table);
dba->advance_command();
EXPECT_EQ(0, CountIterable(dba->vertices()));
EXPECT_EQ(0, CountIterable(dba->edges()));
}
}
TEST(ExpressionEvaluator, XorOperator) {
AstTreeStorage storage;
NoContextExpressionEvaluator eval;
auto *op = storage.Create<XorOperator>(storage.Create<Literal>(true),
storage.Create<Literal>(false));
op->Accept(eval.eval);
ASSERT_EQ(eval.eval.PopBack().Value<bool>(), true);
op = storage.Create<XorOperator>(storage.Create<Literal>(true),
storage.Create<Literal>(true));
op->Accept(eval.eval);
ASSERT_EQ(eval.eval.PopBack().Value<bool>(), false);
}
TEST(Interpreter, DeleteReturn) {
Dbms dbms;
auto dba = dbms.active();
TEST(ExpressionEvaluator, AndOperator) {
AstTreeStorage storage;
NoContextExpressionEvaluator eval;
auto *op = storage.Create<AndOperator>(storage.Create<Literal>(true),
storage.Create<Literal>(true));
op->Accept(eval.eval);
ASSERT_EQ(eval.eval.PopBack().Value<bool>(), true);
op = storage.Create<AndOperator>(storage.Create<Literal>(false),
storage.Create<Literal>(true));
op->Accept(eval.eval);
ASSERT_EQ(eval.eval.PopBack().Value<bool>(), false);
}
// make a fully-connected (one-direction, no cycles) with 4 nodes
auto prop = dba->property("prop");
for (int i = 0; i < 4; ++i) {
auto va = dba->insert_vertex();
va.PropsSet(prop, 42);
}
TEST(ExpressionEvaluator, AdditionOperator) {
AstTreeStorage storage;
NoContextExpressionEvaluator eval;
auto *op = storage.Create<AdditionOperator>(storage.Create<Literal>(2),
storage.Create<Literal>(3));
op->Accept(eval.eval);
ASSERT_EQ(eval.eval.PopBack().Value<int64_t>(), 5);
}
dba->advance_command();
EXPECT_EQ(4, CountIterable(dba->vertices()));
EXPECT_EQ(0, CountIterable(dba->edges()));
TEST(ExpressionEvaluator, SubtractionOperator) {
AstTreeStorage storage;
NoContextExpressionEvaluator eval;
auto *op = storage.Create<SubtractionOperator>(storage.Create<Literal>(2),
storage.Create<Literal>(3));
op->Accept(eval.eval);
ASSERT_EQ(eval.eval.PopBack().Value<int64_t>(), -1);
}
SymbolTable symbol_table;
TEST(ExpressionEvaluator, MultiplicationOperator) {
AstTreeStorage storage;
NoContextExpressionEvaluator eval;
auto *op = storage.Create<MultiplicationOperator>(storage.Create<Literal>(2),
storage.Create<Literal>(3));
op->Accept(eval.eval);
ASSERT_EQ(eval.eval.PopBack().Value<int64_t>(), 6);
}
auto n = MakeScanAll(storage, symbol_table, "n");
TEST(ExpressionEvaluator, DivisionOperator) {
AstTreeStorage storage;
NoContextExpressionEvaluator eval;
auto *op = storage.Create<DivisionOperator>(storage.Create<Literal>(50),
storage.Create<Literal>(10));
op->Accept(eval.eval);
ASSERT_EQ(eval.eval.PopBack().Value<int64_t>(), 5);
}
auto n_get = storage.Create<Identifier>("n");
symbol_table[*n_get] = std::get<2>(n);
auto delete_op = std::make_shared<plan::Delete>(
std::get<1>(n), std::vector<Expression *>{n_get}, true);
TEST(ExpressionEvaluator, ModOperator) {
AstTreeStorage storage;
NoContextExpressionEvaluator eval;
auto *op = storage.Create<ModOperator>(storage.Create<Literal>(65),
storage.Create<Literal>(10));
op->Accept(eval.eval);
ASSERT_EQ(eval.eval.PopBack().Value<int64_t>(), 5);
}
auto prop_lookup =
storage.Create<PropertyLookup>(storage.Create<Identifier>("n"), prop);
symbol_table[*prop_lookup->expression_] = std::get<2>(n);
auto n_p = storage.Create<NamedExpression>("n", prop_lookup);
symbol_table[*n_p] = symbol_table.CreateSymbol("bla");
auto produce = MakeProduce(delete_op, n_p);
TEST(ExpressionEvaluator, EqualOperator) {
AstTreeStorage storage;
NoContextExpressionEvaluator eval;
auto *op = storage.Create<EqualOperator>(storage.Create<Literal>(10),
storage.Create<Literal>(15));
op->Accept(eval.eval);
ASSERT_EQ(eval.eval.PopBack().Value<bool>(), false);
op = storage.Create<EqualOperator>(storage.Create<Literal>(15),
storage.Create<Literal>(15));
op->Accept(eval.eval);
ASSERT_EQ(eval.eval.PopBack().Value<bool>(), true);
op = storage.Create<EqualOperator>(storage.Create<Literal>(20),
storage.Create<Literal>(15));
op->Accept(eval.eval);
ASSERT_EQ(eval.eval.PopBack().Value<bool>(), false);
}
TEST(ExpressionEvaluator, NotEqualOperator) {
AstTreeStorage storage;
NoContextExpressionEvaluator eval;
auto *op = storage.Create<NotEqualOperator>(storage.Create<Literal>(10),
storage.Create<Literal>(15));
op->Accept(eval.eval);
ASSERT_EQ(eval.eval.PopBack().Value<bool>(), true);
op = storage.Create<NotEqualOperator>(storage.Create<Literal>(15),
storage.Create<Literal>(15));
op->Accept(eval.eval);
ASSERT_EQ(eval.eval.PopBack().Value<bool>(), false);
op = storage.Create<NotEqualOperator>(storage.Create<Literal>(20),
storage.Create<Literal>(15));
op->Accept(eval.eval);
ASSERT_EQ(eval.eval.PopBack().Value<bool>(), true);
}
TEST(ExpressionEvaluator, LessOperator) {
AstTreeStorage storage;
NoContextExpressionEvaluator eval;
auto *op = storage.Create<LessOperator>(storage.Create<Literal>(10),
storage.Create<Literal>(15));
op->Accept(eval.eval);
ASSERT_EQ(eval.eval.PopBack().Value<bool>(), true);
op = storage.Create<LessOperator>(storage.Create<Literal>(15),
storage.Create<Literal>(15));
op->Accept(eval.eval);
ASSERT_EQ(eval.eval.PopBack().Value<bool>(), false);
op = storage.Create<LessOperator>(storage.Create<Literal>(20),
storage.Create<Literal>(15));
op->Accept(eval.eval);
ASSERT_EQ(eval.eval.PopBack().Value<bool>(), false);
}
TEST(ExpressionEvaluator, GreaterOperator) {
AstTreeStorage storage;
NoContextExpressionEvaluator eval;
auto *op = storage.Create<GreaterOperator>(storage.Create<Literal>(10),
storage.Create<Literal>(15));
op->Accept(eval.eval);
ASSERT_EQ(eval.eval.PopBack().Value<bool>(), false);
op = storage.Create<GreaterOperator>(storage.Create<Literal>(15),
storage.Create<Literal>(15));
op->Accept(eval.eval);
ASSERT_EQ(eval.eval.PopBack().Value<bool>(), false);
op = storage.Create<GreaterOperator>(storage.Create<Literal>(20),
storage.Create<Literal>(15));
op->Accept(eval.eval);
ASSERT_EQ(eval.eval.PopBack().Value<bool>(), true);
}
TEST(ExpressionEvaluator, LessEqualOperator) {
AstTreeStorage storage;
NoContextExpressionEvaluator eval;
auto *op = storage.Create<LessEqualOperator>(storage.Create<Literal>(10),
storage.Create<Literal>(15));
op->Accept(eval.eval);
ASSERT_EQ(eval.eval.PopBack().Value<bool>(), true);
op = storage.Create<LessEqualOperator>(storage.Create<Literal>(15),
storage.Create<Literal>(15));
op->Accept(eval.eval);
ASSERT_EQ(eval.eval.PopBack().Value<bool>(), true);
op = storage.Create<LessEqualOperator>(storage.Create<Literal>(20),
storage.Create<Literal>(15));
op->Accept(eval.eval);
ASSERT_EQ(eval.eval.PopBack().Value<bool>(), false);
}
TEST(ExpressionEvaluator, GreaterEqualOperator) {
AstTreeStorage storage;
NoContextExpressionEvaluator eval;
auto *op = storage.Create<GreaterEqualOperator>(storage.Create<Literal>(10),
storage.Create<Literal>(15));
op->Accept(eval.eval);
ASSERT_EQ(eval.eval.PopBack().Value<bool>(), false);
op = storage.Create<GreaterEqualOperator>(storage.Create<Literal>(15),
storage.Create<Literal>(15));
op->Accept(eval.eval);
ASSERT_EQ(eval.eval.PopBack().Value<bool>(), true);
op = storage.Create<GreaterEqualOperator>(storage.Create<Literal>(20),
storage.Create<Literal>(15));
op->Accept(eval.eval);
ASSERT_EQ(eval.eval.PopBack().Value<bool>(), true);
}
TEST(ExpressionEvaluator, NotOperator) {
AstTreeStorage storage;
NoContextExpressionEvaluator eval;
auto *op = storage.Create<NotOperator>(storage.Create<Literal>(false));
op->Accept(eval.eval);
ASSERT_EQ(eval.eval.PopBack().Value<bool>(), true);
}
TEST(ExpressionEvaluator, UnaryPlusOperator) {
AstTreeStorage storage;
NoContextExpressionEvaluator eval;
auto *op = storage.Create<UnaryPlusOperator>(storage.Create<Literal>(5));
op->Accept(eval.eval);
ASSERT_EQ(eval.eval.PopBack().Value<int64_t>(), 5);
}
TEST(ExpressionEvaluator, UnaryMinusOperator) {
AstTreeStorage storage;
NoContextExpressionEvaluator eval;
auto *op = storage.Create<UnaryMinusOperator>(storage.Create<Literal>(5));
op->Accept(eval.eval);
ASSERT_EQ(eval.eval.PopBack().Value<int64_t>(), -5);
auto result = CollectProduce(produce, symbol_table, *dba);
EXPECT_EQ(4, result.GetResults().size());
dba->advance_command();
EXPECT_EQ(0, CountIterable(dba->vertices()));
}
TEST(Interpreter, Filter) {

View File

@ -111,12 +111,12 @@ auto GetReturn(AstTreeStorage &storage,
#define EDGE(...) query::test_common::GetEdge(storage, __VA_ARGS__)
#define PATTERN(...) query::test_common::GetPattern(storage, {__VA_ARGS__})
#define MATCH(...) \
query::test_common::GetWithPatterns<Match>(storage, {__VA_ARGS__})
query::test_common::GetWithPatterns<query::Match>(storage, {__VA_ARGS__})
#define CREATE(...) \
query::test_common::GetWithPatterns<Create>(storage, {__VA_ARGS__})
#define IDENT(name) storage.Create<Identifier>((name))
#define LITERAL(val) storage.Create<Literal>((val))
query::test_common::GetWithPatterns<query::Create>(storage, {__VA_ARGS__})
#define IDENT(name) storage.Create<query::Identifier>((name))
#define LITERAL(val) storage.Create<query::Literal>((val))
#define PROPERTY_LOOKUP(...) query::test_common::GetPropertyLookup(storage, __VA_ARGS__)
#define NEXPR(name, expr) storage.Create<NamedExpression>((name), (expr))
#define NEXPR(name, expr) storage.Create<query::NamedExpression>((name), (expr))
#define RETURN(...) query::test_common::GetReturn(storage, {__VA_ARGS__})
#define QUERY(...) query::test_common::GetQuery(storage, {__VA_ARGS__})

View File

@ -0,0 +1,234 @@
//
// Copyright 2017 Memgraph
// Created by Mislav Bradac on 27.03.17.
//
#include <iterator>
#include <memory>
#include <vector>
#include "gmock/gmock.h"
#include "gtest/gtest.h"
#include "query/frontend/ast/ast.hpp"
#include "query/frontend/interpret/interpret.hpp"
#include "query/frontend/opencypher/parser.hpp"
using namespace query;
struct NoContextExpressionEvaluator {
NoContextExpressionEvaluator() {}
Frame frame{0};
SymbolTable symbol_table;
ExpressionEvaluator eval{frame, symbol_table};
};
TEST(ExpressionEvaluator, OrOperator) {
AstTreeStorage storage;
NoContextExpressionEvaluator eval;
auto *op = storage.Create<OrOperator>(storage.Create<Literal>(true),
storage.Create<Literal>(false));
op->Accept(eval.eval);
ASSERT_EQ(eval.eval.PopBack().Value<bool>(), true);
op = storage.Create<OrOperator>(storage.Create<Literal>(true),
storage.Create<Literal>(true));
op->Accept(eval.eval);
ASSERT_EQ(eval.eval.PopBack().Value<bool>(), true);
}
TEST(ExpressionEvaluator, XorOperator) {
AstTreeStorage storage;
NoContextExpressionEvaluator eval;
auto *op = storage.Create<XorOperator>(storage.Create<Literal>(true),
storage.Create<Literal>(false));
op->Accept(eval.eval);
ASSERT_EQ(eval.eval.PopBack().Value<bool>(), true);
op = storage.Create<XorOperator>(storage.Create<Literal>(true),
storage.Create<Literal>(true));
op->Accept(eval.eval);
ASSERT_EQ(eval.eval.PopBack().Value<bool>(), false);
}
TEST(ExpressionEvaluator, AndOperator) {
AstTreeStorage storage;
NoContextExpressionEvaluator eval;
auto *op = storage.Create<AndOperator>(storage.Create<Literal>(true),
storage.Create<Literal>(true));
op->Accept(eval.eval);
ASSERT_EQ(eval.eval.PopBack().Value<bool>(), true);
op = storage.Create<AndOperator>(storage.Create<Literal>(false),
storage.Create<Literal>(true));
op->Accept(eval.eval);
ASSERT_EQ(eval.eval.PopBack().Value<bool>(), false);
}
TEST(ExpressionEvaluator, AdditionOperator) {
AstTreeStorage storage;
NoContextExpressionEvaluator eval;
auto *op = storage.Create<AdditionOperator>(storage.Create<Literal>(2),
storage.Create<Literal>(3));
op->Accept(eval.eval);
ASSERT_EQ(eval.eval.PopBack().Value<int64_t>(), 5);
}
TEST(ExpressionEvaluator, SubtractionOperator) {
AstTreeStorage storage;
NoContextExpressionEvaluator eval;
auto *op = storage.Create<SubtractionOperator>(storage.Create<Literal>(2),
storage.Create<Literal>(3));
op->Accept(eval.eval);
ASSERT_EQ(eval.eval.PopBack().Value<int64_t>(), -1);
}
TEST(ExpressionEvaluator, MultiplicationOperator) {
AstTreeStorage storage;
NoContextExpressionEvaluator eval;
auto *op = storage.Create<MultiplicationOperator>(storage.Create<Literal>(2),
storage.Create<Literal>(3));
op->Accept(eval.eval);
ASSERT_EQ(eval.eval.PopBack().Value<int64_t>(), 6);
}
TEST(ExpressionEvaluator, DivisionOperator) {
AstTreeStorage storage;
NoContextExpressionEvaluator eval;
auto *op = storage.Create<DivisionOperator>(storage.Create<Literal>(50),
storage.Create<Literal>(10));
op->Accept(eval.eval);
ASSERT_EQ(eval.eval.PopBack().Value<int64_t>(), 5);
}
TEST(ExpressionEvaluator, ModOperator) {
AstTreeStorage storage;
NoContextExpressionEvaluator eval;
auto *op = storage.Create<ModOperator>(storage.Create<Literal>(65),
storage.Create<Literal>(10));
op->Accept(eval.eval);
ASSERT_EQ(eval.eval.PopBack().Value<int64_t>(), 5);
}
TEST(ExpressionEvaluator, EqualOperator) {
AstTreeStorage storage;
NoContextExpressionEvaluator eval;
auto *op = storage.Create<EqualOperator>(storage.Create<Literal>(10),
storage.Create<Literal>(15));
op->Accept(eval.eval);
ASSERT_EQ(eval.eval.PopBack().Value<bool>(), false);
op = storage.Create<EqualOperator>(storage.Create<Literal>(15),
storage.Create<Literal>(15));
op->Accept(eval.eval);
ASSERT_EQ(eval.eval.PopBack().Value<bool>(), true);
op = storage.Create<EqualOperator>(storage.Create<Literal>(20),
storage.Create<Literal>(15));
op->Accept(eval.eval);
ASSERT_EQ(eval.eval.PopBack().Value<bool>(), false);
}
TEST(ExpressionEvaluator, NotEqualOperator) {
AstTreeStorage storage;
NoContextExpressionEvaluator eval;
auto *op = storage.Create<NotEqualOperator>(storage.Create<Literal>(10),
storage.Create<Literal>(15));
op->Accept(eval.eval);
ASSERT_EQ(eval.eval.PopBack().Value<bool>(), true);
op = storage.Create<NotEqualOperator>(storage.Create<Literal>(15),
storage.Create<Literal>(15));
op->Accept(eval.eval);
ASSERT_EQ(eval.eval.PopBack().Value<bool>(), false);
op = storage.Create<NotEqualOperator>(storage.Create<Literal>(20),
storage.Create<Literal>(15));
op->Accept(eval.eval);
ASSERT_EQ(eval.eval.PopBack().Value<bool>(), true);
}
TEST(ExpressionEvaluator, LessOperator) {
AstTreeStorage storage;
NoContextExpressionEvaluator eval;
auto *op = storage.Create<LessOperator>(storage.Create<Literal>(10),
storage.Create<Literal>(15));
op->Accept(eval.eval);
ASSERT_EQ(eval.eval.PopBack().Value<bool>(), true);
op = storage.Create<LessOperator>(storage.Create<Literal>(15),
storage.Create<Literal>(15));
op->Accept(eval.eval);
ASSERT_EQ(eval.eval.PopBack().Value<bool>(), false);
op = storage.Create<LessOperator>(storage.Create<Literal>(20),
storage.Create<Literal>(15));
op->Accept(eval.eval);
ASSERT_EQ(eval.eval.PopBack().Value<bool>(), false);
}
TEST(ExpressionEvaluator, GreaterOperator) {
AstTreeStorage storage;
NoContextExpressionEvaluator eval;
auto *op = storage.Create<GreaterOperator>(storage.Create<Literal>(10),
storage.Create<Literal>(15));
op->Accept(eval.eval);
ASSERT_EQ(eval.eval.PopBack().Value<bool>(), false);
op = storage.Create<GreaterOperator>(storage.Create<Literal>(15),
storage.Create<Literal>(15));
op->Accept(eval.eval);
ASSERT_EQ(eval.eval.PopBack().Value<bool>(), false);
op = storage.Create<GreaterOperator>(storage.Create<Literal>(20),
storage.Create<Literal>(15));
op->Accept(eval.eval);
ASSERT_EQ(eval.eval.PopBack().Value<bool>(), true);
}
TEST(ExpressionEvaluator, LessEqualOperator) {
AstTreeStorage storage;
NoContextExpressionEvaluator eval;
auto *op = storage.Create<LessEqualOperator>(storage.Create<Literal>(10),
storage.Create<Literal>(15));
op->Accept(eval.eval);
ASSERT_EQ(eval.eval.PopBack().Value<bool>(), true);
op = storage.Create<LessEqualOperator>(storage.Create<Literal>(15),
storage.Create<Literal>(15));
op->Accept(eval.eval);
ASSERT_EQ(eval.eval.PopBack().Value<bool>(), true);
op = storage.Create<LessEqualOperator>(storage.Create<Literal>(20),
storage.Create<Literal>(15));
op->Accept(eval.eval);
ASSERT_EQ(eval.eval.PopBack().Value<bool>(), false);
}
TEST(ExpressionEvaluator, GreaterEqualOperator) {
AstTreeStorage storage;
NoContextExpressionEvaluator eval;
auto *op = storage.Create<GreaterEqualOperator>(storage.Create<Literal>(10),
storage.Create<Literal>(15));
op->Accept(eval.eval);
ASSERT_EQ(eval.eval.PopBack().Value<bool>(), false);
op = storage.Create<GreaterEqualOperator>(storage.Create<Literal>(15),
storage.Create<Literal>(15));
op->Accept(eval.eval);
ASSERT_EQ(eval.eval.PopBack().Value<bool>(), true);
op = storage.Create<GreaterEqualOperator>(storage.Create<Literal>(20),
storage.Create<Literal>(15));
op->Accept(eval.eval);
ASSERT_EQ(eval.eval.PopBack().Value<bool>(), true);
}
TEST(ExpressionEvaluator, NotOperator) {
AstTreeStorage storage;
NoContextExpressionEvaluator eval;
auto *op = storage.Create<NotOperator>(storage.Create<Literal>(false));
op->Accept(eval.eval);
ASSERT_EQ(eval.eval.PopBack().Value<bool>(), true);
}
TEST(ExpressionEvaluator, UnaryPlusOperator) {
AstTreeStorage storage;
NoContextExpressionEvaluator eval;
auto *op = storage.Create<UnaryPlusOperator>(storage.Create<Literal>(5));
op->Accept(eval.eval);
ASSERT_EQ(eval.eval.PopBack().Value<int64_t>(), 5);
}
TEST(ExpressionEvaluator, UnaryMinusOperator) {
AstTreeStorage storage;
NoContextExpressionEvaluator eval;
auto *op = storage.Create<UnaryMinusOperator>(storage.Create<Literal>(5));
op->Accept(eval.eval);
ASSERT_EQ(eval.eval.PopBack().Value<int64_t>(), -5);
}

View File

@ -11,8 +11,11 @@
#include "query_common.hpp"
using namespace query;
using Direction = EdgeAtom::Direction;
using namespace query::plan;
using query::AstTreeStorage;
using query::SymbolTable;
using query::SymbolGenerator;
using Direction = query::EdgeAtom::Direction;
namespace {