Map type now supported

Summary:
- MapLiteral added
- PropertyLookup on maps added

This is the basic implementation, missing are:
- unit tests
- feature and TCK tests
- documentation
- changelog

That stuff is coming. Please review the implementation (Mislav).

Reviewers: mislav.bradac, buda, teon.banek

Reviewed By: mislav.bradac

Subscribers: pullbot

Differential Revision: https://phabricator.memgraph.io/D640
This commit is contained in:
florijan 2017-08-08 13:43:42 +02:00
parent bfe9ec0ab5
commit b8957c999d
16 changed files with 373 additions and 210 deletions

View File

@ -5,6 +5,7 @@
### Major Features and Improvements
* Support for variable length path `MATCH`.
* Support for map literal.
* Support for `all` function in openCypher.
* User specified transaction execution timeout.
* Support for query parameters (except for parameters in place of property maps).

View File

@ -34,3 +34,11 @@ types. Following is a table of supported data types.
`Integer` | An integer number.
`Float` | A floating-point number, i.e. a real number.
`List` | A list containing any number of property values of any supported type. It can be used to store multiple values under a single property name.
Note that even though map literals are supported in openCypher queries, they can't be stored in the graph. For example, the following query is legal:
MATCH (n:Person) RETURN {name: n.name, age: n.age}
However, the next query is not:
MATCH (n:Person {name: "John"}) SET n.address = {city: "London", street: "Fleet St."}

View File

@ -580,6 +580,38 @@ class ListLiteral : public BaseLiteral {
: BaseLiteral(uid), elements_(elements) {}
};
class MapLiteral : public BaseLiteral {
friend class AstTreeStorage;
public:
DEFVISITABLE(TreeVisitor<TypedValue>);
bool Accept(HierarchicalTreeVisitor &visitor) override {
if (visitor.PreVisit(*this)) {
for (auto pair : elements_)
if (!pair.second->Accept(visitor)) break;
}
return visitor.PostVisit(*this);
}
MapLiteral *Clone(AstTreeStorage &storage) const override {
auto *map = storage.Create<MapLiteral>();
for (auto pair : elements_)
map->elements_.emplace(pair.first, pair.second->Clone(storage));
return map;
}
// maps (property_name, property) to expressions
std::map<std::pair<std::string, GraphDbTypes::Property>, Expression *>
elements_;
protected:
MapLiteral(int uid) : BaseLiteral(uid) {}
MapLiteral(int uid,
const std::map<std::pair<std::string, GraphDbTypes::Property>,
Expression *> &elements)
: BaseLiteral(uid), elements_(elements) {}
};
class Identifier : public Expression {
friend class AstTreeStorage;
@ -614,23 +646,27 @@ class PropertyLookup : public Expression {
PropertyLookup *Clone(AstTreeStorage &storage) const override {
return storage.Create<PropertyLookup>(expression_->Clone(storage),
property_);
property_name_, property_);
}
Expression *expression_ = nullptr;
GraphDbTypes::Property property_ = nullptr;
// TODO potential problem: property lookups are allowed on both map literals
// and records, but map literals have strings as keys and records have
// GraphDbTypes::Property
//
// possible solution: store both string and GraphDbTypes::Property here and
// choose
// between the two depending on Expression result
std::string property_name_;
GraphDbTypes::Property property_;
protected:
PropertyLookup(int uid, Expression *expression,
const std::string &property_name,
GraphDbTypes::Property property)
: Expression(uid), expression_(expression), property_(property) {}
: Expression(uid),
expression_(expression),
property_name_(property_name),
property_(property) {}
PropertyLookup(int uid, Expression *expression,
std::pair<std::string, GraphDbTypes::Property> property)
: Expression(uid),
expression_(expression),
property_name_(property.first),
property_(property.second) {}
};
class LabelsTest : public Expression {
@ -838,8 +874,10 @@ class NodeAtom : public PatternAtom {
}
std::vector<GraphDbTypes::Label> labels_;
// maps (property_name, property) to an expression
// TODO: change to unordered_map
std::map<GraphDbTypes::Property, Expression *> properties_;
std::map<std::pair<std::string, GraphDbTypes::Property>, Expression *>
properties_;
protected:
using PatternAtom::PatternAtom;
@ -887,8 +925,10 @@ class EdgeAtom : public PatternAtom {
Direction direction_ = Direction::BOTH;
std::vector<GraphDbTypes::EdgeType> edge_types_;
// maps (property_name, property) to an expression
// TODO: change to unordered_map
std::map<GraphDbTypes::Property, Expression *> properties_;
std::map<std::pair<std::string, GraphDbTypes::Property>, Expression *>
properties_;
bool has_range_ = false;
Expression *lower_bound_ = nullptr;
Expression *upper_bound_ = nullptr;

View File

@ -24,6 +24,7 @@ class EdgeAtom;
class BreadthFirstAtom;
class PrimitiveLiteral;
class ListLiteral;
class MapLiteral;
class OrOperator;
class XorOperator;
class AndOperator;
@ -64,10 +65,10 @@ using TreeCompositeVisitor = ::utils::CompositeVisitor<
EqualOperator, LessOperator, GreaterOperator, LessEqualOperator,
GreaterEqualOperator, InListOperator, ListIndexingOperator,
ListSlicingOperator, UnaryPlusOperator, UnaryMinusOperator, IsNullOperator,
ListLiteral, PropertyLookup, LabelsTest, EdgeTypeTest, Aggregation,
Function, All, Create, Match, Return, With, Pattern, NodeAtom, EdgeAtom,
BreadthFirstAtom, Delete, Where, SetProperty, SetProperties, SetLabels,
RemoveProperty, RemoveLabels, Merge, Unwind>;
ListLiteral, MapLiteral, PropertyLookup, LabelsTest, EdgeTypeTest,
Aggregation, Function, All, Create, Match, Return, With, Pattern, NodeAtom,
EdgeAtom, BreadthFirstAtom, Delete, Where, SetProperty, SetProperties,
SetLabels, RemoveProperty, RemoveLabels, Merge, Unwind>;
using TreeLeafVisitor =
::utils::LeafVisitor<Identifier, PrimitiveLiteral, CreateIndex>;
@ -89,10 +90,10 @@ using TreeVisitor = ::utils::Visitor<
EqualOperator, LessOperator, GreaterOperator, LessEqualOperator,
GreaterEqualOperator, InListOperator, ListIndexingOperator,
ListSlicingOperator, UnaryPlusOperator, UnaryMinusOperator, IsNullOperator,
ListLiteral, PropertyLookup, LabelsTest, EdgeTypeTest, Aggregation,
Function, All, Create, Match, Return, With, Pattern, NodeAtom, EdgeAtom,
BreadthFirstAtom, Delete, Where, SetProperty, SetProperties, SetLabels,
RemoveProperty, RemoveLabels, Merge, Unwind, Identifier, PrimitiveLiteral,
CreateIndex>;
ListLiteral, MapLiteral, PropertyLookup, LabelsTest, EdgeTypeTest,
Aggregation, Function, All, Create, Match, Return, With, Pattern, NodeAtom,
EdgeAtom, BreadthFirstAtom, Delete, Where, SetProperty, SetProperties,
SetLabels, RemoveProperty, RemoveLabels, Merge, Unwind, Identifier,
PrimitiveLiteral, CreateIndex>;
} // namespace query

View File

@ -5,8 +5,8 @@
#include <codecvt>
#include <cstring>
#include <limits>
#include <map>
#include <string>
#include <unordered_map>
#include <utility>
#include <vector>
@ -177,9 +177,10 @@ antlrcpp::Any CypherMainVisitor::visitCreate(CypherParser::CreateContext *ctx) {
*/
antlrcpp::Any CypherMainVisitor::visitCreateIndex(
CypherParser::CreateIndexContext *ctx) {
std::pair<std::string, GraphDbTypes::Property> key =
ctx->propertyKeyName()->accept(this);
return storage_.Create<CreateIndex>(
ctx_.db_accessor_.label(ctx->labelName()->accept(this)),
ctx->propertyKeyName()->accept(this));
ctx_.db_accessor_.label(ctx->labelName()->accept(this)), key.second);
}
antlrcpp::Any CypherMainVisitor::visitCypherReturn(
@ -274,7 +275,8 @@ antlrcpp::Any CypherMainVisitor::visitNodePattern(
node->properties_ =
ctx->properties()
->accept(this)
.as<std::map<GraphDbTypes::Property, Expression *>>();
.as<std::map<std::pair<std::string, GraphDbTypes::Property>,
Expression *>>();
}
return node;
}
@ -303,9 +305,10 @@ antlrcpp::Any CypherMainVisitor::visitProperties(
antlrcpp::Any CypherMainVisitor::visitMapLiteral(
CypherParser::MapLiteralContext *ctx) {
std::map<GraphDbTypes::Property, Expression *> map;
std::map<std::pair<std::string, GraphDbTypes::Property>, Expression *> map;
for (int i = 0; i < static_cast<int>(ctx->propertyKeyName().size()); ++i) {
GraphDbTypes::Property key = ctx->propertyKeyName()[i]->accept(this);
std::pair<std::string, GraphDbTypes::Property> key =
ctx->propertyKeyName()[i]->accept(this);
Expression *value = ctx->expression()[i]->accept(this);
if (!map.insert({key, value}).second) {
throw SemanticException("Same key can't appear twice in map literal");
@ -324,7 +327,8 @@ antlrcpp::Any CypherMainVisitor::visitListLiteral(
antlrcpp::Any CypherMainVisitor::visitPropertyKeyName(
CypherParser::PropertyKeyNameContext *ctx) {
return ctx_.db_accessor_.property(visitChildren(ctx));
const std::string key_name = visitChildren(ctx);
return std::make_pair(key_name, ctx_.db_accessor_.property(key_name));
}
antlrcpp::Any CypherMainVisitor::visitSymbolicName(
@ -450,7 +454,8 @@ antlrcpp::Any CypherMainVisitor::visitRelationshipPattern(
ctx->relationshipDetail()
->properties()
->accept(this)
.as<std::map<GraphDbTypes::Property, Expression *>>();
.as<std::map<std::pair<std::string, GraphDbTypes::Property>,
Expression *>>();
}
if (ctx->relationshipDetail()->rangeLiteral()) {
edge->has_range_ = true;
@ -751,8 +756,9 @@ antlrcpp::Any CypherMainVisitor::visitExpression2b(
CypherParser::Expression2bContext *ctx) {
Expression *expression = ctx->atom()->accept(this);
for (auto *lookup : ctx->propertyLookup()) {
std::pair<std::string, GraphDbTypes::Property> key = lookup->accept(this);
auto property_lookup =
storage_.Create<PropertyLookup>(expression, lookup->accept(this));
storage_.Create<PropertyLookup>(expression, key.first, key.second);
expression = property_lookup;
}
return expression;
@ -826,8 +832,11 @@ antlrcpp::Any CypherMainVisitor::visitLiteral(
return static_cast<BaseLiteral *>(storage_.Create<ListLiteral>(
ctx->listLiteral()->accept(this).as<std::vector<Expression *>>()));
} else {
// TODO: Implement map literal.
throw utils::NotYetImplemented("map literal");
return static_cast<BaseLiteral *>(storage_.Create<MapLiteral>(
ctx->mapLiteral()
->accept(this)
.as<std::map<std::pair<std::string, GraphDbTypes::Property>,
Expression *>>()));
}
return visitChildren(ctx);
}
@ -1014,8 +1023,9 @@ antlrcpp::Any CypherMainVisitor::visitPropertyExpression(
CypherParser::PropertyExpressionContext *ctx) {
Expression *expression = ctx->atom()->accept(this);
for (auto *lookup : ctx->propertyLookup()) {
std::pair<std::string, GraphDbTypes::Property> key = lookup->accept(this);
auto property_lookup =
storage_.Create<PropertyLookup>(expression, lookup->accept(this));
storage_.Create<PropertyLookup>(expression, key.first, key.second);
expression = property_lookup;
}
// It is guaranteed by grammar that there is at least one propertyLookup.

View File

@ -210,7 +210,7 @@ class CypherMainVisitor : public antlropencypher::CypherBaseVisitor {
antlrcpp::Any visitProperties(CypherParser::PropertiesContext *ctx) override;
/**
* @return unordered_map<GraphDbTypes::Property, Expression*>
* @return map<std::string, Expression*>
*/
antlrcpp::Any visitMapLiteral(CypherParser::MapLiteralContext *ctx) override;

View File

@ -249,9 +249,12 @@ class ExpressionEvaluator : public TreeVisitor<TypedValue> {
case TypedValue::Type::Edge:
return expression_result.Value<EdgeAccessor>().PropsAt(
property_lookup.property_);
case TypedValue::Type::Map:
// TODO implement me
throw utils::NotYetImplemented("property lookup on map");
case TypedValue::Type::Map: {
auto &map = expression_result.Value<std::map<std::string, TypedValue>>();
auto found = map.find(property_lookup.property_name_);
if (found == map.end()) return TypedValue::Null;
return found->second;
}
default:
throw QueryRuntimeException(
"Expected Node, Edge or Map for property lookup");
@ -311,6 +314,13 @@ class ExpressionEvaluator : public TreeVisitor<TypedValue> {
return result;
}
TypedValue Visit(MapLiteral &literal) override {
std::map<std::string, TypedValue> result;
for (const auto &pair : literal.elements_)
result.emplace(pair.first.first, pair.second->Accept(*this));
return result;
}
TypedValue Visit(Aggregation &aggregation) override {
auto value = frame_[symbol_table_.at(aggregation)];
// Aggregation is probably always simple type, but let's switch accessor

View File

@ -94,7 +94,7 @@ void CreateNode::CreateNodeCursor::Create(Frame &frame,
// setting properties on new nodes.
ExpressionEvaluator evaluator(frame, symbol_table, db_, GraphView::NEW);
for (auto &kv : self_.node_atom_->properties_)
PropsSetChecked(new_node, kv.first, kv.second->Accept(evaluator));
PropsSetChecked(new_node, kv.first.second, kv.second->Accept(evaluator));
frame[symbol_table.at(*self_.node_atom_->identifier_)] = new_node;
}
@ -171,7 +171,7 @@ VertexAccessor &CreateExpand::CreateExpandCursor::OtherVertex(
auto node = db_.insert_vertex();
for (auto label : self_.node_atom_->labels_) node.add_label(label);
for (auto kv : self_.node_atom_->properties_)
PropsSetChecked(node, kv.first, kv.second->Accept(evaluator));
PropsSetChecked(node, kv.first.second, kv.second->Accept(evaluator));
auto symbol = symbol_table.at(*self_.node_atom_->identifier_);
frame[symbol] = node;
return frame[symbol].Value<VertexAccessor>();
@ -184,7 +184,7 @@ void CreateExpand::CreateExpandCursor::CreateEdge(
EdgeAccessor edge =
db_.insert_edge(from, to, self_.edge_atom_->edge_types_[0]);
for (auto kv : self_.edge_atom_->properties_)
PropsSetChecked(edge, kv.first, kv.second->Accept(evaluator));
PropsSetChecked(edge, kv.first.second, kv.second->Accept(evaluator));
frame[symbol_table.at(*self_.edge_atom_->identifier_)] = edge;
}

View File

@ -286,6 +286,21 @@ class ReturnBodyContext : public HierarchicalTreeVisitor {
return true;
}
bool PostVisit(MapLiteral &map_literal) override {
debug_assert(
map_literal.elements_.size() <= has_aggregation_.size(),
"Expected has_aggregation_ flags as much as there are map elements.");
bool has_aggr = false;
auto it = has_aggregation_.end();
std::advance(it, -map_literal.elements_.size());
while (it != has_aggregation_.end()) {
has_aggr = has_aggr || *it;
it = has_aggregation_.erase(it);
}
has_aggregation_.emplace_back(has_aggr);
return true;
}
bool PostVisit(All &all) override {
// Remove the symbol which is bound by all, because we are only interested
// in free (unbound) symbols.
@ -1034,7 +1049,7 @@ void Filters::CollectPatternFilters(Pattern &pattern, SymbolTable &symbol_table,
symbol_table.CreateSymbol(identifier->name_, false);
} else {
// Store a PropertyFilter on the value of the property.
property_filters_[symbol][prop_pair.first].emplace_back(
property_filters_[symbol][prop_pair.first.second].emplace_back(
PropertyFilter{collector.symbols_, prop_pair.second});
}
// Create an equality expression and store it in all_filters_.

View File

@ -25,25 +25,33 @@ using testing::Pair;
using testing::ElementsAre;
using testing::UnorderedElementsAre;
// Base class for all test types
class Base {
public:
Base(const std::string &query) : query_string_(query) {}
Dbms dbms_;
std::unique_ptr<GraphDbAccessor> db_accessor_ = dbms_.active();
Context context_{Config{}, *db_accessor_};
std::string query_string_;
auto Prop(const std::string &prop_name) {
return db_accessor_->property(prop_name);
}
auto PropPair(const std::string &prop_name) {
return std::make_pair(prop_name, Prop(prop_name));
}
};
// This generator uses ast constructed by parsing the query.
class AstGenerator {
class AstGenerator : public Base {
public:
AstGenerator(const std::string &query)
: dbms_(),
db_accessor_(dbms_.active()),
context_(Config{}, *db_accessor_),
query_string_(query),
parser_(query),
visitor_(context_),
query_([&]() {
: Base(query), parser_(query), visitor_(context_), query_([&]() {
visitor_.visit(parser_.tree());
return visitor_.query();
}()) {}
Dbms dbms_;
std::unique_ptr<GraphDbAccessor> db_accessor_;
Context context_;
std::string query_string_;
::frontend::opencypher::Parser parser_;
CypherMainVisitor visitor_;
Query *query_;
@ -63,37 +71,26 @@ class OriginalAfterCloningAstGenerator : public AstGenerator {
// This generator clones parsed ast and uses that one.
// Original ast is cleared after cloning to ensure that cloned ast doesn't reuse
// any data from original ast.
class ClonedAstGenerator {
class ClonedAstGenerator : public Base {
public:
ClonedAstGenerator(const std::string &query)
: dbms_(),
db_accessor_(dbms_.active()),
context_(Config{}, *db_accessor_),
query_string_(query),
query_([&]() {
: Base(query), query_([&]() {
::frontend::opencypher::Parser parser(query);
CypherMainVisitor visitor(context_);
visitor.visit(parser.tree());
return visitor.query()->Clone(storage);
}()) {}
Dbms dbms_;
std::unique_ptr<GraphDbAccessor> db_accessor_;
Context context_;
std::string query_string_;
AstTreeStorage storage;
Query *query_;
};
// This generator strips ast, clones it and then plugs stripped out literals in
// the same way it is done in ast cacheing in interpreter.
class CachedAstGenerator {
class CachedAstGenerator : public Base {
public:
CachedAstGenerator(const std::string &query)
: dbms_(),
db_accessor_(dbms_.active()),
context_(Config{}, *db_accessor_),
query_string_(query),
: Base(query),
storage_([&]() {
StrippedQuery stripped(query_string_);
::frontend::opencypher::Parser parser(stripped.query());
@ -104,10 +101,6 @@ class CachedAstGenerator {
}()),
query_(storage_.query()) {}
Dbms dbms_;
std::unique_ptr<GraphDbAccessor> db_accessor_;
Context context_;
std::string query_string_;
AstTreeStorage storage_;
Query *query_;
};
@ -742,6 +735,31 @@ TYPED_TEST(CypherMainVisitorTest, ListLiteral) {
EXPECT_EQ(elem_2->value_.type(), TypedValue::Type::String);
}
TYPED_TEST(CypherMainVisitorTest, MapLiteral) {
TypeParam ast_generator("RETURN {a: 1, b: 'bla', c: [1, {a: 42}]}");
auto *query = ast_generator.query_;
auto *return_clause = dynamic_cast<Return *>(query->clauses_[0]);
auto *map_literal = dynamic_cast<MapLiteral *>(
return_clause->body_.named_expressions[0]->expression_);
ASSERT_TRUE(map_literal);
ASSERT_EQ(3, map_literal->elements_.size());
auto *elem_0 = dynamic_cast<PrimitiveLiteral *>(
map_literal->elements_[ast_generator.PropPair("a")]);
ASSERT_TRUE(elem_0);
EXPECT_EQ(elem_0->value_.type(), TypedValue::Type::Int);
auto *elem_1 = dynamic_cast<PrimitiveLiteral *>(
map_literal->elements_[ast_generator.PropPair("b")]);
ASSERT_TRUE(elem_1);
EXPECT_EQ(elem_1->value_.type(), TypedValue::Type::String);
auto *elem_2 = dynamic_cast<ListLiteral *>(
map_literal->elements_[ast_generator.PropPair("c")]);
ASSERT_TRUE(elem_2);
EXPECT_EQ(2, elem_2->elements_.size());
auto *elem_2_1 = dynamic_cast<MapLiteral *>(elem_2->elements_[1]);
ASSERT_TRUE(elem_2_1);
EXPECT_EQ(1, elem_2_1->elements_.size());
}
TYPED_TEST(CypherMainVisitorTest, NodePattern) {
TypeParam ast_generator(
"MATCH (:label1:label2:label3 {a : 5, b : 10}) RETURN 1");
@ -764,7 +782,7 @@ TYPED_TEST(CypherMainVisitorTest, NodePattern) {
ast_generator.db_accessor_->label("label1"),
ast_generator.db_accessor_->label("label2"),
ast_generator.db_accessor_->label("label3")));
std::unordered_map<GraphDbTypes::Property, int64_t> properties;
std::map<std::pair<std::string, GraphDbTypes::Property>, int64_t> properties;
for (auto x : node->properties_) {
auto *literal = dynamic_cast<PrimitiveLiteral *>(x.second);
ASSERT_TRUE(literal);
@ -772,9 +790,8 @@ TYPED_TEST(CypherMainVisitorTest, NodePattern) {
properties[x.first] = literal->value_.Value<int64_t>();
}
EXPECT_THAT(properties,
UnorderedElementsAre(
Pair(ast_generator.db_accessor_->property("a"), 5),
Pair(ast_generator.db_accessor_->property("b"), 10)));
UnorderedElementsAre(Pair(ast_generator.PropPair("a"), 5),
Pair(ast_generator.PropPair("b"), 10)));
}
TYPED_TEST(CypherMainVisitorTest, PropertyMapSameKeyAppearsTwice) {
@ -858,7 +875,7 @@ TYPED_TEST(CypherMainVisitorTest, RelationshipPatternDetails) {
edge->edge_types_,
UnorderedElementsAre(ast_generator.db_accessor_->edge_type("type1"),
ast_generator.db_accessor_->edge_type("type2")));
std::unordered_map<GraphDbTypes::Property, int64_t> properties;
std::map<std::pair<std::string, GraphDbTypes::Property>, int64_t> properties;
for (auto x : edge->properties_) {
auto *literal = dynamic_cast<PrimitiveLiteral *>(x.second);
ASSERT_TRUE(literal);
@ -866,9 +883,8 @@ TYPED_TEST(CypherMainVisitorTest, RelationshipPatternDetails) {
properties[x.first] = literal->value_.Value<int64_t>();
}
EXPECT_THAT(properties,
UnorderedElementsAre(
Pair(ast_generator.db_accessor_->property("a"), 5),
Pair(ast_generator.db_accessor_->property("b"), 10)));
UnorderedElementsAre(Pair(ast_generator.PropPair("a"), 5),
Pair(ast_generator.PropPair("b"), 10)));
}
TYPED_TEST(CypherMainVisitorTest, RelationshipPatternVariable) {
@ -995,8 +1011,8 @@ TYPED_TEST(CypherMainVisitorTest, RelationshipPatternUnboundedWithProperty) {
EXPECT_TRUE(edge->has_range_);
EXPECT_EQ(edge->lower_bound_, nullptr);
EXPECT_EQ(edge->upper_bound_, nullptr);
auto prop = ast_generator.db_accessor_->property("prop");
auto prop_literal = dynamic_cast<PrimitiveLiteral *>(edge->properties_[prop]);
auto prop_literal = dynamic_cast<PrimitiveLiteral *>(
edge->properties_[ast_generator.PropPair("prop")]);
EXPECT_EQ(prop_literal->value_.Value<int64_t>(), 42);
}
@ -1011,8 +1027,8 @@ TYPED_TEST(CypherMainVisitorTest,
EXPECT_TRUE(edge->has_range_);
EXPECT_EQ(edge->lower_bound_, nullptr);
EXPECT_EQ(edge->upper_bound_, nullptr);
auto prop = ast_generator.db_accessor_->property("prop");
auto prop_literal = dynamic_cast<PrimitiveLiteral *>(edge->properties_[prop]);
auto prop_literal = dynamic_cast<PrimitiveLiteral *>(
edge->properties_[ast_generator.PropPair("prop")]);
EXPECT_EQ(prop_literal->value_.Value<int64_t>(), 42);
ASSERT_EQ(edge->edge_types_.size(), 1U);
auto edge_type = ast_generator.db_accessor_->edge_type("edge_type");
@ -1031,8 +1047,8 @@ TYPED_TEST(CypherMainVisitorTest, RelationshipPatternUpperBoundedWithProperty) {
auto upper_bound = dynamic_cast<PrimitiveLiteral *>(edge->upper_bound_);
ASSERT_TRUE(upper_bound);
EXPECT_EQ(upper_bound->value_.Value<int64_t>(), 2);
auto prop = ast_generator.db_accessor_->property("prop");
auto prop_literal = dynamic_cast<PrimitiveLiteral *>(edge->properties_[prop]);
auto prop_literal = dynamic_cast<PrimitiveLiteral *>(
edge->properties_[ast_generator.PropPair("prop")]);
EXPECT_EQ(prop_literal->value_.Value<int64_t>(), 42);
}
@ -1421,5 +1437,4 @@ TYPED_TEST(CypherMainVisitorTest, MatchBfsReturn) {
auto *eq = dynamic_cast<EqualOperator *>(bfs->filter_expression_);
ASSERT_TRUE(eq);
}
}

View File

@ -5,6 +5,9 @@
/// example:
///
/// AstTreeStorage storage; // Macros rely on storage being in scope.
/// // PROPERTY_LOOKUP and PROPERTY_PAIR macros also rely on a graph DB
/// // accessor
/// std::unique_ptr<GraphDbAccessor> dba;
///
/// QUERY(MATCH(PATTERN(NODE("n"), EDGE("e"), NODE("m"))),
/// WHERE(LESS(PROPERTY_LOOKUP("e", edge_prop), LITERAL(3))),
@ -23,6 +26,7 @@
#include <utility>
#include <vector>
#include "database/dbms.hpp"
#include "database/graph_db_datatypes.hpp"
#include "query/frontend/ast/ast.hpp"
#include "query/interpret/awesome_memgraph_functions.hpp"
@ -93,14 +97,32 @@ auto GetOrderBy(T... exprs) {
///
/// Name is used to create the Identifier which is used for property lookup.
///
auto GetPropertyLookup(AstTreeStorage &storage, const std::string &name,
auto GetPropertyLookup(AstTreeStorage &storage,
std::unique_ptr<GraphDbAccessor> &dba,
const std::string &name,
GraphDbTypes::Property property) {
return storage.Create<PropertyLookup>(storage.Create<Identifier>(name),
dba->property_name(property), property);
}
auto GetPropertyLookup(AstTreeStorage &storage,
std::unique_ptr<GraphDbAccessor> &dba, Expression *expr,
GraphDbTypes::Property property) {
return storage.Create<PropertyLookup>(expr, dba->property_name(property),
property);
}
auto GetPropertyLookup(AstTreeStorage &storage, Expression *expr,
GraphDbTypes::Property property) {
return storage.Create<PropertyLookup>(expr, property);
auto GetPropertyLookup(
AstTreeStorage &storage, std::unique_ptr<GraphDbAccessor> &dba,
const std::string &name,
const std::pair<std::string, GraphDbTypes::Property> &prop_pair) {
return storage.Create<PropertyLookup>(storage.Create<Identifier>(name),
prop_pair.first, prop_pair.second);
}
auto GetPropertyLookup(
AstTreeStorage &storage, std::unique_ptr<GraphDbAccessor> &dba,
Expression *expr,
const std::pair<std::string, GraphDbTypes::Property> &prop_pair) {
return storage.Create<PropertyLookup>(expr, prop_pair.first,
prop_pair.second);
}
///
@ -422,8 +444,14 @@ auto GetMerge(AstTreeStorage &storage, Pattern *pattern, OnMatch on_match,
#define LIST(...) \
storage.Create<query::ListLiteral>( \
std::vector<query::Expression *>{__VA_ARGS__})
#define MAP(...) \
storage.Create<query::MapLiteral>( \
std::map<std::pair<std::string, GraphDbTypes::Property>, \
query::Expression *>{__VA_ARGS__})
#define PROPERTY_PAIR(property_name) \
std::make_pair(property_name, dba->property(property_name))
#define PROPERTY_LOOKUP(...) \
query::test_common::GetPropertyLookup(storage, __VA_ARGS__)
query::test_common::GetPropertyLookup(storage, dba, __VA_ARGS__)
#define NEXPR(name, expr) storage.Create<query::NamedExpression>((name), (expr))
// AS is alternative to NEXPR which does not initialize NamedExpression with
// Expression. It should be used with RETURN or WITH. For example:
@ -473,7 +501,8 @@ auto GetMerge(AstTreeStorage &storage, Pattern *pattern, OnMatch on_match,
#define COUNT(expr) \
storage.Create<query::Aggregation>((expr), query::Aggregation::Op::COUNT)
#define EQ(expr1, expr2) storage.Create<query::EqualOperator>((expr1), (expr2))
#define NEQ(expr1, expr2) storage.Create<query::NotEqualOperator>((expr1), (expr2))
#define NEQ(expr1, expr2) \
storage.Create<query::NotEqualOperator>((expr1), (expr2))
#define AND(expr1, expr2) storage.Create<query::AndOperator>((expr1), (expr2))
#define OR(expr1, expr2) storage.Create<query::OrOperator>((expr1), (expr2))
// Function call

View File

@ -499,34 +499,54 @@ TEST(ExpressionEvaluator, IsNullOperator) {
ASSERT_EQ(val2.Value<bool>(), true);
}
TEST(ExpressionEvaluator, PropertyLookup) {
class ExpressionEvaluatorPropertyLookup : public testing::Test {
protected:
AstTreeStorage storage;
NoContextExpressionEvaluator eval;
Dbms dbms;
auto dba = dbms.active();
std::unique_ptr<GraphDbAccessor> dba = dbms.active();
std::pair<std::string, GraphDbTypes::Property> prop_age =
PROPERTY_PAIR("age");
std::pair<std::string, GraphDbTypes::Property> prop_height =
PROPERTY_PAIR("height");
Expression *identifier = storage.Create<Identifier>("element");
Symbol symbol = eval.symbol_table.CreateSymbol("element", true);
void SetUp() { eval.symbol_table[*identifier] = symbol; }
auto Value(std::pair<std::string, GraphDbTypes::Property> property) {
auto *op = storage.Create<PropertyLookup>(identifier, property);
return op->Accept(eval.eval);
}
};
TEST_F(ExpressionEvaluatorPropertyLookup, Vertex) {
auto v1 = dba->insert_vertex();
v1.PropsSet(dba->property("age"), 10);
auto *identifier = storage.Create<Identifier>("n");
auto node_symbol = eval.symbol_table.CreateSymbol("n", true);
eval.symbol_table[*identifier] = node_symbol;
eval.frame[node_symbol] = v1;
{
auto *op = storage.Create<PropertyLookup>(identifier, dba->property("age"));
auto value = op->Accept(eval.eval);
EXPECT_EQ(value.Value<int64_t>(), 10);
}
{
auto *op =
storage.Create<PropertyLookup>(identifier, dba->property("height"));
auto value = op->Accept(eval.eval);
EXPECT_TRUE(value.IsNull());
}
{
eval.frame[node_symbol] = TypedValue::Null;
auto *op = storage.Create<PropertyLookup>(identifier, dba->property("age"));
auto value = op->Accept(eval.eval);
EXPECT_TRUE(value.IsNull());
}
v1.PropsSet(prop_age.second, 10);
eval.frame[symbol] = v1;
EXPECT_EQ(Value(prop_age).Value<int64_t>(), 10);
EXPECT_TRUE(Value(prop_height).IsNull());
}
TEST_F(ExpressionEvaluatorPropertyLookup, Edge) {
auto v1 = dba->insert_vertex();
auto v2 = dba->insert_vertex();
auto e12 = dba->insert_edge(v1, v2, dba->edge_type("edge_type"));
e12.PropsSet(prop_age.second, 10);
eval.frame[symbol] = e12;
EXPECT_EQ(Value(prop_age).Value<int64_t>(), 10);
EXPECT_TRUE(Value(prop_height).IsNull());
}
TEST_F(ExpressionEvaluatorPropertyLookup, Null) {
eval.frame[symbol] = TypedValue::Null;
EXPECT_TRUE(Value(prop_age).IsNull());
}
TEST_F(ExpressionEvaluatorPropertyLookup, MapLiteral) {
eval.frame[symbol] = std::map<std::string, TypedValue>{{prop_age.first, 10}};
EXPECT_EQ(Value(prop_age).Value<int64_t>(), 10);
EXPECT_TRUE(Value(prop_height).IsNull());
}
TEST(ExpressionEvaluator, LabelsTest) {
@ -1041,5 +1061,4 @@ TEST(ExpressionEvaluator, FunctionAllWhereWrongType) {
eval.symbol_table[*all->identifier_] = x_sym;
EXPECT_THROW(all->Accept(eval.eval), QueryRuntimeException);
}
}

View File

@ -27,7 +27,7 @@ TEST(QueryPlan, CreateNodeWithAttributes) {
auto dba = dbms.active();
GraphDbTypes::Label label = dba->label("Person");
GraphDbTypes::Property property = dba->label("age");
auto property = PROPERTY_PAIR("prop");
AstTreeStorage storage;
SymbolTable symbol_table;
@ -48,7 +48,7 @@ TEST(QueryPlan, CreateNodeWithAttributes) {
EXPECT_EQ(vertex.labels().size(), 1);
EXPECT_EQ(*vertex.labels().begin(), label);
EXPECT_EQ(vertex.Properties().size(), 1);
auto prop_eq = vertex.PropsAt(property) == TypedValue(42);
auto prop_eq = vertex.PropsAt(property.second) == TypedValue(42);
ASSERT_EQ(prop_eq.type(), TypedValue::Type::Bool);
EXPECT_TRUE(prop_eq.Value<bool>());
}
@ -61,7 +61,7 @@ TEST(QueryPlan, CreateReturn) {
auto dba = dbms.active();
GraphDbTypes::Label label = dba->label("Person");
GraphDbTypes::Property property = dba->label("age");
auto property = PROPERTY_PAIR("property");
AstTreeStorage storage;
SymbolTable symbol_table;
@ -103,7 +103,7 @@ TEST(QueryPlan, CreateExpand) {
GraphDbTypes::Label label_node_1 = dba->label("Node1");
GraphDbTypes::Label label_node_2 = dba->label("Node2");
GraphDbTypes::Property property = dba->label("prop");
auto property = PROPERTY_PAIR("property");
GraphDbTypes::EdgeType edge_type = dba->label("edge_type");
SymbolTable symbol_table;
@ -143,7 +143,8 @@ TEST(QueryPlan, CreateExpand) {
EXPECT_EQ(CountIterable(dba->vertices(false)) - before_v,
expected_nodes_created);
EXPECT_EQ(CountIterable(dba->edges(false)) - before_e, expected_edges_created);
EXPECT_EQ(CountIterable(dba->edges(false)) - before_e,
expected_edges_created);
};
test_create_path(false, 2, 1);
@ -154,10 +155,10 @@ TEST(QueryPlan, CreateExpand) {
GraphDbTypes::Label label = vertex.labels()[0];
if (label == label_node_1) {
// node created by first op
EXPECT_EQ(vertex.PropsAt(property).Value<int64_t>(), 1);
EXPECT_EQ(vertex.PropsAt(property.second).Value<int64_t>(), 1);
} else if (label == label_node_2) {
// node create by expansion
EXPECT_EQ(vertex.PropsAt(property).Value<int64_t>(), 2);
EXPECT_EQ(vertex.PropsAt(property.second).Value<int64_t>(), 2);
} else {
// should not happen
FAIL();
@ -165,7 +166,7 @@ TEST(QueryPlan, CreateExpand) {
for (EdgeAccessor edge : dba->edges(false)) {
EXPECT_EQ(edge.edge_type(), edge_type);
EXPECT_EQ(edge.PropsAt(property).Value<int64_t>(), 3);
EXPECT_EQ(edge.PropsAt(property.second).Value<int64_t>(), 3);
}
}
}
@ -241,7 +242,8 @@ TEST(QueryPlan, MatchCreateExpand) {
EXPECT_EQ(CountIterable(dba->vertices(false)) - before_v,
expected_nodes_created);
EXPECT_EQ(CountIterable(dba->edges(false)) - before_e, expected_edges_created);
EXPECT_EQ(CountIterable(dba->edges(false)) - before_e,
expected_edges_created);
};
test_create_path(false, 3, 3);
@ -378,10 +380,10 @@ TEST(QueryPlan, DeleteReturn) {
auto dba = dbms.active();
// make a fully-connected (one-direction, no cycles) with 4 nodes
auto prop = dba->property("prop");
auto prop = PROPERTY_PAIR("property");
for (int i = 0; i < 4; ++i) {
auto va = dba->insert_vertex();
va.PropsSet(prop, 42);
va.PropsSet(prop.second, 42);
}
dba->advance_command();
@ -688,8 +690,8 @@ TEST(QueryPlan, NodeFilterSet) {
auto dba = dbms.active();
// Create a graph such that (v1 {prop: 42}) is connected to v2 and v3.
auto v1 = dba->insert_vertex();
auto prop = dba->property("prop");
v1.PropsSet(prop, 42);
auto prop = PROPERTY_PAIR("property");
v1.PropsSet(prop.second, 42);
auto v2 = dba->insert_vertex();
auto v3 = dba->insert_vertex();
auto edge_type = dba->edge_type("Edge");
@ -718,7 +720,7 @@ TEST(QueryPlan, NodeFilterSet) {
EXPECT_EQ(2, PullAll(set, *dba, symbol_table));
dba->advance_command();
v1.Reconstruct();
auto prop_eq = v1.PropsAt(prop) == TypedValue(42 + 2);
auto prop_eq = v1.PropsAt(prop.second) == TypedValue(42 + 2);
ASSERT_EQ(prop_eq.type(), TypedValue::Type::Bool);
EXPECT_TRUE(prop_eq.Value<bool>());
}
@ -728,8 +730,8 @@ TEST(QueryPlan, FilterRemove) {
auto dba = dbms.active();
// Create a graph such that (v1 {prop: 42}) is connected to v2 and v3.
auto v1 = dba->insert_vertex();
auto prop = dba->property("prop");
v1.PropsSet(prop, 42);
auto prop = PROPERTY_PAIR("property");
v1.PropsSet(prop.second, 42);
auto v2 = dba->insert_vertex();
auto v3 = dba->insert_vertex();
auto edge_type = dba->edge_type("Edge");
@ -756,7 +758,7 @@ TEST(QueryPlan, FilterRemove) {
EXPECT_EQ(2, PullAll(rem, *dba, symbol_table));
dba->advance_command();
v1.Reconstruct();
EXPECT_EQ(v1.PropsAt(prop).type(), PropertyValue::Type::Null);
EXPECT_EQ(v1.PropsAt(prop.second).type(), PropertyValue::Type::Null);
}
TEST(QueryPlan, SetRemove) {
@ -802,7 +804,7 @@ TEST(QueryPlan, Merge) {
AstTreeStorage storage;
SymbolTable symbol_table;
auto prop = dba->property("prop");
auto prop = PROPERTY_PAIR("property");
auto n = MakeScanAll(storage, symbol_table, "n");
// merge_match branch
@ -825,12 +827,12 @@ TEST(QueryPlan, Merge) {
v2.Reconstruct();
v3.Reconstruct();
ASSERT_EQ(v1.PropsAt(prop).type(), PropertyValue::Type::Int);
ASSERT_EQ(v1.PropsAt(prop).Value<int64_t>(), 1);
ASSERT_EQ(v2.PropsAt(prop).type(), PropertyValue::Type::Int);
ASSERT_EQ(v2.PropsAt(prop).Value<int64_t>(), 1);
ASSERT_EQ(v3.PropsAt(prop).type(), PropertyValue::Type::Int);
ASSERT_EQ(v3.PropsAt(prop).Value<int64_t>(), 2);
ASSERT_EQ(v1.PropsAt(prop.second).type(), PropertyValue::Type::Int);
ASSERT_EQ(v1.PropsAt(prop.second).Value<int64_t>(), 1);
ASSERT_EQ(v2.PropsAt(prop.second).type(), PropertyValue::Type::Int);
ASSERT_EQ(v2.PropsAt(prop.second).Value<int64_t>(), 1);
ASSERT_EQ(v3.PropsAt(prop.second).type(), PropertyValue::Type::Int);
ASSERT_EQ(v3.PropsAt(prop.second).Value<int64_t>(), 2);
}
TEST(QueryPlan, MergeNoInput) {
@ -859,7 +861,7 @@ TEST(QueryPlan, SetPropertyOnNull) {
auto dba = dbms.active();
AstTreeStorage storage;
SymbolTable symbol_table;
auto prop = dba->property("prop");
auto prop = PROPERTY_PAIR("property");
auto null = LITERAL(TypedValue::Null);
auto literal = LITERAL(42);
auto n_prop = storage.Create<PropertyLookup>(null, prop);
@ -909,7 +911,7 @@ TEST(QueryPlan, RemovePropertyOnNull) {
auto dba = dbms.active();
AstTreeStorage storage;
SymbolTable symbol_table;
auto prop = dba->property("prop");
auto prop = PROPERTY_PAIR("property");
auto null = LITERAL(TypedValue::Null);
auto n_prop = storage.Create<PropertyLookup>(null, prop);
auto once = std::make_shared<Once>();

View File

@ -118,7 +118,7 @@ TEST(QueryPlan, NodeFilterLabelsAndProperties) {
// add a few nodes to the database
GraphDbTypes::Label label = dba->label("Label");
GraphDbTypes::Property property = dba->property("Property");
auto property = PROPERTY_PAIR("Property");
auto v1 = dba->insert_vertex();
auto v2 = dba->insert_vertex();
auto v3 = dba->insert_vertex();
@ -132,10 +132,10 @@ TEST(QueryPlan, NodeFilterLabelsAndProperties) {
v2.add_label(label);
v3.add_label(label);
// v1 and v4 will have the right properties
v1.PropsSet(property, 42);
v2.PropsSet(property, 1);
v4.PropsSet(property, 42);
v5.PropsSet(property, 1);
v1.PropsSet(property.second, 42);
v2.PropsSet(property.second, 1);
v4.PropsSet(property.second, 42);
v5.PropsSet(property.second, 1);
dba->advance_command();
AstTreeStorage storage;
@ -593,9 +593,12 @@ struct hash<std::pair<int, int>> {
class QueryPlanExpandBreadthFirst : public testing::Test {
protected:
Dbms dbms_;
std::unique_ptr<GraphDbAccessor> dba_ = dbms_.active();
GraphDbTypes::Property prop = dba_->property("property");
GraphDbTypes::EdgeType edge_type = dba_->edge_type("edge_type");
// style-guide non-conformant name due to PROPERTY_PAIR and PROPERTY_LOOKUP
// macro requirements
std::unique_ptr<GraphDbAccessor> dba = dbms_.active();
std::pair<std::string, GraphDbTypes::Property> prop =
PROPERTY_PAIR("property");
GraphDbTypes::EdgeType edge_type = dba->edge_type("edge_type");
// make 4 vertices because we'll need to compare against them exactly
// v[0] has `prop` with the value 0
@ -614,13 +617,13 @@ class QueryPlanExpandBreadthFirst : public testing::Test {
void SetUp() {
for (int i = 0; i < 4; i++) {
v.push_back(dba_->insert_vertex());
v.back().PropsSet(prop, i);
v.push_back(dba->insert_vertex());
v.back().PropsSet(prop.second, i);
}
auto add_edge = [&](int from, int to) {
EdgeAccessor edge = dba_->insert_edge(v[from], v[to], edge_type);
edge.PropsSet(prop, from * 10 + to);
EdgeAccessor edge = dba->insert_edge(v[from], v[to], edge_type);
edge.PropsSet(prop.second, from * 10 + to);
e.emplace(std::make_pair(from, to), edge);
};
@ -631,7 +634,7 @@ class QueryPlanExpandBreadthFirst : public testing::Test {
add_edge(3, 2);
add_edge(2, 2);
dba_->advance_command();
dba->advance_command();
for (auto &vertex : v) vertex.Reconstruct();
for (auto &edge : e) edge.second.Reconstruct();
}
@ -664,7 +667,7 @@ class QueryPlanExpandBreadthFirst : public testing::Test {
graph_view);
Frame frame(symbol_table.max_position());
auto cursor = last_op->MakeCursor(*dba_);
auto cursor = last_op->MakeCursor(*dba);
std::vector<std::pair<std::vector<EdgeAccessor>, VertexAccessor>> results;
while (cursor->Pull(frame, symbol_table)) {
results.emplace_back(std::vector<EdgeAccessor>(),
@ -678,7 +681,7 @@ class QueryPlanExpandBreadthFirst : public testing::Test {
template <typename TAccessor>
auto GetProp(const TAccessor &accessor) {
return accessor.PropsAt(prop).template Value<int64_t>();
return accessor.PropsAt(prop.second).template Value<int64_t>();
}
Expression *PropNe(Symbol symbol, int value) {
@ -757,14 +760,14 @@ TEST_F(QueryPlanExpandBreadthFirst, GraphState) {
};
EXPECT_EQ(ExpandSize(GraphView::OLD), 3);
EXPECT_EQ(ExpandSize(GraphView::NEW), 3);
auto new_vertex = dba_->insert_vertex();
new_vertex.PropsSet(prop, 4);
dba_->insert_edge(v[3], new_vertex, edge_type);
EXPECT_EQ(CountIterable(dba_->vertices(false)), 4);
EXPECT_EQ(CountIterable(dba_->vertices(true)), 5);
auto new_vertex = dba->insert_vertex();
new_vertex.PropsSet(prop.second, 4);
dba->insert_edge(v[3], new_vertex, edge_type);
EXPECT_EQ(CountIterable(dba->vertices(false)), 4);
EXPECT_EQ(CountIterable(dba->vertices(true)), 5);
EXPECT_EQ(ExpandSize(GraphView::OLD), 3);
EXPECT_EQ(ExpandSize(GraphView::NEW), 4);
dba_->advance_command();
dba->advance_command();
EXPECT_EQ(ExpandSize(GraphView::OLD), 4);
EXPECT_EQ(ExpandSize(GraphView::NEW), 4);
}
@ -1129,17 +1132,17 @@ TEST(QueryPlan, EdgeFilter) {
edge_types.push_back(dba->edge_type("et" + std::to_string(j)));
std::vector<VertexAccessor> vertices;
for (int i = 0; i < 7; ++i) vertices.push_back(dba->insert_vertex());
GraphDbTypes::Property prop = dba->property("prop");
auto prop = PROPERTY_PAIR("property");
std::vector<EdgeAccessor> edges;
for (int i = 0; i < 6; ++i) {
edges.push_back(
dba->insert_edge(vertices[0], vertices[i + 1], edge_types[i % 2]));
switch (i % 3) {
case 0:
edges.back().PropsSet(prop, 42);
edges.back().PropsSet(prop.second, 42);
break;
case 1:
edges.back().PropsSet(prop, 100);
edges.back().PropsSet(prop.second, 100);
break;
default:
break;
@ -1179,7 +1182,7 @@ TEST(QueryPlan, EdgeFilter) {
EXPECT_EQ(1, test_filter());
// test that edge filtering always filters on old state
for (auto &edge : edges) edge.PropsSet(prop, 42);
for (auto &edge : edges) edge.PropsSet(prop.second, 42);
EXPECT_EQ(1, test_filter());
dba->advance_command();
EXPECT_EQ(3, test_filter());
@ -1230,9 +1233,9 @@ TEST(QueryPlan, Filter) {
auto dba = dbms.active();
// add a 6 nodes with property 'prop', 2 have true as value
GraphDbTypes::Property property = dba->property("Property");
auto property = PROPERTY_PAIR("property");
for (int i = 0; i < 6; ++i)
dba->insert_vertex().PropsSet(property, i % 3 == 0);
dba->insert_vertex().PropsSet(property.second, i % 3 == 0);
dba->insert_vertex(); // prop not set, gives NULL
dba->advance_command();

View File

@ -218,10 +218,11 @@ class ExpectOptional : public OpChecker<Optional> {
class ExpectScanAllByLabelPropertyValue
: public OpChecker<ScanAllByLabelPropertyValue> {
public:
ExpectScanAllByLabelPropertyValue(GraphDbTypes::Label label,
GraphDbTypes::Property property,
query::Expression *expression)
: label_(label), property_(property), expression_(expression) {}
ExpectScanAllByLabelPropertyValue(
GraphDbTypes::Label label,
const std::pair<std::string, GraphDbTypes::Property> &prop_pair,
query::Expression *expression)
: label_(label), property_(prop_pair.second), expression_(expression) {}
void ExpectOp(ScanAllByLabelPropertyValue &scan_all,
const SymbolTable &) override {
@ -801,13 +802,13 @@ TEST(TestLogicalPlanner, MatchCrossReferenceVariable) {
// Test MATCH (n {prop: m.prop}), (m {prop: n.prop}) RETURN n
Dbms dbms;
auto dba = dbms.active();
auto prop = dba->property("prop");
auto prop = PROPERTY_PAIR("prop");
AstTreeStorage storage;
auto node_n = NODE("n");
auto m_prop = PROPERTY_LOOKUP("m", prop);
auto m_prop = PROPERTY_LOOKUP("m", prop.second);
node_n->properties_[prop] = m_prop;
auto node_m = NODE("m");
auto n_prop = PROPERTY_LOOKUP("n", prop);
auto n_prop = PROPERTY_LOOKUP("n", prop.second);
node_m->properties_[prop] = n_prop;
QUERY(MATCH(PATTERN(node_n), PATTERN(node_m)), RETURN("n"));
// We expect both ScanAll to come before filters (2 are joined into one),
@ -915,10 +916,9 @@ TEST(TestLogicalPlanner, UnwindMergeNodeProperty) {
// Test UNWIND [1] AS i MERGE (n {prop: i})
Dbms dbms;
auto dba = dbms.active();
auto prop = dba->property("prop");
AstTreeStorage storage;
auto node_n = NODE("n");
node_n->properties_[prop] = IDENT("i");
node_n->properties_[PROPERTY_PAIR("prop")] = IDENT("i");
QUERY(UNWIND(LIST(LITERAL(1)), AS("i")), MERGE(PATTERN(node_n)));
std::list<BaseOpChecker *> on_match{new ExpectScanAll(), new ExpectFilter()};
std::list<BaseOpChecker *> on_create{new ExpectCreateNode()};
@ -965,6 +965,18 @@ TEST(TestLogicalPlanner, ListLiteralAggregationReturn) {
CheckPlan(storage, aggr, ExpectProduce());
}
TEST(TestLogicalPlanner, MapLiteralAggregationReturn) {
// Test RETURN {sum: SUM(2)} AS result, 42 AS group_by
AstTreeStorage storage; Dbms dbms;
auto dba = dbms.active();
auto sum = SUM(LITERAL(2));
auto group_by_literal = LITERAL(42);
QUERY(RETURN(MAP({PROPERTY_PAIR("sum"), sum}), AS("result"), group_by_literal,
AS("group_by")));
auto aggr = ExpectAggregate({sum}, {group_by_literal});
CheckPlan(storage, aggr, ExpectProduce());
}
TEST(TestLogicalPlanner, EmptyListIndexAggregation) {
// Test RETURN [][SUM(2)] AS result, 42 AS group_by
AstTreeStorage storage;
@ -1011,14 +1023,14 @@ TEST(TestLogicalPlanner, AtomIndexedLabelProperty) {
Dbms dbms;
auto dba = dbms.active();
auto label = dba->label("label");
auto property = dba->property("property");
auto not_indexed = dba->property("not_indexed");
auto property = PROPERTY_PAIR("property");
auto not_indexed = PROPERTY_PAIR("not_indexed");
auto vertex = dba->insert_vertex();
vertex.add_label(label);
vertex.PropsSet(property, 42);
vertex.PropsSet(property.second, 42);
dba->commit();
dba = dbms.active();
dba->BuildIndex(label, property);
dba->BuildIndex(label, property.second);
dba = dbms.active();
auto node = NODE("n", label);
auto lit_42 = LITERAL(42);
@ -1038,9 +1050,9 @@ TEST(TestLogicalPlanner, AtomPropertyWhereLabelIndexing) {
Dbms dbms;
auto dba = dbms.active();
auto label = dba->label("label");
auto property = dba->property("property");
auto not_indexed = dba->property("not_indexed");
dba->BuildIndex(label, property);
auto property = PROPERTY_PAIR("property");
auto not_indexed = PROPERTY_PAIR("not_indexed");
dba->BuildIndex(label, property.second);
dba = dbms.active();
auto node = NODE("n");
auto lit_42 = LITERAL(42);
@ -1063,8 +1075,8 @@ TEST(TestLogicalPlanner, WhereIndexedLabelProperty) {
Dbms dbms;
auto dba = dbms.active();
auto label = dba->label("label");
auto property = dba->property("property");
dba->BuildIndex(label, property);
auto property = PROPERTY_PAIR("property");
dba->BuildIndex(label, property.second);
dba = dbms.active();
auto lit_42 = LITERAL(42);
QUERY(MATCH(PATTERN(NODE("n", label))),
@ -1093,10 +1105,10 @@ TEST(TestLogicalPlanner, BestPropertyIndexed) {
dba->commit();
dba = dbms.active();
ASSERT_EQ(dba->vertices_count(label, property), 1);
auto better = dba->property("better");
dba->BuildIndex(label, better);
auto better = PROPERTY_PAIR("better");
dba->BuildIndex(label, better.second);
dba = dbms.active();
ASSERT_EQ(dba->vertices_count(label, better), 0);
ASSERT_EQ(dba->vertices_count(label, better.second), 0);
auto lit_42 = LITERAL(42);
QUERY(MATCH(PATTERN(NODE("n", label))),
WHERE(AND(EQ(PROPERTY_LOOKUP("n", property), LITERAL(1)),
@ -1116,11 +1128,11 @@ TEST(TestLogicalPlanner, MultiPropertyIndexScan) {
auto dba = dbms.active();
auto label1 = dba->label("label1");
auto label2 = dba->label("label2");
auto prop1 = dba->property("prop1");
auto prop2 = dba->property("prop2");
dba->BuildIndex(label1, prop1);
auto prop1 = PROPERTY_PAIR("prop1");
auto prop2 = PROPERTY_PAIR("prop2");
dba->BuildIndex(label1, prop1.second);
dba = dbms.active();
dba->BuildIndex(label2, prop2);
dba->BuildIndex(label2, prop2.second);
dba = dbms.active();
AstTreeStorage storage;
auto lit_1 = LITERAL(1);
@ -1243,7 +1255,7 @@ TEST(TestLogicalPlanner, MatchExpandVariableFiltered) {
Dbms dbms;
auto dba = dbms.active();
auto type = dba->edge_type("type");
auto prop = dba->property("prop");
auto prop = PROPERTY_PAIR("prop");
AstTreeStorage storage;
auto edge = EDGE("r", type);
edge->has_range_ = true;

View File

@ -107,8 +107,7 @@ TEST(TestSymbolGenerator, CreatePropertyUnbound) {
auto node = NODE("anon");
Dbms dbms;
auto dba = dbms.active();
auto prop = dba->property("prop");
node->properties_[prop] = IDENT("x");
node->properties_[PROPERTY_PAIR("prop")] = IDENT("x");
auto query_ast = QUERY(CREATE(PATTERN(node)));
SymbolGenerator symbol_generator(symbol_table);
EXPECT_THROW(query_ast->Accept(symbol_generator), UnboundVariableError);
@ -401,10 +400,9 @@ TEST(TestSymbolGenerator, CreateExpandProperty) {
Dbms dbms;
auto dba = dbms.active();
auto r_type = dba->edge_type("r");
auto prop = dba->property("prop");
AstTreeStorage storage;
auto n_prop = NODE("n");
n_prop->properties_[prop] = LITERAL(42);
n_prop->properties_[PROPERTY_PAIR("prop")] = LITERAL(42);
auto query = QUERY(CREATE(
PATTERN(NODE("n"), EDGE("r", r_type, EdgeAtom::Direction::OUT), n_prop)));
SymbolTable symbol_table;
@ -468,11 +466,11 @@ TEST(TestSymbolGenerator, MatchPropCreateNodeProp) {
// Test MATCH (n) CREATE (m {prop: n.prop})
Dbms dbms;
auto dba = dbms.active();
auto prop = dba->property("prop");
auto prop = PROPERTY_PAIR("prop");
AstTreeStorage storage;
auto node_n = NODE("n");
auto node_m = NODE("m");
auto n_prop = PROPERTY_LOOKUP("n", prop);
auto n_prop = PROPERTY_LOOKUP("n", prop.second);
node_m->properties_[prop] = n_prop;
auto query = QUERY(MATCH(PATTERN(node_n)), CREATE(PATTERN(node_m)));
SymbolTable symbol_table;
@ -757,13 +755,13 @@ TEST(TestSymbolGenerator, MatchCrossReferenceVariable) {
// MATCH (n {prop: m.prop}), (m {prop: n.prop}) RETURN n
Dbms dbms;
auto dba = dbms.active();
auto prop = dba->property("prop");
auto prop = PROPERTY_PAIR("prop");
AstTreeStorage storage;
auto node_n = NODE("n");
auto m_prop = PROPERTY_LOOKUP("m", prop);
auto m_prop = PROPERTY_LOOKUP("m", prop.second);
node_n->properties_[prop] = m_prop;
auto node_m = NODE("m");
auto n_prop = PROPERTY_LOOKUP("n", prop);
auto n_prop = PROPERTY_LOOKUP("n", prop.second);
node_m->properties_[prop] = n_prop;
auto ident_n = IDENT("n");
auto as_n = AS("n");
@ -852,10 +850,10 @@ TEST(TestSymbolGenerator, MatchEdgeWithIdentifierInProperty) {
// Test MATCH (n) -[r {prop: n.prop}]- (m) RETURN r
Dbms dbms;
auto dba = dbms.active();
auto prop = dba->property("prop");
auto prop = PROPERTY_PAIR("prop");
AstTreeStorage storage;
auto edge = EDGE("r");
auto n_prop = PROPERTY_LOOKUP("n", prop);
auto n_prop = PROPERTY_LOOKUP("n", prop.second);
edge->properties_[prop] = n_prop;
auto node_n = NODE("n");
auto query = QUERY(MATCH(PATTERN(node_n, edge, NODE("m"))), RETURN("r"));
@ -971,10 +969,10 @@ TEST(TestSymbolGenerator, MatchPropertySameIdentifier) {
// matched symbol is obtained.
Dbms dbms;
auto dba = dbms.active();
auto prop = dba->property("prop");
auto prop = PROPERTY_PAIR("prop");
AstTreeStorage storage;
auto node_n = NODE("n");
auto n_prop = PROPERTY_LOOKUP("n", prop);
auto n_prop = PROPERTY_LOOKUP("n", prop.second);
node_n->properties_[prop] = n_prop;
auto query = QUERY(MATCH(PATTERN(node_n)), RETURN("n"));
SymbolTable symbol_table;