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