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