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