Add missing license

This commit is contained in:
gvolfing 2022-11-28 16:18:28 +01:00
parent a65ea4fe01
commit 22e3164e60
5 changed files with 2620 additions and 1 deletions

View File

@ -93,6 +93,9 @@ target_link_libraries(${test_prefix}query_expression_evaluator mg-query)
add_unit_test(query_plan.cpp)
target_link_libraries(${test_prefix}query_plan mg-query)
add_unit_test(query_plan_v2.cpp)
target_link_libraries(${test_prefix}query_plan_v2 mg-query-v2)
add_unit_test(query_plan_accumulate_aggregate.cpp)
target_link_libraries(${test_prefix}query_plan_accumulate_aggregate mg-query)

View File

@ -46,6 +46,8 @@
#include "storage/v2/id_types.hpp"
#include "utils/string.hpp"
#include "query/v2/frontend/ast/ast.hpp"
namespace memgraph::query {
namespace test_common {
@ -81,12 +83,39 @@ std::string ToString(NamedExpression *expr) {
struct OrderBy {
std::vector<SortItem> expressions;
};
// new stuff begin
struct OrderByv2 {
std::vector<query::v2::SortItem> expressions;
};
// new stuff end
struct Skip {
Expression *expression = nullptr;
};
// new stuff begin
struct Skipv2 {
query::v2::Expression *expression = nullptr;
};
// new stuff end
struct Limit {
Expression *expression = nullptr;
};
// new stuff begin
struct Limitv2 {
query::v2::Expression *expression = nullptr;
};
// new stuff end
struct OnMatch {
std::vector<Clause *> set;
};
@ -153,6 +182,42 @@ auto GetPropertyLookup(AstStorage &storage, TDbAccessor &, Expression *expr,
return storage.Create<PropertyLookup>(expr, storage.GetPropertyIx(prop_pair.first));
}
// new stuff begin
template <class TDbAccessor>
auto GetPropertyLookup(memgraph::query::v2::AstStorage &storage, TDbAccessor &dba, const std::string &name,
memgraph::storage::v3::PropertyId property) {
return storage.Create<query::v2::PropertyLookup>(storage.Create<query::v2::Identifier>(name),
storage.GetPropertyIx(dba.PropertyToName(property)));
}
template <class TDbAccessor>
auto GetPropertyLookup(memgraph::query::v2::AstStorage &storage, TDbAccessor &dba,
memgraph::query::v2::Expression *expr, memgraph::storage::v3::PropertyId property) {
return storage.Create<query::v2::PropertyLookup>(expr, storage.GetPropertyIx(dba.PropertyToName(property)));
}
template <class TDbAccessor>
auto GetPropertyLookup(memgraph::query::v2::AstStorage &storage, TDbAccessor &dba,
memgraph::query::v2::Expression *expr, const std::string &property) {
return storage.Create<query::v2::PropertyLookup>(expr, storage.GetPropertyIx(property));
}
template <class TDbAccessor>
auto GetPropertyLookup(memgraph::query::v2::AstStorage &storage, TDbAccessor &, const std::string &name,
const std::pair<std::string, memgraph::storage::v3::PropertyId> &prop_pair) {
return storage.Create<query::v2::PropertyLookup>(storage.Create<query::v2::Identifier>(name),
storage.GetPropertyIx(prop_pair.first));
}
template <class TDbAccessor>
auto GetPropertyLookup(memgraph::query::v2::AstStorage &storage, TDbAccessor &, memgraph::query::v2::Expression *expr,
const std::pair<std::string, memgraph::storage::v3::PropertyId> &prop_pair) {
return storage.Create<query::v2::PropertyLookup>(expr, storage.GetPropertyIx(prop_pair.first));
}
// new stuff end
/// Create an EdgeAtom with given name, direction and edge_type.
///
/// Name is used to create the Identifier which is assigned to the edge.
@ -211,6 +276,15 @@ auto GetNode(AstStorage &storage, const std::string &name, std::optional<std::st
return node;
}
// new stuff begin
auto GetNode(memgraph::query::v2::AstStorage &storage, const std::string &name,
std::optional<std::string> label = std::nullopt) {
auto node = storage.Create<query::v2::NodeAtom>(storage.Create<query::v2::Identifier>(name));
if (label) node->labels_.emplace_back(storage.GetLabelIx(*label));
return node;
}
// new stuff end
/// Create a Pattern with given atoms.
auto GetPattern(AstStorage &storage, std::vector<PatternAtom *> atoms) {
auto pattern = storage.Create<Pattern>();
@ -227,6 +301,26 @@ auto GetPattern(AstStorage &storage, const std::string &name, std::vector<Patter
return pattern;
}
// new stuff begin
auto GetPattern(memgraph::query::v2::AstStorage &storage, std::vector<query::v2::PatternAtom *> atoms) {
auto pattern = storage.Create<query::v2::Pattern>();
pattern->identifier_ = storage.Create<query::v2::Identifier>(memgraph::utils::RandomString(20), false);
pattern->atoms_.insert(pattern->atoms_.begin(), atoms.begin(), atoms.end());
return pattern;
}
/// Create a Pattern with given name and atoms.
auto GetPattern(memgraph::query::v2::AstStorage &storage, const std::string &name,
std::vector<query::v2::PatternAtom *> atoms) {
auto pattern = storage.Create<query::v2::Pattern>();
pattern->identifier_ = storage.Create<query::v2::Identifier>(name, true);
pattern->atoms_.insert(pattern->atoms_.begin(), atoms.begin(), atoms.end());
return pattern;
}
// new stuff end
/// This function fills an AST node which with given patterns.
///
/// The function is most commonly used to create Match and Create clauses.
@ -236,6 +330,16 @@ auto GetWithPatterns(TWithPatterns *with_patterns, std::vector<Pattern *> patter
return with_patterns;
}
// new stuff begin
template <class TWithPatterns>
auto GetWithPatterns(TWithPatterns *with_patterns, std::vector<query::v2::Pattern *> patterns) {
with_patterns->patterns_.insert(with_patterns->patterns_.begin(), patterns.begin(), patterns.end());
return with_patterns;
}
// new stuff end
/// Create a query with given clauses.
auto GetSingleQuery(SingleQuery *single_query, Clause *clause) {
@ -271,6 +375,45 @@ auto GetSingleQuery(SingleQuery *single_query, Clause *clause, T *...clauses) {
return GetSingleQuery(single_query, clauses...);
}
// new stuff begin
auto GetSingleQuery(query::v2::SingleQuery *single_query, query::v2::Clause *clause) {
single_query->clauses_.emplace_back(clause);
return single_query;
}
auto GetSingleQuery(query::v2::SingleQuery *single_query, query::v2::Match *match, query::v2::Where *where) {
match->where_ = where;
single_query->clauses_.emplace_back(match);
return single_query;
}
auto GetSingleQuery(query::v2::SingleQuery *single_query, query::v2::With *with, query::v2::Where *where) {
with->where_ = where;
single_query->clauses_.emplace_back(with);
return single_query;
}
template <class... T>
auto GetSingleQuery(query::v2::SingleQuery *single_query, query::v2::Match *match, query::v2::Where *where,
T *...clauses) {
match->where_ = where;
single_query->clauses_.emplace_back(match);
return GetSingleQuery(single_query, clauses...);
}
template <class... T>
auto GetSingleQuery(query::v2::SingleQuery *single_query, query::v2::With *with, query::v2::Where *where,
T *...clauses) {
with->where_ = where;
single_query->clauses_.emplace_back(with);
return GetSingleQuery(single_query, clauses...);
}
template <class... T>
auto GetSingleQuery(query::v2::SingleQuery *single_query, query::v2::Clause *clause, T *...clauses) {
single_query->clauses_.emplace_back(clause);
return GetSingleQuery(single_query, clauses...);
}
// new stuff end
auto GetCypherUnion(CypherUnion *cypher_union, SingleQuery *single_query) {
cypher_union->single_query_ = single_query;
return cypher_union;
@ -290,6 +433,24 @@ auto GetQuery(AstStorage &storage, SingleQuery *single_query, T *...cypher_union
return query;
}
// new stuff begin
auto GetQuery(query::v2::AstStorage &storage, query::v2::SingleQuery *single_query) {
auto *query = storage.Create<query::v2::CypherQuery>();
query->single_query_ = single_query;
return query;
}
template <class... T>
auto GetQuery(query::v2::AstStorage &storage, query::v2::SingleQuery *single_query, T *...cypher_unions) {
auto *query = storage.Create<query::v2::CypherQuery>();
query->single_query_ = single_query;
query->cypher_unions_ = std::vector<query::v2::CypherUnion *>{cypher_unions...};
return query;
}
// new stuff end
// Helper functions for constructing RETURN and WITH clauses.
void FillReturnBody(AstStorage &, ReturnBody &body, NamedExpression *named_expr) {
body.named_expressions.emplace_back(named_expr);
@ -353,6 +514,80 @@ void FillReturnBody(AstStorage &storage, ReturnBody &body, const std::string &na
FillReturnBody(storage, body, rest...);
}
// new stuff begin
void FillReturnBody(query::v2::AstStorage &, query::v2::ReturnBody &body, query::v2::NamedExpression *named_expr) {
body.named_expressions.emplace_back(named_expr);
}
void FillReturnBody(query::v2::AstStorage &storage, query::v2::ReturnBody &body, const std::string &name) {
if (name == "*") {
body.all_identifiers = true;
} else {
auto *ident = storage.Create<memgraph::query::v2::Identifier>(name);
auto *named_expr = storage.Create<memgraph::query::v2::NamedExpression>(name, ident);
body.named_expressions.emplace_back(named_expr);
}
}
void FillReturnBody(query::v2::AstStorage &, query::v2::ReturnBody &body, Limitv2 limit) {
body.limit = limit.expression;
}
void FillReturnBody(query::v2::AstStorage &, query::v2::ReturnBody &body, Skipv2 skip, Limitv2 limit = Limitv2{}) {
body.skip = skip.expression;
body.limit = limit.expression;
}
void FillReturnBody(query::v2::AstStorage &, query::v2::ReturnBody &body, OrderByv2 order_by,
Limitv2 limit = Limitv2{}) {
body.order_by = order_by.expressions;
body.limit = limit.expression;
}
void FillReturnBody(query::v2::AstStorage &, query::v2::ReturnBody &body, OrderByv2 order_by, Skipv2 skip,
Limitv2 limit = Limitv2{}) {
body.order_by = order_by.expressions;
body.skip = skip.expression;
body.limit = limit.expression;
}
void FillReturnBody(query::v2::AstStorage &, query::v2::ReturnBody &body, query::v2::Expression *expr,
query::v2::NamedExpression *named_expr) {
// This overload supports `RETURN(expr, AS(name))` construct, since
// NamedExpression does not inherit Expression.
named_expr->expression_ = expr;
body.named_expressions.emplace_back(named_expr);
}
void FillReturnBody(query::v2::AstStorage &storage, query::v2::ReturnBody &body, const std::string &name,
query::v2::NamedExpression *named_expr) {
named_expr->expression_ = storage.Create<memgraph::query::v2::Identifier>(name);
body.named_expressions.emplace_back(named_expr);
}
template <class... T>
void FillReturnBody(query::v2::AstStorage &storage, query::v2::ReturnBody &body, query::v2::Expression *expr,
query::v2::NamedExpression *named_expr, T... rest) {
named_expr->expression_ = expr;
body.named_expressions.emplace_back(named_expr);
FillReturnBody(storage, body, rest...);
}
template <class... T>
void FillReturnBody(query::v2::AstStorage &storage, query::v2::ReturnBody &body, query::v2::NamedExpression *named_expr,
T... rest) {
body.named_expressions.emplace_back(named_expr);
FillReturnBody(storage, body, rest...);
}
template <class... T>
void FillReturnBody(query::v2::AstStorage &storage, query::v2::ReturnBody &body, const std::string &name,
query::v2::NamedExpression *named_expr, T... rest) {
named_expr->expression_ = storage.Create<memgraph::query::v2::Identifier>(name);
body.named_expressions.emplace_back(named_expr);
FillReturnBody(storage, body, rest...);
}
template <class... T>
void FillReturnBody(query::v2::AstStorage &storage, query::v2::ReturnBody &body, const std::string &name, T... rest) {
auto *ident = storage.Create<memgraph::query::v2::Identifier>(name);
auto *named_expr = storage.Create<memgraph::query::v2::NamedExpression>(name, ident);
body.named_expressions.emplace_back(named_expr);
FillReturnBody(storage, body, rest...);
}
// new stuff end
/// Create the return clause with given expressions.
///
/// The supported expression combination of arguments is:
@ -374,6 +609,18 @@ auto GetReturn(AstStorage &storage, bool distinct, T... exprs) {
return ret;
}
// new stuff begin
template <class... T>
auto GetReturn(query::v2::AstStorage &storage, bool distinct, T... exprs) {
auto ret = storage.Create<query::v2::Return>();
ret->body_.distinct = distinct;
FillReturnBody(storage, ret->body_, exprs...);
return ret;
}
// new stuff end
/// Create the with clause with given expressions.
///
/// The supported expression combination is the same as for @c GetReturn.
@ -486,12 +733,16 @@ auto GetForeach(AstStorage &storage, NamedExpression *named_expr, const std::vec
#define EDGE(...) memgraph::query::test_common::GetEdge(storage, __VA_ARGS__)
#define EDGE_VARIABLE(...) memgraph::query::test_common::GetEdgeVariable(storage, __VA_ARGS__)
#define PATTERN(...) memgraph::query::test_common::GetPattern(storage, {__VA_ARGS__})
#define PATTERN(...) memgraph::query::test_common::GetPattern(storage, {__VA_ARGS__})
#define NAMED_PATTERN(name, ...) memgraph::query::test_common::GetPattern(storage, name, {__VA_ARGS__})
#define OPTIONAL_MATCH(...) \
memgraph::query::test_common::GetWithPatterns(storage.Create<memgraph::query::Match>(true), {__VA_ARGS__})
#define MATCH(...) \
memgraph::query::test_common::GetWithPatterns(storage.Create<memgraph::query::Match>(), {__VA_ARGS__})
#define MATCH_V2(...) \
memgraph::query::test_common::GetWithPatterns(storage.Create<memgraph::query::v2::Match>(), {__VA_ARGS__})
#define WHERE(expr) storage.Create<memgraph::query::Where>((expr))
#define WHERE_V2(expr) storage.Create<memgraph::query::v2::Where>((expr))
#define CREATE(...) \
memgraph::query::test_common::GetWithPatterns(storage.Create<memgraph::query::Create>(), {__VA_ARGS__})
#define IDENT(...) storage.Create<memgraph::query::Identifier>(__VA_ARGS__)
@ -501,6 +752,8 @@ auto GetForeach(AstStorage &storage, NamedExpression *named_expr, const std::vec
storage.Create<memgraph::query::MapLiteral>( \
std::unordered_map<memgraph::query::PropertyIx, memgraph::query::Expression *>{__VA_ARGS__})
#define PROPERTY_PAIR(property_name) std::make_pair(property_name, dba.NameToProperty(property_name))
#define PRIMARY_PROPERTY_PAIR(property_name) std::make_pair(property_name, dba.NameToPrimaryProperty(property_name))
#define SECONDARY_PROPERTY_PAIR(property_name) std::make_pair(property_name, dba.NameToSecondaryProperty(property_name))
#define PROPERTY_LOOKUP(...) memgraph::query::test_common::GetPropertyLookup(storage, dba, __VA_ARGS__)
#define PARAMETER_LOOKUP(token_position) storage.Create<memgraph::query::ParameterLookup>((token_position))
#define NEXPR(name, expr) storage.Create<memgraph::query::NamedExpression>((name), (expr))
@ -536,6 +789,8 @@ auto GetForeach(AstStorage &storage, NamedExpression *named_expr, const std::vec
std::vector<memgraph::query::PropertyIx>{(property)})
#define QUERY(...) memgraph::query::test_common::GetQuery(storage, __VA_ARGS__)
#define SINGLE_QUERY(...) memgraph::query::test_common::GetSingleQuery(storage.Create<SingleQuery>(), __VA_ARGS__)
#define SINGLE_QUERY_V2(...) \
memgraph::query::test_common::GetSingleQuery(storage.Create<memgraph::query::v2::SingleQuery>(), __VA_ARGS__)
#define UNION(...) memgraph::query::test_common::GetCypherUnion(storage.Create<CypherUnion>(true), __VA_ARGS__)
#define UNION_ALL(...) memgraph::query::test_common::GetCypherUnion(storage.Create<CypherUnion>(false), __VA_ARGS__)
#define FOREACH(...) memgraph::query::test_common::GetForeach(storage, __VA_ARGS__)

View File

@ -17,6 +17,7 @@
#include "query/plan/operator.hpp"
#include "query/plan/planner.hpp"
#include "query/plan/preprocess.hpp"
#include "query/v2/plan/operator.hpp"
namespace memgraph::query::plan {
@ -90,7 +91,7 @@ class PlanChecker : public virtual HierarchicalLogicalOperatorVisitor {
}
PRE_VISIT(Unwind);
PRE_VISIT(Distinct);
bool PreVisit(Foreach &op) override {
CheckOp(op);
return false;
@ -336,6 +337,39 @@ class ExpectScanAllByLabelProperty : public OpChecker<ScanAllByLabelProperty> {
memgraph::storage::PropertyId property_;
};
class ExpectScanAllByPrimaryKey : public OpChecker<v2::plan::ScanAllByPrimaryKey> {
public:
ExpectScanAllByPrimaryKey(memgraph::storage::v3::LabelId label, const std::vector<Expression *> &properties)
: label_(label), properties_(properties) {}
void ExpectOp(v2::plan::ScanAllByPrimaryKey &scan_all, const SymbolTable &) override {
EXPECT_EQ(scan_all.label_, label_);
// EXPECT_EQ(scan_all.property_, property_);
// TODO(gvolfing) maybe assert the size of the 2 vectors.
// TODO(gvolfing) maybe use some std alg if Expression lets us.
bool primary_property_match = true;
for (const auto &expected_prop : properties_) {
bool has_match = false;
for (const auto &prop : scan_all.primary_key_) {
if (typeid(prop).hash_code() == typeid(expected_prop).hash_code()) {
has_match = true;
}
}
if (!has_match) {
primary_property_match = false;
}
}
EXPECT_TRUE(primary_property_match);
}
private:
memgraph::storage::v3::LabelId label_;
std::vector<Expression *> properties_;
};
class ExpectCartesian : public OpChecker<Cartesian> {
public:
ExpectCartesian(const std::list<std::unique_ptr<BaseOpChecker>> &left,
@ -486,4 +520,108 @@ class FakeDbAccessor {
std::vector<std::tuple<memgraph::storage::LabelId, memgraph::storage::PropertyId, int64_t>> label_property_index_;
};
// class FakeDistributedDbAccessor {
// public:
// int64_t VerticesCount(memgraph::storage::v3::LabelId label) const {
// auto found = label_index_.find(label);
// if (found != label_index_.end()) return found->second;
// return 0;
// }
// int64_t VerticesCount(memgraph::storage::v3::LabelId label, memgraph::storage::PropertyId property) const {
// for (auto &index : label_property_index_) {
// if (std::get<0>(index) == label && std::get<1>(index) == property) {
// return std::get<2>(index);
// }
// }
// return 0;
// }
// bool LabelIndexExists(memgraph::storage::v3::LabelId label) const {
// return label_index_.find(label) != label_index_.end();
// }
// bool LabelPropertyIndexExists(memgraph::storage::v3::LabelId label, memgraph::storage::PropertyId property) const {
// for (auto &index : label_property_index_) {
// if (std::get<0>(index) == label && std::get<1>(index) == property) {
// return true;
// }
// }
// return false;
// }
// void SetIndexCount(memgraph::storage::v3::LabelId label, int64_t count) { label_index_[label] = count; }
// void SetIndexCount(memgraph::storage::v3::LabelId label, memgraph::storage::PropertyId property, int64_t count) {
// for (auto &index : label_property_index_) {
// if (std::get<0>(index) == label && std::get<1>(index) == property) {
// std::get<2>(index) = count;
// return;
// }
// }
// label_property_index_.emplace_back(label, property, count);
// }
// memgraph::storage::v3::LabelId NameToLabel(const std::string &name) {
// auto found = primary_labels_.find(name);
// if (found != primary_labels_.end()) return found->second;
// return primary_labels_.emplace(name,
// memgraph::storage::v3::LabelId::FromUint(primary_labels_.size())).first->second;
// }
// memgraph::storage::v3::LabelId Label(const std::string &name) { return NameToLabel(name); }
// memgraph::storage::EdgeTypeId NameToEdgeType(const std::string &name) {
// auto found = edge_types_.find(name);
// if (found != edge_types_.end()) return found->second;
// return edge_types_.emplace(name, memgraph::storage::EdgeTypeId::FromUint(edge_types_.size())).first->second;
// }
// memgraph::storage::PropertyId NameToPrimaryProperty(const std::string &name) {
// auto found = primary_properties_.find(name);
// if (found != primary_properties_.end()) return found->second;
// return primary_properties_.emplace(name,
// memgraph::storage::PropertyId::FromUint(primary_properties_.size())).first->second;
// }
// memgraph::storage::PropertyId NameToSecondaryProperty(const std::string &name) {
// auto found = secondary_properties_.find(name);
// if (found != secondary_properties_.end()) return found->second;
// return secondary_properties_.emplace(name,
// memgraph::storage::PropertyId::FromUint(secondary_properties_.size())).first->second;
// }
// memgraph::storage::PropertyId PrimaryProperty(const std::string &name) { return NameToPrimaryProperty(name); }
// memgraph::storage::PropertyId SecondaryProperty(const std::string &name) { return NameToSecondaryProperty(name); }
// std::string PrimaryPropertyToName(memgraph::storage::PropertyId property) const {
// for (const auto &kv : primary_properties_) {
// if (kv.second == property) return kv.first;
// }
// LOG_FATAL("Unable to find primary property name");
// }
// std::string SecondaryPropertyToName(memgraph::storage::PropertyId property) const {
// for (const auto &kv : secondary_properties_) {
// if (kv.second == property) return kv.first;
// }
// LOG_FATAL("Unable to find secondary property name");
// }
// std::string PrimaryPropertyName(memgraph::storage::PropertyId property) const { return
// PrimaryPropertyToName(property); } std::string SecondaryPropertyName(memgraph::storage::PropertyId property) const
// { return SecondaryPropertyToName(property); }
// private:
// std::unordered_map<std::string, memgraph::storage::v3::LabelId> primary_labels_;
// std::unordered_map<std::string, memgraph::storage::v3::LabelId> secondary_labels_;
// std::unordered_map<std::string, memgraph::storage::EdgeTypeId> edge_types_;
// std::unordered_map<std::string, memgraph::storage::PropertyId> primary_properties_;
// std::unordered_map<std::string, memgraph::storage::PropertyId> secondary_properties_;
// std::unordered_map<memgraph::storage::v3::LabelId, int64_t> label_index_;
// std::vector<std::tuple<memgraph::storage::v3::LabelId, memgraph::storage::PropertyId, int64_t>>
// label_property_index_;
// };
} // namespace memgraph::query::plan

View File

@ -0,0 +1,559 @@
// Copyright 2022 Memgraph Ltd.
//
// Use of this software is governed by the Business Source License
// included in the file licenses/BSL.txt; by using this file, you agree to be bound by the terms of the Business Source
// License, and you may not use this file except in compliance with the Business Source License.
//
// As of the Change Date specified in that file, in accordance with
// the Business Source License, use of this software will be governed
// by the Apache License, Version 2.0, included in the file
// licenses/APL.txt.
#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include "query/frontend/semantic/symbol_generator.hpp"
#include "query/frontend/semantic/symbol_table.hpp"
#include "query/v2/plan/operator.hpp"
#include "query/v2/plan/planner.hpp"
#include "query/v2/plan/preprocess.hpp"
namespace memgraph::query::v2::plan {
class BaseOpChecker {
public:
virtual ~BaseOpChecker() {}
virtual void CheckOp(LogicalOperator &, const SymbolTable &) = 0;
};
class PlanChecker : public virtual HierarchicalLogicalOperatorVisitor {
public:
using HierarchicalLogicalOperatorVisitor::PostVisit;
using HierarchicalLogicalOperatorVisitor::PreVisit;
using HierarchicalLogicalOperatorVisitor::Visit;
PlanChecker(const std::list<std::unique_ptr<BaseOpChecker>> &checkers, const SymbolTable &symbol_table)
: symbol_table_(symbol_table) {
for (const auto &checker : checkers) checkers_.emplace_back(checker.get());
}
PlanChecker(const std::list<BaseOpChecker *> &checkers, const SymbolTable &symbol_table)
: checkers_(checkers), symbol_table_(symbol_table) {}
#define PRE_VISIT(TOp) \
bool PreVisit(TOp &op) override { \
CheckOp(op); \
return true; \
}
#define VISIT(TOp) \
bool Visit(TOp &op) override { \
CheckOp(op); \
return true; \
}
PRE_VISIT(CreateNode);
PRE_VISIT(CreateExpand);
PRE_VISIT(Delete);
PRE_VISIT(ScanAll);
PRE_VISIT(ScanAllByLabel);
PRE_VISIT(ScanAllByLabelPropertyValue);
PRE_VISIT(ScanAllByLabelPropertyRange);
PRE_VISIT(ScanAllByLabelProperty);
PRE_VISIT(Expand);
PRE_VISIT(ExpandVariable);
PRE_VISIT(Filter);
PRE_VISIT(ConstructNamedPath);
PRE_VISIT(Produce);
PRE_VISIT(SetProperty);
PRE_VISIT(SetProperties);
PRE_VISIT(SetLabels);
PRE_VISIT(RemoveProperty);
PRE_VISIT(RemoveLabels);
PRE_VISIT(EdgeUniquenessFilter);
PRE_VISIT(Accumulate);
PRE_VISIT(Aggregate);
PRE_VISIT(Skip);
PRE_VISIT(Limit);
PRE_VISIT(OrderBy);
bool PreVisit(Merge &op) override {
CheckOp(op);
op.input()->Accept(*this);
return false;
}
bool PreVisit(Optional &op) override {
CheckOp(op);
op.input()->Accept(*this);
return false;
}
PRE_VISIT(Unwind);
PRE_VISIT(Distinct);
bool PreVisit(Foreach &op) override {
CheckOp(op);
return false;
}
bool Visit(Once &) override {
// Ignore checking Once, it is implicitly at the end.
return true;
}
bool PreVisit(Cartesian &op) override {
CheckOp(op);
return false;
}
PRE_VISIT(CallProcedure);
#undef PRE_VISIT
#undef VISIT
void CheckOp(LogicalOperator &op) {
ASSERT_FALSE(checkers_.empty());
checkers_.back()->CheckOp(op, symbol_table_);
checkers_.pop_back();
}
std::list<BaseOpChecker *> checkers_;
const SymbolTable &symbol_table_;
};
template <class TOp>
class OpChecker : public BaseOpChecker {
public:
void CheckOp(LogicalOperator &op, const SymbolTable &symbol_table) override {
auto *expected_op = dynamic_cast<TOp *>(&op);
ASSERT_TRUE(expected_op) << "op is '" << op.GetTypeInfo().name << "' expected '" << TOp::kType.name << "'!";
ExpectOp(*expected_op, symbol_table);
}
virtual void ExpectOp(TOp &, const SymbolTable &) {}
};
// using ExpectScanAllByPrimaryKey = OpChecker<ScanAllByPrimaryKey>;
using ExpectCreateNode = OpChecker<CreateNode>;
using ExpectCreateExpand = OpChecker<CreateExpand>;
using ExpectDelete = OpChecker<Delete>;
using ExpectScanAll = OpChecker<ScanAll>;
using ExpectScanAllByLabel = OpChecker<ScanAllByLabel>;
// using ExpectScanAllById = OpChecker<ScanAllById>;
using ExpectExpand = OpChecker<Expand>;
using ExpectFilter = OpChecker<Filter>;
using ExpectConstructNamedPath = OpChecker<ConstructNamedPath>;
using ExpectProduce = OpChecker<Produce>;
using ExpectSetProperty = OpChecker<SetProperty>;
using ExpectSetProperties = OpChecker<SetProperties>;
using ExpectSetLabels = OpChecker<SetLabels>;
using ExpectRemoveProperty = OpChecker<RemoveProperty>;
using ExpectRemoveLabels = OpChecker<RemoveLabels>;
using ExpectEdgeUniquenessFilter = OpChecker<EdgeUniquenessFilter>;
using ExpectSkip = OpChecker<Skip>;
using ExpectLimit = OpChecker<Limit>;
using ExpectOrderBy = OpChecker<OrderBy>;
using ExpectUnwind = OpChecker<Unwind>;
using ExpectDistinct = OpChecker<Distinct>;
// class ExpectForeach : public OpChecker<Foreach> {
// public:
// ExpectForeach(const std::list<BaseOpChecker *> &input, const std::list<BaseOpChecker *> &updates)
// : input_(input), updates_(updates) {}
// void ExpectOp(Foreach &foreach, const SymbolTable &symbol_table) override {
// PlanChecker check_input(input_, symbol_table);
// foreach
// .input_->Accept(check_input);
// PlanChecker check_updates(updates_, symbol_table);
// foreach
// .update_clauses_->Accept(check_updates);
// }
// private:
// std::list<BaseOpChecker *> input_;
// std::list<BaseOpChecker *> updates_;
// };
// class ExpectExpandVariable : public OpChecker<ExpandVariable> {
// public:
// void ExpectOp(ExpandVariable &op, const SymbolTable &) override {
// EXPECT_EQ(op.type_, memgraph::query::EdgeAtom::Type::DEPTH_FIRST);
// }
// };
// class ExpectExpandBfs : public OpChecker<ExpandVariable> {
// public:
// void ExpectOp(ExpandVariable &op, const SymbolTable &) override {
// EXPECT_EQ(op.type_, memgraph::query::EdgeAtom::Type::BREADTH_FIRST);
// }
// };
// class ExpectAccumulate : public OpChecker<Accumulate> {
// public:
// explicit ExpectAccumulate(const std::unordered_set<Symbol> &symbols) : symbols_(symbols) {}
// void ExpectOp(Accumulate &op, const SymbolTable &) override {
// std::unordered_set<Symbol> got_symbols(op.symbols_.begin(), op.symbols_.end());
// EXPECT_EQ(symbols_, got_symbols);
// }
// private:
// const std::unordered_set<Symbol> symbols_;
// };
// class ExpectAggregate : public OpChecker<Aggregate> {
// public:
// ExpectAggregate(const std::vector<memgraph::query::Aggregation *> &aggregations,
// const std::unordered_set<memgraph::query::Expression *> &group_by)
// : aggregations_(aggregations), group_by_(group_by) {}
// void ExpectOp(Aggregate &op, const SymbolTable &symbol_table) override {
// auto aggr_it = aggregations_.begin();
// for (const auto &aggr_elem : op.aggregations_) {
// ASSERT_NE(aggr_it, aggregations_.end());
// auto aggr = *aggr_it++;
// // TODO: Proper expression equality
// EXPECT_EQ(typeid(aggr_elem.value).hash_code(), typeid(aggr->expression1_).hash_code());
// EXPECT_EQ(typeid(aggr_elem.key).hash_code(), typeid(aggr->expression2_).hash_code());
// EXPECT_EQ(aggr_elem.op, aggr->op_);
// EXPECT_EQ(aggr_elem.output_sym, symbol_table.at(*aggr));
// }
// EXPECT_EQ(aggr_it, aggregations_.end());
// // TODO: Proper group by expression equality
// std::unordered_set<size_t> got_group_by;
// for (auto *expr : op.group_by_) got_group_by.insert(typeid(*expr).hash_code());
// std::unordered_set<size_t> expected_group_by;
// for (auto *expr : group_by_) expected_group_by.insert(typeid(*expr).hash_code());
// EXPECT_EQ(got_group_by, expected_group_by);
// }
// private:
// std::vector<memgraph::query::Aggregation *> aggregations_;
// std::unordered_set<memgraph::query::Expression *> group_by_;
// };
// class ExpectMerge : public OpChecker<Merge> {
// public:
// ExpectMerge(const std::list<BaseOpChecker *> &on_match, const std::list<BaseOpChecker *> &on_create)
// : on_match_(on_match), on_create_(on_create) {}
// void ExpectOp(Merge &merge, const SymbolTable &symbol_table) override {
// PlanChecker check_match(on_match_, symbol_table);
// merge.merge_match_->Accept(check_match);
// PlanChecker check_create(on_create_, symbol_table);
// merge.merge_create_->Accept(check_create);
// }
// private:
// const std::list<BaseOpChecker *> &on_match_;
// const std::list<BaseOpChecker *> &on_create_;
// };
// class ExpectOptional : public OpChecker<Optional> {
// public:
// explicit ExpectOptional(const std::list<BaseOpChecker *> &optional) : optional_(optional) {}
// ExpectOptional(const std::vector<Symbol> &optional_symbols, const std::list<BaseOpChecker *> &optional)
// : optional_symbols_(optional_symbols), optional_(optional) {}
// void ExpectOp(Optional &optional, const SymbolTable &symbol_table) override {
// if (!optional_symbols_.empty()) {
// EXPECT_THAT(optional.optional_symbols_, testing::UnorderedElementsAreArray(optional_symbols_));
// }
// PlanChecker check_optional(optional_, symbol_table);
// optional.optional_->Accept(check_optional);
// }
// private:
// std::vector<Symbol> optional_symbols_;
// const std::list<BaseOpChecker *> &optional_;
// };
// class ExpectScanAllByLabelPropertyValue : public OpChecker<ScanAllByLabelPropertyValue> {
// public:
// ExpectScanAllByLabelPropertyValue(memgraph::storage::LabelId label,
// const std::pair<std::string, memgraph::storage::PropertyId> &prop_pair,
// memgraph::query::Expression *expression)
// : label_(label), property_(prop_pair.second), expression_(expression) {}
// void ExpectOp(ScanAllByLabelPropertyValue &scan_all, const SymbolTable &) override {
// EXPECT_EQ(scan_all.label_, label_);
// EXPECT_EQ(scan_all.property_, property_);
// // TODO: Proper expression equality
// EXPECT_EQ(typeid(scan_all.expression_).hash_code(), typeid(expression_).hash_code());
// }
// private:
// memgraph::storage::LabelId label_;
// memgraph::storage::PropertyId property_;
// memgraph::query::Expression *expression_;
// };
// class ExpectScanAllByLabelPropertyRange : public OpChecker<ScanAllByLabelPropertyRange> {
// public:
// ExpectScanAllByLabelPropertyRange(memgraph::storage::LabelId label, memgraph::storage::PropertyId property,
// std::optional<ScanAllByLabelPropertyRange::Bound> lower_bound,
// std::optional<ScanAllByLabelPropertyRange::Bound> upper_bound)
// : label_(label), property_(property), lower_bound_(lower_bound), upper_bound_(upper_bound) {}
// void ExpectOp(ScanAllByLabelPropertyRange &scan_all, const SymbolTable &) override {
// EXPECT_EQ(scan_all.label_, label_);
// EXPECT_EQ(scan_all.property_, property_);
// if (lower_bound_) {
// ASSERT_TRUE(scan_all.lower_bound_);
// // TODO: Proper expression equality
// EXPECT_EQ(typeid(scan_all.lower_bound_->value()).hash_code(), typeid(lower_bound_->value()).hash_code());
// EXPECT_EQ(scan_all.lower_bound_->type(), lower_bound_->type());
// }
// if (upper_bound_) {
// ASSERT_TRUE(scan_all.upper_bound_);
// // TODO: Proper expression equality
// EXPECT_EQ(typeid(scan_all.upper_bound_->value()).hash_code(), typeid(upper_bound_->value()).hash_code());
// EXPECT_EQ(scan_all.upper_bound_->type(), upper_bound_->type());
// }
// }
// private:
// memgraph::storage::LabelId label_;
// memgraph::storage::PropertyId property_;
// std::optional<ScanAllByLabelPropertyRange::Bound> lower_bound_;
// std::optional<ScanAllByLabelPropertyRange::Bound> upper_bound_;
// };
// class ExpectScanAllByLabelProperty : public OpChecker<ScanAllByLabelProperty> {
// public:
// ExpectScanAllByLabelProperty(memgraph::storage::LabelId label,
// const std::pair<std::string, memgraph::storage::PropertyId> &prop_pair)
// : label_(label), property_(prop_pair.second) {}
// void ExpectOp(ScanAllByLabelProperty &scan_all, const SymbolTable &) override {
// EXPECT_EQ(scan_all.label_, label_);
// EXPECT_EQ(scan_all.property_, property_);
// }
// private:
// memgraph::storage::LabelId label_;
// memgraph::storage::PropertyId property_;
// };
class ExpectScanAllByPrimaryKey : public OpChecker<v2::plan::ScanAllByPrimaryKey> {
public:
ExpectScanAllByPrimaryKey(memgraph::storage::v3::LabelId label, const std::vector<Expression *> &properties)
: label_(label), properties_(properties) {}
void ExpectOp(v2::plan::ScanAllByPrimaryKey &scan_all, const SymbolTable &) override {
EXPECT_EQ(scan_all.label_, label_);
// EXPECT_EQ(scan_all.property_, property_);
// TODO(gvolfing) maybe assert the size of the 2 vectors.
// TODO(gvolfing) maybe use some std alg if Expression lets us.
bool primary_property_match = true;
for (const auto &expected_prop : properties_) {
bool has_match = false;
for (const auto &prop : scan_all.primary_key_) {
if (typeid(prop).hash_code() == typeid(expected_prop).hash_code()) {
has_match = true;
}
}
if (!has_match) {
primary_property_match = false;
}
}
EXPECT_TRUE(primary_property_match);
}
private:
memgraph::storage::v3::LabelId label_;
std::vector<Expression *> properties_;
};
class ExpectCartesian : public OpChecker<Cartesian> {
public:
ExpectCartesian(const std::list<std::unique_ptr<BaseOpChecker>> &left,
const std::list<std::unique_ptr<BaseOpChecker>> &right)
: left_(left), right_(right) {}
void ExpectOp(Cartesian &op, const SymbolTable &symbol_table) override {
ASSERT_TRUE(op.left_op_);
PlanChecker left_checker(left_, symbol_table);
op.left_op_->Accept(left_checker);
ASSERT_TRUE(op.right_op_);
PlanChecker right_checker(right_, symbol_table);
op.right_op_->Accept(right_checker);
}
private:
const std::list<std::unique_ptr<BaseOpChecker>> &left_;
const std::list<std::unique_ptr<BaseOpChecker>> &right_;
};
class ExpectCallProcedure : public OpChecker<CallProcedure> {
public:
ExpectCallProcedure(const std::string &name, const std::vector<memgraph::query::Expression *> &args,
const std::vector<std::string> &fields, const std::vector<Symbol> &result_syms)
: name_(name), args_(args), fields_(fields), result_syms_(result_syms) {}
void ExpectOp(CallProcedure &op, const SymbolTable &symbol_table) override {
EXPECT_EQ(op.procedure_name_, name_);
EXPECT_EQ(op.arguments_.size(), args_.size());
for (size_t i = 0; i < args_.size(); ++i) {
const auto *op_arg = op.arguments_[i];
const auto *expected_arg = args_[i];
// TODO: Proper expression equality
EXPECT_EQ(op_arg->GetTypeInfo(), expected_arg->GetTypeInfo());
}
EXPECT_EQ(op.result_fields_, fields_);
EXPECT_EQ(op.result_symbols_, result_syms_);
}
private:
std::string name_;
std::vector<memgraph::query::Expression *> args_;
std::vector<std::string> fields_;
std::vector<Symbol> result_syms_;
};
template <class T>
std::list<std::unique_ptr<BaseOpChecker>> MakeCheckers(T arg) {
std::list<std::unique_ptr<BaseOpChecker>> l;
l.emplace_back(std::make_unique<T>(arg));
return l;
}
template <class T, class... Rest>
std::list<std::unique_ptr<BaseOpChecker>> MakeCheckers(T arg, Rest &&...rest) {
auto l = MakeCheckers(std::forward<Rest>(rest)...);
l.emplace_front(std::make_unique<T>(arg));
return std::move(l);
}
template <class TPlanner, class TDbAccessor>
TPlanner MakePlanner(TDbAccessor *dba, AstStorage &storage, SymbolTable &symbol_table, CypherQuery *query) {
auto planning_context = MakePlanningContext(&storage, &symbol_table, query, dba);
auto query_parts = CollectQueryParts(symbol_table, storage, query);
auto single_query_parts = query_parts.query_parts.at(0).single_query_parts;
return TPlanner(single_query_parts, planning_context);
}
class FakeDistributedDbAccessor {
public:
int64_t VerticesCount(memgraph::storage::v3::LabelId label) const {
auto found = label_index_.find(label);
if (found != label_index_.end()) return found->second;
return 0;
}
int64_t VerticesCount(memgraph::storage::v3::LabelId label, memgraph::storage::v3::PropertyId property) const {
for (auto &index : label_property_index_) {
if (std::get<0>(index) == label && std::get<1>(index) == property) {
return std::get<2>(index);
}
}
return 0;
}
bool LabelIndexExists(memgraph::storage::v3::LabelId label) const {
return label_index_.find(label) != label_index_.end();
}
bool LabelPropertyIndexExists(memgraph::storage::v3::LabelId label,
memgraph::storage::v3::PropertyId property) const {
for (auto &index : label_property_index_) {
if (std::get<0>(index) == label && std::get<1>(index) == property) {
return true;
}
}
return false;
}
void SetIndexCount(memgraph::storage::v3::LabelId label, int64_t count) { label_index_[label] = count; }
void SetIndexCount(memgraph::storage::v3::LabelId label, memgraph::storage::v3::PropertyId property, int64_t count) {
for (auto &index : label_property_index_) {
if (std::get<0>(index) == label && std::get<1>(index) == property) {
std::get<2>(index) = count;
return;
}
}
label_property_index_.emplace_back(label, property, count);
}
memgraph::storage::v3::LabelId NameToLabel(const std::string &name) {
auto found = primary_labels_.find(name);
if (found != primary_labels_.end()) return found->second;
return primary_labels_.emplace(name, memgraph::storage::v3::LabelId::FromUint(primary_labels_.size()))
.first->second;
}
memgraph::storage::v3::LabelId Label(const std::string &name) { return NameToLabel(name); }
memgraph::storage::v3::EdgeTypeId NameToEdgeType(const std::string &name) {
auto found = edge_types_.find(name);
if (found != edge_types_.end()) return found->second;
return edge_types_.emplace(name, memgraph::storage::v3::EdgeTypeId::FromUint(edge_types_.size())).first->second;
}
memgraph::storage::v3::PropertyId NameToPrimaryProperty(const std::string &name) {
auto found = primary_properties_.find(name);
if (found != primary_properties_.end()) return found->second;
return primary_properties_.emplace(name, memgraph::storage::v3::PropertyId::FromUint(primary_properties_.size()))
.first->second;
}
memgraph::storage::v3::PropertyId NameToSecondaryProperty(const std::string &name) {
auto found = secondary_properties_.find(name);
if (found != secondary_properties_.end()) return found->second;
return secondary_properties_
.emplace(name, memgraph::storage::v3::PropertyId::FromUint(secondary_properties_.size()))
.first->second;
}
memgraph::storage::v3::PropertyId PrimaryProperty(const std::string &name) { return NameToPrimaryProperty(name); }
memgraph::storage::v3::PropertyId SecondaryProperty(const std::string &name) { return NameToSecondaryProperty(name); }
std::string PrimaryPropertyToName(memgraph::storage::v3::PropertyId property) const {
for (const auto &kv : primary_properties_) {
if (kv.second == property) return kv.first;
}
LOG_FATAL("Unable to find primary property name");
}
std::string SecondaryPropertyToName(memgraph::storage::v3::PropertyId property) const {
for (const auto &kv : secondary_properties_) {
if (kv.second == property) return kv.first;
}
LOG_FATAL("Unable to find secondary property name");
}
std::string PrimaryPropertyName(memgraph::storage::v3::PropertyId property) const {
return PrimaryPropertyToName(property);
}
std::string SecondaryPropertyName(memgraph::storage::v3::PropertyId property) const {
return SecondaryPropertyToName(property);
}
memgraph::storage::v3::PropertyId NameToProperty(const std::string &name) {
return storage::v3::PropertyId::FromUint(0);
}
std::vector<query::v2::Expression *> ExtractPrimaryKey(storage::v3::LabelId label,
std::vector<query::v2::plan::FilterInfo> property_filters) {
return std::vector<query::v2::Expression *>{};
}
private:
std::unordered_map<std::string, memgraph::storage::v3::LabelId> primary_labels_;
std::unordered_map<std::string, memgraph::storage::v3::LabelId> secondary_labels_;
std::unordered_map<std::string, memgraph::storage::v3::EdgeTypeId> edge_types_;
std::unordered_map<std::string, memgraph::storage::v3::PropertyId> primary_properties_;
std::unordered_map<std::string, memgraph::storage::v3::PropertyId> secondary_properties_;
std::unordered_map<memgraph::storage::v3::LabelId, int64_t> label_index_;
std::vector<std::tuple<memgraph::storage::v3::LabelId, memgraph::storage::v3::PropertyId, int64_t>>
label_property_index_;
};
} // namespace memgraph::query::v2::plan

1664
tests/unit/query_plan_v2.cpp Normal file

File diff suppressed because it is too large Load Diff