Skip to content

Commit

Permalink
refine AliasType deducing when planning (#4973)
Browse files Browse the repository at this point in the history
* refine AliasType deducing when planning

* refine

* fix lint

* fix tck

* address comment

* refine

* refine code & add tck case

* refine

* fix compile

* fix compile

* fix compile

* fix compile

* fix compile

* fix tck

* fix tck

Co-authored-by: Yee <[email protected]>
  • Loading branch information
codesigner and yixinglu authored Dec 7, 2022
1 parent 4014c9b commit c3ee460
Show file tree
Hide file tree
Showing 11 changed files with 419 additions and 17 deletions.
22 changes: 21 additions & 1 deletion src/graph/context/ast/CypherAstContext.h
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,27 @@ struct EdgeInfo {
Expression* filter{nullptr};
};

enum class AliasType : int8_t { kNode, kEdge, kPath, kEdgeList, kDefault };
enum class AliasType : int8_t { kNode, kEdge, kPath, kNodeList, kEdgeList, kRuntime };

struct AliasTypeName {
static std::string get(AliasType type) {
switch (type) {
case AliasType::kNode:
return "Node";
case AliasType::kEdge:
return "Edge";
case AliasType::kPath:
return "Path";
case AliasType::kNodeList:
return "NodeList";
case AliasType::kEdgeList:
return "EdgeList";
case AliasType::kRuntime:
return "Runtime";
}
return "Error"; // should not reach here
}
};

struct ScanInfo {
Expression* filter{nullptr};
Expand Down
12 changes: 8 additions & 4 deletions src/graph/planner/match/MatchPlanner.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -72,12 +72,16 @@ Status MatchPlanner::connectMatchPlan(SubPlan& queryPlan, MatchClauseContext* ma
for (auto& alias : matchCtx->aliasesGenerated) {
auto it = matchCtx->aliasesAvailable.find(alias.first);
if (it != matchCtx->aliasesAvailable.end()) {
// Joined type should be same
if (it->second != alias.second) {
// Joined type should be same,
// If any type is kRuntime, leave the type check to runtime,
// Primitive types (Integer, String, etc.) or composite types(List, Map etc.)
// are deduced to kRuntime when cannot be deduced during planning.
if (it->second != alias.second && it->second != AliasType::kRuntime &&
alias.second != AliasType::kRuntime) {
return Status::SemanticError(fmt::format("{} binding to different type: {} vs {}",
alias.first,
AliasTypeName[static_cast<int>(alias.second)],
AliasTypeName[static_cast<int>(it->second)]));
AliasTypeName::get(alias.second),
AliasTypeName::get(it->second)));
}
// Joined On EdgeList is not supported
if (alias.second == AliasType::kEdgeList) {
Expand Down
1 change: 0 additions & 1 deletion src/graph/planner/match/MatchPlanner.h
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@ class MatchPlanner final : public Planner {
StatusOr<SubPlan> transform(AstContext* astCtx) override;

private:
static constexpr std::array AliasTypeName = {"Node", "Edge", "Path", "EdgeList", "Default"};
bool tailConnected_{false};
StatusOr<SubPlan> genPlan(CypherClauseContextBase* clauseCtx);
Status connectMatchPlan(SubPlan& queryPlan, MatchClauseContext* matchCtx);
Expand Down
27 changes: 20 additions & 7 deletions src/graph/validator/MatchValidator.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
#include "common/expression/UnaryExpression.h"
#include "graph/planner/match/MatchSolver.h"
#include "graph/util/ExpressionUtils.h"
#include "graph/visitor/DeduceAliasTypeVisitor.h"
#include "graph/visitor/ExtractGroupSuiteVisitor.h"
#include "graph/visitor/RewriteVisitor.h"
#include "graph/visitor/ValidatePatternExpressionVisitor.h"
Expand Down Expand Up @@ -553,13 +554,19 @@ Status MatchValidator::validateWith(const WithClause *with,
exprs.reserve(withClauseCtx.yield->yieldColumns->size());
for (auto *col : withClauseCtx.yield->yieldColumns->columns()) {
auto labelExprs = ExpressionUtils::collectAll(col->expr(), {Expression::Kind::kLabel});
auto aliasType = AliasType::kDefault;
auto aliasType = AliasType::kRuntime;
for (auto *labelExpr : labelExprs) {
auto label = static_cast<const LabelExpression *>(labelExpr)->name();
if (!withClauseCtx.yield->aliasesAvailable.count(label)) {
return Status::SemanticError("Alias `%s` not defined", label.c_str());
}
aliasType = withClauseCtx.yield->aliasesAvailable.at(label);
AliasType inputType = withClauseCtx.yield->aliasesAvailable.at(label);
DeduceAliasTypeVisitor visitor(qctx_, vctx_, space_.id, inputType);
const_cast<Expression *>(col->expr())->accept(&visitor);
if (!visitor.ok()) {
return std::move(visitor).status();
}
aliasType = visitor.outputType();
}
if (col->alias().empty()) {
if (col->expr()->kind() == Expression::Kind::kLabel) {
Expand Down Expand Up @@ -639,7 +646,7 @@ Status MatchValidator::validateUnwind(const UnwindClause *unwindClause,
// set z to the same type
// else
// set z to default
AliasType aliasType = AliasType::kDefault;
AliasType aliasType = AliasType::kRuntime;
if (types.size() > 0 &&
std::adjacent_find(types.begin(), types.end(), std::not_equal_to<>()) == types.end()) {
aliasType = types[0];
Expand Down Expand Up @@ -929,7 +936,7 @@ Status MatchValidator::checkAlias(
const Expression *refExpr,
const std::unordered_map<std::string, AliasType> &aliasesAvailable) const {
auto kind = refExpr->kind();
AliasType aliasType = AliasType::kDefault;
AliasType aliasType = AliasType::kRuntime;

switch (kind) {
case Expression::Kind::kLabel: {
Expand Down Expand Up @@ -1211,7 +1218,9 @@ Status MatchValidator::validatePathInWhere(
matchPath.alias()->c_str());
}
if (find->second != AliasType::kPath) {
return Status::SemanticError("Alias `%s' should be Path.", matchPath.alias()->c_str());
return Status::SemanticError("Alias `%s' should be Path, but got type '%s",
matchPath.alias()->c_str(),
AliasTypeName::get(find->second).c_str());
}
}
for (const auto &node : matchPath.nodes()) {
Expand All @@ -1227,7 +1236,9 @@ Status MatchValidator::validatePathInWhere(
node->alias().c_str());
}
if (find->second != AliasType::kNode) {
return Status::SemanticError("Alias `%s' should be Node.", node->alias().c_str());
return Status::SemanticError("Alias `%s' should be Node, but got type '%s",
node->alias().c_str(),
AliasTypeName::get(find->second).c_str());
}
}
}
Expand All @@ -1240,7 +1251,9 @@ Status MatchValidator::validatePathInWhere(
edge->alias().c_str());
}
if (find->second != AliasType::kEdge) {
return Status::SemanticError("Alias `%s' should be Edge.", edge->alias().c_str());
return Status::SemanticError("Alias `%s' should be Edge, but got type '%s'",
edge->alias().c_str(),
AliasTypeName::get(find->second).c_str());
}
}
}
Expand Down
1 change: 1 addition & 0 deletions src/graph/visitor/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ nebula_add_library(
EvaluableExprVisitor.cpp
ExtractGroupSuiteVisitor.cpp
ValidatePatternExpressionVisitor.cpp
DeduceAliasTypeVisitor.cpp
)

nebula_add_library(
Expand Down
102 changes: 102 additions & 0 deletions src/graph/visitor/DeduceAliasTypeVisitor.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
/* Copyright (c) 2022 vesoft inc. All rights reserved.
*
* This source code is licensed under Apache 2.0 License.
*/

#include "graph/visitor/DeduceAliasTypeVisitor.h"

#include <sstream>
#include <unordered_map>

#include "common/datatypes/DataSet.h"
#include "common/datatypes/Edge.h"
#include "common/datatypes/List.h"
#include "common/datatypes/Map.h"
#include "common/datatypes/Path.h"
#include "common/datatypes/Set.h"
#include "common/function/FunctionManager.h"
#include "graph/context/QueryContext.h"
#include "graph/context/QueryExpressionContext.h"
#include "graph/context/ValidateContext.h"
#include "graph/visitor/EvaluableExprVisitor.h"

namespace nebula {
namespace graph {

DeduceAliasTypeVisitor::DeduceAliasTypeVisitor(QueryContext *qctx,
ValidateContext *vctx,
GraphSpaceID space,
AliasType inputType)
: qctx_(qctx), vctx_(vctx), space_(space), inputType_(inputType) {}

void DeduceAliasTypeVisitor::visit(VertexExpression *expr) {
UNUSED(expr);
outputType_ = AliasType::kNode;
}

void DeduceAliasTypeVisitor::visit(EdgeExpression *expr) {
UNUSED(expr);
outputType_ = AliasType::kEdge;
}

void DeduceAliasTypeVisitor::visit(PathBuildExpression *expr) {
UNUSED(expr);
outputType_ = AliasType::kPath;
}

void DeduceAliasTypeVisitor::visit(FunctionCallExpression *expr) {
std::string funName = expr->name();
std::transform(funName.begin(), funName.end(), funName.begin(), ::tolower);
if (funName == "nodes") {
outputType_ = AliasType::kNodeList;
} else if (funName == "relationships") {
outputType_ = AliasType::kEdgeList;
} else if (funName == "reversepath") {
outputType_ = AliasType::kPath;
} else if (funName == "startnode" || funName == "endnode") {
outputType_ = AliasType::kNode;
}
}

void DeduceAliasTypeVisitor::visit(SubscriptExpression *expr) {
Expression *leftExpr = expr->left();
DeduceAliasTypeVisitor childVisitor(qctx_, vctx_, space_, inputType_);
leftExpr->accept(&childVisitor);
if (!childVisitor.ok()) {
status_ = std::move(childVisitor).status();
return;
}
inputType_ = childVisitor.outputType();
// This is not accurate, since there exist List of List...Edges/Nodes,
// may have opportunities when analyze more detail of the expr.
if (inputType_ == AliasType::kEdgeList) {
outputType_ = AliasType::kEdge;
} else if (inputType_ == AliasType::kNodeList) {
outputType_ = AliasType::kNode;
} else {
outputType_ = inputType_;
}
}

void DeduceAliasTypeVisitor::visit(SubscriptRangeExpression *expr) {
Expression *leftExpr = expr->list();
DeduceAliasTypeVisitor childVisitor(qctx_, vctx_, space_, inputType_);
leftExpr->accept(&childVisitor);
if (!childVisitor.ok()) {
status_ = std::move(childVisitor).status();
return;
}
inputType_ = childVisitor.outputType();
// This is not accurate, since there exist List of List...Edges/Nodes,
// may have opportunities when analyze more detail of the expr.
if (inputType_ == AliasType::kEdgeList) {
outputType_ = AliasType::kEdgeList;
} else if (inputType_ == AliasType::kNodeList) {
outputType_ = AliasType::kNodeList;
} else {
outputType_ = inputType_;
}
}

} // namespace graph
} // namespace nebula
100 changes: 100 additions & 0 deletions src/graph/visitor/DeduceAliasTypeVisitor.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
/* Copyright (c) 2022 vesoft inc. All rights reserved.
*
* This source code is licensed under Apache 2.0 License.
*/

#ifndef NEBULA_GRAPH_DEDUCEALIASTYPEVISITOR_H
#define NEBULA_GRAPH_DEDUCEALIASTYPEVISITOR_H

#include "common/base/Status.h"
#include "common/datatypes/Value.h"
#include "common/expression/ExprVisitor.h"
#include "graph/context/ValidateContext.h"
#include "graph/context/ast/CypherAstContext.h"

namespace nebula {
namespace graph {

class QueryContext;

// An expression visitor enable deducing AliasType when possible.
class DeduceAliasTypeVisitor final : public ExprVisitor {
public:
DeduceAliasTypeVisitor(QueryContext *qctx,
ValidateContext *vctx,
GraphSpaceID space,
AliasType inputType);

~DeduceAliasTypeVisitor() = default;

bool ok() const {
return status_.ok();
}

Status status() && {
return std::move(status_);
}

AliasType outputType() const {
return outputType_;
}

private:
// Most expression cannot be used to deducing,
// or deduced to primitive types, use the default kRuntime type
void visit(ConstantExpression *) override {}
void visit(UnaryExpression *) override {}
void visit(TypeCastingExpression *) override {}
void visit(LabelExpression *) override {}
void visit(LabelAttributeExpression *) override {}
void visit(ArithmeticExpression *) override {}
void visit(RelationalExpression *) override {}
void visit(AttributeExpression *) override {}
void visit(LogicalExpression *) override {}
void visit(AggregateExpression *) override {}
void visit(UUIDExpression *) override {}
void visit(VariableExpression *) override {}
void visit(VersionedVariableExpression *) override {}
void visit(ListExpression *) override {}
void visit(SetExpression *) override {}
void visit(MapExpression *) override {}
void visit(LabelTagPropertyExpression *) override {}
void visit(TagPropertyExpression *) override {}
void visit(EdgePropertyExpression *) override {}
void visit(InputPropertyExpression *) override {}
void visit(VariablePropertyExpression *) override {}
void visit(DestPropertyExpression *) override {}
void visit(SourcePropertyExpression *) override {}
void visit(EdgeSrcIdExpression *) override {}
void visit(EdgeTypeExpression *) override {}
void visit(EdgeRankExpression *) override {}
void visit(EdgeDstIdExpression *) override {}
void visit(CaseExpression *) override {}
void visit(ColumnExpression *) override {}
void visit(PredicateExpression *) override {}
void visit(ListComprehensionExpression *) override {}
void visit(ReduceExpression *) override {}
void visit(MatchPathPatternExpression *) override {}

// Expression may have deducing potential
void visit(VertexExpression *expr) override;
void visit(EdgeExpression *expr) override;
void visit(PathBuildExpression *expr) override;
void visit(FunctionCallExpression *expr) override;
void visit(SubscriptExpression *expr) override;
void visit(SubscriptRangeExpression *expr) override;

private:
QueryContext *qctx_{nullptr};
ValidateContext *vctx_{nullptr};
GraphSpaceID space_;
Status status_;
AliasType inputType_{AliasType::kRuntime};
// Default output type is Runtime
AliasType outputType_{AliasType::kRuntime};
};

} // namespace graph
} // namespace nebula

#endif // NEBULA_GRAPH_DEDUCEALIASTYPEVISITOR_H
1 change: 1 addition & 0 deletions src/graph/visitor/test/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ nebula_add_test(
TestMain.cpp
FoldConstantExprVisitorTest.cpp
DeduceTypeVisitorTest.cpp
DeduceAliasTypeVisitorTest.cpp
RewriteUnaryNotExprVisitorTest.cpp
RewriteRelExprVisitorTest.cpp
FilterTransformTest.cpp
Expand Down
Loading

0 comments on commit c3ee460

Please sign in to comment.