diff --git a/.gitignore b/.gitignore
index 01f7ec21775f2946e6e4d1b3bab3f68f0f7a8cdf..c3914e63ac5ffa59f64b4c01f7f4971f4903b416 100644
--- a/.gitignore
+++ b/.gitignore
@@ -46,6 +46,8 @@ nebula-python
.classpath
cmake-build*/
.vscode/
+core.*
+workspace.*
#py
*.egg-info
diff --git a/src/context/test/CMakeLists.txt b/src/context/test/CMakeLists.txt
index 3c73016437aee984545ad7b022a417ee9166857d..ab775c1da4c3420a58ab2fdad39cc4abbd0a4e7b 100644
--- a/src/context/test/CMakeLists.txt
+++ b/src/context/test/CMakeLists.txt
@@ -4,7 +4,6 @@
# attached with Common Clause Condition 1.0, found in the LICENSES directory.
SET(CONTEXT_TEST_LIBS
- $<TARGET_OBJECTS:common_agg_function_obj>
$<TARGET_OBJECTS:common_charset_obj>
$<TARGET_OBJECTS:common_datatypes_obj>
$<TARGET_OBJECTS:common_encryption_obj>
diff --git a/src/daemons/CMakeLists.txt b/src/daemons/CMakeLists.txt
index 201505bcdd5db42bbfda36e0196b7a244a8b3564..fb69079ac099c7e365d80a1107a70c9f896aecb7 100644
--- a/src/daemons/CMakeLists.txt
+++ b/src/daemons/CMakeLists.txt
@@ -54,7 +54,6 @@ nebula_add_executable(
$<TARGET_OBJECTS:common_charset_obj>
$<TARGET_OBJECTS:common_encryption_obj>
$<TARGET_OBJECTS:common_function_manager_obj>
- $<TARGET_OBJECTS:common_agg_function_obj>
$<TARGET_OBJECTS:common_conf_obj>
$<TARGET_OBJECTS:common_time_utils_obj>
$<TARGET_OBJECTS:common_graph_obj>
diff --git a/src/executor/admin/SubmitJobExecutor.cpp b/src/executor/admin/SubmitJobExecutor.cpp
index 84ba61913f5418ed83a768f9c81ef02b549008be..3e3e59029f1a59a7bdb2cd75484f06d84913891d 100644
--- a/src/executor/admin/SubmitJobExecutor.cpp
+++ b/src/executor/admin/SubmitJobExecutor.cpp
@@ -112,7 +112,7 @@ folly::Future<Status> SubmitJobExecutor::execute() {
// no default so the compiler will warning when lack
}
DLOG(FATAL) << "Unknown job operation " << static_cast<int>(jobOp);
- return Status::Error("Unkown job job operation %d.", static_cast<int>(jobOp));
+ return Status::Error("Unknown job job operation %d.", static_cast<int>(jobOp));
});
}
diff --git a/src/executor/query/AggregateExecutor.cpp b/src/executor/query/AggregateExecutor.cpp
index d1e8316e0990f0f2c65b6a8ebce7caf37a32d3e9..4e0f8841292b88a8273684ecd3a8a12c97facdfe 100644
--- a/src/executor/query/AggregateExecutor.cpp
+++ b/src/executor/query/AggregateExecutor.cpp
@@ -7,7 +7,7 @@
#include "executor/query/AggregateExecutor.h"
#include "common/datatypes/List.h"
-#include "common/function/AggregateFunction.h"
+#include "common/expression/AggregateExpression.h"
#include "context/QueryExpressionContext.h"
#include "context/Result.h"
#include "planner/PlanNode.h"
@@ -26,7 +26,7 @@ folly::Future<Status> AggregateExecutor::execute() {
DCHECK(!!iter);
QueryExpressionContext ctx(ectx_);
- std::unordered_map<List, std::vector<std::unique_ptr<AggFun>>> result;
+ std::unordered_map<List, std::vector<std::unique_ptr<AggData>>, std::hash<nebula::List>> result;
for (; iter->valid(); iter->next()) {
List list;
for (auto& key : groupKeys) {
@@ -35,19 +35,22 @@ folly::Future<Status> AggregateExecutor::execute() {
auto it = result.find(list);
if (it == result.end()) {
- std::vector<std::unique_ptr<AggFun>> funs;
- for (auto& item : groupItems) {
- auto fun = AggFun::aggFunMap_[item.func](item.distinct);
- auto& v = item.expr->eval(ctx(iter.get()));
- fun->apply(v);
- funs.emplace_back(std::move(fun));
- }
- result.emplace(std::make_pair(std::move(list), std::move(funs)));
+ std::vector<std::unique_ptr<AggData>> cols;
+ for (size_t i = 0; i < groupItems.size(); ++i) {
+ cols.emplace_back(new AggData());
+ }
+ result.emplace(std::make_pair(list, std::move(cols)));
} else {
DCHECK_EQ(it->second.size(), groupItems.size());
- for (size_t i = 0; i < groupItems.size(); ++i) {
- auto& v = groupItems[i].expr->eval(ctx(iter.get()));
- it->second[i]->apply(v);
+ }
+
+ for (size_t i = 0; i < groupItems.size(); ++i) {
+ auto* item = groupItems[i];
+ if (item->kind() == Expression::Kind::kAggregate) {
+ static_cast<AggregateExpression*>(item)->setAggData(result[list][i].get());
+ item->eval(ctx(iter.get()));
+ } else {
+ result[list][i]->setResult(item->eval(ctx(iter.get())));
}
}
}
@@ -57,8 +60,8 @@ folly::Future<Status> AggregateExecutor::execute() {
ds.rows.reserve(result.size());
for (auto& kv : result) {
Row row;
- for (auto& f : kv.second) {
- row.values.emplace_back(f->getResult());
+ for (auto& v : kv.second) {
+ row.values.emplace_back(v->result());
}
ds.rows.emplace_back(std::move(row));
}
diff --git a/src/executor/test/AggregateTest.cpp b/src/executor/test/AggregateTest.cpp
index 30b4309a2760edde0d062e56064a7366cb664c68..bb12ce0c4822232d00986382a139aaab86cd77a0 100644
--- a/src/executor/test/AggregateTest.cpp
+++ b/src/executor/test/AggregateTest.cpp
@@ -85,11 +85,11 @@ struct RowCmp {
#define TEST_AGG_1(FUN, COL, DISTINCT) \
std::vector<Expression*> groupKeys; \
- std::vector<Aggregate::GroupItem> groupItems; \
+ std::vector<Expression*> groupItems; \
auto expr = \
std::make_unique<InputPropertyExpression>(new std::string("col1")); \
- Aggregate::GroupItem item(expr.get(), FUN, DISTINCT); \
- groupItems.emplace_back(std::move(item)); \
+ AggregateExpression item(new std::string(FUN), expr.release(), DISTINCT); \
+ groupItems.emplace_back(&item); \
auto* agg = Aggregate::make(qctx_.get(), nullptr, std::move(groupKeys), \
std::move(groupItems)); \
agg->setInputVar(*input_); \
@@ -105,12 +105,13 @@ struct RowCmp {
#define TEST_AGG_2(FUN, COL, DISTINCT) \
std::vector<Expression*> groupKeys; \
- std::vector<Aggregate::GroupItem> groupItems; \
- auto expr = \
+ std::vector<Expression*> groupItems; \
+ auto expr1 = \
std::make_unique<InputPropertyExpression>(new std::string("col2")); \
- groupKeys.emplace_back(expr.get()); \
- Aggregate::GroupItem item(expr.get(), FUN, DISTINCT); \
- groupItems.emplace_back(std::move(item)); \
+ auto expr2 = expr1->clone(); \
+ groupKeys.emplace_back(expr1.get()); \
+ AggregateExpression item(new std::string(FUN), expr2.release(), DISTINCT); \
+ groupItems.emplace_back(&item); \
auto* agg = Aggregate::make(qctx_.get(), nullptr, std::move(groupKeys), \
std::move(groupItems)); \
agg->setInputVar(*input_); \
@@ -128,17 +129,18 @@ struct RowCmp {
#define TEST_AGG_3(FUN, COL, DISTINCT) \
std::vector<Expression*> groupKeys; \
- std::vector<Aggregate::GroupItem> groupItems; \
- auto expr = \
- std::make_unique<InputPropertyExpression>(new std::string("col2")); \
- groupKeys.emplace_back(expr.get()); \
- Aggregate::GroupItem item(expr.get(), AggFun::Function::kNone, false); \
- groupItems.emplace_back(std::move(item)); \
+ std::vector<Expression*> groupItems; \
auto expr1 = \
- std::make_unique<InputPropertyExpression>(new std::string("col3")); \
+ std::make_unique<InputPropertyExpression>(new std::string("col2")); \
+ auto expr2 = expr1->clone(); \
groupKeys.emplace_back(expr1.get()); \
- Aggregate::GroupItem item1(expr1.get(), FUN, DISTINCT); \
- groupItems.emplace_back(std::move(item1)); \
+ AggregateExpression item(new std::string(""), expr2.release(), false); \
+ groupItems.emplace_back(&item); \
+ auto expr3 = \
+ std::make_unique<InputPropertyExpression>(new std::string("col3")); \
+ groupKeys.emplace_back(expr3.get()); \
+ AggregateExpression item1(new std::string(FUN), expr3.release(), DISTINCT); \
+ groupItems.emplace_back(&item1); \
auto* agg = Aggregate::make(qctx_.get(), nullptr, std::move(groupKeys), \
std::move(groupItems)); \
agg->setInputVar(*input_); \
@@ -156,10 +158,10 @@ struct RowCmp {
#define TEST_AGG_4(FUN, COL, DISTINCT) \
std::vector<Expression*> groupKeys; \
- std::vector<Aggregate::GroupItem> groupItems; \
+ std::vector<Expression*> groupItems; \
auto expr = std::make_unique<ConstantExpression>(1); \
- Aggregate::GroupItem item(expr.get(), FUN, DISTINCT); \
- groupItems.emplace_back(std::move(item)); \
+ AggregateExpression item(new std::string(FUN), expr.release(), DISTINCT); \
+ groupItems.emplace_back(&item); \
auto* agg = Aggregate::make(qctx_.get(), nullptr, std::move(groupKeys), \
std::move(groupItems)); \
agg->setInputVar(*input_); \
@@ -204,7 +206,7 @@ TEST_F(AggregateTest, Group) {
// key = col2
// items = col2
- TEST_AGG_2(AggFun::Function::kNone, "col2", false)
+ TEST_AGG_2("", "col2", false)
}
{
// ===============
@@ -237,7 +239,7 @@ TEST_F(AggregateTest, Group) {
// key = col2, col3
// items = col2, col3
- TEST_AGG_3(AggFun::Function::kNone, "col3", false)
+ TEST_AGG_3("", "col3", false)
}
}
@@ -260,7 +262,7 @@ TEST_F(AggregateTest, Collect) {
// key =
// items = collect(col1)
- TEST_AGG_1(AggFun::Function::kCollect, "list", false)
+ TEST_AGG_1("COLLECT", "list", false)
}
{
// ========
@@ -301,12 +303,13 @@ TEST_F(AggregateTest, Collect) {
// key = col1
// items = collect(col1)
std::vector<Expression*> groupKeys;
- std::vector<Aggregate::GroupItem> groupItems;
- auto expr =
+ std::vector<Expression*> groupItems;
+ auto expr1 =
std::make_unique<InputPropertyExpression>(new std::string("col1"));
- groupKeys.emplace_back(expr.get());
- Aggregate::GroupItem item(expr.get(), AggFun::Function::kCollect, false);
- groupItems.emplace_back(std::move(item));
+ auto expr2 = expr1->clone();
+ groupKeys.emplace_back(expr1.get());
+ AggregateExpression item(new std::string("COLLECT"), expr2.release(), false);
+ groupItems.emplace_back(std::move(&item));
auto* agg = Aggregate::make(qctx_.get(), nullptr, std::move(groupKeys),
std::move(groupItems));
agg->setInputVar(*input_);
@@ -367,7 +370,7 @@ TEST_F(AggregateTest, Collect) {
// key = col2, col3
// items = col2, collect(col3)
- TEST_AGG_3(AggFun::Function::kCollect, "list", false)
+ TEST_AGG_3("COLLECT", "list", false)
}
{
// ====================================
@@ -387,7 +390,7 @@ TEST_F(AggregateTest, Collect) {
// key =
// items = collect(1)
- TEST_AGG_4(AggFun::Function::kCollect, "list", false)
+ TEST_AGG_4("COLLECT", "list", false)
}
{
// ====================================
@@ -408,11 +411,11 @@ TEST_F(AggregateTest, Collect) {
// key =
// items = collect(distinct col1)
std::vector<Expression*> groupKeys;
- std::vector<Aggregate::GroupItem> groupItems;
+ std::vector<Expression*> groupItems;
auto expr =
std::make_unique<InputPropertyExpression>(new std::string("col1"));
- Aggregate::GroupItem item(expr.get(), AggFun::Function::kCollect, true);
- groupItems.emplace_back(std::move(item));
+ AggregateExpression item(new std::string("COLLECT"), expr.release(), true);
+ groupItems.emplace_back(std::move(&item));
auto* agg = Aggregate::make(qctx_.get(), nullptr, std::move(groupKeys),
std::move(groupItems));
agg->setInputVar(*input_);
@@ -469,12 +472,12 @@ TEST_F(AggregateTest, Collect) {
// key = col1
// items = collect(distinct col1)
std::vector<Expression*> groupKeys;
- std::vector<Aggregate::GroupItem> groupItems;
- auto expr =
+ std::vector<Expression*> groupItems;
+ auto expr1 =
std::make_unique<InputPropertyExpression>(new std::string("col1"));
- groupKeys.emplace_back(expr.get());
- Aggregate::GroupItem item(expr.get(), AggFun::Function::kCollect, true);
- groupItems.emplace_back(std::move(item));
+ auto expr2 = expr1->clone();
+ AggregateExpression item(new std::string("COLLECT"), expr2.release(), true);
+ groupItems.emplace_back(std::move(&item));
auto* agg = Aggregate::make(qctx_.get(), nullptr, std::move(groupKeys),
std::move(groupItems));
agg->setInputVar(*input_);
@@ -535,7 +538,8 @@ TEST_F(AggregateTest, Collect) {
// key = col2, col3
// items = col2, collect(distinct col3)
- TEST_AGG_3(AggFun::Function::kCollect, "list", true)
+
+ TEST_AGG_3("COLLECT", "list", true)
}
{
// ====================================
@@ -553,7 +557,7 @@ TEST_F(AggregateTest, Collect) {
// key =
// items = collect(distinct 1)
- TEST_AGG_4(AggFun::Function::kCollect, "list", true)
+ TEST_AGG_4("COLLECT", "list", true)
}
}
@@ -572,7 +576,7 @@ TEST_F(AggregateTest, Count) {
// key =
// items = count(col1)
- TEST_AGG_1(AggFun::Function::kCount, "count", false)
+ TEST_AGG_1("COUNT", "count", false)
}
{
// ========
@@ -605,7 +609,7 @@ TEST_F(AggregateTest, Count) {
// key = col2
// items = count(col2)
- TEST_AGG_2(AggFun::Function::kCount, "count", false)
+ TEST_AGG_2("COUNT", "count", false)
}
{
// ================
@@ -638,7 +642,7 @@ TEST_F(AggregateTest, Count) {
// key = col2, col3
// items = col2, count(col3)
- TEST_AGG_3(AggFun::Function::kCount, "count", false)
+ TEST_AGG_3("COUNT", "count", false)
}
{
// ========
@@ -653,7 +657,7 @@ TEST_F(AggregateTest, Count) {
// key =
// items = count(1)
- TEST_AGG_4(AggFun::Function::kCount, "count", false)
+ TEST_AGG_4("COUNT", "count", false)
}
{
// ========
@@ -669,7 +673,7 @@ TEST_F(AggregateTest, Count) {
// key =
// items = count(distinct col1)
- TEST_AGG_1(AggFun::Function::kCount, "count", true)
+ TEST_AGG_1("COUNT", "count", true)
}
{
// ========
@@ -702,7 +706,7 @@ TEST_F(AggregateTest, Count) {
// key = col2
// items = count(distinct col2)
- TEST_AGG_2(AggFun::Function::kCount, "count", true)
+ TEST_AGG_2("COUNT", "count", true)
}
{
// ================
@@ -735,7 +739,7 @@ TEST_F(AggregateTest, Count) {
// key = col2, col3
// items = col2, count(distinct col3)
- TEST_AGG_3(AggFun::Function::kCount, "count", true)
+ TEST_AGG_3("COUNT", "count", true)
}
{
// ========
@@ -750,7 +754,7 @@ TEST_F(AggregateTest, Count) {
// key =
// items = count(distinct 1)
- TEST_AGG_4(AggFun::Function::kCount, "count", true)
+ TEST_AGG_4("COUNT", "count", true)
}
}
@@ -769,7 +773,7 @@ TEST_F(AggregateTest, Sum) {
// key =
// items = sum(col1)
- TEST_AGG_1(AggFun::Function::kSum, "sum", false)
+ TEST_AGG_1("SUM", "sum", false)
}
{
// ========
@@ -800,7 +804,7 @@ TEST_F(AggregateTest, Sum) {
// key = col2
// items = sum(col2)
- TEST_AGG_2(AggFun::Function::kSum, "sum", false)
+ TEST_AGG_2("SUM", "sum", false)
}
{
// ================
@@ -833,7 +837,7 @@ TEST_F(AggregateTest, Sum) {
// key = col2, col3
// items = col2, sum(col3)
- TEST_AGG_3(AggFun::Function::kSum, "sum", false)
+ TEST_AGG_3("SUM", "sum", false)
}
{
// ========
@@ -849,7 +853,7 @@ TEST_F(AggregateTest, Sum) {
// key =
// items = sum(1)
- TEST_AGG_4(AggFun::Function::kSum, "sum", false)
+ TEST_AGG_4("SUM", "sum", false)
}
{
// ========
@@ -865,7 +869,7 @@ TEST_F(AggregateTest, Sum) {
// key =
// items = sum(distinct col1)
- TEST_AGG_1(AggFun::Function::kSum, "sum", true)
+ TEST_AGG_1("SUM", "sum", true)
}
{
// ========
@@ -896,7 +900,7 @@ TEST_F(AggregateTest, Sum) {
// key = col2
// items = sum(distinct col2)
- TEST_AGG_2(AggFun::Function::kSum, "sum", true)
+ TEST_AGG_2("SUM", "sum", true)
}
{
// ================
@@ -929,7 +933,7 @@ TEST_F(AggregateTest, Sum) {
// key = col2, col3
// items = col2, sum(distinct col3)
- TEST_AGG_3(AggFun::Function::kSum, "sum", true)
+ TEST_AGG_3("SUM", "sum", true)
}
{
// ========
@@ -945,7 +949,7 @@ TEST_F(AggregateTest, Sum) {
// key =
// items = sum(distinct 1)
- TEST_AGG_4(AggFun::Function::kSum, "sum", true)
+ TEST_AGG_4("SUM", "sum", true)
}
}
@@ -964,7 +968,7 @@ TEST_F(AggregateTest, Avg) {
// key =
// items = avg(col1)
- TEST_AGG_1(AggFun::Function::kAvg, "avg", false)
+ TEST_AGG_1("AVG", "avg", false)
}
{
// ========
@@ -995,7 +999,7 @@ TEST_F(AggregateTest, Avg) {
// key = col2
// items = avg(col2)
- TEST_AGG_2(AggFun::Function::kAvg, "avg", false)
+ TEST_AGG_2("AVG", "avg", false)
}
{
// ================
@@ -1028,7 +1032,7 @@ TEST_F(AggregateTest, Avg) {
// key = col2, col3
// items = col2, sum(col3)
- TEST_AGG_3(AggFun::Function::kAvg, "avg", false)
+ TEST_AGG_3("AVG", "avg", false)
}
{
// ========
@@ -1044,7 +1048,7 @@ TEST_F(AggregateTest, Avg) {
// key =
// items = avg(1)
- TEST_AGG_4(AggFun::Function::kAvg, "avg", false)
+ TEST_AGG_4("AVG", "avg", false)
}
{
// ========
@@ -1060,7 +1064,7 @@ TEST_F(AggregateTest, Avg) {
// key =
// items = avg(col1)
- TEST_AGG_1(AggFun::Function::kAvg, "avg", true)
+ TEST_AGG_1("AVG", "avg", true)
}
{
// ========
@@ -1091,7 +1095,7 @@ TEST_F(AggregateTest, Avg) {
// key = col2
// items = avg(col2)
- TEST_AGG_2(AggFun::Function::kAvg, "avg", true)
+ TEST_AGG_2("AVG", "avg", true)
}
{
// ================
@@ -1124,7 +1128,7 @@ TEST_F(AggregateTest, Avg) {
// key = col2, col3
// items = col2, sum(col3)
- TEST_AGG_3(AggFun::Function::kAvg, "avg", true)
+ TEST_AGG_3("AVG", "avg", true)
}
{
// ========
@@ -1140,7 +1144,7 @@ TEST_F(AggregateTest, Avg) {
// key =
// items = avg(1)
- TEST_AGG_4(AggFun::Function::kAvg, "avg", true)
+ TEST_AGG_4("AVG", "avg", true)
}
}
@@ -1159,7 +1163,7 @@ TEST_F(AggregateTest, Max) {
// key =
// items = max(col1)
- TEST_AGG_1(AggFun::Function::kMax, "max", false)
+ TEST_AGG_1("MAX", "max", false)
}
{
// ================
@@ -1192,7 +1196,7 @@ TEST_F(AggregateTest, Max) {
// key = col2, col3
// items = col2, max(col3)
- TEST_AGG_3(AggFun::Function::kMax, "max", false)
+ TEST_AGG_3("MAX", "max", false)
}
{
// ========
@@ -1208,7 +1212,7 @@ TEST_F(AggregateTest, Max) {
// key =
// items = max(1)
- TEST_AGG_4(AggFun::Function::kMax, "max", false)
+ TEST_AGG_4("MAX", "max", false)
}
{
// ========
@@ -1224,7 +1228,7 @@ TEST_F(AggregateTest, Max) {
// key =
// items = max(col1)
- TEST_AGG_1(AggFun::Function::kMax, "max", true)
+ TEST_AGG_1("MAX", "max", true)
}
{
// ================
@@ -1257,7 +1261,7 @@ TEST_F(AggregateTest, Max) {
// key = col2, col3
// items = col2, max(col3)
- TEST_AGG_3(AggFun::Function::kMax, "max", true)
+ TEST_AGG_3("MAX", "max", true)
}
{
// ========
@@ -1273,7 +1277,7 @@ TEST_F(AggregateTest, Max) {
// key =
// items = max(1)
- TEST_AGG_4(AggFun::Function::kMax, "max", true)
+ TEST_AGG_4("MAX", "max", true)
}
}
@@ -1292,7 +1296,7 @@ TEST_F(AggregateTest, Min) {
// key =
// items = min(col1)
- TEST_AGG_1(AggFun::Function::kMin, "min", false)
+ TEST_AGG_1("MIN", "min", false)
}
{
// ================
@@ -1325,7 +1329,7 @@ TEST_F(AggregateTest, Min) {
// key = col2, col3
// items = col2, min(col3)
- TEST_AGG_3(AggFun::Function::kMin, "min", false)
+ TEST_AGG_3("MIN", "min", false)
}
{
// ========
@@ -1341,7 +1345,7 @@ TEST_F(AggregateTest, Min) {
// key =
// items = min(1)
- TEST_AGG_4(AggFun::Function::kMin, "min", false)
+ TEST_AGG_4("MIN", "min", false)
}
{
// ========
@@ -1357,7 +1361,7 @@ TEST_F(AggregateTest, Min) {
// key =
// items = min(distinct col1)
- TEST_AGG_1(AggFun::Function::kMin, "min", true)
+ TEST_AGG_1("MIN", "min", true)
}
{
// ================
@@ -1390,7 +1394,7 @@ TEST_F(AggregateTest, Min) {
// key = col2, col3
// items = col2, min(distinct col3)
- TEST_AGG_3(AggFun::Function::kMin, "min", true)
+ TEST_AGG_3("MIN", "min", true)
}
{
// ========
@@ -1406,7 +1410,7 @@ TEST_F(AggregateTest, Min) {
// key =
// items = min(distinct 1)
- TEST_AGG_4(AggFun::Function::kMin, "min", true)
+ TEST_AGG_4("MIN", "min", true)
}
}
@@ -1425,7 +1429,7 @@ TEST_F(AggregateTest, Stdev) {
// key =
// items = stdev(col1)
- TEST_AGG_1(AggFun::Function::kStdev, "stdev", false)
+ TEST_AGG_1("STD", "stdev", false)
}
{
// ===============
@@ -1455,7 +1459,7 @@ TEST_F(AggregateTest, Stdev) {
// key = col2
// items = stdev(col2)
- TEST_AGG_2(AggFun::Function::kStdev, "stdev", false)
+ TEST_AGG_2("STD", "stdev", false)
}
{
// =======================
@@ -1488,7 +1492,7 @@ TEST_F(AggregateTest, Stdev) {
// key = col2, col3
// items = col2, stdev(col3)
- TEST_AGG_3(AggFun::Function::kStdev, "stdev", false)
+ TEST_AGG_3("STD", "stdev", false)
}
{
// ===============
@@ -1503,7 +1507,7 @@ TEST_F(AggregateTest, Stdev) {
// key =
// items = stdev(1)
- TEST_AGG_4(AggFun::Function::kStdev, "stdev", false)
+ TEST_AGG_4("STD", "stdev", false)
}
{
// ===============
@@ -1519,7 +1523,7 @@ TEST_F(AggregateTest, Stdev) {
// key =
// items = stdev(distinct col1)
- TEST_AGG_1(AggFun::Function::kStdev, "stdev", true)
+ TEST_AGG_1("STD", "stdev", true)
}
{
// ===============
@@ -1549,7 +1553,7 @@ TEST_F(AggregateTest, Stdev) {
// key = col2
// items = stdev(distinct col2)
- TEST_AGG_2(AggFun::Function::kStdev, "stdev", true)
+ TEST_AGG_2("STD", "stdev", true)
}
{
// =======================
@@ -1582,7 +1586,7 @@ TEST_F(AggregateTest, Stdev) {
// key = col2, col3
// items = col2, stdev(distinct col3)
- TEST_AGG_3(AggFun::Function::kStdev, "stdev", true)
+ TEST_AGG_3("STD", "stdev", true)
}
{
// ===============
@@ -1598,7 +1602,7 @@ TEST_F(AggregateTest, Stdev) {
// key =
// items = stdev(distinct 1)
- TEST_AGG_4(AggFun::Function::kStdev, "stdev", true)
+ TEST_AGG_4("STD", "stdev", true)
}
}
@@ -1617,7 +1621,7 @@ TEST_F(AggregateTest, BitAnd) {
// key =
// items = bit_and(col1)
- TEST_AGG_1(AggFun::Function::kBitAnd, "bit_and", false)
+ TEST_AGG_1("BIT_AND", "bit_and", false)
}
{
// ===============
@@ -1648,7 +1652,7 @@ TEST_F(AggregateTest, BitAnd) {
// key = col2
// items = bit_and(col2)
- TEST_AGG_2(AggFun::Function::kBitAnd, "bit_and", false)
+ TEST_AGG_2("BIT_AND", "bit_and", false)
}
{
// =======================
@@ -1681,7 +1685,7 @@ TEST_F(AggregateTest, BitAnd) {
// key = col2, col3
// items = col2, bit_and(col3)
- TEST_AGG_3(AggFun::Function::kBitAnd, "bit_and", false)
+ TEST_AGG_3("BIT_AND", "bit_and", false)
}
{
// ===============
@@ -1696,7 +1700,7 @@ TEST_F(AggregateTest, BitAnd) {
// key =
// items = bit_and(1)
- TEST_AGG_4(AggFun::Function::kBitAnd, "bit_and", false)
+ TEST_AGG_4("BIT_AND", "bit_and", false)
}
{
// ===============
@@ -1712,7 +1716,7 @@ TEST_F(AggregateTest, BitAnd) {
// key =
// items = bit_and(distinct col1)
- TEST_AGG_1(AggFun::Function::kBitAnd, "bit_and", true)
+ TEST_AGG_1("BIT_AND", "bit_and", true)
}
{
// ===============
@@ -1743,7 +1747,7 @@ TEST_F(AggregateTest, BitAnd) {
// key = col2
// items = bit_and(col2)
- TEST_AGG_2(AggFun::Function::kBitAnd, "bit_and", true)
+ TEST_AGG_2("BIT_AND", "bit_and", true)
}
{
// =======================
@@ -1776,7 +1780,7 @@ TEST_F(AggregateTest, BitAnd) {
// key = col2, col3
// items = col2, bit_and(col3)
- TEST_AGG_3(AggFun::Function::kBitAnd, "bit_and", true)
+ TEST_AGG_3("BIT_AND", "bit_and", true)
}
{
// ===============
@@ -1792,7 +1796,7 @@ TEST_F(AggregateTest, BitAnd) {
// key =
// items = bit_and(1)
- TEST_AGG_4(AggFun::Function::kBitAnd, "bit_and", true)
+ TEST_AGG_4("BIT_AND", "bit_and", true)
}
}
@@ -1811,7 +1815,7 @@ TEST_F(AggregateTest, BitOr) {
// key =
// items = bit_or(col1)
- TEST_AGG_1(AggFun::Function::kBitOr, "bit_or", false)
+ TEST_AGG_1("BIT_OR", "bit_or", false)
}
{
// ===============
@@ -1842,7 +1846,7 @@ TEST_F(AggregateTest, BitOr) {
// key = col2
// items = bit_or(col2)
- TEST_AGG_2(AggFun::Function::kBitOr, "bit_or", false)
+ TEST_AGG_2("BIT_OR", "bit_or", false)
}
{
// =======================
@@ -1875,7 +1879,7 @@ TEST_F(AggregateTest, BitOr) {
// key = col2, col3
// items = col2, bit_or(col3)
- TEST_AGG_3(AggFun::Function::kBitOr, "bit_or", false)
+ TEST_AGG_3("BIT_OR", "bit_or", false)
}
{
// ===============
@@ -1891,7 +1895,7 @@ TEST_F(AggregateTest, BitOr) {
// key =
// items = bit_or(1)
- TEST_AGG_4(AggFun::Function::kBitOr, "bit_or", false)
+ TEST_AGG_4("BIT_OR", "bit_or", false)
}
{
// ===============
@@ -1907,7 +1911,7 @@ TEST_F(AggregateTest, BitOr) {
// key =
// items = bit_or(distinct col1)
- TEST_AGG_1(AggFun::Function::kBitOr, "bit_or", true)
+ TEST_AGG_1("BIT_OR", "bit_or", true)
}
{
// ===============
@@ -1938,7 +1942,7 @@ TEST_F(AggregateTest, BitOr) {
// key = col2
// items = bit_or(distinct col2)
- TEST_AGG_2(AggFun::Function::kBitOr, "bit_or", true)
+ TEST_AGG_2("BIT_OR", "bit_or", true)
}
{
// =======================
@@ -1971,7 +1975,7 @@ TEST_F(AggregateTest, BitOr) {
// key = col2, col3
// items = col2, bit_or(distinct col3)
- TEST_AGG_3(AggFun::Function::kBitOr, "bit_or", true)
+ TEST_AGG_3("BIT_OR", "bit_or", true)
}
{
// ===============
@@ -1987,7 +1991,7 @@ TEST_F(AggregateTest, BitOr) {
// key =
// items = bit_or(distinct 1)
- TEST_AGG_4(AggFun::Function::kBitOr, "bit_or", true)
+ TEST_AGG_4("BIT_OR", "bit_or", true)
}
}
@@ -2006,7 +2010,7 @@ TEST_F(AggregateTest, BitXor) {
// key =
// items = bit_xor(col1)
- TEST_AGG_1(AggFun::Function::kBitXor, "bit_xor", false)
+ TEST_AGG_1("BIT_XOR", "bit_xor", false)
}
{
// ===============
@@ -2037,7 +2041,7 @@ TEST_F(AggregateTest, BitXor) {
// key = col2
// items = bit_xor(col2)
- TEST_AGG_2(AggFun::Function::kBitXor, "bit_xor", false)
+ TEST_AGG_2("BIT_XOR", "bit_xor", false)
}
{
// =======================
@@ -2070,7 +2074,7 @@ TEST_F(AggregateTest, BitXor) {
// key = col2, col3
// items = col2, bit_xor(col3)
- TEST_AGG_3(AggFun::Function::kBitXor, "bit_xor", false)
+ TEST_AGG_3("BIT_XOR", "bit_xor", false)
}
{
// ===============
@@ -2086,7 +2090,7 @@ TEST_F(AggregateTest, BitXor) {
// key =
// items = bit_xor(1)
- TEST_AGG_4(AggFun::Function::kBitXor, "bit_xor", false)
+ TEST_AGG_4("BIT_XOR", "bit_xor", false)
}
{
// ===============
@@ -2102,7 +2106,7 @@ TEST_F(AggregateTest, BitXor) {
// key =
// items = bit_xor(distinct col1)
- TEST_AGG_1(AggFun::Function::kBitXor, "bit_xor", true)
+ TEST_AGG_1("BIT_XOR", "bit_xor", true)
}
{
// ===============
@@ -2133,7 +2137,7 @@ TEST_F(AggregateTest, BitXor) {
// key = col2
// items = bit_xor(distinct col2)
- TEST_AGG_2(AggFun::Function::kBitXor, "bit_xor", true)
+ TEST_AGG_2("BIT_XOR", "bit_xor", true)
}
{
// =======================
@@ -2166,7 +2170,7 @@ TEST_F(AggregateTest, BitXor) {
// key = col2, col3
// items = col2, bit_xor(distinct col3)
- TEST_AGG_3(AggFun::Function::kBitXor, "bit_xor", true)
+ TEST_AGG_3("BIT_XOR", "bit_xor", true)
}
{
// ===============
@@ -2182,7 +2186,7 @@ TEST_F(AggregateTest, BitXor) {
// key =
// items = bit_xor(distinct 1)
- TEST_AGG_4(AggFun::Function::kBitXor, "bit_xor", true)
+ TEST_AGG_4("BIT_XOR", "bit_xor", true)
}
}
} // namespace graph
diff --git a/src/executor/test/CMakeLists.txt b/src/executor/test/CMakeLists.txt
index a814808d7c9c7f9424bb4f510fd4dc9059507c6d..f2b17693dec199b8e6f688de321b2d40b3b91a89 100644
--- a/src/executor/test/CMakeLists.txt
+++ b/src/executor/test/CMakeLists.txt
@@ -27,7 +27,6 @@ SET(EXEC_QUERY_TEST_OBJS
$<TARGET_OBJECTS:common_file_based_cluster_id_man_obj>
$<TARGET_OBJECTS:common_charset_obj>
$<TARGET_OBJECTS:common_function_manager_obj>
- $<TARGET_OBJECTS:common_agg_function_obj>
$<TARGET_OBJECTS:common_encryption_obj>
$<TARGET_OBJECTS:common_http_client_obj>
$<TARGET_OBJECTS:common_time_utils_obj>
diff --git a/src/optimizer/test/CMakeLists.txt b/src/optimizer/test/CMakeLists.txt
index 14fe395054389f267d8b8d54007f8e2e9d751fd1..ba3c961f0c0752d096d44de3468bbb09c61d991d 100644
--- a/src/optimizer/test/CMakeLists.txt
+++ b/src/optimizer/test/CMakeLists.txt
@@ -26,7 +26,6 @@ set(OPTIMIZER_TEST_LIB
$<TARGET_OBJECTS:common_encryption_obj>
$<TARGET_OBJECTS:common_http_client_obj>
$<TARGET_OBJECTS:common_process_obj>
- $<TARGET_OBJECTS:common_agg_function_obj>
$<TARGET_OBJECTS:common_time_utils_obj>
$<TARGET_OBJECTS:common_graph_obj>
$<TARGET_OBJECTS:common_ft_es_graph_adapter_obj>
diff --git a/src/parser/AdminSentences.cpp b/src/parser/AdminSentences.cpp
index bd036555031e825715922d0e5f6d6e730e4a7783..d00285aee4f666b6d33d01ea1ec1dddf409da1f6 100644
--- a/src/parser/AdminSentences.cpp
+++ b/src/parser/AdminSentences.cpp
@@ -185,7 +185,7 @@ std::string AdminJobSentence::toString() const {
case meta::cpp2::AdminJobOp::RECOVER:
return "recover job";
}
- LOG(FATAL) << "Unkown job operation " << static_cast<uint8_t>(op_);
+ LOG(FATAL) << "Unknown job operation " << static_cast<uint8_t>(op_);
}
meta::cpp2::AdminJobOp AdminJobSentence::getOp() const {
diff --git a/src/parser/Clauses.cpp b/src/parser/Clauses.cpp
index 9044188c9fb64fc92f2895bb17660d8713bce2ec..73104504c33439b6bd59cedd5f3b6ed9b66f5b5b 100644
--- a/src/parser/Clauses.cpp
+++ b/src/parser/Clauses.cpp
@@ -115,14 +115,7 @@ std::string WhereClause::toString() const {
std::string YieldColumn::toString() const {
std::string buf;
buf.reserve(256);
- if (aggFunName_ != nullptr) {
- buf += *aggFunName_;
- buf += "(";
- buf += expr_->toString();
- buf += ")";
- } else {
- buf += expr_->toString();
- }
+ buf += expr_->toString();
if (alias_ != nullptr) {
buf += " AS ";
@@ -162,10 +155,6 @@ bool operator==(const YieldColumn &l, const YieldColumn &r) {
return false;
}
- if (l.getAggFunName() != r.getAggFunName()) {
- return false;
- }
-
return true;
}
diff --git a/src/parser/Clauses.h b/src/parser/Clauses.h
index f20b7fb88f9c1744948b9c8c9bf366e9d9e03efa..856f6ca5aafc41700d1785aa2baac587be7bf4fc 100644
--- a/src/parser/Clauses.h
+++ b/src/parser/Clauses.h
@@ -203,6 +203,10 @@ public:
filter_.reset(expr);
}
+ std::unique_ptr<WhereClause> clone() const {
+ return std::make_unique<WhereClause>(filter_->clone().release());
+ }
+
std::string toString() const;
private:
@@ -223,9 +227,6 @@ public:
if (alias_ != nullptr) {
col->setAlias(new std::string(*alias_));
}
- if (aggFunName_ != nullptr) {
- col->setAggFunction(new std::string(*aggFunName_));
- }
return col;
}
@@ -245,26 +246,11 @@ public:
return alias_.get();
}
- void setAggFunction(std::string* fun = nullptr) {
- if (fun == nullptr) {
- return;
- }
- aggFunName_.reset(fun);
- }
-
- std::string getAggFunName() const {
- if (aggFunName_ == nullptr) {
- return "";
- }
- return *aggFunName_;
- }
-
std::string toString() const;
private:
std::unique_ptr<Expression> expr_;
std::unique_ptr<std::string> alias_;
- std::unique_ptr<std::string> aggFunName_{nullptr};
};
bool operator==(const YieldColumn &l, const YieldColumn &r);
@@ -294,6 +280,14 @@ public:
return columns_.empty();
}
+ std::unique_ptr<YieldColumns> clone() const {
+ auto cols = std::make_unique<YieldColumns>();
+ for (auto& col : columns_) {
+ cols->addColumn(col->clone().release());
+ }
+ return cols;
+ }
+
std::string toString() const;
const YieldColumn* back() const {
@@ -328,6 +322,11 @@ public:
return distinct_;
}
+ std::unique_ptr<YieldClause> clone() const {
+ auto cols = yieldColumns_->clone();
+ return std::make_unique<YieldClause>(cols.release(), distinct_);
+ }
+
std::string toString() const;
private:
diff --git a/src/parser/TraverseSentences.cpp b/src/parser/TraverseSentences.cpp
index 9c689d634f9186e97cb6e4f7b603342c6588e456..c4312ddc87073651f08596e1894eef271cd95ffd 100644
--- a/src/parser/TraverseSentences.cpp
+++ b/src/parser/TraverseSentences.cpp
@@ -6,6 +6,7 @@
#include "common/base/Base.h"
#include "parser/TraverseSentences.h"
+#include "util/ExpressionUtils.h"
namespace nebula {
@@ -104,7 +105,7 @@ std::string OrderFactor::toString() const {
case DESCEND:
return folly::stringPrintf("%s DESC,", expr_->toString().c_str());
default:
- LOG(FATAL) << "Unkown Order Type: " << orderType_;
+ LOG(FATAL) << "Unknown Order Type: " << orderType_;
}
}
@@ -214,6 +215,15 @@ std::string LimitSentence::toString() const {
return folly::stringPrintf("LIMIT %ld,%ld", offset_, count_);
}
+bool YieldSentence::hasAgg() const {
+ for (auto* col : columns()) {
+ if (graph::ExpressionUtils::findAny(col->expr(), {Expression::Kind::kAggregate})) {
+ return true;
+ }
+ }
+ return false;
+}
+
std::string YieldSentence::toString() const {
std::string buf;
buf.reserve(256);
diff --git a/src/parser/TraverseSentences.h b/src/parser/TraverseSentences.h
index f3baece9cb8a243c7aceaab511971f997ecfa026..e23d4797ae013a1a02d265202894ccd139f6c34c 100644
--- a/src/parser/TraverseSentences.h
+++ b/src/parser/TraverseSentences.h
@@ -554,6 +554,7 @@ public:
return yieldClause_.get();
}
+ bool hasAgg() const;
std::string toString() const override;
private:
@@ -563,14 +564,23 @@ private:
class GroupBySentence final : public Sentence {
public:
- GroupBySentence() {
+ GroupBySentence(YieldClause* yield = nullptr,
+ WhereClause* having = nullptr,
+ GroupClause* groupby = nullptr) {
kind_ = Kind::kGroupBy;
+ yieldClause_.reset(yield);
+ havingClause_.reset(having);
+ groupClause_.reset(groupby);
}
void setGroupClause(GroupClause *clause) {
groupClause_.reset(clause);
}
+ void setHavingClause(WhereClause *clause) {
+ havingClause_.reset(clause);
+ }
+
void setYieldClause(YieldClause *clause) {
yieldClause_.reset(clause);
}
@@ -579,6 +589,10 @@ public:
return groupClause_.get();
}
+ const WhereClause* havingClause() const {
+ return havingClause_.get();
+ }
+
const YieldClause* yieldClause() const {
return yieldClause_.get();
}
@@ -586,8 +600,9 @@ public:
std::string toString() const override;
private:
- std::unique_ptr<GroupClause> groupClause_;
std::unique_ptr<YieldClause> yieldClause_;
+ std::unique_ptr<WhereClause> havingClause_;
+ std::unique_ptr<GroupClause> groupClause_;
};
class GetSubgraphSentence final : public Sentence {
diff --git a/src/parser/parser.yy b/src/parser/parser.yy
index 814adbdb79d17b5177428a1e7a6702bf5d05f3f7..6f1c28334d3f46629d09572f307d61cf72130080 100644
--- a/src/parser/parser.yy
+++ b/src/parser/parser.yy
@@ -24,6 +24,8 @@
#include "common/expression/CaseExpression.h"
#include "common/expression/TextSearchExpression.h"
#include "common/expression/ListComprehensionExpression.h"
+#include "common/expression/AggregateExpression.h"
+
#include "util/SchemaUtil.h"
#include "util/ParserUtil.h"
#include "context/QueryContext.h"
@@ -213,6 +215,7 @@ static constexpr size_t MAX_ABS_INTEGER = 9223372036854775808ULL;
%type <expr> case_expression
%type <expr> list_comprehension_expression
%type <expr> compound_expression
+%type <expr> aggregate_expression
%type <expr> text_search_expression
%type <argument_list> argument_list opt_argument_list
%type <type> type_spec
@@ -490,7 +493,6 @@ unreserved_keyword
agg_function
: KW_COUNT { $$ = new std::string("COUNT"); }
- | KW_COUNT_DISTINCT { $$ = new std::string("COUNT_DISTINCT"); }
| KW_SUM { $$ = new std::string("SUM"); }
| KW_AVG { $$ = new std::string("AVG"); }
| KW_MAX { $$ = new std::string("MAX"); }
@@ -527,6 +529,9 @@ expression
| compound_expression {
$$ = $1;
}
+ | aggregate_expression {
+ $$ = $1;
+ }
| MINUS {
scanner.setUnaryMinus(true);
} expression %prec UNARY_MINUS {
@@ -834,6 +839,23 @@ function_call_expression
}
;
+aggregate_expression
+ : agg_function L_PAREN expression R_PAREN {
+ $$ = new AggregateExpression($1, $3, false/*distinct*/);
+ }
+ | agg_function L_PAREN KW_DISTINCT expression R_PAREN {
+ $$ = new AggregateExpression($1, $4, true/*distinct*/);
+ }
+ | agg_function L_PAREN STAR R_PAREN {
+ auto star = new ConstantExpression(std::string("*"));
+ $$ = new AggregateExpression($1, star, false/*distinct*/);
+ }
+ | agg_function L_PAREN KW_DISTINCT STAR R_PAREN {
+ auto star = new ConstantExpression(std::string("*"));
+ $$ = new AggregateExpression($1, star, true/*distinct*/);
+ }
+ ;
+
uuid_expression
: KW_UUID L_PAREN STRING R_PAREN {
$$ = new UUIDExpression($3);
@@ -1148,28 +1170,6 @@ yield_column
| expression KW_AS name_label {
$$ = new YieldColumn($1, $3);
}
- | agg_function L_PAREN expression R_PAREN {
- auto yield = new YieldColumn($3);
- yield->setAggFunction($1);
- $$ = yield;
- }
- | agg_function L_PAREN expression R_PAREN KW_AS name_label {
- auto yield = new YieldColumn($3, $6);
- yield->setAggFunction($1);
- $$ = yield;
- }
- | agg_function L_PAREN STAR R_PAREN {
- auto expr = new ConstantExpression(std::string("*"));
- auto yield = new YieldColumn(expr);
- yield->setAggFunction($1);
- $$ = yield;
- }
- | agg_function L_PAREN STAR R_PAREN KW_AS name_label {
- auto expr = new ConstantExpression(std::string("*"));
- auto yield = new YieldColumn(expr, $6);
- yield->setAggFunction($1);
- $$ = yield;
- }
;
group_clause
diff --git a/src/parser/test/CMakeLists.txt b/src/parser/test/CMakeLists.txt
index 7d64edd4e4d6fc1a747e7936964d32b5287f621e..7eba4edd355a2ec3be355a1e36d3666ea1ccb716 100644
--- a/src/parser/test/CMakeLists.txt
+++ b/src/parser/test/CMakeLists.txt
@@ -17,7 +17,6 @@ set(PARSER_TEST_LIBS
$<TARGET_OBJECTS:common_datatypes_obj>
$<TARGET_OBJECTS:common_base_obj>
$<TARGET_OBJECTS:common_function_manager_obj>
- $<TARGET_OBJECTS:common_agg_function_obj>
$<TARGET_OBJECTS:common_meta_thrift_obj>
$<TARGET_OBJECTS:common_graph_thrift_obj>
$<TARGET_OBJECTS:common_http_client_obj>
diff --git a/src/parser/test/ParserTest.cpp b/src/parser/test/ParserTest.cpp
index 49da92d44eaf8ccd961041fc50a48a8455e995af..5fd931e986e2392190f8cc1ae478997be965811a 100644
--- a/src/parser/test/ParserTest.cpp
+++ b/src/parser/test/ParserTest.cpp
@@ -2061,15 +2061,67 @@ TEST(Parser, GroupBy) {
{
GQLParser parser;
std::string query = "GO FROM \"1\" OVER work "
- "YIELD $$.company.name, $^.person.name "
- "| GROUP BY $$.company.name "
- "YIELD $$.company.name as name, "
+ "YIELD $$.company.name, $^.person.name AS name "
+ "| GROUP BY $$.company.name , abs($$.company.age+1)"
+ "YIELD $$.company.name AS name, "
+ "COUNT($^.person.name ), "
+ "SUM($^.person.name ), "
+ "AVG($^.person.name ), "
+ "(INT)abs($$.company.age+1), "
+ "(INT)COUNT(DISTINCT $^.person.name ), "
+ "COUNT(DISTINCT $-.name )+1, "
+ "abs(SUM(DISTINCT $$.person.name )), "
+ "AVG(DISTINCT $^.person.name ), "
+ "AVG(DISTINCT $-.name ), "
+ "COUNT(*), "
+ "COUNT(DISTINCT *), "
+ "MAX($^.person.name ), "
+ "MIN($$.person.name ), "
+ "STD($^.person.name ), "
+ "BIT_AND($^.person.name ), "
+ "BIT_OR($$.person.name ), "
+ "BIT_XOR($^.person.name ),"
+ "STD(DISTINCT $^.person.name ), "
+ "BIT_AND(DISTINCT $$.person.name ), "
+ "BIT_OR(DISTINCT $^.person.name ), "
+ "BIT_XOR(DISTINCT $$.person.name ),"
+ "BIT_XOR(DISTINCT $-.name ),"
+ "F_STD($^.person.name ), "
+ "F_BIT_AND($^.person.name ), "
+ "F_BIT_OR($^.person.name ), "
+ "F_BIT_XOR($^.person.name )";
+
+ auto result = parser.parse(query);
+ ASSERT_TRUE(result.ok()) << result.status();
+ }
+ {
+ GQLParser parser;
+ std::string query = "GO FROM \"1\" OVER work "
+ "YIELD $$.company.name, $^.person.name AS name "
+ "| YIELD $$.company.name AS name, "
+ " abs($$.company.age+1), "
"COUNT($^.person.name ), "
- "COUNT_DISTINCT($^.person.name ), "
"SUM($^.person.name ), "
"AVG($^.person.name ), "
+ "(INT)abs($$.company.age+1), "
+ "(INT)COUNT(DISTINCT $^.person.name ), "
+ "COUNT(DISTINCT $-.name )+1, "
+ "abs(SUM(DISTINCT $$.person.name )), "
+ "AVG(DISTINCT $^.person.name ), "
+ "AVG(DISTINCT $-.name ), "
+ "COUNT(*), "
+ "COUNT(DISTINCT *), "
"MAX($^.person.name ), "
- "MIN($^.person.name ), "
+ "MIN($$.person.name ), "
+ "STD($^.person.name ), "
+ "BIT_AND($^.person.name ), "
+ "BIT_OR($$.person.name ), "
+ "BIT_XOR($^.person.name ),"
+ "STD(DISTINCT $^.person.name ), "
+ "BIT_AND(DISTINCT $$.person.name ), "
+ "BIT_OR(DISTINCT $^.person.name ), "
+ "BIT_XOR(DISTINCT $$.person.name ),"
+ "BIT_XOR(DISTINCT $-.name ),"
"F_STD($^.person.name ), "
"F_BIT_AND($^.person.name ), "
"F_BIT_OR($^.person.name ), "
diff --git a/src/planner/Query.cpp b/src/planner/Query.cpp
index c9411c7f665cf96d6320ece045e9a2f16df30872..713d752dc8b925c63e0bc37bf4cc6316f857781e 100644
--- a/src/planner/Query.cpp
+++ b/src/planner/Query.cpp
@@ -200,11 +200,9 @@ std::unique_ptr<PlanNodeDescription> Aggregate::explain() const {
auto desc = SingleInputNode::explain();
addDescription("groupKeys", folly::toJson(util::toJson(groupKeys_)), desc.get());
folly::dynamic itemArr = folly::dynamic::array();
- for (const auto& item : groupItems_) {
+ for (auto* item : groupItems_) {
folly::dynamic itemObj = folly::dynamic::object();
- itemObj.insert("distinct", util::toJson(item.distinct));
- itemObj.insert("funcType", static_cast<uint8_t>(item.func));
- itemObj.insert("expr", item.expr ? item.expr->toString() : "");
+ itemObj.insert("expr", item? item->toString() : "");
itemArr.push_back(itemObj);
}
addDescription("groupItems", folly::toJson(itemArr), desc.get());
diff --git a/src/planner/Query.h b/src/planner/Query.h
index 223ea6428a7070ec5ac55f2d6a729ee99df596cf..ab6751c0bbf3e3adcbc8e90f91c95faabb57c28e 100644
--- a/src/planner/Query.h
+++ b/src/planner/Query.h
@@ -8,7 +8,7 @@
#define PLANNER_QUERY_H_
#include "common/base/Base.h"
-#include "common/function/AggregateFunction.h"
+#include "common/expression/AggregateExpression.h"
#include "common/interface/gen-cpp2/storage_types.h"
#include "context/QueryContext.h"
#include "parser/Clauses.h"
@@ -804,18 +804,10 @@ private:
*/
class Aggregate final : public SingleInputNode {
public:
- struct GroupItem {
- GroupItem(Expression* e, AggFun::Function f, bool d)
- : expr(e), func(f), distinct(d) {}
- Expression* expr;
- AggFun::Function func;
- bool distinct = false;
- };
-
static Aggregate* make(QueryContext* qctx,
PlanNode* input,
std::vector<Expression*>&& groupKeys,
- std::vector<GroupItem>&& groupItems) {
+ std::vector<Expression*>&& groupItems) {
return qctx->objPool()->add(
new Aggregate(qctx, input, std::move(groupKeys), std::move(groupItems)));
}
@@ -824,7 +816,7 @@ public:
return groupKeys_;
}
- const std::vector<GroupItem>& groupItems() const {
+ const std::vector<Expression*>& groupItems() const {
return groupItems_;
}
@@ -834,7 +826,7 @@ private:
Aggregate(QueryContext* qctx,
PlanNode* input,
std::vector<Expression*>&& groupKeys,
- std::vector<GroupItem>&& groupItems)
+ std::vector<Expression*>&& groupItems)
: SingleInputNode(qctx, Kind::kAggregate, input) {
groupKeys_ = std::move(groupKeys);
groupItems_ = std::move(groupItems);
@@ -842,7 +834,7 @@ private:
private:
std::vector<Expression*> groupKeys_;
- std::vector<GroupItem> groupItems_;
+ std::vector<Expression*> groupItems_;
};
class SwitchSpace final : public SingleInputNode {
diff --git a/src/planner/match/StartVidFinder.cpp b/src/planner/match/StartVidFinder.cpp
index a8db5e6d98e8e2cd0212222ce6ad03d2234b1146..eeba83d03dd9214afa7ee06db6e67bdb3940ba32 100644
--- a/src/planner/match/StartVidFinder.cpp
+++ b/src/planner/match/StartVidFinder.cpp
@@ -33,7 +33,7 @@ StatusOr<SubPlan> StartVidFinder::transform(PatternContext* patternCtx) {
return transformEdge(edgeCtx);
}
- return Status::Error("Unkown pattern kind.");
+ return Status::Error("Unknown pattern kind.");
}
} // namespace graph
diff --git a/src/util/ExpressionUtils.h b/src/util/ExpressionUtils.h
index 9e549863bd644129287c5cc7c1abb5003e0da41d..2523024c24b6760d009f490c3a9e29258c1ccadf 100644
--- a/src/util/ExpressionUtils.h
+++ b/src/util/ExpressionUtils.h
@@ -10,6 +10,7 @@
#include "common/expression/BinaryExpression.h"
#include "common/expression/Expression.h"
#include "common/expression/FunctionCallExpression.h"
+#include "common/expression/AggregateExpression.h"
#include "common/expression/LabelExpression.h"
#include "common/expression/PropertyExpression.h"
#include "common/expression/TypeCastingExpression.h"
diff --git a/src/util/test/CMakeLists.txt b/src/util/test/CMakeLists.txt
index 98ae172999e2b11fe3346c2d35af48c2a4a6587f..d71c6c17cb0acc9c837683c259aacd722098d8bf 100644
--- a/src/util/test/CMakeLists.txt
+++ b/src/util/test/CMakeLists.txt
@@ -26,7 +26,6 @@ nebula_add_test(
$<TARGET_OBJECTS:common_encryption_obj>
$<TARGET_OBJECTS:common_http_client_obj>
$<TARGET_OBJECTS:common_process_obj>
- $<TARGET_OBJECTS:common_agg_function_obj>
$<TARGET_OBJECTS:common_time_utils_obj>
$<TARGET_OBJECTS:common_graph_obj>
$<TARGET_OBJECTS:common_ft_es_graph_adapter_obj>
diff --git a/src/validator/GetSubgraphValidator.cpp b/src/validator/GetSubgraphValidator.cpp
index ea176d82f4710b88ed60b66184aa1706f78367f0..d45746dbae48da79ea7fbb1df77e6da06ba68463 100644
--- a/src/validator/GetSubgraphValidator.cpp
+++ b/src/validator/GetSubgraphValidator.cpp
@@ -225,15 +225,14 @@ Status GetSubgraphValidator::zeroStep(PlanNode* depend, const std::string& input
getVertex->setInputVar(inputVar);
auto var = vctx_->anonVarGen()->getVar();
- auto* column = new YieldColumn(new VertexExpression(), new std::string("_vertices"));
- qctx_->objPool()->add(column);
- column->setAggFunction(new std::string("COLLECT"));
- auto fun = column->getAggFunName();
+ auto* column = new VertexExpression();
+ auto* func = new AggregateExpression(new std::string("COLLECT"), column, false);
+ qctx_->objPool()->add(func);
auto* collectVertex =
Aggregate::make(qctx_,
getVertex,
{},
- {Aggregate::GroupItem(column->expr(), AggFun::nameIdMap_[fun], false)});
+ {func});
collectVertex->setInputVar(getVertex->outputVar());
collectVertex->setColNames({"_vertices"});
@@ -259,17 +258,14 @@ Status GetSubgraphValidator::toPlan() {
startVidsVar = dedupStartVid->outputVar();
// collect runtime startVids
auto var = vctx_->anonVarGen()->getVar();
- auto* column = new YieldColumn(
- new VariablePropertyExpression(new std::string(var), new std::string(kVid)),
- new std::string(kVid));
- qctx_->objPool()->add(column);
- column->setAggFunction(new std::string("COLLECT_SET"));
- auto fun = column->getAggFunName();
+ auto* column = new VariablePropertyExpression(new std::string(var), new std::string(kVid));
+ auto* func = new AggregateExpression(new std::string("COLLECT_SET"), column, true);
+ qctx_->objPool()->add(func);
collectRunTimeStartVids =
Aggregate::make(qctx_,
dedupStartVid,
{},
- {Aggregate::GroupItem(column->expr(), AggFun::nameIdMap_[fun], true)});
+ {func});
collectRunTimeStartVids->setInputVar(dedupStartVid->outputVar());
collectRunTimeStartVids->setColNames({kVid});
runtimeStartVar_ = collectRunTimeStartVids->outputVar();
@@ -298,17 +294,14 @@ Status GetSubgraphValidator::toPlan() {
auto* projectVids = projectDstVidsFromGN(gn, startVidsVar);
auto var = vctx_->anonVarGen()->getVar();
- auto* column =
- new YieldColumn(new VariablePropertyExpression(new std::string(var), new std::string(kVid)),
- new std::string(kVid));
- qctx_->objPool()->add(column);
- column->setAggFunction(new std::string("COLLECT_SET"));
- auto fun = column->getAggFunName();
+ auto* column = new VariablePropertyExpression(new std::string(var), new std::string(kVid));
+ auto* func = new AggregateExpression(new std::string("COLLECT_SET"), column, true);
+ qctx_->objPool()->add(func);
auto* collect =
Aggregate::make(qctx_,
projectVids,
{},
- {Aggregate::GroupItem(column->expr(), AggFun::nameIdMap_[fun], true)});
+ {func});
collect->setInputVar(projectVids->outputVar());
collect->setColNames({kVid});
collectVar_ = collect->outputVar();
diff --git a/src/validator/GoValidator.cpp b/src/validator/GoValidator.cpp
index 6f241a8906cf097213ff13f70710e73f4f16ea96..ae0ffdc6ad354e14a94fa42e22f0d4e93ce61eba 100644
--- a/src/validator/GoValidator.cpp
+++ b/src/validator/GoValidator.cpp
@@ -51,7 +51,11 @@ Status GoValidator::validateWhere(WhereClause* where) {
}
filter_ = where->filter();
-
+ if (graph::ExpressionUtils::findAny(filter_, {Expression::Kind::kAggregate})) {
+ return Status::SemanticError(
+ "`%s', not support aggregate function in where sentence.",
+ filter_->toString().c_str());
+ }
if (filter_->kind() == Expression::Kind::kLabelAttribute) {
auto laExpr = static_cast<LabelAttributeExpression*>(filter_);
where->setFilter(ExpressionUtils::rewriteLabelAttribute<EdgePropertyExpression>(laExpr));
@@ -107,8 +111,7 @@ Status GoValidator::validateYield(YieldClause* yield) {
} else {
ExpressionUtils::rewriteLabelAttribute<EdgePropertyExpression>(col->expr());
}
-
- if (!col->getAggFunName().empty()) {
+ if (graph::ExpressionUtils::findAny(col->expr(), {Expression::Kind::kAggregate})) {
return Status::SemanticError(
"`%s', not support aggregate function in go sentence.",
col->toString().c_str());
diff --git a/src/validator/GroupByValidator.cpp b/src/validator/GroupByValidator.cpp
index 9885a46508822df3b09191e3bbb5ff5892f1451d..b59281161b70af4325498850c44c7b6ae704d71c 100644
--- a/src/validator/GroupByValidator.cpp
+++ b/src/validator/GroupByValidator.cpp
@@ -6,6 +6,11 @@
#include "validator/GroupByValidator.h"
#include "planner/Query.h"
+#include "util/ExpressionUtils.h"
+#include "util/AnonColGenerator.h"
+#include "util/AnonVarGenerator.h"
+#include "visitor/RewriteAggExprVisitor.h"
+#include "visitor/FindAnySubExprVisitor.h"
namespace nebula {
@@ -15,13 +20,8 @@ Status GroupByValidator::validateImpl() {
auto *groupBySentence = static_cast<GroupBySentence*>(sentence_);
NG_RETURN_IF_ERROR(validateGroup(groupBySentence->groupClause()));
NG_RETURN_IF_ERROR(validateYield(groupBySentence->yieldClause()));
+ NG_RETURN_IF_ERROR(groupClauseSemanticCheck());
- if (!exprProps_.srcTagProps().empty() || !exprProps_.dstTagProps().empty()) {
- return Status::SemanticError("Only support input and variable in GroupBy sentence.");
- }
- if (!exprProps_.inputProps().empty() && !exprProps_.varProps().empty()) {
- return Status::SemanticError("Not support both input and variable in GroupBy sentence.");
- }
return Status::OK();
}
@@ -34,80 +34,87 @@ Status GroupByValidator::validateYield(const YieldClause *yieldClause) {
return Status::SemanticError("Yield cols is Empty");
}
+ projCols_ = qctx_->objPool()->add(new YieldColumns);
for (auto* col : columns) {
- auto fun = col->getAggFunName();
- if (!fun.empty()) {
- auto iter = AggFun::nameIdMap_.find(fun);
- if (iter == AggFun::nameIdMap_.end()) {
- return Status::SemanticError("Unkown aggregate function `%s`", fun.c_str());
- }
- if (iter->second != AggFun::Function::kCount && col->expr()->toString() == "*") {
- return Status::SemanticError("`%s` invaild, * valid in count.",
- col->toString().c_str());
- }
+ auto rewrited = false;
+ auto colOldName = deduceColName(col);
+ if (col->expr()->kind() != Expression::Kind::kAggregate) {
+ NG_RETURN_IF_ERROR(rewriteInnerAggExpr(col, rewrited));
+ }
+ auto colExpr = col->expr();
+ // collect exprs for check later
+ if (colExpr->kind() == Expression::Kind::kAggregate) {
+ auto* aggExpr = static_cast<AggregateExpression*>(colExpr);
+ NG_RETURN_IF_ERROR(checkAggExpr(aggExpr));
+ yieldAggs_.emplace_back(colExpr);
+ } else {
+ yieldCols_.emplace_back(colExpr);
}
- // todo(jmq) count(distinct)
- groupItems_.emplace_back(Aggregate::GroupItem{col->expr(), AggFun::nameIdMap_[fun], false});
-
- auto status = deduceExprType(col->expr());
+ groupItems_.emplace_back(colExpr);
+ auto status = deduceExprType(colExpr);
NG_RETURN_IF_ERROR(status);
auto type = std::move(status).value();
- auto name = deduceColName(col);
+ std::string name;
+ if (!rewrited) {
+ name = deduceColName(col);
+ projCols_->addColumn(new YieldColumn(
+ new VariablePropertyExpression(new std::string(),
+ new std::string(name)),
+ new std::string(colOldName)));
+ } else {
+ name = colExpr->toString();
+ }
+ projOutputColumnNames_.emplace_back(colOldName);
outputs_.emplace_back(name, type);
- outputColumnNames_.emplace_back(std::move(name));
- // todo(jmq) extend $-.*
+ outputColumnNames_.emplace_back(name);
- yieldCols_.emplace_back(col);
- if (col->alias() != nullptr) {
+ if (col->alias() != nullptr && !rewrited) {
aliases_.emplace(*col->alias(), col);
}
- // check input yield filed without agg function and not in group cols
ExpressionProps yieldProps;
- NG_RETURN_IF_ERROR(deduceProps(col->expr(), yieldProps));
- if (col->getAggFunName().empty()) {
- if (!yieldProps.inputProps().empty()) {
- if (!exprProps_.isSubsetOfInput(yieldProps.inputProps())) {
- return Status::SemanticError("Yield `%s` isn't in output fields",
- col->toString().c_str());
- }
- } else if (!yieldProps.varProps().empty()) {
- if (!exprProps_.isSubsetOfVar(yieldProps.varProps())) {
- return Status::SemanticError("Yield `%s` isn't in output fields",
- col->toString().c_str());
- }
- }
- }
+ NG_RETURN_IF_ERROR(deduceProps(colExpr, yieldProps));
exprProps_.unionProps(std::move(yieldProps));
}
+
return Status::OK();
}
-
Status GroupByValidator::validateGroup(const GroupClause *groupClause) {
+ if (!groupClause) return Status::OK();
std::vector<YieldColumn*> columns;
if (groupClause != nullptr) {
columns = groupClause->columns();
}
- if (columns.empty()) {
- return Status::SemanticError("Group cols is Empty");
- }
+ auto groupByValid = [](Expression::Kind kind)->bool {
+ return std::unordered_set<Expression::Kind>{
+ Expression::Kind::kAdd ,
+ Expression::Kind::kMinus,
+ Expression::Kind::kMultiply,
+ Expression::Kind::kDivision,
+ Expression::Kind::kMod,
+ Expression::Kind::kTypeCasting,
+ Expression::Kind::kFunctionCall,
+ Expression::Kind::kInputProperty,
+ Expression::Kind::kVarProperty,
+ Expression::Kind::kCase }
+ .count(kind);};
for (auto* col : columns) {
- if (col->expr()->kind() != Expression::Kind::kInputProperty &&
- col->expr()->kind() != Expression::Kind::kVarProperty &&
- col->expr()->kind() != Expression::Kind::kFunctionCall) {
- return Status::SemanticError("Group `%s` invalid", col->expr()->toString().c_str());
+ if (graph::ExpressionUtils::findAny(col->expr(), {Expression::Kind::kAggregate})
+ || !graph::ExpressionUtils::findAny(col->expr(),
+ {Expression::Kind::kInputProperty,
+ Expression::Kind::kVarProperty})) {
+ return Status::SemanticError("Group `%s' invalid", col->expr()->toString().c_str());
}
- if (!col->getAggFunName().empty()) {
- return Status::SemanticError("Use invalid group function `%s`",
- col->getAggFunName().c_str());
+ if (!groupByValid(col->expr()->kind())) {
+ return Status::SemanticError("Group `%s' invalid", col->expr()->toString().c_str());
}
+
NG_RETURN_IF_ERROR(deduceExprType(col->expr()));
NG_RETURN_IF_ERROR(deduceProps(col->expr(), exprProps_));
- groupCols_.emplace_back(col);
groupKeys_.emplace_back(col->expr());
}
return Status::OK();
@@ -116,10 +123,117 @@ Status GroupByValidator::validateGroup(const GroupClause *groupClause) {
Status GroupByValidator::toPlan() {
auto *groupBy = Aggregate::make(qctx_, nullptr, std::move(groupKeys_), std::move(groupItems_));
groupBy->setColNames(std::vector<std::string>(outputColumnNames_));
- root_ = groupBy;
+ if (needGenProject_) {
+ // rewrite Expr which has inner aggExpr and push it up to Project.
+ auto *project = Project::make(qctx_, groupBy, projCols_);
+ project->setInputVar(groupBy->outputVar());
+ project->setColNames(projOutputColumnNames_);
+ root_ = project;
+ } else {
+ root_ = groupBy;
+ }
tail_ = groupBy;
return Status::OK();
}
+Status GroupByValidator::groupClauseSemanticCheck() {
+ // check exprProps
+ if (!exprProps_.srcTagProps().empty() || !exprProps_.dstTagProps().empty()) {
+ return Status::SemanticError("Only support input and variable in GroupBy sentence.");
+ }
+ if (!exprProps_.inputProps().empty() && !exprProps_.varProps().empty()) {
+ return Status::SemanticError("Not support both input and variable in GroupBy sentence.");
+ }
+ if (!exprProps_.varProps().empty() && exprProps_.varProps().size() > 1) {
+ return Status::SemanticError("Only one variable allowed to use.");
+ }
+
+ if (groupKeys_.empty()) {
+ groupKeys_ = yieldCols_;
+ } else {
+ std::unordered_set<Expression*> groupSet(groupKeys_.begin(), groupKeys_.end());
+ FindAnySubExprVisitor groupVisitor(groupSet, true);
+ for (auto* expr : yieldCols_) {
+ if (evaluableExpr(expr)) {
+ continue;
+ }
+ expr->accept(&groupVisitor);
+ if (!groupVisitor.found()) {
+ return Status::SemanticError("Yield non-agg expression `%s' must be"
+ " functionally dependent on items in GROUP BY clause", expr->toString().c_str());
+ }
+ }
+ }
+ return Status::OK();
+}
+
+Status GroupByValidator::rewriteInnerAggExpr(YieldColumn* col, bool& rewrited) {
+ auto colOldName = deduceColName(col);
+ auto* colExpr = col->expr();
+ // agg col need not rewrite
+ DCHECK(colExpr->kind() != Expression::Kind::kAggregate);
+ auto collectAggCol = colExpr->clone();
+ auto aggs = ExpressionUtils::collectAll(collectAggCol.get(),
+ {Expression::Kind::kAggregate});
+ if (aggs.size() > 1) {
+ return Status::SemanticError("Aggregate function nesting is not allowed: `%s'",
+ collectAggCol->toString().c_str());
+ }
+ if (aggs.size() == 1) {
+ auto colRewrited = colExpr->clone();
+ // set aggExpr
+ col->setExpr(aggs[0]->clone().release());
+ auto aggColName = col->expr()->toString();
+ // rewrite to VariablePropertyExpression
+ RewriteAggExprVisitor rewriteAggVisitor(new std::string(),
+ new std::string(aggColName));
+ colRewrited->accept(&rewriteAggVisitor);
+ rewrited = true;
+ needGenProject_ = true;
+ projCols_->addColumn(new YieldColumn(colRewrited.release(),
+ new std::string(colOldName)));
+ }
+
+ return Status::OK();
+}
+
+Status GroupByValidator::checkAggExpr(AggregateExpression* aggExpr) {
+ auto func = aggExpr->name();
+ if (!func) {
+ return Status::SemanticError("`%s' aggregate function not set.",
+ aggExpr->toString().c_str());
+ }
+
+ auto iter = AggregateExpression::NAME_ID_MAP.find(func->c_str());
+ if (iter == AggregateExpression::NAME_ID_MAP.end()) {
+ return Status::SemanticError("Unknown aggregate function `%s'", func->c_str());
+ }
+
+ auto* aggArg = aggExpr->arg();
+ if (graph::ExpressionUtils::findAny(aggArg,
+ {Expression::Kind::kAggregate})) {
+ return Status::SemanticError("Aggregate function nesting is not allowed: `%s'",
+ aggExpr->toString().c_str());
+ }
+
+ if (iter->second != AggregateExpression::Function::kCount) {
+ if (aggArg->toString() == "*") {
+ return Status::SemanticError("Could not apply aggregation function `%s' on `*`",
+ aggExpr->toString().c_str());
+ }
+ if (aggArg->kind() == Expression::Kind::kInputProperty
+ || aggArg->kind() == Expression::Kind::kVarProperty) {
+ auto propExpr = static_cast<PropertyExpression*>(aggArg);
+ if (*propExpr->prop() == "*") {
+ return Status::SemanticError(
+ "Could not apply aggregation function `%s' on `%s'",
+ aggExpr->toString().c_str(), propExpr->toString().c_str());
+ }
+ }
+ }
+
+ return Status::OK();
+}
+
} // namespace graph
} // namespace nebula
diff --git a/src/validator/GroupByValidator.h b/src/validator/GroupByValidator.h
index 56d5cf3569e0b75945db283e87a4681d913179ec..c8ac22c026bc6ccbd690293b7104a152531bff3c 100644
--- a/src/validator/GroupByValidator.h
+++ b/src/validator/GroupByValidator.h
@@ -20,26 +20,35 @@ public:
GroupByValidator(Sentence *sentence, QueryContext *context)
: Validator(sentence, context) {}
-private:
Status validateImpl() override;
Status toPlan() override;
+private:
Status validateGroup(const GroupClause *groupClause);
Status validateYield(const YieldClause *yieldClause);
+ Status groupClauseSemanticCheck();
+ Status rewriteInnerAggExpr(YieldColumn* col, bool& rewrited);
+ Status checkAggExpr(AggregateExpression* aggExpr);
+
private:
- std::vector<YieldColumn*> groupCols_;
- std::vector<YieldColumn*> yieldCols_;
+ std::vector<Expression*> yieldCols_;
+ std::vector<Expression*> yieldAggs_;
// key: alias, value: input name
std::unordered_map<std::string, YieldColumn*> aliases_;
+ bool needGenProject_{false};
std::vector<std::string> outputColumnNames_;
+ std::vector<std::string> projOutputColumnNames_;
+
+ // used to generate Project node when there is an internally nested aggregateExpression
+ YieldColumns* projCols_;
std::vector<Expression*> groupKeys_;
- std::vector<Aggregate::GroupItem> groupItems_;
+ std::vector<Expression*> groupItems_;
};
diff --git a/src/validator/Validator.h b/src/validator/Validator.h
index c14a759cabb5b8dadef3a40c2d49ba153a122032..c3ac76f1fcc31c0425a28742d0884f7e87891f29 100644
--- a/src/validator/Validator.h
+++ b/src/validator/Validator.h
@@ -42,10 +42,6 @@ public:
inputVarName_ = std::move(name);
}
- void setInputCols(ColsDef&& inputs) {
- inputs_ = std::move(inputs);
- }
-
QueryContext* qctx() {
return qctx_;
}
@@ -62,10 +58,50 @@ public:
return outputs_;
}
+ void setOutputCols(const ColsDef&& outputCols) {
+ outputs_ = std::move(outputCols);
+ }
+
+ void setOutputCols(ColsDef& outputCols) {
+ outputs_ = outputCols;
+ }
+
ColsDef inputCols() const {
return inputs_;
}
+ void setInputCols(ColsDef&& inputCols) {
+ inputs_ = std::move(inputCols);
+ }
+
+ void setInputCols(const ColsDef& inputCols) {
+ inputs_ = inputCols;
+ }
+
+ ExpressionProps exprProps() const {
+ return exprProps_;
+ }
+
+ void setExprProps(ExpressionProps&& exprProps) {
+ exprProps_ = std::move(exprProps);
+ }
+
+ void setExprProps(const ExpressionProps& exprProps) {
+ exprProps_ = exprProps;
+ }
+
+ const std::set<std::string>& userDefinedVarNameList() const {
+ return userDefinedVarNameList_;
+ }
+
+ void setUserDefinedVarNameList(std::set<std::string>&& userDefinedVarNameList) {
+ userDefinedVarNameList_ = std::move(userDefinedVarNameList);
+ }
+
+ void setUserDefinedVarNameList(const std::set<std::string>& userDefinedVarNameList) {
+ userDefinedVarNameList_ = userDefinedVarNameList;
+ }
+
void setNoSpaceRequired() {
noSpaceRequired_ = true;
}
@@ -141,19 +177,19 @@ protected:
Sentence* sentence_{nullptr};
QueryContext* qctx_{nullptr};
ValidateContext* vctx_{nullptr};
- // The input columns and output columns of a sentence.
- ColsDef outputs_;
- ColsDef inputs_;
// The variable name of the input node.
std::string inputVarName_;
// Admin sentences do not requires a space to be chosen.
bool noSpaceRequired_{false};
+ // The input columns and output columns of a sentence.
+ ColsDef outputs_;
+ ColsDef inputs_;
+ ExpressionProps exprProps_;
+
// root and tail of a subplan.
PlanNode* root_{nullptr};
PlanNode* tail_{nullptr};
-
- ExpressionProps exprProps_;
// user define Variable name list
std::set<std::string> userDefinedVarNameList_;
// vid's Type
diff --git a/src/validator/YieldValidator.cpp b/src/validator/YieldValidator.cpp
index 8d175de3b7a94d7c95439dd0cc453a70ccda9b2e..4de8f571785cfa95bd39b15ad84c4ea4a4f9d10b 100644
--- a/src/validator/YieldValidator.cpp
+++ b/src/validator/YieldValidator.cpp
@@ -28,8 +28,15 @@ Status YieldValidator::validateImpl() {
}
auto yield = static_cast<YieldSentence *>(sentence_);
- NG_RETURN_IF_ERROR(validateYieldAndBuildOutputs(yield->yield()));
+ if (yield->hasAgg()) {
+ NG_RETURN_IF_ERROR(makeImplicitGroupByValidator());
+ }
NG_RETURN_IF_ERROR(validateWhere(yield->where()));
+ if (groupByValidator_) {
+ NG_RETURN_IF_ERROR(validateImplicitGroupBy());
+ } else {
+ NG_RETURN_IF_ERROR(validateYieldAndBuildOutputs(yield->yield()));
+ }
if (!exprProps_.srcTagProps().empty() || !exprProps_.dstTagProps().empty() ||
!exprProps_.edgeProps().empty()) {
@@ -44,39 +51,23 @@ Status YieldValidator::validateImpl() {
return Status::SemanticError("Only one variable allowed to use.");
}
- if (hasAggFun_) {
- NG_RETURN_IF_ERROR(checkAggFunAndBuildGroupItems(yield->yield()));
- }
-
- if (exprProps_.inputProps().empty() && exprProps_.varProps().empty() && inputVarName_.empty()) {
+ if (!groupByValidator_ && exprProps_.inputProps().empty()
+ && exprProps_.varProps().empty() && inputVarName_.empty()) {
// generate constant expression result into querycontext
genConstantExprValues();
}
if (!exprProps_.varProps().empty() && !userDefinedVarNameList_.empty()) {
+ // TODO: Support Multiple userDefinedVars
+ if (userDefinedVarNameList_.size() != 1) {
+ return Status::SemanticError("Multiple user defined vars not supported yet.");
+ }
userDefinedVarName_ = *userDefinedVarNameList_.begin();
}
return Status::OK();
}
-Status YieldValidator::checkAggFunAndBuildGroupItems(const YieldClause *clause) {
- auto yield = clause->yields();
- for (auto column : yield->columns()) {
- auto expr = column->expr();
- auto fun = column->getAggFunName();
- if (!evaluableExpr(expr) && fun.empty()) {
- return Status::SemanticError(
- "Input columns without aggregation are not supported in YIELD statement "
- "without GROUP BY, near `%s'",
- expr->toString().c_str());
- }
-
- groupItems_.emplace_back(Aggregate::GroupItem{expr, AggFun::nameIdMap_[fun], false});
- }
- return Status::OK();
-}
-
Status YieldValidator::makeOutputColumn(YieldColumn *column) {
columns_->addColumn(column);
@@ -117,6 +108,30 @@ void YieldValidator::genConstantExprValues() {
ResultBuilder().value(Value(std::move(ds))).finish());
}
+Status YieldValidator::makeImplicitGroupByValidator() {
+ auto* groupSentence = qctx()->objPool()->add(
+ new GroupBySentence(
+ static_cast<YieldSentence *>(sentence_)->yield()->clone().release(),
+ nullptr, nullptr));
+ groupByValidator_ = std::make_unique<GroupByValidator>(groupSentence, qctx());
+ groupByValidator_->setInputCols(inputs_);
+
+ return Status::OK();
+}
+
+Status YieldValidator::validateImplicitGroupBy() {
+ NG_RETURN_IF_ERROR(groupByValidator_->validateImpl());
+ inputs_ = groupByValidator_->inputCols();
+ outputs_ = groupByValidator_->outputCols();
+ exprProps_.unionProps(groupByValidator_->exprProps());
+
+ const auto& groupVars = groupByValidator_->userDefinedVarNameList();
+ // TODO: Support Multiple userDefinedVars
+ userDefinedVarNameList_.insert(groupVars.begin(), groupVars.end());
+
+ return Status::OK();
+}
+
Status YieldValidator::validateYieldAndBuildOutputs(const YieldClause *clause) {
auto columns = clause->columns();
columns_ = qctx_->objPool()->add(new YieldColumns);
@@ -133,9 +148,6 @@ Status YieldValidator::validateYieldAndBuildOutputs(const YieldClause *clause) {
auto newExpr = new InputPropertyExpression(new std::string(colDef.name));
NG_RETURN_IF_ERROR(makeOutputColumn(new YieldColumn(newExpr)));
}
- if (!column->getAggFunName().empty()) {
- return Status::SemanticError("could not apply aggregation function on `$-.*'");
- }
continue;
}
} else if (expr->kind() == Expression::Kind::kVarProperty) {
@@ -152,23 +164,10 @@ Status YieldValidator::validateYieldAndBuildOutputs(const YieldClause *clause) {
new std::string(colDef.name));
NG_RETURN_IF_ERROR(makeOutputColumn(new YieldColumn(newExpr)));
}
- if (!column->getAggFunName().empty()) {
- return Status::SemanticError("could not apply aggregation function on `$%s.*'",
- var->c_str());
- }
continue;
}
}
- auto fun = column->getAggFunName();
- if (!fun.empty()) {
- auto foundAgg = AggFun::nameIdMap_.find(fun);
- if (foundAgg == AggFun::nameIdMap_.end()) {
- return Status::SemanticError("Unkown aggregate function: `%s'", fun.c_str());
- }
- hasAggFun_ = true;
- }
-
NG_RETURN_IF_ERROR(makeOutputColumn(column->clone().release()));
}
@@ -208,28 +207,43 @@ Status YieldValidator::toPlan() {
}
SingleInputNode *dedupDep = nullptr;
- if (!hasAggFun_) {
- dedupDep = Project::make(qctx_, filter, columns_);
- } else {
- // We do not use group items later, so move it is safe
- dedupDep = Aggregate::make(qctx_, filter, {}, std::move(groupItems_));
- }
- if (filter == nullptr && !inputVar.empty()) {
- dedupDep->setInputVar(inputVar);
- }
- dedupDep->setColNames(std::move(outputColumnNames_));
- if (filter != nullptr) {
- dedupDep->setInputVar(filter->outputVar());
- tail_ = filter;
+ if (groupByValidator_) {
+ groupByValidator_->toPlan();
+ auto* groupByValidatorRoot = groupByValidator_->root();
+ auto* groupByValidatorTail = groupByValidator_->tail();
+ // groupBy validator only gen Project or Aggregate Node
+ DCHECK(groupByValidatorRoot->isSingleInput());
+ DCHECK(groupByValidatorTail->isSingleInput());
+ dedupDep = static_cast<SingleInputNode*>(groupByValidatorRoot);
+ if (filter != nullptr) {
+ if (!inputVar.empty()) {
+ filter->setInputVar(inputVar);
+ }
+ static_cast<SingleInputNode*>(groupByValidatorTail)->dependsOn(filter);
+ static_cast<SingleInputNode*>(groupByValidatorTail)->setInputVar(filter->outputVar());
+ tail_ = filter;
+ } else {
+ tail_ = groupByValidatorTail;
+ if (!inputVar.empty()) {
+ static_cast<SingleInputNode*>(tail_)->setInputVar(inputVar);
+ }
+ }
} else {
- tail_ = dedupDep;
+ dedupDep = Project::make(qctx_, filter, columns_);
+ dedupDep->setColNames(std::move(outputColumnNames_));
+ if (filter != nullptr) {
+ dedupDep->setInputVar(filter->outputVar());
+ tail_ = filter;
+ } else {
+ tail_ = dedupDep;
+ }
+ // Otherwise the input of tail_ would be set by pipe.
+ if (!inputVar.empty()) {
+ static_cast<SingleInputNode *>(tail_)->setInputVar(inputVar);
+ }
}
- // Otherwise the input of tail_ would be set by pipe.
- if (!inputVar.empty()) {
- static_cast<SingleInputNode *>(tail_)->setInputVar(inputVar);
- }
if (yield->yield()->isDistinct()) {
auto dedup = Dedup::make(qctx_, dedupDep);
diff --git a/src/validator/YieldValidator.h b/src/validator/YieldValidator.h
index f5b50a91df953911b8d0d1eb6956e6df71cc557d..93b7c108f28362cf5d30e276143f74ae87bc9300 100644
--- a/src/validator/YieldValidator.h
+++ b/src/validator/YieldValidator.h
@@ -12,6 +12,7 @@
#include "common/base/Status.h"
#include "planner/Query.h"
#include "validator/Validator.h"
+#include "validator/GroupByValidator.h"
namespace nebula {
@@ -37,18 +38,19 @@ public:
private:
Status validateYieldAndBuildOutputs(const YieldClause *clause);
Status validateWhere(const WhereClause *clause);
- Status checkAggFunAndBuildGroupItems(const YieldClause *clause);
Status makeOutputColumn(YieldColumn *column);
+ Status makeImplicitGroupByValidator();
+ Status validateImplicitGroupBy();
void genConstantExprValues();
private:
- bool hasAggFun_{false};
YieldColumns *columns_{nullptr};
std::vector<std::string> outputColumnNames_;
- std::vector<Aggregate::GroupItem> groupItems_;
std::string constantExprVar_;
std::string userDefinedVarName_;
Expression *filterCondition_{nullptr};
+ // validate for agg
+ std::unique_ptr<GroupByValidator> groupByValidator_{nullptr};
};
} // namespace graph
diff --git a/src/validator/test/CMakeLists.txt b/src/validator/test/CMakeLists.txt
index af6ea318724f795b3ec6729552e1e73bb4f99a72..243bff7349225f2abfa8e7500d7e2d92e33668d9 100644
--- a/src/validator/test/CMakeLists.txt
+++ b/src/validator/test/CMakeLists.txt
@@ -41,7 +41,6 @@ set(VALIDATOR_TEST_LIBS
$<TARGET_OBJECTS:common_meta_client_obj>
$<TARGET_OBJECTS:common_file_based_cluster_id_man_obj>
$<TARGET_OBJECTS:common_function_manager_obj>
- $<TARGET_OBJECTS:common_agg_function_obj>
$<TARGET_OBJECTS:common_conf_obj>
$<TARGET_OBJECTS:common_encryption_obj>
$<TARGET_OBJECTS:common_http_client_obj>
diff --git a/src/validator/test/GroupByValidatorTest.cpp b/src/validator/test/GroupByValidatorTest.cpp
index 70023d12b1f23cdd44c238aad876071887e6a61f..501051d05602a73f073cf85db4286959b950d268 100644
--- a/src/validator/test/GroupByValidatorTest.cpp
+++ b/src/validator/test/GroupByValidatorTest.cpp
@@ -21,7 +21,7 @@ TEST_F(GroupByValidatorTest, TestGroupBy) {
{
std::string query =
"GO FROM \"1\" OVER like YIELD like._dst AS id, $^.person.age AS age "
- "| GROUP BY $-.age YIELD COUNT($-.id)";
+ "| GROUP BY $-.age YIELD $-.age, COUNT($-.id)";
std::vector<PlanNode::Kind> expected = {
PK::kAggregate,
PK::kProject,
@@ -42,6 +42,19 @@ TEST_F(GroupByValidatorTest, TestGroupBy) {
};
EXPECT_TRUE(checkResult(query, expected));
}
+ {
+ std::string query =
+ "GO FROM \"NoExist\" OVER like YIELD like._dst AS id, $^.person.age AS age "
+ "| GROUP BY $-.id YIELD $-.id AS id, abs(avg($-.age)) AS age";
+ std::vector<PlanNode::Kind> expected = {
+ PK::kProject,
+ PK::kAggregate,
+ PK::kProject,
+ PK::kGetNeighbors,
+ PK::kStart
+ };
+ EXPECT_TRUE(checkResult(query, expected));
+ }
{
std::string query = "GO FROM \"1\", \"2\" OVER like "
"YIELD $$.person.name as name, "
@@ -71,6 +84,36 @@ TEST_F(GroupByValidatorTest, TestGroupBy) {
};
EXPECT_TRUE(checkResult(query, expected));
}
+ {
+ std::string query = "GO FROM \"1\", \"2\" OVER like "
+ "YIELD $$.person.name as name, "
+ "$$.person.age AS dst_age, "
+ "$$.person.age AS src_age"
+ "| GROUP BY $-.name "
+ "YIELD $-.name AS name, "
+ "SUM($-.dst_age) AS sum_dst_age, "
+ "abs(AVG($-.dst_age)) AS avg_dst_age, "
+ "MAX($-.src_age) AS max_src_age, "
+ "MIN($-.src_age) AS min_src_age, "
+ "STD($-.src_age) AS std_src_age, "
+ "BIT_AND(1) AS bit_and, "
+ "BIT_OR(2) AS bit_or, "
+ "BIT_XOR(3) AS bit_xor";
+ std::vector<PlanNode::Kind> expected = {
+ PK::kProject,
+ PK::kAggregate,
+ PK::kProject,
+ PK::kDataJoin,
+ PK::kProject,
+ PK::kGetVertices,
+ PK::kDedup,
+ PK::kProject,
+ PK::kProject,
+ PK::kGetNeighbors,
+ PK::kStart
+ };
+ EXPECT_TRUE(checkResult(query, expected));
+ }
{
// group one col
std::string query = "GO FROM \"1\" OVER like "
@@ -103,7 +146,7 @@ TEST_F(GroupByValidatorTest, TestGroupBy) {
"like._dst AS id, "
"like.start AS start_year, "
"like.end AS end_year"
- "| GROUP BY $-.name, abs(5) "
+ "| GROUP BY $-.name, abs($-.end_year) "
"YIELD $-.name AS name, "
"SUM(1.5) AS sum, "
"COUNT(*) AS count, "
@@ -154,7 +197,7 @@ TEST_F(GroupByValidatorTest, VariableTest) {
{
std::string query =
"$a = GO FROM \"1\" OVER like YIELD like._dst AS id, $^.person.age AS age; "
- "GROUP BY $a.age YIELD COUNT($a.id)";
+ "GROUP BY $a.age YIELD $a.age, COUNT($a.id)";
std::vector<PlanNode::Kind> expected = {
PK::kAggregate, PK::kProject, PK::kGetNeighbors, PK::kStart};
EXPECT_TRUE(checkResult(query, expected));
@@ -192,7 +235,7 @@ TEST_F(GroupByValidatorTest, VariableTest) {
"like._dst AS id, "
"like.start AS start_year, "
"like.end AS end_year;"
- "GROUP BY $a.name, abs(5) "
+ "GROUP BY $a.name, abs($a.end_year) "
"YIELD $a.name AS name, "
"SUM(1.5) AS sum, "
"COUNT(*) AS count, "
@@ -220,7 +263,21 @@ TEST_F(GroupByValidatorTest, InvalidTest) {
std::string query = "GO FROM \"1\" OVER like YIELD like._dst AS id, $^.person.age AS age "
"| GROUP BY 1+1 YIELD COUNT(1), 1+1";
auto result = checkResult(query);
- EXPECT_EQ(std::string(result.message()), "SemanticError: Group `(1+1)` invalid");
+ EXPECT_EQ(std::string(result.message()), "SemanticError: Group `(1+1)' invalid");
+ }
+ {
+ // use groupby without input
+ std::string query = "GO FROM \"1\" OVER like YIELD like._dst AS id, $^.person.age AS age "
+ "| GROUP BY count(*)+1 YIELD COUNT(1), 1+1";
+ auto result = checkResult(query);
+ EXPECT_EQ(std::string(result.message()), "SemanticError: Group `(COUNT(*)+1)' invalid");
+ }
+ {
+ // use groupby without input
+ std::string query = "GO FROM \"1\" OVER like YIELD like._dst AS id, $^.person.age AS age "
+ "| GROUP BY abs(1)+1 YIELD COUNT(1), 1+1";
+ auto result = checkResult(query);
+ EXPECT_EQ(std::string(result.message()), "SemanticError: Group `(abs(1)+1)' invalid");
}
{
// use dst
@@ -243,7 +300,8 @@ TEST_F(GroupByValidatorTest, InvalidTest) {
std::string query = "GO FROM \"1\" OVER like YIELD like._dst AS id, $^.person.age AS age "
"| GROUP BY noexist YIELD COUNT($-.age)";
auto result = checkResult(query);
- EXPECT_EQ(std::string(result.message()), "SemanticError: Group `noexist` invalid");
+ EXPECT_EQ(std::string(result.message()),
+ "SemanticError: Group `noexist' invalid");
}
{
// use sum(*)
@@ -251,7 +309,7 @@ TEST_F(GroupByValidatorTest, InvalidTest) {
"| GROUP BY $-.id YIELD SUM(*)";
auto result = checkResult(query);
EXPECT_EQ(std::string(result.message()),
- "SemanticError: `SUM(*)` invaild, * valid in count.");
+ "SemanticError: Could not apply aggregation function `SUM(*)' on `*`");
}
{
// use agg fun has more than two inputs
@@ -265,7 +323,7 @@ TEST_F(GroupByValidatorTest, InvalidTest) {
std::string query = "GO FROM \"1\" OVER like YIELD like._dst AS id, $^.person.age AS age "
"| GROUP BY $-.id, SUM($-.age) YIELD $-.id, SUM($-.age)";
auto result = checkResult(query);
- EXPECT_EQ(std::string(result.message()), "SemanticError: Use invalid group function `SUM`");
+ EXPECT_EQ(std::string(result.message()), "SemanticError: Group `SUM($-.age)' invalid");
}
{
// yield without group by
@@ -273,24 +331,44 @@ TEST_F(GroupByValidatorTest, InvalidTest) {
"COUNT(like._dst) AS id ";
auto result = checkResult(query);
EXPECT_EQ(std::string(result.message()),
- "SemanticError: `COUNT(like._dst) AS id', not support "
- "aggregate function in go sentence.");
+ "SemanticError: `COUNT(like._dst) AS id', "
+ "not support aggregate function in go sentence.");
+ }
+ {
+ // yield without group by
+ std::string query = "GO FROM \"1\" OVER like YIELD $^.person.age AS age, "
+ "COUNT(like._dst)+1 AS id ";
+ auto result = checkResult(query);
+ EXPECT_EQ(std::string(result.message()),
+ "SemanticError: `(COUNT(like._dst)+1) AS id', "
+ "not support aggregate function in go sentence.");
+ }
+ {
+ // yield without group by
+ std::string query = "GO FROM \"1\" OVER like WHERE count(*) + 1 >3 "
+ "YIELD $^.person.age AS age, "
+ "COUNT(like._dst)+1 AS id ";
+ auto result = checkResult(query);
+ EXPECT_EQ(std::string(result.message()),
+ "SemanticError: `((COUNT(*)+1)>3)', "
+ "not support aggregate function in where sentence.");
}
- {
+ {
// yield col not in group output
std::string query = "GO FROM \"1\" OVER like "
"YIELD $$.person.name as name, "
"like._dst AS id, "
"like.start AS start_year, "
"like.end AS end_year"
- "| GROUP BY $-.start_year, abs(5) "
+ "| GROUP BY $-.start_year, abs($-.end_year) "
"YIELD $-.name AS name, "
"SUM(1.5) AS sum, "
"COUNT(*) AS count, "
"1+1 AS cal";
auto result = checkResult(query);
EXPECT_EQ(std::string(result.message()),
- "SemanticError: Yield `$-.name AS name` isn't in output fields");
+ "SemanticError: Yield non-agg expression `$-.name' "
+ "must be functionally dependent on items in GROUP BY clause");
}
{
// duplicate col
diff --git a/src/validator/test/QueryValidatorTest.cpp b/src/validator/test/QueryValidatorTest.cpp
index 084add461315013eaad4a9cf8dd97e598e7f3453..260c226ff4a42cd18e276c299ab07fed2085e1b4 100644
--- a/src/validator/test/QueryValidatorTest.cpp
+++ b/src/validator/test/QueryValidatorTest.cpp
@@ -1086,8 +1086,11 @@ TEST_F(QueryValidatorTest, GoInvalid) {
EXPECT_FALSE(checkResult(query));
}
{
+ // yield agg without groupBy is not supported
std::string query = "GO FROM \"2\" OVER like YIELD COUNT(123);";
- EXPECT_FALSE(checkResult(query));
+ auto result = checkResult(query);
+ EXPECT_EQ(std::string(result.message()), "SemanticError: `COUNT(123)', "
+ "not support aggregate function in go sentence.");
}
{
std::string query = "GO FROM \"1\" OVER like YIELD like._dst AS id, like._src AS id | GO "
diff --git a/src/validator/test/YieldValidatorTest.cpp b/src/validator/test/YieldValidatorTest.cpp
index 88087a25d08320870b0050141ab3d6566d8b2f92..6458c3495553fb6a0c0c46cb1de722d4c6e6ce89 100644
--- a/src/validator/test/YieldValidatorTest.cpp
+++ b/src/validator/test/YieldValidatorTest.cpp
@@ -113,19 +113,19 @@ TEST_F(YieldValidatorTest, FuncitonCall) {
std::string query = "YIELD abs(true)";
auto result = checkResult(query);
EXPECT_EQ(std::string(result.message()),
- "SemanticError: `abs(true)` is not a valid expression : Parameter's type error");
+ "SemanticError: `abs(true)' is not a valid expression : Parameter's type error");
}
{
std::string query = "YIELD abs(\"test\")";
auto result = checkResult(query);
EXPECT_EQ(std::string(result.message()),
- "SemanticError: `abs(test)` is not a valid expression : Parameter's type error");
+ "SemanticError: `abs(test)' is not a valid expression : Parameter's type error");
}
{
std::string query = "YIELD noexist(12)";
auto result = checkResult(query);
EXPECT_EQ(std::string(result.message()),
- "SemanticError: `noexist(12)` is not a valid expression : Function `noexist' not "
+ "SemanticError: `noexist(12)' is not a valid expression : Function `noexist' not "
"defined");
}
}
@@ -164,13 +164,13 @@ TEST_F(YieldValidatorTest, TypeCastTest) {
std::string query = "YIELD (int)\"123abc\"";
auto result = checkResult(query);
EXPECT_EQ(std::string(result.message()),
- "SemanticError: `(INT)123abc` is not a valid expression ");
+ "SemanticError: `(INT)123abc' is not a valid expression ");
}
{
std::string query = "YIELD (int)\"abc123\"";
auto result = checkResult(query);
EXPECT_EQ(std::string(result.message()),
- "SemanticError: `(INT)abc123` is not a valid expression ");
+ "SemanticError: `(INT)abc123' is not a valid expression ");
}
{
std::string query = "YIELD (doublE)\"123\"";
@@ -184,7 +184,7 @@ TEST_F(YieldValidatorTest, TypeCastTest) {
std::string query = "YIELD (doublE)\".a123\"";
auto result = checkResult(query);
EXPECT_EQ(std::string(result.message()),
- "SemanticError: `(FLOAT).a123` is not a valid expression ");
+ "SemanticError: `(FLOAT).a123' is not a valid expression ");
}
{
std::string query = "YIELD (STRING)1.23";
@@ -485,12 +485,8 @@ TEST_F(YieldValidatorTest, AggCall) {
"YIELD $^.person.age AS age, "
"like.likeness AS like"
"| YIELD COUNT(*), $-.age";
- auto result = checkResult(query);
- EXPECT_EQ(std::string(result.message()),
- "SemanticError: Input columns without aggregation are not supported in YIELD "
- "statement without GROUP BY, near `$-.age'");
+ EXPECT_TRUE(checkResult(query));
}
- // Test input
{
auto query = "GO FROM \"1\" OVER like "
"YIELD $^.person.age AS age, "
@@ -537,7 +533,7 @@ TEST_F(YieldValidatorTest, AggCall) {
"YIELD $^.person.age AS age, "
"like.likeness AS like"
"| YIELD AVG($-.age), SUM($-.like), COUNT(*), $-.age + 1";
- EXPECT_FALSE(checkResult(query));
+ EXPECT_TRUE(checkResult(query));
}
{
auto query = "GO FROM \"1\" OVER like "
@@ -546,7 +542,7 @@ TEST_F(YieldValidatorTest, AggCall) {
"| YIELD AVG($-.*)";
auto result = checkResult(query);
EXPECT_EQ(std::string(result.message()),
- "SemanticError: could not apply aggregation function on `$-.*'");
+ "SemanticError: Could not apply aggregation function `AVG($-.*)' on `$-.*'");
}
// Yield field has not input
{
@@ -589,7 +585,7 @@ TEST_F(YieldValidatorTest, AggCall) {
"YIELD $^.person.age AS age, "
"like.likeness AS like;"
"YIELD AVG($var.age), SUM($var.like), COUNT(*), $var.age + 1";
- EXPECT_FALSE(checkResult(query));
+ EXPECT_TRUE(checkResult(query));
}
{
auto query = "$var = GO FROM \"1\" OVER like "
@@ -626,7 +622,7 @@ TEST_F(YieldValidatorTest, AggCall) {
"YIELD AVG($var.*)";
auto result = checkResult(query);
EXPECT_EQ(std::string(result.message()),
- "SemanticError: could not apply aggregation function on `$var.*'");
+ "SemanticError: Could not apply aggregation function `AVG($var.*)' on `$var.*'");
}
}
diff --git a/src/visitor/CMakeLists.txt b/src/visitor/CMakeLists.txt
index a7e61a16b0ea7e16a236aeb708459fabc7caefc7..01a11588c3e42dab2f57c99ec874ca6fce458a64 100644
--- a/src/visitor/CMakeLists.txt
+++ b/src/visitor/CMakeLists.txt
@@ -12,8 +12,10 @@ nebula_add_library(
ExtractPropExprVisitor.cpp
ExtractFilterExprVisitor.cpp
FindAnyExprVisitor.cpp
+ FindAnySubExprVisitor.cpp
FoldConstantExprVisitor.cpp
RewriteLabelAttrVisitor.cpp
+ RewriteAggExprVisitor.cpp
RewriteInputPropVisitor.cpp
RewriteSymExprVisitor.cpp
RewriteMatchLabelVisitor.cpp
diff --git a/src/visitor/CollectAllExprsVisitor.cpp b/src/visitor/CollectAllExprsVisitor.cpp
index 7f6366d1a3e105e84e63ea9fb86c14d1f45c29f9..e1599c1e064b59f5f08886ea9f548be7becd3d37 100644
--- a/src/visitor/CollectAllExprsVisitor.cpp
+++ b/src/visitor/CollectAllExprsVisitor.cpp
@@ -30,6 +30,11 @@ void CollectAllExprsVisitor::visit(FunctionCallExpression *expr) {
}
}
+void CollectAllExprsVisitor::visit(AggregateExpression *expr) {
+ collectExpr(expr);
+ expr->arg()->accept(this);
+}
+
void CollectAllExprsVisitor::visit(ListExpression *expr) {
collectExpr(expr);
for (auto &item : expr->items()) {
diff --git a/src/visitor/CollectAllExprsVisitor.h b/src/visitor/CollectAllExprsVisitor.h
index ba6aa8daa5b4f485c611675ee66c69fc8175324e..fa6b16c25490bda6b9386f6592cc0badf0d3c264 100644
--- a/src/visitor/CollectAllExprsVisitor.h
+++ b/src/visitor/CollectAllExprsVisitor.h
@@ -32,6 +32,7 @@ private:
void visit(TypeCastingExpression* expr) override;
void visit(UnaryExpression* expr) override;
void visit(FunctionCallExpression* expr) override;
+ void visit(AggregateExpression* expr) override;
void visit(ListExpression* expr) override;
void visit(SetExpression* expr) override;
void visit(MapExpression* expr) override;
diff --git a/src/visitor/DeduceTypeVisitor.cpp b/src/visitor/DeduceTypeVisitor.cpp
index 49f2258c94498b695e9b830671dff67c63722af1..1bc42b21e942f86acdb26ed2620ac992e47b2ee1 100644
--- a/src/visitor/DeduceTypeVisitor.cpp
+++ b/src/visitor/DeduceTypeVisitor.cpp
@@ -182,7 +182,7 @@ void DeduceTypeVisitor::visit(TypeCastingExpression *expr) {
auto val = expr->eval(ctx(nullptr));
if (val.isNull()) {
status_ =
- Status::SemanticError("`%s` is not a valid expression ", expr->toString().c_str());
+ Status::SemanticError("`%s' is not a valid expression ", expr->toString().c_str());
return;
}
type_ = val.type();
@@ -386,7 +386,7 @@ void DeduceTypeVisitor::visit(FunctionCallExpression *expr) {
}
auto result = FunctionManager::getReturnType(funName, argsTypeList);
if (!result.ok()) {
- status_ = Status::SemanticError("`%s` is not a valid expression : %s",
+ status_ = Status::SemanticError("`%s' is not a valid expression : %s",
expr->toString().c_str(),
result.status().toString().c_str());
return;
@@ -394,6 +394,65 @@ void DeduceTypeVisitor::visit(FunctionCallExpression *expr) {
type_ = result.value();
}
+void DeduceTypeVisitor::visit(AggregateExpression *expr) {
+ expr->arg()->accept(this);
+ if (!ok()) return;
+ auto arg_type = type_;
+
+ auto func = AggregateExpression::NAME_ID_MAP[expr->name()->c_str()];
+ switch (func) {
+ case AggregateExpression::Function::kCount: {
+ type_ = Value::Type::INT;
+ break;
+ }
+ case AggregateExpression::Function::kSum: {
+ type_ = arg_type;
+ break;
+ }
+ case AggregateExpression::Function::kAvg: {
+ type_ = Value::Type::FLOAT;
+ break;
+ }
+ case AggregateExpression::Function::kMax: {
+ type_ = arg_type;
+ break;
+ }
+ case AggregateExpression::Function::kMin: {
+ type_ = arg_type;
+ break;
+ }
+ case AggregateExpression::Function::kStdev: {
+ type_ = Value::Type::FLOAT;
+ break;
+ }
+ case AggregateExpression::Function::kBitAnd: {
+ type_ = Value::Type::INT;
+ break;
+ }
+ case AggregateExpression::Function::kBitOr: {
+ type_ = Value::Type::INT;
+ break;
+ }
+ case AggregateExpression::Function::kBitXor: {
+ type_ = Value::Type::INT;
+ break;
+ }
+ case AggregateExpression::Function::kCollect: {
+ type_ = Value::Type::LIST;
+ break;
+ }
+ case AggregateExpression::Function::kCollectSet: {
+ type_ = Value::Type::SET;
+ break;
+ }
+ default: {
+ LOG(FATAL) << "Invalid Aggregate expression kind: "
+ << expr->name()->c_str();
+ break;
+ }
+ }
+}
+
void DeduceTypeVisitor::visit(UUIDExpression *) {
type_ = Value::Type::STRING;
}
diff --git a/src/visitor/DeduceTypeVisitor.h b/src/visitor/DeduceTypeVisitor.h
index ed35c9cb78d31d9f43cd2386e9ea2f29b4efecd1..fe3e0a9a34bac539cf4014af5b8e104b9fe6d488 100644
--- a/src/visitor/DeduceTypeVisitor.h
+++ b/src/visitor/DeduceTypeVisitor.h
@@ -55,6 +55,7 @@ private:
void visit(LogicalExpression *expr) override;
// function call
void visit(FunctionCallExpression *expr) override;
+ void visit(AggregateExpression *expr) override;
void visit(UUIDExpression *expr) override;
// variable expression
void visit(VariableExpression *expr) override;
diff --git a/src/visitor/ExprVisitorImpl.cpp b/src/visitor/ExprVisitorImpl.cpp
index 2a2cfd0aa5f7376a0081f26dffd1f00271c7d9f5..9f549e99fdfd71eceedd08fec2f12f6503ebd668 100644
--- a/src/visitor/ExprVisitorImpl.cpp
+++ b/src/visitor/ExprVisitorImpl.cpp
@@ -64,6 +64,11 @@ void ExprVisitorImpl::visit(FunctionCallExpression *expr) {
}
}
+void ExprVisitorImpl::visit(AggregateExpression *expr) {
+ DCHECK(ok());
+ expr->arg()->accept(this);
+}
+
// container expression
void ExprVisitorImpl::visit(ListExpression *expr) {
DCHECK(ok());
diff --git a/src/visitor/ExprVisitorImpl.h b/src/visitor/ExprVisitorImpl.h
index 4ebf4db09ff38855d3cbaa4ff710996a00077d3c..2f24b5d7aad75363834b5a5ebfbb064a18629c79 100644
--- a/src/visitor/ExprVisitorImpl.h
+++ b/src/visitor/ExprVisitorImpl.h
@@ -25,6 +25,7 @@ public:
void visit(LogicalExpression *expr) override;
// function call
void visit(FunctionCallExpression *expr) override;
+ void visit(AggregateExpression *expr) override;
// container expression
void visit(ListExpression *expr) override;
void visit(SetExpression *expr) override;
diff --git a/src/visitor/FindAnyExprVisitor.cpp b/src/visitor/FindAnyExprVisitor.cpp
index c3abfb42c3376c05f857466f6dd55c8bbeceb1e3..6b85c5d618bbbfef7ba807cfa98c001d525410a9 100644
--- a/src/visitor/FindAnyExprVisitor.cpp
+++ b/src/visitor/FindAnyExprVisitor.cpp
@@ -35,6 +35,12 @@ void FindAnyExprVisitor::visit(FunctionCallExpression *expr) {
}
}
+void FindAnyExprVisitor::visit(AggregateExpression *expr) {
+ findExpr(expr);
+ if (found_) return;
+ expr->arg()->accept(this);
+}
+
void FindAnyExprVisitor::visit(ListExpression *expr) {
findExpr(expr);
if (found_) return;
diff --git a/src/visitor/FindAnyExprVisitor.h b/src/visitor/FindAnyExprVisitor.h
index 8f91bc222621a1add4bf5080e58c71d33833646a..fb53103b03cabb4f91e2d81476ffec89dbacf8b7 100644
--- a/src/visitor/FindAnyExprVisitor.h
+++ b/src/visitor/FindAnyExprVisitor.h
@@ -34,6 +34,7 @@ private:
void visit(TypeCastingExpression* expr) override;
void visit(UnaryExpression* expr) override;
void visit(FunctionCallExpression* expr) override;
+ void visit(AggregateExpression* expr) override;
void visit(ListExpression* expr) override;
void visit(SetExpression* expr) override;
void visit(MapExpression* expr) override;
diff --git a/src/visitor/FindAnySubExprVisitor.cpp b/src/visitor/FindAnySubExprVisitor.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..79dbf8df048ba05f3787128917e67e5b42142cd3
--- /dev/null
+++ b/src/visitor/FindAnySubExprVisitor.cpp
@@ -0,0 +1,206 @@
+/* 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 "visitor/FindAnySubExprVisitor.h"
+
+namespace nebula {
+namespace graph {
+
+FindAnySubExprVisitor::FindAnySubExprVisitor(std::unordered_set<Expression*> &subExprs,
+ bool needRecursiveSearch)
+ : subExprs_(subExprs), needRecursiveSearch_(needRecursiveSearch) {
+ DCHECK(!subExprs_.empty());
+}
+
+void FindAnySubExprVisitor::visit(TypeCastingExpression *expr) {
+ compareWithSubExprs<TypeCastingExpression>(expr);
+ if (needRecursiveSearch_) {
+ expr->operand()->accept(this);
+ }
+}
+
+void FindAnySubExprVisitor::visit(UnaryExpression *expr) {
+ compareWithSubExprs<UnaryExpression>(expr);
+ if (needRecursiveSearch_) {
+ expr->operand()->accept(this);
+ }
+}
+
+void FindAnySubExprVisitor::visit(FunctionCallExpression *expr) {
+ compareWithSubExprs<FunctionCallExpression>(expr);
+ if (needRecursiveSearch_) {
+ for (const auto &arg : expr->args()->args()) {
+ arg->accept(this);
+ if (found_) return;
+ }
+ }
+}
+
+void FindAnySubExprVisitor::visit(AggregateExpression *expr) {
+ compareWithSubExprs<AggregateExpression>(expr);
+ if (needRecursiveSearch_) {
+ expr->arg()->accept(this);
+ }
+}
+
+void FindAnySubExprVisitor::visit(ListExpression *expr) {
+ compareWithSubExprs<ListExpression>(expr);
+ if (needRecursiveSearch_) {
+ for (const auto &item : expr->items()) {
+ item->accept(this);
+ if (found_) return;
+ }
+ }
+}
+
+void FindAnySubExprVisitor::visit(SetExpression *expr) {
+ compareWithSubExprs<SetExpression>(expr);
+ if (needRecursiveSearch_) {
+ for (const auto &item : expr->items()) {
+ item->accept(this);
+ if (found_) return;
+ }
+ }
+}
+
+void FindAnySubExprVisitor::visit(MapExpression *expr) {
+ compareWithSubExprs<MapExpression>(expr);
+ if (needRecursiveSearch_) {
+ for (const auto &pair : expr->items()) {
+ pair.second->accept(this);
+ if (found_) return;
+ }
+ }
+}
+
+void FindAnySubExprVisitor::visit(CaseExpression *expr) {
+ compareWithSubExprs<CaseExpression>(expr);
+ if (needRecursiveSearch_) {
+ if (expr->hasCondition()) {
+ expr->condition()->accept(this);
+ if (found_) return;
+ }
+ if (expr->hasDefault()) {
+ expr->defaultResult()->accept(this);
+ if (found_) return;
+ }
+ for (const auto &whenThen : expr->cases()) {
+ whenThen.when->accept(this);
+ if (found_) return;
+ whenThen.then->accept(this);
+ if (found_) return;
+ }
+ }
+}
+
+void FindAnySubExprVisitor::visit(ConstantExpression *expr) {
+ compareWithSubExprs<ConstantExpression>(expr);
+}
+
+void FindAnySubExprVisitor::visit(EdgePropertyExpression *expr) {
+ compareWithSubExprs<EdgePropertyExpression>(expr);
+}
+
+void FindAnySubExprVisitor::visit(TagPropertyExpression *expr) {
+ compareWithSubExprs<TagPropertyExpression>(expr);
+}
+
+void FindAnySubExprVisitor::visit(InputPropertyExpression *expr) {
+ compareWithSubExprs<InputPropertyExpression>(expr);
+}
+
+void FindAnySubExprVisitor::visit(VariablePropertyExpression *expr) {
+ compareWithSubExprs<VariablePropertyExpression>(expr);
+}
+
+void FindAnySubExprVisitor::visit(SourcePropertyExpression *expr) {
+ compareWithSubExprs<SourcePropertyExpression>(expr);
+}
+
+void FindAnySubExprVisitor::visit(DestPropertyExpression *expr) {
+ compareWithSubExprs<DestPropertyExpression>(expr);
+}
+
+void FindAnySubExprVisitor::visit(EdgeSrcIdExpression *expr) {
+ compareWithSubExprs<EdgeSrcIdExpression>(expr);
+}
+
+void FindAnySubExprVisitor::visit(EdgeTypeExpression *expr) {
+ compareWithSubExprs<EdgeTypeExpression>(expr);
+}
+
+void FindAnySubExprVisitor::visit(EdgeRankExpression *expr) {
+ compareWithSubExprs<EdgeRankExpression>(expr);
+}
+
+void FindAnySubExprVisitor::visit(EdgeDstIdExpression *expr) {
+ compareWithSubExprs<EdgeDstIdExpression>(expr);
+}
+
+void FindAnySubExprVisitor::visit(UUIDExpression *expr) {
+ compareWithSubExprs<UUIDExpression>(expr);
+}
+
+void FindAnySubExprVisitor::visit(VariableExpression *expr) {
+ compareWithSubExprs<VariableExpression>(expr);
+}
+
+void FindAnySubExprVisitor::visit(VersionedVariableExpression *expr) {
+ compareWithSubExprs<VersionedVariableExpression>(expr);
+}
+
+void FindAnySubExprVisitor::visit(LabelExpression *expr) {
+ compareWithSubExprs<LabelExpression>(expr);
+}
+
+void FindAnySubExprVisitor::visit(VertexExpression *expr) {
+ compareWithSubExprs<VertexExpression>(expr);
+}
+
+void FindAnySubExprVisitor::visit(EdgeExpression *expr) {
+ compareWithSubExprs<EdgeExpression>(expr);
+}
+
+void FindAnySubExprVisitor::visit(ColumnExpression *expr) {
+ compareWithSubExprs<ColumnExpression>(expr);
+}
+
+void FindAnySubExprVisitor::visitBinaryExpr(BinaryExpression *expr) {
+ compareWithSubExprs<BinaryExpression>(expr);
+ if (needRecursiveSearch_) {
+ expr->left()->accept(this);
+ if (found_) return;
+ expr->right()->accept(this);
+ }
+}
+
+void FindAnySubExprVisitor::checkExprKind(const Expression *expr, const Expression *sub_expr) {
+ if (expr == sub_expr) {
+ found_ = true;
+ continue_ = false;
+ } else if (expr == nullptr || sub_expr == nullptr) {
+ continue_ = false;
+ } else if (expr->kind() != sub_expr->kind()) {
+ continue_ = false;
+ }
+}
+
+template <typename T>
+void FindAnySubExprVisitor::compareWithSubExprs(T* expr) {
+ for (Expression* sub : subExprs_) {
+ continue_ = true;
+ checkExprKind(expr, sub);
+ if (found_) return;
+ if (!continue_) continue;
+ if (*static_cast<T*>(sub) == *expr) {
+ found_ = true;
+ return;
+ }
+ }
+}
+
+} // namespace graph
+} // namespace nebula
diff --git a/src/visitor/FindAnySubExprVisitor.h b/src/visitor/FindAnySubExprVisitor.h
new file mode 100644
index 0000000000000000000000000000000000000000..c9e33489b51a5452b24834446d520acb67316fb2
--- /dev/null
+++ b/src/visitor/FindAnySubExprVisitor.h
@@ -0,0 +1,83 @@
+/* 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 VISITOR_FINDANYSUBEXPRVISITOR_H_
+#define VISITOR_FINDANYSUBEXPRVISITOR_H_
+
+#include <unordered_set>
+
+#include "common/expression/Expression.h"
+#include "visitor/ExprVisitorImpl.h"
+
+namespace nebula {
+namespace graph {
+
+class FindAnySubExprVisitor final : public ExprVisitorImpl {
+public:
+ explicit FindAnySubExprVisitor(std::unordered_set<Expression*> &subExprs,
+ bool needRecursiveSearch);
+
+ bool ok() const override {
+ return !found_;
+ }
+
+ bool found() const {
+ return found_;
+ }
+
+ const Expression* expr() const {
+ return expr_;
+ }
+
+private:
+ using ExprVisitorImpl::visit;
+
+ void visit(TypeCastingExpression* expr) override;
+ void visit(UnaryExpression* expr) override;
+ void visit(FunctionCallExpression* expr) override;
+ void visit(AggregateExpression* expr) override;
+ void visit(ListExpression* expr) override;
+ void visit(SetExpression* expr) override;
+ void visit(MapExpression* expr) override;
+ void visit(CaseExpression* expr) override;
+
+ void visit(ConstantExpression* expr) override;
+ void visit(EdgePropertyExpression* expr) override;
+ void visit(TagPropertyExpression* expr) override;
+ void visit(InputPropertyExpression* expr) override;
+ void visit(VariablePropertyExpression* expr) override;
+ void visit(SourcePropertyExpression* expr) override;
+ void visit(DestPropertyExpression* expr) override;
+ void visit(EdgeSrcIdExpression* expr) override;
+ void visit(EdgeTypeExpression* expr) override;
+ void visit(EdgeRankExpression* expr) override;
+ void visit(EdgeDstIdExpression* expr) override;
+ void visit(UUIDExpression* expr) override;
+ void visit(VariableExpression* expr) override;
+ void visit(VersionedVariableExpression* expr) override;
+ void visit(LabelExpression* expr) override;
+ void visit(VertexExpression* expr) override;
+ void visit(EdgeExpression* expr) override;
+ void visit(ColumnExpression* expr) override;
+ void visitBinaryExpr(BinaryExpression* expr) override;
+
+ void checkExprKind(const Expression*, const Expression*);
+
+ template <typename T>
+ void compareWithSubExprs(T* expr);
+
+ bool found_{false};
+ // need continue search
+ bool continue_{true};
+ const Expression* expr_{nullptr};
+ const std::unordered_set<Expression*> subExprs_;
+ bool needRecursiveSearch_{false};
+};
+
+} // namespace graph
+} // namespace nebula
+
+#endif // VISITOR_FINDANYSUBEXPRVISITOR_H_
diff --git a/src/visitor/FoldConstantExprVisitor.cpp b/src/visitor/FoldConstantExprVisitor.cpp
index 82184be908e1ed68929a4c6b8fbc8841400b3e8d..a8abe6ac06ea18d434b023b10b5c8387fca5408f 100644
--- a/src/visitor/FoldConstantExprVisitor.cpp
+++ b/src/visitor/FoldConstantExprVisitor.cpp
@@ -115,6 +115,16 @@ void FoldConstantExprVisitor::visit(FunctionCallExpression *expr) {
canBeFolded_ = canBeFolded;
}
+void FoldConstantExprVisitor::visit(AggregateExpression *expr) {
+ // TODO : impl AggExpr foldConstantExprVisitor
+ if (!isConstant(expr->arg())) {
+ expr->arg()->accept(this);
+ if (canBeFolded_) {
+ expr->setArg(fold(expr->arg()));
+ }
+ }
+}
+
void FoldConstantExprVisitor::visit(UUIDExpression *expr) {
UNUSED(expr);
canBeFolded_ = false;
diff --git a/src/visitor/FoldConstantExprVisitor.h b/src/visitor/FoldConstantExprVisitor.h
index a111122b8ae4622e4319c4c3cf15a2cb1c3cf589..18995604ebcfb659f67c269155595cd128e996a7 100644
--- a/src/visitor/FoldConstantExprVisitor.h
+++ b/src/visitor/FoldConstantExprVisitor.h
@@ -35,6 +35,7 @@ public:
void visit(LogicalExpression *expr) override;
// function call
void visit(FunctionCallExpression *expr) override;
+ void visit(AggregateExpression *expr) override;
void visit(UUIDExpression *expr) override;
// variable expression
void visit(VariableExpression *expr) override;
diff --git a/src/visitor/RewriteAggExprVisitor.cpp b/src/visitor/RewriteAggExprVisitor.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..223c42c3ef9e0160fc96f5757fb8ba45970ebdb3
--- /dev/null
+++ b/src/visitor/RewriteAggExprVisitor.cpp
@@ -0,0 +1,71 @@
+/* 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 "visitor/RewriteAggExprVisitor.h"
+#include "util/ExpressionUtils.h"
+
+#include "common/base/Logging.h"
+
+namespace nebula {
+namespace graph {
+
+RewriteAggExprVisitor::RewriteAggExprVisitor(std::string* var,
+ std::string* prop) {
+ var_.reset(var);
+ prop_.reset(prop);
+}
+
+void RewriteAggExprVisitor::visit(TypeCastingExpression* expr) {
+ if (isAggExpr(expr->operand())) {
+ expr->setOperand(ExpressionUtils::newVarPropExpr(prop_->c_str(), var_->c_str()));
+ } else {
+ expr->operand()->accept(this);
+ }
+}
+
+// rewrite AggregateExpression to VariablePropertyExpression
+void RewriteAggExprVisitor::visit(FunctionCallExpression* expr) {
+ for (auto& arg : expr->args()->args()) {
+ if (isAggExpr(arg.get())) {
+ arg.reset(new VariablePropertyExpression(var_.release(),
+ prop_.release()));
+ } else {
+ arg->accept(this);
+ }
+ }
+}
+
+void RewriteAggExprVisitor::visit(ArithmeticExpression *expr) {
+ visitBinaryExpr(static_cast<BinaryExpression*>(expr));
+}
+
+void RewriteAggExprVisitor::visitBinaryExpr(BinaryExpression *expr) {
+ DCHECK(ok());
+ auto* lhs = expr->left();
+ if (isAggExpr(lhs)) {
+ expr->setLeft(new VariablePropertyExpression(std::move(var_).release(),
+ std::move(prop_).release()));
+ // only support rewrite single agg expr
+ return;
+ } else {
+ lhs->accept(this);
+ }
+ auto* rhs = expr->right();
+ if (isAggExpr(rhs)) {
+ expr->setRight(new VariablePropertyExpression(std::move(var_).release(),
+ std::move(prop_).release()));
+ return;
+ } else {
+ rhs->accept(this);
+ }
+}
+bool RewriteAggExprVisitor::isAggExpr(const Expression* expr) {
+ return expr->kind() == Expression::Kind::kAggregate;
+}
+
+
+} // namespace graph
+} // namespace nebula
diff --git a/src/visitor/RewriteAggExprVisitor.h b/src/visitor/RewriteAggExprVisitor.h
new file mode 100644
index 0000000000000000000000000000000000000000..598366d5883d2d2e107682d42572081a40ffd0f9
--- /dev/null
+++ b/src/visitor/RewriteAggExprVisitor.h
@@ -0,0 +1,70 @@
+/* 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 VISITOR_REWRITEAGGEXPRVISITOR_H_
+#define VISITOR_REWRITEAGGEXPRVISITOR_H_
+
+#include <memory>
+#include <vector>
+
+#include "visitor/ExprVisitorImpl.h"
+
+namespace nebula {
+namespace graph {
+
+class RewriteAggExprVisitor final : public ExprVisitorImpl {
+public:
+ explicit RewriteAggExprVisitor(std::string* var,
+ std::string* prop);
+
+ bool ok() const override {
+ return true;
+ }
+
+private:
+ using ExprVisitorImpl::visit;
+
+ void visit(TypeCastingExpression *expr) override;
+ void visit(FunctionCallExpression *expr) override;
+ void visit(ArithmeticExpression *expr) override;
+
+ void visit(UnaryExpression *) override {}
+ void visit(ListExpression *) override {}
+ void visit(SetExpression *) override {}
+ void visit(MapExpression *) override {}
+ void visit(CaseExpression *) override {}
+ void visit(ConstantExpression *) override {}
+ void visit(LabelExpression *) override {}
+ void visit(UUIDExpression *) override {}
+ void visit(LabelAttributeExpression *) override {}
+ void visit(VariableExpression *) override {}
+ void visit(VersionedVariableExpression *) 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(VertexExpression *) override {}
+ void visit(EdgeExpression *) override {}
+ void visit(ColumnExpression *) override {}
+ void visitBinaryExpr(BinaryExpression *) override;
+
+ static bool isAggExpr(const Expression* expr);
+
+private:
+ std::unique_ptr<std::string> var_;
+ std::unique_ptr<std::string> prop_;
+};
+
+} // namespace graph
+} // namespace nebula
+
+#endif // VISITOR_REWRITEAGGEXPRVISITOR_H_
diff --git a/src/visitor/RewriteInputPropVisitor.cpp b/src/visitor/RewriteInputPropVisitor.cpp
index bd5db3d2fd781ded158a2055418b3aa27de1632f..b93635ca7dee09e790417bc523d27aba6d054c56 100644
--- a/src/visitor/RewriteInputPropVisitor.cpp
+++ b/src/visitor/RewriteInputPropVisitor.cpp
@@ -171,6 +171,14 @@ void RewriteInputPropVisitor::visit(FunctionCallExpression* expr) {
}
}
+void RewriteInputPropVisitor::visit(AggregateExpression* expr) {
+ auto* arg = expr->arg();
+ arg->accept(this);
+ if (ok()) {
+ expr->setArg(std::move(result_).release());
+ }
+}
+
void RewriteInputPropVisitor::visit(TypeCastingExpression* expr) {
expr->operand()->accept(this);
if (ok()) {
diff --git a/src/visitor/RewriteInputPropVisitor.h b/src/visitor/RewriteInputPropVisitor.h
index b14d363939a051005311ed169b8d75cae50a7cd7..a15a94797ebf9fabdedf1733fc83e2c8924a0513 100644
--- a/src/visitor/RewriteInputPropVisitor.h
+++ b/src/visitor/RewriteInputPropVisitor.h
@@ -47,6 +47,7 @@ private:
void visit(LogicalExpression *) override;
// function call
void visit(FunctionCallExpression *) override;
+ void visit(AggregateExpression *) override;
void visit(UUIDExpression *) override;
// variable expression
void visit(VariableExpression *) override;
diff --git a/src/visitor/RewriteSymExprVisitor.cpp b/src/visitor/RewriteSymExprVisitor.cpp
index 403627aef43beea4e69d14cb2b399c654f9b4407..892c1fd5908f381db5ae8029b0b925383f6cc4f3 100644
--- a/src/visitor/RewriteSymExprVisitor.cpp
+++ b/src/visitor/RewriteSymExprVisitor.cpp
@@ -93,6 +93,14 @@ void RewriteSymExprVisitor::visit(FunctionCallExpression *expr) {
}
}
+void RewriteSymExprVisitor::visit(AggregateExpression *expr) {
+ auto* arg = expr->arg();
+ arg->accept(this);
+ if (expr_) {
+ expr->setArg(std::move(expr_).release());
+ }
+}
+
void RewriteSymExprVisitor::visit(UUIDExpression *expr) {
UNUSED(expr);
hasWrongType_ = true;
diff --git a/src/visitor/RewriteSymExprVisitor.h b/src/visitor/RewriteSymExprVisitor.h
index b37fa3c3db66e87ae7af6d24feaa3a84087060e5..6e14c56fa589c9da5f7f147dcc7173c04868ef97 100644
--- a/src/visitor/RewriteSymExprVisitor.h
+++ b/src/visitor/RewriteSymExprVisitor.h
@@ -42,6 +42,7 @@ public:
void visit(LogicalExpression *expr) override;
// function call
void visit(FunctionCallExpression *expr) override;
+ void visit(AggregateExpression *expr) override;
void visit(UUIDExpression *expr) override;
// variable expression
void visit(VariableExpression *expr) override;
diff --git a/src/visitor/test/CMakeLists.txt b/src/visitor/test/CMakeLists.txt
index 583e5a4d2b1db848ab63f0aa59759c37eb4ce532..231eb9b868b14706f1e0e91fc7dfdcb1221d3aea 100644
--- a/src/visitor/test/CMakeLists.txt
+++ b/src/visitor/test/CMakeLists.txt
@@ -42,7 +42,6 @@ nebula_add_test(
$<TARGET_OBJECTS:common_meta_client_obj>
$<TARGET_OBJECTS:common_file_based_cluster_id_man_obj>
$<TARGET_OBJECTS:common_function_manager_obj>
- $<TARGET_OBJECTS:common_agg_function_obj>
$<TARGET_OBJECTS:common_conf_obj>
$<TARGET_OBJECTS:common_encryption_obj>
$<TARGET_OBJECTS:common_http_client_obj>
diff --git a/tests/query/v1/test_yield.py b/tests/query/v1/test_yield.py
index 83842f07f71d3b2f6f21033d49e60642f492e9bd..31c414a513962ce9c7d197c115df0001382f5362 100644
--- a/tests/query/v1/test_yield.py
+++ b/tests/query/v1/test_yield.py
@@ -404,13 +404,13 @@ class TestYield(NebulaTestSuite):
resp = self.execute(query)
self.check_resp_failed(resp, ttypes.ErrorCode.E_SEMANTIC_ERROR)
- # query = '''YIELD 1+COUNT(*), 1+1'''
- # resp = self.execute(query)
- # self.check_resp_succeeded(resp)
- # columns = ["(1+count(*))", "(1+1)"]
- # self.check_column_names(resp, columns)
- # expect_result = [[2, 2]]
- # self.check_result(resp, expect_result)
+ query = '''YIELD 1+COUNT(*), 1+1'''
+ resp = self.execute(query)
+ self.check_resp_succeeded(resp)
+ columns = ["(1+COUNT(*))", "(1+1)"]
+ self.check_column_names(resp, columns)
+ expect_result = [[2, 2]]
+ self.check_result(resp, expect_result)
query = '''YIELD COUNT(*), 1+1'''
resp = self.execute(query)
@@ -423,7 +423,7 @@ class TestYield(NebulaTestSuite):
query = '''GO FROM "Carmelo Anthony" OVER like YIELD $$.player.age AS age, like.likeness AS like \
| YIELD COUNT(*), $-.age'''
resp = self.execute(query)
- self.check_resp_failed(resp, ttypes.ErrorCode.E_SEMANTIC_ERROR)
+ self.check_resp_succeeded(resp)
# Test input
query = '''GO FROM "Carmelo Anthony" OVER like YIELD $$.player.age AS age, like.likeness AS like \
diff --git a/tests/tck/features/agg/Agg.feature b/tests/tck/features/agg/Agg.feature
new file mode 100644
index 0000000000000000000000000000000000000000..d9998f9c83586b9fae89554d8c65f3d9c7b7f287
--- /dev/null
+++ b/tests/tck/features/agg/Agg.feature
@@ -0,0 +1,559 @@
+Feature: Basic Aggregate and GroupBy
+
+ Background:
+ Given a graph with space named "nba"
+
+ Scenario: Basic Aggregate
+ When executing query:
+ """
+ YIELD COUNT(*), 1+1
+ """
+ Then the result should be, in any order, with relax comparison:
+ | COUNT(*) | (1+1) |
+ | 1 | 2 |
+ When executing query:
+ """
+ YIELD count(*)+1 ,1+2 ,(INT)abs(count(2))
+ """
+ Then the result should be, in any order, with relax comparison:
+ | (COUNT(*)+1) | (1+2) | (INT)abs(COUNT(2)) |
+ | 2 | 3 | 1 |
+
+ Scenario: Basic GroupBy
+ When executing query:
+ """
+ GO FROM "Tim Duncan" OVER like YIELD like._dst AS dst, $$.player.age AS age
+ | YIELD count(*) AS count
+ """
+ Then the result should be, in any order, with relax comparison:
+ | count |
+ | 2 |
+ When executing query:
+ """
+ GO FROM "Tim Duncan" OVER like YIELD like._dst AS dst, $$.player.age AS age
+ | YIELD DISTINCT count(*) AS count where $-.age > 40
+ """
+ Then the result should be, in any order, with relax comparison:
+ | count |
+ | 1 |
+ When executing query:
+ """
+ $var=GO FROM "Tim Duncan" OVER like YIELD like._dst AS dst, $$.player.age AS age;
+ YIELD count($var.dst) AS count
+ """
+ Then the result should be, in any order, with relax comparison:
+ | count |
+ | 2 |
+ When executing query:
+ """
+ $var=GO FROM "Tim Duncan" OVER like YIELD DISTINCT like._dst AS dst, $$.player.age AS age;
+ YIELD count($var.dst) AS count where $var.age > 40
+ """
+ Then the result should be, in any order, with relax comparison:
+ | count |
+ | 1 |
+ When executing query:
+ """
+ GO FROM "Tim Duncan" OVER like YIELD like._dst AS dst, $$.player.age AS age
+ | GROUP BY $-.dst YIELD $-.dst AS dst, avg(distinct $-.age) AS age
+ """
+ Then the result should be, in any order, with relax comparison:
+ | dst | age |
+ | "Tony Parker" | 36.0 |
+ | "Manu Ginobili" | 41.0 |
+ When executing query:
+ """
+ $var=GO FROM "Tim Duncan" OVER like YIELD like._dst AS dst, $$.player.age AS age
+ | GROUP BY $-.dst YIELD $-.dst AS dst, avg(distinct $-.age) AS age
+ """
+ Then the result should be, in any order, with relax comparison:
+ | dst | age |
+ | "Tony Parker" | 36.0 |
+ | "Manu Ginobili" | 41.0 |
+ When executing query:
+ """
+ GO FROM "Tim Duncan" OVER like YIELD like._dst AS dst, $$.player.age AS age
+ | GROUP BY $-.dst YIELD $-.dst AS dst, avg(distinct $-.age) AS age
+ """
+ Then the result should be, in any order, with relax comparison:
+ | dst | age |
+ | "Tony Parker" | 36.0 |
+ | "Manu Ginobili" | 41.0 |
+ When executing query:
+ """
+ $var=GO FROM "Tim Duncan" OVER like YIELD like._dst AS dst, $$.player.age AS age;
+ YIELD DISTINCT $var.dst AS dst, avg(distinct $var.age) AS age
+ """
+ Then the result should be, in any order, with relax comparison:
+ | dst | age |
+ | "Tony Parker" | 36.0 |
+ | "Manu Ginobili" | 41.0 |
+ When executing query:
+ """
+ GO FROM 'Aron Baynes', 'Tracy McGrady' OVER serve
+ YIELD $$.team.name AS name,
+ serve._dst AS id,
+ serve.start_year AS start_year,
+ serve.end_year AS end_year
+ | GROUP BY $-.name, $-.start_year
+ YIELD $-.name AS teamName,
+ $-.start_year AS start_year,
+ MAX($-.start_year),
+ MIN($-.end_year),
+ AVG($-.end_year) AS avg_end_year,
+ STD($-.end_year) AS std_end_year,
+ COUNT($-.id)
+ """
+ Then the result should be, in any order, with relax comparison:
+ | teamName | start_year | MAX($-.start_year) | MIN($-.end_year) | avg_end_year | std_end_year | COUNT($-.id) |
+ | "Celtics" | 2017 | 2017 | 2019 | 2019.0 | 0.0 | 1 |
+ | "Magic" | 2000 | 2000 | 2004 | 2004.0 | 0.0 | 1 |
+ | "Pistons" | 2015 | 2015 | 2017 | 2017.0 | 0.0 | 1 |
+ | "Raptors" | 1997 | 1997 | 2000 | 2000.0 | 0.0 | 1 |
+ | "Rockets" | 2004 | 2004 | 2010 | 2010.0 | 0.0 | 1 |
+ | "Spurs" | 2013 | 2013 | 2013 | 2014.0 | 1.0 | 2 |
+ When executing query:
+ """
+ GO FROM "Marco Belinelli" OVER serve
+ YIELD $$.team.name AS name,
+ serve._dst AS id,
+ serve.start_year AS start_year,
+ serve.end_year AS end_year
+ | GROUP BY $-.start_year
+ YIELD COUNT($-.id),
+ $-.start_year AS start_year,
+ AVG($-.end_year) as avg
+ """
+ Then the result should be, in any order, with relax comparison:
+ | COUNT($-.id) | start_year | avg |
+ | 2 | 2018 | 2018.5 |
+ | 1 | 2017 | 2018.0 |
+ | 1 | 2016 | 2017.0 |
+ | 1 | 2009 | 2010.0 |
+ | 1 | 2007 | 2009.0 |
+ | 1 | 2012 | 2013.0 |
+ | 1 | 2013 | 2015.0 |
+ | 1 | 2015 | 2016.0 |
+ | 1 | 2010 | 2012.0 |
+ When executing query:
+ """
+ GO FROM 'Carmelo Anthony', 'Dwyane Wade' OVER like
+ YIELD $$.player.name AS name,
+ $$.player.age AS dst_age,
+ $$.player.age AS src_age,
+ like.likeness AS likeness
+ | GROUP BY $-.name
+ YIELD $-.name AS name,
+ SUM($-.dst_age) AS sum_dst_age,
+ AVG($-.dst_age) AS avg_dst_age,
+ MAX($-.src_age) AS max_src_age,
+ MIN($-.src_age) AS min_src_age,
+ BIT_AND(1) AS bit_and,
+ BIT_OR(2) AS bit_or,
+ BIT_XOR(3) AS bit_xor,
+ COUNT($-.likeness),
+ COUNT(DISTINCT $-.likeness)
+ """
+ Then the result should be, in any order, with relax comparison:
+ | name | sum_dst_age | avg_dst_age | max_src_age | min_src_age | bit_and | bit_or | bit_xor | COUNT($-.likeness) | COUNT(distinct $-.likeness) |
+ | "LeBron James" | 68 | 34.0 | 34 | 34 | 1 | 2 | 0 | 2 | 1 |
+ | "Chris Paul" | 66 | 33.0 | 33 | 33 | 1 | 2 | 0 | 2 | 1 |
+ | "Dwyane Wade" | 37 | 37.0 | 37 | 37 | 1 | 2 | 3 | 1 | 1 |
+ | "Carmelo Anthony" | 34 | 34.0 | 34 | 34 | 1 | 2 | 3 | 1 | 1 |
+ When executing query:
+ """
+ GO FROM 'Carmelo Anthony', 'Dwyane Wade' OVER like
+ YIELD $$.player.name AS name,
+ $$.player.age AS dst_age,
+ $$.player.age AS src_age,
+ like.likeness AS likeness
+ | GROUP BY $-.name
+ YIELD $-.name AS name,
+ SUM($-.dst_age) AS sum_dst_age,
+ AVG($-.dst_age) AS avg_dst_age,
+ MAX($-.src_age) AS max_src_age,
+ MIN($-.src_age) AS min_src_age,
+ BIT_AND(1) AS bit_and,
+ BIT_OR(2) AS bit_or,
+ BIT_XOR(3) AS bit_xor,
+ COUNT($-.likeness)
+ """
+ Then the result should be, in any order, with relax comparison:
+ | name | sum_dst_age | avg_dst_age | max_src_age | min_src_age | bit_and | bit_or | bit_xor | COUNT($-.likeness) |
+ | "LeBron James" | 68 | 34.0 | 34 | 34 | 1 | 2 | 0 | 2 |
+ | "Chris Paul" | 66 | 33.0 | 33 | 33 | 1 | 2 | 0 | 2 |
+ | "Dwyane Wade" | 37 | 37.0 | 37 | 37 | 1 | 2 | 3 | 1 |
+ | "Carmelo Anthony" | 34 | 34.0 | 34 | 34 | 1 | 2 | 3 | 1 |
+ When executing query:
+ """
+ GO FROM 'Tim Duncan' OVER like YIELD like._dst as dst
+ | GO FROM $-.dst over like YIELD $-.dst as dst, like._dst == 'Tim Duncan' as following
+ | GROUP BY $-.dst
+ YIELD $-.dst AS dst, BIT_OR($-.following) AS following
+ """
+ Then the result should be, in any order, with relax comparison:
+ | dst | following |
+ | "Tony Parker" | BAD_TYPE |
+ | "Manu Ginobili" | BAD_TYPE |
+ When executing query:
+ """
+ GO FROM 'Tim Duncan' OVER like YIELD like._dst as dst
+ | GO FROM $-.dst over like YIELD $-.dst as dst, like._dst == 'Tim Duncan' as following
+ | GROUP BY $-.dst
+ YIELD $-.dst AS dst,
+ BIT_OR(case when $-.following==true then 1 else 0 end) AS following
+ """
+ Then the result should be, in any order, with relax comparison:
+ | dst | following |
+ | "Tony Parker" | 1 |
+ | "Manu Ginobili" | 1 |
+ When executing query:
+ """
+ GO FROM 'Tim Duncan' OVER like YIELD like._dst as dst
+ | GO FROM $-.dst over like YIELD $-.dst as dst, like._dst == 'Tim Duncan' as following
+ | GROUP BY $-.dst
+ YIELD $-.dst AS dst,
+ BIT_AND($-.following) AS following
+ """
+ Then the result should be, in any order, with relax comparison:
+ | dst | following |
+ | "Tony Parker" | BAD_TYPE |
+ | "Manu Ginobili" | BAD_TYPE |
+ When executing query:
+ """
+ GO FROM 'Tim Duncan' OVER like YIELD like._dst as dst
+ | GO FROM $-.dst over like YIELD $-.dst as dst, like._dst == 'Tim Duncan' as following
+ | GROUP BY $-.dst
+ YIELD $-.dst AS dst,
+ BIT_AND(case when $-.following==true then 1 else 0 end) AS following
+ """
+ Then the result should be, in any order, with relax comparison:
+ | dst | following |
+ | "Tony Parker" | 0 |
+ | "Manu Ginobili" | 1 |
+ When executing query:
+ """
+ GO FROM 'Carmelo Anthony', 'Dwyane Wade' OVER like
+ YIELD $$.player.name AS name
+ | GROUP BY $-.name
+ YIELD $-.name AS name,
+ SUM(1.5) AS sum,
+ COUNT(*) AS count,
+ 1+1 AS cal
+ """
+ Then the result should be, in any order, with relax comparison:
+ | name | sum | count | cal |
+ | "LeBron James" | 3.0 | 2 | 2 |
+ | "Chris Paul" | 3.0 | 2 | 2 |
+ | "Dwyane Wade" | 1.5 | 1 | 2 |
+ | "Carmelo Anthony" | 1.5 | 1 | 2 |
+ When executing query:
+ """
+ GO FROM 'Paul Gasol' OVER like
+ YIELD $$.player.age AS age,
+ like._dst AS id
+ | GROUP BY $-.id
+ YIELD $-.id AS id,
+ SUM($-.age) AS age
+ | GO FROM $-.id OVER serve
+ YIELD $$.team.name AS name,
+ $-.age AS sumAge
+ """
+ Then the result should be, in any order, with relax comparison:
+ | name | sumAge |
+ | "Grizzlies" | 34 |
+ | "Raptors" | 34 |
+ | "Lakers" | 40 |
+
+ Scenario: Implicit GroupBy
+ When executing query:
+ """
+ GO FROM "Tim Duncan" OVER like YIELD like._dst AS dst, $$.player.age AS age
+ | YIELD $-.dst AS dst, 1+avg(distinct $-.age) AS age, abs(5) as abs
+ """
+ Then the result should be, in any order, with relax comparison:
+ | dst | age | abs |
+ | "Tony Parker" | 37.0 | 5 |
+ | "Manu Ginobili" | 42.0 | 5 |
+ When executing query:
+ """
+ GO FROM "Tim Duncan" OVER like YIELD like._dst AS dst, $$.player.age AS age
+ | YIELD $-.dst AS dst, 1+avg(distinct $-.age) AS age where $-.age > 40
+ """
+ Then the result should be, in any order, with relax comparison:
+ | dst | age |
+ | "Manu Ginobili" | 42.0 |
+ When executing query:
+ """
+ GO FROM "Tim Duncan" OVER like YIELD like._dst AS dst, $$.player.age AS age
+ | GROUP BY $-.age+1 YIELD (INT)($-.age+1) AS age, 1+count(distinct $-.dst) AS count
+ """
+ Then the result should be, in any order, with relax comparison:
+ | age | count |
+ | 37 | 2 |
+ | 42 | 2 |
+ When executing query:
+ """
+ $var=GO FROM "Tim Duncan" OVER like YIELD like._dst AS dst, $$.player.age AS age;
+ YIELD $var.dst AS dst, (INT)abs(1+avg(distinct $var.age)) AS age
+ """
+ Then the result should be, in any order, with relax comparison:
+ | dst | age |
+ | "Tony Parker" | 37 |
+ | "Manu Ginobili" | 42 |
+ When executing query:
+ """
+ $var1=GO FROM "Tim Duncan" OVER like YIELD like._dst AS dst;
+ $var2=GO FROM "Tim Duncan" OVER serve YIELD serve._dst AS dst;
+ YIELD $var1.dst AS dst, count($var1.dst) AS count
+ """
+ Then the result should be, in any order, with relax comparison:
+ | dst | count |
+ | "Tony Parker" | 1 |
+ | "Manu Ginobili" | 1 |
+
+ Scenario: Empty input
+ When executing query:
+ """
+ GO FROM 'noexist' OVER like
+ YIELD $$.player.name AS name
+ | GROUP BY $-.name
+ YIELD $-.name AS name,
+ SUM(1.5) AS sum,
+ COUNT(*) AS count
+ | ORDER BY $-.sum
+ | LIMIT 2
+ """
+ Then the result should be, in order, with relax comparison:
+ | name | sum | count |
+ When executing query:
+ """
+ GO FROM 'noexist' OVER serve
+ YIELD $^.player.name as name,
+ serve.start_year as start,
+ $$.team.name as team
+ | YIELD $-.name as name
+ WHERE $-.start > 20000
+ | GROUP BY $-.name
+ YIELD $-.name AS name
+ """
+ Then the result should be, in order, with relax comparison:
+ | name |
+ When executing query:
+ """
+ GO FROM 'noexist' OVER serve
+ YIELD $^.player.name as name,
+ serve.start_year as start,
+ $$.team.name as team
+ | YIELD $-.name as name
+ WHERE $-.start > 20000
+ | Limit 1
+ """
+ Then the result should be, in any order, with relax comparison:
+ | name |
+
+ Scenario: Duplicate column
+ When executing query:
+ """
+ GO FROM "Marco Belinelli" OVER serve
+ YIELD $$.team.name AS name,
+ serve._dst AS id,
+ serve.start_year AS start_year,
+ serve.end_year AS start_year
+ | GROUP BY $-.start_year
+ YIELD COUNT($-.id),
+ $-.start_year AS start_year,
+ AVG($-.end_year) as avg
+ """
+ Then a SemanticError should be raised at runtime:
+ When executing query:
+ """
+ GO FROM 'noexist' OVER serve
+ YIELD $^.player.name as name,
+ serve.start_year as start,
+ $$.team.name as name
+ | GROUP BY $-.name
+ YIELD $-.name AS name
+ """
+ Then a SemanticError should be raised at runtime:
+
+ Scenario: order by and limit
+ When executing query:
+ """
+ GO FROM 'Carmelo Anthony', 'Dwyane Wade' OVER like
+ YIELD $$.player.name AS name
+ | GROUP BY $-.name
+ YIELD $-.name AS name,
+ SUM(1.5) AS sum,
+ COUNT(*) AS count
+ | ORDER BY $-.sum, $-.name
+ """
+ Then the result should be, in any order, with relax comparison:
+ | name | sum | count |
+ | "Carmelo Anthony" | 1.5 | 1 |
+ | "Dwyane Wade" | 1.5 | 1 |
+ | "Chris Paul" | 3.0 | 2 |
+ | "LeBron James" | 3.0 | 2 |
+ When executing query:
+ """
+ GO FROM 'Carmelo Anthony', 'Dwyane Wade' OVER like
+ YIELD $$.player.name AS name
+ | GROUP BY $-.name
+ YIELD $-.name AS name,
+ SUM(1.5) AS sum,
+ COUNT(*) AS count
+ | ORDER BY $-.sum, $-.name DESC
+ | LIMIT 2
+ """
+ Then the result should be, in any order, with relax comparison:
+ | name | sum | count |
+ | "Carmelo Anthony" | 1.5 | 1 |
+ | "Dwyane Wade" | 1.5 | 1 |
+
+ Scenario: Error Check
+ When executing query:
+ """
+ YIELD avg(*)+1 ,1+2 ,(INT)abs(min(2))
+ """
+ Then a SemanticError should be raised at runtime: Could not apply aggregation function `AVG(*)' on `*`
+ When executing query:
+ """
+ GO FROM "Tim Duncan" OVER like YIELD like._dst AS dst, $$.player.age AS age
+ | GROUP BY $-.dst,$-.x YIELD avg(distinct $-.age) AS age
+ """
+ Then a SemanticError should be raised at runtime: `$-.x', not exist prop `x'
+ When executing query:
+ """
+ GO FROM "Tim Duncan" OVER like YIELD like._dst AS dst, $$.player.age AS age
+ | GROUP BY $-.age+1 YIELD $-.age+1,age,avg(distinct $-.age) AS age
+ """
+ Then a SemanticError should be raised at runtime: Not supported expression `age' for props deduction.
+ When executing query:
+ """
+ GO FROM "Tim Duncan" OVER like YIELD like._dst AS dst, $$.player.age AS age
+ | GROUP BY $-.age+1 YIELD $-.age,avg(distinct $-.age) AS age
+ """
+ Then a SemanticError should be raised at runtime: Yield non-agg expression `$-.age' must be functionally dependent on items in GROUP BY clause
+ When executing query:
+ """
+ GO FROM "Tim Duncan" OVER like YIELD like._dst AS dst, $$.player.age AS age
+ | GROUP BY $-.age+1 YIELD $-.age+1,abs(avg(distinct count($-.age))) AS age
+ """
+ Then a SemanticError should be raised at runtime: Aggregate function nesting is not allowed: `abs(AVG(distinct COUNT($-.age)))'
+ When executing query:
+ """
+ GO FROM "Tim Duncan" OVER like YIELD like._dst AS dst, $$.player.age AS age
+ | GROUP BY $-.age+1 YIELD $-.age+1,avg(distinct count($-.age+1)) AS age
+ """
+ Then a SemanticError should be raised at runtime: Aggregate function nesting is not allowed: `AVG(distinct COUNT(($-.age+1)))'
+ When executing query:
+ """
+ GO FROM "Tim Duncan" OVER like YIELD like._dst AS dst, $$.player.age AS age
+ | GROUP BY avg($-.age+1)+1 YIELD $-.age,avg(distinct $-.age) AS age
+ """
+ Then a SemanticError should be raised at runtime: Group `(AVG(($-.age+1))+1)' invalid
+ When executing query:
+ """
+ $var=GO FROM "Tim Duncan" OVER like YIELD like._dst AS dst, $$.player.age AS age;
+ GO FROM "Tim Duncan" OVER like YIELD like._dst AS dst, $$.player.age AS age
+ | YIELD $var.dst AS dst, avg(distinct $-.age) AS age
+ """
+ Then a SemanticError should be raised at runtime: Not support both input and variable in GroupBy sentence.
+ When executing query:
+ """
+ $var1=GO FROM "Tim Duncan" OVER like YIELD like._dst AS dst;
+ $var2=GO FROM "Tim Duncan" OVER serve YIELD serve._dst AS dst;
+ YIELD count($var1.dst),$var2.dst AS count
+ """
+ Then a SemanticError should be raised at runtime: Only one variable allowed to use.
+ When executing query:
+ """
+ GO FROM "Tim Duncan" OVER like YIELD count(*)
+ """
+ Then a SemanticError should be raised at runtime: `COUNT(*)', not support aggregate function in go sentence.
+ When executing query:
+ """
+ GO FROM "Tim Duncan" OVER like where count(*) > 2
+ """
+ Then a SemanticError should be raised at runtime: `(COUNT(*)>2)', not support aggregate function in where sentence.
+ When executing query:
+ """
+ GO FROM "Marco Belinelli" OVER serve
+ YIELD $$.team.name AS name,
+ serve.end_year AS end_year
+ | GROUP BY $-.end_year
+ YIELD COUNT($$.team.name)
+ """
+ Then a SemanticError should be raised at runtime: Only support input and variable in GroupBy sentence.
+ When executing query:
+ """
+ GO FROM "Marco Belinelli" OVER serve
+ YIELD $$.team.name AS name,
+ serve._dst AS id
+ | GROUP BY $-.start_year
+ YIELD COUNT($-.id)
+ """
+ Then a SemanticError should be raised at runtime: `$-.start_year', not exist prop `start_year'
+ When executing query:
+ """
+ GO FROM "Marco Belinelli" OVER serve
+ YIELD $$.team.name AS name,
+ serve._dst AS id
+ | GROUP BY team
+ YIELD COUNT($-.id),
+ $-.name AS teamName
+ """
+ Then a SemanticError should be raised at runtime: Group `team' invalid
+ When executing query:
+ """
+ GO FROM "Marco Belinelli" OVER serve
+ YIELD $$.team.name AS name,
+ serve._dst AS id
+ | GROUP BY $-.name
+ YIELD COUNT($-.start_year)
+ """
+ Then a SemanticError should be raised at runtime: `$-.start_year', not exist prop `start_year'
+ When executing query:
+ """
+ GO FROM "Marco Belinelli" OVER serve
+ YIELD $$.team.name AS name,
+ serve._dst AS id
+ | GROUP BY $-.name
+ YIELD SUM(*)
+ """
+ Then a SemanticError should be raised at runtime: Could not apply aggregation function `SUM(*)' on `*`
+ When executing query:
+ """
+ GO FROM "Marco Belinelli" OVER serve
+ YIELD $$.team.name AS name,
+ serve._dst AS id
+ | GROUP BY $-.name
+ YIELD COUNT($-.name, $-.id)
+ """
+ Then a SyntaxError should be raised at runtime: syntax error near `, $-.id)'
+ When executing query:
+ """
+ GO FROM "Marco Belinelli" OVER serve
+ YIELD $$.team.name AS name,
+ serve._dst AS id
+ | GROUP BY $-.name, SUM($-.id)
+ YIELD $-.name, SUM($-.id)
+ """
+ Then a SemanticError should be raised at runtime: Group `SUM($-.id)' invalid
+ When executing query:
+ """
+ GO FROM "Marco Belinelli" OVER serve
+ YIELD $$.team.name AS name,
+ COUNT(serve._dst) AS id
+ """
+ Then a SemanticError should be raised at runtime: `COUNT(serve._dst) AS id', not support aggregate function in go sentence.
+
+# When executing query:
+# """
+# GO FROM "Marco Belinelli" OVER serve
+# YIELD $$.team.name AS name,
+# serve.end_year AS end_year
+# | GROUP BY $-.end_year
+# YIELD COUNT($var)
+# """
+# Then a SemanticError should be raised at runtime:
diff --git a/tests/tck/features/go/GO.feature b/tests/tck/features/go/GO.feature
index e8b4ae4c46b3fabfc01c41d31105f7ee1010866b..ab7754b541878f098cc164aed90de06ee6ebf8e0 100644
--- a/tests/tck/features/go/GO.feature
+++ b/tests/tck/features/go/GO.feature
@@ -1610,7 +1610,8 @@ Feature: Go Sentence
When executing query:
"""
GO 1 TO 2 STEPS FROM "Tim Duncan" OVER like WHERE like._dst != "YAO MING" YIELD like._dst AS vid
- | GROUP BY $-.vid YIELD 1 AS id | GROUP BY $-.id YIELD COUNT($-.id);
+ | GROUP BY $-.vid YIELD 1 AS id
+ | GROUP BY $-.id YIELD COUNT($-.id);
"""
Then the result should be, in any order, with relax comparison:
| COUNT($-.id) |
diff --git a/tests/tck/features/go/GroupbyLimit.IntVid.feature b/tests/tck/features/go/GroupbyLimit.IntVid.feature
index e919b03624e75bcd14296dcc165a3d2f9ef3c8fc..ce2ad2ae14e74faaab0965664de95d5cca7de36a 100644
--- a/tests/tck/features/go/GroupbyLimit.IntVid.feature
+++ b/tests/tck/features/go/GroupbyLimit.IntVid.feature
@@ -178,7 +178,7 @@ Feature: Groupby & limit Sentence
Then a SemanticError should be raised at runtime:
When executing query:
"""
- GO FROM hash("Carmelo Anthony"),hash("Dwyane Wade") OVER like YIELD $$.player.name AS name | GROUP BY $-.name, abs(5)
+ GO FROM hash("Carmelo Anthony"),hash("Dwyane Wade") OVER like YIELD $$.player.name AS name | GROUP BY $-.name
YIELD $-.name AS name, SUM(1.5) AS sum, COUNT(*) AS count, 1+1 AS cal
"""
Then the result should be, in any order, with relax comparison:
@@ -275,7 +275,7 @@ Feature: Groupby & limit Sentence
Scenario: Groupby works with orderby or limit test
When executing query:
"""
- GO FROM hash("Carmelo Anthony"),hash("Dwyane Wade") OVER like YIELD $$.player.name AS name | GROUP BY $-.name, abs(5)
+ GO FROM hash("Carmelo Anthony"),hash("Dwyane Wade") OVER like YIELD $$.player.name AS name | GROUP BY $-.name
YIELD $-.name AS name, SUM(1.5) AS sum, COUNT(*) AS count | ORDER BY $-.sum, $-.name
"""
Then the result should be, in order, with relax comparison:
@@ -286,7 +286,7 @@ Feature: Groupby & limit Sentence
| "LeBron James" | 3.0 | 2 |
When executing query:
"""
- GO FROM hash("Carmelo Anthony"),hash("Dwyane Wade") OVER like YIELD $$.player.name AS name | GROUP BY $-.name, abs(5)
+ GO FROM hash("Carmelo Anthony"),hash("Dwyane Wade") OVER like YIELD $$.player.name AS name | GROUP BY $-.name
YIELD $-.name AS name, SUM(1.5) AS sum, COUNT(*) AS count | ORDER BY $-.sum, $-.name DESC | LIMIT 2
"""
Then the result should be, in order, with relax comparison:
@@ -303,7 +303,7 @@ Feature: Groupby & limit Sentence
Then a SemanticError should be raised at runtime:
When executing query:
"""
- GO FROM hash("NON EXIST VERTEX ID") OVER like YIELD $$.player.name AS name | GROUP BY $-.name, abs(5)
+ GO FROM hash("NON EXIST VERTEX ID") OVER like YIELD $$.player.name AS name | GROUP BY $-.name
YIELD $-.name AS name, SUM(1.5) AS sum, COUNT(*) AS count | ORDER BY $-.sum | LIMIT 2
"""
Then the result should be, in order, with relax comparison:
diff --git a/tests/tck/features/go/GroupbyLimit.feature b/tests/tck/features/go/GroupbyLimit.feature
index 6445ce972947fd6c59e6eacd7ec80bd6fd10827a..fb2a65cb49150d885106af0841a63fb82ae33ecc 100644
--- a/tests/tck/features/go/GroupbyLimit.feature
+++ b/tests/tck/features/go/GroupbyLimit.feature
@@ -193,7 +193,7 @@ Feature: Groupby & limit Sentence
Then a SemanticError should be raised at runtime:
When executing query:
"""
- GO FROM "Carmelo Anthony","Dwyane Wade" OVER like YIELD $$.player.name AS name | GROUP BY $-.name, abs(5)
+ GO FROM "Carmelo Anthony","Dwyane Wade" OVER like YIELD $$.player.name AS name | GROUP BY $-.name
YIELD $-.name AS name, SUM(1.5) AS sum, COUNT(*) AS count, 1+1 AS cal
"""
Then the result should be, in any order, with relax comparison:
@@ -290,7 +290,7 @@ Feature: Groupby & limit Sentence
Scenario: Groupby works with orderby or limit test
When executing query:
"""
- GO FROM "Carmelo Anthony","Dwyane Wade" OVER like YIELD $$.player.name AS name | GROUP BY $-.name, abs(5)
+ GO FROM "Carmelo Anthony","Dwyane Wade" OVER like YIELD $$.player.name AS name | GROUP BY $-.name
YIELD $-.name AS name, SUM(1.5) AS sum, COUNT(*) AS count | ORDER BY $-.sum, $-.name
"""
Then the result should be, in order, with relax comparison:
@@ -301,7 +301,7 @@ Feature: Groupby & limit Sentence
| "LeBron James" | 3.0 | 2 |
When executing query:
"""
- GO FROM "Carmelo Anthony","Dwyane Wade" OVER like YIELD $$.player.name AS name | GROUP BY $-.name, abs(5)
+ GO FROM "Carmelo Anthony","Dwyane Wade" OVER like YIELD $$.player.name AS name | GROUP BY $-.name
YIELD $-.name AS name, SUM(1.5) AS sum, COUNT(*) AS count | ORDER BY $-.sum, $-.name DESC | LIMIT 2
"""
Then the result should be, in order, with relax comparison:
@@ -318,7 +318,7 @@ Feature: Groupby & limit Sentence
Then a SemanticError should be raised at runtime:
When executing query:
"""
- GO FROM "NON EXIST VERTEX ID" OVER like YIELD $$.player.name AS name | GROUP BY $-.name, abs(5)
+ GO FROM "NON EXIST VERTEX ID" OVER like YIELD $$.player.name AS name | GROUP BY $-.name
YIELD $-.name AS name, SUM(1.5) AS sum, COUNT(*) AS count | ORDER BY $-.sum | LIMIT 2
"""
Then the result should be, in order, with relax comparison: