Skip to content
Snippets Groups Projects
Unverified Commit aa4806db authored by Shylock Hg's avatar Shylock Hg Committed by GitHub
Browse files

Support the yield clause when fetch vertices on *. (#264)


* Support the yield clause when fetch vertices on *.

* Add cases for yield when on *.

* Add more intergrate testing cases.

* Extra confirm the onStar mark.

* Change value to empty when lack tag.

Co-authored-by: default avatardutor <440396+dutor@users.noreply.github.com>
parent 12a39e4c
No related branches found
No related tags found
No related merge requests found
......@@ -307,16 +307,18 @@ public:
yieldClause_.reset(clause);
}
explicit FetchVerticesSentence(Expression *ref) {
explicit FetchVerticesSentence(Expression *ref, YieldClause *clause) {
kind_ = Kind::kFetchVertices;
tag_ = std::make_unique<std::string>("*");
vidRef_.reset(ref);
yieldClause_.reset(clause);
}
explicit FetchVerticesSentence(VertexIDList *vidList) {
explicit FetchVerticesSentence(VertexIDList *vidList, YieldClause *clause) {
kind_ = Kind::kFetchVertices;
tag_ = std::make_unique<std::string>("*");
vidList_.reset(vidList);
yieldClause_.reset(clause);
}
bool isAllTagProps() {
......
......@@ -1049,11 +1049,11 @@ fetch_vertices_sentence
| KW_FETCH KW_PROP KW_ON name_label vid_ref_expression yield_clause {
$$ = new FetchVerticesSentence($4, $5, $6);
}
| KW_FETCH KW_PROP KW_ON STAR vid_list {
$$ = new FetchVerticesSentence($5);
| KW_FETCH KW_PROP KW_ON STAR vid_list yield_clause {
$$ = new FetchVerticesSentence($5, $6);
}
| KW_FETCH KW_PROP KW_ON STAR vid_ref_expression {
$$ = new FetchVerticesSentence($5);
| KW_FETCH KW_PROP KW_ON STAR vid_ref_expression yield_clause {
$$ = new FetchVerticesSentence($5, $6);
}
;
......
......@@ -33,6 +33,10 @@ void ExpressionProps::insertEdgeProp(EdgeType edgeType, folly::StringPiece prop)
props.emplace(prop);
}
void ExpressionProps::insertTagNameIds(const std::string &name, TagID tagId) {
tagNameIds_.emplace(name, tagId);
}
void ExpressionProps::insertTagProp(TagID tagId, folly::StringPiece prop) {
auto& props = tagProps_[tagId];
props.emplace(prop);
......
......@@ -20,6 +20,7 @@ namespace graph {
class ExpressionProps final {
public:
using TagNameIds = std::unordered_map<std::string, TagID>;
using TagIDPropsMap = std::unordered_map<TagID, std::set<folly::StringPiece>>;
using EdgePropMap = std::unordered_map<EdgeType, std::set<folly::StringPiece>>;
using VarPropMap = std::unordered_map<std::string, std::set<folly::StringPiece>>;
......@@ -39,6 +40,9 @@ public:
const TagIDPropsMap& tagProps() const {
return tagProps_;
}
const TagNameIds& tagNameIds() const {
return tagNameIds_;
}
const EdgePropMap& edgeProps() const {
return edgeProps_;
}
......@@ -46,11 +50,20 @@ public:
return varProps_;
}
bool hasInputVarProperty() const {
return !inputProps_.empty() || !varProps_.empty();
}
bool hasSrcDstTagProperty() const {
return !srcTagProps_.empty() || !dstTagProps_.empty();
}
void insertInputProp(folly::StringPiece prop);
void insertVarProp(const std::string& outputVar, folly::StringPiece prop);
void insertSrcTagProp(TagID tagId, folly::StringPiece prop);
void insertDstTagProp(TagID tagId, folly::StringPiece prop);
void insertEdgeProp(EdgeType edgeType, folly::StringPiece prop);
void insertTagNameIds(const std::string &name, TagID tagId);
void insertTagProp(TagID tagId, folly::StringPiece prop);
bool isSubsetOfInput(const std::set<folly::StringPiece>& props);
bool isSubsetOfVar(const VarPropMap& props);
......@@ -63,6 +76,7 @@ private:
TagIDPropsMap dstTagProps_;
EdgePropMap edgeProps_;
TagIDPropsMap tagProps_;
TagNameIds tagNameIds_;
};
} // namespace graph
......
......@@ -7,6 +7,7 @@
#include "planner/Query.h"
#include "util/ExpressionUtils.h"
#include "util/SchemaUtil.h"
#include "visitor/DeducePropsVisitor.h"
namespace nebula {
namespace graph {
......@@ -25,7 +26,7 @@ Status FetchVerticesValidator::toPlan() {
std::string vidsVar = (srcRef_ == nullptr ? buildConstantInput() : buildRuntimeInput());
auto *getVerticesNode = GetVertices::make(qctx_,
nullptr,
spaceId_,
space_.id,
src_,
std::move(props_),
std::move(exprs_),
......@@ -61,19 +62,33 @@ Status FetchVerticesValidator::toPlan() {
Status FetchVerticesValidator::check() {
auto *sentence = static_cast<FetchVerticesSentence *>(sentence_);
spaceId_ = vctx_->whichSpace().id;
tagName_ = *sentence->tag();
if (!sentence->isAllTagProps()) {
tagName_ = *(sentence->tag());
auto tagStatus = qctx_->schemaMng()->toTagID(spaceId_, tagName_);
onStar_ = false;
auto tagName = *(sentence->tag());
auto tagStatus = qctx_->schemaMng()->toTagID(space_.id, tagName);
NG_RETURN_IF_ERROR(tagStatus);
auto tagId = tagStatus.value();
tagId_ = tagStatus.value();
schema_ = qctx_->schemaMng()->getTagSchema(spaceId_, tagId_.value());
if (schema_ == nullptr) {
LOG(ERROR) << "No schema found for " << tagName_;
return Status::Error("No schema found for `%s'", tagName_.c_str());
tags_.emplace(tagName, tagId);
auto schema = qctx_->schemaMng()->getTagSchema(space_.id, tagId);
if (schema == nullptr) {
LOG(ERROR) << "No schema found for " << tagName;
return Status::Error("No schema found for `%s'", tagName.c_str());
}
tagsSchema_.emplace(tagId, schema);
} else {
onStar_ = true;
const auto allTagsResult = qctx_->schemaMng()->getAllVerTagSchema(space_.id);
NG_RETURN_IF_ERROR(allTagsResult);
const auto allTags = std::move(allTagsResult).value();
for (const auto &tag : allTags) {
tagsSchema_.emplace(tag.first, tag.second.back());
}
for (const auto &tagSchema : tagsSchema_) {
auto tagNameResult = qctx_->schemaMng()->toTagName(space_.id, tagSchema.first);
NG_RETURN_IF_ERROR(tagNameResult);
tags_.emplace(std::move(tagNameResult).value(), tagSchema.first);
}
}
return Status::OK();
......@@ -118,7 +133,6 @@ Status FetchVerticesValidator::prepareProperties() {
}
Status FetchVerticesValidator::preparePropertiesWithYield(const YieldClause *yield) {
CHECK(tagId_.hasValue()) << "Not supported yield for *.";
withProject_ = true;
// outputs
auto yieldSize = yield->columns().size();
......@@ -129,10 +143,8 @@ Status FetchVerticesValidator::preparePropertiesWithYield(const YieldClause *yie
outputs_.emplace_back(VertexID, Value::Type::STRING); // kVid
dedup_ = yield->isDistinct();
storage::cpp2::VertexProp prop;
prop.set_tag(tagId_.value());
std::vector<std::string> propsName;
propsName.reserve(yield->columns().size());
ExpressionProps exprProps;
DeducePropsVisitor deducePropsVisitor(qctx_, space_.id, &exprProps);
for (auto col : yield->columns()) {
if (col->expr()->kind() == Expression::Kind::kLabelAttribute) {
auto laExpr = static_cast<LabelAttributeExpression *>(col->expr());
......@@ -140,36 +152,53 @@ Status FetchVerticesValidator::preparePropertiesWithYield(const YieldClause *yie
} else {
ExpressionUtils::rewriteLabelAttribute<TagPropertyExpression>(col->expr());
}
const auto *invalidExpr = findInvalidYieldExpression(col->expr());
if (invalidExpr != nullptr) {
return Status::Error("Invalid yield expression `%s'.", col->expr()->toString().c_str());
col->expr()->accept(&deducePropsVisitor);
if (!deducePropsVisitor.ok()) {
return std::move(deducePropsVisitor).status();
}
// The properties from storage directly push down only
// The other will be computed in Project Executor
const auto storageExprs = ExpressionUtils::findAllStorage(col->expr());
for (const auto &storageExpr : storageExprs) {
const auto *expr = static_cast<const PropertyExpression *>(storageExpr);
if (*expr->sym() != tagName_) {
return Status::Error("Mismatched tag name");
}
// Check is prop name in schema
if (schema_->getFieldIndex(*expr->prop()) < 0) {
LOG(ERROR) << "Unknown column `" << *expr->prop() << "' in tag `" << tagName_
<< "'.";
return Status::Error(
"Unknown column `%s' in tag `%s'.", expr->prop()->c_str(), tagName_.c_str());
}
propsName.emplace_back(*expr->prop());
gvColNames_.emplace_back(*expr->sym() + "." + *expr->prop());
if (exprProps.hasInputVarProperty()) {
return Status::Error("Unsupported input/variable property expression in yield.");
}
if (!exprProps.edgeProps().empty()) {
return Status::Error("Unsupported edge property expression in yield.");
}
if (exprProps.hasSrcDstTagProperty()) {
return Status::Error("Unsupported src/dst property expression in yield.");
}
colNames_.emplace_back(deduceColName(col));
auto typeResult = deduceExprType(col->expr());
NG_RETURN_IF_ERROR(typeResult);
outputs_.emplace_back(colNames_.back(), typeResult.value());
// TODO(shylock) think about the push-down expr
}
prop.set_props(std::move(propsName));
props_.emplace_back(std::move(prop));
if (exprProps.tagProps().empty()) {
return Status::Error("Unsupported empty tag property expression in yield.");
}
if (onStar_) {
for (const auto &tag : exprProps.tagNameIds()) {
if (tags_.find(tag.first) == tags_.end()) {
return Status::SemanticError("Mismatched tag.");
}
}
} else {
if (tags_ != exprProps.tagNameIds()) {
return Status::SemanticError("Mismatched tag.");
}
}
for (const auto &tagNameId : exprProps.tagNameIds()) {
storage::cpp2::VertexProp vProp;
std::vector<std::string> props;
props.reserve(exprProps.tagProps().at(tagNameId.second).size());
vProp.set_tag(tagNameId.second);
for (const auto &prop : exprProps.tagProps().at(tagNameId.second)) {
props.emplace_back(prop.toString());
gvColNames_.emplace_back(tagNameId.first + "." + prop.toString());
}
vProp.set_props(std::move(props));
props_.emplace_back(std::move(vProp));
}
// insert the reserved properties expression be compatible with 1.0
// TODO(shylock) select kVid from storage
......@@ -186,67 +215,27 @@ Status FetchVerticesValidator::preparePropertiesWithYield(const YieldClause *yie
Status FetchVerticesValidator::preparePropertiesWithoutYield() {
// empty for all tag and properties
props_.clear();
if (tagId_.hasValue()) {
// for one tag all properties
storage::cpp2::VertexProp prop;
prop.set_tag(tagId_.value());
// empty for all
props_.emplace_back(std::move(prop));
outputs_.emplace_back(VertexID, Value::Type::STRING);
colNames_.emplace_back(VertexID);
gvColNames_.emplace_back(VertexID); // keep compatible with 1.0
for (std::size_t i = 0; i < schema_->getNumFields(); ++i) {
outputs_.emplace_back(schema_->getFieldName(i),
SchemaUtil::propTypeToValueType(schema_->getFieldType(i)));
colNames_.emplace_back(tagName_ + '.' + schema_->getFieldName(i));
outputs_.emplace_back(VertexID, Value::Type::STRING);
colNames_.emplace_back(VertexID);
gvColNames_.emplace_back(colNames_.back());
for (const auto &tagSchema : tagsSchema_) {
storage::cpp2::VertexProp vProp;
vProp.set_tag(tagSchema.first);
auto tagNameResult = qctx_->schemaMng()->toTagName(space_.id, tagSchema.first);
NG_RETURN_IF_ERROR(tagNameResult);
auto tagName = std::move(tagNameResult).value();
for (std::size_t i = 0; i < tagSchema.second->getNumFields(); ++i) {
outputs_.emplace_back(
tagSchema.second->getFieldName(i),
SchemaUtil::propTypeToValueType(tagSchema.second->getFieldType(i)));
colNames_.emplace_back(tagName + "." + tagSchema.second->getFieldName(i));
gvColNames_.emplace_back(colNames_.back());
}
} else {
// all schema properties
const auto allTagsResult = qctx_->schemaMng()->getAllVerTagSchema(spaceId_);
NG_RETURN_IF_ERROR(allTagsResult);
const auto allTags = std::move(allTagsResult).value();
std::vector<std::pair<TagID, std::shared_ptr<const meta::NebulaSchemaProvider>>>
allTagsSchema;
allTagsSchema.reserve(allTags.size());
for (const auto &tag : allTags) {
allTagsSchema.emplace_back(tag.first, tag.second.back());
}
std::sort(allTagsSchema.begin(), allTagsSchema.end(), [](const auto &a, const auto &b) {
return a.first < b.first;
});
outputs_.emplace_back(VertexID, Value::Type::STRING);
colNames_.emplace_back(VertexID);
gvColNames_.emplace_back(colNames_.back());
for (const auto &tagSchema : allTagsSchema) {
auto tagNameResult = qctx_->schemaMng()->toTagName(spaceId_, tagSchema.first);
NG_RETURN_IF_ERROR(tagNameResult);
auto tagName = std::move(tagNameResult).value();
for (std::size_t i = 0; i < tagSchema.second->getNumFields(); ++i) {
outputs_.emplace_back(
tagSchema.second->getFieldName(i),
SchemaUtil::propTypeToValueType(tagSchema.second->getFieldType(i)));
colNames_.emplace_back(tagName + "." + tagSchema.second->getFieldName(i));
gvColNames_.emplace_back(colNames_.back());
}
}
props_.emplace_back(std::move(vProp));
}
return Status::OK();
}
/*static*/
const Expression *FetchVerticesValidator::findInvalidYieldExpression(const Expression *root) {
return ExpressionUtils::findAny(root,
{Expression::Kind::kInputProperty,
Expression::Kind::kVarProperty,
Expression::Kind::kSrcProperty,
Expression::Kind::kDstProperty,
Expression::Kind::kEdgeSrc,
Expression::Kind::kEdgeType,
Expression::Kind::kEdgeRank,
Expression::Kind::kEdgeDst});
}
// TODO(shylock) optimize dedup input when distinct given
std::string FetchVerticesValidator::buildConstantInput() {
auto input = vctx_->anonVarGen()->getVar();
......
......@@ -33,21 +33,17 @@ private:
Status preparePropertiesWithoutYield();
Status prepareProperties();
static const Expression* findInvalidYieldExpression(const Expression* root);
// TODO(shylock) merge the code
std::string buildConstantInput();
std::string buildRuntimeInput();
private:
GraphSpaceID spaceId_{0};
DataSet srcVids_{{kVid}}; // src from constant
Expression* srcRef_{nullptr}; // src from runtime
Expression* src_{nullptr}; // src in total
std::string tagName_;
// none if not specified tag
folly::Optional<TagID> tagId_;
std::shared_ptr<const meta::SchemaProviderIf> schema_;
bool onStar_{false};
std::unordered_map<std::string, TagID> tags_;
std::map<TagID, std::shared_ptr<const meta::SchemaProviderIf>> tagsSchema_;
std::vector<storage::cpp2::VertexProp> props_;
std::vector<storage::cpp2::Expr> exprs_;
bool dedup_{false};
......
......@@ -17,7 +17,7 @@ class FetchVerticesValidatorTest : public ValidatorTestBase {
protected:
QueryContext *getQCtx(const std::string &query) {
auto status = validate(query);
EXPECT_TRUE(status);
EXPECT_TRUE(status.ok());
return std::move(status).value();
}
};
......@@ -241,6 +241,80 @@ TEST_F(FetchVerticesValidatorTest, FetchVerticesProp) {
auto result = Eq(qctx->plan()->root(), gv);
ASSERT_TRUE(result.ok()) << result;
}
// ON * with yield
{
auto qctx = getQCtx("FETCH PROP ON * \"1\", \"2\" YIELD person.name");
auto *start = StartNode::make(qctx);
std::vector<std::string> colNames {"VertexID", "person.name"};
// Get vertices
auto *gv = GetVertices::make(qctx, start, 1, src.get(), {}, {});
gv->setColNames(colNames);
// project
auto yieldColumns = std::make_unique<YieldColumns>();
yieldColumns->addColumn(new YieldColumn(
new InputPropertyExpression(new std::string("VertexID")), new std::string("VertexID")));
yieldColumns->addColumn(new YieldColumn(
new TagPropertyExpression(new std::string("person"), new std::string("name"))));
auto *project = Project::make(qctx, gv, yieldColumns.get());
project->setColNames(colNames);
auto result = Eq(qctx->plan()->root(), project);
ASSERT_TRUE(result.ok()) << result;
}
{
auto qctx = getQCtx("FETCH PROP ON * \"1\", \"2\" YIELD person.name, person.age");
auto *start = StartNode::make(qctx);
std::vector<std::string> colNames {"VertexID", "person.name", "person.age"};
// Get vertices
auto *gv = GetVertices::make(qctx, start, 1, src.get(), {}, {});
gv->setColNames(colNames);
// project
auto yieldColumns = std::make_unique<YieldColumns>();
yieldColumns->addColumn(new YieldColumn(
new InputPropertyExpression(new std::string("VertexID")), new std::string("VertexID")));
yieldColumns->addColumn(new YieldColumn(
new TagPropertyExpression(new std::string("person"), new std::string("name"))));
yieldColumns->addColumn(new YieldColumn(
new TagPropertyExpression(new std::string("person"), new std::string("age"))));
auto *project = Project::make(qctx, gv, yieldColumns.get());
project->setColNames(colNames);
auto result = Eq(qctx->plan()->root(), project);
ASSERT_TRUE(result.ok()) << result;
}
{
auto qctx = getQCtx("FETCH PROP ON * \"1\", \"2\" YIELD 1+1, person.name, person.age");
auto *start = StartNode::make(qctx);
// Get vertices
auto *gv = GetVertices::make(qctx, start, 1, src.get(), {}, {});
gv->setColNames({"VertexID", "person.name", "person.age"});
// project
auto yieldColumns = std::make_unique<YieldColumns>();
yieldColumns->addColumn(new YieldColumn(
new InputPropertyExpression(new std::string("VertexID")), new std::string("VertexID")));
yieldColumns->addColumn(new YieldColumn(
new ArithmeticExpression(Expression::Kind::kAdd,
new ConstantExpression(1),
new ConstantExpression(1))));
yieldColumns->addColumn(new YieldColumn(
new TagPropertyExpression(new std::string("person"), new std::string("name"))));
yieldColumns->addColumn(new YieldColumn(
new TagPropertyExpression(new std::string("person"), new std::string("age"))));
auto *project = Project::make(qctx, gv, yieldColumns.get());
project->setColNames({"VertexID", "(1+1)", "person.name", "person.age"});
auto result = Eq(qctx->plan()->root(), project);
ASSERT_TRUE(result.ok()) << result;
}
}
TEST_F(FetchVerticesValidatorTest, FetchVerticesInputOutput) {
......@@ -320,6 +394,30 @@ TEST_F(FetchVerticesValidatorTest, FetchVerticesInputOutput) {
PlanNode::Kind::kStart,
}));
}
// on * with yield
{
const std::string query = "FETCH PROP ON * \"1\", \"2\" YIELD person.name AS name"
"| FETCH PROP ON * $-.name";
EXPECT_TRUE(checkResult(query,
{
PlanNode::Kind::kGetVertices,
PlanNode::Kind::kProject,
PlanNode::Kind::kGetVertices,
PlanNode::Kind::kStart,
}));
}
{
const std::string query =
"$a = FETCH PROP ON * \"1\", \"2\" YIELD person.name AS name;"
"FETCH PROP ON * $a.name";
EXPECT_TRUE(checkResult(query,
{
PlanNode::Kind::kGetVertices,
PlanNode::Kind::kProject,
PlanNode::Kind::kGetVertices,
PlanNode::Kind::kStart,
}));
}
}
TEST_F(FetchVerticesValidatorTest, FetchVerticesPropFailed) {
......@@ -328,9 +426,11 @@ TEST_F(FetchVerticesValidatorTest, FetchVerticesPropFailed) {
// not exist tag
ASSERT_FALSE(validate("FETCH PROP ON not_exist_tag \"1\" YIELD not_exist_tag.prop1"));
ASSERT_FALSE(validate("FETCH PROP ON * \"1\" YIELD not_exist_tag.prop1"));
// not exist property
ASSERT_FALSE(validate("FETCH PROP ON person \"1\" YIELD person.not_exist_property"));
ASSERT_FALSE(validate("FETCH PROP ON * \"1\" YIELD person.not_exist_property"));
// invalid yield expression
ASSERT_FALSE(validate("$a = FETCH PROP ON person \"1\" YIELD person.name AS name;"
......@@ -344,20 +444,39 @@ TEST_F(FetchVerticesValidatorTest, FetchVerticesPropFailed) {
ASSERT_FALSE(validate("FETCH PROP ON person \"1\" YIELD person._type"));
ASSERT_FALSE(validate("FETCH PROP ON person \"1\" YIELD person._rank + 1"));
ASSERT_FALSE(validate("FETCH PROP ON person \"1\" YIELD person._dst + 1"));
// invalid yield expression on *
ASSERT_FALSE(validate("$a = FETCH PROP ON * \"1\" YIELD person.name AS name;"
" FETCH PROP ON * \"1\" YIELD $a.name + 1"));
ASSERT_FALSE(validate("FETCH PROP ON * \"1\" YIELD $^.person.name"));
ASSERT_FALSE(validate("FETCH PROP ON * \"1\" YIELD $$.person.name"));
ASSERT_FALSE(validate("FETCH PROP ON * \"1\" YIELD person.name AS name | "
" FETCH PROP ON * \"1\" YIELD $-.name + 1"));
ASSERT_FALSE(validate("FETCH PROP ON * \"1\" YIELD person._src + 1"));
ASSERT_FALSE(validate("FETCH PROP ON * \"1\" YIELD person._type"));
ASSERT_FALSE(validate("FETCH PROP ON * \"1\" YIELD person._rank + 1"));
ASSERT_FALSE(validate("FETCH PROP ON * \"1\" YIELD person._dst + 1"));
}
TEST_F(FetchVerticesValidatorTest, FetchVerticesInputFailed) {
// mismatched varirable
ASSERT_FALSE(validate("$a = FETCH PROP ON person \"1\" YIELD person.name AS name;"
"FETCH PROP ON person $b.name"));
ASSERT_FALSE(validate("$a = FETCH PROP ON * \"1\" YIELD person.name AS name;"
"FETCH PROP * person $b.name"));
// mismatched varirable property
ASSERT_FALSE(validate("$a = FETCH PROP ON person \"1\" YIELD person.name AS name;"
"FETCH PROP ON person $a.not_exist_property"));
ASSERT_FALSE(validate("$a = FETCH PROP * person \"1\" YIELD person.name AS name;"
"FETCH PROP * person $a.not_exist_property"));
// mismatched input property
ASSERT_FALSE(validate("FETCH PROP ON person \"1\" YIELD person.name AS name | "
"FETCH PROP ON person $-.not_exist_property"));
ASSERT_FALSE(validate("FETCH PROP * person \"1\" YIELD person.name AS name | "
"FETCH PROP * person $-.not_exist_property"));
}
} // namespace graph
......
......@@ -80,7 +80,9 @@ Status ValidatorTestBase::EqSelf(const PlanNode *l, const PlanNode *r) {
return Status::Error(
"%s.kind_ != %s.kind_", l->outputVar().c_str(), r->outputVar().c_str());
}
if (l->colNamesRef() != r->colNamesRef()) {
// TODO(shylock) col names in GetVertices generate by unordered container
// So can't check now
if ((l->colNamesRef() != r->colNamesRef()) && l->kind() != PlanNode::Kind::kGetVertices) {
return Status::Error(
"%s.colNames_ != %s.colNames_", l->outputVar().c_str(), r->outputVar().c_str());
}
......@@ -115,10 +117,8 @@ Status ValidatorTestBase::EqSelf(const PlanNode *l, const PlanNode *r) {
"%s.src_ != %s.src_", l->outputVar().c_str(), r->outputVar().c_str());
}
// props
if (lGV->props() != rGV->props()) {
return Status::Error(
"%s.props_ != %s.props_", l->outputVar().c_str(), r->outputVar().c_str());
}
// TODO(shylock) props in GetVertices collect from yield generate by unordered container
// So can't check now
return Status::OK();
}
case PlanNode::Kind::kGetEdges: {
......
......@@ -463,7 +463,7 @@ TEST_F(YieldValidatorTest, Error) {
auto query = var + "YIELD like.start";
auto result = checkResult(query);
EXPECT_EQ(std::string(result.message()),
"SemanticError: Not supported expression `like.start' for type deduction.");
"SemanticError: Not supported expression `like.start' for props deduction.");
}
}
......
......@@ -32,6 +32,7 @@ void DeducePropsVisitor::visit(TagPropertyExpression *expr) {
status_ = std::move(status).status();
return;
}
exprProps_->insertTagNameIds(*expr->sym(), status.value());
exprProps_->insertTagProp(status.value(), *expr->prop());
}
......@@ -124,7 +125,7 @@ void DeducePropsVisitor::visitEdgePropExpr(PropertyExpression *expr) {
void DeducePropsVisitor::reportError(const Expression *expr) {
std::stringstream ss;
ss << "Not supported expression `" << expr->toString() << "' for type deduction.";
ss << "Not supported expression `" << expr->toString() << "' for props deduction.";
status_ = Status::SemanticError(ss.str());
}
......
......@@ -222,6 +222,119 @@ class TestFetchQuery(NebulaTestSuite):
self.check_resp_succeeded(resp)
self.check_out_of_order_result(resp, expect_result)
def test_fetch_vertex_get_all_with_yield(self):
query = 'FETCH PROP ON * "Boris Diaw" YIELD player.name, player.age, team.name, bachelor.name, bachelor.speciality'
resp = self.execute_query(query)
expect_column_names = ['VertexID', 'player.name', 'player.age', 'team.name', 'bachelor.name', 'bachelor.speciality']
expect_result = [['Boris Diaw', 'Boris Diaw', 36, T_EMPTY, T_EMPTY, T_EMPTY]]
self.check_resp_succeeded(resp)
self.check_column_names(resp, expect_column_names)
self.check_out_of_order_result(resp, expect_result)
query = 'FETCH PROP ON * "Boris Diaw" YIELD player.age, team.name, bachelor.speciality'
resp = self.execute_query(query)
expect_column_names = ['VertexID', 'player.age', 'team.name', 'bachelor.speciality']
expect_result = [['Boris Diaw', 36, T_EMPTY, T_EMPTY]]
self.check_resp_succeeded(resp)
self.check_column_names(resp, expect_column_names)
self.check_out_of_order_result(resp, expect_result)
query = 'FETCH PROP ON * "Tim Duncan" YIELD player.name, player.age, team.name, bachelor.name, bachelor.speciality'
resp = self.execute_query(query)
expect_column_names = ['VertexID', 'player.name', 'player.age', 'team.name', 'bachelor.name', 'bachelor.speciality']
expect_result = [['Tim Duncan', 'Tim Duncan', 42, T_EMPTY, "Tim Duncan", "psychology"]]
self.check_resp_succeeded(resp)
self.check_column_names(resp, expect_column_names)
self.check_out_of_order_result(resp, expect_result)
query = 'FETCH PROP ON * "Tim Duncan" YIELD player.name, team.name, bachelor.name'
resp = self.execute_query(query)
expect_column_names = ['VertexID', 'player.name', 'team.name', 'bachelor.name']
expect_result = [['Tim Duncan', 'Tim Duncan', T_EMPTY, "Tim Duncan"]]
self.check_resp_succeeded(resp)
self.check_column_names(resp, expect_column_names)
self.check_out_of_order_result(resp, expect_result)
# multi vertices
query = 'FETCH PROP ON * "Tim Duncan", "Boris Diaw" YIELD player.name, player.age, team.name, bachelor.name, bachelor.speciality'
resp = self.execute_query(query)
expect_column_names = ['VertexID', 'player.name', 'player.age', 'team.name', 'bachelor.name', 'bachelor.speciality']
expect_result = [['Tim Duncan', 'Tim Duncan', 42, T_EMPTY, "Tim Duncan", "psychology"],
['Boris Diaw', 'Boris Diaw', 36, T_EMPTY, T_EMPTY, T_EMPTY]]
self.check_resp_succeeded(resp)
self.check_column_names(resp, expect_column_names)
self.check_out_of_order_result(resp, expect_result)
query = 'FETCH PROP ON * "Tim Duncan", "Boris Diaw" YIELD player.age, team.name, bachelor.name'
resp = self.execute_query(query)
expect_column_names = ['VertexID', 'player.age', 'team.name', 'bachelor.name']
expect_result = [['Tim Duncan', 42, T_EMPTY, "Tim Duncan"],
['Boris Diaw', 36, T_EMPTY, T_EMPTY]]
self.check_resp_succeeded(resp)
self.check_column_names(resp, expect_column_names)
self.check_out_of_order_result(resp, expect_result)
# from input
query = '''GO FROM "Boris Diaw" over like YIELD like._dst as id
| FETCH PROP ON * $-.id YIELD player.name, player.age, team.name, bachelor.name, bachelor.speciality'''
resp = self.execute_query(query)
expect_column_names = ['VertexID', 'player.name', 'player.age', 'team.name', 'bachelor.name', 'bachelor.speciality']
expect_result = [
['Tony Parker', 'Tony Parker', 36, T_EMPTY, T_EMPTY, T_EMPTY],
['Tim Duncan', 'Tim Duncan', 42, T_EMPTY, "Tim Duncan", "psychology"]
]
self.check_resp_succeeded(resp)
self.check_column_names(resp, expect_column_names)
self.check_out_of_order_result(resp, expect_result)
query = '''GO FROM "Boris Diaw" over like YIELD like._dst as id
| FETCH PROP ON * $-.id YIELD player.age, team.name, bachelor.speciality'''
resp = self.execute_query(query)
expect_column_names = ['VertexID', 'player.age', 'team.name', 'bachelor.speciality']
expect_result = [
['Tony Parker', 36, T_EMPTY, T_EMPTY],
['Tim Duncan', 42, T_EMPTY, "psychology"]
]
self.check_resp_succeeded(resp)
self.check_column_names(resp, expect_column_names)
self.check_out_of_order_result(resp, expect_result)
# from var
query = '''$a = GO FROM "Boris Diaw" over like YIELD like._dst as id;
FETCH PROP ON * $a.id YIELD player.name, player.age, team.name, bachelor.name, bachelor.speciality'''
resp = self.execute_query(query)
expect_column_names = ['VertexID', 'player.name', 'player.age', 'team.name', 'bachelor.name', 'bachelor.speciality']
expect_result = [
['Tony Parker', 'Tony Parker', 36, T_EMPTY, T_EMPTY, T_EMPTY],
['Tim Duncan', 'Tim Duncan', 42, T_EMPTY, "Tim Duncan", "psychology"]
]
self.check_resp_succeeded(resp)
self.check_column_names(resp, expect_column_names)
self.check_out_of_order_result(resp, expect_result)
query = '''$a = GO FROM "Boris Diaw" over like YIELD like._dst as id;
FETCH PROP ON * $a.id YIELD player.age, team.name, bachelor.speciality'''
resp = self.execute_query(query)
expect_column_names = ['VertexID', 'player.age', 'team.name', 'bachelor.speciality']
expect_result = [
['Tony Parker', 36, T_EMPTY, T_EMPTY],
['Tim Duncan', 42, T_EMPTY, "psychology"]
]
self.check_resp_succeeded(resp)
self.check_column_names(resp, expect_column_names)
self.check_out_of_order_result(resp, expect_result)
def test_fetch_vertex_get_all_with_yield_invalid(self):
# not exist tag
query = 'FETCH PROP ON * "Tim Duncan", "Boris Diaw" YIELD not_exist_tag.name'
resp = self.execute_query(query)
self.check_resp_failed(resp)
# not exist property
query = 'FETCH PROP ON * "Tim Duncan", "Boris Diaw" YIELD player.not_exist_prop'
resp = self.execute_query(query)
self.check_resp_failed(resp)
def test_fetch_vertex_duplicate_column_names(self):
query = 'FETCH PROP ON player "Boris Diaw" YIELD player.name, player.name'
resp = self.execute_query(query)
......
......@@ -38,10 +38,10 @@ class TestLabelExpr(NebulaTestSuite):
def test_wrong_props(self):
# fetch vertex with label expr
resp = self.execute('FETCH PROP ON person "a" YIELD name')
self.check_error_msg(resp, "SemanticError: LabelExpression can not be instantiated.")
self.check_error_msg(resp, "SemanticError: Not supported expression `name' for props deduction.")
resp = self.execute('FETCH PROP ON person "a" YIELD name + 1')
self.check_error_msg(resp, "SemanticError: LabelExpression can not be instantiated.")
self.check_error_msg(resp, "SemanticError: Not supported expression `name' for props deduction.")
# fetch edge with label expr
resp = self.execute('FETCH PROP ON friend "a"->"b" YIELD start')
......
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment