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:
parent
eca90c43b3
commit
1280e77fd3
@ -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;
|
||||
|
@ -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
|
||||
|
||||
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -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));
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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) {
|
||||
|
@ -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__})
|
||||
|
234
tests/unit/query_expression_evaluator.cpp
Normal file
234
tests/unit/query_expression_evaluator.cpp
Normal 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);
|
||||
}
|
@ -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 {
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user