>> The attached test patch is mostly the same as in the previous patch
>> set, but it doesn't fail on row_number anymore as the main patch
>> only rejects aggregate functions. The test patch also adds a test for
>
>> +SELECT sum(orbit) RESPECT NULLS OVER () FROM planets; -- succeeds
>
> I think the standard does not allow to specify RESPECT NULLS other
> than lead, lag, first_value, last_value and nth_value. Unless we agree
> that PostgreSQL violates the standard in this regard, you should not
> allow to use RESPECT NULLS for the window functions, expect lead etc.
> and aggregates.
>
> See my patch.
>
>> +/*
>> + * Window function option clauses
>> + */
>> +opt_null_treatment:
>> + RESPECT NULLS_P
>> { $$ = RESPECT_NULLS; }
>> + | IGNORE_P NULLS_P
>> { $$ = IGNORE_NULLS; }
>> + | /*EMPTY*/
>> { $$ = NULL_TREATMENT_NOT_SET; }
>> + ;
>
> With this, you can check if null treatment clause is used or not in
> each window function.
>
> In my previous patch I did the check in parse/analysis but I think
> it's better to be checked in each window function. This way,
>
> - need not to add a column to pg_proc.
>
> - allow user defined window functions to decide by themselves whether
> they can accept null treatment option.
Attached is the patch to implement this (on top of your patch).
test=# SELECT row_number() RESPECT NULLS OVER () FROM (SELECT 1) AS s;
ERROR: window function row_number cannot have RESPECT NULLS or IGNORE NULLS
Best reagards,
--
Tatsuo Ishii
SRA OSS LLC
English: http://www.sraoss.co.jp/index_en/
Japanese:http://www.sraoss.co.jp
diff --git a/src/backend/executor/nodeWindowAgg.c b/src/backend/executor/nodeWindowAgg.c
index fac0e05dee..b8519d9890 100644
--- a/src/backend/executor/nodeWindowAgg.c
+++ b/src/backend/executor/nodeWindowAgg.c
@@ -74,6 +74,7 @@ typedef struct WindowObjectData
int64 *win_nonnulls; /* tracks non-nulls in ignore nulls mode */
int nonnulls_size; /* track size of the win_nonnulls array */
int nonnulls_len; /* track length of the win_nonnulls array */
+ WindowFunc *wfunc; /* WindowFunc of this function */
} WindowObjectData;
/*
@@ -2634,6 +2635,8 @@ ExecInitWindowAgg(WindowAgg *node, EState *estate, int eflags)
winobj->nonnulls_len = 0;
}
+ winobj->wfunc = wfunc;
+
/* It's a real window function, so set up to call it. */
fmgr_info_cxt(wfunc->winfnoid, &perfuncstate->flinfo,
econtext->ecxt_per_query_memory);
@@ -3881,3 +3884,24 @@ WinGetFuncArgCurrent(WindowObject winobj, int argno, bool *isnull)
return ExecEvalExpr((ExprState *) list_nth(winobj->argstates, argno),
econtext, isnull);
}
+
+/*
+ * Error out that this window function cannot have null treatement.
+ */
+void
+ErrorOutNullTreatment(WindowObject winobj)
+{
+ char *fname;
+
+ Assert(WindowObjectIsValid(winobj));
+
+ if (winobj->wfunc->null_treatment == NULL_TREATMENT_NOT_SET)
+ return;
+
+ fname = get_func_name(winobj->wfunc->winfnoid);
+
+ ereport(ERROR,
+ (errcode(ERRCODE_WRONG_OBJECT_TYPE),
+ errmsg("window function %s cannot have RESPECT NULLS or IGNORE NULLS",
+ fname)));
+}
diff --git a/src/backend/optimizer/util/clauses.c b/src/backend/optimizer/util/clauses.c
index 01fd16acf9..05e64c4569 100644
--- a/src/backend/optimizer/util/clauses.c
+++ b/src/backend/optimizer/util/clauses.c
@@ -2475,6 +2475,7 @@ eval_const_expressions_mutator(Node *node,
newexpr->winstar = expr->winstar;
newexpr->winagg = expr->winagg;
newexpr->ignore_nulls = expr->ignore_nulls;
+ newexpr->null_treatment = expr->null_treatment;
newexpr->location = expr->location;
return (Node *) newexpr;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 58c00bfd4f..e131428e85 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -276,6 +276,7 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
MergeWhenClause *mergewhen;
struct KeyActions *keyactions;
struct KeyAction *keyaction;
+ NullTreatment nulltreatment;
}
%type <node> stmt toplevel_stmt schema_stmt routine_body_stmt
@@ -633,7 +634,6 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
opt_frame_clause frame_extent frame_bound
%type <ival> opt_window_exclusion_clause
%type <str> opt_existing_window_name
-%type <boolean> null_treatment
%type <boolean> opt_if_not_exists
%type <boolean> opt_unique_null_treatment
%type <ival> generated_when override_kind
@@ -662,6 +662,8 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
json_object_constructor_null_clause_opt
json_array_constructor_null_clause_opt
+%type <nulltreatment> null_treatment
+
/*
* Non-keyword token types. These are hard-wired into the "flex" lexer.
* They must be listed first so that their numeric codes do not depend on
@@ -15247,7 +15249,7 @@ func_expr: func_application within_group_clause filter_clause null_treatment ove
n->agg_within_group = true;
}
n->agg_filter = $3;
- n->ignore_nulls = $4;
+ n->null_treatment = $4;
n->over = $5;
$$ = (Node *) n;
}
@@ -15797,9 +15799,9 @@ filter_clause:
* Window Definitions
*/
null_treatment:
- IGNORE_P NULLS_P { $$ = true; }
- | RESPECT_P NULLS_P { $$ = false; }
- | /*EMPTY*/ { $$ = false; }
+ RESPECT_P NULLS_P { $$ = RESPECT_NULLS; }
+ | IGNORE_P NULLS_P { $$ = IGNORE_NULLS; }
+ | /*EMPTY*/ { $$ = NULL_TREATMENT_NOT_SET; }
;
window_clause:
diff --git a/src/backend/parser/parse_func.c b/src/backend/parser/parse_func.c
index afa4bcc8d1..63af8ca6aa 100644
--- a/src/backend/parser/parse_func.c
+++ b/src/backend/parser/parse_func.c
@@ -98,7 +98,7 @@ ParseFuncOrColumn(ParseState *pstate, List *funcname, List *fargs,
bool agg_star = (fn ? fn->agg_star : false);
bool agg_distinct = (fn ? fn->agg_distinct : false);
bool func_variadic = (fn ? fn->func_variadic : false);
- bool ignore_nulls = (fn ? fn->ignore_nulls : false);
+ NullTreatment null_treatment = (fn ? fn->null_treatment : NULL_TREATMENT_NOT_SET);
CoercionForm funcformat = (fn ? fn->funcformat : COERCE_EXPLICIT_CALL);
bool could_be_projection;
Oid rettype;
@@ -516,11 +516,12 @@ ParseFuncOrColumn(ParseState *pstate, List *funcname, List *fargs,
NameListToString(funcname)),
parser_errposition(pstate, location)));
- /* It also can't treat nulls as a window function */
- if (ignore_nulls)
+ /* Aggregate functions cannot have null treatment clause */
+ if (null_treatment != NULL_TREATMENT_NOT_SET)
ereport(ERROR,
(errcode(ERRCODE_WRONG_OBJECT_TYPE),
- errmsg("aggregate functions do not accept RESPECT/IGNORE NULLS"),
+ errmsg("aggregate function %s cannot have RESPECT NULLS or IGNORE NULLS",
+ NameListToString(funcname)),
parser_errposition(pstate, location)));
}
}
@@ -842,7 +843,8 @@ ParseFuncOrColumn(ParseState *pstate, List *funcname, List *fargs,
wfunc->winstar = agg_star;
wfunc->winagg = (fdresult == FUNCDETAIL_AGGREGATE);
wfunc->aggfilter = agg_filter;
- wfunc->ignore_nulls = ignore_nulls;
+ wfunc->null_treatment = null_treatment;
+ wfunc->ignore_nulls = (null_treatment == IGNORE_NULLS);
wfunc->location = location;
/*
diff --git a/src/backend/utils/adt/windowfuncs.c b/src/backend/utils/adt/windowfuncs.c
index b87a624fb2..297e787927 100644
--- a/src/backend/utils/adt/windowfuncs.c
+++ b/src/backend/utils/adt/windowfuncs.c
@@ -85,6 +85,9 @@ window_row_number(PG_FUNCTION_ARGS)
WindowObject winobj = PG_WINDOW_OBJECT();
int64 curpos = WinGetCurrentPosition(winobj);
+ /* row_number() does not support null treatment */
+ ErrorOutNullTreatment(winobj);
+
WinSetMarkPosition(winobj, curpos);
PG_RETURN_INT64(curpos + 1);
}
@@ -140,6 +143,9 @@ window_rank(PG_FUNCTION_ARGS)
rank_context *context;
bool up;
+ /* rank() does not support null treatment */
+ ErrorOutNullTreatment(winobj);
+
up = rank_up(winobj);
context = (rank_context *)
WinGetPartitionLocalMemory(winobj, sizeof(rank_context));
@@ -202,6 +208,9 @@ window_dense_rank(PG_FUNCTION_ARGS)
rank_context *context;
bool up;
+ /* dense_rank() does not support null treatment */
+ ErrorOutNullTreatment(winobj);
+
up = rank_up(winobj);
context = (rank_context *)
WinGetPartitionLocalMemory(winobj, sizeof(rank_context));
@@ -266,6 +275,9 @@ window_percent_rank(PG_FUNCTION_ARGS)
Assert(totalrows > 0);
+ /* percent_rank() does not support null treatment */
+ ErrorOutNullTreatment(winobj);
+
up = rank_up(winobj);
context = (rank_context *)
WinGetPartitionLocalMemory(winobj, sizeof(rank_context));
@@ -335,6 +347,9 @@ window_cume_dist(PG_FUNCTION_ARGS)
Assert(totalrows > 0);
+ /* cume_dist() does not support null treatment */
+ ErrorOutNullTreatment(winobj);
+
up = rank_up(winobj);
context = (rank_context *)
WinGetPartitionLocalMemory(winobj, sizeof(rank_context));
@@ -412,6 +427,9 @@ window_ntile(PG_FUNCTION_ARGS)
WindowObject winobj = PG_WINDOW_OBJECT();
ntile_context *context;
+ /* ntile() does not support null treatment */
+ ErrorOutNullTreatment(winobj);
+
context = (ntile_context *)
WinGetPartitionLocalMemory(winobj, sizeof(ntile_context));
diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h
index 073e2469ba..32fbab46a0 100644
--- a/src/include/nodes/parsenodes.h
+++ b/src/include/nodes/parsenodes.h
@@ -432,6 +432,7 @@ typedef struct FuncCall
bool agg_distinct; /* arguments were labeled DISTINCT */
bool func_variadic; /* last argument was labeled VARIADIC */
CoercionForm funcformat; /* how to display this node */
+ NullTreatment null_treatment; /* RESPECT_NULLS or IGNORE NULLS */
int location; /* token location, or -1 if unknown */
} FuncCall;
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 221b5e6218..545b5e5ac8 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -532,6 +532,13 @@ typedef struct GroupingFunc
int location;
} GroupingFunc;
+typedef enum NullTreatment
+{
+ NULL_TREATMENT_NOT_SET = 0,
+ RESPECT_NULLS,
+ IGNORE_NULLS
+} NullTreatment;
+
/*
* WindowFunc
*
@@ -559,7 +566,8 @@ typedef struct WindowFunc
bool winstar pg_node_attr(query_jumble_ignore);
/* is function a simple aggregate? */
bool winagg pg_node_attr(query_jumble_ignore);
- /* ignore nulls */
+ /* null treatement */
+ NullTreatment null_treatment pg_node_attr(query_jumble_ignore);
bool ignore_nulls;
/* token location, or -1 if unknown */
int location;
diff --git a/src/include/windowapi.h b/src/include/windowapi.h
index b8c2c565d1..8a50478ee9 100644
--- a/src/include/windowapi.h
+++ b/src/include/windowapi.h
@@ -61,4 +61,6 @@ extern Datum WinGetFuncArgInFrame(WindowObject winobj, int argno,
extern Datum WinGetFuncArgCurrent(WindowObject winobj, int argno,
bool *isnull);
+extern void ErrorOutNullTreatment(WindowObject winobj);
+
#endif /* WINDOWAPI_H */