Skip to content
Snippets Groups Projects
Unverified Commit 3207041d authored by bright-starry-sky's avatar bright-starry-sky Committed by GitHub
Browse files

Optimizer rule for index scan (#272)

* optimizer_rule_index

* added test cases

* Addressed yee's comments

* Addressed CPWstatic's comments

* To improve the FilterItems from tuple to struct

* fixed column def error;  improved error message.

* Improve code

* Resolved conflict

* Removed useless code

* LabelAttributeExpression rewrite
parent 50061c0e
No related branches found
No related tags found
No related merge requests found
......@@ -11,6 +11,7 @@ nebula_add_library(
OptGroup.cpp
OptRule.cpp
rule/PushFilterDownGetNbrsRule.cpp
rule/IndexScanRule.cpp
)
nebula_add_subdirectory(test)
This diff is collapsed.
/* Copyright (c) 2020 vesoft inc. All rights reserved.
*
* This source code is licensed under Apache 2.0 License,
* attached with Common Clause Condition 1.0, found in the LICENSES directory.
*/
#ifndef OPTIMIZER_INDEXSCANRULE_H_
#define OPTIMIZER_INDEXSCANRULE_H_
#include "optimizer/OptRule.h"
#include "planner/Query.h"
#include "optimizer/OptimizerUtils.h"
namespace nebula {
namespace graph {
class IndexScan;
} // namespace graph
namespace opt {
using graph::IndexScan;
using graph::OptimizerUtils;
using graph::PlanNode;
using graph::QueryContext;
using storage::cpp2::IndexQueryContext;
using storage::cpp2::IndexColumnHint;
using BVO = graph::OptimizerUtils::BoundValueOperator;
using IndexItem = std::shared_ptr<meta::cpp2::IndexItem>;
using IndexQueryCtx = std::unique_ptr<std::vector<IndexQueryContext>>;
class IndexScanRule final : public OptRule {
FRIEND_TEST(IndexScanRuleTest, BoundValueTest);
FRIEND_TEST(IndexScanRuleTest, IQCtxTest);
public:
static std::unique_ptr<OptRule> kInstance;
bool match(const OptGroupExpr *groupExpr) const override;
Status transform(graph::QueryContext *qctx,
const OptGroupExpr *groupExpr,
TransformResult *result) const override;
std::string toString() const override;
private:
struct ScanKind {
enum class Kind {
UNKNOWN = 0,
LOGICAL_OR,
LOGICAL_AND,
};
private:
Kind kind_;
public:
ScanKind() {
kind_ = Kind::UNKNOWN;
}
void setKind(Kind k) {
kind_ = k;
}
Kind getKind() {
return kind_;
}
bool isLogicalAnd() {
return kind_ == Kind::LOGICAL_AND;
}
};
// col_ : index column name
// relOP_ : Relational operator , for example c1 > 1 , the relOP_ == kRelGT
// 1 > c1 , the relOP_ == kRelLT
// value_ : Constant value. from ConstantExpression.
struct FilterItem {
std::string col_;
RelationalExpression::Kind relOP_;
Value value_;
FilterItem(const std::string& col,
RelationalExpression::Kind relOP,
const Value& value)
: col_(col)
, relOP_(relOP)
, value_(value) {}
};
// FilterItems used for optimal index fetch and index scan context optimize.
// for example : where c1 > 1 and c1 < 2 , the FilterItems should be :
// {c1, kRelGT, 1} , {c1, kRelLT, 2}
struct FilterItems {
std::vector<FilterItem> items;
FilterItems() {}
explicit FilterItems(const std::vector<FilterItem>& i) {
items = i;
}
void addItem(const std::string& field,
RelationalExpression::Kind kind,
const Value& v) {
items.emplace_back(FilterItem(field, kind, v));
}
};
IndexScanRule();
Status createIndexQueryCtx(IndexQueryCtx &iqctx,
ScanKind kind,
const FilterItems& items,
graph::QueryContext *qctx,
const OptGroupExpr *groupExpr) const;
Status createIQCWithLogicAnd(IndexQueryCtx &iqctx,
const FilterItems& items,
graph::QueryContext *qctx,
const OptGroupExpr *groupExpr) const;
Status createIQCWithLogicOR(IndexQueryCtx &iqctx,
const FilterItems& items,
graph::QueryContext *qctx,
const OptGroupExpr *groupExpr) const;
Status appendIQCtx(const IndexItem& index,
const FilterItems& items,
IndexQueryCtx &iqctx) const;
Status appendColHint(std::vector<IndexColumnHint>& hitns,
const FilterItems& items,
const meta::cpp2::ColumnDef& col) const;
Status boundValue(const FilterItem& item,
const meta::cpp2::ColumnDef& col,
Value& begin, Value& end) const;
IndexScan* cloneIndexScan(graph::QueryContext *qctx, const OptGroupExpr *groupExpr) const;
bool isEdge(const OptGroupExpr *groupExpr) const;
int32_t schemaId(const OptGroupExpr *groupExpr) const;
GraphSpaceID spaceId(const OptGroupExpr *groupExpr) const;
std::unique_ptr<Expression> filterExpr(const OptGroupExpr *groupExpr) const;
Status analyzeExpression(Expression* expr, FilterItems* items,
ScanKind* kind, bool isEdge) const;
template <typename E,
typename = std::enable_if_t<std::is_same<E, EdgePropertyExpression>::value ||
std::is_same<E, TagPropertyExpression>::value>>
Status addFilterItem(RelationalExpression* expr, FilterItems* items) const;
Expression::Kind reverseRelationalExprKind(Expression::Kind kind) const;
IndexItem findOptimalIndex(graph::QueryContext *qctx,
const OptGroupExpr *groupExpr,
const FilterItems& items) const;
std::vector<IndexItem>
allIndexesBySchema(graph::QueryContext *qctx, const OptGroupExpr *groupExpr) const;
std::vector<IndexItem> findValidIndex(graph::QueryContext *qctx,
const OptGroupExpr *groupExpr,
const FilterItems& items) const;
std::vector<IndexItem> findIndexForEqualScan(const std::vector<IndexItem>& indexes,
const FilterItems& items) const;
std::vector<IndexItem> findIndexForRangeScan(const std::vector<IndexItem>& indexes,
const FilterItems& items) const;
};
} // namespace opt
} // namespace nebula
#endif // OPTIMIZER_INDEXSCANRULE_H_
# Copyright (c) 2020 vesoft inc. All rights reserved.
#
# This source code is licensed under Apache 2.0 License,
# attached with Common Clause Condition 1.0, found in the LICENSES directory.
#
set(OPTIMIZER_TEST_LIB
$<TARGET_OBJECTS:common_base_obj>
$<TARGET_OBJECTS:common_concurrent_obj>
$<TARGET_OBJECTS:common_datatypes_obj>
$<TARGET_OBJECTS:common_expression_obj>
$<TARGET_OBJECTS:common_function_manager_obj>
$<TARGET_OBJECTS:common_time_obj>
$<TARGET_OBJECTS:common_time_function_obj>
$<TARGET_OBJECTS:common_meta_thrift_obj>
$<TARGET_OBJECTS:common_meta_client_obj>
$<TARGET_OBJECTS:common_meta_obj>
$<TARGET_OBJECTS:common_storage_thrift_obj>
$<TARGET_OBJECTS:common_graph_thrift_obj>
$<TARGET_OBJECTS:common_conf_obj>
$<TARGET_OBJECTS:common_fs_obj>
$<TARGET_OBJECTS:common_thrift_obj>
$<TARGET_OBJECTS:common_common_thrift_obj>
$<TARGET_OBJECTS:common_thread_obj>
$<TARGET_OBJECTS:common_file_based_cluster_id_man_obj>
$<TARGET_OBJECTS:common_charset_obj>
$<TARGET_OBJECTS:common_encryption_obj>
$<TARGET_OBJECTS:common_http_client_obj>
$<TARGET_OBJECTS:common_process_obj>
$<TARGET_OBJECTS:common_agg_function_obj>
$<TARGET_OBJECTS:idgenerator_obj>
$<TARGET_OBJECTS:expr_visitor_obj>
$<TARGET_OBJECTS:session_obj>
$<TARGET_OBJECTS:graph_auth_obj>
$<TARGET_OBJECTS:graph_flags_obj>
$<TARGET_OBJECTS:util_obj>
$<TARGET_OBJECTS:planner_obj>
$<TARGET_OBJECTS:parser_obj>
$<TARGET_OBJECTS:context_obj>
$<TARGET_OBJECTS:validator_obj>
$<TARGET_OBJECTS:optimizer_obj>
)
nebula_add_test(
NAME
index_bound_value_test
SOURCES
IndexBoundValueTest.cpp
OBJECTS
$<TARGET_OBJECTS:common_base_obj>
$<TARGET_OBJECTS:common_concurrent_obj>
$<TARGET_OBJECTS:common_datatypes_obj>
$<TARGET_OBJECTS:common_expression_obj>
$<TARGET_OBJECTS:common_function_manager_obj>
$<TARGET_OBJECTS:common_time_obj>
$<TARGET_OBJECTS:common_time_function_obj>
$<TARGET_OBJECTS:common_meta_thrift_obj>
$<TARGET_OBJECTS:common_meta_client_obj>
$<TARGET_OBJECTS:common_meta_obj>
$<TARGET_OBJECTS:common_storage_thrift_obj>
$<TARGET_OBJECTS:common_graph_thrift_obj>
$<TARGET_OBJECTS:common_conf_obj>
$<TARGET_OBJECTS:common_fs_obj>
$<TARGET_OBJECTS:common_thrift_obj>
$<TARGET_OBJECTS:common_common_thrift_obj>
$<TARGET_OBJECTS:common_thread_obj>
$<TARGET_OBJECTS:common_file_based_cluster_id_man_obj>
$<TARGET_OBJECTS:common_charset_obj>
$<TARGET_OBJECTS:common_encryption_obj>
$<TARGET_OBJECTS:common_http_client_obj>
$<TARGET_OBJECTS:common_process_obj>
$<TARGET_OBJECTS:common_agg_function_obj>
$<TARGET_OBJECTS:idgenerator_obj>
$<TARGET_OBJECTS:expr_visitor_obj>
$<TARGET_OBJECTS:session_obj>
$<TARGET_OBJECTS:graph_auth_obj>
$<TARGET_OBJECTS:graph_flags_obj>
$<TARGET_OBJECTS:util_obj>
$<TARGET_OBJECTS:planner_obj>
$<TARGET_OBJECTS:parser_obj>
$<TARGET_OBJECTS:context_obj>
$<TARGET_OBJECTS:validator_obj>
$<TARGET_OBJECTS:optimizer_obj>
${OPTIMIZER_TEST_LIB}
LIBRARIES
proxygenhttpserver
proxygenlib
......@@ -46,3 +55,19 @@ nebula_add_test(
gtest
gtest_main
)
nebula_add_test(
NAME
index_scan_rule_test
SOURCES
IndexScanRuleTest.cpp
OBJECTS
${OPTIMIZER_TEST_LIB}
LIBRARIES
proxygenhttpserver
proxygenlib
${THRIFT_LIBRARIES}
wangle
gtest
gtest_main
)
/* Copyright (c) 2020 vesoft inc. All rights reserved.
*
* This source code is licensed under Apache 2.0 License,
* attached with Common Clause Condition 1.0, found in the LICENSES directory.
*/
#include <gtest/gtest.h>
#include "optimizer/OptimizerUtils.h"
#include "optimizer/rule/IndexScanRule.h"
namespace nebula {
namespace opt {
TEST(IndexScanRuleTest, BoundValueTest) {
meta::cpp2::ColumnDef col;
auto* inst = std::move(IndexScanRule::kInstance).get();
auto* instance = static_cast<IndexScanRule*>(inst);
IndexScanRule::FilterItems items;
{
Value begin, end;
col.set_name("col1");
col.type.set_type(meta::cpp2::PropertyType::INT64);
// col > 1 and col < 5
items.addItem("col1", RelationalExpression::Kind::kRelGT, Value(1L));
items.addItem("col1", RelationalExpression::Kind::kRelLT, Value(5L));
for (const auto& item : items.items) {
auto ret = instance->boundValue(item, col, begin, end);
ASSERT_TRUE(ret.ok());
}
// Expect begin = 2 , end = 5;
EXPECT_EQ((Value(2L)), begin);
EXPECT_EQ((Value(5L)), end);
}
{
Value begin, end;
items.items.clear();
col.set_name("col1");
col.type.set_type(meta::cpp2::PropertyType::INT64);
// col > 1 and col > 6
items.addItem("col1", RelationalExpression::Kind::kRelGT, Value(1L));
items.addItem("col1", RelationalExpression::Kind::kRelGT, Value(6L));
for (const auto& item : items.items) {
auto ret = instance->boundValue(item, col, begin, end);
ASSERT_TRUE(ret.ok());
}
// Expect begin = 7
EXPECT_EQ(Value(7L), begin);
EXPECT_EQ(Value(), end);
}
{
Value begin, end;
items.items.clear();
col.set_name("col1");
col.type.set_type(meta::cpp2::PropertyType::INT64);
// col > 1 and col >= 6
items.addItem("col1", RelationalExpression::Kind::kRelGT, Value(1L));
items.addItem("col1", RelationalExpression::Kind::kRelGE, Value(6L));
for (const auto& item : items.items) {
auto ret = instance->boundValue(item, col, begin, end);
ASSERT_TRUE(ret.ok());
}
// Expect begin = 6
EXPECT_EQ(Value(6L), begin);
EXPECT_EQ(Value(), end);
}
{
Value begin, end;
items.items.clear();
col.set_name("col1");
col.type.set_type(meta::cpp2::PropertyType::INT64);
// col < 1 and col <= 6
items.addItem("col1", RelationalExpression::Kind::kRelLT, Value(1L));
items.addItem("col1", RelationalExpression::Kind::kRelLE, Value(6L));
for (const auto& item : items.items) {
auto ret = instance->boundValue(item, col, begin, end);
ASSERT_TRUE(ret.ok());
}
// Expect end = 1
EXPECT_EQ(Value(1L), end);
EXPECT_EQ(Value(), begin);
}
}
TEST(IndexScanRuleTest, IQCtxTest) {
auto* inst = std::move(IndexScanRule::kInstance).get();
auto* instance = static_cast<IndexScanRule*>(inst);
{
IndexItem index = std::make_unique<meta::cpp2::IndexItem>();
IndexScanRule::FilterItems items;
IndexQueryCtx iqctx = std::make_unique<std::vector<IndexQueryContext>>();
auto ret = instance->appendIQCtx(index, items, iqctx);
ASSERT_TRUE(ret.ok());
}
{
IndexItem index = std::make_unique<meta::cpp2::IndexItem>();
IndexScanRule::FilterItems items;
IndexQueryCtx iqctx = std::make_unique<std::vector<IndexQueryContext>>();
// setup index
{
std::vector<meta::cpp2::ColumnDef> cols;
for (int8_t i = 0; i < 5; i++) {
meta::cpp2::ColumnDef col;
col.set_name(folly::stringPrintf("col%d", i));
col.type.set_type(meta::cpp2::PropertyType::INT64);
cols.emplace_back(std::move(col));
}
index->set_fields(std::move(cols));
index->set_index_id(1);
}
// setup FilterItems col0 < 1 and col0 <= 6
{
items.items.clear();
items.addItem("col0", RelationalExpression::Kind::kRelLT, Value(1L));
items.addItem("col0", RelationalExpression::Kind::kRelLE, Value(6L));
auto ret = instance->appendIQCtx(index, items, iqctx);
ASSERT_TRUE(ret.ok());
ASSERT_EQ(1, iqctx->size());
ASSERT_EQ(1, (iqctx.get()->begin())->get_column_hints().size());
ASSERT_EQ(1, (iqctx.get()->begin())->get_index_id());
ASSERT_EQ("", (iqctx.get()->begin())->get_filter());
const auto& colHints = (iqctx.get()->begin())->get_column_hints();
ASSERT_EQ("col0", colHints.begin()->get_column_name());
ASSERT_EQ(storage::cpp2::ScanType::RANGE, colHints.begin()->get_scan_type());
ASSERT_EQ(Value(std::numeric_limits<int64_t>::min()),
colHints.begin()->get_begin_value());
ASSERT_EQ(Value(1L), colHints.begin()->get_end_value());
}
// setup FilterItems col0 > 1 and col1 <= 2 and col1 > -1 and col2 > 3
// and col3 < 4 and col4 == 4
{
items.items.clear();
iqctx.get()->clear();
items.addItem("col0", RelationalExpression::Kind::kRelGT, Value(1L));
items.addItem("col1", RelationalExpression::Kind::kRelLE, Value(2L));
items.addItem("col1", RelationalExpression::Kind::kRelGT, Value(-1L));
items.addItem("col2", RelationalExpression::Kind::kRelGT, Value(3L));
items.addItem("col3", RelationalExpression::Kind::kRelLT, Value(4L));
items.addItem("col4", RelationalExpression::Kind::kRelEQ, Value(4L));
auto ret = instance->appendIQCtx(index, items, iqctx);
ASSERT_TRUE(ret.ok());
ASSERT_EQ(1, iqctx->size());
ASSERT_EQ(5, (iqctx.get()->begin())->get_column_hints().size());
ASSERT_EQ(1, (iqctx.get()->begin())->get_index_id());
ASSERT_EQ("", (iqctx.get()->begin())->get_filter());
const auto& colHints = (iqctx.get()->begin())->get_column_hints();
{
auto hint = colHints[0];
ASSERT_EQ("col0", hint.get_column_name());
ASSERT_EQ(storage::cpp2::ScanType::RANGE, hint.get_scan_type());
ASSERT_EQ(Value(2L), hint.get_begin_value());
ASSERT_EQ(Value(std::numeric_limits<int64_t>::max()), hint.get_end_value());
}
{
auto hint = colHints[1];
ASSERT_EQ("col1", hint.get_column_name());
ASSERT_EQ(storage::cpp2::ScanType::RANGE, hint.get_scan_type());
ASSERT_EQ(Value(0L), hint.get_begin_value());
ASSERT_EQ(Value(3L), hint.get_end_value());
}
{
auto hint = colHints[2];
ASSERT_EQ("col2", hint.get_column_name());
ASSERT_EQ(storage::cpp2::ScanType::RANGE, hint.get_scan_type());
ASSERT_EQ(Value(4L), hint.get_begin_value());
ASSERT_EQ(Value(std::numeric_limits<int64_t>::max()), hint.get_end_value());
}
{
auto hint = colHints[3];
ASSERT_EQ("col3", hint.get_column_name());
ASSERT_EQ(storage::cpp2::ScanType::RANGE, hint.get_scan_type());
ASSERT_EQ(Value(std::numeric_limits<int64_t>::min()), hint.get_begin_value());
ASSERT_EQ(Value(4L), hint.get_end_value());
}
{
auto hint = colHints[4];
ASSERT_EQ("col4", hint.get_column_name());
ASSERT_EQ(storage::cpp2::ScanType::PREFIX, hint.get_scan_type());
ASSERT_EQ(Value(4L), hint.get_begin_value());
}
}
// setup FilterItems col0 > 1 and col1 <= 2 and col1 > -1 and col2 != 3
// and col3 < 4
// only expect col0 and col1 exits in column hints.
// col2 and col3 should be filter in storage layer.
{
items.items.clear();
iqctx.get()->clear();
items.addItem("col0", RelationalExpression::Kind::kRelGT, Value(1L));
items.addItem("col1", RelationalExpression::Kind::kRelLE, Value(2L));
items.addItem("col1", RelationalExpression::Kind::kRelGT, Value(-1L));
items.addItem("col2", RelationalExpression::Kind::kRelNE, Value(3L));
items.addItem("col3", RelationalExpression::Kind::kRelLT, Value(4L));
auto ret = instance->appendIQCtx(index, items, iqctx);
ASSERT_TRUE(ret.ok());
ASSERT_EQ(1, iqctx->size());
ASSERT_EQ(2, (iqctx.get()->begin())->get_column_hints().size());
ASSERT_EQ(1, (iqctx.get()->begin())->get_index_id());
ASSERT_EQ("", (iqctx.get()->begin())->get_filter());
const auto& colHints = (iqctx.get()->begin())->get_column_hints();
{
auto hint = colHints[0];
ASSERT_EQ("col0", hint.get_column_name());
ASSERT_EQ(storage::cpp2::ScanType::RANGE, hint.get_scan_type());
ASSERT_EQ(Value(2L), hint.get_begin_value());
ASSERT_EQ(Value(std::numeric_limits<int64_t>::max()), hint.get_end_value());
}
{
auto hint = colHints[1];
ASSERT_EQ("col1", hint.get_column_name());
ASSERT_EQ(storage::cpp2::ScanType::RANGE, hint.get_scan_type());
ASSERT_EQ(Value(0L), hint.get_begin_value());
ASSERT_EQ(Value(3L), hint.get_end_value());
}
}
}
}
} // namespace opt
} // namespace nebula
......@@ -448,7 +448,7 @@ public:
return schemaId_;
}
void setQueryContext(IndexQueryCtx contexts) {
void setIndexQueryContext(IndexQueryCtx contexts) {
contexts_ = std::move(contexts);
}
......
......@@ -31,22 +31,24 @@ Status IndexScanValidator::prepareFrom() {
auto *sentence = static_cast<const LookupSentence *>(sentence_);
spaceId_ = vctx_->whichSpace().id;
const auto* from = sentence->from();
auto ret = qctx_->schemaMng()->toEdgeType(spaceId_, *from);
if (ret.ok()) {
isEdge_ = true;
} else {
ret = qctx_->schemaMng()->toTagID(spaceId_, *from);
NG_RETURN_IF_ERROR(ret);
isEdge_ = false;
auto ret = qctx_->schemaMng()->getSchemaIDByName(spaceId_, *from);
if (!ret.ok()) {
return ret.status();
}
schemaId_ = ret.value();
isEdge_ = ret.value().first;
schemaId_ = ret.value().second;
return Status::OK();
}
Status IndexScanValidator::prepareYield() {
auto *sentence = static_cast<const LookupSentence *>(sentence_);
if (sentence->yieldClause() == nullptr) {
return Status::OK();
}
auto columns = sentence->yieldClause()->columns();
auto schema = qctx_->schemaMng()->getEdgeSchema(spaceId_, schemaId_);
auto schema = isEdge_
? qctx_->schemaMng()->getEdgeSchema(spaceId_, schemaId_)
: qctx_->schemaMng()->getTagSchema(spaceId_, schemaId_);
const auto* from = sentence->from();
if (schema == nullptr) {
return isEdge_
......@@ -54,26 +56,26 @@ Status IndexScanValidator::prepareYield() {
: Status::TagNotFound("Tag schema not found : %s", from->c_str());
}
returnCols_ = std::make_unique<std::vector<std::string>>();
for (const auto* col : columns) {
for (auto col : columns) {
std::string schemaName, colName;
if (col->expr()->kind() == Expression::Kind::kLabelAttribute) {
auto laExpr = static_cast<LabelAttributeExpression *>(col->expr());
if (*laExpr->left()->name() != *from) {
return Status::SemanticError("Schema name error : %s",
laExpr->left()->name()->c_str());
}
auto ret = schema->getFieldType(*laExpr->right()->name());
if (ret == meta::cpp2::PropertyType::UNKNOWN) {
return Status::SemanticError("Column %s not found in schema %s",
laExpr->right()->name()->c_str(),
from->c_str());
}
returnCols_->emplace_back(*laExpr->right()->name());
auto typeResult = deduceExprType(col->expr());
NG_RETURN_IF_ERROR(typeResult);
outputs_.emplace_back(deduceColName(col), typeResult.value());
auto la = static_cast<LabelAttributeExpression *>(col->expr());
schemaName = *la->left()->name();
colName = *la->right()->name();
} else {
return Status::SemanticError();
return Status::SemanticError("Yield clauses are not supported : %s",
col->expr()->toString().c_str());
}
if (schemaName != *from) {
return Status::SemanticError("Schema name error : %s", schemaName.c_str());
}
auto ret = schema->getFieldType(colName);
if (ret == meta::cpp2::PropertyType::UNKNOWN) {
return Status::SemanticError("Column %s not found in schema %s",
colName.c_str(), from->c_str());
}
returnCols_->emplace_back(colName);
}
return Status::OK();
}
......@@ -110,30 +112,7 @@ Status IndexScanValidator::checkFilter(Expression* expr, const std::string& from
case Expression::Kind::kRelGT:
case Expression::Kind::kRelNE: {
auto* rExpr = static_cast<RelationalExpression*>(expr);
auto* left = rExpr->left();
auto* right = rExpr->right();
// Does not support filter : schema.col1 > schema.col2
if (left->kind() == Expression::Kind::kLabelAttribute &&
right->kind() == Expression::Kind::kLabelAttribute) {
return Status::NotSupported("Expression %s not supported yet",
rExpr->toString().c_str());
} else if (left->kind() == Expression::Kind::kLabelAttribute) {
auto* attExpr = static_cast<LabelAttributeExpression *>(left);
if (*attExpr->left()->name() != from) {
return Status::SemanticError("Schema name error : %s",
attExpr->left()->name()->c_str());
}
} else if (right->kind() == Expression::Kind::kLabelAttribute) {
auto* attExpr = static_cast<LabelAttributeExpression *>(right);
if (*attExpr->left()->name() != from) {
return Status::SemanticError("Schema name error : %s",
attExpr->left()->name()->c_str());
}
} else {
return Status::NotSupported("Expression %s not supported yet",
rExpr->toString().c_str());
}
break;
return checkRelExpr(rExpr, from);
}
default: {
return Status::NotSupported("Expression %s not supported yet",
......@@ -143,5 +122,89 @@ Status IndexScanValidator::checkFilter(Expression* expr, const std::string& from
return Status::OK();
}
Status IndexScanValidator::checkRelExpr(RelationalExpression* expr,
const std::string& from) {
auto* left = expr->left();
auto* right = expr->right();
// Does not support filter : schema.col1 > schema.col2
if (left->kind() == Expression::Kind::kLabelAttribute &&
right->kind() == Expression::Kind::kLabelAttribute) {
return Status::NotSupported("Expression %s not supported yet",
expr->toString().c_str());
} else if (left->kind() == Expression::Kind::kLabelAttribute ||
right->kind() == Expression::Kind::kLabelAttribute) {
auto ret = rewriteRelExpr(expr, from);
NG_RETURN_IF_ERROR(ret);
} else {
return Status::NotSupported("Expression %s not supported yet",
expr->toString().c_str());
}
return Status::OK();
}
Status IndexScanValidator::rewriteRelExpr(RelationalExpression* expr,
const std::string& from) {
auto* left = expr->left();
auto* right = expr->right();
auto leftIsAE = left->kind() == Expression::Kind::kLabelAttribute;
std::string ref, prop;
auto* la = leftIsAE
? static_cast<LabelAttributeExpression *>(left)
: static_cast<LabelAttributeExpression *>(right);
if (*la->left()->name() != from) {
return Status::SemanticError("Schema name error : %s",
la->left()->name()->c_str());
}
ref = *la->left()->name();
prop = *la->right()->name();
// rewrite ConstantExpression
auto c = leftIsAE
? checkConstExpr(right, prop)
: checkConstExpr(left, prop);
if (!c.ok()) {
return Status::SemanticError("expression error : %s", left->toString().c_str());
}
if (leftIsAE) {
expr->setRight(new ConstantExpression(std::move(c).value()));
} else {
expr->setLeft(new ConstantExpression(std::move(c).value()));
}
// rewrite PropertyExpression
if (leftIsAE) {
if (isEdge_) {
expr->setLeft(ExpressionUtils::rewriteLabelAttribute<EdgePropertyExpression>(la));
} else {
expr->setLeft(ExpressionUtils::rewriteLabelAttribute<TagPropertyExpression>(la));
}
} else {
if (isEdge_) {
expr->setRight(ExpressionUtils::rewriteLabelAttribute<EdgePropertyExpression>(la));
} else {
expr->setRight(ExpressionUtils::rewriteLabelAttribute<TagPropertyExpression>(la));
}
}
return Status::OK();
}
StatusOr<Value> IndexScanValidator::checkConstExpr(Expression* expr,
const std::string& prop) {
auto schema = isEdge_
? qctx_->schemaMng()->getEdgeSchema(spaceId_, schemaId_)
: qctx_->schemaMng()->getTagSchema(spaceId_, schemaId_);
auto type = schema->getFieldType(prop);
QueryExpressionContext dummy(nullptr);
auto v = Expression::eval(expr, dummy);
if (v.type() != SchemaUtil::propTypeToValueType(type)) {
return Status::SemanticError("Column type error : %s", prop.c_str());
}
return v;
}
} // namespace graph
} // namespace nebula
......@@ -34,6 +34,12 @@ private:
Status checkFilter(Expression* expr, const std::string& from);
Status checkRelExpr(RelationalExpression* expr, const std::string& from);
Status rewriteRelExpr(RelationalExpression* expr, const std::string& from);
StatusOr<Value> checkConstExpr(Expression* expr, const std::string& prop);
private:
GraphSpaceID spaceId_{0};
IndexScan::IndexQueryCtx contexts_{};
......
......@@ -36,6 +36,7 @@
#include "visitor/DeducePropsVisitor.h"
#include "visitor/DeduceTypeVisitor.h"
#include "visitor/EvaluableExprVisitor.h"
#include "validator/IndexScanValidator.h"
namespace nebula {
namespace graph {
......@@ -188,9 +189,10 @@ std::unique_ptr<Validator> Validator::makeValidator(Sentence* sentence, QueryCon
return std::make_unique<RebuildEdgeIndexValidator>(sentence, context);
case Sentence::Kind::kDropEdgeIndex:
return std::make_unique<DropEdgeIndexValidator>(sentence, context);
case Sentence::Kind::kLookup:
return std::make_unique<IndexScanValidator>(sentence, context);
case Sentence::Kind::kMatch:
case Sentence::Kind::kUnknown:
case Sentence::Kind::kLookup:
case Sentence::Kind::kDownload:
case Sentence::Kind::kIngest:
case Sentence::Kind::kReturn: {
......
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