179 lines
7.0 KiB
C++
179 lines
7.0 KiB
C++
// Copyright 2023 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 "query_plan_checker_v2.hpp"
|
|
|
|
#include <iostream>
|
|
#include <list>
|
|
#include <sstream>
|
|
#include <tuple>
|
|
#include <typeinfo>
|
|
#include <unordered_set>
|
|
#include <variant>
|
|
|
|
#include <gmock/gmock.h>
|
|
#include <gtest/gtest.h>
|
|
|
|
#include "expr/semantic/symbol_generator.hpp"
|
|
#include "query/frontend/semantic/symbol_table.hpp"
|
|
#include "query/v2/frontend/ast/ast.hpp"
|
|
#include "query/v2/plan/operator.hpp"
|
|
#include "query/v2/plan/planner.hpp"
|
|
|
|
#include "query_v2_common.hpp"
|
|
|
|
namespace memgraph::query {
|
|
::std::ostream &operator<<(::std::ostream &os, const Symbol &sym) {
|
|
return os << "Symbol{\"" << sym.name() << "\" [" << sym.position() << "] " << Symbol::TypeToString(sym.type()) << "}";
|
|
}
|
|
} // namespace memgraph::query
|
|
|
|
// using namespace memgraph::query::v2::plan;
|
|
using namespace memgraph::expr::plan;
|
|
using memgraph::query::Symbol;
|
|
using memgraph::query::SymbolGenerator;
|
|
using memgraph::query::v2::AstStorage;
|
|
using memgraph::query::v2::SingleQuery;
|
|
using memgraph::query::v2::SymbolTable;
|
|
using Type = memgraph::query::v2::EdgeAtom::Type;
|
|
using Direction = memgraph::query::v2::EdgeAtom::Direction;
|
|
using Bound = ScanAllByLabelPropertyRange::Bound;
|
|
|
|
namespace {
|
|
|
|
class Planner {
|
|
public:
|
|
template <class TDbAccessor>
|
|
Planner(std::vector<SingleQueryPart> single_query_parts, PlanningContext<TDbAccessor> context) {
|
|
memgraph::expr::Parameters parameters;
|
|
PostProcessor post_processor(parameters);
|
|
plan_ = MakeLogicalPlanForSingleQuery<RuleBasedPlanner>(single_query_parts, &context);
|
|
plan_ = post_processor.Rewrite(std::move(plan_), &context);
|
|
}
|
|
|
|
auto &plan() { return *plan_; }
|
|
|
|
private:
|
|
std::unique_ptr<LogicalOperator> plan_;
|
|
};
|
|
|
|
template <class... TChecker>
|
|
auto CheckPlan(LogicalOperator &plan, const SymbolTable &symbol_table, TChecker... checker) {
|
|
std::list<BaseOpChecker *> checkers{&checker...};
|
|
PlanChecker plan_checker(checkers, symbol_table);
|
|
plan.Accept(plan_checker);
|
|
EXPECT_TRUE(plan_checker.checkers_.empty());
|
|
}
|
|
|
|
template <class TPlanner, class... TChecker>
|
|
auto CheckPlan(memgraph::query::v2::CypherQuery *query, AstStorage &storage, TChecker... checker) {
|
|
auto symbol_table = memgraph::expr::MakeSymbolTable(query);
|
|
FakeDistributedDbAccessor dba;
|
|
auto planner = MakePlanner<TPlanner>(&dba, storage, symbol_table, query);
|
|
CheckPlan(planner.plan(), symbol_table, checker...);
|
|
}
|
|
|
|
template <class T>
|
|
class TestPlanner : public ::testing::Test {};
|
|
|
|
using PlannerTypes = ::testing::Types<Planner>;
|
|
|
|
TYPED_TEST_SUITE(TestPlanner, PlannerTypes);
|
|
|
|
TYPED_TEST(TestPlanner, MatchFilterPropIsNotNull) {
|
|
const char *prim_label_name = "prim_label_one";
|
|
// Exact primary key match, one elem as PK.
|
|
{
|
|
FakeDistributedDbAccessor dba;
|
|
auto label = dba.Label(prim_label_name);
|
|
auto prim_prop_one = PRIMARY_PROPERTY_PAIR("prim_prop_one");
|
|
|
|
dba.SetIndexCount(label, 1);
|
|
dba.SetIndexCount(label, prim_prop_one.second, 1);
|
|
|
|
dba.CreateSchema(label, {prim_prop_one.second});
|
|
|
|
memgraph::query::v2::AstStorage storage;
|
|
|
|
memgraph::query::v2::Expression *expected_primary_key;
|
|
expected_primary_key = PROPERTY_LOOKUP("n", prim_prop_one);
|
|
auto *query = QUERY(SINGLE_QUERY(MATCH(PATTERN(NODE("n", prim_label_name))),
|
|
WHERE(EQ(PROPERTY_LOOKUP("n", prim_prop_one), LITERAL(1))), RETURN("n")));
|
|
auto symbol_table = (memgraph::expr::MakeSymbolTable(query));
|
|
auto planner = MakePlanner<TypeParam>(&dba, storage, symbol_table, query);
|
|
CheckPlan(planner.plan(), symbol_table, ExpectScanByPrimaryKey(label, {expected_primary_key}), ExpectProduce());
|
|
}
|
|
// Exact primary key match, two elem as PK.
|
|
{
|
|
FakeDistributedDbAccessor dba;
|
|
auto label = dba.Label(prim_label_name);
|
|
auto prim_prop_one = PRIMARY_PROPERTY_PAIR("prim_prop_one");
|
|
|
|
auto prim_prop_two = PRIMARY_PROPERTY_PAIR("prim_prop_two");
|
|
auto sec_prop_one = PRIMARY_PROPERTY_PAIR("sec_prop_one");
|
|
auto sec_prop_two = PRIMARY_PROPERTY_PAIR("sec_prop_two");
|
|
auto sec_prop_three = PRIMARY_PROPERTY_PAIR("sec_prop_three");
|
|
|
|
dba.SetIndexCount(label, 1);
|
|
dba.SetIndexCount(label, prim_prop_one.second, 1);
|
|
|
|
dba.CreateSchema(label, {prim_prop_one.second, prim_prop_two.second});
|
|
|
|
dba.SetIndexCount(label, prim_prop_two.second, 1);
|
|
dba.SetIndexCount(label, sec_prop_one.second, 1);
|
|
dba.SetIndexCount(label, sec_prop_two.second, 1);
|
|
dba.SetIndexCount(label, sec_prop_three.second, 1);
|
|
memgraph::query::v2::AstStorage storage;
|
|
|
|
memgraph::query::v2::Expression *expected_primary_key;
|
|
expected_primary_key = PROPERTY_LOOKUP("n", prim_prop_one);
|
|
auto *query = QUERY(SINGLE_QUERY(MATCH(PATTERN(NODE("n", prim_label_name))),
|
|
WHERE(AND(EQ(PROPERTY_LOOKUP("n", prim_prop_one), LITERAL(1)),
|
|
EQ(PROPERTY_LOOKUP("n", prim_prop_two), LITERAL(1)))),
|
|
RETURN("n")));
|
|
auto symbol_table = (memgraph::expr::MakeSymbolTable(query));
|
|
auto planner = MakePlanner<TypeParam>(&dba, storage, symbol_table, query);
|
|
CheckPlan(planner.plan(), symbol_table, ExpectScanByPrimaryKey(label, {expected_primary_key}), ExpectProduce());
|
|
}
|
|
// One elem is missing from PK, default to ScanAllByLabelPropertyValue.
|
|
{
|
|
FakeDistributedDbAccessor dba;
|
|
auto label = dba.Label(prim_label_name);
|
|
|
|
auto prim_prop_one = PRIMARY_PROPERTY_PAIR("prim_prop_one");
|
|
auto prim_prop_two = PRIMARY_PROPERTY_PAIR("prim_prop_two");
|
|
|
|
auto sec_prop_one = PRIMARY_PROPERTY_PAIR("sec_prop_one");
|
|
auto sec_prop_two = PRIMARY_PROPERTY_PAIR("sec_prop_two");
|
|
auto sec_prop_three = PRIMARY_PROPERTY_PAIR("sec_prop_three");
|
|
|
|
dba.SetIndexCount(label, 1);
|
|
dba.SetIndexCount(label, prim_prop_one.second, 1);
|
|
|
|
dba.CreateSchema(label, {prim_prop_one.second, prim_prop_two.second});
|
|
|
|
dba.SetIndexCount(label, prim_prop_two.second, 1);
|
|
dba.SetIndexCount(label, sec_prop_one.second, 1);
|
|
dba.SetIndexCount(label, sec_prop_two.second, 1);
|
|
dba.SetIndexCount(label, sec_prop_three.second, 1);
|
|
memgraph::query::v2::AstStorage storage;
|
|
|
|
auto *query = QUERY(SINGLE_QUERY(MATCH(PATTERN(NODE("n", prim_label_name))),
|
|
WHERE(EQ(PROPERTY_LOOKUP("n", prim_prop_one), LITERAL(1))), RETURN("n")));
|
|
auto symbol_table = (memgraph::expr::MakeSymbolTable(query));
|
|
auto planner = MakePlanner<TypeParam>(&dba, storage, symbol_table, query);
|
|
CheckPlan(planner.plan(), symbol_table, ExpectScanAllByLabelPropertyValue(label, prim_prop_one, IDENT("n")),
|
|
ExpectProduce());
|
|
}
|
|
}
|
|
|
|
} // namespace
|