Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refine AliasType deducing when planning #4973

Merged
merged 25 commits into from
Dec 7, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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;
}
codesigner marked this conversation as resolved.
Show resolved Hide resolved
}

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 {}
czpmango marked this conversation as resolved.
Show resolved Hide resolved
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 {}
Comment on lines +61 to +71
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

catalog needs to be introduced to enhance the inference of these expression types.
We can do that later.

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