From 632cd9f892119858bc5b617bb60c0377a8a2ed13 Mon Sep 17 00:00:00 2001
From: Joe Conway <mail@joeconway.com>
Date: Wed, 29 Jul 2015 15:37:48 -0700
Subject: [PATCH] Create new ParseExprKind for use by policy expressions.

Policy USING and WITH CHECK expressions were using EXPR_KIND_WHERE for
parse analysis, which results in inappropriate ERROR messages when
the expression contains unsupported constructs such as aggregates.
Create a new ParseExprKind called EXPR_KIND_POLICY and tailor the
related messages to fit.

Reported by Noah Misch. Reviewed by Dean Rasheed, Alvaro Herrera,
and Robert Haas. Back-patch to 9.5 where RLS was introduced.
---
 src/backend/commands/policy.c                    |  8 ++++----
 src/backend/parser/parse_agg.c                   | 10 ++++++++++
 src/backend/parser/parse_expr.c                  |  3 +++
 src/include/parser/parse_node.h                  |  3 ++-
 src/test/modules/test_rls_hooks/test_rls_hooks.c |  4 ++--
 src/test/regress/expected/rowsecurity.out        |  9 +++++++++
 src/test/regress/sql/rowsecurity.sql             |  9 +++++++++
 7 files changed, 39 insertions(+), 7 deletions(-)

diff --git a/src/backend/commands/policy.c b/src/backend/commands/policy.c
index d8b43908ec..bcf4a8f35d 100644
--- a/src/backend/commands/policy.c
+++ b/src/backend/commands/policy.c
@@ -534,12 +534,12 @@ CreatePolicy(CreatePolicyStmt *stmt)
 
 	qual = transformWhereClause(qual_pstate,
 								copyObject(stmt->qual),
-								EXPR_KIND_WHERE,
+								EXPR_KIND_POLICY,
 								"POLICY");
 
 	with_check_qual = transformWhereClause(with_check_pstate,
 										   copyObject(stmt->with_check),
-										   EXPR_KIND_WHERE,
+										   EXPR_KIND_POLICY,
 										   "POLICY");
 
 	/* Fix up collation information */
@@ -707,7 +707,7 @@ AlterPolicy(AlterPolicyStmt *stmt)
 		addRTEtoQuery(qual_pstate, rte, false, true, true);
 
 		qual = transformWhereClause(qual_pstate, copyObject(stmt->qual),
-									EXPR_KIND_WHERE,
+									EXPR_KIND_POLICY,
 									"POLICY");
 
 		/* Fix up collation information */
@@ -730,7 +730,7 @@ AlterPolicy(AlterPolicyStmt *stmt)
 
 		with_check_qual = transformWhereClause(with_check_pstate,
 											   copyObject(stmt->with_check),
-											   EXPR_KIND_WHERE,
+											   EXPR_KIND_POLICY,
 											   "POLICY");
 
 		/* Fix up collation information */
diff --git a/src/backend/parser/parse_agg.c b/src/backend/parser/parse_agg.c
index 478d8ca70b..3846b569d6 100644
--- a/src/backend/parser/parse_agg.c
+++ b/src/backend/parser/parse_agg.c
@@ -372,6 +372,13 @@ check_agglevels_and_constraints(ParseState *pstate, Node *expr)
 			break;
 		case EXPR_KIND_WHERE:
 			errkind = true;
+			break;
+		case EXPR_KIND_POLICY:
+			if (isAgg)
+				err = _("aggregate functions are not allowed in policy expressions");
+			else
+				err = _("grouping operations are not allowed in policy expressions");
+
 			break;
 		case EXPR_KIND_HAVING:
 			/* okay */
@@ -770,6 +777,9 @@ transformWindowFuncCall(ParseState *pstate, WindowFunc *wfunc,
 		case EXPR_KIND_WHERE:
 			errkind = true;
 			break;
+		case EXPR_KIND_POLICY:
+			err = _("window functions are not allowed in policy expressions");
+			break;
 		case EXPR_KIND_HAVING:
 			errkind = true;
 			break;
diff --git a/src/backend/parser/parse_expr.c b/src/backend/parser/parse_expr.c
index 0ff46dd457..fa77ef1f8b 100644
--- a/src/backend/parser/parse_expr.c
+++ b/src/backend/parser/parse_expr.c
@@ -1672,6 +1672,7 @@ transformSubLink(ParseState *pstate, SubLink *sublink)
 		case EXPR_KIND_FROM_SUBSELECT:
 		case EXPR_KIND_FROM_FUNCTION:
 		case EXPR_KIND_WHERE:
+		case EXPR_KIND_POLICY:
 		case EXPR_KIND_HAVING:
 		case EXPR_KIND_FILTER:
 		case EXPR_KIND_WINDOW_PARTITION:
@@ -3173,6 +3174,8 @@ ParseExprKindName(ParseExprKind exprKind)
 			return "function in FROM";
 		case EXPR_KIND_WHERE:
 			return "WHERE";
+		case EXPR_KIND_POLICY:
+			return "POLICY";
 		case EXPR_KIND_HAVING:
 			return "HAVING";
 		case EXPR_KIND_FILTER:
diff --git a/src/include/parser/parse_node.h b/src/include/parser/parse_node.h
index 7ecaffc0dc..5249945369 100644
--- a/src/include/parser/parse_node.h
+++ b/src/include/parser/parse_node.h
@@ -63,7 +63,8 @@ typedef enum ParseExprKind
 	EXPR_KIND_INDEX_PREDICATE,	/* index predicate */
 	EXPR_KIND_ALTER_COL_TRANSFORM,		/* transform expr in ALTER COLUMN TYPE */
 	EXPR_KIND_EXECUTE_PARAMETER,	/* parameter value in EXECUTE */
-	EXPR_KIND_TRIGGER_WHEN		/* WHEN condition in CREATE TRIGGER */
+	EXPR_KIND_TRIGGER_WHEN,		/* WHEN condition in CREATE TRIGGER */
+	EXPR_KIND_POLICY			/* USING or WITH CHECK expr in policy */
 } ParseExprKind;
 
 
diff --git a/src/test/modules/test_rls_hooks/test_rls_hooks.c b/src/test/modules/test_rls_hooks/test_rls_hooks.c
index 61b62d55b4..d76b17ae46 100644
--- a/src/test/modules/test_rls_hooks/test_rls_hooks.c
+++ b/src/test/modules/test_rls_hooks/test_rls_hooks.c
@@ -106,7 +106,7 @@ test_rls_hooks_permissive(CmdType cmdtype, Relation relation)
 	e = (Node *) makeSimpleA_Expr(AEXPR_OP, "=", (Node *) n, (Node *) c, 0);
 
 	policy->qual = (Expr *) transformWhereClause(qual_pstate, copyObject(e),
-												 EXPR_KIND_WHERE,
+												 EXPR_KIND_POLICY,
 												 "POLICY");
 
 	policy->with_check_qual = copyObject(policy->qual);
@@ -160,7 +160,7 @@ test_rls_hooks_restrictive(CmdType cmdtype, Relation relation)
 	e = (Node *) makeSimpleA_Expr(AEXPR_OP, "=", (Node *) n, (Node *) c, 0);
 
 	policy->qual = (Expr *) transformWhereClause(qual_pstate, copyObject(e),
-												 EXPR_KIND_WHERE,
+												 EXPR_KIND_POLICY,
 												 "POLICY");
 
 	policy->with_check_qual = copyObject(policy->qual);
diff --git a/src/test/regress/expected/rowsecurity.out b/src/test/regress/expected/rowsecurity.out
index b146da373c..b0556c2ff1 100644
--- a/src/test/regress/expected/rowsecurity.out
+++ b/src/test/regress/expected/rowsecurity.out
@@ -3024,6 +3024,15 @@ CREATE RULE "_RETURN" AS ON SELECT TO t DO INSTEAD
   SELECT * FROM generate_series(1,5) t0(c); -- succeeds
 ROLLBACK;
 --
+-- Policy expression handling
+--
+BEGIN;
+SET row_security = FORCE;
+CREATE TABLE t (c) AS VALUES ('bar'::text);
+CREATE POLICY p ON t USING (max(c)); -- fails: aggregate functions are not allowed in policy expressions
+ERROR:  aggregate functions are not allowed in policy expressions
+ROLLBACK;
+--
 -- Clean up objects
 --
 RESET SESSION AUTHORIZATION;
diff --git a/src/test/regress/sql/rowsecurity.sql b/src/test/regress/sql/rowsecurity.sql
index 54f2c89eda..300f34ad4b 100644
--- a/src/test/regress/sql/rowsecurity.sql
+++ b/src/test/regress/sql/rowsecurity.sql
@@ -1289,6 +1289,15 @@ CREATE RULE "_RETURN" AS ON SELECT TO t DO INSTEAD
   SELECT * FROM generate_series(1,5) t0(c); -- succeeds
 ROLLBACK;
 
+--
+-- Policy expression handling
+--
+BEGIN;
+SET row_security = FORCE;
+CREATE TABLE t (c) AS VALUES ('bar'::text);
+CREATE POLICY p ON t USING (max(c)); -- fails: aggregate functions are not allowed in policy expressions
+ROLLBACK;
+
 --
 -- Clean up objects
 --
-- 
2.40.0