#include "binder/expression_visitor.h"
#include "binder/query/reading_clause/bound_load_from.h"
#include "binder/query/reading_clause/bound_match_clause.h"
#include "binder/query/reading_clause/bound_table_function_call.h"
#include "planner/planner.h"
using namespace lbug::binder;
using namespace lbug::common;
namespace lbug {
namespace planner {
void Planner::planReadingClause(const BoundReadingClause& readingClause, LogicalPlan& plan) {
switch (readingClause.getClauseType()) {
case ClauseType::MATCH: {
planMatchClause(readingClause, plan);
} break;
case ClauseType::UNWIND: {
planUnwindClause(readingClause, plan);
} break;
case ClauseType::TABLE_FUNCTION_CALL: {
planTableFunctionCall(readingClause, plan);
} break;
case ClauseType::LOAD_FROM: {
planLoadFrom(readingClause, plan);
} break;
default:
UNREACHABLE_CODE;
}
}
void Planner::planMatchClause(const BoundReadingClause& readingClause, LogicalPlan& plan) {
auto& boundMatchClause = readingClause.constCast<BoundMatchClause>();
auto queryGraphCollection = boundMatchClause.getQueryGraphCollection();
auto predicates = boundMatchClause.getConjunctivePredicates();
switch (boundMatchClause.getMatchClauseType()) {
case MatchClauseType::MATCH: {
if (plan.isEmpty()) {
auto info = QueryGraphPlanningInfo();
info.predicates = predicates;
info.hint = boundMatchClause.getHint();
plan = planQueryGraphCollection(*queryGraphCollection, info);
} else {
planRegularMatch(*queryGraphCollection, predicates, plan, boundMatchClause.getHint());
}
} break;
case MatchClauseType::OPTIONAL_MATCH: {
planOptionalMatch(*queryGraphCollection, predicates, plan, boundMatchClause.getHint());
} break;
default:
UNREACHABLE_CODE;
}
}
void Planner::planUnwindClause(const BoundReadingClause& boundReadingClause, LogicalPlan& plan) {
if (plan.isEmpty()) { appendDummyScan(plan);
}
appendUnwind(boundReadingClause, plan);
}
class PredicatesDependencyAnalyzer {
public:
explicit PredicatesDependencyAnalyzer(const expression_vector& outputColumns) {
for (auto& column : outputColumns) {
columnNameSet.insert(column->getUniqueName());
}
}
void analyze(const expression_vector& predicates) {
predicatesDependsOnlyOnOutputColumns.clear();
predicatesWithOtherDependencies.clear();
for (auto& predicate : predicates) {
if (hasExternalDependency(predicate)) {
predicatesWithOtherDependencies.push_back(predicate);
} else {
predicatesDependsOnlyOnOutputColumns.push_back(predicate);
}
}
}
private:
bool hasExternalDependency(const std::shared_ptr<Expression>& expression) {
auto collector = DependentVarNameCollector();
collector.visit(expression);
for (auto& name : collector.getVarNames()) {
if (!columnNameSet.contains(name)) {
return true;
}
}
return false;
}
public:
expression_vector predicatesDependsOnlyOnOutputColumns;
expression_vector predicatesWithOtherDependencies;
private:
std::unordered_set<std::string> columnNameSet;
};
void Planner::planTableFunctionCall(const BoundReadingClause& readingClause, LogicalPlan& plan) {
auto& boundCall = readingClause.constCast<BoundTableFunctionCall>();
auto analyzer = PredicatesDependencyAnalyzer(boundCall.getBindData()->columns);
analyzer.analyze(boundCall.getConjunctivePredicates());
DASSERT(boundCall.getTableFunc().getLogicalPlanFunc);
boundCall.getTableFunc().getLogicalPlanFunc(this, readingClause,
analyzer.predicatesDependsOnlyOnOutputColumns, plan);
if (!analyzer.predicatesWithOtherDependencies.empty()) {
appendFilters(analyzer.predicatesWithOtherDependencies, plan);
}
}
void Planner::planLoadFrom(const BoundReadingClause& readingClause, LogicalPlan& plan) {
auto& loadFrom = readingClause.constCast<BoundLoadFrom>();
auto analyzer = PredicatesDependencyAnalyzer(loadFrom.getInfo()->bindData->columns);
analyzer.analyze(loadFrom.getConjunctivePredicates());
auto op = getTableFunctionCall(*loadFrom.getInfo());
planReadOp(std::move(op), analyzer.predicatesDependsOnlyOnOutputColumns, plan);
if (!analyzer.predicatesWithOtherDependencies.empty()) {
appendFilters(analyzer.predicatesWithOtherDependencies, plan);
}
}
void Planner::planReadOp(std::shared_ptr<LogicalOperator> op, const expression_vector& predicates,
LogicalPlan& plan) {
if (!plan.isEmpty()) {
auto tmpPlan = LogicalPlan();
tmpPlan.setLastOperator(std::move(op));
if (!predicates.empty()) {
appendFilters(predicates, tmpPlan);
}
appendCrossProduct(plan, tmpPlan, plan);
} else {
plan.setLastOperator(std::move(op));
if (!predicates.empty()) {
appendFilters(predicates, plan);
}
}
}
} }