reject_null_treatment.patch

text/x-patch

Filename: reject_null_treatment.patch
Type: text/x-patch
Part: 0
Message: Re: Add RESPECT/IGNORE NULLS and FROM FIRST/LAST options

Patch

Same data as JSON: GET /api/v1/attachments/:id/patch the parsed metadata as JSON — format, series position, per-file stats; never the diff bytes. API reference →
Format: unified
File+
src/backend/executor/nodeWindowAgg.c 24 0
src/backend/optimizer/util/clauses.c 1 0
src/backend/parser/gram.y 7 5
src/backend/parser/parse_func.c 7 5
src/backend/utils/adt/windowfuncs.c 18 0
src/include/nodes/parsenodes.h 1 0
src/include/nodes/primnodes.h 9 1
src/include/windowapi.h 2 0
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 */