diff --git a/src/common/backend/nodes/copyfuncs.cpp b/src/common/backend/nodes/copyfuncs.cpp index 4d5151bcfb32a8fc3c629a8612748b55e11e2521..1fd21b991afabfcfe1600bfde69a22b916899841 100644 --- a/src/common/backend/nodes/copyfuncs.cpp +++ b/src/common/backend/nodes/copyfuncs.cpp @@ -2777,6 +2777,8 @@ static WindowFunc* _copyWindowFunc(const WindowFunc* from) COPY_SCALAR_FIELD(winkpfirst); } COPY_LOCATION_FIELD(location); + COPY_SCALAR_FIELD(ignore_nulls); + COPY_SCALAR_FIELD(from_last); return newnode; } @@ -4168,6 +4170,8 @@ static FuncCall* _copyFuncCall(const FuncCall* from) COPY_NODE_FIELD(over); COPY_LOCATION_FIELD(location); COPY_SCALAR_FIELD(call_func); + COPY_SCALAR_FIELD(ignore_nulls); + COPY_SCALAR_FIELD(from_last); return newnode; } diff --git a/src/common/backend/nodes/equalfuncs.cpp b/src/common/backend/nodes/equalfuncs.cpp index 89a1121b2db253b53da573b7005a7d8262fd2d6a..f10335712b58f10922075d2ec5d00dc1589ddf3e 100644 --- a/src/common/backend/nodes/equalfuncs.cpp +++ b/src/common/backend/nodes/equalfuncs.cpp @@ -284,6 +284,8 @@ static bool _equalWindowFunc(const WindowFunc* a, const WindowFunc* b) COMPARE_SCALAR_FIELD(winkpfirst); } COMPARE_LOCATION_FIELD(location); + COMPARE_SCALAR_FIELD(ignore_nulls); + COMPARE_SCALAR_FIELD(from_last); return true; } @@ -2688,6 +2690,8 @@ static bool _equalFuncCall(const FuncCall* a, const FuncCall* b) COMPARE_NODE_FIELD(over); COMPARE_LOCATION_FIELD(location); COMPARE_SCALAR_FIELD(call_func); + COMPARE_SCALAR_FIELD(ignore_nulls); + COMPARE_SCALAR_FIELD(from_last); return true; } diff --git a/src/common/backend/nodes/outfuncs.cpp b/src/common/backend/nodes/outfuncs.cpp index b7b12d7319754ca876c6cf1efc90b596a2642dd5..c2216eacfeb149c67a50d7a14e205a0648aa2ad4 100755 --- a/src/common/backend/nodes/outfuncs.cpp +++ b/src/common/backend/nodes/outfuncs.cpp @@ -2643,6 +2643,10 @@ static void _outWindowFunc(StringInfo str, WindowFunc* node) WRITE_TYPEINFO_FIELD(wintype); WRITE_FUNCINFO_FIELD(winfnoid); + if (t_thrd.proc->workingVersionNum >= IGNORE_NULLS_VERSION_NUMBER) { + WRITE_BOOL_FIELD(ignore_nulls); + WRITE_BOOL_FIELD(from_last); + } } static void _outArrayRef(StringInfo str, ArrayRef* node) @@ -4345,6 +4349,10 @@ static void _outFuncCall(StringInfo str, FuncCall* node) WRITE_NODE_FIELD(over); WRITE_LOCATION_FIELD(location); WRITE_BOOL_FIELD(call_func); + if (t_thrd.proc->workingVersionNum >= IGNORE_NULLS_VERSION_NUMBER) { + WRITE_BOOL_FIELD(ignore_nulls); + WRITE_BOOL_FIELD(from_last); + } } static void _outTableLikeClause(StringInfo str, const TableLikeClause* node) diff --git a/src/common/backend/nodes/readfuncs.cpp b/src/common/backend/nodes/readfuncs.cpp index c1da9745e03f90808bbddd2d844cb2ed6a395a70..51b72df53633d9f0c8f01f538682e14068c2637d 100755 --- a/src/common/backend/nodes/readfuncs.cpp +++ b/src/common/backend/nodes/readfuncs.cpp @@ -2393,6 +2393,12 @@ static WindowFunc* _readWindowFunc(void) READ_TYPEINFO_FIELD(wintype); READ_FUNCINFO_FIELD(winfnoid); + IF_EXIST(ignore_nulls) { + READ_BOOL_FIELD(ignore_nulls); + } + IF_EXIST(from_last) { + READ_BOOL_FIELD(from_last); + } READ_DONE(); } diff --git a/src/common/backend/parser/gram.y b/src/common/backend/parser/gram.y index 953eda8e914c1ac0e7b0b8b6f6cce384a772251f..2a433109c4e43f79e54f7f1ab389046368156782 100644 --- a/src/common/backend/parser/gram.y +++ b/src/common/backend/parser/gram.y @@ -645,6 +645,7 @@ static char* IdentResolveToChar(char *ident, core_yyscan_t yyscanner); %type opt_percent only_or_ties %type OptSeqOptList SeqOptList +%type ignNulls fromLast %type SeqOptElem /* INSERT */ @@ -760,7 +761,7 @@ static char* IdentResolveToChar(char *ident, core_yyscan_t yyscanner); %type xml_whitespace_option %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 func_expr func_expr_windowless analytic_func_expr %type common_table_expr %type keep_clause %type with_clause opt_with_clause @@ -770,7 +771,7 @@ static char* IdentResolveToChar(char *ident, core_yyscan_t yyscanner); %type within_group_clause pkg_body_subprogram %type window_clause window_definition_list opt_partition_clause -%type window_definition over_clause window_specification +%type window_definition over_clause window_specification opt_over_clause opt_frame_clause frame_extent frame_bound %type opt_existing_window_name opt_unique_key %type opt_if_not_exists @@ -971,7 +972,7 @@ static char* IdentResolveToChar(char *ident, core_yyscan_t yyscanner); MODEL MODIFY_P MONTH_P MOVE MOVEMENT MYSQL_ERRNO // DB4AI NAME_P NAMES NAN_P NATIONAL NATURAL NCHAR NEXT NO NOCOMPRESS NOCYCLE NODE NOLOGGING NOMAXVALUE NOMINVALUE NONE - NOT NOTHING NOTIFY NOTNULL NOVALIDATE NOWAIT NULL_P NULLCOLS NULLIF NULLS_P NUMBER_P NUMERIC NUMSTR NVARCHAR NVARCHAR2 NVL + NOT NOTHING NOTIFY NOTNULL NOVALIDATE NOWAIT NTH_VALUE_P NULL_P NULLCOLS NULLIF NULLS_P NUMBER_P NUMERIC NUMSTR NVARCHAR NVARCHAR2 NVL OBJECT_P OF OFF OFFSET OIDS ON ONLY OPERATOR OPTIMIZATION OPTION OPTIONALLY OPTIONS OR ORDER OUT_P OUTER_P OVER OVERLAPS OVERLAY OWNED OWNER OUTFILE @@ -991,7 +992,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 ROTATE + RESET RESIZE RESOURCE RESPECT_P 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 @@ -28896,7 +28897,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 keep_clause over_clause +func_expr: func_application within_group_clause filter_clause keep_clause opt_over_clause { FuncCall *n = (FuncCall *) $1; @@ -29054,6 +29055,92 @@ func_expr: func_application within_group_clause filter_clause keep_clause over_c { $$ = MakeNoArgFunctionCall(SystemFuncName("current_schema"), @1); } + | analytic_func_expr + { $$ = $1; } + ; +opt_over_clause: + over_clause { $$ = $1; } + | /*EMPTY*/ { $$ = NULL; } + ; +analytic_func_expr: + NTH_VALUE_P '(' func_arg_list ')' fromLast ignNulls over_clause + { + FuncCall *n = makeNode(FuncCall); + n->funcname = SystemFuncName("nth_value"); + n->args = $3; + n->agg_order = NIL; + n->agg_star = FALSE; + n->agg_distinct = FALSE; + n->func_variadic = FALSE; + n->over = $7; + n->location = @1; + n->call_func = false; + n->from_last = $5; + n->ignore_nulls = $6; + $$ = (Node *)n; + } + | NTH_VALUE_P '(' func_arg_list ')' fromLast over_clause + { + FuncCall *n = makeNode(FuncCall); + n->funcname = SystemFuncName("nth_value"); + n->args = $3; + n->agg_order = NIL; + n->agg_star = FALSE; + n->agg_distinct = FALSE; + n->func_variadic = FALSE; + n->over = $6; + n->location = @1; + n->call_func = false; + n->from_last = $5; + $$ = (Node *)n; + } + | NTH_VALUE_P '(' func_arg_list ')' ignNulls over_clause + { + FuncCall *n = makeNode(FuncCall); + n->funcname = SystemFuncName("nth_value"); + n->args = $3; + n->agg_order = NIL; + n->agg_star = FALSE; + n->agg_distinct = FALSE; + n->func_variadic = FALSE; + n->over = $6; + n->location = @1; + n->call_func = false; + n->ignore_nulls = $5; + $$ = (Node *)n; + } + | NTH_VALUE_P '(' func_arg_list ')' over_clause + { + FuncCall *n = makeNode(FuncCall); + n->funcname = SystemFuncName("nth_value"); + n->args = $3; + n->agg_order = NIL; + n->agg_star = FALSE; + n->agg_distinct = FALSE; + n->func_variadic = FALSE; + n->over = $5; + n->location = @1; + n->call_func = false; + $$ = (Node *)n; + } + ; +fromLast: FROM FIRST_P + { + $$ = false; + } + | FROM LAST_P + { + $$ = true; + } + ; +ignNulls: IGNORE NULLS_P + { + $$ = true; + } + | RESPECT_P NULLS_P + { + $$ = false; + } ; func_application: func_name '(' func_arg_list opt_sort_clause ')' @@ -30122,8 +30209,6 @@ over_clause: OVER window_specification n->location = @2; $$ = n; } - | /*EMPTY*/ - { $$ = NULL; } ; window_specification: '(' opt_existing_window_name opt_partition_clause @@ -31818,6 +31903,7 @@ unreserved_keyword: | RESET | RESIZE | RESOURCE + | RESPECT_P | RESTART | RESTRICT | RESULT @@ -32012,6 +32098,7 @@ col_name_keyword: | NATIONAL | NCHAR | NONE + | NTH_VALUE_P | NULLIF | NUMBER_P | NUMERIC diff --git a/src/common/backend/parser/parse_func.cpp b/src/common/backend/parser/parse_func.cpp index 3e2aa1e25a71b6cb6a3cc22285c44de27fb6d90b..c074e5fc2e1a03cb409a623a676441f61b3547c3 100644 --- a/src/common/backend/parser/parse_func.cpp +++ b/src/common/backend/parser/parse_func.cpp @@ -615,6 +615,8 @@ Node* ParseFuncOrColumn(ParseState* pstate, List* funcname, List* fargs, Node* l wfunc->winstar = agg_star; wfunc->winagg = (fdresult == FUNCDETAIL_AGGREGATE); wfunc->location = location; + wfunc->from_last = fn->from_last; + wfunc->ignore_nulls = fn->ignore_nulls; /* * agg_star is allowed for aggregate functions but distinct isn't diff --git a/src/common/backend/utils/adt/windowfuncs.cpp b/src/common/backend/utils/adt/windowfuncs.cpp index 349bb3c69a3d5aac3c6d615cc6939fd5199e28ea..ee8551d1296f2f2f62fa73fc73e08e420685c453 100644 --- a/src/common/backend/utils/adt/windowfuncs.cpp +++ b/src/common/backend/utils/adt/windowfuncs.cpp @@ -396,6 +396,9 @@ Datum window_nth_value(PG_FUNCTION_ARGS) Datum result; bool isnull = false; int32 nth; + bool isout = false; + bool is_from_last = winobj->from_last; + bool is_ignore_nulls = winobj->ignore_nulls; nth = DatumGetInt32(WinGetFuncArgCurrent(winobj, 1, &isnull)); if (isnull) @@ -407,7 +410,29 @@ Datum window_nth_value(PG_FUNCTION_ARGS) (errcode(ERRCODE_INVALID_ARGUMENT_FOR_NTH_VALUE), errmsg("argument of nth_value must be greater than zero"))); - result = WinGetFuncArgInFrame(winobj, 0, nth - 1, WINDOW_SEEK_HEAD, const_offset, &isnull, NULL); + int startingPos = 0; + if (is_from_last) { + if (is_ignore_nulls){ + do { + result = WinGetFuncArgInFrame(winobj, 0, startingPos--, WINDOW_SEEK_TAIL, false, &isnull, &isout); + } while(isnull && !isout); + if (nth > 1) + result = WinGetFuncArgInFrame(winobj, 0, startingPos-nth+2, WINDOW_SEEK_TAIL, false, &isnull, &isout); + } else { + result = WinGetFuncArgInFrame(winobj, 0, 1-nth, WINDOW_SEEK_TAIL, const_offset, &isnull, NULL); + } + } else { + if (is_ignore_nulls){ + do { + result = WinGetFuncArgInFrame(winobj, 0, startingPos++, WINDOW_SEEK_HEAD, false, &isnull, &isout); + } while(isnull && !isout); + if (nth > 1) + result = WinGetFuncArgInFrame(winobj, 0, startingPos+nth-2, WINDOW_SEEK_HEAD, false, &isnull, &isout); + } else { + result = WinGetFuncArgInFrame(winobj, 0, nth - 1, WINDOW_SEEK_HEAD, const_offset, &isnull, NULL); + } + } + if (isnull) PG_RETURN_NULL(); @@ -448,4 +473,4 @@ Datum window_delta(PG_FUNCTION_ARGS) } PG_RETURN_DATUM(res); -} \ No newline at end of file +} diff --git a/src/common/backend/utils/init/globals.cpp b/src/common/backend/utils/init/globals.cpp index 184ffd51ec45157b4b94d0708ce9b899a3fdfa63..286de98b8d4ae2bd5ccd4a3dcacdbb8dd2656f72 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 = 93021; +const uint32 GRAND_VERSION_NUM = 93022; /******************************************** * 2.VERSION NUM FOR EACH FEATURE * Please write indescending order. ********************************************/ +const uint32 IGNORE_NULLS_VERSION_NUMBER = 93022; const uint32 DATAVEC_VERSION_NUMBER = 93019; const uint32 HTAP_VERSION_NUMBER = 93015; const uint32 KEEP_FUNC_VERSION_NUMBER = 93014; diff --git a/src/common/interfaces/libpq/frontend_parser/gram.y b/src/common/interfaces/libpq/frontend_parser/gram.y index 0108d541ad699bdfb27de68379e25b089f371265..d4f62acb6cbd53eede9d015067815c1acc7820db 100755 --- a/src/common/interfaces/libpq/frontend_parser/gram.y +++ b/src/common/interfaces/libpq/frontend_parser/gram.y @@ -577,7 +577,7 @@ extern THR_LOCAL bool stmt_contains_operator_plus; MAPPING MASKING MASTER MASTR MATCH MATERIALIZED MATCHED MAXEXTENTS MAXSIZE MAXTRANS MAXVALUE MERGE MESSAGE_TEXT METHOD MINUS_P MINUTE_P MINUTE_SECOND_P MINVALUE MINEXTENTS MODE MODIFY_P MONTH_P MOVE MOVEMENT MODEL MYSQL_ERRNO// DB4AI NAME_P NAMES NAN_P NATIONAL NATURAL NCHAR NEXT NLSSORT NO NOCOMPRESS NOCYCLE NODE NOLOGGING NOMAXVALUE NOMINVALUE NONE - NOT NOTHING NOTIFY NOTNULL NOVALIDATE NOWAIT NULL_P NULLCOLS NULLIF NULLS_P NUMBER_P NUMERIC NUMSTR NVARCHAR NVARCHAR2 NVL + NOT NOTHING NOTIFY NOTNULL NOVALIDATE NOWAIT NTH_VALUE_P NULL_P NULLCOLS NULLIF NULLS_P NUMBER_P NUMERIC NUMSTR NVARCHAR NVARCHAR2 NVL OBJECT_P OF OFF OFFSET OIDS ON ONLY OPERATOR OPTIMIZATION OPTION OPTIONALLY OPTIONS OR ORDER OUT_P OUTER_P OVER OVERLAPS OVERLAY OWNED OWNER OUTFILE @@ -597,7 +597,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 ROTATE + RESET RESIZE RESOURCE RESPECT_P 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 @@ -12075,6 +12075,7 @@ unreserved_keyword: | RESET | RESIZE | RESOURCE + | RESPECT_P | RESTART | RESTRICT | RETURNS @@ -12242,6 +12243,7 @@ col_name_keyword: | NATIONAL | NCHAR | NONE + | NTH_VALUE_P | NULLIF | NUMBER_P | NUMERIC diff --git a/src/gausskernel/optimizer/util/clauses.cpp b/src/gausskernel/optimizer/util/clauses.cpp index 94897a76ec3691a381d0a1cc1abe7d6878d69ba0..69921870b41ed69d5d682048fb4e30bfef2aaecd 100644 --- a/src/gausskernel/optimizer/util/clauses.cpp +++ b/src/gausskernel/optimizer/util/clauses.cpp @@ -2600,6 +2600,8 @@ Node* eval_const_expressions_mutator(Node* node, eval_const_expressions_context* (Node*)expr->winkporder, (Node* (*)(Node*, void*)) eval_const_expressions_mutator, (void*)context); newexpr->winkpfirst = expr->winkpfirst; newexpr->location = expr->location; + newexpr->ignore_nulls = expr->ignore_nulls; + newexpr->from_last = expr->from_last; return (Node*)newexpr; } diff --git a/src/gausskernel/runtime/executor/nodeWindowAgg.cpp b/src/gausskernel/runtime/executor/nodeWindowAgg.cpp index d684fc95fb4cb61486f693744dfdd5116a90a217..f52333b08a21cb6c7237361bf84ab5d03edc274c 100644 --- a/src/gausskernel/runtime/executor/nodeWindowAgg.cpp +++ b/src/gausskernel/runtime/executor/nodeWindowAgg.cpp @@ -1488,6 +1488,8 @@ WindowAggState* ExecInitWindowAgg(WindowAgg* node, EState* estate, int eflags) winobj->winstate = winstate; winobj->argstates = wfuncstate->args; winobj->localmem = NULL; + winobj->from_last = wfunc->from_last; + winobj->ignore_nulls = wfunc->ignore_nulls; perfuncstate->winobj = winobj; } } diff --git a/src/gausskernel/runtime/vecexecutor/vecnode/vecwindowagg.cpp b/src/gausskernel/runtime/vecexecutor/vecnode/vecwindowagg.cpp index 697aa508abf358e1a5edbc86f2d7dea41a5716c9..e0f30d51f680eeb41e754d8cda8ca4adad95aacb 100644 --- a/src/gausskernel/runtime/vecexecutor/vecnode/vecwindowagg.cpp +++ b/src/gausskernel/runtime/vecexecutor/vecnode/vecwindowagg.cpp @@ -267,6 +267,8 @@ VecWindowAggState* ExecInitVecWindowAgg(VecWindowAgg* node, EState* estate, int winobj->winstate = winstate; winobj->argstates = wfuncstate->args; winobj->localmem = NULL; + winobj->from_last = wfunc->from_last; + winobj->ignore_nulls = wfunc->ignore_nulls; perfuncstate->winobj = winobj; } } diff --git a/src/include/miscadmin.h b/src/include/miscadmin.h index 9acb8e45f2605306dc8c424658d65b27e06c4ad3..a4fbc05a73f1f33dcd78d151da2dd00609e3cf4c 100644 --- a/src/include/miscadmin.h +++ b/src/include/miscadmin.h @@ -162,6 +162,7 @@ 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 const uint32 IGNORE_NULLS_VERSION_NUMBER; extern void register_backend_version(uint32 backend_version); extern bool contain_backend_version(uint32 version_number); diff --git a/src/include/nodes/parsenodes_common.h b/src/include/nodes/parsenodes_common.h index f8dacb2436da762eb4cfa2106efd431e1a423f07..461052fa3dcb2a394b373b8046fb8b23aa223b20 100644 --- a/src/include/nodes/parsenodes_common.h +++ b/src/include/nodes/parsenodes_common.h @@ -1741,6 +1741,8 @@ typedef struct FuncCall { struct WindowDef *over; /* OVER clause, if any */ int location; /* token location, or -1 if unknown */ bool call_func; /* call function, false is select function */ + bool ignore_nulls = false; + bool from_last = false; } FuncCall; /* diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h index 0333e1c4136687cbfb13ba6529d73e37da9529d0..c61fcd8fdf7e61bad692121f461872011047d787 100644 --- a/src/include/nodes/primnodes.h +++ b/src/include/nodes/primnodes.h @@ -367,6 +367,8 @@ typedef struct WindowFunc { #ifdef USE_SPQ bool windistinct; /* TRUE if it's agg(DISTINCT ...) */ #endif + bool ignore_nulls = false; + bool from_last = false; } WindowFunc; /* diff --git a/src/include/parser/kwlist.h b/src/include/parser/kwlist.h index ef67eb08a64105bc1970245125bb4bf846b1fcdb..ef4bfd7cb6da73762111efbd48ba5195d1033529 100644 --- a/src/include/parser/kwlist.h +++ b/src/include/parser/kwlist.h @@ -442,6 +442,7 @@ PG_KEYWORD("notify", NOTIFY, UNRESERVED_KEYWORD) PG_KEYWORD("notnull", NOTNULL, TYPE_FUNC_NAME_KEYWORD) PG_KEYWORD("novalidate", NOVALIDATE, UNRESERVED_KEYWORD) PG_KEYWORD("nowait", NOWAIT, UNRESERVED_KEYWORD) +PG_KEYWORD("nth_value", NTH_VALUE_P, COL_NAME_KEYWORD) PG_KEYWORD("null", NULL_P, RESERVED_KEYWORD) PG_KEYWORD("nullcols", NULLCOLS, UNRESERVED_KEYWORD) PG_KEYWORD("nullif", NULLIF, COL_NAME_KEYWORD) @@ -550,6 +551,7 @@ PG_KEYWORD("replica", REPLICA, UNRESERVED_KEYWORD) PG_KEYWORD("reset", RESET, UNRESERVED_KEYWORD) PG_KEYWORD("resize", RESIZE, UNRESERVED_KEYWORD) PG_KEYWORD("resource", RESOURCE, UNRESERVED_KEYWORD) +PG_KEYWORD("respect", RESPECT_P, UNRESERVED_KEYWORD) PG_KEYWORD("restart", RESTART, UNRESERVED_KEYWORD) PG_KEYWORD("restrict", RESTRICT, UNRESERVED_KEYWORD) PG_KEYWORD("result", RESULT, UNRESERVED_KEYWORD) diff --git a/src/include/windowapi.h b/src/include/windowapi.h index 31c4d4f4b156cab3827481045c264d70440e5548..8cb9a9dc6b9799cc0342a3c8e97aa5f2cd83bba9 100644 --- a/src/include/windowapi.h +++ b/src/include/windowapi.h @@ -57,6 +57,8 @@ typedef struct WindowObjectData { int readptr; /* tuplestore read pointer for this fn */ int64 markpos; /* row that markptr is positioned on */ int64 seekpos; /* row that readptr is positioned on */ + bool ignore_nulls; + bool from_last; } WindowObjectData; /* this struct is private in nodeWindowAgg.c */ diff --git a/src/test/regress/expected/analytic_funcs.out b/src/test/regress/expected/analytic_funcs.out new file mode 100644 index 0000000000000000000000000000000000000000..cb6f3ffeefd5c8a3a99e389d1049a5933f8d8b04 --- /dev/null +++ b/src/test/regress/expected/analytic_funcs.out @@ -0,0 +1,294 @@ +--nth_value support algorithm FROM LAST/FIRST and IGNORE/REPSECT NULLS +DROP TABLE IF EXISTS temp_table; +NOTICE: table "temp_table" does not exist, skipping +CREATE TABLE temp_table(ten int, four int); +insert into temp_table values(1,1),(1,1),(7,1),(9,1),(0,2),(1,3),(3,3),(null,1),(null,2),(8,1),(null,1),(null,1); +--check whole partition +SELECT NTH_VALUE(TEN, 1) OVER (PARTITION BY FOUR order by ten ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING) AS NTH_VALUE, TEN, FOUR FROM temp_table ORDER BY FOUR, TEN; + nth_value | ten | four +-----------+-----+------ + 1 | 1 | 1 + 1 | 1 | 1 + 1 | 7 | 1 + 1 | 8 | 1 + 1 | 9 | 1 + 1 | | 1 + 1 | | 1 + 1 | | 1 + 0 | 0 | 2 + 0 | | 2 + 1 | 1 | 3 + 1 | 3 | 3 +(12 rows) + +SELECT NTH_VALUE(TEN, 1) FROM LAST OVER (PARTITION BY FOUR order by ten ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING) AS NTH_VALUE, TEN, FOUR FROM temp_table ORDER BY FOUR, TEN; + nth_value | ten | four +-----------+-----+------ + | 1 | 1 + | 1 | 1 + | 7 | 1 + | 8 | 1 + | 9 | 1 + | | 1 + | | 1 + | | 1 + | 0 | 2 + | | 2 + 3 | 1 | 3 + 3 | 3 | 3 +(12 rows) + +SELECT NTH_VALUE(TEN, 1) FROM FIRST OVER (PARTITION BY FOUR order by ten ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING) AS NTH_VALUE, TEN, FOUR FROM temp_table ORDER BY FOUR, TEN; + nth_value | ten | four +-----------+-----+------ + 1 | 1 | 1 + 1 | 1 | 1 + 1 | 7 | 1 + 1 | 8 | 1 + 1 | 9 | 1 + 1 | | 1 + 1 | | 1 + 1 | | 1 + 0 | 0 | 2 + 0 | | 2 + 1 | 1 | 3 + 1 | 3 | 3 +(12 rows) + +SELECT NTH_VALUE(TEN, 1) IGNORE NULLS OVER (PARTITION BY FOUR order by ten ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING) AS NTH_VALUE, TEN, FOUR FROM temp_table ORDER BY FOUR, TEN; + nth_value | ten | four +-----------+-----+------ + 1 | 1 | 1 + 1 | 1 | 1 + 1 | 7 | 1 + 1 | 8 | 1 + 1 | 9 | 1 + 1 | | 1 + 1 | | 1 + 1 | | 1 + 0 | 0 | 2 + 0 | | 2 + 1 | 1 | 3 + 1 | 3 | 3 +(12 rows) + +SELECT NTH_VALUE(TEN, 1) RESPECT NULLS OVER (PARTITION BY FOUR order by ten desc ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING) AS NTH_VALUE, TEN, FOUR FROM temp_table ORDER BY FOUR, TEN desc; + nth_value | ten | four +-----------+-----+------ + | | 1 + | | 1 + | | 1 + | 9 | 1 + | 8 | 1 + | 7 | 1 + | 1 | 1 + | 1 | 1 + | | 2 + | 0 | 2 + 3 | 3 | 3 + 3 | 1 | 3 +(12 rows) + +SELECT NTH_VALUE(TEN, 1) FROM LAST IGNORE NULLS OVER (PARTITION BY FOUR order by ten ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING) AS NTH_VALUE, TEN, FOUR FROM temp_table ORDER BY FOUR, TEN; + nth_value | ten | four +-----------+-----+------ + 9 | 1 | 1 + 9 | 1 | 1 + 9 | 7 | 1 + 9 | 8 | 1 + 9 | 9 | 1 + 9 | | 1 + 9 | | 1 + 9 | | 1 + 0 | 0 | 2 + 0 | | 2 + 3 | 1 | 3 + 3 | 3 | 3 +(12 rows) + +SELECT NTH_VALUE(TEN,2) FROM LAST IGNORE NULLS OVER (PARTITION BY FOUR order by ten ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING) AS NTH_VALUE, TEN, FOUR FROM temp_table ORDER BY FOUR, TEN; + nth_value | ten | four +-----------+-----+------ + 8 | 1 | 1 + 8 | 1 | 1 + 8 | 7 | 1 + 8 | 8 | 1 + 8 | 9 | 1 + 8 | | 1 + 8 | | 1 + 8 | | 1 + | 0 | 2 + | | 2 + 1 | 1 | 3 + 1 | 3 | 3 +(12 rows) + +SELECT NTH_VALUE(TEN,3) FROM LAST IGNORE NULLS OVER (PARTITION BY FOUR order by ten ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING) AS NTH_VALUE, TEN, FOUR FROM temp_table ORDER BY FOUR, TEN; + nth_value | ten | four +-----------+-----+------ + 7 | 1 | 1 + 7 | 1 | 1 + 7 | 7 | 1 + 7 | 8 | 1 + 7 | 9 | 1 + 7 | | 1 + 7 | | 1 + 7 | | 1 + | 0 | 2 + | | 2 + | 1 | 3 + | 3 | 3 +(12 rows) + +SELECT NTH_VALUE(TEN,2) FROM FIRST IGNORE NULLS OVER (PARTITION BY FOUR order by ten desc ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING) AS NTH_VALUE, TEN, FOUR FROM temp_table ORDER BY FOUR, TEN desc; + nth_value | ten | four +-----------+-----+------ + 8 | | 1 + 8 | | 1 + 8 | | 1 + 8 | 9 | 1 + 8 | 8 | 1 + 8 | 7 | 1 + 8 | 1 | 1 + 8 | 1 | 1 + | | 2 + | 0 | 2 + 1 | 3 | 3 + 1 | 1 | 3 +(12 rows) + +SELECT NTH_VALUE(TEN,3) FROM FIRST IGNORE NULLS OVER (PARTITION BY FOUR order by ten desc ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING) AS NTH_VALUE, TEN, FOUR FROM temp_table ORDER BY FOUR, TEN desc; + nth_value | ten | four +-----------+-----+------ + 7 | | 1 + 7 | | 1 + 7 | | 1 + 7 | 9 | 1 + 7 | 8 | 1 + 7 | 7 | 1 + 7 | 1 | 1 + 7 | 1 | 1 + | | 2 + | 0 | 2 + | 3 | 3 + | 1 | 3 +(12 rows) + +--check default frame head to current row +SELECT NTH_VALUE(TEN, 1) FROM FIRST IGNORE NULLS OVER (PARTITION BY FOUR order by ten desc) AS NTH_VALUE, TEN, FOUR FROM temp_table ORDER BY FOUR, TEN desc; + nth_value | ten | four +-----------+-----+------ + | | 1 + | | 1 + | | 1 + 9 | 9 | 1 + 9 | 8 | 1 + 9 | 7 | 1 + 9 | 1 | 1 + 9 | 1 | 1 + | | 2 + 0 | 0 | 2 + 3 | 3 | 3 + 3 | 1 | 3 +(12 rows) + +SELECT NTH_VALUE(TEN, 2) FROM FIRST IGNORE NULLS OVER (PARTITION BY FOUR order by ten desc) AS NTH_VALUE, TEN, FOUR FROM temp_table ORDER BY FOUR, TEN desc; + nth_value | ten | four +-----------+-----+------ + | | 1 + | | 1 + | | 1 + | 9 | 1 + 8 | 8 | 1 + 8 | 7 | 1 + 8 | 1 | 1 + 8 | 1 | 1 + | | 2 + | 0 | 2 + | 3 | 3 + 1 | 1 | 3 +(12 rows) + +SELECT NTH_VALUE(TEN, 3) FROM FIRST IGNORE NULLS OVER (PARTITION BY FOUR order by ten desc) AS NTH_VALUE, TEN, FOUR FROM temp_table ORDER BY FOUR, TEN desc; + nth_value | ten | four +-----------+-----+------ + | | 1 + | | 1 + | | 1 + | 9 | 1 + | 8 | 1 + 7 | 7 | 1 + 7 | 1 | 1 + 7 | 1 | 1 + | | 2 + | 0 | 2 + | 3 | 3 + | 1 | 3 +(12 rows) + +SELECT NTH_VALUE(TEN, 1) FROM LAST IGNORE NULLS OVER (PARTITION BY FOUR order by ten desc) AS NTH_VALUE, TEN, FOUR FROM temp_table ORDER BY FOUR, TEN desc; + nth_value | ten | four +-----------+-----+------ + | | 1 + | | 1 + | | 1 + 9 | 9 | 1 + 8 | 8 | 1 + 7 | 7 | 1 + 1 | 1 | 1 + 1 | 1 | 1 + | | 2 + 0 | 0 | 2 + 3 | 3 | 3 + 1 | 1 | 3 +(12 rows) + +SELECT NTH_VALUE(TEN, 2) FROM LAST IGNORE NULLS OVER (PARTITION BY FOUR order by ten desc) AS NTH_VALUE, TEN, FOUR FROM temp_table ORDER BY FOUR, TEN desc; + nth_value | ten | four +-----------+-----+------ + | | 1 + | | 1 + | | 1 + | 9 | 1 + 9 | 8 | 1 + 8 | 7 | 1 + 1 | 1 | 1 + 1 | 1 | 1 + | | 2 + | 0 | 2 + | 3 | 3 + 3 | 1 | 3 +(12 rows) + +SELECT NTH_VALUE(TEN, 3) FROM LAST IGNORE NULLS OVER (PARTITION BY FOUR order by ten desc) AS NTH_VALUE, TEN, FOUR FROM temp_table ORDER BY FOUR, TEN desc; + nth_value | ten | four +-----------+-----+------ + | | 1 + | | 1 + | | 1 + | 9 | 1 + | 8 | 1 + 9 | 7 | 1 + 7 | 1 | 1 + 7 | 1 | 1 + | | 2 + | 0 | 2 + | 3 | 3 + | 1 | 3 +(12 rows) + +--check error case +SELECT NTH_VALUE(TEN, 0) FROM LAST IGNORE NULLS OVER (PARTITION BY FOUR order by ten desc) AS NTH_VALUE, TEN, FOUR FROM temp_table ORDER BY FOUR, TEN desc; +ERROR: argument of nth_value must be greater than zero +SELECT NTH_VALUE(TEN, 1) IGNORE NULLS FROM LAST OVER (PARTITION BY FOUR order by ten desc) AS NTH_VALUE, TEN, FOUR FROM temp_table ORDER BY FOUR, TEN desc; +ERROR: syntax error at or near "FROM" +LINE 1: SELECT NTH_VALUE(TEN, 1) IGNORE NULLS FROM LAST OVER (PARTIT... + ^ +SELECT NTH_VALUE(TEN, 1) FROM LAST IGNORE NULLS OVER (PARTITION BY FOUR order by ten desc) AS NTH_VALUE, TEN, FOUR +FROM temp_table +GROUP BY 1 +ORDER BY FOUR, TEN desc; +ERROR: window functions not allowed in GROUP BY clause +LINE 1: SELECT NTH_VALUE(TEN, 1) FROM LAST IGNORE NULLS OVER (PARTIT... + ^ +DROP TABLE IF EXISTS temp_table; diff --git a/src/test/regress/parallel_schedule0A b/src/test/regress/parallel_schedule0A index 21d790c0bdbd8018702dd9388894488266edbbb7..962ab37a7cf7730d3aba92c4b0d34fe8cdc9e797 100644 --- a/src/test/regress/parallel_schedule0A +++ b/src/test/regress/parallel_schedule0A @@ -14,6 +14,7 @@ # usecases for improving coverage # these use cases may affect other usecases, please keep these usecases unique in their parallel groups # -------------------------- +test: analytic_funcs test: timecapsule_partition_ustore_test_1 test: timecapsule_partition_ustore_test_2 test: ddl diff --git a/src/test/regress/sql/analytic_funcs.sql b/src/test/regress/sql/analytic_funcs.sql new file mode 100644 index 0000000000000000000000000000000000000000..2de70982b768fdc52c8900fa6f50dd6f2c5a951f --- /dev/null +++ b/src/test/regress/sql/analytic_funcs.sql @@ -0,0 +1,30 @@ +--nth_value support algorithm FROM LAST/FIRST and IGNORE/REPSECT NULLS +DROP TABLE IF EXISTS temp_table; +CREATE TABLE temp_table(ten int, four int); +insert into temp_table values(1,1),(1,1),(7,1),(9,1),(0,2),(1,3),(3,3),(null,1),(null,2),(8,1),(null,1),(null,1); +--check whole partition +SELECT NTH_VALUE(TEN, 1) OVER (PARTITION BY FOUR order by ten ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING) AS NTH_VALUE, TEN, FOUR FROM temp_table ORDER BY FOUR, TEN; +SELECT NTH_VALUE(TEN, 1) FROM LAST OVER (PARTITION BY FOUR order by ten ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING) AS NTH_VALUE, TEN, FOUR FROM temp_table ORDER BY FOUR, TEN; +SELECT NTH_VALUE(TEN, 1) FROM FIRST OVER (PARTITION BY FOUR order by ten ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING) AS NTH_VALUE, TEN, FOUR FROM temp_table ORDER BY FOUR, TEN; +SELECT NTH_VALUE(TEN, 1) IGNORE NULLS OVER (PARTITION BY FOUR order by ten ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING) AS NTH_VALUE, TEN, FOUR FROM temp_table ORDER BY FOUR, TEN; +SELECT NTH_VALUE(TEN, 1) RESPECT NULLS OVER (PARTITION BY FOUR order by ten desc ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING) AS NTH_VALUE, TEN, FOUR FROM temp_table ORDER BY FOUR, TEN desc; +SELECT NTH_VALUE(TEN, 1) FROM LAST IGNORE NULLS OVER (PARTITION BY FOUR order by ten ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING) AS NTH_VALUE, TEN, FOUR FROM temp_table ORDER BY FOUR, TEN; +SELECT NTH_VALUE(TEN,2) FROM LAST IGNORE NULLS OVER (PARTITION BY FOUR order by ten ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING) AS NTH_VALUE, TEN, FOUR FROM temp_table ORDER BY FOUR, TEN; +SELECT NTH_VALUE(TEN,3) FROM LAST IGNORE NULLS OVER (PARTITION BY FOUR order by ten ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING) AS NTH_VALUE, TEN, FOUR FROM temp_table ORDER BY FOUR, TEN; +SELECT NTH_VALUE(TEN,2) FROM FIRST IGNORE NULLS OVER (PARTITION BY FOUR order by ten desc ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING) AS NTH_VALUE, TEN, FOUR FROM temp_table ORDER BY FOUR, TEN desc; +SELECT NTH_VALUE(TEN,3) FROM FIRST IGNORE NULLS OVER (PARTITION BY FOUR order by ten desc ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING) AS NTH_VALUE, TEN, FOUR FROM temp_table ORDER BY FOUR, TEN desc; +--check default frame head to current row +SELECT NTH_VALUE(TEN, 1) FROM FIRST IGNORE NULLS OVER (PARTITION BY FOUR order by ten desc) AS NTH_VALUE, TEN, FOUR FROM temp_table ORDER BY FOUR, TEN desc; +SELECT NTH_VALUE(TEN, 2) FROM FIRST IGNORE NULLS OVER (PARTITION BY FOUR order by ten desc) AS NTH_VALUE, TEN, FOUR FROM temp_table ORDER BY FOUR, TEN desc; +SELECT NTH_VALUE(TEN, 3) FROM FIRST IGNORE NULLS OVER (PARTITION BY FOUR order by ten desc) AS NTH_VALUE, TEN, FOUR FROM temp_table ORDER BY FOUR, TEN desc; +SELECT NTH_VALUE(TEN, 1) FROM LAST IGNORE NULLS OVER (PARTITION BY FOUR order by ten desc) AS NTH_VALUE, TEN, FOUR FROM temp_table ORDER BY FOUR, TEN desc; +SELECT NTH_VALUE(TEN, 2) FROM LAST IGNORE NULLS OVER (PARTITION BY FOUR order by ten desc) AS NTH_VALUE, TEN, FOUR FROM temp_table ORDER BY FOUR, TEN desc; +SELECT NTH_VALUE(TEN, 3) FROM LAST IGNORE NULLS OVER (PARTITION BY FOUR order by ten desc) AS NTH_VALUE, TEN, FOUR FROM temp_table ORDER BY FOUR, TEN desc; +--check error case +SELECT NTH_VALUE(TEN, 0) FROM LAST IGNORE NULLS OVER (PARTITION BY FOUR order by ten desc) AS NTH_VALUE, TEN, FOUR FROM temp_table ORDER BY FOUR, TEN desc; +SELECT NTH_VALUE(TEN, 1) IGNORE NULLS FROM LAST OVER (PARTITION BY FOUR order by ten desc) AS NTH_VALUE, TEN, FOUR FROM temp_table ORDER BY FOUR, TEN desc; +SELECT NTH_VALUE(TEN, 1) FROM LAST IGNORE NULLS OVER (PARTITION BY FOUR order by ten desc) AS NTH_VALUE, TEN, FOUR +FROM temp_table +GROUP BY 1 +ORDER BY FOUR, TEN desc; +DROP TABLE IF EXISTS temp_table;