From dc095f6bbc31045d62a515aebd5e349bd341845a Mon Sep 17 00:00:00 2001
From: Andreas Lind <andreas.lind@workday.com>
Date: Thu, 25 Sep 2025 21:40:18 +0200
Subject: [PATCH v2 1/5] Support BYPASSLEAKPROOF flag for policies

---
 src/backend/catalog/system_views.sql |  3 ++-
 src/backend/commands/policy.c        |  8 +++++++
 src/backend/parser/gram.y            | 35 +++++++++++++++++++++++++++-
 src/bin/pg_dump/pg_dump.c            | 11 +++++++++
 src/bin/pg_dump/pg_dump.h            |  1 +
 src/include/catalog/catversion.h     |  2 +-
 src/include/catalog/pg_policy.h      |  2 ++
 src/include/nodes/parsenodes.h       | 10 ++++++++
 src/include/parser/kwlist.h          |  2 ++
 9 files changed, 71 insertions(+), 3 deletions(-)

diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index c77fa0234bb..7122e9c2ae4 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -100,7 +100,8 @@ CREATE VIEW pg_policies AS
             WHEN '*' THEN 'ALL'
         END AS cmd,
         pg_catalog.pg_get_expr(pol.polqual, pol.polrelid) AS qual,
-        pg_catalog.pg_get_expr(pol.polwithcheck, pol.polrelid) AS with_check
+        pg_catalog.pg_get_expr(pol.polwithcheck, pol.polrelid) AS with_check,
+        pol.polbypassleakproof AS bypassleakproof
     FROM pg_catalog.pg_policy pol
     JOIN pg_catalog.pg_class C ON (C.oid = pol.polrelid)
     LEFT JOIN pg_catalog.pg_namespace N ON (N.oid = C.relnamespace);
diff --git a/src/backend/commands/policy.c b/src/backend/commands/policy.c
index 83056960fe4..a5b2a9dfc87 100644
--- a/src/backend/commands/policy.c
+++ b/src/backend/commands/policy.c
@@ -694,6 +694,7 @@ CreatePolicy(CreatePolicyStmt *stmt)
 															 CStringGetDatum(stmt->policy_name));
 	values[Anum_pg_policy_polcmd - 1] = CharGetDatum(polcmd);
 	values[Anum_pg_policy_polpermissive - 1] = BoolGetDatum(stmt->permissive);
+	values[Anum_pg_policy_polbypassleakproof - 1] = BoolGetDatum(stmt->bypassleakproof);
 	values[Anum_pg_policy_polroles - 1] = PointerGetDatum(role_ids);
 
 	/* Add qual if present. */
@@ -1036,6 +1037,13 @@ AlterPolicy(AlterPolicyStmt *stmt)
 		}
 	}
 
+	/* Only update bypassleakproof if the clause was specified */
+	if (stmt->bypassleakproof_given)
+	{
+		replaces[Anum_pg_policy_polbypassleakproof - 1] = true;
+		values[Anum_pg_policy_polbypassleakproof - 1] = BoolGetDatum(stmt->bypassleakproof);
+	}
+
 	new_tuple = heap_modify_tuple(policy_tuple,
 								  RelationGetDescr(pg_policy_rel),
 								  values, isnull, replaces);
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 9fd48acb1f8..7de026c0e16 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -268,6 +268,7 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
 	struct KeyAction *keyaction;
 	ReturningClause *retclause;
 	ReturningOptionKind retoptionkind;
+	struct { bool given; bool val; } bypassopt;
 }
 
 %type <node>	stmt toplevel_stmt schema_stmt routine_body_stmt
@@ -395,6 +396,7 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
 %type <boolean> RowSecurityDefaultPermissive
 %type <node>	RowSecurityOptionalWithCheck RowSecurityOptionalExpr
 %type <list>	RowSecurityDefaultToRole RowSecurityOptionalToRole
+%type <bypassopt>	RowSecurityOptionalBypassleakproof
 
 %type <str>		iso_level opt_encoding
 %type <rolespec> grantee
@@ -702,7 +704,7 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
 	ASENSITIVE ASSERTION ASSIGNMENT ASYMMETRIC ATOMIC AT ATTACH ATTRIBUTE AUTHORIZATION
 
 	BACKWARD BEFORE BEGIN_P BETWEEN BIGINT BINARY BIT
-	BOOLEAN_P BOTH BREADTH BY
+	BOOLEAN_P BOTH BREADTH BY BYPASSLEAKPROOF
 
 	CACHE CALL CALLED CASCADE CASCADED CASE CAST CATALOG_P CHAIN CHAR_P
 	CHARACTER CHARACTERISTICS CHECK CHECKPOINT CLASS CLOSE
@@ -747,6 +749,7 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
 	MINUTE_P MINVALUE MODE MONTH_P MOVE
 
 	NAME_P NAMES NATIONAL NATURAL NCHAR NESTED NEW NEXT NFC NFD NFKC NFKD NO
+	NOBYPASSLEAKPROOF
 	NONE NORMALIZE NORMALIZED
 	NOT NOTHING NOTIFY NOTNULL NOWAIT NULL_P NULLIF
 	NULLS_P NUMERIC
@@ -5929,8 +5932,10 @@ AlterUserMappingStmt: ALTER USER MAPPING FOR auth_ident SERVER name alter_generi
  *					[FOR { SELECT | INSERT | UPDATE | DELETE } ]
  *					[TO role, ...]
  *					[USING (qual)] [WITH CHECK (with check qual)]
+ *					[{ BYPASSLEAKPROOF | NOBYPASSLEAKPROOF }]
  *				ALTER POLICY name ON table [TO role, ...]
  *					[USING (qual)] [WITH CHECK (with check qual)]
+ *					[{ BYPASSLEAKPROOF | NOBYPASSLEAKPROOF }]
  *
  *****************************************************************************/
 
@@ -5938,6 +5943,7 @@ CreatePolicyStmt:
 			CREATE POLICY name ON qualified_name RowSecurityDefaultPermissive
 				RowSecurityDefaultForCmd RowSecurityDefaultToRole
 				RowSecurityOptionalExpr RowSecurityOptionalWithCheck
+				RowSecurityOptionalBypassleakproof
 				{
 					CreatePolicyStmt *n = makeNode(CreatePolicyStmt);
 
@@ -5948,6 +5954,8 @@ CreatePolicyStmt:
 					n->roles = $8;
 					n->qual = $9;
 					n->with_check = $10;
+					n->bypassleakproof = $11.val;
+					n->bypassleakproof_given = $11.given;
 					$$ = (Node *) n;
 				}
 		;
@@ -5955,6 +5963,7 @@ CreatePolicyStmt:
 AlterPolicyStmt:
 			ALTER POLICY name ON qualified_name RowSecurityOptionalToRole
 				RowSecurityOptionalExpr RowSecurityOptionalWithCheck
+				RowSecurityOptionalBypassleakproof
 				{
 					AlterPolicyStmt *n = makeNode(AlterPolicyStmt);
 
@@ -5963,6 +5972,8 @@ AlterPolicyStmt:
 					n->roles = $6;
 					n->qual = $7;
 					n->with_check = $8;
+					n->bypassleakproof = $9.val;
+					n->bypassleakproof_given = $9.given;
 					$$ = (Node *) n;
 				}
 		;
@@ -6005,6 +6016,24 @@ RowSecurityDefaultPermissive:
 			| /* EMPTY */			{ $$ = true; }
 		;
 
+RowSecurityOptionalBypassleakproof:
+			BYPASSLEAKPROOF
+				{
+					$$.given = true;
+					$$.val = true;
+				}
+			| NOBYPASSLEAKPROOF
+				{
+					$$.given = true;
+					$$.val = false;
+				}
+			| /* EMPTY */
+				{
+					$$.given = false;
+					$$.val = false;
+				}
+		;
+
 RowSecurityDefaultForCmd:
 			FOR row_security_cmd	{ $$ = $2; }
 			| /* EMPTY */			{ $$ = "all"; }
@@ -17755,6 +17784,7 @@ unreserved_keyword:
 			| BEGIN_P
 			| BREADTH
 			| BY
+			| BYPASSLEAKPROOF
 			| CACHE
 			| CALL
 			| CALLED
@@ -17904,6 +17934,7 @@ unreserved_keyword:
 			| NFKC
 			| NFKD
 			| NO
+			| NOBYPASSLEAKPROOF
 			| NORMALIZED
 			| NOTHING
 			| NOTIFY
@@ -18310,6 +18341,7 @@ bare_label_keyword:
 			| BOTH
 			| BREADTH
 			| BY
+			| BYPASSLEAKPROOF
 			| CACHE
 			| CALL
 			| CALLED
@@ -18521,6 +18553,7 @@ bare_label_keyword:
 			| NFKC
 			| NFKD
 			| NO
+			| NOBYPASSLEAKPROOF
 			| NONE
 			| NORMALIZE
 			| NORMALIZED
diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c
index 9fc3671cb35..f4a1df3af85 100644
--- a/src/bin/pg_dump/pg_dump.c
+++ b/src/bin/pg_dump/pg_dump.c
@@ -4243,6 +4243,7 @@ getPolicies(Archive *fout, TableInfo tblinfo[], int numTables)
 	int			i_polname;
 	int			i_polcmd;
 	int			i_polpermissive;
+	int			i_polbypassleakproof;
 	int			i_polroles;
 	int			i_polqual;
 	int			i_polwithcheck;
@@ -4306,6 +4307,7 @@ getPolicies(Archive *fout, TableInfo tblinfo[], int numTables)
 			polinfo->polname = NULL;
 			polinfo->polcmd = '\0';
 			polinfo->polpermissive = 0;
+			polinfo->polbypassleakproof = 0;
 			polinfo->polroles = NULL;
 			polinfo->polqual = NULL;
 			polinfo->polwithcheck = NULL;
@@ -4327,6 +4329,10 @@ getPolicies(Archive *fout, TableInfo tblinfo[], int numTables)
 		appendPQExpBufferStr(query, "pol.polpermissive, ");
 	else
 		appendPQExpBufferStr(query, "'t' as polpermissive, ");
+	if (fout->remoteVersion >= 190000)	/* polbypassleakproof added in v19 */
+		appendPQExpBufferStr(query, "pol.polbypassleakproof, ");
+	else
+		appendPQExpBufferStr(query, "'f' as polbypassleakproof, ");
 	appendPQExpBuffer(query,
 					  "CASE WHEN pol.polroles = '{0}' THEN NULL ELSE "
 					  "   pg_catalog.array_to_string(ARRAY(SELECT pg_catalog.quote_ident(rolname) from pg_catalog.pg_roles WHERE oid = ANY(pol.polroles)), ', ') END AS polroles, "
@@ -4347,6 +4353,7 @@ getPolicies(Archive *fout, TableInfo tblinfo[], int numTables)
 		i_polname = PQfnumber(res, "polname");
 		i_polcmd = PQfnumber(res, "polcmd");
 		i_polpermissive = PQfnumber(res, "polpermissive");
+		i_polbypassleakproof = PQfnumber(res, "polbypassleakproof");
 		i_polroles = PQfnumber(res, "polroles");
 		i_polqual = PQfnumber(res, "polqual");
 		i_polwithcheck = PQfnumber(res, "polwithcheck");
@@ -4372,6 +4379,7 @@ getPolicies(Archive *fout, TableInfo tblinfo[], int numTables)
 
 			polinfo[j].polcmd = *(PQgetvalue(res, j, i_polcmd));
 			polinfo[j].polpermissive = *(PQgetvalue(res, j, i_polpermissive)) == 't';
+			polinfo[j].polbypassleakproof = *(PQgetvalue(res, j, i_polbypassleakproof)) == 't';
 
 			if (PQgetisnull(res, j, i_polroles))
 				polinfo[j].polroles = NULL;
@@ -4483,6 +4491,9 @@ dumpPolicy(Archive *fout, const PolicyInfo *polinfo)
 	if (polinfo->polwithcheck != NULL)
 		appendPQExpBuffer(query, " WITH CHECK (%s)", polinfo->polwithcheck);
 
+	if (polinfo->polbypassleakproof)
+		appendPQExpBufferStr(query, " BYPASSLEAKPROOF");
+
 	appendPQExpBufferStr(query, ";\n");
 
 	appendPQExpBuffer(delqry, "DROP POLICY %s", fmtId(polinfo->polname));
diff --git a/src/bin/pg_dump/pg_dump.h b/src/bin/pg_dump/pg_dump.h
index bcc94ff07cc..7d616044913 100644
--- a/src/bin/pg_dump/pg_dump.h
+++ b/src/bin/pg_dump/pg_dump.h
@@ -656,6 +656,7 @@ typedef struct _policyInfo
 	char	   *polname;		/* null indicates RLS is enabled on rel */
 	char		polcmd;
 	bool		polpermissive;
+	bool		polbypassleakproof;
 	char	   *polroles;
 	char	   *polqual;
 	char	   *polwithcheck;
diff --git a/src/include/catalog/catversion.h b/src/include/catalog/catversion.h
index 62c21d3670d..6564e7ab6e1 100644
--- a/src/include/catalog/catversion.h
+++ b/src/include/catalog/catversion.h
@@ -57,6 +57,6 @@
  */
 
 /*							yyyymmddN */
-#define CATALOG_VERSION_NO	202509191
+#define CATALOG_VERSION_NO	202509241
 
 #endif
diff --git a/src/include/catalog/pg_policy.h b/src/include/catalog/pg_policy.h
index 3c2498cdf11..2b202c18973 100644
--- a/src/include/catalog/pg_policy.h
+++ b/src/include/catalog/pg_policy.h
@@ -34,6 +34,8 @@ CATALOG(pg_policy,3256,PolicyRelationId)
 												 * policy. */
 	char		polcmd;			/* One of ACL_*_CHR, or '*' for all */
 	bool		polpermissive;	/* restrictive or permissive policy */
+	bool		polbypassleakproof; /* does the policy bypass the leakproof
+									 * requirement for functions? */
 
 #ifdef CATALOG_VARLEN
 	/* Roles to which the policy is applied; zero means PUBLIC */
diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h
index 4ed14fc5b78..d4a56b0ac3a 100644
--- a/src/include/nodes/parsenodes.h
+++ b/src/include/nodes/parsenodes.h
@@ -3068,6 +3068,11 @@ typedef struct CreatePolicyStmt
 	List	   *roles;			/* the roles associated with the policy */
 	Node	   *qual;			/* the policy's condition */
 	Node	   *with_check;		/* the policy's WITH CHECK condition. */
+	bool		bypassleakproof;	/* does the policy bypass the leakproof
+									 * requirement for functions? */
+	bool		bypassleakproof_given;	/* whether
+										 * BYPASSLEAKPROOF/NOBYPASSLEAKPROOF
+										 * appeared */
 } CreatePolicyStmt;
 
 /*----------------------
@@ -3082,6 +3087,11 @@ typedef struct AlterPolicyStmt
 	List	   *roles;			/* the roles associated with the policy */
 	Node	   *qual;			/* the policy's condition */
 	Node	   *with_check;		/* the policy's WITH CHECK condition. */
+	bool		bypassleakproof;	/* does the policy bypass the leakproof
+									 * requirement for functions? */
+	bool		bypassleakproof_given;	/* whether
+										 * BYPASSLEAKPROOF/NOBYPASSLEAKPROOF
+										 * appeared */
 } AlterPolicyStmt;
 
 /*----------------------
diff --git a/src/include/parser/kwlist.h b/src/include/parser/kwlist.h
index a4af3f717a1..65248b076bf 100644
--- a/src/include/parser/kwlist.h
+++ b/src/include/parser/kwlist.h
@@ -65,6 +65,7 @@ PG_KEYWORD("boolean", BOOLEAN_P, COL_NAME_KEYWORD, BARE_LABEL)
 PG_KEYWORD("both", BOTH, RESERVED_KEYWORD, BARE_LABEL)
 PG_KEYWORD("breadth", BREADTH, UNRESERVED_KEYWORD, BARE_LABEL)
 PG_KEYWORD("by", BY, UNRESERVED_KEYWORD, BARE_LABEL)
+PG_KEYWORD("bypassleakproof", BYPASSLEAKPROOF, UNRESERVED_KEYWORD, BARE_LABEL)
 PG_KEYWORD("cache", CACHE, UNRESERVED_KEYWORD, BARE_LABEL)
 PG_KEYWORD("call", CALL, UNRESERVED_KEYWORD, BARE_LABEL)
 PG_KEYWORD("called", CALLED, UNRESERVED_KEYWORD, BARE_LABEL)
@@ -295,6 +296,7 @@ PG_KEYWORD("nfd", NFD, UNRESERVED_KEYWORD, BARE_LABEL)
 PG_KEYWORD("nfkc", NFKC, UNRESERVED_KEYWORD, BARE_LABEL)
 PG_KEYWORD("nfkd", NFKD, UNRESERVED_KEYWORD, BARE_LABEL)
 PG_KEYWORD("no", NO, UNRESERVED_KEYWORD, BARE_LABEL)
+PG_KEYWORD("nobypassleakproof", NOBYPASSLEAKPROOF, UNRESERVED_KEYWORD, BARE_LABEL)
 PG_KEYWORD("none", NONE, COL_NAME_KEYWORD, BARE_LABEL)
 PG_KEYWORD("normalize", NORMALIZE, COL_NAME_KEYWORD, BARE_LABEL)
 PG_KEYWORD("normalized", NORMALIZED, UNRESERVED_KEYWORD, BARE_LABEL)
-- 
2.50.1 (Apple Git-155)

