diff --git a/src/common/backend/nodes/copyfuncs.cpp b/src/common/backend/nodes/copyfuncs.cpp index 067d6d3d9..89a92daf0 100644 --- a/src/common/backend/nodes/copyfuncs.cpp +++ b/src/common/backend/nodes/copyfuncs.cpp @@ -2695,6 +2695,10 @@ static Aggref* _copyAggref(const Aggref* from) COPY_SCALAR_FIELD(aggvariadic); COPY_SCALAR_FIELD(aggkind); COPY_SCALAR_FIELD(agglevelsup); + if (t_thrd.proc->workingVersionNum >= KEEP_FUNC_VERSION_NUMBER) { + COPY_SCALAR_FIELD(aggiskeep); + COPY_SCALAR_FIELD(aggkpfirst); + } COPY_LOCATION_FIELD(location); COPY_NODE_FIELD(aggargtypes); COPY_SCALAR_FIELD(aggsplit); @@ -2748,6 +2752,11 @@ static WindowFunc* _copyWindowFunc(const WindowFunc* from) COPY_SCALAR_FIELD(winref); COPY_SCALAR_FIELD(winstar); COPY_SCALAR_FIELD(winagg); + if (t_thrd.proc->workingVersionNum >= KEEP_FUNC_VERSION_NUMBER) { + COPY_NODE_FIELD(keep_args); + COPY_NODE_FIELD(winkporder); + COPY_SCALAR_FIELD(winkpfirst); + } COPY_LOCATION_FIELD(location); return newnode; @@ -4130,6 +4139,9 @@ static FuncCall* _copyFuncCall(const FuncCall* from) COPY_NODE_FIELD(args); COPY_NODE_FIELD(agg_order); COPY_NODE_FIELD(agg_filter); + if (t_thrd.proc->workingVersionNum >= KEEP_FUNC_VERSION_NUMBER) { + COPY_NODE_FIELD(aggKeep); + } COPY_SCALAR_FIELD(agg_star); COPY_SCALAR_FIELD(agg_distinct); COPY_SCALAR_FIELD(func_variadic); @@ -7315,6 +7327,16 @@ static CleanConnStmt* _copyCleanConnStmt(const CleanConnStmt* from) return newnode; } +static KeepClause* _copyKeepClause(const KeepClause* from) +{ + KeepClause* newnode = makeNode(KeepClause); + + COPY_SCALAR_FIELD(rank_first); + COPY_NODE_FIELD(keep_order); + COPY_LOCATION_FIELD(location); + + return newnode; +} #endif static ErrorCacheEntry* _copyErrorCacheEntry(const ErrorCacheEntry* from) @@ -9075,6 +9097,9 @@ void* copyObject(const void* from) case T_UnrotateInCell: retval = _copyUnrotateInCell((UnrotateInCell*)from); break; + case T_KeepClause: + retval = _copyKeepClause((KeepClause*)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 87c9fbe30..89a1121b2 100644 --- a/src/common/backend/nodes/equalfuncs.cpp +++ b/src/common/backend/nodes/equalfuncs.cpp @@ -243,6 +243,10 @@ static bool _equalAggref(const Aggref* a, const Aggref* b) COMPARE_SCALAR_FIELD(aggvariadic); COMPARE_SCALAR_FIELD(aggkind); COMPARE_SCALAR_FIELD(agglevelsup); + if (t_thrd.proc->workingVersionNum >= KEEP_FUNC_VERSION_NUMBER) { + COMPARE_SCALAR_FIELD(aggiskeep); + COMPARE_SCALAR_FIELD(aggkpfirst); + } COMPARE_LOCATION_FIELD(location); COMPARE_NODE_FIELD(aggargtypes); COMPARE_SCALAR_FIELD(aggsplit); @@ -274,6 +278,11 @@ static bool _equalWindowFunc(const WindowFunc* a, const WindowFunc* b) COMPARE_SCALAR_FIELD(winref); COMPARE_SCALAR_FIELD(winstar); COMPARE_SCALAR_FIELD(winagg); + if (t_thrd.proc->workingVersionNum >= KEEP_FUNC_VERSION_NUMBER) { + COMPARE_NODE_FIELD(keep_args); + COMPARE_NODE_FIELD(winkporder); + COMPARE_SCALAR_FIELD(winkpfirst); + } COMPARE_LOCATION_FIELD(location); return true; @@ -2669,6 +2678,9 @@ static bool _equalFuncCall(const FuncCall* a, const FuncCall* b) COMPARE_NODE_FIELD(args); COMPARE_NODE_FIELD(agg_order); COMPARE_NODE_FIELD(agg_filter); + if (t_thrd.proc->workingVersionNum >= KEEP_FUNC_VERSION_NUMBER) { + COMPARE_NODE_FIELD(aggKeep); + } COMPARE_SCALAR_FIELD(agg_within_group); COMPARE_SCALAR_FIELD(agg_star); COMPARE_SCALAR_FIELD(agg_distinct); @@ -3619,6 +3631,14 @@ static bool _equalCleanConnStmt(const CleanConnStmt* a, const CleanConnStmt* b) return true; } +static bool _equalKeepClause(const KeepClause* a, const KeepClause* b) +{ + COMPARE_SCALAR_FIELD(rank_first); + COMPARE_NODE_FIELD(keep_order); + COMPARE_LOCATION_FIELD(location); + + return true; +} #endif bool _equalSimpleVar(void* va, void* vb) @@ -4619,6 +4639,9 @@ bool equal(const void* a, const void* b) case T_UnrotateInCell: retval = _equalUnrotateInCell((UnrotateInCell*)a, (UnrotateInCell*)b); break; + case T_KeepClause: + retval = _equalKeepClause((KeepClause*)a, (KeepClause*)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 4bd13b80c..30c786dca 100644 --- a/src/common/backend/nodes/makefuncs.cpp +++ b/src/common/backend/nodes/makefuncs.cpp @@ -644,6 +644,7 @@ FuncCall* makeFuncCall(List* funcname, List* args, int location) funcCall->agg_distinct = FALSE; funcCall->agg_order = NIL; funcCall->agg_filter = NULL; + funcCall->aggKeep = NULL; funcCall->over = NULL; funcCall->location = location; diff --git a/src/common/backend/nodes/nodeFuncs.cpp b/src/common/backend/nodes/nodeFuncs.cpp index 2175674ea..a83394fda 100644 --- a/src/common/backend/nodes/nodeFuncs.cpp +++ b/src/common/backend/nodes/nodeFuncs.cpp @@ -1818,6 +1818,14 @@ bool expression_tree_walker(Node* node, bool (*walker)(), void* context) if (expression_tree_walker((Node*)expr->args, walker, context)) { return true; } + /* recurse directly on List */ + if (expression_tree_walker((Node*)expr->keep_args, walker, context)) { + return true; + } + /* recurse directly on List */ + if (expression_tree_walker((Node*)expr->winkporder, walker, context)) { + return true; + } } break; case T_ArrayRef: { ArrayRef* aref = (ArrayRef*)node; @@ -2426,6 +2434,8 @@ Node* expression_tree_mutator(Node* node, Node* (*mutator)(Node*, void*), void* FLATCOPY(newnode, wfunc, WindowFunc, isCopy); MUTATE(newnode->args, wfunc->args, List*); + MUTATE(newnode->keep_args, wfunc->keep_args, List*); + MUTATE(newnode->winkporder, wfunc->winkporder, List*); return (Node*)newnode; } break; case T_ArrayRef: { @@ -3412,6 +3422,9 @@ bool raw_expression_tree_walker(Node* node, bool (*walker)(), void* context) if (p2walker(fcall->agg_filter, context)) { return true; } + if (p2walker(fcall->aggKeep, context)) { + return true; + } if (p2walker(fcall->over, context)) { return true; } @@ -3600,6 +3613,12 @@ bool raw_expression_tree_walker(Node* node, bool (*walker)(), void* context) if (p2walker(stmt->unrotateInExpr, context)) return true; } break; + case T_KeepClause: { + KeepClause *kp = (KeepClause *) node; + + if (p2walker(kp->keep_order, 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 ce21ef1b0..a45f115fa 100755 --- a/src/common/backend/nodes/nodes.cpp +++ b/src/common/backend/nodes/nodes.cpp @@ -631,6 +631,7 @@ static const TagStr g_tagStrArr[] = {{T_Invalid, "Invalid"}, {T_UnrotateClause, "UnrotateClause"}, {T_RotateInCell, "RotateInCell"}, {T_UnrotateInCell, "UnrotateInCell"}, + {T_KeepClause, "KeepClause"}, {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 d3ef0de18..1bace1827 100755 --- a/src/common/backend/nodes/outfuncs.cpp +++ b/src/common/backend/nodes/outfuncs.cpp @@ -2588,6 +2588,10 @@ static void _outAggref(StringInfo str, Aggref* node) WRITE_CHAR_FIELD(aggkind); WRITE_BOOL_FIELD(aggvariadic); WRITE_UINT_FIELD(agglevelsup); + if (t_thrd.proc->workingVersionNum >= KEEP_FUNC_VERSION_NUMBER) { + WRITE_BOOL_FIELD(aggiskeep); + WRITE_BOOL_FIELD(aggkpfirst); + } WRITE_LOCATION_FIELD(location); WRITE_TYPEINFO_FIELD(aggtype); @@ -2630,6 +2634,11 @@ static void _outWindowFunc(StringInfo str, WindowFunc* node) WRITE_UINT_FIELD(winref); WRITE_BOOL_FIELD(winstar); WRITE_BOOL_FIELD(winagg); + if (t_thrd.proc->workingVersionNum >= KEEP_FUNC_VERSION_NUMBER) { + WRITE_NODE_FIELD(keep_args); + WRITE_NODE_FIELD(winkporder); + WRITE_BOOL_FIELD(winkpfirst); + } WRITE_LOCATION_FIELD(location); WRITE_TYPEINFO_FIELD(wintype); @@ -4326,6 +4335,9 @@ static void _outFuncCall(StringInfo str, FuncCall* node) if (t_thrd.proc->workingVersionNum >= ROTATE_UNROTATE_VERSION_NUM) { WRITE_NODE_FIELD(agg_filter); } + if (t_thrd.proc->workingVersionNum >= KEEP_FUNC_VERSION_NUMBER) { + WRITE_NODE_FIELD(aggKeep); + } WRITE_BOOL_FIELD(agg_within_group); WRITE_BOOL_FIELD(agg_star); WRITE_BOOL_FIELD(agg_distinct); @@ -6159,6 +6171,14 @@ static void _outVecRemoteQuery(StringInfo str, VecRemoteQuery* node) WRITE_NODE_TYPE("VECREMOTEQUERY"); _outCommonRemoteQueryPart(str, node); } +static void _outKeepClause(StringInfo str, KeepClause* node) +{ + WRITE_NODE_TYPE("KEEP"); + + WRITE_BOOL_FIELD(rank_first); + WRITE_NODE_FIELD(keep_order); + WRITE_LOCATION_FIELD(location); +} #endif static void _outVecWindowAgg(StringInfo str, VecWindowAgg* node) @@ -7237,6 +7257,9 @@ static void _outNode(StringInfo str, const void* obj) case T_UnrotateInCell: _outUnrotateInCell(str, (UnrotateInCell*)obj); break; + case T_KeepClause: + _outKeepClause(str, (KeepClause *)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 b811d21b5..73913203e 100755 --- a/src/common/backend/nodes/readfuncs.cpp +++ b/src/common/backend/nodes/readfuncs.cpp @@ -2312,6 +2312,14 @@ static Aggref* _readAggref(void) READ_BOOL_FIELD(aggvariadic); } READ_UINT_FIELD(agglevelsup); + IF_EXIST(aggiskeep) + { + READ_BOOL_FIELD(aggiskeep); + } + IF_EXIST(aggkpfirst) + { + READ_BOOL_FIELD(aggkpfirst); + } READ_LOCATION_FIELD(location); READ_TYPEINFO_FIELD(aggtype); READ_TYPEINFO_FIELD(aggtrantype); @@ -2372,6 +2380,15 @@ static WindowFunc* _readWindowFunc(void) READ_UINT_FIELD(winref); READ_BOOL_FIELD(winstar); READ_BOOL_FIELD(winagg); + IF_EXIST(keep_args) { + READ_NODE_FIELD(keep_args); + } + IF_EXIST(winkporder) { + READ_NODE_FIELD(winkporder); + } + IF_EXIST(winkpfirst) { + READ_BOOL_FIELD(winkpfirst); + } READ_LOCATION_FIELD(location); READ_TYPEINFO_FIELD(wintype); diff --git a/src/common/backend/parser/gram.y b/src/common/backend/parser/gram.y index 198bbabda..fd5186832 100644 --- a/src/common/backend/parser/gram.y +++ b/src/common/backend/parser/gram.y @@ -342,6 +342,7 @@ static char* IdentResolveToChar(char *ident, core_yyscan_t yyscanner); DefElem *defelt; SortBy *sortby; WindowDef *windef; + KeepClause *keep; JoinExpr *jexpr; IndexElem *ielem; Alias *alias; @@ -761,6 +762,7 @@ static char* IdentResolveToChar(char *ident, core_yyscan_t yyscanner); %type func_application func_with_separator func_expr_common_subexpr index_functional_expr_key func_application_special functime_app %type func_expr func_expr_windowless %type common_table_expr +%type keep_clause %type with_clause opt_with_clause %type cte_list @@ -931,7 +933,7 @@ static char* IdentResolveToChar(char *ident, core_yyscan_t yyscanner); SHRINK USE_P DATA_P DATABASE DATAFILE DATANODE DATANODES DATATYPE_CL DATE_P DATE_FORMAT_P DAY_P DAY_HOUR_P DAY_MINUTE_P DAY_SECOND_P DBCOMPATIBILITY_P DEALLOCATE DEC DECIMAL_P DECLARE DECODE DEFAULT DEFAULTS - DEFERRABLE DEFERRED DEFINER DELETE_P DELIMITER DELIMITERS DELTA DELTAMERGE DESC DETERMINISTIC + DEFERRABLE DEFERRED DEFINER DELETE_P DELIMITER DELIMITERS DELTA DELTAMERGE DENSE_RANK DESC DETERMINISTIC /* PGXC_BEGIN */ DIAGNOSTICS DICTIONARY DIRECT DIRECTORY DISABLE_P DISCARD DISTINCT DISTRIBUTE DISTRIBUTION DO DOCUMENT_P DOMAIN_P DOUBLE_P /* PGXC_END */ @@ -957,7 +959,7 @@ static char* IdentResolveToChar(char *ident, core_yyscan_t yyscanner); JOIN - KEY KILL KEY_PATH KEY_STORE + KEEP KEY KILL KEY_PATH KEY_STORE LABEL LANGUAGE LARGE_P LAST_P LATERAL_P LC_COLLATE_P LC_CTYPE_P LEADING LEAKPROOF LINES LEAST LESS LEFT LEVEL LIKE LIMIT LIST LISTEN LOAD LOCAL LOCALTIME LOCALTIMESTAMP @@ -28844,7 +28846,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 filter_clause over_clause +func_expr: func_application within_group_clause filter_clause keep_clause over_clause { FuncCall *n = (FuncCall *) $1; @@ -28894,8 +28896,16 @@ func_expr: func_application within_group_clause filter_clause over_clause } } n->agg_order = $2; - - WindowDef *wd = (WindowDef*) $4; + if ($4 != NULL) + { + const char* message = "KEEP for this function is not supported."; + InsertErrorMessage(message, u_sess->plsql_cxt.plpgsql_yylloc); + ereport(errstate, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("KEEP for this function is not supported."), + parser_errposition(@2))); + } + WindowDef *wd = (WindowDef*) $5; if (wd != NULL) wd->frameOptions = FRAMEOPTION_NONDEFAULT | FRAMEOPTION_ROWS | FRAMEOPTION_START_UNBOUNDED_PRECEDING | @@ -28932,13 +28942,31 @@ func_expr: func_application within_group_clause filter_clause over_clause n->agg_order = $2; n->agg_within_group = TRUE; n->agg_filter = $3; - n->over = $4; + n->over = $5; $$ = (Node *) n; } else { n->agg_filter = $3; - n->over = $4; + n->aggKeep = $4; + if ($4 != NULL && + pg_strcasecmp(strVal(linitial(n->funcname)), "MIN") != 0 && + pg_strcasecmp(strVal(linitial(n->funcname)), "MAX") != 0 && + pg_strcasecmp(strVal(linitial(n->funcname)), "SUM") != 0 && + pg_strcasecmp(strVal(linitial(n->funcname)), "AVG") != 0 && + pg_strcasecmp(strVal(linitial(n->funcname)), "COUNT") != 0 && + pg_strcasecmp(strVal(linitial(n->funcname)), "VARIANCE") != 0 && + pg_strcasecmp(strVal(linitial(n->funcname)), "STDDEV") != 0) + { + const char* message = "KEEP for this function is not supported."; + InsertErrorMessage(message, u_sess->plsql_cxt.plpgsql_yylloc); + ereport(errstate, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("KEEP for this function is not supported."), + parser_errposition(@2))); + } + n->over = $5; + } if (pg_strcasecmp(strVal(linitial(n->funcname)), "group_concat") == 0) { @@ -28949,7 +28977,7 @@ func_expr: func_application within_group_clause filter_clause over_clause (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("group_concat is not yet supported in distributed database."))); #endif - WindowDef *wd = (WindowDef*) $4; + WindowDef *wd = (WindowDef*) $5; if (wd != NULL) { ereport(errstate, (errcode(ERRCODE_SYNTAX_ERROR), @@ -29934,6 +29962,39 @@ xmlexists_argument: } ; +keep_clause: + KEEP '(' DENSE_RANK FIRST_P sort_clause ')' + { + if( u_sess->attr.attr_sql.sql_compatibility != A_FORMAT ) + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("keep clause is supported only in A_FORMAT database."))); + KeepClause *n = makeNode(KeepClause); + n->rank_first = true; + n->keep_order =$5; + n->location = @1; + + $$ = n; + } + | KEEP '(' DENSE_RANK LAST_P sort_clause ')' + { + if( u_sess->attr.attr_sql.sql_compatibility != A_FORMAT ) + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("keep clause is supported only in A_FORMAT database."))); + KeepClause *n = makeNode(KeepClause); + n->rank_first = false; + n->keep_order =$5; + n->location = @1; + + $$ = n; + } + | /*EMPTY*/ + { + $$ = NULL; + } + ; + /* * Aggregate decoration clauses */ @@ -31393,6 +31454,7 @@ unreserved_keyword: | DELIMITER | DELIMITERS | DELTA + | DENSE_RANK | DETERMINISTIC | DIAGNOSTICS | DICTIONARY @@ -31501,6 +31563,7 @@ unreserved_keyword: | IP | ISNULL | ISOLATION + | KEEP | KEY | KEY_PATH | KEY_STORE diff --git a/src/common/backend/parser/parse_collate.cpp b/src/common/backend/parser/parse_collate.cpp index 25197aa4c..8a9be8bab 100644 --- a/src/common/backend/parser/parse_collate.cpp +++ b/src/common/backend/parser/parse_collate.cpp @@ -701,6 +701,7 @@ static bool assign_collations_walker(Node* node, assign_collations_context* cont case T_WindowFunc: { /* We nned handle the aggfilter clause for WindowFunc */ (void)assign_collations_walker((Node*)((WindowFunc*)node)->args, &loccontext); + (void)assign_collations_walker((Node*)((WindowFunc*)node)->keep_args, &loccontext); } break; default: /* All child expressions contribute equally to loccontext. */ diff --git a/src/common/backend/parser/parse_func.cpp b/src/common/backend/parser/parse_func.cpp index e9eed9439..3e2aa1e25 100644 --- a/src/common/backend/parser/parse_func.cpp +++ b/src/common/backend/parser/parse_func.cpp @@ -77,6 +77,7 @@ Node* ParseFuncOrColumn(ParseState* pstate, List* funcname, List* fargs, Node* l bool agg_distinct = (fn ? fn->agg_distinct : false); bool func_variadic = (fn ? fn->func_variadic : false); WindowDef* over = (fn ? fn->over : NULL); + KeepClause *aggKeep = (fn ? fn->aggKeep : NULL); Oid rettype; int rettype_orig = -1; Oid funcid; @@ -371,6 +372,11 @@ Node* ParseFuncOrColumn(ParseState* pstate, List* funcname, List* fargs, Node* l (errcode(ERRCODE_WRONG_OBJECT_TYPE), errmsg("%s is not an ordered-set aggregate, so it cannot have WITHIN GROUP", name_string), parser_errposition(pstate, location))); + if (aggKeep != NULL && over != NULL && over->orderClause != NIL) + ereport(ERROR, + (errcode(ERRCODE_WRONG_OBJECT_TYPE), + errmsg("ORDER BY of OVER clause is prohibited in function %s with keep", name_string), + parser_errposition(pstate, location))); } } else if (fdresult == FUNCDETAIL_WINDOWFUNC) { /* @@ -575,7 +581,13 @@ Node* ParseFuncOrColumn(ParseState* pstate, List* funcname, List* fargs, Node* l (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("aggregates cannot use named arguments"), parser_errposition(pstate, location))); - + if (aggKeep != NULL) { + aggref->aggiskeep = true; + agg_order = aggKeep->keep_order; + aggref->aggkpfirst = aggKeep->rank_first; + } else { + aggref->aggiskeep = false; + } /* parse_agg.c does additional aggregate-specific processing */ transformAggregateCall(pstate, aggref, fargs, agg_order, agg_distinct); @@ -679,6 +691,26 @@ Node* ParseFuncOrColumn(ParseState* pstate, List* funcname, List* fargs, Node* l if (over->orderClause == NIL) over->orderClause = agg_order; + if (aggKeep != NULL) { + ListCell* lc = NULL; + List* tlist = NIL; + + wfunc->winkpfirst = aggKeep->rank_first; + + foreach (lc, aggKeep->keep_order) { + SortBy* arg = (SortBy*)lfirst(lc); + wfunc->keep_args = lappend(wfunc->keep_args, transformExprRecurse(pstate, arg->node)); + } + int save_next_resno = pstate->p_next_resno; + wfunc->winkporder = transformSortClause(pstate, + aggKeep->keep_order, + &tlist, + EXPR_KIND_ORDER_BY, + true, + true); + pstate->p_next_resno = save_next_resno; + list_free_deep(tlist); + } /* parse_agg.c does additional window-func-specific processing */ transformWindowFuncCall(pstate, wfunc, over); diff --git a/src/common/backend/utils/adt/ruleutils.cpp b/src/common/backend/utils/adt/ruleutils.cpp index ed24d8352..3c2e22ab2 100644 --- a/src/common/backend/utils/adt/ruleutils.cpp +++ b/src/common/backend/utils/adt/ruleutils.cpp @@ -11137,7 +11137,9 @@ static void get_agg_expr(Aggref* aggref, deparse_context* context) get_rule_expr((Node*)tle->expr, context, true); } } - + if (aggref->aggiskeep) { + appendStringInfo(buf, ") KEEP(DENSE_RANK %s", (aggref->aggkpfirst ? "FIRST" : "LAST")); + } if (aggref->aggorder != NIL) { Assert(funcname); if (pg_strcasecmp(funcname, "listagg") == 0) { @@ -11314,6 +11316,48 @@ static void construct_windowClause(deparse_context* context) context->windowTList = node->plan.targetlist; } +static void get_keep_orderby(List* argList, List* orderList, bool keepfirst, deparse_context* context) +{ + if (list_length(argList) <= 0) { + return; + } + StringInfo buf = context->buf; + const char* sep = NULL; + ListCell* arg_l = NULL, *arg_lc; + appendStringInfo(buf, ") KEEP(DENSE_RANK %s ORDER BY ", (keepfirst ? "FIRST" : "LAST")); + sep = ""; + forboth (arg_lc, argList, arg_l, orderList) { + SortGroupClause* srt = (SortGroupClause*)lfirst(arg_l); + Node *arg = (Node*)lfirst(arg_lc); + Oid sortcoltype; + TypeCacheEntry* typentry = NULL; + + appendStringInfoString(buf, sep); + get_rule_expr(arg, context, true); + sortcoltype = exprType(arg); + /* See whether operator is default < or > for datatype */ + typentry = lookup_type_cache(sortcoltype, TYPECACHE_LT_OPR | TYPECACHE_GT_OPR); + if (srt->sortop == typentry->lt_opr) { + /* ASC is default, so emit nothing for it */ + if (srt->nulls_first) + appendStringInfo(buf, " NULLS FIRST"); + } else if (srt->sortop == typentry->gt_opr) { + appendStringInfo(buf, " DESC"); + /* DESC defaults to NULLS FIRST */ + if (!srt->nulls_first) + appendStringInfo(buf, " NULLS LAST"); + } else { + appendStringInfo(buf, " USING %s", generate_operator_name(srt->sortop, sortcoltype, sortcoltype)); + /* be specific to eliminate ambiguity */ + if (srt->nulls_first) + appendStringInfo(buf, " NULLS FIRST"); + else + appendStringInfo(buf, " NULLS LAST"); + } + sep = ", "; + } +} + /* * get_windowfunc_expr - Parse back a WindowFunc node */ @@ -11323,14 +11367,14 @@ static void get_windowfunc_expr(WindowFunc* wfunc, deparse_context* context) Oid argtypes[FUNC_MAX_ARGS]; int nargs; List* argnames = NIL; - ListCell* l = NULL; + ListCell* arg_l = NULL; char* funcname = NULL; if (list_length(wfunc->args) > FUNC_MAX_ARGS) ereport(ERROR, (errcode(ERRCODE_TOO_MANY_ARGUMENTS), errmsg("too many arguments"))); nargs = 0; - foreach (l, wfunc->args) { - Node* arg = (Node*)lfirst(l); + foreach (arg_l, wfunc->args) { + Node* arg = (Node*)lfirst(arg_l); if (IsA(arg, NamedArgExpr)) argnames = lappend(argnames, ((NamedArgExpr*)arg)->name); @@ -11347,6 +11391,8 @@ static void get_windowfunc_expr(WindowFunc* wfunc, deparse_context* context) else get_rule_expr((Node*)wfunc->args, context, true); + get_keep_orderby(wfunc->keep_args, wfunc->winkporder, wfunc->winkpfirst, context); + Assert(funcname); if (pg_strcasecmp(funcname, "listagg") == 0) appendStringInfoString(buf, ") WITHIN GROUP "); @@ -11355,8 +11401,8 @@ static void get_windowfunc_expr(WindowFunc* wfunc, deparse_context* context) construct_windowClause(context); - foreach (l, context->windowClause) { - WindowClause* wc = (WindowClause*)lfirst(l); + foreach (arg_l, context->windowClause) { + WindowClause* wc = (WindowClause*)lfirst(arg_l); if (wc->winref == wfunc->winref) { if (wc->name) @@ -11370,7 +11416,7 @@ static void get_windowfunc_expr(WindowFunc* wfunc, deparse_context* context) break; } } - if (l == NULL) { + if (arg_l == NULL) { if (context->windowClause != NULL) ereport(ERROR, (errmodule(MOD_OPT), (errcode(ERRCODE_WINDOWING_ERROR), errmsg("could not find window clause for winref %u", wfunc->winref)))); diff --git a/src/common/backend/utils/init/globals.cpp b/src/common/backend/utils/init/globals.cpp index 6ee5a199a..fe5b0b947 100644 --- a/src/common/backend/utils/init/globals.cpp +++ b/src/common/backend/utils/init/globals.cpp @@ -77,12 +77,13 @@ bool will_shutdown = false; * ********************************************/ -const uint32 GRAND_VERSION_NUM = 93013; +const uint32 GRAND_VERSION_NUM = 93014; /******************************************** * 2.VERSION NUM FOR EACH FEATURE * Please write indescending order. ********************************************/ +const uint32 KEEP_FUNC_VERSION_NUMBER = 93014; const uint32 PUBLIC_SYNONYM_VERSION_NUMBER = 93013; const uint32 FETCH_ENHANCE_VERSION_NUM = 93012; const uint32 APPLY_JOIN_VERSION_NUMBER = 93011; diff --git a/src/common/backend/utils/sort/tuplesort.cpp b/src/common/backend/utils/sort/tuplesort.cpp index 3a1133d74..ca3898c05 100644 --- a/src/common/backend/utils/sort/tuplesort.cpp +++ b/src/common/backend/utils/sort/tuplesort.cpp @@ -546,6 +546,14 @@ struct Tuplesortstate { #define WORKER(state) ((state)->shared && (state)->worker != -1) #define LEADER(state) ((state)->shared && (state)->worker == -1) +void* TuplesortGetSortkeys(Tuplesortstate* state) +{ + return (state->onlyKey ? state->onlyKey : state->sortKeys); +} +int TuplesortGetNsortkey(Tuplesortstate* state) +{ + return state->nKeys; +} /* Check if system in status of lacking memory */ static bool LACKMEM(Tuplesortstate* state) { diff --git a/src/common/interfaces/libpq/frontend_parser/gram.y b/src/common/interfaces/libpq/frontend_parser/gram.y index 50648396d..5a0690ce5 100755 --- a/src/common/interfaces/libpq/frontend_parser/gram.y +++ b/src/common/interfaces/libpq/frontend_parser/gram.y @@ -209,6 +209,7 @@ extern THR_LOCAL bool stmt_contains_operator_plus; DefElem *defelt; SortBy *sortby; WindowDef *windef; + KeepClause *keep; JoinExpr *jexpr; IndexElem *ielem; Alias *alias; @@ -429,6 +430,7 @@ extern THR_LOCAL bool stmt_contains_operator_plus; %type func_application func_expr_common_subexpr %type common_table_expr +%type keep_clause %type with_clause opt_with_clause %type cte_list @@ -541,7 +543,7 @@ extern THR_LOCAL bool stmt_contains_operator_plus; SHRINK USE_P DATA_P DATABASE DATAFILE DATANODE DATANODES DATATYPE_CL DATE_P DATE_FORMAT_P DAY_P DAY_HOUR_P DAY_MINUTE_P DAY_SECOND_P DBCOMPATIBILITY_P DEALLOCATE DEC DECIMAL_P DECLARE DECODE DEFAULT DEFAULTS - DEFERRABLE DEFERRED DEFINER DELETE_P DELIMITER DELIMITERS DELTA DELTAMERGE DESC DETERMINISTIC + DEFERRABLE DEFERRED DEFINER DELETE_P DELIMITER DELIMITERS DELTA DELTAMERGE DENSE_RANK DESC DETERMINISTIC /* PGXC_BEGIN */ DIAGNOSTICS DICTIONARY DIRECT DIRECTORY DISABLE_P DISCARD DISTINCT DISTRIBUTE DISTRIBUTION DO DOCUMENT_P DOMAIN_P DOUBLE_P /* PGXC_END */ @@ -567,7 +569,7 @@ extern THR_LOCAL bool stmt_contains_operator_plus; JOIN - KEY KILL KEY_PATH KEY_STORE + KEEP KEY KILL KEY_PATH KEY_STORE LABEL LANGUAGE LARGE_P LAST_P LATERAL_P LC_COLLATE_P LC_CTYPE_P LEADING LEAKPROOF LINES LEAST LESS LEFT LEVEL LIST LIKE LIMIT LISTEN LOAD LOCAL LOCALTIME LOCALTIMESTAMP @@ -11825,6 +11827,7 @@ unreserved_keyword: | DELIMITER | DELIMITERS | DELTA + | DENSE_RANK | DETERMINISTIC | DICTIONARY | DIRECT @@ -11920,6 +11923,7 @@ unreserved_keyword: | INVOKER | IP | ISOLATION + | KEEP | KEY | KEY_PATH | KEY_STORE diff --git a/src/gausskernel/cbb/instruments/utils/unique_query.cpp b/src/gausskernel/cbb/instruments/utils/unique_query.cpp index d770bf79b..741da81d2 100755 --- a/src/gausskernel/cbb/instruments/utils/unique_query.cpp +++ b/src/gausskernel/cbb/instruments/utils/unique_query.cpp @@ -485,6 +485,8 @@ void UniqueSql::JumbleExpr(pgssJumbleState* jstate, Node* node) APP_JUMB(expr->winfnoid); APP_JUMB(expr->winref); UniqueSql::JumbleExpr(jstate, (Node*)expr->args); + UniqueSql::JumbleExpr(jstate, (Node*)expr->keep_args); + UniqueSql::JumbleExpr(jstate, (Node*)expr->winkporder); break; } case T_InitList: { diff --git a/src/gausskernel/optimizer/commands/tablecmds.cpp b/src/gausskernel/optimizer/commands/tablecmds.cpp index d0a615335..06f749e6e 100755 --- a/src/gausskernel/optimizer/commands/tablecmds.cpp +++ b/src/gausskernel/optimizer/commands/tablecmds.cpp @@ -11571,6 +11571,9 @@ static Node *UpdateVarattnoAfterAddColumn(Node *node, int startattnum, int endat List *expr_args = (List *)UpdateVarattnoAfterAddColumn((Node *)expr->args, startattnum, endattnum, is_increase); newexpr->args = expr_args; + List *expr_keep_args = (List *)UpdateVarattnoAfterAddColumn((Node *)expr->keep_args, + startattnum, endattnum, is_increase); + newexpr->keep_args = expr_keep_args; return (Node *)newexpr; } case T_ArrayCoerceExpr: { diff --git a/src/gausskernel/optimizer/sqladvisor/sqladvisor_extract.cpp b/src/gausskernel/optimizer/sqladvisor/sqladvisor_extract.cpp index 9ff94dccf..e805db9c5 100644 --- a/src/gausskernel/optimizer/sqladvisor/sqladvisor_extract.cpp +++ b/src/gausskernel/optimizer/sqladvisor/sqladvisor_extract.cpp @@ -689,6 +689,7 @@ static List* extractVecSubplan(Expr* node, List* resSubplan, List* subplans) case T_WindowFunc: { WindowFunc* wfunc = (WindowFunc*)node; resSubplan = extractVecSubplan((Expr*)wfunc->args, resSubplan, subplans); + resSubplan = extractVecSubplan((Expr*)wfunc->keep_args, resSubplan, subplans); } break; case T_FuncExpr: { FuncExpr* funcexpr = (FuncExpr*)node; @@ -882,6 +883,7 @@ static List* extractSubplan(Expr* node, List* resSubplan, List* subplans) case T_WindowFunc: { WindowFunc* wfunc = (WindowFunc*)node; resSubplan = extractSubplan((Expr*)wfunc->args, resSubplan, subplans); + resSubplan = extractSubplan((Expr*)wfunc->keep_args, resSubplan, subplans); } break; case T_ArrayRef: { ArrayRef* aref = (ArrayRef*)node; diff --git a/src/gausskernel/optimizer/util/clauses.cpp b/src/gausskernel/optimizer/util/clauses.cpp index cb8840923..94897a76e 100644 --- a/src/gausskernel/optimizer/util/clauses.cpp +++ b/src/gausskernel/optimizer/util/clauses.cpp @@ -2594,6 +2594,11 @@ Node* eval_const_expressions_mutator(Node* node, eval_const_expressions_context* newexpr->winref = expr->winref; newexpr->winstar = expr->winstar; newexpr->winagg = expr->winagg; + newexpr->keep_args = (List*)expression_tree_mutator( + (Node*)expr->keep_args, (Node* (*)(Node*, void*)) eval_const_expressions_mutator, (void*)context); + newexpr->winkporder = (List*)expression_tree_mutator( + (Node*)expr->winkporder, (Node* (*)(Node*, void*)) eval_const_expressions_mutator, (void*)context); + newexpr->winkpfirst = expr->winkpfirst; newexpr->location = expr->location; return (Node*)newexpr; diff --git a/src/gausskernel/runtime/executor/execQual.cpp b/src/gausskernel/runtime/executor/execQual.cpp index 00a09a264..e860482e3 100644 --- a/src/gausskernel/runtime/executor/execQual.cpp +++ b/src/gausskernel/runtime/executor/execQual.cpp @@ -1233,6 +1233,10 @@ static void find_uservar_in_expr(ExprState *root, char *return_name, bool *if_us ExprState* child =(ExprState*)lfirst(arg); find_uservar_in_expr(child, return_name, if_use); } + foreach(arg, parent->keep_args) { + ExprState* child =(ExprState*)lfirst(arg); + find_uservar_in_expr(child, return_name, if_use); + } } break; case T_BoolExprState: { BoolExprState* parent = (BoolExprState*)root; @@ -6131,7 +6135,9 @@ ExprState* ExecInitExprByRecursion(Expr* node, PlanState* parent) if (wfunc->winagg) winstate->numaggs++; - wfstate->args = (List*)ExecInitExprByRecursion((Expr*)wfunc->args, parent); + wfstate->keep_args = (List*)ExecInitExprByRecursion((Expr*)wfunc->keep_args, parent); + wfstate->keep_first = wfunc->winkpfirst; + wfstate->args = (List*)ExecInitExprByRecursion((Expr*)wfunc->args, parent); /* * Complain if the windowfunc's arguments contain any diff --git a/src/gausskernel/runtime/executor/nodeAgg.cpp b/src/gausskernel/runtime/executor/nodeAgg.cpp index eb610fd67..ebcb71028 100644 --- a/src/gausskernel/runtime/executor/nodeAgg.cpp +++ b/src/gausskernel/runtime/executor/nodeAgg.cpp @@ -146,6 +146,7 @@ #include "utils/tuplesort.h" #include "utils/datum.h" #include "utils/memprot.h" +#include "utils/sortsupport_gs.h" #include "workload/workload.h" static TupleTableSlot* ExecAgg(PlanState* state); @@ -332,7 +333,7 @@ static TupleTableSlot* fetch_input_tuple(AggState* aggstate) static void initialize_aggregate(AggState* aggstate, AggStatePerAgg peraggstate, AggStatePerGroup pergroupstate) { Plan* plan = aggstate->ss.ps.plan; - int64 local_work_mem = SET_NODEMEM(plan->operatorMemKB[0], plan->dop); + int64 localWorkMem = SET_NODEMEM(plan->operatorMemKB[0], plan->dop); int64 max_mem = (plan->operatorMaxMem > 0) ? SET_NODEMEM(plan->operatorMaxMem, plan->dop) : 0; /* @@ -357,7 +358,7 @@ static void initialize_aggregate(AggState* aggstate, AggStatePerAgg peraggstate, peraggstate->sortOperators[0], peraggstate->sortCollations[0], peraggstate->sortNullsFirst[0], - local_work_mem, + localWorkMem, false); } else { peraggstate->sortstates[aggstate->current_set] = tuplesort_begin_heap(peraggstate->sortdesc, @@ -366,7 +367,7 @@ static void initialize_aggregate(AggState* aggstate, AggStatePerAgg peraggstate, peraggstate->sortOperators, peraggstate->sortCollations, peraggstate->sortNullsFirst, - local_work_mem, + localWorkMem, false, max_mem, plan->plan_node_id, @@ -430,6 +431,8 @@ static void initialize_aggregate(AggState* aggstate, AggStatePerAgg peraggstate, */ pergroupstate->noCollectValue = peraggstate->initCollectValueIsNull; #endif /* PGXC */ + if (peraggstate->keep_slot) + ExecClearTuple(peraggstate->keep_slot[aggstate->current_set]); } /* @@ -677,6 +680,138 @@ static void advance_collection_function( } #endif /* PGXC */ +static bool ExecKeepDatum(AggState* aggstate, AggStatePerAgg peraggstate, int setno, TupleTableSlot *cur_slot) +{ + TupleTableSlot *preSlot; + int inputoff = peraggstate->inputoff; + bool replace = false; + bool keep = false; + + if (!peraggstate->is_keep) { + return true; + } + Plan* plan = aggstate->ss.ps.plan; + int64 localWorkMem = SET_NODEMEM(plan->operatorMemKB[0], plan->dop); + SortSupport sortKey = (SortSupport)TuplesortGetSortkeys(peraggstate->sortstates[setno]); + + preSlot = peraggstate->keep_slot[setno]; + if (TupIsNull(preSlot)) { + keep = replace = true; + } else { + int compare; + if (sortKey->abbrev_converter) { + compare = ApplySortAbbrevFullComparator(preSlot->tts_values[0], preSlot->tts_isnull[0], + cur_slot->tts_values[inputoff], cur_slot->tts_isnull[inputoff], + sortKey); + } else { + compare = ApplySortComparator(preSlot->tts_values[0], preSlot->tts_isnull[0], + cur_slot->tts_values[inputoff], cur_slot->tts_isnull[inputoff], sortKey); + } + + if (compare == 0) { + keep = true; + } else if ((compare > 0) == peraggstate->aggref->aggkpfirst) { + tuplesort_end(peraggstate->sortstates[setno]); + peraggstate->sortstates[aggstate->current_set] = + tuplesort_begin_datum(peraggstate->sortdesc->attrs[0].atttypid, + peraggstate->sortOperators[0], peraggstate->sortCollations[0], peraggstate->sortNullsFirst[0], + localWorkMem, false); + replace = keep = true; + } + } + + if (replace) { + int errorno; + ExecClearTuple(preSlot); + errorno = memcpy_s(preSlot->tts_values, peraggstate->numInputs * sizeof(Datum), + &cur_slot->tts_values[inputoff], peraggstate->numInputs * sizeof(Datum)); + securec_check(errorno, "\0", "\0"); + errorno = memcpy_s(preSlot->tts_isnull, peraggstate->numInputs * sizeof(bool), + &cur_slot->tts_isnull[inputoff], peraggstate->numInputs * sizeof(bool)); + securec_check(errorno, "\0", "\0"); + preSlot->tts_nvalid = peraggstate->numInputs; + ExecStoreVirtualTuple(preSlot); + } + return keep; +} + +static bool ExecKeepTuple(AggState* aggstate, AggStatePerAgg peraggstate, int setno, TupleTableSlot *cur_slot) +{ + TupleTableSlot *preSlot; + int i, result; + Datum datum1, datum2; + bool isNull1, isNull2; + bool replace = false; + bool keep = false; + + if (!peraggstate->is_keep) { + return true; + } + Plan* plan = aggstate->ss.ps.plan; + int64 localWorkMem = SET_NODEMEM(plan->operatorMemKB[0], plan->dop); + int64 max_mem = (plan->operatorMaxMem > 0) ? SET_NODEMEM(plan->operatorMaxMem, plan->dop) : 0; + SortSupport sortKey = (SortSupport)TuplesortGetSortkeys(peraggstate->sortstates[setno]); + int nSortKey = TuplesortGetNsortkey(peraggstate->sortstates[setno]); + preSlot = peraggstate->keep_slot[setno]; + if (TupIsNull(preSlot)) { + keep = replace = true; + } else { + for (i = 0; i < nSortKey; i++, sortKey++) { + datum1 = tableam_tslot_getattr(preSlot, sortKey->ssup_attno, &isNull1); + datum2 = tableam_tslot_getattr(cur_slot, sortKey->ssup_attno, &isNull2); + if (sortKey->abbrev_converter) { + result = ApplySortAbbrevFullComparator(datum1, isNull1, datum2, isNull2, sortKey); + } else { + result = ApplySortComparator(datum1, isNull1, datum2, isNull2, sortKey); + } + if (result != 0) { + break; + } + } + if (result == 0) { + keep = true; + } else if ((result > 0) == peraggstate->aggref->aggkpfirst) { + tuplesort_end(peraggstate->sortstates[setno]); + peraggstate->sortstates[setno] = tuplesort_begin_heap(peraggstate->sortdesc, + peraggstate->numSortCols, peraggstate->sortColIdx, peraggstate->sortOperators, + peraggstate->sortCollations, peraggstate->sortNullsFirst, localWorkMem, false, max_mem, + plan->plan_node_id, SET_DOP(plan->dop)); + replace = keep = true; + } + } + if (replace) { + ExecCopySlot(preSlot, cur_slot); + } + return keep; +} + +void processTuples( + AggState *aggstate, AggStatePerAgg peraggstate, int numGroupingSets, TupleTableSlot *slot, int inputoff) +{ + for (int setno = 0; setno < numGroupingSets; setno++) { + /* OK, put the tuple into the tuplesort object */ + if (peraggstate->numInputs == 1) { + if (ExecKeepDatum(aggstate, peraggstate, setno, slot)) { + tuplesort_putdatum(peraggstate->sortstates[setno], slot->tts_values[inputoff], + slot->tts_isnull[inputoff]); + } + } else { + errno_t errorno = EOK; + if (ExecKeepTuple(aggstate, peraggstate, setno, slot)) { + ExecClearTuple(peraggstate->sortslot); + errorno = memcpy_s(peraggstate->sortslot->tts_values, peraggstate->numInputs * sizeof(Datum), + &slot->tts_values[inputoff], peraggstate->numInputs * sizeof(Datum)); + securec_check(errorno, "\0", "\0"); + errorno = memcpy_s(peraggstate->sortslot->tts_isnull, peraggstate->numInputs * sizeof(bool), + &slot->tts_isnull[inputoff], peraggstate->numInputs * sizeof(bool)); + securec_check(errorno, "\0", "\0"); + peraggstate->sortslot->tts_nvalid = peraggstate->numInputs; + ExecStoreVirtualTuple(peraggstate->sortslot); + tuplesort_puttupleslot(peraggstate->sortstates[setno], peraggstate->sortslot); + } + } + } +} /* * Advance all the aggregates for one input tuple. The input tuple * has been stored in tmpcontext->ecxt_outertuple, so that it is accessible @@ -726,30 +861,10 @@ static void advance_aggregates(AggState* aggstate, AggStatePerGroup pergroup) if (slot->tts_isnull[i + inputoff]) break; } - if (i < numTransInputs) + if (i < numTransInputs && !peraggstate->is_keep) continue; } - - for (setno = 0; setno < numGroupingSets; setno++) { - - /* OK, put the tuple into the tuplesort object */ - if (peraggstate->numInputs == 1) - tuplesort_putdatum(peraggstate->sortstates[setno], slot->tts_values[inputoff], - slot->tts_isnull[inputoff]); - else { - errno_t errorno = EOK; - ExecClearTuple(peraggstate->sortslot); - errorno = memcpy_s(peraggstate->sortslot->tts_values, peraggstate->numInputs * sizeof(Datum), - &slot->tts_values[inputoff], peraggstate->numInputs * sizeof(Datum)); - securec_check(errorno, "\0", "\0"); - errorno = memcpy_s(peraggstate->sortslot->tts_isnull, peraggstate->numInputs * sizeof(bool), - &slot->tts_isnull[inputoff], peraggstate->numInputs * sizeof(bool)); - securec_check(errorno, "\0", "\0"); - peraggstate->sortslot->tts_nvalid = peraggstate->numInputs; - ExecStoreVirtualTuple(peraggstate->sortslot); - tuplesort_puttupleslot(peraggstate->sortstates[setno], peraggstate->sortslot); - } - } + processTuples(aggstate, peraggstate, numGroupingSets, slot, inputoff); } else { /* We can apply the transition function immediately */ FunctionCallInfo fcinfo = &peraggstate->transfn_fcinfo; @@ -2396,6 +2511,18 @@ AggState* ExecInitAgg(Agg* node, EState* estate, int eflags) eflags &= ~EXEC_FLAG_REWIND; outerPlan = outerPlan(node); + foreach (l, aggstate->aggs) { + AggrefExprState* aggrefstate = (AggrefExprState*)lfirst(l); + Aggref* aggref = (Aggref*)aggrefstate->xprstate.expr; + + if (aggref->aggiskeep && (nodeTag(outerPlan) == T_VecToRow + || u_sess->attr.attr_sql.vectorEngineStrategy == FORCE_VECTOR_ENGINE)) { + ereport(ERROR, + (errmodule(MOD_VEC_EXECUTOR), + errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("keep feature for vector executor is not implemented"))); + } + } outerPlanState(aggstate) = ExecInitNode(outerPlan, estate, eflags); if (node->aggstrategy == AGG_SORT_GROUP) { SortGroupState* srotGroup = (SortGroupState*) outerPlanState(aggstate); @@ -2447,7 +2574,7 @@ AggState* ExecInitAgg(Agg* node, EState* estate, int eflags) Agg* aggnode = NULL; Sort* sortnode = NULL; SortGroup* sortGroupNode = NULL; - int num_sets; + int numSets = 0; if (phase > 0) { aggnode = (Agg*)list_nth(node->chain, phase - 1); @@ -2462,11 +2589,11 @@ AggState* ExecInitAgg(Agg* node, EState* estate, int eflags) aggnode = node; } - phasedata->numsets = num_sets = list_length(aggnode->groupingSets); + phasedata->numsets = numSets = list_length(aggnode->groupingSets); - if (num_sets) { - phasedata->gset_lengths = (int*)palloc(num_sets * sizeof(int)); - phasedata->grouped_cols = (Bitmapset**)palloc(num_sets * sizeof(Bitmapset*)); + if (numSets) { + phasedata->gset_lengths = (int*)palloc(numSets * sizeof(int)); + phasedata->grouped_cols = (Bitmapset**)palloc(numSets * sizeof(Bitmapset*)); i = 0; foreach (l, aggnode->groupingSets) { @@ -3239,7 +3366,7 @@ static void initialize_aggregate_flattened(AggState *aggstate, AggStatePerTrans AggStatePerGroup pergroupstate) { Plan *plan = aggstate->ss.ps.plan; - int64 local_work_mem = SET_NODEMEM(plan->operatorMemKB[0], plan->dop); + int64 localWorkMem = SET_NODEMEM(plan->operatorMemKB[0], plan->dop); int64 max_mem = (plan->operatorMaxMem > 0) ? SET_NODEMEM(plan->operatorMaxMem, plan->dop) : 0; if (pertrans->numSortCols > 0) { @@ -3250,12 +3377,12 @@ static void initialize_aggregate_flattened(AggState *aggstate, AggStatePerTrans if (pertrans->numInputs == 1) { pertrans->sortstates[aggstate->current_set] = tuplesort_begin_datum(pertrans->sortdesc->attrs[0].atttypid, pertrans->sortOperators[0], - pertrans->sortCollations[0], pertrans->sortNullsFirst[0], local_work_mem, false); + pertrans->sortCollations[0], pertrans->sortNullsFirst[0], localWorkMem, false); } else { pertrans->sortstates[aggstate->current_set] = tuplesort_begin_heap(pertrans->sortdesc, pertrans->numSortCols, pertrans->sortColIdx, pertrans->sortOperators, pertrans->sortCollations, pertrans->sortNullsFirst, - local_work_mem, false, max_mem, plan->plan_node_id, SET_DOP(plan->dop)); + localWorkMem, false, max_mem, plan->plan_node_id, SET_DOP(plan->dop)); } } @@ -4125,6 +4252,15 @@ static void exec_lookups_agg(AggState *aggstate, Agg *node, EState *estate) peraggstate->sortdesc = ExecTypeFromTL(aggref->args, false); peraggstate->sortslot = ExecInitExtraTupleSlot(estate); ExecSetSlotDescriptor(peraggstate->sortslot, peraggstate->sortdesc); + + if (aggref->aggiskeep) { + peraggstate->is_keep = true; + peraggstate->keep_slot = (TupleTableSlot**)palloc0(sizeof(TupleTableSlot*) * numGroupingSets); + for (int i = 0; i < numGroupingSets; i++) { + peraggstate->keep_slot[i] = ExecInitExtraTupleSlot(estate); + ExecSetSlotDescriptor(peraggstate->keep_slot[i], peraggstate->sortdesc); + } + } /* * We don't implement DISTINCT or ORDER BY aggs in the HASHED case * (yet) diff --git a/src/gausskernel/runtime/executor/nodeWindowAgg.cpp b/src/gausskernel/runtime/executor/nodeWindowAgg.cpp index ed806bc38..d684fc95f 100644 --- a/src/gausskernel/runtime/executor/nodeWindowAgg.cpp +++ b/src/gausskernel/runtime/executor/nodeWindowAgg.cpp @@ -50,6 +50,7 @@ #include "utils/lsyscache.h" #include "utils/memutils.h" #include "utils/syscache.h" +#include "utils/sortsupport_gs.h" #include "windowapi.h" #include "executor/node/nodeAgg.h" @@ -98,6 +99,111 @@ static void initialize_windowaggregate( peraggstate->transValueIsNull = peraggstate->initValueIsNull; peraggstate->noTransValue = peraggstate->initValueIsNull; peraggstate->resultValueIsNull = true; + peraggstate->keep_init = false; +} + +void updateAggregateState(WindowStatePerAgg peraggstate, Datum *values, bool *isnulls) +{ + for (int i = 0; i < peraggstate->numKeepCols; i++) { + KeepRank *keep_data = &peraggstate->keepRank[i]; + + if (!keep_data->keeptypeByVal && DatumGetPointer(values[i]) != DatumGetPointer(keep_data->keepValue) + && !keep_data->keepValueIsNull) { + pfree(DatumGetPointer(keep_data->keepValue)); + } + + keep_data->keepValue = values[i]; + keep_data->keepValueIsNull = isnulls[i]; + } + + if (!peraggstate->transValueIsNull && !peraggstate->transtypeByVal) { + pfree(DatumGetPointer(peraggstate->transValue)); + } +} +void initialize_peraggstate(WindowStatePerAgg peraggstate) +{ + if (peraggstate->initValueIsNull) { + peraggstate->transValue = peraggstate->initValue; + } else { + peraggstate->transValue = + datumCopy(peraggstate->initValue, peraggstate->transtypeByVal, peraggstate->transtypeLen); + } + + peraggstate->transValueIsNull = peraggstate->initValueIsNull; + peraggstate->noTransValue = peraggstate->initValueIsNull; +} + +void checkAndUpdateNeedAggregate( + int* needAgregate, WindowStatePerAgg peraggstate, KeepRank* rank_data, Datum curValue, bool curIsNull) +{ + if (rank_data->keepSort.abbrev_converter) + *needAgregate = ApplySortAbbrevFullComparator(rank_data->keepValue, + rank_data->keepValueIsNull, curValue, curIsNull, &rank_data->keepSort); + else + *needAgregate = ApplySortComparator(rank_data->keepValue, + rank_data->keepValueIsNull, curValue, curIsNull, &rank_data->keepSort); +} +static int keep_rank_windowaggregate( + WindowAggState* winstate, WindowStatePerAgg peraggstate, WindowFuncExprState* wfuncstate) +{ + int i = 0; + Datum curValue; + bool curIsNull; + int needAgregate = 0; + ListCell* lc; + ExprContext* econtext = winstate->tmpcontext; + + if (peraggstate->numKeepCols <= 0) { + return 0; + } + Datum* values = (Datum*) palloc0(sizeof(Datum) * peraggstate->numKeepCols); + bool* isnulls = (bool*) palloc0(sizeof(bool) * peraggstate->numKeepCols); + MemoryContext old_context = MemoryContextSwitchTo(winstate->aggcontext); + /* copy value */ + foreach(lc, wfuncstate->keep_args) + { + ExprState* arg_state = (ExprState*)lfirst(lc); + KeepRank* rank_data = &peraggstate->keepRank[i]; + curValue = ExecEvalExpr(arg_state, econtext, &curIsNull, NULL); + values[i] = datumCopy(curValue, rank_data->keeptypeByVal, rank_data->keeptypeLen); + isnulls[i] = curIsNull; + if (needAgregate == 0 && peraggstate->keep_init) { + checkAndUpdateNeedAggregate(&needAgregate, peraggstate, rank_data, curValue, curIsNull); + } + i++; + } + MemoryContextSwitchTo(old_context); + if (!peraggstate->keep_init) { + for (i = 0; i numKeepCols; i++) { + KeepRank* keep_data = &peraggstate->keepRank[i]; + /* set keep value */ + keep_data->keepValue = values[i]; + keep_data->keepValueIsNull = isnulls[i]; + } + peraggstate->keep_init = true; + } + if (needAgregate == 0) { + pfree(values); + pfree(isnulls); + return 0; + } + /* + * keep value < current value, dense_rank last, replace keep value + * keep value > current value, denase_rank first, replace keep value + */ + if ((needAgregate < 0) != wfuncstate->keep_first) { + updateAggregateState(peraggstate, values, isnulls); + /* reset transValue */ + old_context = MemoryContextSwitchTo(winstate->aggcontext); + initialize_peraggstate(peraggstate); + MemoryContextSwitchTo(old_context); + needAgregate = 0; + } else { + needAgregate = 1; + } + pfree(values); + pfree(isnulls); + return needAgregate; } /* @@ -117,6 +223,10 @@ static void advance_windowaggregate( MemoryContext old_context; ExprContext* econtext = winstate->tmpcontext; + if (keep_rank_windowaggregate(winstate, peraggstate, wfuncstate)) { + return; + } + old_context = MemoryContextSwitchTo(econtext->ecxt_per_tuple_memory); /* init the number of arguments to a function. */ @@ -1144,6 +1254,39 @@ restart: return result; } +/* setup sort op for keep order */ +static void initialize_peragg_keep(WindowFunc* wfunc, WindowStatePerAgg peraggstate) +{ + int i = 0; + int numKeepCols; + ListCell *sort_lc; + ListCell *arg_lc; + + numKeepCols = peraggstate->numKeepCols = list_length(wfunc->keep_args); + + Assert(list_length(wfunc->winkporder) == numKeepCols); + + peraggstate->keepRank = (KeepRank*)palloc0(sizeof(KeepRank) * numKeepCols); + forboth(sort_lc, wfunc->winkporder, arg_lc, wfunc->keep_args) { + const Node *arg = (Node*)lfirst(arg_lc); + SortGroupClause *sortcl = lfirst_node(SortGroupClause, sort_lc); + KeepRank* rank_data = &peraggstate->keepRank[i]; + SortSupport sortKey = &rank_data->keepSort; + + /* the parser should have made sure of this */ + Assert(OidIsValid(sortcl->sortop)); + + sortKey->ssup_attno = 0; /* unused */ + sortKey->ssup_collation = exprCollation(arg); + sortKey->ssup_nulls_first = sortcl->nulls_first; + sortKey->abbreviate = false; + sortKey->ssup_cxt = CurrentMemoryContext; + PrepareSortSupportFromOrderingOp(sortcl->sortop, sortKey); + get_typlenbyval(exprType(arg), &rank_data->keeptypeLen, &rank_data->keeptypeByVal); + i++; + } +} + /* ----------------- * ExecInitWindowAgg * @@ -1338,6 +1481,7 @@ WindowAggState* ExecInitWindowAgg(WindowAgg* node, EState* estate, int eflags) peraggstate = &winstate->peragg[aggno]; initialize_peragg(winstate, wfunc, peraggstate); peraggstate->wfuncno = wfuncno; + initialize_peragg_keep(wfunc, peraggstate); } else { WindowObject winobj = makeNode(WindowObjectData); diff --git a/src/gausskernel/runtime/vecexecutor/vecexpression.cpp b/src/gausskernel/runtime/vecexecutor/vecexpression.cpp index 1b8197347..7d2d9640d 100644 --- a/src/gausskernel/runtime/vecexecutor/vecexpression.cpp +++ b/src/gausskernel/runtime/vecexecutor/vecexpression.cpp @@ -2576,6 +2576,8 @@ ExprState* ExecInitVecExpr(Expr* node, PlanState* parent) if (wfunc->winagg) winstate->numaggs++; + wfstate->keep_args = (List*)ExecInitExpr((Expr*)wfunc->keep_args, parent); + wfstate->keep_first = wfunc->winkpfirst; wfstate->args = (List*)ExecInitVecExpr((Expr*)wfunc->args, parent); /* diff --git a/src/gausskernel/runtime/vecexecutor/vecnode/vecagg.cpp b/src/gausskernel/runtime/vecexecutor/vecnode/vecagg.cpp index 7d2a9973a..f701a5104 100644 --- a/src/gausskernel/runtime/vecexecutor/vecnode/vecagg.cpp +++ b/src/gausskernel/runtime/vecexecutor/vecnode/vecagg.cpp @@ -390,7 +390,11 @@ VecAggState* ExecInitVecAggregation(VecAgg* node, EState* estate, int eflags) Expr* finalfnexpr = NULL; Datum text_init_val; ListCell* lc = NULL; - + if (aggref->aggiskeep) + ereport(ERROR, + (errmodule(MOD_VEC_EXECUTOR), + errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("keep feature for vector executor is not implemented"))); /* Planner should have assigned aggregate to correct level */ Assert(aggref->agglevelsup == 0); diff --git a/src/gausskernel/runtime/vecexecutor/vecnode/vecwindowagg.cpp b/src/gausskernel/runtime/vecexecutor/vecnode/vecwindowagg.cpp index 0b0cd05d7..697aa508a 100644 --- a/src/gausskernel/runtime/vecexecutor/vecnode/vecwindowagg.cpp +++ b/src/gausskernel/runtime/vecexecutor/vecnode/vecwindowagg.cpp @@ -191,6 +191,11 @@ VecWindowAggState* ExecInitVecWindowAgg(VecWindowAgg* node, EState* estate, int WindowFunc* wfunc = (WindowFunc*)wfuncstate->xprstate.expr; WindowStatePerFunc perfuncstate; AclResult aclresult; + if (list_length(wfunc->keep_args) > 0) + ereport(ERROR, + (errmodule(MOD_VEC_EXECUTOR), + errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("keep feature for vector executor is not implemented"))); if (wfunc->winref != node->winref) /* planner screwed up? */ ereport(ERROR, diff --git a/src/include/executor/node/nodeAgg.h b/src/include/executor/node/nodeAgg.h index eaecc8f2c..a901dbc3e 100644 --- a/src/include/executor/node/nodeAgg.h +++ b/src/include/executor/node/nodeAgg.h @@ -195,6 +195,8 @@ typedef struct AggStatePerAggData { ProjectionInfo *evalproj; /* projection machinery */ ProjectionInfo *combinedproj; /* projection machinery */ TupleTableSlot *evalslot; /* current input tuple */ + TupleTableSlot **keep_slot; + bool is_keep; } AggStatePerAggData; /* diff --git a/src/include/miscadmin.h b/src/include/miscadmin.h index edc19b7b3..6a5b2e304 100644 --- a/src/include/miscadmin.h +++ b/src/include/miscadmin.h @@ -161,6 +161,7 @@ extern const uint32 MINMAXEXPR_CMPTYPE_VERSION_NUM; extern const uint32 CHARBYTE_SEMANTIC_VERSION_NUMBER; extern const uint32 APPLY_JOIN_VERSION_NUMBER; extern const uint32 PUBLIC_SYNONYM_VERSION_NUMBER; +extern const uint32 KEEP_FUNC_VERSION_NUMBER; extern void register_backend_version(uint32 backend_version); extern bool contain_backend_version(uint32 version_number); diff --git a/src/include/nodes/execnodes.h b/src/include/nodes/execnodes.h index 8f3f669a8..67feec616 100755 --- a/src/include/nodes/execnodes.h +++ b/src/include/nodes/execnodes.h @@ -909,6 +909,8 @@ typedef struct WindowFuncExprState { // Vectorized aggregation fields // ScalarVector* m_resultVector; + List *keep_args; + bool keep_first; } WindowFuncExprState; diff --git a/src/include/nodes/nodes.h b/src/include/nodes/nodes.h index c5d5b0822..e642641c5 100755 --- a/src/include/nodes/nodes.h +++ b/src/include/nodes/nodes.h @@ -915,7 +915,8 @@ typedef enum NodeTag { T_RotateClause = 6000, T_UnrotateClause, T_RotateInCell, - T_UnrotateInCell + T_UnrotateInCell, + T_KeepClause } NodeTag; diff --git a/src/include/nodes/parsenodes_common.h b/src/include/nodes/parsenodes_common.h index 4703562b0..42ca6c97f 100644 --- a/src/include/nodes/parsenodes_common.h +++ b/src/include/nodes/parsenodes_common.h @@ -1702,6 +1702,14 @@ typedef struct CommonTableExpr { StartWithOptions *swoptions; /* START WITH CONNECT BY options */ } CommonTableExpr; +typedef struct KeepClause +{ + NodeTag type; + bool rank_first; + List *keep_order; + int location; +} KeepClause; + /* * FuncCall - a function or aggregate invocation * @@ -1719,6 +1727,7 @@ typedef struct FuncCall { List *args; /* the arguments (list of exprs) */ List *agg_order; /* ORDER BY (list of SortBy) */ Node *agg_filter; + KeepClause *aggKeep; bool agg_within_group; bool agg_star; /* argument was really '*' */ bool agg_distinct; /* arguments were labeled DISTINCT */ diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h index 52fd0b6a4..0333e1c41 100644 --- a/src/include/nodes/primnodes.h +++ b/src/include/nodes/primnodes.h @@ -295,6 +295,8 @@ typedef struct Aggref { List* args; /* arguments and sort expressions */ List* aggorder; /* ORDER BY (list of SortGroupClause) */ List* aggdistinct; /* DISTINCT (list of SortGroupClause) */ + bool aggiskeep; + bool aggkpfirst; bool aggstar; /* TRUE if argument list was really '*' */ bool aggvariadic; /* true if variadic arguments have been * combined into an array last argument */ @@ -359,6 +361,9 @@ typedef struct WindowFunc { bool winstar; /* TRUE if argument list was really '*' */ bool winagg; /* is function a simple aggregate? */ int location; /* token location, or -1 if unknown */ + List* keep_args; + List* winkporder; + bool winkpfirst; #ifdef USE_SPQ bool windistinct; /* TRUE if it's agg(DISTINCT ...) */ #endif diff --git a/src/include/parser/kwlist.h b/src/include/parser/kwlist.h index 64e2c7f5e..369990430 100644 --- a/src/include/parser/kwlist.h +++ b/src/include/parser/kwlist.h @@ -205,6 +205,7 @@ PG_KEYWORD("delimiter", DELIMITER, UNRESERVED_KEYWORD) PG_KEYWORD("delimiters", DELIMITERS, UNRESERVED_KEYWORD) PG_KEYWORD("delta", DELTA, UNRESERVED_KEYWORD) PG_KEYWORD("deltamerge", DELTAMERGE, TYPE_FUNC_NAME_KEYWORD) +PG_KEYWORD("dense_rank", DENSE_RANK, UNRESERVED_KEYWORD) PG_KEYWORD("desc", DESC, RESERVED_KEYWORD) PG_KEYWORD("deterministic", DETERMINISTIC, UNRESERVED_KEYWORD) PG_KEYWORD("diagnostics", DIAGNOSTICS, UNRESERVED_KEYWORD) @@ -352,6 +353,7 @@ PG_KEYWORD("is", IS, RESERVED_KEYWORD) PG_KEYWORD("isnull", ISNULL, UNRESERVED_KEYWORD) PG_KEYWORD("isolation", ISOLATION, UNRESERVED_KEYWORD) PG_KEYWORD("join", JOIN, TYPE_FUNC_NAME_KEYWORD) +PG_KEYWORD("keep", KEEP, UNRESERVED_KEYWORD) PG_KEYWORD("key", KEY, UNRESERVED_KEYWORD) PG_KEYWORD("key_path", KEY_PATH, UNRESERVED_KEYWORD) PG_KEYWORD("key_store", KEY_STORE, UNRESERVED_KEYWORD) diff --git a/src/include/utils/tuplesort.h b/src/include/utils/tuplesort.h index f1f7dde48..326ecfddb 100644 --- a/src/include/utils/tuplesort.h +++ b/src/include/utils/tuplesort.h @@ -190,5 +190,6 @@ extern void UpdateUniqueSQLSortStats(Tuplesortstate* state, TimestampTz* start_t extern int64 tuplesort_get_peak_memory(Tuplesortstate* state); extern void tuplesort_workerfinish(Sharedsort *shared); - +extern void* TuplesortGetSortkeys(Tuplesortstate* state); +extern int TuplesortGetNsortkey(Tuplesortstate* state); #endif /* TUPLESORT_H */ diff --git a/src/include/windowapi.h b/src/include/windowapi.h index d61e9b4d5..31c4d4f4b 100644 --- a/src/include/windowapi.h +++ b/src/include/windowapi.h @@ -94,6 +94,13 @@ typedef struct WindowStatePerFuncData { } WindowStatePerFuncData; +typedef struct KeepRank { + bool keeptypeByVal; + bool keepValueIsNull; + int16 keeptypeLen; + Datum keepValue; + SortSupportData keepSort; +} KeepRank; /* * For plain aggregate window functions, we also have one of these. */ @@ -136,6 +143,10 @@ typedef struct WindowStatePerAggData { bool transValueIsNull; bool noTransValue; /* true if transValue not set yet */ + int numKeepCols; + bool keep_first; + bool keep_init; + KeepRank* keepRank; } WindowStatePerAggData; #define PG_WINDOW_OBJECT() ((WindowObject)fcinfo->context) diff --git a/src/test/regress/expected/keep_dense_rank.out b/src/test/regress/expected/keep_dense_rank.out new file mode 100644 index 000000000..7e1e6772f --- /dev/null +++ b/src/test/regress/expected/keep_dense_rank.out @@ -0,0 +1,354 @@ +create database keep_func_adb with dbcompatibility = 'A'; +\c keep_func_adb +CREATE TABLE employees (department_id INT,manager_id INT,last_name varchar(50),hiredate varchar(50),SALARY INT); +INSERT INTO employees VALUES(30, 100, 'Raphaely', '2017-07-01', 1700); +INSERT INTO employees VALUES(30, 100, 'De Haan', '2018-05-01', 11000); +INSERT INTO employees VALUES(40, 100, 'Errazuriz', '2017-07-21', 1400); +INSERT INTO employees VALUES(50, 100, 'Hartstein', '2019-10-05', 14000); +INSERT INTO employees VALUES(50, 100, 'Raphaely', '2017-07-22', 1700); +INSERT INTO employees VALUES(50, 100, 'Weiss', '2019-10-05', 13500); +INSERT INTO employees VALUES(90, 100, 'Russell', '2019-07-11', 13000); +INSERT INTO employees VALUES(90, 100, 'Partners', '2018-12-01', 14000); +explain (verbose on, costs off) SELECT SUM(salary) KEEP (DENSE_RANK FIRST ORDER BY salary) "Worst", SUM(salary) KEEP (DENSE_RANK LAST ORDER BY HIREDATE) "Best" FROM employees; + QUERY PLAN +------------------------------------------------------------------------------------------------------------------- + Aggregate + Output: sum(salary) KEEP(DENSE_RANK FIRST ORDER BY salary), sum(salary) KEEP(DENSE_RANK LAST ORDER BY hiredate) + -> Seq Scan on public.employees + Output: department_id, manager_id, last_name, hiredate, salary +(4 rows) + +SELECT SUM(salary) KEEP (DENSE_RANK FIRST ORDER BY salary) "Worst", SUM(salary) KEEP (DENSE_RANK LAST ORDER BY HIREDATE) "Best" FROM employees; + Worst | Best +-------+------- + 1400 | 27500 +(1 row) + +SELECT department_id, SUM(salary) KEEP (DENSE_RANK FIRST ORDER BY salary) "Worst", SUM(salary) KEEP (DENSE_RANK LAST ORDER BY HIREDATE) "Best" FROM employees GROUP BY department_id ORDER BY 1 DESC; + department_id | Worst | Best +---------------+-------+------- + 90 | 13000 | 13000 + 50 | 1700 | 27500 + 40 | 1400 | 1400 + 30 | 1700 | 11000 +(4 rows) + +SELECT last_name,department_id,salary, SUM(salary) KEEP (DENSE_RANK FIRST ORDER BY salary) OVER (PARTITION BY department_id) "Worst", + SUM(salary) KEEP (DENSE_RANK LAST ORDER BY HIREDATE) OVER (PARTITION BY department_id) "Best" + FROM employees ORDER BY department_id, salary, last_name; + last_name | department_id | salary | Worst | Best +-----------+---------------+--------+-------+------- + Raphaely | 30 | 1700 | 1700 | 11000 + De Haan | 30 | 11000 | 1700 | 11000 + Errazuriz | 40 | 1400 | 1400 | 1400 + Raphaely | 50 | 1700 | 1700 | 27500 + Weiss | 50 | 13500 | 1700 | 27500 + Hartstein | 50 | 14000 | 1700 | 27500 + Russell | 90 | 13000 | 13000 | 13000 + Partners | 90 | 14000 | 13000 | 13000 +(8 rows) + +explain (verbose on, costs off) SELECT last_name,department_id,salary, SUM(salary) KEEP (DENSE_RANK FIRST ORDER BY salary) OVER (PARTITION BY department_id) "Worst", + SUM(salary) KEEP (DENSE_RANK LAST ORDER BY HIREDATE) OVER (PARTITION BY department_id) "Best" + FROM employees ORDER BY department_id, salary, last_name; + QUERY PLAN +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + Sort + Output: last_name, department_id, salary, (sum(salary) KEEP(DENSE_RANK FIRST ORDER BY salary) OVER (PARTITION BY department_id)), (sum(salary) KEEP(DENSE_RANK LAST ORDER BY hiredate) OVER (PARTITION BY department_id)) + Sort Key: employees.department_id, employees.salary, employees.last_name + -> WindowAgg + Output: last_name, department_id, salary, sum(salary) KEEP(DENSE_RANK FIRST ORDER BY salary) OVER (PARTITION BY department_id), sum(salary) KEEP(DENSE_RANK LAST ORDER BY hiredate) OVER (PARTITION BY department_id) + -> Sort + Output: department_id, last_name, salary, hiredate + Sort Key: employees.department_id + -> Seq Scan on public.employees + Output: department_id, last_name, salary, hiredate +(10 rows) + +-- test keep for agg and window agg. +SELECT stddev(salary) KEEP (DENSE_RANK LAST ORDER BY HIREDATE) over(partition by department_id) FROM employees; + stddev +------------------ + + + + 353.553390593274 + 353.553390593274 + 353.553390593274 + + +(8 rows) + +SELECT department_id, stddev(salary) KEEP (DENSE_RANK LAST ORDER BY HIREDATE) FROM employees GROUP BY department_id; + department_id | stddev +---------------+------------------ + 30 | + 40 | + 50 | 353.553390593274 + 90 | +(4 rows) + +SELECT variance(salary) KEEP (DENSE_RANK LAST ORDER BY HIREDATE) over(partition by department_id) FROM employees; + variance +--------------------- + + + + 125000.000000000000 + 125000.000000000000 + 125000.000000000000 + + +(8 rows) + +SELECT department_id, variance(salary) KEEP (DENSE_RANK LAST ORDER BY HIREDATE) FROM employees GROUP BY department_id; + department_id | variance +---------------+--------------------- + 30 | + 40 | + 50 | 125000.000000000000 + 90 | +(4 rows) + +SELECT min(salary) KEEP (DENSE_RANK LAST ORDER BY HIREDATE) over(partition by department_id) FROM employees; + min +------- + 11000 + 11000 + 1400 + 13500 + 13500 + 13500 + 13000 + 13000 +(8 rows) + +SELECT department_id, min(salary) KEEP (DENSE_RANK LAST ORDER BY HIREDATE) FROM employees GROUP BY department_id; + department_id | min +---------------+------- + 30 | 11000 + 40 | 1400 + 50 | 13500 + 90 | 13000 +(4 rows) + +SELECT max(salary) KEEP (DENSE_RANK LAST ORDER BY HIREDATE) over(partition by department_id) FROM employees; + max +------- + 11000 + 11000 + 1400 + 14000 + 14000 + 14000 + 13000 + 13000 +(8 rows) + +SELECT department_id, max(salary) KEEP (DENSE_RANK LAST ORDER BY HIREDATE) FROM employees GROUP BY department_id; + department_id | max +---------------+------- + 30 | 11000 + 40 | 1400 + 50 | 14000 + 90 | 13000 +(4 rows) + +SELECT count(salary) KEEP (DENSE_RANK LAST ORDER BY HIREDATE) over(partition by department_id) FROM employees; + count +------- + 1 + 1 + 1 + 2 + 2 + 2 + 1 + 1 +(8 rows) + +SELECT department_id, count(salary) KEEP (DENSE_RANK LAST ORDER BY HIREDATE) FROM employees GROUP BY department_id; + department_id | count +---------------+------- + 30 | 1 + 40 | 1 + 50 | 2 + 90 | 1 +(4 rows) + +--not first/last +SELECT department_id, max(salary) KEEP (DENSE_RANK ORDER BY HIREDATE) FROM employees GROUP BY department_id; +ERROR: syntax error at or near "ORDER" +LINE 1: ...ELECT department_id, max(salary) KEEP (DENSE_RANK ORDER BY H... + ^ +-- syntax error +-- test multi order by +SELECT stddev(salary) KEEP (DENSE_RANK LAST ORDER BY HIREDATE) over(partition by department_id ORDER BY salary) FROM employees; +ERROR: ORDER BY of OVER clause is prohibited in function stddev with keep +LINE 1: SELECT stddev(salary) KEEP (DENSE_RANK LAST ORDER BY HIREDAT... + ^ +CONTEXT: referenced column: stddev +-- test vector executore unsupport +set try_vector_engine_strategy=force; +SELECT stddev(salary) KEEP (DENSE_RANK LAST ORDER BY HIREDATE) over(partition by department_id) FROM employees; +ERROR: keep feature for vector executor is not implemented +set try_vector_engine_strategy=off; +-- test var_pop unsupport +SELECT var_pop(salary) KEEP (DENSE_RANK LAST ORDER BY HIREDATE) FROM employees; +ERROR: KEEP for this function is not supported. +LINE 1: SELECT var_pop(salary) KEEP (DENSE_RANK LAST ORDER BY HIREDA... + ^ +--test include null and order by xxx nulls first/last +create table keep_table(id1 int, id2 int, id3 int); +insert into keep_table values (1, 11, 21); +insert into keep_table values (2, 12, 22); +insert into keep_table values (NULL, NULL, 23); +select min(id1) keep (DENSE_RANK FIRST ORDER BY id2 asc nulls first) from keep_table; + min +----- + +(1 row) + +select min(id1) keep (DENSE_RANK LAST ORDER BY id2 asc nulls first) from keep_table; + min +----- + 2 +(1 row) + +select min(id1) keep (DENSE_RANK FIRST ORDER BY id2 desc nulls first) from keep_table; + min +----- + +(1 row) + +select min(id1) keep (DENSE_RANK LAST ORDER BY id2 desc nulls first) from keep_table; + min +----- + 1 +(1 row) + +select min(id1) keep (DENSE_RANK FIRST ORDER BY id2 nulls first) from keep_table; + min +----- + +(1 row) + +select min(id1) keep (DENSE_RANK FIRST ORDER BY id2 nulls last) from keep_table; + min +----- + 1 +(1 row) + +insert into keep_table values (7, NULL, 24); +insert into keep_table values (8, NULL, 24); +select sum(id1) keep (DENSE_RANK FIRST ORDER BY id2 asc nulls first) from keep_table; + sum +----- + 15 +(1 row) + +select count(id1) keep (DENSE_RANK FIRST ORDER BY id2 asc nulls first) from keep_table; + count +------- + 2 +(1 row) + +\c regression; +drop database if exists keep_func_adb; +--only A_FORMAT support keep func +create database keep_func_bdb with dbcompatibility = 'B'; +\c keep_func_bdb +CREATE TABLE employees (department_id INT,manager_id INT,last_name varchar(50),hiredate varchar(50),SALARY INT); +INSERT INTO employees VALUES(30, 100, 'Raphaely', '2017-07-01', 1700); +INSERT INTO employees VALUES(30, 100, 'De Haan', '2018-05-01', 11000); +INSERT INTO employees VALUES(40, 100, 'Errazuriz', '2017-07-21', 1400); +INSERT INTO employees VALUES(50, 100, 'Hartstein', '2019-10-05', 14000); +INSERT INTO employees VALUES(50, 100, 'Raphaely', '2017-07-22', 1700); +INSERT INTO employees VALUES(50, 100, 'Weiss', '2019-10-05', 13500); +INSERT INTO employees VALUES(90, 100, 'Russell', '2019-07-11', 13000); +INSERT INTO employees VALUES(90, 100, 'Partners', '2018-12-01', 14000); +explain (verbose on, costs off) SELECT SUM(salary) KEEP (DENSE_RANK FIRST ORDER BY salary) "Worst", SUM(salary) KEEP (DENSE_RANK LAST ORDER BY HIREDATE) "Best" FROM employees; +ERROR: keep clause is supported only in A_FORMAT database. +SELECT SUM(salary) KEEP (DENSE_RANK FIRST ORDER BY salary) "Worst", SUM(salary) KEEP (DENSE_RANK LAST ORDER BY HIREDATE) "Best" FROM employees; +ERROR: keep clause is supported only in A_FORMAT database. +SELECT department_id, SUM(salary) KEEP (DENSE_RANK FIRST ORDER BY salary) "Worst", SUM(salary) KEEP (DENSE_RANK LAST ORDER BY HIREDATE) "Best" FROM employees GROUP BY department_id ORDER BY 1 DESC; +ERROR: keep clause is supported only in A_FORMAT database. +SELECT last_name,department_id,salary, SUM(salary) KEEP (DENSE_RANK FIRST ORDER BY salary) OVER (PARTITION BY department_id) "Worst", + SUM(salary) KEEP (DENSE_RANK LAST ORDER BY HIREDATE) OVER (PARTITION BY department_id) "Best" + FROM employees ORDER BY department_id, salary, last_name; +ERROR: keep clause is supported only in A_FORMAT database. +explain (verbose on, costs off) SELECT last_name,department_id,salary, SUM(salary) KEEP (DENSE_RANK FIRST ORDER BY salary) OVER (PARTITION BY department_id) "Worst", + SUM(salary) KEEP (DENSE_RANK LAST ORDER BY HIREDATE) OVER (PARTITION BY department_id) "Best" + FROM employees ORDER BY department_id, salary, last_name; +ERROR: keep clause is supported only in A_FORMAT database. +-- test keep for agg and window agg. +SELECT stddev(salary) KEEP (DENSE_RANK LAST ORDER BY HIREDATE) over(partition by department_id) FROM employees; +ERROR: keep clause is supported only in A_FORMAT database. +SELECT department_id, stddev(salary) KEEP (DENSE_RANK LAST ORDER BY HIREDATE) FROM employees GROUP BY department_id; +ERROR: keep clause is supported only in A_FORMAT database. +SELECT variance(salary) KEEP (DENSE_RANK LAST ORDER BY HIREDATE) over(partition by department_id) FROM employees; +ERROR: keep clause is supported only in A_FORMAT database. +SELECT department_id, variance(salary) KEEP (DENSE_RANK LAST ORDER BY HIREDATE) FROM employees GROUP BY department_id; +ERROR: keep clause is supported only in A_FORMAT database. +SELECT min(salary) KEEP (DENSE_RANK LAST ORDER BY HIREDATE) over(partition by department_id) FROM employees; +ERROR: keep clause is supported only in A_FORMAT database. +SELECT department_id, min(salary) KEEP (DENSE_RANK LAST ORDER BY HIREDATE) FROM employees GROUP BY department_id; +ERROR: keep clause is supported only in A_FORMAT database. +SELECT max(salary) KEEP (DENSE_RANK LAST ORDER BY HIREDATE) over(partition by department_id) FROM employees; +ERROR: keep clause is supported only in A_FORMAT database. +SELECT department_id, max(salary) KEEP (DENSE_RANK LAST ORDER BY HIREDATE) FROM employees GROUP BY department_id; +ERROR: keep clause is supported only in A_FORMAT database. +SELECT count(salary) KEEP (DENSE_RANK LAST ORDER BY HIREDATE) over(partition by department_id) FROM employees; +ERROR: keep clause is supported only in A_FORMAT database. +SELECT department_id, count(salary) KEEP (DENSE_RANK LAST ORDER BY HIREDATE) FROM employees GROUP BY department_id; +ERROR: keep clause is supported only in A_FORMAT database. +\c regression; +drop database if exists keep_func_bdb; +create database keep_func_pgdb with dbcompatibility = 'PG'; +\c keep_func_pgdb +CREATE TABLE employees (department_id INT,manager_id INT,last_name varchar(50),hiredate varchar(50),SALARY INT); +INSERT INTO employees VALUES(30, 100, 'Raphaely', '2017-07-01', 1700); +INSERT INTO employees VALUES(30, 100, 'De Haan', '2018-05-01', 11000); +INSERT INTO employees VALUES(40, 100, 'Errazuriz', '2017-07-21', 1400); +INSERT INTO employees VALUES(50, 100, 'Hartstein', '2019-10-05', 14000); +INSERT INTO employees VALUES(50, 100, 'Raphaely', '2017-07-22', 1700); +INSERT INTO employees VALUES(50, 100, 'Weiss', '2019-10-05', 13500); +INSERT INTO employees VALUES(90, 100, 'Russell', '2019-07-11', 13000); +INSERT INTO employees VALUES(90, 100, 'Partners', '2018-12-01', 14000); +explain (verbose on, costs off) SELECT SUM(salary) KEEP (DENSE_RANK FIRST ORDER BY salary) "Worst", SUM(salary) KEEP (DENSE_RANK LAST ORDER BY HIREDATE) "Best" FROM employees; +ERROR: keep clause is supported only in A_FORMAT database. +SELECT SUM(salary) KEEP (DENSE_RANK FIRST ORDER BY salary) "Worst", SUM(salary) KEEP (DENSE_RANK LAST ORDER BY HIREDATE) "Best" FROM employees; +ERROR: keep clause is supported only in A_FORMAT database. +SELECT department_id, SUM(salary) KEEP (DENSE_RANK FIRST ORDER BY salary) "Worst", SUM(salary) KEEP (DENSE_RANK LAST ORDER BY HIREDATE) "Best" FROM employees GROUP BY department_id ORDER BY 1 DESC; +ERROR: keep clause is supported only in A_FORMAT database. +SELECT last_name,department_id,salary, SUM(salary) KEEP (DENSE_RANK FIRST ORDER BY salary) OVER (PARTITION BY department_id) "Worst", + SUM(salary) KEEP (DENSE_RANK LAST ORDER BY HIREDATE) OVER (PARTITION BY department_id) "Best" + FROM employees ORDER BY department_id, salary, last_name; +ERROR: keep clause is supported only in A_FORMAT database. +explain (verbose on, costs off) SELECT last_name,department_id,salary, SUM(salary) KEEP (DENSE_RANK FIRST ORDER BY salary) OVER (PARTITION BY department_id) "Worst", + SUM(salary) KEEP (DENSE_RANK LAST ORDER BY HIREDATE) OVER (PARTITION BY department_id) "Best" + FROM employees ORDER BY department_id, salary, last_name; +ERROR: keep clause is supported only in A_FORMAT database. +-- test keep for agg and window agg. +SELECT stddev(salary) KEEP (DENSE_RANK LAST ORDER BY HIREDATE) over(partition by department_id) FROM employees; +ERROR: keep clause is supported only in A_FORMAT database. +SELECT department_id, stddev(salary) KEEP (DENSE_RANK LAST ORDER BY HIREDATE) FROM employees GROUP BY department_id; +ERROR: keep clause is supported only in A_FORMAT database. +SELECT variance(salary) KEEP (DENSE_RANK LAST ORDER BY HIREDATE) over(partition by department_id) FROM employees; +ERROR: keep clause is supported only in A_FORMAT database. +SELECT department_id, variance(salary) KEEP (DENSE_RANK LAST ORDER BY HIREDATE) FROM employees GROUP BY department_id; +ERROR: keep clause is supported only in A_FORMAT database. +SELECT min(salary) KEEP (DENSE_RANK LAST ORDER BY HIREDATE) over(partition by department_id) FROM employees; +ERROR: keep clause is supported only in A_FORMAT database. +SELECT department_id, min(salary) KEEP (DENSE_RANK LAST ORDER BY HIREDATE) FROM employees GROUP BY department_id; +ERROR: keep clause is supported only in A_FORMAT database. +SELECT max(salary) KEEP (DENSE_RANK LAST ORDER BY HIREDATE) over(partition by department_id) FROM employees; +ERROR: keep clause is supported only in A_FORMAT database. +SELECT department_id, max(salary) KEEP (DENSE_RANK LAST ORDER BY HIREDATE) FROM employees GROUP BY department_id; +ERROR: keep clause is supported only in A_FORMAT database. +SELECT count(salary) KEEP (DENSE_RANK LAST ORDER BY HIREDATE) over(partition by department_id) FROM employees; +ERROR: keep clause is supported only in A_FORMAT database. +SELECT department_id, count(salary) KEEP (DENSE_RANK LAST ORDER BY HIREDATE) FROM employees GROUP BY department_id; +ERROR: keep clause is supported only in A_FORMAT database. +\c regression; +drop database if exists keep_func_pgdb; diff --git a/src/test/regress/parallel_schedule0 b/src/test/regress/parallel_schedule0 index 56157affc..a9186e5f2 100644 --- a/src/test/regress/parallel_schedule0 +++ b/src/test/regress/parallel_schedule0 @@ -1136,11 +1136,11 @@ test: pg_object_type dump_object_type # test for on update timestamp and generated column test: on_update_session1 on_update_session2 - +test: keep_dense_rank test: ts_gb18030_utf8 test: backup_tool_audit # to_number func test: to_number_default # to_timestamp func -test: to_timestamp_default \ No newline at end of file +test: to_timestamp_default diff --git a/src/test/regress/parallel_schedule0C b/src/test/regress/parallel_schedule0C index ce05194c8..d306be467 100644 --- a/src/test/regress/parallel_schedule0C +++ b/src/test/regress/parallel_schedule0C @@ -192,7 +192,8 @@ test: user_host_test # test for new_expr_by_flatten test: enable_expr_fusion_flatten - +#keep func +test: keep_dense_rank # to_number func test: to_number_default # to_timestamp func diff --git a/src/test/regress/sql/keep_dense_rank.sql b/src/test/regress/sql/keep_dense_rank.sql new file mode 100644 index 000000000..d9ca89d8a --- /dev/null +++ b/src/test/regress/sql/keep_dense_rank.sql @@ -0,0 +1,129 @@ +create database keep_func_adb with dbcompatibility = 'A'; +\c keep_func_adb +CREATE TABLE employees (department_id INT,manager_id INT,last_name varchar(50),hiredate varchar(50),SALARY INT); +INSERT INTO employees VALUES(30, 100, 'Raphaely', '2017-07-01', 1700); +INSERT INTO employees VALUES(30, 100, 'De Haan', '2018-05-01', 11000); +INSERT INTO employees VALUES(40, 100, 'Errazuriz', '2017-07-21', 1400); +INSERT INTO employees VALUES(50, 100, 'Hartstein', '2019-10-05', 14000); +INSERT INTO employees VALUES(50, 100, 'Raphaely', '2017-07-22', 1700); +INSERT INTO employees VALUES(50, 100, 'Weiss', '2019-10-05', 13500); +INSERT INTO employees VALUES(90, 100, 'Russell', '2019-07-11', 13000); +INSERT INTO employees VALUES(90, 100, 'Partners', '2018-12-01', 14000); +explain (verbose on, costs off) SELECT SUM(salary) KEEP (DENSE_RANK FIRST ORDER BY salary) "Worst", SUM(salary) KEEP (DENSE_RANK LAST ORDER BY HIREDATE) "Best" FROM employees; +SELECT SUM(salary) KEEP (DENSE_RANK FIRST ORDER BY salary) "Worst", SUM(salary) KEEP (DENSE_RANK LAST ORDER BY HIREDATE) "Best" FROM employees; +SELECT department_id, SUM(salary) KEEP (DENSE_RANK FIRST ORDER BY salary) "Worst", SUM(salary) KEEP (DENSE_RANK LAST ORDER BY HIREDATE) "Best" FROM employees GROUP BY department_id ORDER BY 1 DESC; +SELECT last_name,department_id,salary, SUM(salary) KEEP (DENSE_RANK FIRST ORDER BY salary) OVER (PARTITION BY department_id) "Worst", + SUM(salary) KEEP (DENSE_RANK LAST ORDER BY HIREDATE) OVER (PARTITION BY department_id) "Best" + FROM employees ORDER BY department_id, salary, last_name; +explain (verbose on, costs off) SELECT last_name,department_id,salary, SUM(salary) KEEP (DENSE_RANK FIRST ORDER BY salary) OVER (PARTITION BY department_id) "Worst", + SUM(salary) KEEP (DENSE_RANK LAST ORDER BY HIREDATE) OVER (PARTITION BY department_id) "Best" + FROM employees ORDER BY department_id, salary, last_name; +-- test keep for agg and window agg. +SELECT stddev(salary) KEEP (DENSE_RANK LAST ORDER BY HIREDATE) over(partition by department_id) FROM employees; +SELECT department_id, stddev(salary) KEEP (DENSE_RANK LAST ORDER BY HIREDATE) FROM employees GROUP BY department_id; +SELECT variance(salary) KEEP (DENSE_RANK LAST ORDER BY HIREDATE) over(partition by department_id) FROM employees; +SELECT department_id, variance(salary) KEEP (DENSE_RANK LAST ORDER BY HIREDATE) FROM employees GROUP BY department_id; +SELECT min(salary) KEEP (DENSE_RANK LAST ORDER BY HIREDATE) over(partition by department_id) FROM employees; +SELECT department_id, min(salary) KEEP (DENSE_RANK LAST ORDER BY HIREDATE) FROM employees GROUP BY department_id; +SELECT max(salary) KEEP (DENSE_RANK LAST ORDER BY HIREDATE) over(partition by department_id) FROM employees; +SELECT department_id, max(salary) KEEP (DENSE_RANK LAST ORDER BY HIREDATE) FROM employees GROUP BY department_id; +SELECT count(salary) KEEP (DENSE_RANK LAST ORDER BY HIREDATE) over(partition by department_id) FROM employees; +SELECT department_id, count(salary) KEEP (DENSE_RANK LAST ORDER BY HIREDATE) FROM employees GROUP BY department_id; +--not first/last +SELECT department_id, max(salary) KEEP (DENSE_RANK ORDER BY HIREDATE) FROM employees GROUP BY department_id; +-- syntax error +-- test multi order by +SELECT stddev(salary) KEEP (DENSE_RANK LAST ORDER BY HIREDATE) over(partition by department_id ORDER BY salary) FROM employees; +-- test vector executore unsupport +set try_vector_engine_strategy=force; +SELECT stddev(salary) KEEP (DENSE_RANK LAST ORDER BY HIREDATE) over(partition by department_id) FROM employees; +set try_vector_engine_strategy=off; +-- test var_pop unsupport +SELECT var_pop(salary) KEEP (DENSE_RANK LAST ORDER BY HIREDATE) FROM employees; +--test include null and order by xxx nulls first/last +create table keep_table(id1 int, id2 int, id3 int); +insert into keep_table values (1, 11, 21); +insert into keep_table values (2, 12, 22); +insert into keep_table values (NULL, NULL, 23); +select min(id1) keep (DENSE_RANK FIRST ORDER BY id2 asc nulls first) from keep_table; +select min(id1) keep (DENSE_RANK LAST ORDER BY id2 asc nulls first) from keep_table; + +select min(id1) keep (DENSE_RANK FIRST ORDER BY id2 desc nulls first) from keep_table; +select min(id1) keep (DENSE_RANK LAST ORDER BY id2 desc nulls first) from keep_table; + +select min(id1) keep (DENSE_RANK FIRST ORDER BY id2 nulls first) from keep_table; +select min(id1) keep (DENSE_RANK FIRST ORDER BY id2 nulls last) from keep_table; + +insert into keep_table values (7, NULL, 24); +insert into keep_table values (8, NULL, 24); +select sum(id1) keep (DENSE_RANK FIRST ORDER BY id2 asc nulls first) from keep_table; +select count(id1) keep (DENSE_RANK FIRST ORDER BY id2 asc nulls first) from keep_table; +\c regression; +drop database if exists keep_func_adb; +--only A_FORMAT support keep func +create database keep_func_bdb with dbcompatibility = 'B'; +\c keep_func_bdb +CREATE TABLE employees (department_id INT,manager_id INT,last_name varchar(50),hiredate varchar(50),SALARY INT); +INSERT INTO employees VALUES(30, 100, 'Raphaely', '2017-07-01', 1700); +INSERT INTO employees VALUES(30, 100, 'De Haan', '2018-05-01', 11000); +INSERT INTO employees VALUES(40, 100, 'Errazuriz', '2017-07-21', 1400); +INSERT INTO employees VALUES(50, 100, 'Hartstein', '2019-10-05', 14000); +INSERT INTO employees VALUES(50, 100, 'Raphaely', '2017-07-22', 1700); +INSERT INTO employees VALUES(50, 100, 'Weiss', '2019-10-05', 13500); +INSERT INTO employees VALUES(90, 100, 'Russell', '2019-07-11', 13000); +INSERT INTO employees VALUES(90, 100, 'Partners', '2018-12-01', 14000); +explain (verbose on, costs off) SELECT SUM(salary) KEEP (DENSE_RANK FIRST ORDER BY salary) "Worst", SUM(salary) KEEP (DENSE_RANK LAST ORDER BY HIREDATE) "Best" FROM employees; +SELECT SUM(salary) KEEP (DENSE_RANK FIRST ORDER BY salary) "Worst", SUM(salary) KEEP (DENSE_RANK LAST ORDER BY HIREDATE) "Best" FROM employees; +SELECT department_id, SUM(salary) KEEP (DENSE_RANK FIRST ORDER BY salary) "Worst", SUM(salary) KEEP (DENSE_RANK LAST ORDER BY HIREDATE) "Best" FROM employees GROUP BY department_id ORDER BY 1 DESC; +SELECT last_name,department_id,salary, SUM(salary) KEEP (DENSE_RANK FIRST ORDER BY salary) OVER (PARTITION BY department_id) "Worst", + SUM(salary) KEEP (DENSE_RANK LAST ORDER BY HIREDATE) OVER (PARTITION BY department_id) "Best" + FROM employees ORDER BY department_id, salary, last_name; +explain (verbose on, costs off) SELECT last_name,department_id,salary, SUM(salary) KEEP (DENSE_RANK FIRST ORDER BY salary) OVER (PARTITION BY department_id) "Worst", + SUM(salary) KEEP (DENSE_RANK LAST ORDER BY HIREDATE) OVER (PARTITION BY department_id) "Best" + FROM employees ORDER BY department_id, salary, last_name; +-- test keep for agg and window agg. +SELECT stddev(salary) KEEP (DENSE_RANK LAST ORDER BY HIREDATE) over(partition by department_id) FROM employees; +SELECT department_id, stddev(salary) KEEP (DENSE_RANK LAST ORDER BY HIREDATE) FROM employees GROUP BY department_id; +SELECT variance(salary) KEEP (DENSE_RANK LAST ORDER BY HIREDATE) over(partition by department_id) FROM employees; +SELECT department_id, variance(salary) KEEP (DENSE_RANK LAST ORDER BY HIREDATE) FROM employees GROUP BY department_id; +SELECT min(salary) KEEP (DENSE_RANK LAST ORDER BY HIREDATE) over(partition by department_id) FROM employees; +SELECT department_id, min(salary) KEEP (DENSE_RANK LAST ORDER BY HIREDATE) FROM employees GROUP BY department_id; +SELECT max(salary) KEEP (DENSE_RANK LAST ORDER BY HIREDATE) over(partition by department_id) FROM employees; +SELECT department_id, max(salary) KEEP (DENSE_RANK LAST ORDER BY HIREDATE) FROM employees GROUP BY department_id; +SELECT count(salary) KEEP (DENSE_RANK LAST ORDER BY HIREDATE) over(partition by department_id) FROM employees; +SELECT department_id, count(salary) KEEP (DENSE_RANK LAST ORDER BY HIREDATE) FROM employees GROUP BY department_id; +\c regression; +drop database if exists keep_func_bdb; +create database keep_func_pgdb with dbcompatibility = 'PG'; +\c keep_func_pgdb +CREATE TABLE employees (department_id INT,manager_id INT,last_name varchar(50),hiredate varchar(50),SALARY INT); +INSERT INTO employees VALUES(30, 100, 'Raphaely', '2017-07-01', 1700); +INSERT INTO employees VALUES(30, 100, 'De Haan', '2018-05-01', 11000); +INSERT INTO employees VALUES(40, 100, 'Errazuriz', '2017-07-21', 1400); +INSERT INTO employees VALUES(50, 100, 'Hartstein', '2019-10-05', 14000); +INSERT INTO employees VALUES(50, 100, 'Raphaely', '2017-07-22', 1700); +INSERT INTO employees VALUES(50, 100, 'Weiss', '2019-10-05', 13500); +INSERT INTO employees VALUES(90, 100, 'Russell', '2019-07-11', 13000); +INSERT INTO employees VALUES(90, 100, 'Partners', '2018-12-01', 14000); +explain (verbose on, costs off) SELECT SUM(salary) KEEP (DENSE_RANK FIRST ORDER BY salary) "Worst", SUM(salary) KEEP (DENSE_RANK LAST ORDER BY HIREDATE) "Best" FROM employees; +SELECT SUM(salary) KEEP (DENSE_RANK FIRST ORDER BY salary) "Worst", SUM(salary) KEEP (DENSE_RANK LAST ORDER BY HIREDATE) "Best" FROM employees; +SELECT department_id, SUM(salary) KEEP (DENSE_RANK FIRST ORDER BY salary) "Worst", SUM(salary) KEEP (DENSE_RANK LAST ORDER BY HIREDATE) "Best" FROM employees GROUP BY department_id ORDER BY 1 DESC; +SELECT last_name,department_id,salary, SUM(salary) KEEP (DENSE_RANK FIRST ORDER BY salary) OVER (PARTITION BY department_id) "Worst", + SUM(salary) KEEP (DENSE_RANK LAST ORDER BY HIREDATE) OVER (PARTITION BY department_id) "Best" + FROM employees ORDER BY department_id, salary, last_name; +explain (verbose on, costs off) SELECT last_name,department_id,salary, SUM(salary) KEEP (DENSE_RANK FIRST ORDER BY salary) OVER (PARTITION BY department_id) "Worst", + SUM(salary) KEEP (DENSE_RANK LAST ORDER BY HIREDATE) OVER (PARTITION BY department_id) "Best" + FROM employees ORDER BY department_id, salary, last_name; +-- test keep for agg and window agg. +SELECT stddev(salary) KEEP (DENSE_RANK LAST ORDER BY HIREDATE) over(partition by department_id) FROM employees; +SELECT department_id, stddev(salary) KEEP (DENSE_RANK LAST ORDER BY HIREDATE) FROM employees GROUP BY department_id; +SELECT variance(salary) KEEP (DENSE_RANK LAST ORDER BY HIREDATE) over(partition by department_id) FROM employees; +SELECT department_id, variance(salary) KEEP (DENSE_RANK LAST ORDER BY HIREDATE) FROM employees GROUP BY department_id; +SELECT min(salary) KEEP (DENSE_RANK LAST ORDER BY HIREDATE) over(partition by department_id) FROM employees; +SELECT department_id, min(salary) KEEP (DENSE_RANK LAST ORDER BY HIREDATE) FROM employees GROUP BY department_id; +SELECT max(salary) KEEP (DENSE_RANK LAST ORDER BY HIREDATE) over(partition by department_id) FROM employees; +SELECT department_id, max(salary) KEEP (DENSE_RANK LAST ORDER BY HIREDATE) FROM employees GROUP BY department_id; +SELECT count(salary) KEEP (DENSE_RANK LAST ORDER BY HIREDATE) over(partition by department_id) FROM employees; +SELECT department_id, count(salary) KEEP (DENSE_RANK LAST ORDER BY HIREDATE) FROM employees GROUP BY department_id; +\c regression; +drop database if exists keep_func_pgdb;