1 /*-------------------------------------------------------------------------
2  *
3  * policy.c
4  *	  Commands for manipulating policies.
5  *
6  * Portions Copyright (c) 1996-2021, PostgreSQL Global Development Group
7  * Portions Copyright (c) 1994, Regents of the University of California
8  *
9  * src/backend/commands/policy.c
10  *
11  *-------------------------------------------------------------------------
12  */
13 #include "postgres.h"
14 
15 #include "access/genam.h"
16 #include "access/htup.h"
17 #include "access/htup_details.h"
18 #include "access/relation.h"
19 #include "access/sysattr.h"
20 #include "access/table.h"
21 #include "access/xact.h"
22 #include "catalog/catalog.h"
23 #include "catalog/dependency.h"
24 #include "catalog/indexing.h"
25 #include "catalog/namespace.h"
26 #include "catalog/objectaccess.h"
27 #include "catalog/pg_authid.h"
28 #include "catalog/pg_policy.h"
29 #include "catalog/pg_type.h"
30 #include "commands/policy.h"
31 #include "miscadmin.h"
32 #include "nodes/makefuncs.h"
33 #include "nodes/pg_list.h"
34 #include "parser/parse_clause.h"
35 #include "parser/parse_collate.h"
36 #include "parser/parse_node.h"
37 #include "parser/parse_relation.h"
38 #include "rewrite/rewriteManip.h"
39 #include "rewrite/rowsecurity.h"
40 #include "storage/lock.h"
41 #include "utils/acl.h"
42 #include "utils/array.h"
43 #include "utils/builtins.h"
44 #include "utils/fmgroids.h"
45 #include "utils/inval.h"
46 #include "utils/lsyscache.h"
47 #include "utils/memutils.h"
48 #include "utils/rel.h"
49 #include "utils/syscache.h"
50 
51 static void RangeVarCallbackForPolicy(const RangeVar *rv,
52 									  Oid relid, Oid oldrelid, void *arg);
53 static char parse_policy_command(const char *cmd_name);
54 static Datum *policy_role_list_to_array(List *roles, int *num_roles);
55 
56 /*
57  * Callback to RangeVarGetRelidExtended().
58  *
59  * Checks the following:
60  *	- the relation specified is a table.
61  *	- current user owns the table.
62  *	- the table is not a system table.
63  *
64  * If any of these checks fails then an error is raised.
65  */
66 static void
RangeVarCallbackForPolicy(const RangeVar * rv,Oid relid,Oid oldrelid,void * arg)67 RangeVarCallbackForPolicy(const RangeVar *rv, Oid relid, Oid oldrelid,
68 						  void *arg)
69 {
70 	HeapTuple	tuple;
71 	Form_pg_class classform;
72 	char		relkind;
73 
74 	tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relid));
75 	if (!HeapTupleIsValid(tuple))
76 		return;
77 
78 	classform = (Form_pg_class) GETSTRUCT(tuple);
79 	relkind = classform->relkind;
80 
81 	/* Must own relation. */
82 	if (!pg_class_ownercheck(relid, GetUserId()))
83 		aclcheck_error(ACLCHECK_NOT_OWNER, get_relkind_objtype(get_rel_relkind(relid)), rv->relname);
84 
85 	/* No system table modifications unless explicitly allowed. */
86 	if (!allowSystemTableMods && IsSystemClass(relid, classform))
87 		ereport(ERROR,
88 				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
89 				 errmsg("permission denied: \"%s\" is a system catalog",
90 						rv->relname)));
91 
92 	/* Relation type MUST be a table. */
93 	if (relkind != RELKIND_RELATION && relkind != RELKIND_PARTITIONED_TABLE)
94 		ereport(ERROR,
95 				(errcode(ERRCODE_WRONG_OBJECT_TYPE),
96 				 errmsg("\"%s\" is not a table", rv->relname)));
97 
98 	ReleaseSysCache(tuple);
99 }
100 
101 /*
102  * parse_policy_command -
103  *	 helper function to convert full command strings to their char
104  *	 representation.
105  *
106  * cmd_name - full string command name. Valid values are 'all', 'select',
107  *			  'insert', 'update' and 'delete'.
108  *
109  */
110 static char
parse_policy_command(const char * cmd_name)111 parse_policy_command(const char *cmd_name)
112 {
113 	char		polcmd;
114 
115 	if (!cmd_name)
116 		elog(ERROR, "unrecognized policy command");
117 
118 	if (strcmp(cmd_name, "all") == 0)
119 		polcmd = '*';
120 	else if (strcmp(cmd_name, "select") == 0)
121 		polcmd = ACL_SELECT_CHR;
122 	else if (strcmp(cmd_name, "insert") == 0)
123 		polcmd = ACL_INSERT_CHR;
124 	else if (strcmp(cmd_name, "update") == 0)
125 		polcmd = ACL_UPDATE_CHR;
126 	else if (strcmp(cmd_name, "delete") == 0)
127 		polcmd = ACL_DELETE_CHR;
128 	else
129 		elog(ERROR, "unrecognized policy command");
130 
131 	return polcmd;
132 }
133 
134 /*
135  * policy_role_list_to_array
136  *	 helper function to convert a list of RoleSpecs to an array of
137  *	 role id Datums.
138  */
139 static Datum *
policy_role_list_to_array(List * roles,int * num_roles)140 policy_role_list_to_array(List *roles, int *num_roles)
141 {
142 	Datum	   *role_oids;
143 	ListCell   *cell;
144 	int			i = 0;
145 
146 	/* Handle no roles being passed in as being for public */
147 	if (roles == NIL)
148 	{
149 		*num_roles = 1;
150 		role_oids = (Datum *) palloc(*num_roles * sizeof(Datum));
151 		role_oids[0] = ObjectIdGetDatum(ACL_ID_PUBLIC);
152 
153 		return role_oids;
154 	}
155 
156 	*num_roles = list_length(roles);
157 	role_oids = (Datum *) palloc(*num_roles * sizeof(Datum));
158 
159 	foreach(cell, roles)
160 	{
161 		RoleSpec   *spec = lfirst(cell);
162 
163 		/*
164 		 * PUBLIC covers all roles, so it only makes sense alone.
165 		 */
166 		if (spec->roletype == ROLESPEC_PUBLIC)
167 		{
168 			if (*num_roles != 1)
169 			{
170 				ereport(WARNING,
171 						(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
172 						 errmsg("ignoring specified roles other than PUBLIC"),
173 						 errhint("All roles are members of the PUBLIC role.")));
174 				*num_roles = 1;
175 			}
176 			role_oids[0] = ObjectIdGetDatum(ACL_ID_PUBLIC);
177 
178 			return role_oids;
179 		}
180 		else
181 			role_oids[i++] =
182 				ObjectIdGetDatum(get_rolespec_oid(spec, false));
183 	}
184 
185 	return role_oids;
186 }
187 
188 /*
189  * Load row security policy from the catalog, and store it in
190  * the relation's relcache entry.
191  *
192  * Note that caller should have verified that pg_class.relrowsecurity
193  * is true for this relation.
194  */
195 void
RelationBuildRowSecurity(Relation relation)196 RelationBuildRowSecurity(Relation relation)
197 {
198 	MemoryContext rscxt;
199 	MemoryContext oldcxt = CurrentMemoryContext;
200 	RowSecurityDesc *rsdesc;
201 	Relation	catalog;
202 	ScanKeyData skey;
203 	SysScanDesc sscan;
204 	HeapTuple	tuple;
205 
206 	/*
207 	 * Create a memory context to hold everything associated with this
208 	 * relation's row security policy.  This makes it easy to clean up during
209 	 * a relcache flush.  However, to cover the possibility of an error
210 	 * partway through, we don't make the context long-lived till we're done.
211 	 */
212 	rscxt = AllocSetContextCreate(CurrentMemoryContext,
213 								  "row security descriptor",
214 								  ALLOCSET_SMALL_SIZES);
215 	MemoryContextCopyAndSetIdentifier(rscxt,
216 									  RelationGetRelationName(relation));
217 
218 	rsdesc = MemoryContextAllocZero(rscxt, sizeof(RowSecurityDesc));
219 	rsdesc->rscxt = rscxt;
220 
221 	/*
222 	 * Now scan pg_policy for RLS policies associated with this relation.
223 	 * Because we use the index on (polrelid, polname), we should consistently
224 	 * visit the rel's policies in name order, at least when system indexes
225 	 * aren't disabled.  This simplifies equalRSDesc().
226 	 */
227 	catalog = table_open(PolicyRelationId, AccessShareLock);
228 
229 	ScanKeyInit(&skey,
230 				Anum_pg_policy_polrelid,
231 				BTEqualStrategyNumber, F_OIDEQ,
232 				ObjectIdGetDatum(RelationGetRelid(relation)));
233 
234 	sscan = systable_beginscan(catalog, PolicyPolrelidPolnameIndexId, true,
235 							   NULL, 1, &skey);
236 
237 	while (HeapTupleIsValid(tuple = systable_getnext(sscan)))
238 	{
239 		Form_pg_policy policy_form = (Form_pg_policy) GETSTRUCT(tuple);
240 		RowSecurityPolicy *policy;
241 		Datum		datum;
242 		bool		isnull;
243 		char	   *str_value;
244 
245 		policy = MemoryContextAllocZero(rscxt, sizeof(RowSecurityPolicy));
246 
247 		/*
248 		 * Note: we must be sure that pass-by-reference data gets copied into
249 		 * rscxt.  We avoid making that context current over wider spans than
250 		 * we have to, though.
251 		 */
252 
253 		/* Get policy command */
254 		policy->polcmd = policy_form->polcmd;
255 
256 		/* Get policy, permissive or restrictive */
257 		policy->permissive = policy_form->polpermissive;
258 
259 		/* Get policy name */
260 		policy->policy_name =
261 			MemoryContextStrdup(rscxt, NameStr(policy_form->polname));
262 
263 		/* Get policy roles */
264 		datum = heap_getattr(tuple, Anum_pg_policy_polroles,
265 							 RelationGetDescr(catalog), &isnull);
266 		/* shouldn't be null, but let's check for luck */
267 		if (isnull)
268 			elog(ERROR, "unexpected null value in pg_policy.polroles");
269 		MemoryContextSwitchTo(rscxt);
270 		policy->roles = DatumGetArrayTypePCopy(datum);
271 		MemoryContextSwitchTo(oldcxt);
272 
273 		/* Get policy qual */
274 		datum = heap_getattr(tuple, Anum_pg_policy_polqual,
275 							 RelationGetDescr(catalog), &isnull);
276 		if (!isnull)
277 		{
278 			str_value = TextDatumGetCString(datum);
279 			MemoryContextSwitchTo(rscxt);
280 			policy->qual = (Expr *) stringToNode(str_value);
281 			MemoryContextSwitchTo(oldcxt);
282 			pfree(str_value);
283 		}
284 		else
285 			policy->qual = NULL;
286 
287 		/* Get WITH CHECK qual */
288 		datum = heap_getattr(tuple, Anum_pg_policy_polwithcheck,
289 							 RelationGetDescr(catalog), &isnull);
290 		if (!isnull)
291 		{
292 			str_value = TextDatumGetCString(datum);
293 			MemoryContextSwitchTo(rscxt);
294 			policy->with_check_qual = (Expr *) stringToNode(str_value);
295 			MemoryContextSwitchTo(oldcxt);
296 			pfree(str_value);
297 		}
298 		else
299 			policy->with_check_qual = NULL;
300 
301 		/* We want to cache whether there are SubLinks in these expressions */
302 		policy->hassublinks = checkExprHasSubLink((Node *) policy->qual) ||
303 			checkExprHasSubLink((Node *) policy->with_check_qual);
304 
305 		/*
306 		 * Add this object to list.  For historical reasons, the list is built
307 		 * in reverse order.
308 		 */
309 		MemoryContextSwitchTo(rscxt);
310 		rsdesc->policies = lcons(policy, rsdesc->policies);
311 		MemoryContextSwitchTo(oldcxt);
312 	}
313 
314 	systable_endscan(sscan);
315 	table_close(catalog, AccessShareLock);
316 
317 	/*
318 	 * Success.  Reparent the descriptor's memory context under
319 	 * CacheMemoryContext so that it will live indefinitely, then attach the
320 	 * policy descriptor to the relcache entry.
321 	 */
322 	MemoryContextSetParent(rscxt, CacheMemoryContext);
323 
324 	relation->rd_rsdesc = rsdesc;
325 }
326 
327 /*
328  * RemovePolicyById -
329  *	 remove a policy by its OID.  If a policy does not exist with the provided
330  *	 oid, then an error is raised.
331  *
332  * policy_id - the oid of the policy.
333  */
334 void
RemovePolicyById(Oid policy_id)335 RemovePolicyById(Oid policy_id)
336 {
337 	Relation	pg_policy_rel;
338 	SysScanDesc sscan;
339 	ScanKeyData skey[1];
340 	HeapTuple	tuple;
341 	Oid			relid;
342 	Relation	rel;
343 
344 	pg_policy_rel = table_open(PolicyRelationId, RowExclusiveLock);
345 
346 	/*
347 	 * Find the policy to delete.
348 	 */
349 	ScanKeyInit(&skey[0],
350 				Anum_pg_policy_oid,
351 				BTEqualStrategyNumber, F_OIDEQ,
352 				ObjectIdGetDatum(policy_id));
353 
354 	sscan = systable_beginscan(pg_policy_rel, PolicyOidIndexId, true,
355 							   NULL, 1, skey);
356 
357 	tuple = systable_getnext(sscan);
358 
359 	/* If the policy exists, then remove it, otherwise raise an error. */
360 	if (!HeapTupleIsValid(tuple))
361 		elog(ERROR, "could not find tuple for policy %u", policy_id);
362 
363 	/*
364 	 * Open and exclusive-lock the relation the policy belongs to.  (We need
365 	 * exclusive lock to lock out queries that might otherwise depend on the
366 	 * set of policies the rel has; furthermore we've got to hold the lock
367 	 * till commit.)
368 	 */
369 	relid = ((Form_pg_policy) GETSTRUCT(tuple))->polrelid;
370 
371 	rel = table_open(relid, AccessExclusiveLock);
372 	if (rel->rd_rel->relkind != RELKIND_RELATION &&
373 		rel->rd_rel->relkind != RELKIND_PARTITIONED_TABLE)
374 		ereport(ERROR,
375 				(errcode(ERRCODE_WRONG_OBJECT_TYPE),
376 				 errmsg("\"%s\" is not a table",
377 						RelationGetRelationName(rel))));
378 
379 	if (!allowSystemTableMods && IsSystemRelation(rel))
380 		ereport(ERROR,
381 				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
382 				 errmsg("permission denied: \"%s\" is a system catalog",
383 						RelationGetRelationName(rel))));
384 
385 	CatalogTupleDelete(pg_policy_rel, &tuple->t_self);
386 
387 	systable_endscan(sscan);
388 
389 	/*
390 	 * Note that, unlike some of the other flags in pg_class, relrowsecurity
391 	 * is not just an indication of if policies exist.  When relrowsecurity is
392 	 * set by a user, then all access to the relation must be through a
393 	 * policy.  If no policy is defined for the relation then a default-deny
394 	 * policy is created and all records are filtered (except for queries from
395 	 * the owner).
396 	 */
397 	CacheInvalidateRelcache(rel);
398 
399 	table_close(rel, NoLock);
400 
401 	/* Clean up */
402 	table_close(pg_policy_rel, RowExclusiveLock);
403 }
404 
405 /*
406  * RemoveRoleFromObjectPolicy -
407  *	 remove a role from a policy's applicable-roles list.
408  *
409  * Returns true if the role was successfully removed from the policy.
410  * Returns false if the role was not removed because it would have left
411  * polroles empty (which is disallowed, though perhaps it should not be).
412  * On false return, the caller should instead drop the policy altogether.
413  *
414  * roleid - the oid of the role to remove
415  * classid - should always be PolicyRelationId
416  * policy_id - the oid of the policy.
417  */
418 bool
RemoveRoleFromObjectPolicy(Oid roleid,Oid classid,Oid policy_id)419 RemoveRoleFromObjectPolicy(Oid roleid, Oid classid, Oid policy_id)
420 {
421 	Relation	pg_policy_rel;
422 	SysScanDesc sscan;
423 	ScanKeyData skey[1];
424 	HeapTuple	tuple;
425 	Oid			relid;
426 	ArrayType  *policy_roles;
427 	Datum		roles_datum;
428 	Oid		   *roles;
429 	int			num_roles;
430 	Datum	   *role_oids;
431 	bool		attr_isnull;
432 	bool		keep_policy = true;
433 	int			i,
434 				j;
435 
436 	Assert(classid == PolicyRelationId);
437 
438 	pg_policy_rel = table_open(PolicyRelationId, RowExclusiveLock);
439 
440 	/*
441 	 * Find the policy to update.
442 	 */
443 	ScanKeyInit(&skey[0],
444 				Anum_pg_policy_oid,
445 				BTEqualStrategyNumber, F_OIDEQ,
446 				ObjectIdGetDatum(policy_id));
447 
448 	sscan = systable_beginscan(pg_policy_rel, PolicyOidIndexId, true,
449 							   NULL, 1, skey);
450 
451 	tuple = systable_getnext(sscan);
452 
453 	/* Raise an error if we don't find the policy. */
454 	if (!HeapTupleIsValid(tuple))
455 		elog(ERROR, "could not find tuple for policy %u", policy_id);
456 
457 	/* Identify rel the policy belongs to */
458 	relid = ((Form_pg_policy) GETSTRUCT(tuple))->polrelid;
459 
460 	/* Get the current set of roles */
461 	roles_datum = heap_getattr(tuple,
462 							   Anum_pg_policy_polroles,
463 							   RelationGetDescr(pg_policy_rel),
464 							   &attr_isnull);
465 
466 	Assert(!attr_isnull);
467 
468 	policy_roles = DatumGetArrayTypePCopy(roles_datum);
469 	roles = (Oid *) ARR_DATA_PTR(policy_roles);
470 	num_roles = ARR_DIMS(policy_roles)[0];
471 
472 	/*
473 	 * Rebuild the polroles array, without any mentions of the target role.
474 	 * Ordinarily there'd be exactly one, but we must cope with duplicate
475 	 * mentions, since CREATE/ALTER POLICY historically have allowed that.
476 	 */
477 	role_oids = (Datum *) palloc(num_roles * sizeof(Datum));
478 	for (i = 0, j = 0; i < num_roles; i++)
479 	{
480 		if (roles[i] != roleid)
481 			role_oids[j++] = ObjectIdGetDatum(roles[i]);
482 	}
483 	num_roles = j;
484 
485 	/* If any roles remain, update the policy entry. */
486 	if (num_roles > 0)
487 	{
488 		ArrayType  *role_ids;
489 		Datum		values[Natts_pg_policy];
490 		bool		isnull[Natts_pg_policy];
491 		bool		replaces[Natts_pg_policy];
492 		HeapTuple	new_tuple;
493 		HeapTuple	reltup;
494 		ObjectAddress target;
495 		ObjectAddress myself;
496 
497 		/* zero-clear */
498 		memset(values, 0, sizeof(values));
499 		memset(replaces, 0, sizeof(replaces));
500 		memset(isnull, 0, sizeof(isnull));
501 
502 		/* This is the array for the new tuple */
503 		role_ids = construct_array(role_oids, num_roles, OIDOID,
504 								   sizeof(Oid), true, TYPALIGN_INT);
505 
506 		replaces[Anum_pg_policy_polroles - 1] = true;
507 		values[Anum_pg_policy_polroles - 1] = PointerGetDatum(role_ids);
508 
509 		new_tuple = heap_modify_tuple(tuple,
510 									  RelationGetDescr(pg_policy_rel),
511 									  values, isnull, replaces);
512 		CatalogTupleUpdate(pg_policy_rel, &new_tuple->t_self, new_tuple);
513 
514 		/* Remove all the old shared dependencies (roles) */
515 		deleteSharedDependencyRecordsFor(PolicyRelationId, policy_id, 0);
516 
517 		/* Record the new shared dependencies (roles) */
518 		myself.classId = PolicyRelationId;
519 		myself.objectId = policy_id;
520 		myself.objectSubId = 0;
521 
522 		target.classId = AuthIdRelationId;
523 		target.objectSubId = 0;
524 		for (i = 0; i < num_roles; i++)
525 		{
526 			target.objectId = DatumGetObjectId(role_oids[i]);
527 			/* no need for dependency on the public role */
528 			if (target.objectId != ACL_ID_PUBLIC)
529 				recordSharedDependencyOn(&myself, &target,
530 										 SHARED_DEPENDENCY_POLICY);
531 		}
532 
533 		InvokeObjectPostAlterHook(PolicyRelationId, policy_id, 0);
534 
535 		heap_freetuple(new_tuple);
536 
537 		/* Make updates visible */
538 		CommandCounterIncrement();
539 
540 		/*
541 		 * Invalidate relcache entry for rel the policy belongs to, to force
542 		 * redoing any dependent plans.  In case of a race condition where the
543 		 * rel was just dropped, we need do nothing.
544 		 */
545 		reltup = SearchSysCache1(RELOID, ObjectIdGetDatum(relid));
546 		if (HeapTupleIsValid(reltup))
547 		{
548 			CacheInvalidateRelcacheByTuple(reltup);
549 			ReleaseSysCache(reltup);
550 		}
551 	}
552 	else
553 	{
554 		/* No roles would remain, so drop the policy instead. */
555 		keep_policy = false;
556 	}
557 
558 	/* Clean up. */
559 	systable_endscan(sscan);
560 
561 	table_close(pg_policy_rel, RowExclusiveLock);
562 
563 	return keep_policy;
564 }
565 
566 /*
567  * CreatePolicy -
568  *	 handles the execution of the CREATE POLICY command.
569  *
570  * stmt - the CreatePolicyStmt that describes the policy to create.
571  */
572 ObjectAddress
CreatePolicy(CreatePolicyStmt * stmt)573 CreatePolicy(CreatePolicyStmt *stmt)
574 {
575 	Relation	pg_policy_rel;
576 	Oid			policy_id;
577 	Relation	target_table;
578 	Oid			table_id;
579 	char		polcmd;
580 	Datum	   *role_oids;
581 	int			nitems = 0;
582 	ArrayType  *role_ids;
583 	ParseState *qual_pstate;
584 	ParseState *with_check_pstate;
585 	ParseNamespaceItem *nsitem;
586 	Node	   *qual;
587 	Node	   *with_check_qual;
588 	ScanKeyData skey[2];
589 	SysScanDesc sscan;
590 	HeapTuple	policy_tuple;
591 	Datum		values[Natts_pg_policy];
592 	bool		isnull[Natts_pg_policy];
593 	ObjectAddress target;
594 	ObjectAddress myself;
595 	int			i;
596 
597 	/* Parse command */
598 	polcmd = parse_policy_command(stmt->cmd_name);
599 
600 	/*
601 	 * If the command is SELECT or DELETE then WITH CHECK should be NULL.
602 	 */
603 	if ((polcmd == ACL_SELECT_CHR || polcmd == ACL_DELETE_CHR)
604 		&& stmt->with_check != NULL)
605 		ereport(ERROR,
606 				(errcode(ERRCODE_SYNTAX_ERROR),
607 				 errmsg("WITH CHECK cannot be applied to SELECT or DELETE")));
608 
609 	/*
610 	 * If the command is INSERT then WITH CHECK should be the only expression
611 	 * provided.
612 	 */
613 	if (polcmd == ACL_INSERT_CHR && stmt->qual != NULL)
614 		ereport(ERROR,
615 				(errcode(ERRCODE_SYNTAX_ERROR),
616 				 errmsg("only WITH CHECK expression allowed for INSERT")));
617 
618 	/* Collect role ids */
619 	role_oids = policy_role_list_to_array(stmt->roles, &nitems);
620 	role_ids = construct_array(role_oids, nitems, OIDOID,
621 							   sizeof(Oid), true, TYPALIGN_INT);
622 
623 	/* Parse the supplied clause */
624 	qual_pstate = make_parsestate(NULL);
625 	with_check_pstate = make_parsestate(NULL);
626 
627 	/* zero-clear */
628 	memset(values, 0, sizeof(values));
629 	memset(isnull, 0, sizeof(isnull));
630 
631 	/* Get id of table.  Also handles permissions checks. */
632 	table_id = RangeVarGetRelidExtended(stmt->table, AccessExclusiveLock,
633 										0,
634 										RangeVarCallbackForPolicy,
635 										(void *) stmt);
636 
637 	/* Open target_table to build quals. No additional lock is necessary. */
638 	target_table = relation_open(table_id, NoLock);
639 
640 	/* Add for the regular security quals */
641 	nsitem = addRangeTableEntryForRelation(qual_pstate, target_table,
642 										   AccessShareLock,
643 										   NULL, false, false);
644 	addNSItemToQuery(qual_pstate, nsitem, false, true, true);
645 
646 	/* Add for the with-check quals */
647 	nsitem = addRangeTableEntryForRelation(with_check_pstate, target_table,
648 										   AccessShareLock,
649 										   NULL, false, false);
650 	addNSItemToQuery(with_check_pstate, nsitem, false, true, true);
651 
652 	qual = transformWhereClause(qual_pstate,
653 								stmt->qual,
654 								EXPR_KIND_POLICY,
655 								"POLICY");
656 
657 	with_check_qual = transformWhereClause(with_check_pstate,
658 										   stmt->with_check,
659 										   EXPR_KIND_POLICY,
660 										   "POLICY");
661 
662 	/* Fix up collation information */
663 	assign_expr_collations(qual_pstate, qual);
664 	assign_expr_collations(with_check_pstate, with_check_qual);
665 
666 	/* Open pg_policy catalog */
667 	pg_policy_rel = table_open(PolicyRelationId, RowExclusiveLock);
668 
669 	/* Set key - policy's relation id. */
670 	ScanKeyInit(&skey[0],
671 				Anum_pg_policy_polrelid,
672 				BTEqualStrategyNumber, F_OIDEQ,
673 				ObjectIdGetDatum(table_id));
674 
675 	/* Set key - policy's name. */
676 	ScanKeyInit(&skey[1],
677 				Anum_pg_policy_polname,
678 				BTEqualStrategyNumber, F_NAMEEQ,
679 				CStringGetDatum(stmt->policy_name));
680 
681 	sscan = systable_beginscan(pg_policy_rel,
682 							   PolicyPolrelidPolnameIndexId, true, NULL, 2,
683 							   skey);
684 
685 	policy_tuple = systable_getnext(sscan);
686 
687 	/* Complain if the policy name already exists for the table */
688 	if (HeapTupleIsValid(policy_tuple))
689 		ereport(ERROR,
690 				(errcode(ERRCODE_DUPLICATE_OBJECT),
691 				 errmsg("policy \"%s\" for table \"%s\" already exists",
692 						stmt->policy_name, RelationGetRelationName(target_table))));
693 
694 	policy_id = GetNewOidWithIndex(pg_policy_rel, PolicyOidIndexId,
695 								   Anum_pg_policy_oid);
696 	values[Anum_pg_policy_oid - 1] = ObjectIdGetDatum(policy_id);
697 	values[Anum_pg_policy_polrelid - 1] = ObjectIdGetDatum(table_id);
698 	values[Anum_pg_policy_polname - 1] = DirectFunctionCall1(namein,
699 															 CStringGetDatum(stmt->policy_name));
700 	values[Anum_pg_policy_polcmd - 1] = CharGetDatum(polcmd);
701 	values[Anum_pg_policy_polpermissive - 1] = BoolGetDatum(stmt->permissive);
702 	values[Anum_pg_policy_polroles - 1] = PointerGetDatum(role_ids);
703 
704 	/* Add qual if present. */
705 	if (qual)
706 		values[Anum_pg_policy_polqual - 1] = CStringGetTextDatum(nodeToString(qual));
707 	else
708 		isnull[Anum_pg_policy_polqual - 1] = true;
709 
710 	/* Add WITH CHECK qual if present */
711 	if (with_check_qual)
712 		values[Anum_pg_policy_polwithcheck - 1] = CStringGetTextDatum(nodeToString(with_check_qual));
713 	else
714 		isnull[Anum_pg_policy_polwithcheck - 1] = true;
715 
716 	policy_tuple = heap_form_tuple(RelationGetDescr(pg_policy_rel), values,
717 								   isnull);
718 
719 	CatalogTupleInsert(pg_policy_rel, policy_tuple);
720 
721 	/* Record Dependencies */
722 	target.classId = RelationRelationId;
723 	target.objectId = table_id;
724 	target.objectSubId = 0;
725 
726 	myself.classId = PolicyRelationId;
727 	myself.objectId = policy_id;
728 	myself.objectSubId = 0;
729 
730 	recordDependencyOn(&myself, &target, DEPENDENCY_AUTO);
731 
732 	recordDependencyOnExpr(&myself, qual, qual_pstate->p_rtable,
733 						   DEPENDENCY_NORMAL);
734 
735 	recordDependencyOnExpr(&myself, with_check_qual,
736 						   with_check_pstate->p_rtable, DEPENDENCY_NORMAL);
737 
738 	/* Register role dependencies */
739 	target.classId = AuthIdRelationId;
740 	target.objectSubId = 0;
741 	for (i = 0; i < nitems; i++)
742 	{
743 		target.objectId = DatumGetObjectId(role_oids[i]);
744 		/* no dependency if public */
745 		if (target.objectId != ACL_ID_PUBLIC)
746 			recordSharedDependencyOn(&myself, &target,
747 									 SHARED_DEPENDENCY_POLICY);
748 	}
749 
750 	InvokeObjectPostCreateHook(PolicyRelationId, policy_id, 0);
751 
752 	/* Invalidate Relation Cache */
753 	CacheInvalidateRelcache(target_table);
754 
755 	/* Clean up. */
756 	heap_freetuple(policy_tuple);
757 	free_parsestate(qual_pstate);
758 	free_parsestate(with_check_pstate);
759 	systable_endscan(sscan);
760 	relation_close(target_table, NoLock);
761 	table_close(pg_policy_rel, RowExclusiveLock);
762 
763 	return myself;
764 }
765 
766 /*
767  * AlterPolicy -
768  *	 handles the execution of the ALTER POLICY command.
769  *
770  * stmt - the AlterPolicyStmt that describes the policy and how to alter it.
771  */
772 ObjectAddress
AlterPolicy(AlterPolicyStmt * stmt)773 AlterPolicy(AlterPolicyStmt *stmt)
774 {
775 	Relation	pg_policy_rel;
776 	Oid			policy_id;
777 	Relation	target_table;
778 	Oid			table_id;
779 	Datum	   *role_oids = NULL;
780 	int			nitems = 0;
781 	ArrayType  *role_ids = NULL;
782 	List	   *qual_parse_rtable = NIL;
783 	List	   *with_check_parse_rtable = NIL;
784 	Node	   *qual = NULL;
785 	Node	   *with_check_qual = NULL;
786 	ScanKeyData skey[2];
787 	SysScanDesc sscan;
788 	HeapTuple	policy_tuple;
789 	HeapTuple	new_tuple;
790 	Datum		values[Natts_pg_policy];
791 	bool		isnull[Natts_pg_policy];
792 	bool		replaces[Natts_pg_policy];
793 	ObjectAddress target;
794 	ObjectAddress myself;
795 	Datum		polcmd_datum;
796 	char		polcmd;
797 	bool		polcmd_isnull;
798 	int			i;
799 
800 	/* Parse role_ids */
801 	if (stmt->roles != NULL)
802 	{
803 		role_oids = policy_role_list_to_array(stmt->roles, &nitems);
804 		role_ids = construct_array(role_oids, nitems, OIDOID,
805 								   sizeof(Oid), true, TYPALIGN_INT);
806 	}
807 
808 	/* Get id of table.  Also handles permissions checks. */
809 	table_id = RangeVarGetRelidExtended(stmt->table, AccessExclusiveLock,
810 										0,
811 										RangeVarCallbackForPolicy,
812 										(void *) stmt);
813 
814 	target_table = relation_open(table_id, NoLock);
815 
816 	/* Parse the using policy clause */
817 	if (stmt->qual)
818 	{
819 		ParseNamespaceItem *nsitem;
820 		ParseState *qual_pstate = make_parsestate(NULL);
821 
822 		nsitem = addRangeTableEntryForRelation(qual_pstate, target_table,
823 											   AccessShareLock,
824 											   NULL, false, false);
825 
826 		addNSItemToQuery(qual_pstate, nsitem, false, true, true);
827 
828 		qual = transformWhereClause(qual_pstate, stmt->qual,
829 									EXPR_KIND_POLICY,
830 									"POLICY");
831 
832 		/* Fix up collation information */
833 		assign_expr_collations(qual_pstate, qual);
834 
835 		qual_parse_rtable = qual_pstate->p_rtable;
836 		free_parsestate(qual_pstate);
837 	}
838 
839 	/* Parse the with-check policy clause */
840 	if (stmt->with_check)
841 	{
842 		ParseNamespaceItem *nsitem;
843 		ParseState *with_check_pstate = make_parsestate(NULL);
844 
845 		nsitem = addRangeTableEntryForRelation(with_check_pstate, target_table,
846 											   AccessShareLock,
847 											   NULL, false, false);
848 
849 		addNSItemToQuery(with_check_pstate, nsitem, false, true, true);
850 
851 		with_check_qual = transformWhereClause(with_check_pstate,
852 											   stmt->with_check,
853 											   EXPR_KIND_POLICY,
854 											   "POLICY");
855 
856 		/* Fix up collation information */
857 		assign_expr_collations(with_check_pstate, with_check_qual);
858 
859 		with_check_parse_rtable = with_check_pstate->p_rtable;
860 		free_parsestate(with_check_pstate);
861 	}
862 
863 	/* zero-clear */
864 	memset(values, 0, sizeof(values));
865 	memset(replaces, 0, sizeof(replaces));
866 	memset(isnull, 0, sizeof(isnull));
867 
868 	/* Find policy to update. */
869 	pg_policy_rel = table_open(PolicyRelationId, RowExclusiveLock);
870 
871 	/* Set key - policy's relation id. */
872 	ScanKeyInit(&skey[0],
873 				Anum_pg_policy_polrelid,
874 				BTEqualStrategyNumber, F_OIDEQ,
875 				ObjectIdGetDatum(table_id));
876 
877 	/* Set key - policy's name. */
878 	ScanKeyInit(&skey[1],
879 				Anum_pg_policy_polname,
880 				BTEqualStrategyNumber, F_NAMEEQ,
881 				CStringGetDatum(stmt->policy_name));
882 
883 	sscan = systable_beginscan(pg_policy_rel,
884 							   PolicyPolrelidPolnameIndexId, true, NULL, 2,
885 							   skey);
886 
887 	policy_tuple = systable_getnext(sscan);
888 
889 	/* Check that the policy is found, raise an error if not. */
890 	if (!HeapTupleIsValid(policy_tuple))
891 		ereport(ERROR,
892 				(errcode(ERRCODE_UNDEFINED_OBJECT),
893 				 errmsg("policy \"%s\" for table \"%s\" does not exist",
894 						stmt->policy_name,
895 						RelationGetRelationName(target_table))));
896 
897 	/* Get policy command */
898 	polcmd_datum = heap_getattr(policy_tuple, Anum_pg_policy_polcmd,
899 								RelationGetDescr(pg_policy_rel),
900 								&polcmd_isnull);
901 	Assert(!polcmd_isnull);
902 	polcmd = DatumGetChar(polcmd_datum);
903 
904 	/*
905 	 * If the command is SELECT or DELETE then WITH CHECK should be NULL.
906 	 */
907 	if ((polcmd == ACL_SELECT_CHR || polcmd == ACL_DELETE_CHR)
908 		&& stmt->with_check != NULL)
909 		ereport(ERROR,
910 				(errcode(ERRCODE_SYNTAX_ERROR),
911 				 errmsg("only USING expression allowed for SELECT, DELETE")));
912 
913 	/*
914 	 * If the command is INSERT then WITH CHECK should be the only expression
915 	 * provided.
916 	 */
917 	if ((polcmd == ACL_INSERT_CHR)
918 		&& stmt->qual != NULL)
919 		ereport(ERROR,
920 				(errcode(ERRCODE_SYNTAX_ERROR),
921 				 errmsg("only WITH CHECK expression allowed for INSERT")));
922 
923 	policy_id = ((Form_pg_policy) GETSTRUCT(policy_tuple))->oid;
924 
925 	if (role_ids != NULL)
926 	{
927 		replaces[Anum_pg_policy_polroles - 1] = true;
928 		values[Anum_pg_policy_polroles - 1] = PointerGetDatum(role_ids);
929 	}
930 	else
931 	{
932 		Oid		   *roles;
933 		Datum		roles_datum;
934 		bool		attr_isnull;
935 		ArrayType  *policy_roles;
936 
937 		/*
938 		 * We need to pull the set of roles this policy applies to from what's
939 		 * in the catalog, so that we can recreate the dependencies correctly
940 		 * for the policy.
941 		 */
942 
943 		roles_datum = heap_getattr(policy_tuple, Anum_pg_policy_polroles,
944 								   RelationGetDescr(pg_policy_rel),
945 								   &attr_isnull);
946 		Assert(!attr_isnull);
947 
948 		policy_roles = DatumGetArrayTypePCopy(roles_datum);
949 
950 		roles = (Oid *) ARR_DATA_PTR(policy_roles);
951 
952 		nitems = ARR_DIMS(policy_roles)[0];
953 
954 		role_oids = (Datum *) palloc(nitems * sizeof(Datum));
955 
956 		for (i = 0; i < nitems; i++)
957 			role_oids[i] = ObjectIdGetDatum(roles[i]);
958 	}
959 
960 	if (qual != NULL)
961 	{
962 		replaces[Anum_pg_policy_polqual - 1] = true;
963 		values[Anum_pg_policy_polqual - 1]
964 			= CStringGetTextDatum(nodeToString(qual));
965 	}
966 	else
967 	{
968 		Datum		value_datum;
969 		bool		attr_isnull;
970 
971 		/*
972 		 * We need to pull the USING expression and build the range table for
973 		 * the policy from what's in the catalog, so that we can recreate the
974 		 * dependencies correctly for the policy.
975 		 */
976 
977 		/* Check if the policy has a USING expr */
978 		value_datum = heap_getattr(policy_tuple, Anum_pg_policy_polqual,
979 								   RelationGetDescr(pg_policy_rel),
980 								   &attr_isnull);
981 		if (!attr_isnull)
982 		{
983 			char	   *qual_value;
984 			ParseState *qual_pstate;
985 
986 			/* parsestate is built just to build the range table */
987 			qual_pstate = make_parsestate(NULL);
988 
989 			qual_value = TextDatumGetCString(value_datum);
990 			qual = stringToNode(qual_value);
991 
992 			/* Add this rel to the parsestate's rangetable, for dependencies */
993 			(void) addRangeTableEntryForRelation(qual_pstate, target_table,
994 												 AccessShareLock,
995 												 NULL, false, false);
996 
997 			qual_parse_rtable = qual_pstate->p_rtable;
998 			free_parsestate(qual_pstate);
999 		}
1000 	}
1001 
1002 	if (with_check_qual != NULL)
1003 	{
1004 		replaces[Anum_pg_policy_polwithcheck - 1] = true;
1005 		values[Anum_pg_policy_polwithcheck - 1]
1006 			= CStringGetTextDatum(nodeToString(with_check_qual));
1007 	}
1008 	else
1009 	{
1010 		Datum		value_datum;
1011 		bool		attr_isnull;
1012 
1013 		/*
1014 		 * We need to pull the WITH CHECK expression and build the range table
1015 		 * for the policy from what's in the catalog, so that we can recreate
1016 		 * the dependencies correctly for the policy.
1017 		 */
1018 
1019 		/* Check if the policy has a WITH CHECK expr */
1020 		value_datum = heap_getattr(policy_tuple, Anum_pg_policy_polwithcheck,
1021 								   RelationGetDescr(pg_policy_rel),
1022 								   &attr_isnull);
1023 		if (!attr_isnull)
1024 		{
1025 			char	   *with_check_value;
1026 			ParseState *with_check_pstate;
1027 
1028 			/* parsestate is built just to build the range table */
1029 			with_check_pstate = make_parsestate(NULL);
1030 
1031 			with_check_value = TextDatumGetCString(value_datum);
1032 			with_check_qual = stringToNode(with_check_value);
1033 
1034 			/* Add this rel to the parsestate's rangetable, for dependencies */
1035 			(void) addRangeTableEntryForRelation(with_check_pstate,
1036 												 target_table,
1037 												 AccessShareLock,
1038 												 NULL, false, false);
1039 
1040 			with_check_parse_rtable = with_check_pstate->p_rtable;
1041 			free_parsestate(with_check_pstate);
1042 		}
1043 	}
1044 
1045 	new_tuple = heap_modify_tuple(policy_tuple,
1046 								  RelationGetDescr(pg_policy_rel),
1047 								  values, isnull, replaces);
1048 	CatalogTupleUpdate(pg_policy_rel, &new_tuple->t_self, new_tuple);
1049 
1050 	/* Update Dependencies. */
1051 	deleteDependencyRecordsFor(PolicyRelationId, policy_id, false);
1052 
1053 	/* Record Dependencies */
1054 	target.classId = RelationRelationId;
1055 	target.objectId = table_id;
1056 	target.objectSubId = 0;
1057 
1058 	myself.classId = PolicyRelationId;
1059 	myself.objectId = policy_id;
1060 	myself.objectSubId = 0;
1061 
1062 	recordDependencyOn(&myself, &target, DEPENDENCY_AUTO);
1063 
1064 	recordDependencyOnExpr(&myself, qual, qual_parse_rtable, DEPENDENCY_NORMAL);
1065 
1066 	recordDependencyOnExpr(&myself, with_check_qual, with_check_parse_rtable,
1067 						   DEPENDENCY_NORMAL);
1068 
1069 	/* Register role dependencies */
1070 	deleteSharedDependencyRecordsFor(PolicyRelationId, policy_id, 0);
1071 	target.classId = AuthIdRelationId;
1072 	target.objectSubId = 0;
1073 	for (i = 0; i < nitems; i++)
1074 	{
1075 		target.objectId = DatumGetObjectId(role_oids[i]);
1076 		/* no dependency if public */
1077 		if (target.objectId != ACL_ID_PUBLIC)
1078 			recordSharedDependencyOn(&myself, &target,
1079 									 SHARED_DEPENDENCY_POLICY);
1080 	}
1081 
1082 	InvokeObjectPostAlterHook(PolicyRelationId, policy_id, 0);
1083 
1084 	heap_freetuple(new_tuple);
1085 
1086 	/* Invalidate Relation Cache */
1087 	CacheInvalidateRelcache(target_table);
1088 
1089 	/* Clean up. */
1090 	systable_endscan(sscan);
1091 	relation_close(target_table, NoLock);
1092 	table_close(pg_policy_rel, RowExclusiveLock);
1093 
1094 	return myself;
1095 }
1096 
1097 /*
1098  * rename_policy -
1099  *	 change the name of a policy on a relation
1100  */
1101 ObjectAddress
rename_policy(RenameStmt * stmt)1102 rename_policy(RenameStmt *stmt)
1103 {
1104 	Relation	pg_policy_rel;
1105 	Relation	target_table;
1106 	Oid			table_id;
1107 	Oid			opoloid;
1108 	ScanKeyData skey[2];
1109 	SysScanDesc sscan;
1110 	HeapTuple	policy_tuple;
1111 	ObjectAddress address;
1112 
1113 	/* Get id of table.  Also handles permissions checks. */
1114 	table_id = RangeVarGetRelidExtended(stmt->relation, AccessExclusiveLock,
1115 										0,
1116 										RangeVarCallbackForPolicy,
1117 										(void *) stmt);
1118 
1119 	target_table = relation_open(table_id, NoLock);
1120 
1121 	pg_policy_rel = table_open(PolicyRelationId, RowExclusiveLock);
1122 
1123 	/* First pass -- check for conflict */
1124 
1125 	/* Add key - policy's relation id. */
1126 	ScanKeyInit(&skey[0],
1127 				Anum_pg_policy_polrelid,
1128 				BTEqualStrategyNumber, F_OIDEQ,
1129 				ObjectIdGetDatum(table_id));
1130 
1131 	/* Add key - policy's name. */
1132 	ScanKeyInit(&skey[1],
1133 				Anum_pg_policy_polname,
1134 				BTEqualStrategyNumber, F_NAMEEQ,
1135 				CStringGetDatum(stmt->newname));
1136 
1137 	sscan = systable_beginscan(pg_policy_rel,
1138 							   PolicyPolrelidPolnameIndexId, true, NULL, 2,
1139 							   skey);
1140 
1141 	if (HeapTupleIsValid(systable_getnext(sscan)))
1142 		ereport(ERROR,
1143 				(errcode(ERRCODE_DUPLICATE_OBJECT),
1144 				 errmsg("policy \"%s\" for table \"%s\" already exists",
1145 						stmt->newname, RelationGetRelationName(target_table))));
1146 
1147 	systable_endscan(sscan);
1148 
1149 	/* Second pass -- find existing policy and update */
1150 	/* Add key - policy's relation id. */
1151 	ScanKeyInit(&skey[0],
1152 				Anum_pg_policy_polrelid,
1153 				BTEqualStrategyNumber, F_OIDEQ,
1154 				ObjectIdGetDatum(table_id));
1155 
1156 	/* Add key - policy's name. */
1157 	ScanKeyInit(&skey[1],
1158 				Anum_pg_policy_polname,
1159 				BTEqualStrategyNumber, F_NAMEEQ,
1160 				CStringGetDatum(stmt->subname));
1161 
1162 	sscan = systable_beginscan(pg_policy_rel,
1163 							   PolicyPolrelidPolnameIndexId, true, NULL, 2,
1164 							   skey);
1165 
1166 	policy_tuple = systable_getnext(sscan);
1167 
1168 	/* Complain if we did not find the policy */
1169 	if (!HeapTupleIsValid(policy_tuple))
1170 		ereport(ERROR,
1171 				(errcode(ERRCODE_UNDEFINED_OBJECT),
1172 				 errmsg("policy \"%s\" for table \"%s\" does not exist",
1173 						stmt->subname, RelationGetRelationName(target_table))));
1174 
1175 	opoloid = ((Form_pg_policy) GETSTRUCT(policy_tuple))->oid;
1176 
1177 	policy_tuple = heap_copytuple(policy_tuple);
1178 
1179 	namestrcpy(&((Form_pg_policy) GETSTRUCT(policy_tuple))->polname,
1180 			   stmt->newname);
1181 
1182 	CatalogTupleUpdate(pg_policy_rel, &policy_tuple->t_self, policy_tuple);
1183 
1184 	InvokeObjectPostAlterHook(PolicyRelationId, opoloid, 0);
1185 
1186 	ObjectAddressSet(address, PolicyRelationId, opoloid);
1187 
1188 	/*
1189 	 * Invalidate relation's relcache entry so that other backends (and this
1190 	 * one too!) are sent SI message to make them rebuild relcache entries.
1191 	 * (Ideally this should happen automatically...)
1192 	 */
1193 	CacheInvalidateRelcache(target_table);
1194 
1195 	/* Clean up. */
1196 	systable_endscan(sscan);
1197 	table_close(pg_policy_rel, RowExclusiveLock);
1198 	relation_close(target_table, NoLock);
1199 
1200 	return address;
1201 }
1202 
1203 /*
1204  * get_relation_policy_oid - Look up a policy by name to find its OID
1205  *
1206  * If missing_ok is false, throw an error if policy not found.  If
1207  * true, just return InvalidOid.
1208  */
1209 Oid
get_relation_policy_oid(Oid relid,const char * policy_name,bool missing_ok)1210 get_relation_policy_oid(Oid relid, const char *policy_name, bool missing_ok)
1211 {
1212 	Relation	pg_policy_rel;
1213 	ScanKeyData skey[2];
1214 	SysScanDesc sscan;
1215 	HeapTuple	policy_tuple;
1216 	Oid			policy_oid;
1217 
1218 	pg_policy_rel = table_open(PolicyRelationId, AccessShareLock);
1219 
1220 	/* Add key - policy's relation id. */
1221 	ScanKeyInit(&skey[0],
1222 				Anum_pg_policy_polrelid,
1223 				BTEqualStrategyNumber, F_OIDEQ,
1224 				ObjectIdGetDatum(relid));
1225 
1226 	/* Add key - policy's name. */
1227 	ScanKeyInit(&skey[1],
1228 				Anum_pg_policy_polname,
1229 				BTEqualStrategyNumber, F_NAMEEQ,
1230 				CStringGetDatum(policy_name));
1231 
1232 	sscan = systable_beginscan(pg_policy_rel,
1233 							   PolicyPolrelidPolnameIndexId, true, NULL, 2,
1234 							   skey);
1235 
1236 	policy_tuple = systable_getnext(sscan);
1237 
1238 	if (!HeapTupleIsValid(policy_tuple))
1239 	{
1240 		if (!missing_ok)
1241 			ereport(ERROR,
1242 					(errcode(ERRCODE_UNDEFINED_OBJECT),
1243 					 errmsg("policy \"%s\" for table \"%s\" does not exist",
1244 							policy_name, get_rel_name(relid))));
1245 
1246 		policy_oid = InvalidOid;
1247 	}
1248 	else
1249 		policy_oid = ((Form_pg_policy) GETSTRUCT(policy_tuple))->oid;
1250 
1251 	/* Clean up. */
1252 	systable_endscan(sscan);
1253 	table_close(pg_policy_rel, AccessShareLock);
1254 
1255 	return policy_oid;
1256 }
1257 
1258 /*
1259  * relation_has_policies - Determine if relation has any policies
1260  */
1261 bool
relation_has_policies(Relation rel)1262 relation_has_policies(Relation rel)
1263 {
1264 	Relation	catalog;
1265 	ScanKeyData skey;
1266 	SysScanDesc sscan;
1267 	HeapTuple	policy_tuple;
1268 	bool		ret = false;
1269 
1270 	catalog = table_open(PolicyRelationId, AccessShareLock);
1271 	ScanKeyInit(&skey,
1272 				Anum_pg_policy_polrelid,
1273 				BTEqualStrategyNumber, F_OIDEQ,
1274 				ObjectIdGetDatum(RelationGetRelid(rel)));
1275 	sscan = systable_beginscan(catalog, PolicyPolrelidPolnameIndexId, true,
1276 							   NULL, 1, &skey);
1277 	policy_tuple = systable_getnext(sscan);
1278 	if (HeapTupleIsValid(policy_tuple))
1279 		ret = true;
1280 
1281 	systable_endscan(sscan);
1282 	table_close(catalog, AccessShareLock);
1283 
1284 	return ret;
1285 }
1286