From e72208e2df22fd549c3a074e3764b5695894a50b Mon Sep 17 00:00:00 2001 From: liujinyu Date: Mon, 18 Mar 2024 17:34:11 +0800 Subject: [PATCH] =?UTF-8?q?select=E6=94=AF=E6=8C=81rotate/not=20rotate?= =?UTF-8?q?=E5=87=BD=E6=95=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/common/backend/nodes/copyfuncs.cpp | 57 ++ src/common/backend/nodes/equalfuncs.cpp | 51 ++ src/common/backend/nodes/makefuncs.cpp | 1 + src/common/backend/nodes/nodeFuncs.cpp | 45 ++ src/common/backend/nodes/nodes.cpp | 4 + src/common/backend/nodes/outfuncs.cpp | 58 ++ src/common/backend/nodes/readfuncs.cpp | 49 ++ src/common/backend/parser/analyze.cpp | 243 +++++++++ src/common/backend/parser/gram.y | 306 ++++++++++- src/common/backend/parser/parse_clause.cpp | 175 +++++- src/common/backend/parser/parse_collate.cpp | 1 + src/common/backend/parser/parse_expr.cpp | 2 + src/common/backend/parser/parse_func.cpp | 12 + src/common/backend/parser/parse_relation.cpp | 19 +- src/common/backend/parser/parse_utilcmd.cpp | 1 + src/common/backend/utils/adt/ruleutils.cpp | 4 + src/common/backend/utils/init/globals.cpp | 3 +- .../interfaces/libpq/frontend_parser/gram.y | 3 +- .../cbb/instruments/utils/unique_query.cpp | 1 + src/gausskernel/optimizer/plan/planagg.cpp | 2 + src/gausskernel/optimizer/plan/subselect.cpp | 1 + src/gausskernel/runtime/executor/execQual.cpp | 1 + src/gausskernel/runtime/executor/nodeAgg.cpp | 7 + .../runtime/vecexecutor/vecexpression.cpp | 1 + .../runtime/vecexecutor/vecnode/vecagg.cpp | 17 + src/include/executor/node/nodeAgg.h | 1 + src/include/miscadmin.h | 1 + src/include/nodes/nodes.h | 11 +- src/include/nodes/parsenodes_common.h | 30 + src/include/nodes/primnodes.h | 1 + src/include/parser/kwlist.h | 1 + .../expected/gb_ora_rotate_unrotate.out | 512 ++++++++++++++++++ src/test/regress/parallel_schedule0C | 4 + .../regress/sql/gb_ora_rotate_unrotate.sql | 192 +++++++ 34 files changed, 1791 insertions(+), 26 deletions(-) create mode 100644 src/test/regress/expected/gb_ora_rotate_unrotate.out create mode 100644 src/test/regress/sql/gb_ora_rotate_unrotate.sql diff --git a/src/common/backend/nodes/copyfuncs.cpp b/src/common/backend/nodes/copyfuncs.cpp index 8d9b2cdb6..655d89b16 100644 --- a/src/common/backend/nodes/copyfuncs.cpp +++ b/src/common/backend/nodes/copyfuncs.cpp @@ -2675,6 +2675,7 @@ static Aggref* _copyAggref(const Aggref* from) COPY_NODE_FIELD(aggargtypes); COPY_SCALAR_FIELD(aggsplit); COPY_SCALAR_FIELD(aggtranstype); + COPY_NODE_FIELD(aggfilter); return newnode; } @@ -4068,6 +4069,7 @@ static FuncCall* _copyFuncCall(const FuncCall* from) COPY_STRING_FIELD(colname); COPY_NODE_FIELD(args); COPY_NODE_FIELD(agg_order); + COPY_NODE_FIELD(agg_filter); COPY_SCALAR_FIELD(agg_star); COPY_SCALAR_FIELD(agg_distinct); COPY_SCALAR_FIELD(func_variadic); @@ -4176,6 +4178,47 @@ static WindowDef* _copyWindowDef(const WindowDef* from) return newnode; } +static RotateClause* _copyRotateInfo(const RotateClause* from) +{ + RotateClause *newnode = makeNode(RotateClause); + + COPY_NODE_FIELD(forColName); + COPY_NODE_FIELD(inExprList); + COPY_NODE_FIELD(aggregateFuncCallList); + + return newnode; +} + +static UnrotateClause* _copyUnrotateInfo(const UnrotateClause* from) +{ + UnrotateClause *newnode = makeNode(UnrotateClause); + + COPY_SCALAR_FIELD(includeNull); + COPY_NODE_FIELD(colNameList); + COPY_NODE_FIELD(forColName); + COPY_NODE_FIELD(inExprList); + + return newnode; +} + +static RotateInCell *_copyRotateInCell(const RotateInCell *from) +{ + RotateInCell *newnode = makeNode(RotateInCell); + + COPY_STRING_FIELD(aliasname); + COPY_NODE_FIELD(rotateInExpr); + + return newnode; +} +static UnrotateInCell *_copyUnrotateInCell(const UnrotateInCell *from) +{ + UnrotateInCell *newnode = makeNode(UnrotateInCell); + + COPY_NODE_FIELD(aliaList); + COPY_NODE_FIELD(unrotateInExpr); + + return newnode; +} static RangeSubselect* _copyRangeSubselect(const RangeSubselect* from) { @@ -4184,6 +4227,7 @@ static RangeSubselect* _copyRangeSubselect(const RangeSubselect* from) COPY_SCALAR_FIELD(lateral); COPY_NODE_FIELD(subquery); COPY_NODE_FIELD(alias); + COPY_NODE_FIELD(rotate); return newnode; } @@ -5040,6 +5084,7 @@ static SelectStmt* _copySelectStmt(const SelectStmt* from) COPY_NODE_FIELD(intoClause); COPY_NODE_FIELD(targetList); COPY_NODE_FIELD(fromClause); + COPY_NODE_FIELD(unrotateInfo); COPY_NODE_FIELD(startWithClause); COPY_NODE_FIELD(whereClause); COPY_NODE_FIELD(groupClause); @@ -8863,6 +8908,18 @@ void* copyObject(const void* from) case T_WindowDef: retval = _copyWindowDef((WindowDef*)from); break; + case T_RotateClause: + retval = _copyRotateInfo((RotateClause*)from); + break; + case T_UnrotateClause: + retval = _copyUnrotateInfo((UnrotateClause*)from); + break; + case T_RotateInCell: + retval = _copyRotateInCell((RotateInCell*)from); + break; + case T_UnrotateInCell: + retval = _copyUnrotateInCell((UnrotateInCell*)from); + break; case T_RangeSubselect: retval = _copyRangeSubselect((RangeSubselect*)from); break; diff --git a/src/common/backend/nodes/equalfuncs.cpp b/src/common/backend/nodes/equalfuncs.cpp index bbebbd0cc..c2b4440e8 100644 --- a/src/common/backend/nodes/equalfuncs.cpp +++ b/src/common/backend/nodes/equalfuncs.cpp @@ -246,6 +246,7 @@ static bool _equalAggref(const Aggref* a, const Aggref* b) COMPARE_LOCATION_FIELD(location); COMPARE_NODE_FIELD(aggargtypes); COMPARE_SCALAR_FIELD(aggsplit); + COMPARE_NODE_FIELD(aggfilter); return true; } @@ -1073,6 +1074,7 @@ static bool _equalSelectStmt(const SelectStmt* a, const SelectStmt* b) COMPARE_NODE_FIELD(intoClause); COMPARE_NODE_FIELD(targetList); COMPARE_NODE_FIELD(fromClause); + COMPARE_NODE_FIELD(unrotateInfo); COMPARE_NODE_FIELD(startWithClause); COMPARE_NODE_FIELD(whereClause); COMPARE_NODE_FIELD(groupClause); @@ -2584,6 +2586,7 @@ static bool _equalFuncCall(const FuncCall* a, const FuncCall* b) COMPARE_STRING_FIELD(colname); COMPARE_NODE_FIELD(args); COMPARE_NODE_FIELD(agg_order); + COMPARE_NODE_FIELD(agg_filter); COMPARE_SCALAR_FIELD(agg_within_group); COMPARE_SCALAR_FIELD(agg_star); COMPARE_SCALAR_FIELD(agg_distinct); @@ -2733,11 +2736,47 @@ static bool _equalWindowDef(const WindowDef* a, const WindowDef* b) return true; } +static bool _equalRotateInfo(const RotateClause* a, const RotateClause* b) +{ + COMPARE_NODE_FIELD(forColName); + COMPARE_NODE_FIELD(inExprList); + COMPARE_NODE_FIELD(aggregateFuncCallList); + + return true; +} + +static bool _equalUnrotateInfo(const UnrotateClause* a, const UnrotateClause* b) +{ + COMPARE_SCALAR_FIELD(includeNull); + COMPARE_NODE_FIELD(colNameList); + COMPARE_NODE_FIELD(forColName); + COMPARE_NODE_FIELD(inExprList); + + return true; +} + +static bool _equalRotateInCell(const RotateInCell *a, const RotateInCell *b) +{ + COMPARE_STRING_FIELD(aliasname); + COMPARE_NODE_FIELD(rotateInExpr); + + return true; +} + +static bool _equalUnrotateInCell(const UnrotateInCell *a, const UnrotateInCell *b) +{ + COMPARE_NODE_FIELD(aliaList); + COMPARE_NODE_FIELD(unrotateInExpr); + + return true; +} + static bool _equalRangeSubselect(const RangeSubselect* a, const RangeSubselect* b) { COMPARE_SCALAR_FIELD(lateral); COMPARE_NODE_FIELD(subquery); COMPARE_NODE_FIELD(alias); + COMPARE_NODE_FIELD(rotate); return true; } @@ -4454,6 +4493,18 @@ bool equal(const void* a, const void* b) case T_StartWithClause: retval = _equalStartWithClause((StartWithClause*) a, (StartWithClause*) b); break; + case T_RotateClause: + retval = _equalRotateInfo((RotateClause*)a, (RotateClause*)b); + break; + case T_UnrotateClause: + retval = _equalUnrotateInfo((UnrotateClause*)a, (UnrotateClause*)b); + break; + case T_RotateInCell: + retval = _equalRotateInCell((RotateInCell*)a, (RotateInCell*)b); + break; + case T_UnrotateInCell: + retval = _equalUnrotateInCell((UnrotateInCell*)a, (UnrotateInCell*)b); + break; case T_UpsertClause: retval = _equalUpsertClause((UpsertClause*)a, (UpsertClause*)b); break; diff --git a/src/common/backend/nodes/makefuncs.cpp b/src/common/backend/nodes/makefuncs.cpp index ccd789e06..4bd13b80c 100644 --- a/src/common/backend/nodes/makefuncs.cpp +++ b/src/common/backend/nodes/makefuncs.cpp @@ -643,6 +643,7 @@ FuncCall* makeFuncCall(List* funcname, List* args, int location) funcCall->func_variadic = FALSE; funcCall->agg_distinct = FALSE; funcCall->agg_order = NIL; + funcCall->agg_filter = NULL; funcCall->over = NULL; funcCall->location = location; diff --git a/src/common/backend/nodes/nodeFuncs.cpp b/src/common/backend/nodes/nodeFuncs.cpp index 57c2a0945..01b25da07 100644 --- a/src/common/backend/nodes/nodeFuncs.cpp +++ b/src/common/backend/nodes/nodeFuncs.cpp @@ -1765,6 +1765,9 @@ bool expression_tree_walker(Node* node, bool (*walker)(), void* context) if (expression_tree_walker((Node*)expr->aggdistinct, walker, context)) { return true; } + if (expression_tree_walker((Node*)expr->aggfilter, walker, context)) { + return true; + } } break; case T_GroupingFunc: { GroupingFunc* grouping = (GroupingFunc*)node; @@ -2375,6 +2378,7 @@ Node* expression_tree_mutator(Node* node, Node* (*mutator)(Node*, void*), void* MUTATE(newnode->args, aggref->args, List*); MUTATE(newnode->aggorder, aggref->aggorder, List*); MUTATE(newnode->aggdistinct, aggref->aggdistinct, List*); + MUTATE(newnode->aggfilter, aggref->aggfilter, Expr*); return (Node*)newnode; } break; case T_WindowFunc: { @@ -3275,6 +3279,9 @@ bool raw_expression_tree_walker(Node* node, bool (*walker)(), void* context) if (p2walker(stmt->fromClause, context)) { return true; } + if (p2walker(stmt->unrotateInfo, context)) { + return true; + } if (p2walker(stmt->whereClause, context)) { return true; } @@ -3335,6 +3342,9 @@ bool raw_expression_tree_walker(Node* node, bool (*walker)(), void* context) if (p2walker(fcall->agg_order, context)) { return true; } + if (p2walker(fcall->agg_filter, context)) { + return true; + } if (p2walker(fcall->over, context)) { return true; } @@ -3413,6 +3423,9 @@ bool raw_expression_tree_walker(Node* node, bool (*walker)(), void* context) if (p2walker(rs->alias, context)) { return true; } + if (p2walker(rs->rotate, context)) { + return true; + } } break; case T_RangeFunction: { RangeFunction* rf = (RangeFunction*)node; @@ -3488,6 +3501,38 @@ bool raw_expression_tree_walker(Node* node, bool (*walker)(), void* context) } break; case T_WithClause: return p2walker(((WithClause*)node)->ctes, context); + case T_RotateClause: { + RotateClause *stmt = (RotateClause*)node; + + if (p2walker(stmt->forColName, context)) + return true; + if (p2walker(stmt->inExprList, context)) + return true; + if (p2walker(stmt->aggregateFuncCallList, context)) + return true; + } break; + case T_UnrotateClause: { + UnrotateClause *stmt = (UnrotateClause*)node; + + if (p2walker(stmt->forColName, context)) + return true; + if (p2walker(stmt->inExprList, context)) + return true; + } break; + case T_RotateInCell: { + RotateInCell *stmt = (RotateInCell*)node; + + if (p2walker(stmt->rotateInExpr, context)) + return true; + } break; + case T_UnrotateInCell: { + UnrotateInCell *stmt = (UnrotateInCell*)node; + + if (p2walker(stmt->aliaList, context)) + return true; + if (p2walker(stmt->unrotateInExpr, context)) + return true; + } break; case T_UpsertClause: return p2walker(((UpsertClause*)node)->targetList, context); case T_CommonTableExpr: diff --git a/src/common/backend/nodes/nodes.cpp b/src/common/backend/nodes/nodes.cpp index b61be2af4..16e21982e 100755 --- a/src/common/backend/nodes/nodes.cpp +++ b/src/common/backend/nodes/nodes.cpp @@ -622,6 +622,10 @@ static const TagStr g_tagStrArr[] = {{T_Invalid, "Invalid"}, // End DB4AI {T_TdigestData, "TdigestData"}, {T_CentroidPoint, "CentroidPoint"}, + {T_RotateClause, "RotateClause"}, + {T_UnrotateClause, "UnrotateClause"}, + {T_RotateInCell, "RotateInCell"}, + {T_UnrotateInCell, "UnrotateInCell"}, {T_AdvanceCatalogXminCmd, "AdvanceCatalogXminCmd"}, {T_UserSetElem, "UserSetElem"}, {T_UserVar, "UserVar"}, diff --git a/src/common/backend/nodes/outfuncs.cpp b/src/common/backend/nodes/outfuncs.cpp index 6495a1dba..5e4ef26eb 100755 --- a/src/common/backend/nodes/outfuncs.cpp +++ b/src/common/backend/nodes/outfuncs.cpp @@ -2565,6 +2565,9 @@ static void _outAggref(StringInfo str, Aggref* node) WRITE_INT_FIELD(aggsplit); WRITE_OID_FIELD(aggtranstype); } + if (t_thrd.proc->workingVersionNum >= ROTATE_UNROTATE_VERSION_NUM) { + WRITE_NODE_FIELD(aggfilter); + } } static void _outGroupingFunc(StringInfo str, const GroupingFunc* node) @@ -4193,6 +4196,9 @@ static void _outSelectStmt(StringInfo str, SelectStmt* node) WRITE_NODE_FIELD(intoClause); WRITE_NODE_FIELD(targetList); WRITE_NODE_FIELD(fromClause); + if (t_thrd.proc->workingVersionNum >= ROTATE_UNROTATE_VERSION_NUM) { + WRITE_NODE_FIELD(unrotateInfo); + } if (t_thrd.proc->workingVersionNum >= SWCB_VERSION_NUM) { WRITE_NODE_FIELD(startWithClause); } @@ -4222,6 +4228,9 @@ static void _outFuncCall(StringInfo str, FuncCall* node) WRITE_STRING_FIELD(colname); WRITE_NODE_FIELD(args); WRITE_NODE_FIELD(agg_order); + if (t_thrd.proc->workingVersionNum >= ROTATE_UNROTATE_VERSION_NUM) { + WRITE_NODE_FIELD(agg_filter); + } WRITE_BOOL_FIELD(agg_within_group); WRITE_BOOL_FIELD(agg_star); WRITE_BOOL_FIELD(agg_distinct); @@ -5526,6 +5535,40 @@ static void _outWindowDef(StringInfo str, WindowDef* node) WRITE_LOCATION_FIELD(location); } +static void _outRotateInfo(StringInfo str, RotateClause* node) +{ + WRITE_NODE_TYPE("ROTATEINFO"); + + WRITE_NODE_FIELD(forColName); + WRITE_NODE_FIELD(inExprList); + WRITE_NODE_FIELD(aggregateFuncCallList); +} + +static void _outUnrotateInfo(StringInfo str, UnrotateClause* node) +{ + WRITE_NODE_TYPE("UNROTATEINFO"); + + WRITE_BOOL_FIELD(includeNull); + WRITE_NODE_FIELD(colNameList); + WRITE_NODE_FIELD(forColName); + WRITE_NODE_FIELD(inExprList); +} + +static void _outRotateInCell(StringInfo str, RotateInCell *node) +{ + WRITE_NODE_TYPE("ROTATEINCELL"); + + WRITE_STRING_FIELD(aliasname); + WRITE_NODE_FIELD(rotateInExpr); +} +static void _outUnrotateInCell(StringInfo str, UnrotateInCell *node) +{ + WRITE_NODE_TYPE("UNROTATEINCELL"); + + WRITE_NODE_FIELD(aliaList); + WRITE_NODE_FIELD(unrotateInExpr); +} + static void _outRangeSubselect(StringInfo str, RangeSubselect* node) { WRITE_NODE_TYPE("RANGESUBSELECT"); @@ -5533,6 +5576,9 @@ static void _outRangeSubselect(StringInfo str, RangeSubselect* node) WRITE_BOOL_FIELD(lateral); WRITE_NODE_FIELD(subquery); WRITE_NODE_FIELD(alias); + if (t_thrd.proc->workingVersionNum >= ROTATE_UNROTATE_VERSION_NUM) { + WRITE_NODE_FIELD(rotate); + } } static void _outRangeFunction(StringInfo str, RangeFunction* node) @@ -6997,6 +7043,18 @@ static void _outNode(StringInfo str, const void* obj) break; #endif + case T_RotateClause: + _outRotateInfo(str, (RotateClause*)obj); + break; + case T_UnrotateClause: + _outUnrotateInfo(str, (UnrotateClause*)obj); + break; + case T_RotateInCell: + _outRotateInCell(str, (RotateInCell*)obj); + break; + case T_UnrotateInCell: + _outUnrotateInCell(str, (UnrotateInCell*)obj); + break; case T_PruningResult: _outPruningResult(str, (PruningResult *)obj); break; diff --git a/src/common/backend/nodes/readfuncs.cpp b/src/common/backend/nodes/readfuncs.cpp index 130aaf1d3..eb15a3ee1 100755 --- a/src/common/backend/nodes/readfuncs.cpp +++ b/src/common/backend/nodes/readfuncs.cpp @@ -2295,6 +2295,10 @@ static Aggref* _readAggref(void) { READ_OID_FIELD(aggtranstype); } + IF_EXIST(aggfilter) + { + READ_NODE_FIELD(aggfilter); + } READ_DONE(); } @@ -6421,6 +6425,47 @@ static DependenciesType* _readDependenciesType() READ_DONE(); } +static RotateClause* _readRotateClause() +{ + READ_LOCALS(RotateClause); + + READ_NODE_FIELD(forColName); + READ_NODE_FIELD(inExprList); + READ_NODE_FIELD(aggregateFuncCallList); + + READ_DONE(); +} + +static UnrotateClause* _readUnrotateClause() +{ + READ_LOCALS(UnrotateClause); + + READ_BOOL_FIELD(includeNull); + READ_NODE_FIELD(colNameList); + READ_NODE_FIELD(forColName); + READ_NODE_FIELD(inExprList); + + READ_DONE(); +} + +static RotateInCell *_readRotateCell() +{ + READ_LOCALS(RotateInCell); + READ_STRING_FIELD(aliasname); + READ_NODE_FIELD(rotateInExpr); + + READ_DONE(); +} + +static UnrotateInCell *_readUnrotateCell() +{ + READ_LOCALS(UnrotateInCell); + READ_NODE_FIELD(aliaList); + READ_NODE_FIELD(unrotateInExpr); + + READ_DONE(); +} + /* * parseNodeString * @@ -6930,6 +6975,10 @@ Node* parseNodeString(void) return_value = _readDependenciesProchead(); } else if (MATCH("DependenciesType", 16)) { return_value = _readDependenciesType(); + } else if (MATCH("ROTATEINFO", 10)) { + return_value = _readRotateClause(); + } else if (MATCH("UNROTATEINFO", 12)) { + return_value = _readUnrotateClause(); } else { ereport(ERROR, (errcode(ERRCODE_UNRECOGNIZED_NODE_TYPE), diff --git a/src/common/backend/parser/analyze.cpp b/src/common/backend/parser/analyze.cpp index f45d66ef0..658823c2f 100644 --- a/src/common/backend/parser/analyze.cpp +++ b/src/common/backend/parser/analyze.cpp @@ -114,6 +114,7 @@ static void transformVariableSetStmt(ParseState* pstate, VariableSetStmt* stmt); static Query* transformVariableMutiSetStmt(ParseState* pstate, VariableMultiSetStmt* muti_stmt); static Query* transformSelectStmt( ParseState* pstate, SelectStmt* stmt, bool isFirstNode = true, bool isCreateView = false); +static Query *transformUnrotateStmt(ParseState *pstate, SelectStmt *stmt); static Query* transformValuesClause(ParseState* pstate, SelectStmt* stmt); static Query* transformSetOperationStmt(ParseState* pstate, SelectStmt* stmt); static Node* transformSetOperationTree(ParseState* pstate, SelectStmt* stmt, bool isTopLevel, List** targetlist); @@ -2902,6 +2903,11 @@ static Query* transformSelectStmt(ParseState* pstate, SelectStmt* stmt, bool isF Node* qual = NULL; ListCell* l = NULL; + /* support unrotate grammar */ + if (stmt->unrotateInfo != NULL) { + return transformUnrotateStmt(pstate, stmt); + } + qry->commandType = CMD_SELECT; if (stmt->startWithClause != NULL) { @@ -3089,6 +3095,243 @@ static Query* transformSelectStmt(ParseState* pstate, SelectStmt* stmt, bool isF return qry; } +static List* removeTargetListByNameList(List* targetList, List* nameList) +{ + ListCell * cell, *targetCell, *next, *prev; + bool isfind = false; + prev = NULL; + for (targetCell = list_head(targetList); targetCell; targetCell = next) { + ResTarget *resTarget = (ResTarget *)lfirst(targetCell); + next = lnext(targetCell); + if (IsA(resTarget->val, ColumnRef)) { + isfind = false; + char *colName = strVal(linitial(((ColumnRef *)resTarget->val)->fields)); + foreach (cell, nameList) { + if (strcmp(strVal((Value *)lfirst(cell)), colName) == 0) { + targetList = list_delete_cell(targetList, targetCell, prev); + isfind = true; + break; + } + } + if (!isfind) + prev = targetCell; + } + } + return targetList; +} + +static Query* transformUnrotateStmt(ParseState* pstate, SelectStmt* stmt) +{ + List *parsetree_list; + ListCell *cell, *cell1, *targetCell, *next, *prev; + ListCell *inexprCell, *colCell, *forCell, *aliasCell; + StringInfoData union_all_sql; + StringInfoData from_clause_sql; + int counter = 0; + int in_counter = 0; + List *targetList = NIL; + List *aStarList = NIL; + ParseState *pstate1 = make_parsestate(NULL); + pstate1->p_sourcetext = pstrdup(pstate->p_sourcetext); + + transformFromClause(pstate1, stmt->fromClause); + initStringInfo(&from_clause_sql); + RangeTblEntry *rte = (RangeTblEntry *)linitial(pstate1->p_rtable); + if (RTE_RELATION == rte->rtekind) + appendStringInfo(&from_clause_sql, " FROM %s ", quote_identifier(rte->relname)); + else if (RTE_SUBQUERY == rte->rtekind) { + StringInfo select_sql = makeStringInfo(); + deparse_query(rte->subquery, select_sql, NIL, false, false); + appendStringInfo(&from_clause_sql, " FROM (%s) ", select_sql->data); + DestroyStringInfo(select_sql); + } else { + ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR), errmsg("NOT ROTATE in from clause error"))); + } + if (rte->alias) + appendStringInfo(&from_clause_sql, "AS %s ", rte->alias->aliasname); + List *stmt_targetList = list_copy(stmt->targetList); + /* remove target in colNameList */ + stmt_targetList = removeTargetListByNameList(stmt_targetList, stmt->unrotateInfo->colNameList); + /* remove target in forColName */ + stmt_targetList = removeTargetListByNameList(stmt_targetList, stmt->unrotateInfo->forColName); + + foreach(targetCell, stmt_targetList) { + ResTarget *resTarget1 = (ResTarget *)lfirst(targetCell); + ColumnRef *cref = (ColumnRef *)resTarget1->val; + Node *field = (Node *)linitial(cref->fields); + if (IsA(field, A_Star)) { + if (!targetList) { + aStarList = list_make1(lfirst(targetCell)); + targetList = transformTargetList(pstate1, aStarList, EXPR_KIND_SELECT_TARGET); + free_parsestate(pstate1); + break; + } + } + } + + /* remove target in unrotateInExpr */ + foreach (inexprCell, stmt->unrotateInfo->inExprList) { + UnrotateInCell *unrotateinCell = (UnrotateInCell *)lfirst(inexprCell); + foreach (cell, unrotateinCell->unrotateInExpr) { + ResTarget *resTarget = (ResTarget *)lfirst(cell); + if (IsA(resTarget->val, ColumnRef)) { + const char *colName = strVal((Value *)linitial(((ColumnRef *)resTarget->val)->fields)); + prev = NULL; + for (targetCell = list_head(targetList); targetCell; targetCell = next) { + TargetEntry *te = (TargetEntry *)lfirst(targetCell); + next = lnext(targetCell); + if (strcmp(te->resname, colName) == 0) + targetList = list_delete_cell(targetList, targetCell, prev); + else + prev = targetCell; + } + for (targetCell = list_head(stmt_targetList); targetCell; targetCell = next) { + ResTarget *rt = (ResTarget *)lfirst(targetCell); + char *colName1 = strVal((Value *)linitial(((ColumnRef *)rt->val)->fields)); + next = lnext(targetCell); + if (strcmp(colName1, colName) == 0) + stmt_targetList = list_delete_cell(stmt_targetList, targetCell, prev); + else + prev = targetCell; + } + } else { + ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR), errmsg("NOT ROTATE in clause error"), + parser_errposition(pstate, exprLocation((Node *)resTarget->val)))); + } + } + } + + initStringInfo(&union_all_sql); + foreach (inexprCell, stmt->unrotateInfo->inExprList) { + UnrotateInCell *unrotateinCell = (UnrotateInCell *)lfirst(inexprCell); + + if (counter > 0) + appendStringInfo(&union_all_sql, "UNION ALL "); + + appendStringInfo(&union_all_sql, "SELECT "); + if (list_length(stmt->unrotateInfo->colNameList) != list_length(unrotateinCell->unrotateInExpr)) { + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("the number of elements in NOT ROTATE IN CLAUSE doesn't agree with the number of columns NOT ROTATE specified"))); + } + + foreach (targetCell, stmt_targetList) { + ResTarget *resTarget1 = (ResTarget *)lfirst(targetCell); + ColumnRef *cref = (ColumnRef *)resTarget1->val; + Node *field = (Node *)linitial(cref->fields); + if (IsA(field, A_Star)) { + foreach (cell1, targetList) { + TargetEntry *te = (TargetEntry *)lfirst(cell1); + appendStringInfo(&union_all_sql, "%s, ", te->resname); + } + } else + appendStringInfo(&union_all_sql, "%s, ", strVal((Value *)field)); + } + + if (!unrotateinCell->aliaList) { + foreach(forCell,stmt->unrotateInfo->forColName) { + in_counter = 0; + appendStringInfo(&union_all_sql, "\'"); + foreach(cell, unrotateinCell->unrotateInExpr) { + if (in_counter > 0) { + appendStringInfo(&union_all_sql, "_"); + } + ResTarget *resTarget = (ResTarget *)lfirst(cell); + if (IsA(resTarget->val, ColumnRef)) { + if (NULL != resTarget->name) { + appendStringInfo(&union_all_sql, "%s", resTarget->name); + } + else { + appendStringInfo(&union_all_sql, "%s", strVal((Value *) linitial(((ColumnRef*) resTarget->val)->fields))); + } + } + else { + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("NOT ROTATE in clause error"), + parser_errposition(pstate, exprLocation((Node*)resTarget->val)))); + } + in_counter++; + } + appendStringInfo(&union_all_sql, "\' AS %s,", strVal((Value *)lfirst(forCell))); + } + } + else { + if (list_length(stmt->unrotateInfo->forColName) != list_length(unrotateinCell->aliaList)) { + ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("the number of elements in alias clause doesn't agree with the number of columns NOT ROTATE_FOR specified"))); + } + forboth(forCell, stmt->unrotateInfo->forColName, aliasCell, unrotateinCell->aliaList) { + if (!IsA(lfirst(aliasCell), A_Const)) { + ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR), errmsg("NOT ROTATE alias clause error"))); + } + appendStringInfo(&union_all_sql, "\'%s\' AS %s,", strVal(&((A_Const *)lfirst(aliasCell))->val), strVal((Value *)lfirst(forCell))); + } + } + int counter1 = 0; + forboth(colCell, stmt->unrotateInfo->colNameList, cell, unrotateinCell->unrotateInExpr) { + ResTarget *resTarget = (ResTarget *)lfirst(cell); + if (IsA(resTarget->val, ColumnRef)) + { + if (counter1 > 0) + appendStringInfo(&union_all_sql, ","); + appendStringInfo(&union_all_sql, " %s AS %s", strVal((Value *)linitial(((ColumnRef *)resTarget->val)->fields)), strVal((Value *)lfirst(colCell))); + counter1++; + } + else { + ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR), errmsg("NOT ROTATE in clause error"), + parser_errposition(pstate, exprLocation((Node *)resTarget->val)))); + } + } + + appendStringInfo(&union_all_sql, "%s", from_clause_sql.data); + in_counter = 0; + if (stmt->unrotateInfo->includeNull == false) { + appendStringInfo(&union_all_sql, "where "); + foreach(cell, unrotateinCell->unrotateInExpr) { + if (in_counter > 0) + appendStringInfo(&union_all_sql, "or "); + ResTarget *resTarget = (ResTarget *)lfirst(cell); + if (IsA(resTarget->val, ColumnRef)) { + appendStringInfo(&union_all_sql, "%s is not null ", + strVal((Value *) linitial(((ColumnRef*) resTarget->val)->fields))); + } + else { + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("NOT ROTATE in clause error"), + parser_errposition(pstate, exprLocation((Node*)resTarget->val)))); + } + in_counter++; + } + } + counter++; + } + appendStringInfo(&union_all_sql, ";"); + pfree_ext(from_clause_sql.data); + list_free_ext(stmt_targetList); + list_free_ext(aStarList); + list_free_ext(targetList); + + parsetree_list = pg_parse_query(union_all_sql.data); + + /* rewrite unrotate clause as a subquery */ + RangeSubselect *subselect = makeNode(RangeSubselect); + Alias *sub_alias = makeAlias("unrotate_rewrite", NIL); + subselect->subquery = (Node *)linitial(parsetree_list); + subselect->alias = sub_alias; + + list_free_ext(stmt->fromClause); + stmt->fromClause = list_make1((Node *)subselect); + + list_free_ext(stmt->unrotateInfo->colNameList); + list_free_ext(stmt->unrotateInfo->forColName); + list_free_ext(stmt->unrotateInfo->inExprList); + pfree_ext(stmt->unrotateInfo); + + return transformStmt(pstate, (Node *)stmt); +} + /* * transformValuesClause - * transforms a VALUES clause that's being used as a standalone SELECT diff --git a/src/common/backend/parser/gram.y b/src/common/backend/parser/gram.y index 18d6e0d19..9620c5ae4 100644 --- a/src/common/backend/parser/gram.y +++ b/src/common/backend/parser/gram.y @@ -229,6 +229,7 @@ static long long get_pid(const char *strsid); static Node *MakeAnonyBlockFuncStmt(int flag, const char * str); static CharsetCollateOptions* MakeCharsetCollateOptions(CharsetCollateOptions *options, CharsetCollateOptions *option); static Node *checkNullNode(Node *n); +static Node *make_AStar_subquery(RangeVar *rangeVar); #define TYPE_LEN 4 /* strlen("TYPE") */ #define DATE_LEN 4 /* strlen("DATE") */ #define DECLARE_LEN 9 /* strlen(" DECLARE ") */ @@ -354,6 +355,8 @@ static char* IdentResolveToChar(char *ident, core_yyscan_t yyscanner); CharsetCollateOptions *charsetcollateopt; OnDuplicateAction onduplicate; struct CondInfo* condinfo; + RotateClause *rotateinfo; + UnrotateClause *unrotateinfo; } %type stmt schema_stmt @@ -615,6 +618,12 @@ static char* IdentResolveToChar(char *ident, core_yyscan_t yyscanner); /* INSERT */ %type insert_rest +%type rotate_for_clause rotate_in_clause unrotate_in_clause func_application_list unrotate_name_list rotatein_list_single rotatein_list_multi +%type unrotatein_list_single unrotatein_list_multi in_expr_list unrotatein_alias_clause unrotatein_alias +%type rotate_clause +%type unrotate_clause +%type include_exclude_null_clause +%type filter_clause %type upsert_clause %type merge_insert merge_update @@ -942,7 +951,7 @@ static char* IdentResolveToChar(char *ident, core_yyscan_t yyscanner); RANDOMIZED RANGE RATIO RAW READ REAL REASSIGN REBUILD RECHECK RECURSIVE RECYCLEBIN REDISANYVALUE REF REFERENCES REFRESH REINDEX REJECT_P RELATIVE_P RELEASE RELOPTIONS REMOTE_P REMOVE RENAME REPEAT REPEATABLE REPLACE REPLICA - RESET RESIZE RESOURCE RESTART RESTRICT RETURN RETURNED_SQLSTATE RETURNING RETURNS REUSE REVOKE RIGHT ROLE ROLES ROLLBACK ROLLUP + RESET RESIZE RESOURCE RESTART RESTRICT RETURN RETURNED_SQLSTATE RETURNING RETURNS REUSE REVOKE RIGHT ROLE ROLES ROLLBACK ROLLUP ROTATE ROTATION ROW ROW_COUNT ROWNUM ROWS ROWTYPE_P RULE SAMPLE SAVEPOINT SCHEDULE SCHEMA SCHEMA_NAME SCROLL SEARCH SECOND_P SECURITY SELECT SEPARATOR_P SEQUENCE SEQUENCES @@ -1021,6 +1030,9 @@ static char* IdentResolveToChar(char *ident, core_yyscan_t yyscanner); %left POSTFIXOP /* dummy for postfix Op rules */ %nonassoc lower_than_index %nonassoc INDEX +%nonassoc ROTATE +%nonassoc higher_than_rotate +%left ',' /* * To support target_el without AS, we must give IDENT an explicit priority * between POSTFIXOP and Op. We can safely assign the same priority to @@ -23792,7 +23804,7 @@ select_clause: */ simple_select: SELECT hint_string opt_distinct target_list - opt_into_clause from_clause where_clause start_with_clause + opt_into_clause from_clause unrotate_clause where_clause start_with_clause group_clause having_clause window_clause { SelectStmt *n = makeNode(SelectStmt); @@ -23800,11 +23812,12 @@ simple_select: n->targetList = $4; n->intoClause = $5; n->fromClause = $6; - n->whereClause = $7; - n->startWithClause = $8; - n->groupClause = $9; - n->havingClause = $10; - n->windowClause = $11; + n->unrotateInfo = $7; + n->whereClause = $8; + n->startWithClause = $9; + n->groupClause = $10; + n->havingClause = $11; + n->windowClause = $12; n->hintState = create_hintstate($2); n->hasPlus = getOperatorPlusFlag(); $$ = (Node *)n; @@ -24925,6 +24938,36 @@ table_ref_for_no_table_function: relation_expr %prec UMINUS n->alias = $2; $$ = (Node *) n; } + | select_with_parens opt_alias_clause rotate_clause + { + RangeSubselect *n = makeNode(RangeSubselect); + n->lateral = false; + n->subquery = $1; + if ( $2 != NULL ) + n->alias = $2; + else + { + n->alias = makeNode(Alias); + n->alias->aliasname = pstrdup("rotate_as_internal_t"); + } + n->rotate = $3; + $$ = (Node *) n; + } + | relation_expr opt_alias_clause rotate_clause + { + RangeSubselect *n = makeNode(RangeSubselect); + n->lateral = false; + n->subquery = make_AStar_subquery($1); + if($2 != NULL) + n->alias = $2; + else + { + n->alias = makeNode(Alias); + n->alias->aliasname = pstrdup("rotate_as_internal_t"); + } + n->rotate = $3; + $$ = (Node *) n; + } | joined_table { $$ = (Node *) $1; @@ -25049,9 +25092,212 @@ alias_clause: ; opt_alias_clause: alias_clause { $$ = $1; } - | /*EMPTY*/ { $$ = NULL; } + | /*EMPTY*/ { $$ = NULL; } %prec higher_than_rotate ; +rotate_clause: + ROTATE '(' func_application_list rotate_for_clause rotate_in_clause ')' %prec ROTATE + { + if( u_sess->attr.attr_sql.sql_compatibility != A_FORMAT ) + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("rotate clause is supported only in A_FORMAT database."))); + RotateClause *n = makeNode(RotateClause); + n->aggregateFuncCallList = $3; + n->forColName = $4; + n->inExprList = $5; + $$ = n; + } + ; + +func_application_list: + func_application opt_alias_clause + { + if (NULL != $2) + ((FuncCall*)$1)->colname = ((Alias*)$2)->aliasname; + $$ = list_make1($1); + } + | func_application_list ',' func_application opt_alias_clause + { + if (NULL != $4) + ((FuncCall*)$3)->colname = ((Alias*)$4)->aliasname; + $$ = lappend ($1, $3); + } + ; +unrotate_clause: + NOT ROTATE include_exclude_null_clause '(' unrotate_name_list rotate_for_clause unrotate_in_clause ')' %prec ROTATE + { + if( u_sess->attr.attr_sql.sql_compatibility != A_FORMAT ) + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("not rotate clause is supported only in A_FORMAT database."))); + UnrotateClause *n = makeNode(UnrotateClause); + n->includeNull = $3; + n->colNameList = $5; + n->forColName = $6; + n->inExprList = $7; + $$ = n; + } + | { $$ = NULL; } + ; + +include_exclude_null_clause: + INCLUDE NULLS_P { $$ = true; } + | EXCLUDE NULLS_P { $$ = false; } + | /*EMPTY*/ { $$ = false; } + ; +unrotate_name_list: + ColId { $$ = list_make1(makeString($1)); } + | '(' name_list ')' { $$ = $2; } + ; +rotate_for_clause: + FOR ColId + { + $$ = list_make1(makeString($2)); + } + | FOR '(' name_list ')' + { + $$ = $3; + } + ; +rotate_in_clause: + IN_P '(' rotatein_list_single ')' + { + $$ = $3; + } + | IN_P '(' rotatein_list_multi ')' + { + $$ = $3; + } + ; +unrotate_in_clause: + IN_P '(' unrotatein_list_single ')' + { + $$ = $3; + } + | IN_P '(' unrotatein_list_multi ')' + { + $$ = $3; + } + ; +rotatein_list_single: + target_el + { + RotateInCell *incell = makeNode(RotateInCell); + List *l = list_make1($1); + incell->rotateInExpr = l; + incell->aliasname = NULL; + $$ = list_make1(incell); + } + | rotatein_list_single ',' target_el + { + RotateInCell *incell = makeNode(RotateInCell); + List *l = list_make1($3); + incell->rotateInExpr = l; + incell->aliasname = NULL; + $$ = lappend($1, incell); + } + ; +rotatein_list_multi: + '(' in_expr_list ')' opt_alias_clause + { + RotateInCell *incell = makeNode(RotateInCell); + incell->rotateInExpr = $2; + if (NULL != $4){ + incell->aliasname = ((Alias*)$4)->aliasname; + if (((Alias*)$4)->colnames) + ereport(errstate, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("multiple values cannot be specified in alias clause of ROTATE"), + parser_errposition(@4))); + } + $$ = list_make1(incell); + } + | rotatein_list_multi ',' '(' in_expr_list ')' opt_alias_clause + { + RotateInCell *incell = makeNode(RotateInCell); + incell->rotateInExpr = $4; + if (NULL != $6){ + incell->aliasname = ((Alias*)$6)->aliasname; + if (((Alias*)$6)->colnames) + ereport(errstate, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("multiple values cannot be specified in alias clause of ROTATE"), + parser_errposition(@6))); + } + $$ = lappend($1, incell); + } + ; +unrotatein_list_single: + target_el + { + UnrotateInCell *incell = makeNode(UnrotateInCell); + List *l = list_make1($1); + incell->unrotateInExpr = l; + incell->aliaList = NULL; + $$ = list_make1(incell); + } + | unrotatein_list_single ',' target_el + { + UnrotateInCell *incell = makeNode(UnrotateInCell); + List *l = list_make1($3); + incell->unrotateInExpr = l; + incell->aliaList = NULL; + $$ = lappend($1, incell); + } + ; +unrotatein_list_multi: + '(' in_expr_list ')' unrotatein_alias_clause + { + UnrotateInCell *incell = makeNode(UnrotateInCell); + incell->unrotateInExpr = $2; + incell->aliaList = $4; + $$ = list_make1(incell); + } + | unrotatein_list_multi ',' '(' in_expr_list ')' unrotatein_alias_clause + { + UnrotateInCell *incell = makeNode(UnrotateInCell); + incell->unrotateInExpr = $4; + incell->aliaList = $6; + $$ = lappend($1, incell); + } + ; +in_expr_list: + a_expr %prec ROTATE + { + ResTarget *rt = makeNode(ResTarget); + rt->name = NULL; + rt->indirection = NIL; + rt->val = (Node *)$1; + rt->location = -1; + $$ = list_make1(rt); + } + | a_expr ',' in_expr_list + { + ResTarget *rt = makeNode(ResTarget); + rt->name = NULL; + rt->indirection = NIL; + rt->val = (Node *)$1; + rt->location = -1; + $$ = lcons(rt, $3); + } + ; +unrotatein_alias_clause: + unrotatein_alias { $$ = $1; } + | /*EMPTY*/ { $$ = NULL; } + ; +unrotatein_alias: + AS AexprConst + { + $$ = list_make1($2); + } + | AS '(' expr_list ')' %prec ROTATE + { + $$ = $3; + } + ; + + join_type: FULL join_outer { $$ = JOIN_FULL; } | LEFT join_outer { $$ = JOIN_LEFT; } | RIGHT join_outer { $$ = JOIN_RIGHT; } @@ -27212,7 +27458,7 @@ c_expr_noparen: columnref { $$ = $1; } * (Note that many of the special SQL functions wouldn't actually make any * sense as functional index entries, but we ignore that consideration here.) */ -func_expr: func_application within_group_clause over_clause +func_expr: func_application within_group_clause filter_clause over_clause { FuncCall *n = (FuncCall *) $1; @@ -27263,7 +27509,7 @@ func_expr: func_application within_group_clause over_clause } n->agg_order = $2; - WindowDef *wd = (WindowDef*) $3; + WindowDef *wd = (WindowDef*) $4; if (wd != NULL) wd->frameOptions = FRAMEOPTION_NONDEFAULT | FRAMEOPTION_ROWS | FRAMEOPTION_START_UNBOUNDED_PRECEDING | @@ -27299,12 +27545,14 @@ func_expr: func_application within_group_clause over_clause } n->agg_order = $2; n->agg_within_group = TRUE; - n->over = $3; + n->agg_filter = $3; + n->over = $4; $$ = (Node *) n; } else { - n->over = $3; + n->agg_filter = $3; + n->over = $4; } if (pg_strcasecmp(strVal(linitial(n->funcname)), "group_concat") == 0) { @@ -27315,7 +27563,7 @@ func_expr: func_application within_group_clause over_clause (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("group_concat is not yet supported in distributed database."))); #endif - WindowDef *wd = (WindowDef*) $3; + WindowDef *wd = (WindowDef*) $4; if (wd != NULL) { ereport(errstate, (errcode(ERRCODE_SYNTAX_ERROR), @@ -28169,6 +28417,11 @@ within_group_clause: | /*EMPTY*/ { $$ = NIL; } ; +filter_clause: + FILTER '(' WHERE a_expr ')' { $$ = $4; } + | /*EMPTY*/ { $$ = NULL; } + ; + /* * Window Definitions */ @@ -28497,7 +28750,7 @@ subquery_Op: */ ; -expr_list: a_expr +expr_list: a_expr %prec higher_than_rotate { $$ = list_make1($1); } @@ -28914,6 +29167,14 @@ target_el: a_expr AS ColLabel $$->val = (Node *)$1; $$->location = @1; } + | a_expr AS Sconst + { + $$ = makeNode(ResTarget); + $$->name = $3; + $$->indirection = NIL; + $$->val = (Node *)$1; + $$->location = @1; + } /* * We support omitting AS only for column labels that aren't * any known keyword. There is an ambiguity against postfix @@ -29397,6 +29658,7 @@ ColLabel: IDENT { $$ = IdentResolveToChar($1, yyscanner); } + | '\''IDENT'\'' { $$ = $2; } | unreserved_keyword { $$ = pstrdup($1); } | col_name_keyword { $$ = pstrdup($1); } | type_func_name_keyword { $$ = pstrdup($1); } @@ -29847,6 +30109,7 @@ unreserved_keyword: | ROLES | ROLLBACK | ROLLUP + | ROTATE | ROTATION | ROW_COUNT | ROWS @@ -31401,6 +31664,21 @@ MakeAnonyBlockFuncStmt(int flag, const char *str) return (Node*)n; } +static Node *make_AStar_subquery(RangeVar *rangeVar) +{ + SelectStmt *n = makeNode(SelectStmt); + ColumnRef *col = makeNode (ColumnRef); + col->fields = list_make1(makeNode(A_Star)); + col->indnum = 0; + ResTarget *res = makeNode(ResTarget); + res->name = NULL; + res->indirection = NIL; + res->val = (Node *)col; + n->targetList = list_make1(res); + n->fromClause = list_make1(rangeVar); + return (Node*)n; +} + // get arg info with arg position or arg name static void get_arg_mode_by_name(const char *argname, const char * const *argnames, diff --git a/src/common/backend/parser/parse_clause.cpp b/src/common/backend/parser/parse_clause.cpp index 0a172e9a0..800402a2f 100644 --- a/src/common/backend/parser/parse_clause.cpp +++ b/src/common/backend/parser/parse_clause.cpp @@ -89,6 +89,7 @@ static Node* transformGroupingSet(List** flatresult, ParseState* pstate, Groupin static Index transformGroupClauseExpr(List** flatresult, Bitmapset* seen_local, ParseState* pstate, Node* gexpr, List** targetlist, List* sortClause, ParseExprKind exprKind, bool useSQL99, bool toplevel); static void CheckOrderbyColumns(ParseState* pstate, List* targetList, bool isAggregate); +static bool ColNameInFuncParasList(char *colName, List *funcParaList); /* * @Description: append from clause item to the left tree @@ -607,7 +608,7 @@ static RangeTblEntry* transformRangeSubselect(ParseState* pstate, RangeSubselect * unlabeled subselect. (This is just elog, not ereport, because the * grammar should have enforced it already.) */ - if (r->alias == NULL) { + if (r->alias == NULL && r->rotate == NULL) { ereport(ERROR, (errcode(ERRCODE_UNEXPECTED_NULL_VALUE), errmsg("subquery in FROM must have an alias"))); } /* @@ -618,10 +619,163 @@ static RangeTblEntry* transformRangeSubselect(ParseState* pstate, RangeSubselect Assert(!pstate->p_lateral_active); pstate->p_lateral_active = r->lateral; + if (r->rotate) { + ListCell *exprCell, *targetCell, *aggCell, *prev, *next, *colCell, *inexprCell, *filtercell; + SelectStmt *subQueryStmt; + ColumnRef *cref; + ResTarget *resT; + List *exprlist, *filterlist; + subQueryStmt = (SelectStmt *)r->subquery; + prev = NULL; + if (1 == list_length(subQueryStmt->targetList)) { + List *wholeTarget = NIL; + ParseState *pstate1 = make_parsestate(NULL); + pstate1->p_sourcetext = pstate->p_sourcetext; + + transformFromClause(pstate1, subQueryStmt->fromClause); + ResTarget *resTarget = (ResTarget *)lfirst(list_head(subQueryStmt->targetList)); + if (IsA(resTarget->val, ColumnRef)) { + Node *field = (Node *)lfirst(list_head(((ColumnRef *)resTarget->val)->fields)); + if (IsA(field, A_Star)) { + wholeTarget = transformTargetList(pstate1, subQueryStmt->targetList, EXPR_KIND_SELECT_TARGET); + subQueryStmt->targetList = + list_delete_cell(subQueryStmt->targetList, list_head(subQueryStmt->targetList), prev); + } + } + free_parsestate(pstate1); + + foreach (targetCell, wholeTarget) { + TargetEntry *te = (TargetEntry *)lfirst(targetCell); + ColumnRef *col = makeNode(ColumnRef); + col->fields = list_make1(makeString(te->resname)); + col->indnum = 0; + ResTarget *res = makeNode(ResTarget); + res->name = NULL; + res->indirection = NIL; + res->val = (Node *)col; + subQueryStmt->targetList = lappend(subQueryStmt->targetList, res); + } + } + + /* remove target */ + for (targetCell = list_head(subQueryStmt->targetList); targetCell; targetCell = next) { + ResTarget *resTarget = (ResTarget *)lfirst(targetCell); + next = lnext(targetCell); + if (IsA(resTarget->val, ColumnRef)) { + char *colName = strVal(lfirst(list_head(((ColumnRef *)resTarget->val)->fields))); + if (list_member(r->rotate->forColName, lfirst(list_head(((ColumnRef *)resTarget->val)->fields))) || + ColNameInFuncParasList(colName, r->rotate->aggregateFuncCallList)) + subQueryStmt->targetList = list_delete_cell(subQueryStmt->targetList, targetCell, prev); + else + prev = targetCell; + } else + prev = targetCell; + } + + /* add group by to query */ + foreach (targetCell, subQueryStmt->targetList) { + ResTarget *resTarget = (ResTarget *)lfirst(targetCell); + if (IsA(resTarget->val, ColumnRef)) { + cref = (ColumnRef *)copyObject(resTarget->val); + if (subQueryStmt->groupClause == NIL) + subQueryStmt->groupClause = list_make1(cref); + else + subQueryStmt->groupClause = lappend(subQueryStmt->groupClause, cref); + } + } + + /* add target */ + foreach (inexprCell, r->rotate->inExprList) { + RotateInCell *rotateinCell = (RotateInCell *)lfirst(inexprCell); + foreach (aggCell, r->rotate->aggregateFuncCallList) { + ResTarget *resTarget = NULL; + FuncCall *aggregateFuncCall = (FuncCall *)lfirst(aggCell); + FuncCall *funcCall; + funcCall = (FuncCall *)copyObject(aggregateFuncCall); + filterlist = NIL; + bool check_multivalue = false; + errno_t rc; + char new_col_name[NAMEDATALEN]; + rc = memset_s(new_col_name, NAMEDATALEN, 0, NAMEDATALEN); + securec_check_c(rc, "\0", "\0"); + if (NULL != rotateinCell->aliasname) { + rc = strncat_s(new_col_name, NAMEDATALEN, rotateinCell->aliasname, strlen(rotateinCell->aliasname)); + securec_check_c(rc, "\0", "\0"); + } + + if (list_length(r->rotate->forColName) != list_length(rotateinCell->rotateInExpr)) { + ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("the number of elements in ROTATE IN CLAUSE doesn't agree with the number of columns specified in ROTATE FOR CLAUSE"))); + } + forboth(colCell, r->rotate->forColName, exprCell, rotateinCell->rotateInExpr) { + resTarget = (ResTarget *)lfirst(exprCell); + char *colName = strVal(lfirst(colCell)); + cref = makeNode(ColumnRef); + cref->fields = list_make1(makeString(colName)); + cref->location = -1; + filterlist = lappend(filterlist, (Node *)makeSimpleA_Expr(AEXPR_OP, "=", (Node *)cref, resTarget->val, -1)); + if (NULL == rotateinCell->aliasname) { + if (check_multivalue) { + rc = strncat_s(new_col_name, NAMEDATALEN, "_", 1); + securec_check_c(rc, "\0", "\0"); + } + if (NULL != resTarget->name) { + rc = strncat_s(new_col_name, NAMEDATALEN, resTarget->name, strlen(resTarget->name)); + securec_check_c(rc, "\0", "\0"); + } else if (IsA(resTarget->val, A_Const)) { + Value *val = &((A_Const *)resTarget->val)->val; + if (val->type == T_String){ + rc = strncat_s(new_col_name, NAMEDATALEN, strVal(val), strlen(strVal(val))); + securec_check_c(rc, "\0", "\0"); + } else if (val->type == T_Float) { + rc = snprintf_s(new_col_name + strlen(new_col_name), NAMEDATALEN - strlen(new_col_name), + NAMEDATALEN - strlen(new_col_name) - 1, "%f", floatVal(val)); + securec_check_ss(rc, "\0", "\0"); + } else { + rc = snprintf_s(new_col_name + strlen(new_col_name), NAMEDATALEN - strlen(new_col_name), + NAMEDATALEN - strlen(new_col_name) - 1, "%ld", intVal(val)); + securec_check_ss(rc, "\0", "\0"); + } + } + else{ + ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR), +errmsg("ROTATE in clause error"), + parser_errposition(pstate, exprLocation((Node *)resTarget->val)))); + } + } + check_multivalue = true; + } + if (NULL != aggregateFuncCall->colname) { + rc = strncat_s(new_col_name, NAMEDATALEN, "_", 1); + securec_check_c(rc, "\0", "\0"); + rc = strncat_s(new_col_name, NAMEDATALEN, aggregateFuncCall->colname, + strlen(aggregateFuncCall->colname)); + securec_check_c(rc, "\0", "\0"); + } + Node* and_expr = NULL; + foreach (filtercell, filterlist) { + Node *filter = (Node*)lfirst(filtercell); + if (and_expr == NULL) + and_expr = filter; + else + and_expr = (Node*)makeA_Expr(AEXPR_AND, NIL, and_expr, filter, -1); + } + funcCall->agg_filter = and_expr; + resT = makeNode(ResTarget); + resT->name = pstrdup(new_col_name); + resT->indirection = NIL; + resT->val = (Node *)funcCall; + resT->location = 1; + + subQueryStmt->targetList = lappend(subQueryStmt->targetList, resT); + } + } + } + /* * Analyze and transform the subquery. */ - query = parse_sub_analyze(r->subquery, pstate, NULL, isLockedRefname(pstate, r->alias->aliasname), true); + query = parse_sub_analyze(r->subquery, pstate, NULL, isLockedRefname(pstate, r->alias ? r->alias->aliasname : NULL), + true); pstate->p_lateral_active = false; @@ -642,6 +796,21 @@ static RangeTblEntry* transformRangeSubselect(ParseState* pstate, RangeSubselect return rte; } +static bool ColNameInFuncParasList(char *colName, List *funcParaList) +{ + ListCell *cell; + ListCell *paracell; + foreach (cell, funcParaList) { + FuncCall *aggregateFuncCall = (FuncCall *)lfirst(cell); + foreach (paracell, aggregateFuncCall->args) { + ColumnRef *cr = (ColumnRef *)lfirst(paracell); + if (strcmp(colName, strVal(lfirst(list_head(cr->fields)))) == 0) + return true; + } + } + return false; +} + /* * transformRangeFunction --- transform a function call appearing in FROM */ @@ -2995,4 +3164,4 @@ void pretransformAggWithUserSet(ParseState* pstate, List** targetList, Node* gro pstate->p_target_list = *targetList; return; -} \ No newline at end of file +} diff --git a/src/common/backend/parser/parse_collate.cpp b/src/common/backend/parser/parse_collate.cpp index 306a30d33..25197aa4c 100644 --- a/src/common/backend/parser/parse_collate.cpp +++ b/src/common/backend/parser/parse_collate.cpp @@ -672,6 +672,7 @@ static bool assign_collations_walker(Node* node, assign_collations_context* cont /* support ordered set agg at 91269 kernel version */ assign_aggregate_collations((Aggref*)node, &loccontext); } + assign_expr_collations(context->pstate, (Node *)((Aggref *)node)->aggfilter); } break; case T_CaseExpr: { /* diff --git a/src/common/backend/parser/parse_expr.cpp b/src/common/backend/parser/parse_expr.cpp index b79387639..681cf44c5 100644 --- a/src/common/backend/parser/parse_expr.cpp +++ b/src/common/backend/parser/parse_expr.cpp @@ -1312,6 +1312,7 @@ static Node* tryTransformFunc(ParseState* pstate, List* fields, int location) fn->funcname = fields; fn->args = NIL; fn->agg_order = NIL; + fn->agg_filter = NULL; fn->agg_star = FALSE; fn->agg_distinct = FALSE; fn->func_variadic = FALSE; @@ -2894,6 +2895,7 @@ static Node* transformPredictByFunction(ParseState* pstate, PredictByFunction* p } n->agg_order = NULL; + n->agg_filter = NULL; n->agg_star = FALSE; n->agg_distinct = FALSE; n->func_variadic = FALSE; diff --git a/src/common/backend/parser/parse_func.cpp b/src/common/backend/parser/parse_func.cpp index ce5c6b00d..dbb514d09 100644 --- a/src/common/backend/parser/parse_func.cpp +++ b/src/common/backend/parser/parse_func.cpp @@ -71,6 +71,7 @@ Node* ParseFuncOrColumn(ParseState* pstate, List* funcname, List* fargs, Node* l { bool is_column = (fn == NULL); List* agg_order = (fn ? fn->agg_order : NIL); + Expr *agg_filter = NULL; bool agg_within_group = (fn ? fn->agg_within_group : false); bool agg_star = (fn ? fn->agg_star : false); bool agg_distinct = (fn ? fn->agg_distinct : false); @@ -96,6 +97,11 @@ Node* ParseFuncOrColumn(ParseState* pstate, List* funcname, List* fargs, Node* l char aggkind = 'n'; char* name_string = NULL; Oid refSynOid = InvalidOid; + /* + * If there's an aggregate filter, transform it using transformWhereClause + */ + if (fn && fn->agg_filter != NULL) + agg_filter = (Expr *)transformWhereClause(pstate, fn->agg_filter, EXPR_KIND_FILTER, "FILTER"); /* * Most of the rest of the parser just assumes that functions do not have * more than FUNC_MAX_ARGS parameters. We have to test here to protect @@ -180,6 +186,7 @@ Node* ParseFuncOrColumn(ParseState* pstate, List* funcname, List* fargs, Node* l * wasn't any aggregate or variadic decoration, nor an argument name. */ if (nargs == 1 && agg_order == NIL && !agg_star && !agg_distinct && over == NULL && !func_variadic && + agg_filter == NULL && argnames == NIL && list_length(funcname) == 1) { Oid argtype = actual_arg_types[0]; @@ -266,6 +273,10 @@ Node* ParseFuncOrColumn(ParseState* pstate, List* funcname, List* fargs, Node* l (errcode(ERRCODE_WRONG_OBJECT_TYPE), errmsg("ORDER BY specified, but %s is not an aggregate function", name_string), parser_errposition(pstate, location))); + if (agg_filter) + ereport(ERROR, (errcode(ERRCODE_WRONG_OBJECT_TYPE), + errmsg("FILTER specified, but %s is not an aggregate function", NameListToString(funcname)), + parser_errposition(pstate, location))); if (over != NULL) ereport(ERROR, (errcode(ERRCODE_WRONG_OBJECT_TYPE), @@ -530,6 +541,7 @@ Node* ParseFuncOrColumn(ParseState* pstate, List* funcname, List* fargs, Node* l aggref->aggtype = rettype; /* aggcollid and inputcollid will be set by parse_collate.c */ /* args, aggorder, aggdistinct will be set by transformAggregateCall */ + aggref->aggfilter = agg_filter; aggref->aggstar = agg_star; aggref->aggvariadic = func_variadic; aggref->aggkind = aggkind; diff --git a/src/common/backend/parser/parse_relation.cpp b/src/common/backend/parser/parse_relation.cpp index 7df833604..98d9678d2 100755 --- a/src/common/backend/parser/parse_relation.cpp +++ b/src/common/backend/parser/parse_relation.cpp @@ -567,7 +567,9 @@ Node* scanRTEForColumn(ParseState* pstate, RangeTblEntry* rte, char* colname, in int attnum = 0; Var* var = NULL; ListCell* c = NULL; - + if (rte->eref == NULL) + return result; + /* * Scan the user column names (or aliases) for a match. Complain if * multiple matches. @@ -1658,16 +1660,19 @@ RangeTblEntry* addRangeTableEntryForSubquery( query_alias = makeAlias(block_name, NIL); } - refname = query_alias->aliasname; + refname = query_alias ? query_alias->aliasname : (char *)"unknown"; rte->rtekind = RTE_SUBQUERY; rte->relid = InvalidOid; rte->subquery = subquery; rte->alias = query_alias; rte->sublink_pull_up = sublinkPullUp; rte->orientation = REL_ORIENT_UNKNOWN; - eref = (Alias*)copyObject(query_alias); - numaliases = list_length(eref->colnames); - + if (query_alias != NULL) { + eref = (Alias *)copyObject(query_alias); + numaliases = list_length(eref->colnames); + } else { + eref = NULL; + } /* fill in any unspecified alias columns */ varattno = 0; foreach (tlistitem, subquery->targetList) { @@ -1678,7 +1683,7 @@ RangeTblEntry* addRangeTableEntryForSubquery( } varattno++; AssertEreport(varattno == te->resno, MOD_OPT, ""); - if (varattno > numaliases) { + if (varattno > numaliases && alias != NULL) { char* attrname = NULL; if (te->resname != NULL && @@ -2229,6 +2234,8 @@ void expandRTE(RangeTblEntry* rte, int rtindex, int sublevels_up, int location, expandRelation(rte->relid, rte->eref, rtindex, sublevels_up, location, include_dropped, colnames, colvars); break; case RTE_SUBQUERY: { + if (rte->eref == NULL) + break; /* Subquery RTE */ /* for start with pseudo table, we mark its targetlist resjunk */ if (rte->alias != NULL && rte->alias->aliasname != NULL && diff --git a/src/common/backend/parser/parse_utilcmd.cpp b/src/common/backend/parser/parse_utilcmd.cpp index 2c99d1948..e30d305e1 100644 --- a/src/common/backend/parser/parse_utilcmd.cpp +++ b/src/common/backend/parser/parse_utilcmd.cpp @@ -1139,6 +1139,7 @@ static void createSeqOwnedByTable(CreateStmtContext* cxt, ColumnDef* column, boo funccallnode->funcname = SystemFuncName("nextval"); funccallnode->args = list_make1(castnode); funccallnode->agg_order = NIL; + funccallnode->agg_filter = NULL; funccallnode->agg_star = false; funccallnode->agg_distinct = false; funccallnode->func_variadic = false; diff --git a/src/common/backend/utils/adt/ruleutils.cpp b/src/common/backend/utils/adt/ruleutils.cpp index a971423e1..a8b0d9e91 100644 --- a/src/common/backend/utils/adt/ruleutils.cpp +++ b/src/common/backend/utils/adt/ruleutils.cpp @@ -10883,6 +10883,10 @@ static void get_agg_expr(Aggref* aggref, deparse_context* context) get_rule_orderby(aggref->aggorder, aggref->args, false, context); } } + if (aggref->aggfilter != NULL) { + appendStringInfoString(buf, ") FILTER (WHERE "); + get_rule_expr((Node *)aggref->aggfilter, context, false); + } if (isGroupCatAggFunc) { appendStringInfoString(buf, " SEPARATOR "); diff --git a/src/common/backend/utils/init/globals.cpp b/src/common/backend/utils/init/globals.cpp index bd8bfdd1b..8e42331b0 100644 --- a/src/common/backend/utils/init/globals.cpp +++ b/src/common/backend/utils/init/globals.cpp @@ -75,12 +75,13 @@ bool will_shutdown = false; * NEXT | 92899 | ? | ? * ********************************************/ -const uint32 GRAND_VERSION_NUM = 92936; +const uint32 GRAND_VERSION_NUM = 92937; /******************************************** * 2.VERSION NUM FOR EACH FEATURE * Please write indescending order. ********************************************/ +const uint32 ROTATE_UNROTATE_VERSION_NUM = 92937; const uint32 PIPELINED_FUNCTION_VERSION_NUM = 92936; const uint32 DISABLE_CONSTRAINT_VERSION_NUM = 92931; const uint32 ADD_CLEAN_CASCADE_STANDBY_SLOT_MESSAGE_NUM = 92930; diff --git a/src/common/interfaces/libpq/frontend_parser/gram.y b/src/common/interfaces/libpq/frontend_parser/gram.y index 9fa2ea236..523899fe0 100755 --- a/src/common/interfaces/libpq/frontend_parser/gram.y +++ b/src/common/interfaces/libpq/frontend_parser/gram.y @@ -580,7 +580,7 @@ extern THR_LOCAL bool stmt_contains_operator_plus; RANDOMIZED RANGE RATIO RAW READ REAL REASSIGN REBUILD RECHECK RECURSIVE RECYCLEBIN REDISANYVALUE REF REFERENCES REFRESH REINDEX REJECT_P RELATIVE_P RELEASE RELOPTIONS REMOTE_P REMOVE RENAME REPEAT REPEATABLE REPLACE REPLICA - RESET RESIZE RESOURCE RESTART RESTRICT RETURN RETURNED_SQLSTATE RETURNING RETURNS REUSE REVOKE RIGHT ROLE ROLES ROLLBACK ROLLUP + RESET RESIZE RESOURCE RESTART RESTRICT RETURN RETURNED_SQLSTATE RETURNING RETURNS REUSE REVOKE RIGHT ROLE ROLES ROLLBACK ROLLUP ROTATE ROTATION ROW ROW_COUNT ROWNUM ROWS ROWTYPE_P RULE SAMPLE SAVEPOINT SCHEDULE SCHEMA SCHEMA_NAME SCROLL SEARCH SECOND_P SECURITY SELECT SEPARATOR_P SEQUENCE SEQUENCES @@ -11947,6 +11947,7 @@ unreserved_keyword: | ROLES | ROLLBACK | ROLLUP + | ROTATE | ROTATION | ROWS | RULE diff --git a/src/gausskernel/cbb/instruments/utils/unique_query.cpp b/src/gausskernel/cbb/instruments/utils/unique_query.cpp index d2cd9ffb6..7c0ad6997 100755 --- a/src/gausskernel/cbb/instruments/utils/unique_query.cpp +++ b/src/gausskernel/cbb/instruments/utils/unique_query.cpp @@ -471,6 +471,7 @@ void UniqueSql::JumbleExpr(pgssJumbleState* jstate, Node* node) UniqueSql::JumbleExpr(jstate, (Node*)expr->args); UniqueSql::JumbleExpr(jstate, (Node*)expr->aggorder); UniqueSql::JumbleExpr(jstate, (Node*)expr->aggdistinct); + UniqueSql::JumbleExpr(jstate, (Node*)expr->aggfilter); break; } diff --git a/src/gausskernel/optimizer/plan/planagg.cpp b/src/gausskernel/optimizer/plan/planagg.cpp index f2352c332..47fb4bff3 100644 --- a/src/gausskernel/optimizer/plan/planagg.cpp +++ b/src/gausskernel/optimizer/plan/planagg.cpp @@ -342,6 +342,8 @@ static bool find_minmax_aggs_walker(Node* node, List** context) "when scaning the Aggref nodes in an expression tree recursively to find a MIN/MAX aggregate."); if (list_length(aggref->args) != 1 || aggref->aggorder != NIL) return true; /* it couldn't be MIN/MAX */ + if (aggref->aggfilter != NULL) + return true; /* note: we do not care if DISTINCT is mentioned ... */ curTarget = (TargetEntry*)linitial(aggref->args); diff --git a/src/gausskernel/optimizer/plan/subselect.cpp b/src/gausskernel/optimizer/plan/subselect.cpp index 67ea14296..8df04c225 100644 --- a/src/gausskernel/optimizer/plan/subselect.cpp +++ b/src/gausskernel/optimizer/plan/subselect.cpp @@ -2987,6 +2987,7 @@ static bool finalize_agg_primnode(Node* node, finalize_primnode_context* context /* we should not consider the direct arguments, if any */ (void)finalize_primnode((Node*)agg->args, context); + (void)finalize_primnode((Node*)agg->aggfilter, context); return false; /* there can't be any Aggrefs below here */ } diff --git a/src/gausskernel/runtime/executor/execQual.cpp b/src/gausskernel/runtime/executor/execQual.cpp index 2879c5ba9..69e631247 100644 --- a/src/gausskernel/runtime/executor/execQual.cpp +++ b/src/gausskernel/runtime/executor/execQual.cpp @@ -5846,6 +5846,7 @@ ExprState* ExecInitExprByRecursion(Expr* node, PlanState* parent) astate->aggdirectargs = (List*)ExecInitExprByRecursion((Expr*)aggref->aggdirectargs, parent); astate->args = (List*)ExecInitExprByRecursion((Expr*)aggref->args, parent); + astate->aggfilter = ExecInitExprByRecursion((Expr *)aggref->aggfilter, parent); /* * Complain if the aggregate's arguments contain any diff --git a/src/gausskernel/runtime/executor/nodeAgg.cpp b/src/gausskernel/runtime/executor/nodeAgg.cpp index 5d2f0d8f2..cc540f10f 100644 --- a/src/gausskernel/runtime/executor/nodeAgg.cpp +++ b/src/gausskernel/runtime/executor/nodeAgg.cpp @@ -661,6 +661,13 @@ static void advance_aggregates(AggState* aggstate, AggStatePerGroup pergroup) int i; int inputoff = peraggstate->inputoff; + if (peraggstate->aggrefstate->aggfilter) { + bool eisnull = false; + Datum exprValue = + ExecEvalExpr(peraggstate->aggrefstate->aggfilter, aggstate->evalproj->pi_exprContext, &eisnull, NULL); + if (eisnull || !DatumGetBool(exprValue)) + continue; + } if (peraggstate->numSortCols > 0) { /* DISTINCT and/or ORDER BY case */ Assert(slot->tts_nvalid >= (peraggstate->numInputs + inputoff)); diff --git a/src/gausskernel/runtime/vecexecutor/vecexpression.cpp b/src/gausskernel/runtime/vecexecutor/vecexpression.cpp index 624bf272c..f069e1824 100644 --- a/src/gausskernel/runtime/vecexecutor/vecexpression.cpp +++ b/src/gausskernel/runtime/vecexecutor/vecexpression.cpp @@ -2495,6 +2495,7 @@ ExprState* ExecInitVecExpr(Expr* node, PlanState* parent) naggs = ++aggstate->numaggs; astate->args = (List*)ExecInitVecExpr((Expr*)aggref->args, parent); + astate->aggfilter = ExecInitVecExpr((Expr *)aggref->aggfilter, parent); /* * Complain if the aggregate's arguments contain any diff --git a/src/gausskernel/runtime/vecexecutor/vecnode/vecagg.cpp b/src/gausskernel/runtime/vecexecutor/vecnode/vecagg.cpp index 0caaac1da..7d2a9973a 100644 --- a/src/gausskernel/runtime/vecexecutor/vecnode/vecagg.cpp +++ b/src/gausskernel/runtime/vecexecutor/vecnode/vecagg.cpp @@ -1214,6 +1214,23 @@ void BaseAggRunner::BatchAggregation(VectorBatch* batch) p_vector->m_rows = Min(p_vector->m_rows, nrows); + if (per_agg_state->aggrefstate->aggfilter) { + ScalarVector vector; + bool selection = false; + int j = 0; + vector.init(CurrentMemoryContext, p_vector->m_desc); + vector.copy(p_vector); + ScalarVector *exprValue = VectorExprEngine(per_agg_state->aggrefstate->aggfilter, per_agg_state->evalproj->pi_exprContext, + &selection, &vector, NULL); + if (selection || exprValue == NULL) + continue; + for (j = 0; j < vector.m_rows; j++) { + if (vector.m_vals[j]) { + vector.m_vals[j] = p_vector->m_vals[j]; + } + } + AggregationOnScalar(&m_runtime->aggInfo[i], &vector, m_aggIdx[i], &m_Loc[0]); + } else AggregationOnScalar(&m_runtime->aggInfo[i], p_vector, m_aggIdx[i], &m_Loc[0]); if (econtext != NULL) diff --git a/src/include/executor/node/nodeAgg.h b/src/include/executor/node/nodeAgg.h index 3ee84d772..b38e927bd 100644 --- a/src/include/executor/node/nodeAgg.h +++ b/src/include/executor/node/nodeAgg.h @@ -193,6 +193,7 @@ typedef struct AggStatePerAggData { /* XXX: use for vector engine now, better remove later*/ TupleDesc evaldesc; /* descriptor of input tuples */ ProjectionInfo *evalproj; /* projection machinery */ + ProjectionInfo *combinedproj; /* projection machinery */ TupleTableSlot *evalslot; /* current input tuple */ } AggStatePerAggData; diff --git a/src/include/miscadmin.h b/src/include/miscadmin.h index ad90da058..228fda2d1 100644 --- a/src/include/miscadmin.h +++ b/src/include/miscadmin.h @@ -145,6 +145,7 @@ extern const uint32 SELECT_STMT_HAS_USERVAR; extern const uint32 PUBLICATION_DDL_VERSION_NUM; extern const uint32 PRIOR_EXPR_VERSION_NUM; extern const uint32 CURSOR_EXPRESSION_VERSION_NUMBER; +extern const uint32 ROTATE_UNROTATE_VERSION_NUM; extern void register_backend_version(uint32 backend_version); diff --git a/src/include/nodes/nodes.h b/src/include/nodes/nodes.h index 6800a2097..a29b9c0e8 100755 --- a/src/include/nodes/nodes.h +++ b/src/include/nodes/nodes.h @@ -585,6 +585,8 @@ typedef enum NodeTag { T_CursorExpression, /* * TAGS FOR PARSE TREE NODES (parsenodes.h) + * note: TAGS FOR PARSE TREE NODES (parsenodes.h) can no longer place new tags, + * please go to the location marked 6000 to continue to place new tags. */ T_A_Expr = 900, T_ColumnRef, @@ -897,7 +899,14 @@ typedef enum NodeTag { T_MinMaxAggPath, T_GatherPath, T_ForeignKeyCacheInfo, - T_Gather + T_Gather, + /* + * TAGS FOR PARSE TREE NODES (parsenodes.h), the area above where such information is placed is full. + */ + T_RotateClause = 6000, + T_UnrotateClause, + T_RotateInCell, + T_UnrotateInCell } NodeTag; diff --git a/src/include/nodes/parsenodes_common.h b/src/include/nodes/parsenodes_common.h index bee418eb2..fd13f75aa 100644 --- a/src/include/nodes/parsenodes_common.h +++ b/src/include/nodes/parsenodes_common.h @@ -675,6 +675,20 @@ typedef struct StartWithClause { bool opt; } StartWithClause; +typedef struct UnrotateClause { + NodeTag type; + bool includeNull; + List *colNameList; + List *forColName; + List *inExprList; +} UnrotateClause; + +typedef struct UnrotateInCell { + NodeTag type; + List *aliaList; + List *unrotateInExpr; +} UnrotateInCell; + typedef struct SelectStmt { NodeTag type; @@ -686,6 +700,7 @@ typedef struct SelectStmt { IntoClause *intoClause; /* target for SELECT INTO */ List *targetList; /* the target list (of ResTarget) */ List *fromClause; /* the FROM clause */ + UnrotateClause *unrotateInfo; Node *startWithClause; /* START WITH...CONNECT BY clause */ Node *whereClause; /* WHERE qualification */ List *groupClause; /* GROUP BY clauses */ @@ -1684,6 +1699,7 @@ typedef struct FuncCall { char *colname; /* column name for the function */ List *args; /* the arguments (list of exprs) */ List *agg_order; /* ORDER BY (list of SortBy) */ + Node *agg_filter; bool agg_within_group; bool agg_star; /* argument was really '*' */ bool agg_distinct; /* arguments were labeled DISTINCT */ @@ -1814,6 +1830,19 @@ typedef struct RangeFunction { * of function returning RECORD */ } RangeFunction; +typedef struct RotateClause { + NodeTag type; + List *forColName; + List *inExprList; + List *aggregateFuncCallList; +} RotateClause; + +typedef struct RotateInCell { + NodeTag type; + char *aliasname; + List *rotateInExpr; +} RotateInCell; + /* * RangeSubselect - subquery appearing in a FROM clause */ @@ -1822,6 +1851,7 @@ typedef struct RangeSubselect { bool lateral; /* does it have LATERAL prefix? */ Node *subquery; /* the untransformed sub-select clause */ Alias *alias; /* table alias & optional column aliases */ + RotateClause *rotate; } RangeSubselect; /* diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h index 99f3d4ed7..45e67f5d2 100644 --- a/src/include/nodes/primnodes.h +++ b/src/include/nodes/primnodes.h @@ -304,6 +304,7 @@ typedef struct Aggref { List* aggargtypes; /* type Oids of direct and aggregated args */ int aggsplit; /* expected agg-splitting mode of parent Agg */ Oid aggtranstype; /* type Oid of aggregate's transition value */ + Expr *aggfilter; /* FILTER expression, if any */ } Aggref; /* diff --git a/src/include/parser/kwlist.h b/src/include/parser/kwlist.h index 36e06ed9f..23082f704 100644 --- a/src/include/parser/kwlist.h +++ b/src/include/parser/kwlist.h @@ -545,6 +545,7 @@ PG_KEYWORD("role", ROLE, UNRESERVED_KEYWORD) PG_KEYWORD("roles", ROLES, UNRESERVED_KEYWORD) PG_KEYWORD("rollback", ROLLBACK, UNRESERVED_KEYWORD) PG_KEYWORD("rollup", ROLLUP, UNRESERVED_KEYWORD) +PG_KEYWORD("rotate", ROTATE, UNRESERVED_KEYWORD) PG_KEYWORD("rotation", ROTATION, UNRESERVED_KEYWORD) PG_KEYWORD("row", ROW, COL_NAME_KEYWORD) PG_KEYWORD("row_count", ROW_COUNT, UNRESERVED_KEYWORD) diff --git a/src/test/regress/expected/gb_ora_rotate_unrotate.out b/src/test/regress/expected/gb_ora_rotate_unrotate.out new file mode 100644 index 000000000..1864323e5 --- /dev/null +++ b/src/test/regress/expected/gb_ora_rotate_unrotate.out @@ -0,0 +1,512 @@ +-- +-- Test rotate and not rotate grammer +-- +-- Create +create table original_orders (id int, year int, order_mode text, order_total int); +insert into original_orders values (1,2020,'direct',5000), (2,2020,'online',1000), (3,2021,'online',1000), (4,2021,'direct',1000), (5,2022,'direct',5000), (6,2020,'direct',500); +select * from ( select year, order_mode, order_total from original_orders) rotate (sum(order_total) for order_mode in ('direct' as store, 'online' as internet)) order by year; + year | store | internet +------+-------+---------- + 2020 | 5500 | 1000 + 2021 | 1000 | 1000 + 2022 | 5000 | +(3 rows) + +select * from (select year, order_mode, order_total from original_orders) rotate (sum(order_total) for order_mode in ('online' as internet )) order by year; + year | internet +------+---------- + 2020 | 1000 + 2021 | 1000 + 2022 | +(3 rows) + +create table rotate_orders as (select * from (select year, order_mode, order_total from original_orders) as t rotate (sum(order_total) for order_mode in ('direct' as store, 'online' as internet)) order by year); +-- test not rotate (column transform to row) +select * from rotate_orders not rotate ( yearly_total for order_mode in ( store as 'direct', internet as 'online')); + year | order_mode | yearly_total +------+------------+-------------- + 2020 | direct | 5500 + 2021 | direct | 1000 + 2022 | direct | 5000 + 2020 | online | 1000 + 2021 | online | 1000 +(5 rows) + +select * from rotate_orders not rotate exclude nulls (yearly_total for order_mode in ( store as 'direct', internet as 'online')); + year | order_mode | yearly_total +------+------------+-------------- + 2020 | direct | 5500 + 2021 | direct | 1000 + 2022 | direct | 5000 + 2020 | online | 1000 + 2021 | online | 1000 +(5 rows) + +select * from rotate_orders not rotate include nulls ( yearly_total for ordre_mode in ( store as 'direct', internet as 'online')); + year | ordre_mode | yearly_total +------+------------+-------------- + 2020 | direct | 5500 + 2021 | direct | 1000 + 2022 | direct | 5000 + 2020 | online | 1000 + 2021 | online | 1000 + 2022 | online | +(6 rows) + +-- rotate and not rotate are in the same sql +select * from (select year, direct, online from (select year, order_mode, order_total from original_orders) rotate (sum(order_total) for order_mode in ('direct', 'online')) order by year) as rotate_t not rotate ( yearly_total for order_mode in (direct, online)); + year | order_mode | yearly_total +------+------------+-------------- + 2022 | direct | 5000 + 2021 | direct | 1000 + 2020 | direct | 5500 + 2021 | online | 1000 + 2020 | online | 1000 +(5 rows) + +select * from (select year, order_mode, yearly_total from (select * from rotate_orders not rotate ( yearly_total for order_mode in ( store as 'direct', internet as 'online')))) rotate (sum(yearly_total) for order_mode in ('direct' as store, 'online' as internet) ) order by year; + year | store | internet +------+-------+---------- + 2020 | 5500 | 1000 + 2021 | 1000 | 1000 + 2022 | 5000 | +(3 rows) + +-- create view +create view rotate_view as (select * from (select year, order_mode, order_total from original_orders) as t rotate (sum(order_total) for order_mode in ('direct' as store, 'online' as internet)) order by year); +select * from rotate_view; + year | store | internet +------+-------+---------- + 2020 | 5500 | 1000 + 2021 | 1000 | 1000 + 2022 | 5000 | +(3 rows) + +create view notrotate_view as (select * from rotate_orders not rotate ( yearly_total for order_mode in ( store as 'direct', internet as 'online'))); +select * from notrotate_view; + year | order_mode | yearly_total +------+------------+-------------- + 2020 | direct | 5500 + 2021 | direct | 1000 + 2022 | direct | 5000 + 2020 | online | 1000 + 2021 | online | 1000 +(5 rows) + +drop view rotate_view; +drop view notrotate_view; +-- 子查询 +select * from (select year, direct as store, online as internet from (select year, order_mode, order_total from original_orders ) as orders rotate (sum(order_total) for order_mode in ('direct', 'online')) )order by year; + year | store | internet +------+-------+---------- + 2020 | 5500 | 1000 + 2021 | 1000 | 1000 + 2022 | 5000 | +(3 rows) + +select year, order_mode, yearly_total from(select * from rotate_orders not rotate ( yearly_total for order_mode in ( store as 'direct', internet as 'online'))) where year> 2020; + year | order_mode | yearly_total +------+------------+-------------- + 2021 | direct | 1000 + 2022 | direct | 5000 + 2021 | online | 1000 +(3 rows) + +-- SMP +set query_dop = 4; +select * from ( select year, order_mode, order_total from original_orders) rotate (sum(order_total) for order_mode in ('direct' as store, 'online' as internet)) order by year; + year | store | internet +------+-------+---------- + 2020 | 5500 | 1000 + 2021 | 1000 | 1000 + 2022 | 5000 | +(3 rows) + +select * from rotate_orders not rotate ( yearly_total for order_mode in ( store as 'direct', internet as 'online')); + year | order_mode | yearly_total +------+------------+-------------- + 2020 | direct | 5500 + 2021 | direct | 1000 + 2022 | direct | 5000 + 2020 | online | 1000 + 2021 | online | 1000 +(5 rows) + +set query_dop = 1; +-- table name contain quotes +create table "'rotate'orders" as (select * from (select year, order_mode, order_total from original_orders) as t rotate (sum(order_total) for order_mode in ('direct' as store, 'online' as internet)) order by year); +select * from "'rotate'orders" not rotate ( yearly_total for order_mode in ( store as 'direct', internet as 'online')); + year | order_mode | yearly_total +------+------------+-------------- + 2020 | direct | 5500 + 2021 | direct | 1000 + 2022 | direct | 5000 + 2020 | online | 1000 + 2021 | online | 1000 +(5 rows) + +create table "ROTATEorders" as (select * from (select year, order_mode, order_total from original_orders) as t rotate (sum(order_total) for order_mode in ('direct' as store, 'online' as internet)) order by year); +select * from "ROTATEorders" not rotate ( yearly_total for order_mode in ( store as 'direct', internet as 'online')); + year | order_mode | yearly_total +------+------------+-------------- + 2020 | direct | 5500 + 2021 | direct | 1000 + 2022 | direct | 5000 + 2020 | online | 1000 + 2021 | online | 1000 +(5 rows) + +create table "rotate@orders" as (select * from (select year, order_mode, order_total from original_orders) as t rotate (sum(order_total) for order_mode in ('direct' as store, 'online' as internet)) order by year); +select * from "rotate@orders" not rotate ( yearly_total for order_mode in ( store as 'direct', internet as 'online')); + year | order_mode | yearly_total +------+------------+-------------- + 2020 | direct | 5500 + 2021 | direct | 1000 + 2022 | direct | 5000 + 2020 | online | 1000 + 2021 | online | 1000 +(5 rows) + +drop table "'rotate'orders"; +drop table "ROTATEorders"; +drop table "rotate@orders"; +drop table original_orders; +drop table rotate_orders; +-- more than one col +create table original_orders2 (id int, year int, order_mode text, order_total int, order_value int); +insert into original_orders2 values (1,2020,'direct',5000,10),(2,2020,'online',1000,20), (3,2021,'online',1000,30), (4,2021,'direct',1000,50), (5,2022,'direct',5000,100), (6,2020,'direct',500,2); +select * from ( select year, order_mode, order_total from original_orders2) rotate (sum(order_total) ss,AVG(order_value) av for order_mode in ('direct' as store, 'online' as internet)) order by year; + year | store_ss | store_av | internet_ss | internet_av +------+----------+----------------------+-------------+--------------------- + 2020 | 5500 | 6.0000000000000000 | 1000 | 20.0000000000000000 + 2021 | 1000 | 50.0000000000000000 | 1000 | 30.0000000000000000 + 2022 | 5000 | 100.0000000000000000 | | +(3 rows) + +create table rotate_orders2 as ( select * from ( select year, order_mode, order_total from original_orders2 ) as t rotate (sum(order_total) ss, avg(order_value) av for order_mode in ( 'direct' as store, 'online' as internet)) order by year); +select * from rotate_orders2 not rotate ( (yearly_total, yearly_arv) for order_mode in ( ( store_ss,store_av) as 'direct', (internet_ss,internet_av) as 'online')); + year | order_mode | yearly_total | yearly_arv +------+------------+--------------+---------------------- + 2020 | direct | 5500 | 6.0000000000000000 + 2021 | direct | 1000 | 50.0000000000000000 + 2022 | direct | 5000 | 100.0000000000000000 + 2020 | online | 1000 | 20.0000000000000000 + 2021 | online | 1000 | 30.0000000000000000 +(5 rows) + +drop table original_orders2; +drop table rotate_orders2; +--in clause without alias +create table sale ( product varchar(50), month varchar(30), sales int); +insert into sale (product, month, sales) values ('a','january',100),('a','february',100),('a','march',100); +select * from sale rotate (sum(sales) for month in ('january','february','march')); + product | january | february | march +---------+---------+----------+------- + a | 100 | 100 | 100 +(1 row) + +create table stu(name varchar(50), math int, english int, chinese int); +insert into stu values ('zhang',7,8,9); +select * from stu; + name | math | english | chinese +-------+------+---------+--------- + zhang | 7 | 8 | 9 +(1 row) + +select * from stu not rotate (num for list in (math, english, chinese)); + name | list | num +-------+---------+----- + zhang | math | 7 + zhang | english | 8 + zhang | chinese | 9 +(3 rows) + +drop table sale; +drop table stu; +--in clause not string +create table sale ( product varchar(50), month int, sales int); +insert into sale (product, month, sales) values ('a',1,100),('a',2,100),('a',3,100); +select * from sale rotate (sum(sales) for month in (1,2,3)); + product | 1 | 2 | 3 +---------+-----+-----+----- + a | 100 | 100 | 100 +(1 row) + +drop table sale; +create table sale ( product varchar(50), month float, sales int); +insert into sale (product, month, sales) values ('a',1.1,100),('a',1.2,100),('a',1.3,100); +select * from sale rotate (sum(sales) for month in (1.1,1.2,1.3)); + product | 1.100000 | 1.200000 | 1.300000 +---------+----------+----------+---------- + a | 100 | 100 | 100 +(1 row) + +drop table sale; +--not rotate clause with different arguments +create table saleslist (consumer varchar2(20), commodity varchar2(20), salesnum number(8)); +insert into saleslist values ('aaa','上衣',5),('bbb','裤子',3),('ccc','袜子',2),('ddd','上衣',4),('eee','裤子',6); +create table rotate_sales as select * from saleslist rotate(max(salesnum) for commodity in ('上衣' as 上衣, '裤子' as 裤子, '袜子' as 袜子, '帽子' as 帽子)) where 1=1; +select * from rotate_sales not rotate (salesnum for aaa in (上衣, 裤子, 袜子, 帽子)); + consumer | aaa | salesnum +----------+------+---------- + aaa | 上衣 | 5 + ddd | 上衣 | 4 + bbb | 裤子 | 3 + eee | 裤子 | 6 + ccc | 袜子 | 2 +(5 rows) + +select * from rotate_sales not rotate (salesnum for aaa in (上衣)); + consumer | 裤子 | 袜子 | 帽子 | aaa | salesnum +----------+------+------+------+------+---------- + aaa | | | | 上衣 | 5 + ddd | | | | 上衣 | 4 +(2 rows) + +drop table rotate_sales; +drop table saleslist; +-- column table +create table product_column (id int, name varchar(20), value int) with (orientation = column); +insert into product_column values +(1,'a',10),(2,'b',20),(3,'c',30),(4,'a',40),(5,'b',50),(6,'c',60); +select * from (select name, value from product_column) rotate(sum(value) for name in ('a','b','c')); + a | b | c +----+----+---- + 50 | 70 | 90 +(1 row) + +create table product_column_un (a int, b int, c int) with (orientation = column); +insert into product_column_un values(50,70,90); +select * from product_column_un not rotate (value for name in (a,b,c)); + name | value +------+------- + a | 50 + b | 70 + c | 90 +(3 rows) + +drop table product_column; +drop table product_column_un; +--partition table +create table orders_par (id int, year int, order_mode text, order_total int) partition by range(order_total) (partition par1 values less than (1000),partition par2 values less than (2000),partition par3 values less than (maxvalue)); +insert into orders_par values (1,2020,'direct',500), (2,2020,'online',1000), (3,2021,'online',200), (4,2021,'direct',100), (5,2022,'direct',2000), (6,2020,'direct',5000); +select * from ( select year, order_mode, order_total from orders_par) rotate (sum(order_total) +for order_mode in ('direct' as store, 'online' as internet)) order by year; + year | store | internet +------+-------+---------- + 2020 | 5500 | 1000 + 2021 | 100 | 200 + 2022 | 2000 | +(3 rows) + +select * from ( select year, order_mode, order_total from orders_par partition(par1)) rotate (sum(order_total) +for order_mode in ('direct' as store, 'online' as internet)) order by year; + year | store | internet +------+-------+---------- + 2020 | 500 | + 2021 | 100 | 200 +(2 rows) + +create table rotate_orders_par(year int, store int, internet int) partition by range(store)(partition par1 values less than (1000),partition par2 values less than (maxvalue)); +insert into rotate_orders_par values (2020, 5500,1000),(2021,100,200),(2022,2000,null); +select * from rotate_orders_par not rotate ( yearly_total for order_mode in ( store as 'direct', internet as 'online')); + year | order_mode | yearly_total +------+------------+-------------- + 2021 | direct | 100 + 2020 | direct | 5500 + 2022 | direct | 2000 + 2021 | online | 200 + 2020 | online | 1000 +(5 rows) + +drop table orders_par; +drop table rotate_orders_par; +--ustore +create table product_ustore(id int, name varchar(10), value int) with (storage_type=ustore); +insert into product_ustore values (10,'a',10),(20,'b',20),(30,'c',30),(101,'a',40),(201,'b',50),(301,'c',60); +select * from (select name, value from product_ustore) rotate(sum(value) for name in ('a','b','c')); + a | b | c +----+----+---- + 50 | 70 | 90 +(1 row) + +create table stu_ustore (name varchar(20), math int, english int, chinese int) with (storage_type=ustore); +insert into stu_ustore values('Tom',10,20,30); +select * from stu_ustore not rotate (num for list in (math, english,chinese)); + name | list | num +------+---------+----- + Tom | math | 10 + Tom | english | 20 + Tom | chinese | 30 +(3 rows) + +drop table product_ustore; +drop table stu_ustore; +--segment +create table product_segment(id int, name varchar(10), value int) with (segment=on); +insert into product_segment values (1,'a',10),(2,'b',20),(3,'c',30),(4,'a',40),(5,'b',50),(6,'c',60); +select * from (select name, value from product_segment) rotate(sum(value) for name in ('a','b','c')); + a | b | c +----+----+---- + 50 | 70 | 90 +(1 row) + +create table stu_segment (name varchar(20), math int, english int, chinese int) with (segment=on); +insert into stu_segment values('Tom',10,20,30); +select * from stu_segment not rotate (num for list in (math, english,chinese)); + name | list | num +------+---------+----- + Tom | math | 10 + Tom | english | 20 + Tom | chinese | 30 +(3 rows) + +drop table product_segment; +drop table stu_segment; +-- multi-value +create table rotate_case (id int, score int, class varchar(20), semester int); +insert into rotate_case values (2, 35, null, null),(2, 35, 'phy', null),(2, 35, null, 1),(3, 40, 'math',1),(3, 50, 'math',1),(4, 55, 'phy', 1); +select * from rotate_case rotate (max(score) for (class, semester) in (('phy',1),('math',1))) order by id; + id | phy_1 | math_1 +----+-------+-------- + 2 | | + 3 | | 50 + 4 | 55 | +(3 rows) + +select * from rotate_case rotate (max(score) for (class, semester) in (('phy',1) as physics, ('math',1) as mathematics)) order by id; + id | physics | mathematics +----+---------+------------- + 2 | | + 3 | | 50 + 4 | 55 | +(3 rows) + +create table stu_segment1 (name varchar(20), math1 int, math2 int, english1 int, english2 int, chinese1 int, chinese2 int); +insert into stu_segment1 values('Tom',10,20,30,40,50,60); +insert into stu_segment1 values('Jery',10,null,20,null,null,null); +select * from stu_segment1 not rotate include nulls ((num1,num2) for list1 in ((math1, math2), (english1, english2), (chinese1, chinese2))); + name | list1 | num1 | num2 +------+-------------------+------+------ + Tom | math1_math2 | 10 | 20 + Jery | math1_math2 | 10 | + Tom | english1_english2 | 30 | 40 + Jery | english1_english2 | 20 | + Tom | chinese1_chinese2 | 50 | 60 + Jery | chinese1_chinese2 | | +(6 rows) + +select * from stu_segment1 not rotate exclude nulls ((num1,num2) for list1 in ((math1, math2), (english1, english2), (chinese1, chinese2))); + name | list1 | num1 | num2 +------+-------------------+------+------ + Tom | math1_math2 | 10 | 20 + Jery | math1_math2 | 10 | + Tom | english1_english2 | 30 | 40 + Jery | english1_english2 | 20 | + Tom | chinese1_chinese2 | 50 | 60 +(5 rows) + +select * from stu_segment1 not rotate ((num1,num2) for list1 in ((math1, math2), (english1, english2), (chinese1, chinese2))); + name | list1 | num1 | num2 +------+-------------------+------+------ + Tom | math1_math2 | 10 | 20 + Jery | math1_math2 | 10 | + Tom | english1_english2 | 30 | 40 + Jery | english1_english2 | 20 | + Tom | chinese1_chinese2 | 50 | 60 +(5 rows) + +select * from stu_segment1 not rotate include nulls ((num1,num2) for list1 in ((math1, math2) as 'math', (english1, english2) as 'english', (chinese1, chinese2) as 'chinese')); + name | list1 | num1 | num2 +------+---------+------+------ + Tom | math | 10 | 20 + Jery | math | 10 | + Tom | english | 30 | 40 + Jery | english | 20 | + Tom | chinese | 50 | 60 + Jery | chinese | | +(6 rows) + +select * from stu_segment1 not rotate include nulls ((num1,num2) for (list1,list2) in ((math1, math2) as ('m1','m2'), (english1, english2) as ('e1','e2'), (chinese1, chinese2) as ('c1','c2'))); + name | list1 | list2 | num1 | num2 +------+-------+-------+------+------ + Tom | m1 | m2 | 10 | 20 + Jery | m1 | m2 | 10 | + Tom | e1 | e2 | 30 | 40 + Jery | e1 | e2 | 20 | + Tom | c1 | c2 | 50 | 60 + Jery | c1 | c2 | | +(6 rows) + +select * from stu_segment1 not rotate include nulls ((num1,num2) for (list1,list2) in ((math1, math2) as 'math', (english1, english2) as 'english', (chinese1, chinese2) as 'chinese')); +ERROR: the number of elements in alias clause doesn't agree with the number of columns NOT ROTATE_FOR specified +drop table rotate_case; +drop table stu_segment1; +-- test other condition for not rotate +create table rotate_orders (year int, store int, internet int); +insert into rotate_orders values (2020, 5500, 1000),(2021, 1000, 1000),(2022, 5000, null),(2023, null, 2000); +select * from rotate_orders not rotate include nulls (yearly_total for order_mode in (internet as 'online', store as 'direct')) where yearly_total is not null; + year | order_mode | yearly_total +------+------------+-------------- + 2020 | online | 1000 + 2021 | online | 1000 + 2023 | online | 2000 + 2020 | direct | 5500 + 2021 | direct | 1000 + 2022 | direct | 5000 +(6 rows) + +select * from rotate_orders not rotate include nulls (yearly_total for order_mode in (internet as 'online', store as 'direct')) limit 1; + year | order_mode | yearly_total +------+------------+-------------- + 2020 | online | 1000 +(1 row) + +select * from rotate_orders not rotate include nulls (yearly_total for order_mode in (internet as 'online', store as 'direct')) order by yearly_total desc; + year | order_mode | yearly_total +------+------------+-------------- + 2022 | online | + 2023 | direct | + 2020 | direct | 5500 + 2022 | direct | 5000 + 2023 | online | 2000 + 2020 | online | 1000 + 2021 | direct | 1000 + 2021 | online | 1000 +(8 rows) + +--test alias for not rotate +select year as yy, order_mode as mode, yearly_total as total from rotate_orders not rotate (yearly_total for order_mode in (internet as 'online', store as 'direct')); + yy | mode | total +------+--------+------- + 2020 | online | 1000 + 2021 | online | 1000 + 2023 | online | 2000 + 2020 | direct | 5500 + 2021 | direct | 1000 + 2022 | direct | 5000 +(6 rows) + +select year as yy, yearly_total as total from rotate_orders not rotate (yearly_total for order_mode in (internet as 'online', store as 'direct')); + yy | total +------+------- + 2020 | 1000 + 2021 | 1000 + 2023 | 2000 + 2020 | 5500 + 2021 | 1000 + 2022 | 5000 +(6 rows) + +select year as yy, order_mode as mode, yearly_total as total from rotate_orders not rotate include nulls (yearly_total for order_mode in (internet as 'online', store as 'direct')) where yearly_total is not null order by yy desc; + yy | mode | total +------+--------+------- + 2023 | online | 2000 + 2022 | direct | 5000 + 2021 | online | 1000 + 2021 | direct | 1000 + 2020 | online | 1000 + 2020 | direct | 5500 +(6 rows) + +drop table rotate_orders; diff --git a/src/test/regress/parallel_schedule0C b/src/test/regress/parallel_schedule0C index 19fb8fb58..809f0d54c 100644 --- a/src/test/regress/parallel_schedule0C +++ b/src/test/regress/parallel_schedule0C @@ -189,3 +189,7 @@ test: user_host_test # test for new_expr_by_flatten test: enable_expr_fusion_flatten + +# test for rotate and unrotate function +test: gb_ora_rotate_unrotate + diff --git a/src/test/regress/sql/gb_ora_rotate_unrotate.sql b/src/test/regress/sql/gb_ora_rotate_unrotate.sql new file mode 100644 index 000000000..91eb31b98 --- /dev/null +++ b/src/test/regress/sql/gb_ora_rotate_unrotate.sql @@ -0,0 +1,192 @@ +-- +-- Test rotate and not rotate grammer +-- + +-- Create +create table original_orders (id int, year int, order_mode text, order_total int); +insert into original_orders values (1,2020,'direct',5000), (2,2020,'online',1000), (3,2021,'online',1000), (4,2021,'direct',1000), (5,2022,'direct',5000), (6,2020,'direct',500); + +select * from ( select year, order_mode, order_total from original_orders) rotate (sum(order_total) for order_mode in ('direct' as store, 'online' as internet)) order by year; + +select * from (select year, order_mode, order_total from original_orders) rotate (sum(order_total) for order_mode in ('online' as internet )) order by year; + +create table rotate_orders as (select * from (select year, order_mode, order_total from original_orders) as t rotate (sum(order_total) for order_mode in ('direct' as store, 'online' as internet)) order by year); + +-- test not rotate (column transform to row) +select * from rotate_orders not rotate ( yearly_total for order_mode in ( store as 'direct', internet as 'online')); + +select * from rotate_orders not rotate exclude nulls (yearly_total for order_mode in ( store as 'direct', internet as 'online')); + +select * from rotate_orders not rotate include nulls ( yearly_total for ordre_mode in ( store as 'direct', internet as 'online')); + +-- rotate and not rotate are in the same sql +select * from (select year, direct, online from (select year, order_mode, order_total from original_orders) rotate (sum(order_total) for order_mode in ('direct', 'online')) order by year) as rotate_t not rotate ( yearly_total for order_mode in (direct, online)); + +select * from (select year, order_mode, yearly_total from (select * from rotate_orders not rotate ( yearly_total for order_mode in ( store as 'direct', internet as 'online')))) rotate (sum(yearly_total) for order_mode in ('direct' as store, 'online' as internet) ) order by year; + +-- create view +create view rotate_view as (select * from (select year, order_mode, order_total from original_orders) as t rotate (sum(order_total) for order_mode in ('direct' as store, 'online' as internet)) order by year); +select * from rotate_view; + +create view notrotate_view as (select * from rotate_orders not rotate ( yearly_total for order_mode in ( store as 'direct', internet as 'online'))); +select * from notrotate_view; + +drop view rotate_view; +drop view notrotate_view; + +-- 子查询 +select * from (select year, direct as store, online as internet from (select year, order_mode, order_total from original_orders ) as orders rotate (sum(order_total) for order_mode in ('direct', 'online')) )order by year; + +select year, order_mode, yearly_total from(select * from rotate_orders not rotate ( yearly_total for order_mode in ( store as 'direct', internet as 'online'))) where year> 2020; + +-- SMP +set query_dop = 4; +select * from ( select year, order_mode, order_total from original_orders) rotate (sum(order_total) for order_mode in ('direct' as store, 'online' as internet)) order by year; +select * from rotate_orders not rotate ( yearly_total for order_mode in ( store as 'direct', internet as 'online')); +set query_dop = 1; + +-- table name contain quotes +create table "'rotate'orders" as (select * from (select year, order_mode, order_total from original_orders) as t rotate (sum(order_total) for order_mode in ('direct' as store, 'online' as internet)) order by year); +select * from "'rotate'orders" not rotate ( yearly_total for order_mode in ( store as 'direct', internet as 'online')); + +create table "ROTATEorders" as (select * from (select year, order_mode, order_total from original_orders) as t rotate (sum(order_total) for order_mode in ('direct' as store, 'online' as internet)) order by year); +select * from "ROTATEorders" not rotate ( yearly_total for order_mode in ( store as 'direct', internet as 'online')); + +create table "rotate@orders" as (select * from (select year, order_mode, order_total from original_orders) as t rotate (sum(order_total) for order_mode in ('direct' as store, 'online' as internet)) order by year); +select * from "rotate@orders" not rotate ( yearly_total for order_mode in ( store as 'direct', internet as 'online')); + +drop table "'rotate'orders"; +drop table "ROTATEorders"; +drop table "rotate@orders"; +drop table original_orders; +drop table rotate_orders; + +-- more than one col +create table original_orders2 (id int, year int, order_mode text, order_total int, order_value int); +insert into original_orders2 values (1,2020,'direct',5000,10),(2,2020,'online',1000,20), (3,2021,'online',1000,30), (4,2021,'direct',1000,50), (5,2022,'direct',5000,100), (6,2020,'direct',500,2); + +select * from ( select year, order_mode, order_total from original_orders2) rotate (sum(order_total) ss,AVG(order_value) av for order_mode in ('direct' as store, 'online' as internet)) order by year; + +create table rotate_orders2 as ( select * from ( select year, order_mode, order_total from original_orders2 ) as t rotate (sum(order_total) ss, avg(order_value) av for order_mode in ( 'direct' as store, 'online' as internet)) order by year); +select * from rotate_orders2 not rotate ( (yearly_total, yearly_arv) for order_mode in ( ( store_ss,store_av) as 'direct', (internet_ss,internet_av) as 'online')); + +drop table original_orders2; +drop table rotate_orders2; + +--in clause without alias +create table sale ( product varchar(50), month varchar(30), sales int); +insert into sale (product, month, sales) values ('a','january',100),('a','february',100),('a','march',100); +select * from sale rotate (sum(sales) for month in ('january','february','march')); + +create table stu(name varchar(50), math int, english int, chinese int); +insert into stu values ('zhang',7,8,9); +select * from stu; +select * from stu not rotate (num for list in (math, english, chinese)); + +drop table sale; +drop table stu; + +--in clause not string +create table sale ( product varchar(50), month int, sales int); +insert into sale (product, month, sales) values ('a',1,100),('a',2,100),('a',3,100); +select * from sale rotate (sum(sales) for month in (1,2,3)); +drop table sale; + +create table sale ( product varchar(50), month float, sales int); +insert into sale (product, month, sales) values ('a',1.1,100),('a',1.2,100),('a',1.3,100); +select * from sale rotate (sum(sales) for month in (1.1,1.2,1.3)); +drop table sale; + +--not rotate clause with different arguments +create table saleslist (consumer varchar2(20), commodity varchar2(20), salesnum number(8)); +insert into saleslist values ('aaa','上衣',5),('bbb','裤子',3),('ccc','袜子',2),('ddd','上衣',4),('eee','裤子',6); +create table rotate_sales as select * from saleslist rotate(max(salesnum) for commodity in ('上衣' as 上衣, '裤子' as 裤子, '袜子' as 袜子, '帽子' as 帽子)) where 1=1; +select * from rotate_sales not rotate (salesnum for aaa in (上衣, 裤子, 袜子, 帽子)); +select * from rotate_sales not rotate (salesnum for aaa in (上衣)); +drop table rotate_sales; +drop table saleslist; + +-- column table +create table product_column (id int, name varchar(20), value int) with (orientation = column); +insert into product_column values +(1,'a',10),(2,'b',20),(3,'c',30),(4,'a',40),(5,'b',50),(6,'c',60); +select * from (select name, value from product_column) rotate(sum(value) for name in ('a','b','c')); + +create table product_column_un (a int, b int, c int) with (orientation = column); +insert into product_column_un values(50,70,90); +select * from product_column_un not rotate (value for name in (a,b,c)); + +drop table product_column; +drop table product_column_un; + +--partition table +create table orders_par (id int, year int, order_mode text, order_total int) partition by range(order_total) (partition par1 values less than (1000),partition par2 values less than (2000),partition par3 values less than (maxvalue)); +insert into orders_par values (1,2020,'direct',500), (2,2020,'online',1000), (3,2021,'online',200), (4,2021,'direct',100), (5,2022,'direct',2000), (6,2020,'direct',5000); +select * from ( select year, order_mode, order_total from orders_par) rotate (sum(order_total) +for order_mode in ('direct' as store, 'online' as internet)) order by year; +select * from ( select year, order_mode, order_total from orders_par partition(par1)) rotate (sum(order_total) +for order_mode in ('direct' as store, 'online' as internet)) order by year; + +create table rotate_orders_par(year int, store int, internet int) partition by range(store)(partition par1 values less than (1000),partition par2 values less than (maxvalue)); +insert into rotate_orders_par values (2020, 5500,1000),(2021,100,200),(2022,2000,null); +select * from rotate_orders_par not rotate ( yearly_total for order_mode in ( store as 'direct', internet as 'online')); + +drop table orders_par; +drop table rotate_orders_par; + +--ustore +create table product_ustore(id int, name varchar(10), value int) with (storage_type=ustore); +insert into product_ustore values (10,'a',10),(20,'b',20),(30,'c',30),(101,'a',40),(201,'b',50),(301,'c',60); +select * from (select name, value from product_ustore) rotate(sum(value) for name in ('a','b','c')); + +create table stu_ustore (name varchar(20), math int, english int, chinese int) with (storage_type=ustore); +insert into stu_ustore values('Tom',10,20,30); +select * from stu_ustore not rotate (num for list in (math, english,chinese)); + +drop table product_ustore; +drop table stu_ustore; + +--segment +create table product_segment(id int, name varchar(10), value int) with (segment=on); +insert into product_segment values (1,'a',10),(2,'b',20),(3,'c',30),(4,'a',40),(5,'b',50),(6,'c',60); +select * from (select name, value from product_segment) rotate(sum(value) for name in ('a','b','c')); + +create table stu_segment (name varchar(20), math int, english int, chinese int) with (segment=on); +insert into stu_segment values('Tom',10,20,30); +select * from stu_segment not rotate (num for list in (math, english,chinese)); + +drop table product_segment; +drop table stu_segment; + +-- multi-value +create table rotate_case (id int, score int, class varchar(20), semester int); +insert into rotate_case values (2, 35, null, null),(2, 35, 'phy', null),(2, 35, null, 1),(3, 40, 'math',1),(3, 50, 'math',1),(4, 55, 'phy', 1); +select * from rotate_case rotate (max(score) for (class, semester) in (('phy',1),('math',1))) order by id; +select * from rotate_case rotate (max(score) for (class, semester) in (('phy',1) as physics, ('math',1) as mathematics)) order by id; + +create table stu_segment1 (name varchar(20), math1 int, math2 int, english1 int, english2 int, chinese1 int, chinese2 int); +insert into stu_segment1 values('Tom',10,20,30,40,50,60); +insert into stu_segment1 values('Jery',10,null,20,null,null,null); + +select * from stu_segment1 not rotate include nulls ((num1,num2) for list1 in ((math1, math2), (english1, english2), (chinese1, chinese2))); +select * from stu_segment1 not rotate exclude nulls ((num1,num2) for list1 in ((math1, math2), (english1, english2), (chinese1, chinese2))); +select * from stu_segment1 not rotate ((num1,num2) for list1 in ((math1, math2), (english1, english2), (chinese1, chinese2))); +select * from stu_segment1 not rotate include nulls ((num1,num2) for list1 in ((math1, math2) as 'math', (english1, english2) as 'english', (chinese1, chinese2) as 'chinese')); +select * from stu_segment1 not rotate include nulls ((num1,num2) for (list1,list2) in ((math1, math2) as ('m1','m2'), (english1, english2) as ('e1','e2'), (chinese1, chinese2) as ('c1','c2'))); +select * from stu_segment1 not rotate include nulls ((num1,num2) for (list1,list2) in ((math1, math2) as 'math', (english1, english2) as 'english', (chinese1, chinese2) as 'chinese')); + +drop table rotate_case; +drop table stu_segment1; + +-- test other condition for not rotate +create table rotate_orders (year int, store int, internet int); +insert into rotate_orders values (2020, 5500, 1000),(2021, 1000, 1000),(2022, 5000, null),(2023, null, 2000); +select * from rotate_orders not rotate include nulls (yearly_total for order_mode in (internet as 'online', store as 'direct')) where yearly_total is not null; +select * from rotate_orders not rotate include nulls (yearly_total for order_mode in (internet as 'online', store as 'direct')) limit 1; +select * from rotate_orders not rotate include nulls (yearly_total for order_mode in (internet as 'online', store as 'direct')) order by yearly_total desc; + +--test alias for not rotate +select year as yy, order_mode as mode, yearly_total as total from rotate_orders not rotate (yearly_total for order_mode in (internet as 'online', store as 'direct')); +select year as yy, yearly_total as total from rotate_orders not rotate (yearly_total for order_mode in (internet as 'online', store as 'direct')); +select year as yy, order_mode as mode, yearly_total as total from rotate_orders not rotate include nulls (yearly_total for order_mode in (internet as 'online', store as 'direct')) where yearly_total is not null order by yy desc; +drop table rotate_orders; \ No newline at end of file