1/*-------------------------------------------------------------------------
2 *
3 * policy.c
4 * Commands for manipulating policies.
5 *
6 * Portions Copyright (c) 1996-2019, PostgreSQL Global Development Group
7 * Portions Copyright (c) 1994, Regents of the University of California
8 *
9 * src/backend/commands/policy.c
10 *
11 *-------------------------------------------------------------------------
12 */
13#include "postgres.h"
14
15#include "access/genam.h"
16#include "access/htup.h"
17#include "access/htup_details.h"
18#include "access/relation.h"
19#include "access/table.h"
20#include "access/sysattr.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
50static void RangeVarCallbackForPolicy(const RangeVar *rv,
51 Oid relid, Oid oldrelid, void *arg);
52static char parse_policy_command(const char *cmd_name);
53static 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 */
65static void
66RangeVarCallbackForPolicy(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 */
109static char
110parse_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 */
138static Datum *
139policy_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 */
191void
192RelationBuildRowSecurity(Relation relation)
193{
194 MemoryContext rscxt;
195 MemoryContext oldcxt = CurrentMemoryContext;
196 RowSecurityDesc *volatile rsdesc = NULL;
197
198 /*
199 * Create a memory context to hold everything associated with this
200 * relation's row security policy. This makes it easy to clean up during
201 * a relcache flush.
202 */
203 rscxt = AllocSetContextCreate(CacheMemoryContext,
204 "row security descriptor",
205 ALLOCSET_SMALL_SIZES);
206
207 /*
208 * Since rscxt lives under CacheMemoryContext, it is long-lived. Use a
209 * PG_TRY block to ensure it'll get freed if we fail partway through.
210 */
211 PG_TRY();
212 {
213 Relation catalog;
214 ScanKeyData skey;
215 SysScanDesc sscan;
216 HeapTuple tuple;
217
218 MemoryContextCopyAndSetIdentifier(rscxt,
219 RelationGetRelationName(relation));
220
221 rsdesc = MemoryContextAllocZero(rscxt, sizeof(RowSecurityDesc));
222 rsdesc->rscxt = rscxt;
223
224 catalog = table_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 /*
235 * Loop through the row level security policies for this relation, if
236 * any.
237 */
238 while (HeapTupleIsValid(tuple = systable_getnext(sscan)))
239 {
240 Datum value_datum;
241 char cmd_value;
242 bool permissive_value;
243 Datum roles_datum;
244 char *qual_value;
245 Expr *qual_expr;
246 char *with_check_value;
247 Expr *with_check_qual;
248 char *policy_name_value;
249 bool isnull;
250 RowSecurityPolicy *policy;
251
252 /*
253 * Note: all the pass-by-reference data we collect here is either
254 * still stored in the tuple, or constructed in the caller's
255 * short-lived memory context. We must copy it into rscxt
256 * explicitly below.
257 */
258
259 /* Get policy command */
260 value_datum = heap_getattr(tuple, Anum_pg_policy_polcmd,
261 RelationGetDescr(catalog), &isnull);
262 Assert(!isnull);
263 cmd_value = DatumGetChar(value_datum);
264
265 /* Get policy permissive or restrictive */
266 value_datum = heap_getattr(tuple, Anum_pg_policy_polpermissive,
267 RelationGetDescr(catalog), &isnull);
268 Assert(!isnull);
269 permissive_value = DatumGetBool(value_datum);
270
271 /* Get policy name */
272 value_datum = heap_getattr(tuple, Anum_pg_policy_polname,
273 RelationGetDescr(catalog), &isnull);
274 Assert(!isnull);
275 policy_name_value = NameStr(*(DatumGetName(value_datum)));
276
277 /* Get policy roles */
278 roles_datum = heap_getattr(tuple, Anum_pg_policy_polroles,
279 RelationGetDescr(catalog), &isnull);
280 /* shouldn't be null, but initdb doesn't mark it so, so check */
281 if (isnull)
282 elog(ERROR, "unexpected null value in pg_policy.polroles");
283
284 /* Get policy qual */
285 value_datum = heap_getattr(tuple, Anum_pg_policy_polqual,
286 RelationGetDescr(catalog), &isnull);
287 if (!isnull)
288 {
289 qual_value = TextDatumGetCString(value_datum);
290 qual_expr = (Expr *) stringToNode(qual_value);
291 }
292 else
293 qual_expr = NULL;
294
295 /* Get WITH CHECK qual */
296 value_datum = heap_getattr(tuple, Anum_pg_policy_polwithcheck,
297 RelationGetDescr(catalog), &isnull);
298 if (!isnull)
299 {
300 with_check_value = TextDatumGetCString(value_datum);
301 with_check_qual = (Expr *) stringToNode(with_check_value);
302 }
303 else
304 with_check_qual = NULL;
305
306 /* Now copy everything into the cache context */
307 MemoryContextSwitchTo(rscxt);
308
309 policy = palloc0(sizeof(RowSecurityPolicy));
310 policy->policy_name = pstrdup(policy_name_value);
311 policy->polcmd = cmd_value;
312 policy->permissive = permissive_value;
313 policy->roles = DatumGetArrayTypePCopy(roles_datum);
314 policy->qual = copyObject(qual_expr);
315 policy->with_check_qual = copyObject(with_check_qual);
316 policy->hassublinks = checkExprHasSubLink((Node *) qual_expr) ||
317 checkExprHasSubLink((Node *) with_check_qual);
318
319 rsdesc->policies = lcons(policy, rsdesc->policies);
320
321 MemoryContextSwitchTo(oldcxt);
322
323 /* clean up some (not all) of the junk ... */
324 if (qual_expr != NULL)
325 pfree(qual_expr);
326 if (with_check_qual != NULL)
327 pfree(with_check_qual);
328 }
329
330 systable_endscan(sscan);
331 table_close(catalog, AccessShareLock);
332 }
333 PG_CATCH();
334 {
335 /* Delete rscxt, first making sure it isn't active */
336 MemoryContextSwitchTo(oldcxt);
337 MemoryContextDelete(rscxt);
338 PG_RE_THROW();
339 }
340 PG_END_TRY();
341
342 /* Success --- attach the policy descriptor to the relcache entry */
343 relation->rd_rsdesc = rsdesc;
344}
345
346/*
347 * RemovePolicyById -
348 * remove a policy by its OID. If a policy does not exist with the provided
349 * oid, then an error is raised.
350 *
351 * policy_id - the oid of the policy.
352 */
353void
354RemovePolicyById(Oid policy_id)
355{
356 Relation pg_policy_rel;
357 SysScanDesc sscan;
358 ScanKeyData skey[1];
359 HeapTuple tuple;
360 Oid relid;
361 Relation rel;
362
363 pg_policy_rel = table_open(PolicyRelationId, RowExclusiveLock);
364
365 /*
366 * Find the policy to delete.
367 */
368 ScanKeyInit(&skey[0],
369 Anum_pg_policy_oid,
370 BTEqualStrategyNumber, F_OIDEQ,
371 ObjectIdGetDatum(policy_id));
372
373 sscan = systable_beginscan(pg_policy_rel, PolicyOidIndexId, true,
374 NULL, 1, skey);
375
376 tuple = systable_getnext(sscan);
377
378 /* If the policy exists, then remove it, otherwise raise an error. */
379 if (!HeapTupleIsValid(tuple))
380 elog(ERROR, "could not find tuple for policy %u", policy_id);
381
382 /*
383 * Open and exclusive-lock the relation the policy belongs to. (We need
384 * exclusive lock to lock out queries that might otherwise depend on the
385 * set of policies the rel has; furthermore we've got to hold the lock
386 * till commit.)
387 */
388 relid = ((Form_pg_policy) GETSTRUCT(tuple))->polrelid;
389
390 rel = table_open(relid, AccessExclusiveLock);
391 if (rel->rd_rel->relkind != RELKIND_RELATION &&
392 rel->rd_rel->relkind != RELKIND_PARTITIONED_TABLE)
393 ereport(ERROR,
394 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
395 errmsg("\"%s\" is not a table",
396 RelationGetRelationName(rel))));
397
398 if (!allowSystemTableMods && IsSystemRelation(rel))
399 ereport(ERROR,
400 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
401 errmsg("permission denied: \"%s\" is a system catalog",
402 RelationGetRelationName(rel))));
403
404 CatalogTupleDelete(pg_policy_rel, &tuple->t_self);
405
406 systable_endscan(sscan);
407
408 /*
409 * Note that, unlike some of the other flags in pg_class, relrowsecurity
410 * is not just an indication of if policies exist. When relrowsecurity is
411 * set by a user, then all access to the relation must be through a
412 * policy. If no policy is defined for the relation then a default-deny
413 * policy is created and all records are filtered (except for queries from
414 * the owner).
415 */
416 CacheInvalidateRelcache(rel);
417
418 table_close(rel, NoLock);
419
420 /* Clean up */
421 table_close(pg_policy_rel, RowExclusiveLock);
422}
423
424/*
425 * RemoveRoleFromObjectPolicy -
426 * remove a role from a policy by its OID. If the role is not a member of
427 * the policy then an error is raised. False is returned to indicate that
428 * the role could not be removed due to being the only role on the policy
429 * and therefore the entire policy should be removed.
430 *
431 * Note that a warning will be thrown and true will be returned on a
432 * permission error, as the policy should not be removed in that case.
433 *
434 * roleid - the oid of the role to remove
435 * classid - should always be PolicyRelationId
436 * policy_id - the oid of the policy.
437 */
438bool
439RemoveRoleFromObjectPolicy(Oid roleid, Oid classid, Oid policy_id)
440{
441 Relation pg_policy_rel;
442 SysScanDesc sscan;
443 ScanKeyData skey[1];
444 HeapTuple tuple;
445 Oid relid;
446 Relation rel;
447 ArrayType *policy_roles;
448 int num_roles;
449 Datum roles_datum;
450 bool attr_isnull;
451 bool noperm = true;
452
453 Assert(classid == PolicyRelationId);
454
455 pg_policy_rel = table_open(PolicyRelationId, RowExclusiveLock);
456
457 /*
458 * Find the policy to update.
459 */
460 ScanKeyInit(&skey[0],
461 Anum_pg_policy_oid,
462 BTEqualStrategyNumber, F_OIDEQ,
463 ObjectIdGetDatum(policy_id));
464
465 sscan = systable_beginscan(pg_policy_rel, PolicyOidIndexId, true,
466 NULL, 1, skey);
467
468 tuple = systable_getnext(sscan);
469
470 /* Raise an error if we don't find the policy. */
471 if (!HeapTupleIsValid(tuple))
472 elog(ERROR, "could not find tuple for policy %u", policy_id);
473
474 /*
475 * Open and exclusive-lock the relation the policy belongs to.
476 */
477 relid = ((Form_pg_policy) GETSTRUCT(tuple))->polrelid;
478
479 rel = relation_open(relid, AccessExclusiveLock);
480
481 if (rel->rd_rel->relkind != RELKIND_RELATION &&
482 rel->rd_rel->relkind != RELKIND_PARTITIONED_TABLE)
483 ereport(ERROR,
484 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
485 errmsg("\"%s\" is not a table",
486 RelationGetRelationName(rel))));
487
488 if (!allowSystemTableMods && IsSystemRelation(rel))
489 ereport(ERROR,
490 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
491 errmsg("permission denied: \"%s\" is a system catalog",
492 RelationGetRelationName(rel))));
493
494 /* Get the current set of roles */
495 roles_datum = heap_getattr(tuple,
496 Anum_pg_policy_polroles,
497 RelationGetDescr(pg_policy_rel),
498 &attr_isnull);
499
500 Assert(!attr_isnull);
501
502 policy_roles = DatumGetArrayTypePCopy(roles_datum);
503
504 /* We should be removing exactly one entry from the roles array */
505 num_roles = ARR_DIMS(policy_roles)[0] - 1;
506
507 Assert(num_roles >= 0);
508
509 /* Must own relation. */
510 if (pg_class_ownercheck(relid, GetUserId()))
511 noperm = false; /* user is allowed to modify this policy */
512 else
513 ereport(WARNING,
514 (errcode(ERRCODE_WARNING_PRIVILEGE_NOT_REVOKED),
515 errmsg("role \"%s\" could not be removed from policy \"%s\" on \"%s\"",
516 GetUserNameFromId(roleid, false),
517 NameStr(((Form_pg_policy) GETSTRUCT(tuple))->polname),
518 RelationGetRelationName(rel))));
519
520 /*
521 * If multiple roles exist on this policy, then remove the one we were
522 * asked to and leave the rest.
523 */
524 if (!noperm && num_roles > 0)
525 {
526 int i,
527 j;
528 Oid *roles = (Oid *) ARR_DATA_PTR(policy_roles);
529 Datum *role_oids;
530 char *qual_value;
531 Node *qual_expr;
532 List *qual_parse_rtable = NIL;
533 char *with_check_value;
534 Node *with_check_qual;
535 List *with_check_parse_rtable = NIL;
536 Datum values[Natts_pg_policy];
537 bool isnull[Natts_pg_policy];
538 bool replaces[Natts_pg_policy];
539 Datum value_datum;
540 ArrayType *role_ids;
541 HeapTuple new_tuple;
542 ObjectAddress target;
543 ObjectAddress myself;
544
545 /* zero-clear */
546 memset(values, 0, sizeof(values));
547 memset(replaces, 0, sizeof(replaces));
548 memset(isnull, 0, sizeof(isnull));
549
550 /*
551 * All of the dependencies will be removed from the policy and then
552 * re-added. In order to get them correct, we need to extract out the
553 * expressions in the policy and construct a parsestate just enough to
554 * build the range table(s) to then pass to recordDependencyOnExpr().
555 */
556
557 /* Get policy qual, to update dependencies */
558 value_datum = heap_getattr(tuple, Anum_pg_policy_polqual,
559 RelationGetDescr(pg_policy_rel), &attr_isnull);
560 if (!attr_isnull)
561 {
562 ParseState *qual_pstate;
563
564 /* parsestate is built just to build the range table */
565 qual_pstate = make_parsestate(NULL);
566
567 qual_value = TextDatumGetCString(value_datum);
568 qual_expr = stringToNode(qual_value);
569
570 /* Add this rel to the parsestate's rangetable, for dependencies */
571 addRangeTableEntryForRelation(qual_pstate, rel,
572 AccessShareLock,
573 NULL, false, false);
574
575 qual_parse_rtable = qual_pstate->p_rtable;
576 free_parsestate(qual_pstate);
577 }
578 else
579 qual_expr = NULL;
580
581 /* Get WITH CHECK qual, to update dependencies */
582 value_datum = heap_getattr(tuple, Anum_pg_policy_polwithcheck,
583 RelationGetDescr(pg_policy_rel), &attr_isnull);
584 if (!attr_isnull)
585 {
586 ParseState *with_check_pstate;
587
588 /* parsestate is built just to build the range table */
589 with_check_pstate = make_parsestate(NULL);
590
591 with_check_value = TextDatumGetCString(value_datum);
592 with_check_qual = stringToNode(with_check_value);
593
594 /* Add this rel to the parsestate's rangetable, for dependencies */
595 addRangeTableEntryForRelation(with_check_pstate, rel,
596 AccessShareLock,
597 NULL, false, false);
598
599 with_check_parse_rtable = with_check_pstate->p_rtable;
600 free_parsestate(with_check_pstate);
601 }
602 else
603 with_check_qual = NULL;
604
605 /* Rebuild the roles array to then update the pg_policy tuple with */
606 role_oids = (Datum *) palloc(num_roles * sizeof(Datum));
607 for (i = 0, j = 0; i < ARR_DIMS(policy_roles)[0]; i++)
608 /* Copy over all of the roles which are not the one being removed */
609 if (roles[i] != roleid)
610 role_oids[j++] = ObjectIdGetDatum(roles[i]);
611
612 /* We should have only removed the one role */
613 Assert(j == num_roles);
614
615 /* This is the array for the new tuple */
616 role_ids = construct_array(role_oids, num_roles, OIDOID,
617 sizeof(Oid), true, 'i');
618
619 replaces[Anum_pg_policy_polroles - 1] = true;
620 values[Anum_pg_policy_polroles - 1] = PointerGetDatum(role_ids);
621
622 new_tuple = heap_modify_tuple(tuple,
623 RelationGetDescr(pg_policy_rel),
624 values, isnull, replaces);
625 CatalogTupleUpdate(pg_policy_rel, &new_tuple->t_self, new_tuple);
626
627 /* Remove all old dependencies. */
628 deleteDependencyRecordsFor(PolicyRelationId, policy_id, false);
629
630 /* Record the new set of dependencies */
631 target.classId = RelationRelationId;
632 target.objectId = relid;
633 target.objectSubId = 0;
634
635 myself.classId = PolicyRelationId;
636 myself.objectId = policy_id;
637 myself.objectSubId = 0;
638
639 recordDependencyOn(&myself, &target, DEPENDENCY_AUTO);
640
641 if (qual_expr)
642 recordDependencyOnExpr(&myself, qual_expr, qual_parse_rtable,
643 DEPENDENCY_NORMAL);
644
645 if (with_check_qual)
646 recordDependencyOnExpr(&myself, with_check_qual,
647 with_check_parse_rtable,
648 DEPENDENCY_NORMAL);
649
650 /* Remove all the old shared dependencies (roles) */
651 deleteSharedDependencyRecordsFor(PolicyRelationId, policy_id, 0);
652
653 /* Record the new shared dependencies (roles) */
654 target.classId = AuthIdRelationId;
655 target.objectSubId = 0;
656 for (i = 0; i < num_roles; i++)
657 {
658 target.objectId = DatumGetObjectId(role_oids[i]);
659 /* no need for dependency on the public role */
660 if (target.objectId != ACL_ID_PUBLIC)
661 recordSharedDependencyOn(&myself, &target,
662 SHARED_DEPENDENCY_POLICY);
663 }
664
665 InvokeObjectPostAlterHook(PolicyRelationId, policy_id, 0);
666
667 heap_freetuple(new_tuple);
668
669 /* Invalidate Relation Cache */
670 CacheInvalidateRelcache(rel);
671 }
672
673 /* Clean up. */
674 systable_endscan(sscan);
675
676 relation_close(rel, NoLock);
677
678 table_close(pg_policy_rel, RowExclusiveLock);
679
680 return (noperm || num_roles > 0);
681}
682
683/*
684 * CreatePolicy -
685 * handles the execution of the CREATE POLICY command.
686 *
687 * stmt - the CreatePolicyStmt that describes the policy to create.
688 */
689ObjectAddress
690CreatePolicy(CreatePolicyStmt *stmt)
691{
692 Relation pg_policy_rel;
693 Oid policy_id;
694 Relation target_table;
695 Oid table_id;
696 char polcmd;
697 Datum *role_oids;
698 int nitems = 0;
699 ArrayType *role_ids;
700 ParseState *qual_pstate;
701 ParseState *with_check_pstate;
702 RangeTblEntry *rte;
703 Node *qual;
704 Node *with_check_qual;
705 ScanKeyData skey[2];
706 SysScanDesc sscan;
707 HeapTuple policy_tuple;
708 Datum values[Natts_pg_policy];
709 bool isnull[Natts_pg_policy];
710 ObjectAddress target;
711 ObjectAddress myself;
712 int i;
713
714 /* Parse command */
715 polcmd = parse_policy_command(stmt->cmd_name);
716
717 /*
718 * If the command is SELECT or DELETE then WITH CHECK should be NULL.
719 */
720 if ((polcmd == ACL_SELECT_CHR || polcmd == ACL_DELETE_CHR)
721 && stmt->with_check != NULL)
722 ereport(ERROR,
723 (errcode(ERRCODE_SYNTAX_ERROR),
724 errmsg("WITH CHECK cannot be applied to SELECT or DELETE")));
725
726 /*
727 * If the command is INSERT then WITH CHECK should be the only expression
728 * provided.
729 */
730 if (polcmd == ACL_INSERT_CHR && stmt->qual != NULL)
731 ereport(ERROR,
732 (errcode(ERRCODE_SYNTAX_ERROR),
733 errmsg("only WITH CHECK expression allowed for INSERT")));
734
735 /* Collect role ids */
736 role_oids = policy_role_list_to_array(stmt->roles, &nitems);
737 role_ids = construct_array(role_oids, nitems, OIDOID,
738 sizeof(Oid), true, 'i');
739
740 /* Parse the supplied clause */
741 qual_pstate = make_parsestate(NULL);
742 with_check_pstate = make_parsestate(NULL);
743
744 /* zero-clear */
745 memset(values, 0, sizeof(values));
746 memset(isnull, 0, sizeof(isnull));
747
748 /* Get id of table. Also handles permissions checks. */
749 table_id = RangeVarGetRelidExtended(stmt->table, AccessExclusiveLock,
750 0,
751 RangeVarCallbackForPolicy,
752 (void *) stmt);
753
754 /* Open target_table to build quals. No additional lock is necessary. */
755 target_table = relation_open(table_id, NoLock);
756
757 /* Add for the regular security quals */
758 rte = addRangeTableEntryForRelation(qual_pstate, target_table,
759 AccessShareLock,
760 NULL, false, false);
761 addRTEtoQuery(qual_pstate, rte, false, true, true);
762
763 /* Add for the with-check quals */
764 rte = addRangeTableEntryForRelation(with_check_pstate, target_table,
765 AccessShareLock,
766 NULL, false, false);
767 addRTEtoQuery(with_check_pstate, rte, false, true, true);
768
769 qual = transformWhereClause(qual_pstate,
770 copyObject(stmt->qual),
771 EXPR_KIND_POLICY,
772 "POLICY");
773
774 with_check_qual = transformWhereClause(with_check_pstate,
775 copyObject(stmt->with_check),
776 EXPR_KIND_POLICY,
777 "POLICY");
778
779 /* Fix up collation information */
780 assign_expr_collations(qual_pstate, qual);
781 assign_expr_collations(with_check_pstate, with_check_qual);
782
783 /* Open pg_policy catalog */
784 pg_policy_rel = table_open(PolicyRelationId, RowExclusiveLock);
785
786 /* Set key - policy's relation id. */
787 ScanKeyInit(&skey[0],
788 Anum_pg_policy_polrelid,
789 BTEqualStrategyNumber, F_OIDEQ,
790 ObjectIdGetDatum(table_id));
791
792 /* Set key - policy's name. */
793 ScanKeyInit(&skey[1],
794 Anum_pg_policy_polname,
795 BTEqualStrategyNumber, F_NAMEEQ,
796 CStringGetDatum(stmt->policy_name));
797
798 sscan = systable_beginscan(pg_policy_rel,
799 PolicyPolrelidPolnameIndexId, true, NULL, 2,
800 skey);
801
802 policy_tuple = systable_getnext(sscan);
803
804 /* Complain if the policy name already exists for the table */
805 if (HeapTupleIsValid(policy_tuple))
806 ereport(ERROR,
807 (errcode(ERRCODE_DUPLICATE_OBJECT),
808 errmsg("policy \"%s\" for table \"%s\" already exists",
809 stmt->policy_name, RelationGetRelationName(target_table))));
810
811 policy_id = GetNewOidWithIndex(pg_policy_rel, PolicyOidIndexId,
812 Anum_pg_policy_oid);
813 values[Anum_pg_policy_oid - 1] = ObjectIdGetDatum(policy_id);
814 values[Anum_pg_policy_polrelid - 1] = ObjectIdGetDatum(table_id);
815 values[Anum_pg_policy_polname - 1] = DirectFunctionCall1(namein,
816 CStringGetDatum(stmt->policy_name));
817 values[Anum_pg_policy_polcmd - 1] = CharGetDatum(polcmd);
818 values[Anum_pg_policy_polpermissive - 1] = BoolGetDatum(stmt->permissive);
819 values[Anum_pg_policy_polroles - 1] = PointerGetDatum(role_ids);
820
821 /* Add qual if present. */
822 if (qual)
823 values[Anum_pg_policy_polqual - 1] = CStringGetTextDatum(nodeToString(qual));
824 else
825 isnull[Anum_pg_policy_polqual - 1] = true;
826
827 /* Add WITH CHECK qual if present */
828 if (with_check_qual)
829 values[Anum_pg_policy_polwithcheck - 1] = CStringGetTextDatum(nodeToString(with_check_qual));
830 else
831 isnull[Anum_pg_policy_polwithcheck - 1] = true;
832
833 policy_tuple = heap_form_tuple(RelationGetDescr(pg_policy_rel), values,
834 isnull);
835
836 CatalogTupleInsert(pg_policy_rel, policy_tuple);
837
838 /* Record Dependencies */
839 target.classId = RelationRelationId;
840 target.objectId = table_id;
841 target.objectSubId = 0;
842
843 myself.classId = PolicyRelationId;
844 myself.objectId = policy_id;
845 myself.objectSubId = 0;
846
847 recordDependencyOn(&myself, &target, DEPENDENCY_AUTO);
848
849 recordDependencyOnExpr(&myself, qual, qual_pstate->p_rtable,
850 DEPENDENCY_NORMAL);
851
852 recordDependencyOnExpr(&myself, with_check_qual,
853 with_check_pstate->p_rtable, DEPENDENCY_NORMAL);
854
855 /* Register role dependencies */
856 target.classId = AuthIdRelationId;
857 target.objectSubId = 0;
858 for (i = 0; i < nitems; i++)
859 {
860 target.objectId = DatumGetObjectId(role_oids[i]);
861 /* no dependency if public */
862 if (target.objectId != ACL_ID_PUBLIC)
863 recordSharedDependencyOn(&myself, &target,
864 SHARED_DEPENDENCY_POLICY);
865 }
866
867 InvokeObjectPostCreateHook(PolicyRelationId, policy_id, 0);
868
869 /* Invalidate Relation Cache */
870 CacheInvalidateRelcache(target_table);
871
872 /* Clean up. */
873 heap_freetuple(policy_tuple);
874 free_parsestate(qual_pstate);
875 free_parsestate(with_check_pstate);
876 systable_endscan(sscan);
877 relation_close(target_table, NoLock);
878 table_close(pg_policy_rel, RowExclusiveLock);
879
880 return myself;
881}
882
883/*
884 * AlterPolicy -
885 * handles the execution of the ALTER POLICY command.
886 *
887 * stmt - the AlterPolicyStmt that describes the policy and how to alter it.
888 */
889ObjectAddress
890AlterPolicy(AlterPolicyStmt *stmt)
891{
892 Relation pg_policy_rel;
893 Oid policy_id;
894 Relation target_table;
895 Oid table_id;
896 Datum *role_oids = NULL;
897 int nitems = 0;
898 ArrayType *role_ids = NULL;
899 List *qual_parse_rtable = NIL;
900 List *with_check_parse_rtable = NIL;
901 Node *qual = NULL;
902 Node *with_check_qual = NULL;
903 ScanKeyData skey[2];
904 SysScanDesc sscan;
905 HeapTuple policy_tuple;
906 HeapTuple new_tuple;
907 Datum values[Natts_pg_policy];
908 bool isnull[Natts_pg_policy];
909 bool replaces[Natts_pg_policy];
910 ObjectAddress target;
911 ObjectAddress myself;
912 Datum polcmd_datum;
913 char polcmd;
914 bool polcmd_isnull;
915 int i;
916
917 /* Parse role_ids */
918 if (stmt->roles != NULL)
919 {
920 role_oids = policy_role_list_to_array(stmt->roles, &nitems);
921 role_ids = construct_array(role_oids, nitems, OIDOID,
922 sizeof(Oid), true, 'i');
923 }
924
925 /* Get id of table. Also handles permissions checks. */
926 table_id = RangeVarGetRelidExtended(stmt->table, AccessExclusiveLock,
927 0,
928 RangeVarCallbackForPolicy,
929 (void *) stmt);
930
931 target_table = relation_open(table_id, NoLock);
932
933 /* Parse the using policy clause */
934 if (stmt->qual)
935 {
936 RangeTblEntry *rte;
937 ParseState *qual_pstate = make_parsestate(NULL);
938
939 rte = addRangeTableEntryForRelation(qual_pstate, target_table,
940 AccessShareLock,
941 NULL, false, false);
942
943 addRTEtoQuery(qual_pstate, rte, false, true, true);
944
945 qual = transformWhereClause(qual_pstate, copyObject(stmt->qual),
946 EXPR_KIND_POLICY,
947 "POLICY");
948
949 /* Fix up collation information */
950 assign_expr_collations(qual_pstate, qual);
951
952 qual_parse_rtable = qual_pstate->p_rtable;
953 free_parsestate(qual_pstate);
954 }
955
956 /* Parse the with-check policy clause */
957 if (stmt->with_check)
958 {
959 RangeTblEntry *rte;
960 ParseState *with_check_pstate = make_parsestate(NULL);
961
962 rte = addRangeTableEntryForRelation(with_check_pstate, target_table,
963 AccessShareLock,
964 NULL, false, false);
965
966 addRTEtoQuery(with_check_pstate, rte, false, true, true);
967
968 with_check_qual = transformWhereClause(with_check_pstate,
969 copyObject(stmt->with_check),
970 EXPR_KIND_POLICY,
971 "POLICY");
972
973 /* Fix up collation information */
974 assign_expr_collations(with_check_pstate, with_check_qual);
975
976 with_check_parse_rtable = with_check_pstate->p_rtable;
977 free_parsestate(with_check_pstate);
978 }
979
980 /* zero-clear */
981 memset(values, 0, sizeof(values));
982 memset(replaces, 0, sizeof(replaces));
983 memset(isnull, 0, sizeof(isnull));
984
985 /* Find policy to update. */
986 pg_policy_rel = table_open(PolicyRelationId, RowExclusiveLock);
987
988 /* Set key - policy's relation id. */
989 ScanKeyInit(&skey[0],
990 Anum_pg_policy_polrelid,
991 BTEqualStrategyNumber, F_OIDEQ,
992 ObjectIdGetDatum(table_id));
993
994 /* Set key - policy's name. */
995 ScanKeyInit(&skey[1],
996 Anum_pg_policy_polname,
997 BTEqualStrategyNumber, F_NAMEEQ,
998 CStringGetDatum(stmt->policy_name));
999
1000 sscan = systable_beginscan(pg_policy_rel,
1001 PolicyPolrelidPolnameIndexId, true, NULL, 2,
1002 skey);
1003
1004 policy_tuple = systable_getnext(sscan);
1005
1006 /* Check that the policy is found, raise an error if not. */
1007 if (!HeapTupleIsValid(policy_tuple))
1008 ereport(ERROR,
1009 (errcode(ERRCODE_UNDEFINED_OBJECT),
1010 errmsg("policy \"%s\" for table \"%s\" does not exist",
1011 stmt->policy_name,
1012 RelationGetRelationName(target_table))));
1013
1014 /* Get policy command */
1015 polcmd_datum = heap_getattr(policy_tuple, Anum_pg_policy_polcmd,
1016 RelationGetDescr(pg_policy_rel),
1017 &polcmd_isnull);
1018 Assert(!polcmd_isnull);
1019 polcmd = DatumGetChar(polcmd_datum);
1020
1021 /*
1022 * If the command is SELECT or DELETE then WITH CHECK should be NULL.
1023 */
1024 if ((polcmd == ACL_SELECT_CHR || polcmd == ACL_DELETE_CHR)
1025 && stmt->with_check != NULL)
1026 ereport(ERROR,
1027 (errcode(ERRCODE_SYNTAX_ERROR),
1028 errmsg("only USING expression allowed for SELECT, DELETE")));
1029
1030 /*
1031 * If the command is INSERT then WITH CHECK should be the only expression
1032 * provided.
1033 */
1034 if ((polcmd == ACL_INSERT_CHR)
1035 && stmt->qual != NULL)
1036 ereport(ERROR,
1037 (errcode(ERRCODE_SYNTAX_ERROR),
1038 errmsg("only WITH CHECK expression allowed for INSERT")));
1039
1040 policy_id = ((Form_pg_policy) GETSTRUCT(policy_tuple))->oid;
1041
1042 if (role_ids != NULL)
1043 {
1044 replaces[Anum_pg_policy_polroles - 1] = true;
1045 values[Anum_pg_policy_polroles - 1] = PointerGetDatum(role_ids);
1046 }
1047 else
1048 {
1049 Oid *roles;
1050 Datum roles_datum;
1051 bool attr_isnull;
1052 ArrayType *policy_roles;
1053
1054 /*
1055 * We need to pull the set of roles this policy applies to from what's
1056 * in the catalog, so that we can recreate the dependencies correctly
1057 * for the policy.
1058 */
1059
1060 roles_datum = heap_getattr(policy_tuple, Anum_pg_policy_polroles,
1061 RelationGetDescr(pg_policy_rel),
1062 &attr_isnull);
1063 Assert(!attr_isnull);
1064
1065 policy_roles = DatumGetArrayTypePCopy(roles_datum);
1066
1067 roles = (Oid *) ARR_DATA_PTR(policy_roles);
1068
1069 nitems = ARR_DIMS(policy_roles)[0];
1070
1071 role_oids = (Datum *) palloc(nitems * sizeof(Datum));
1072
1073 for (i = 0; i < nitems; i++)
1074 role_oids[i] = ObjectIdGetDatum(roles[i]);
1075 }
1076
1077 if (qual != NULL)
1078 {
1079 replaces[Anum_pg_policy_polqual - 1] = true;
1080 values[Anum_pg_policy_polqual - 1]
1081 = CStringGetTextDatum(nodeToString(qual));
1082 }
1083 else
1084 {
1085 Datum value_datum;
1086 bool attr_isnull;
1087
1088 /*
1089 * We need to pull the USING expression and build the range table for
1090 * the policy from what's in the catalog, so that we can recreate the
1091 * dependencies correctly for the policy.
1092 */
1093
1094 /* Check if the policy has a USING expr */
1095 value_datum = heap_getattr(policy_tuple, Anum_pg_policy_polqual,
1096 RelationGetDescr(pg_policy_rel),
1097 &attr_isnull);
1098 if (!attr_isnull)
1099 {
1100 char *qual_value;
1101 ParseState *qual_pstate;
1102
1103 /* parsestate is built just to build the range table */
1104 qual_pstate = make_parsestate(NULL);
1105
1106 qual_value = TextDatumGetCString(value_datum);
1107 qual = stringToNode(qual_value);
1108
1109 /* Add this rel to the parsestate's rangetable, for dependencies */
1110 addRangeTableEntryForRelation(qual_pstate, target_table,
1111 AccessShareLock,
1112 NULL, false, false);
1113
1114 qual_parse_rtable = qual_pstate->p_rtable;
1115 free_parsestate(qual_pstate);
1116 }
1117 }
1118
1119 if (with_check_qual != NULL)
1120 {
1121 replaces[Anum_pg_policy_polwithcheck - 1] = true;
1122 values[Anum_pg_policy_polwithcheck - 1]
1123 = CStringGetTextDatum(nodeToString(with_check_qual));
1124 }
1125 else
1126 {
1127 Datum value_datum;
1128 bool attr_isnull;
1129
1130 /*
1131 * We need to pull the WITH CHECK expression and build the range table
1132 * for the policy from what's in the catalog, so that we can recreate
1133 * the dependencies correctly for the policy.
1134 */
1135
1136 /* Check if the policy has a WITH CHECK expr */
1137 value_datum = heap_getattr(policy_tuple, Anum_pg_policy_polwithcheck,
1138 RelationGetDescr(pg_policy_rel),
1139 &attr_isnull);
1140 if (!attr_isnull)
1141 {
1142 char *with_check_value;
1143 ParseState *with_check_pstate;
1144
1145 /* parsestate is built just to build the range table */
1146 with_check_pstate = make_parsestate(NULL);
1147
1148 with_check_value = TextDatumGetCString(value_datum);
1149 with_check_qual = stringToNode(with_check_value);
1150
1151 /* Add this rel to the parsestate's rangetable, for dependencies */
1152 addRangeTableEntryForRelation(with_check_pstate, target_table,
1153 AccessShareLock,
1154 NULL, false, false);
1155
1156 with_check_parse_rtable = with_check_pstate->p_rtable;
1157 free_parsestate(with_check_pstate);
1158 }
1159 }
1160
1161 new_tuple = heap_modify_tuple(policy_tuple,
1162 RelationGetDescr(pg_policy_rel),
1163 values, isnull, replaces);
1164 CatalogTupleUpdate(pg_policy_rel, &new_tuple->t_self, new_tuple);
1165
1166 /* Update Dependencies. */
1167 deleteDependencyRecordsFor(PolicyRelationId, policy_id, false);
1168
1169 /* Record Dependencies */
1170 target.classId = RelationRelationId;
1171 target.objectId = table_id;
1172 target.objectSubId = 0;
1173
1174 myself.classId = PolicyRelationId;
1175 myself.objectId = policy_id;
1176 myself.objectSubId = 0;
1177
1178 recordDependencyOn(&myself, &target, DEPENDENCY_AUTO);
1179
1180 recordDependencyOnExpr(&myself, qual, qual_parse_rtable, DEPENDENCY_NORMAL);
1181
1182 recordDependencyOnExpr(&myself, with_check_qual, with_check_parse_rtable,
1183 DEPENDENCY_NORMAL);
1184
1185 /* Register role dependencies */
1186 deleteSharedDependencyRecordsFor(PolicyRelationId, policy_id, 0);
1187 target.classId = AuthIdRelationId;
1188 target.objectSubId = 0;
1189 for (i = 0; i < nitems; i++)
1190 {
1191 target.objectId = DatumGetObjectId(role_oids[i]);
1192 /* no dependency if public */
1193 if (target.objectId != ACL_ID_PUBLIC)
1194 recordSharedDependencyOn(&myself, &target,
1195 SHARED_DEPENDENCY_POLICY);
1196 }
1197
1198 InvokeObjectPostAlterHook(PolicyRelationId, policy_id, 0);
1199
1200 heap_freetuple(new_tuple);
1201
1202 /* Invalidate Relation Cache */
1203 CacheInvalidateRelcache(target_table);
1204
1205 /* Clean up. */
1206 systable_endscan(sscan);
1207 relation_close(target_table, NoLock);
1208 table_close(pg_policy_rel, RowExclusiveLock);
1209
1210 return myself;
1211}
1212
1213/*
1214 * rename_policy -
1215 * change the name of a policy on a relation
1216 */
1217ObjectAddress
1218rename_policy(RenameStmt *stmt)
1219{
1220 Relation pg_policy_rel;
1221 Relation target_table;
1222 Oid table_id;
1223 Oid opoloid;
1224 ScanKeyData skey[2];
1225 SysScanDesc sscan;
1226 HeapTuple policy_tuple;
1227 ObjectAddress address;
1228
1229 /* Get id of table. Also handles permissions checks. */
1230 table_id = RangeVarGetRelidExtended(stmt->relation, AccessExclusiveLock,
1231 0,
1232 RangeVarCallbackForPolicy,
1233 (void *) stmt);
1234
1235 target_table = relation_open(table_id, NoLock);
1236
1237 pg_policy_rel = table_open(PolicyRelationId, RowExclusiveLock);
1238
1239 /* First pass -- check for conflict */
1240
1241 /* Add key - policy's relation id. */
1242 ScanKeyInit(&skey[0],
1243 Anum_pg_policy_polrelid,
1244 BTEqualStrategyNumber, F_OIDEQ,
1245 ObjectIdGetDatum(table_id));
1246
1247 /* Add key - policy's name. */
1248 ScanKeyInit(&skey[1],
1249 Anum_pg_policy_polname,
1250 BTEqualStrategyNumber, F_NAMEEQ,
1251 CStringGetDatum(stmt->newname));
1252
1253 sscan = systable_beginscan(pg_policy_rel,
1254 PolicyPolrelidPolnameIndexId, true, NULL, 2,
1255 skey);
1256
1257 if (HeapTupleIsValid(systable_getnext(sscan)))
1258 ereport(ERROR,
1259 (errcode(ERRCODE_DUPLICATE_OBJECT),
1260 errmsg("policy \"%s\" for table \"%s\" already exists",
1261 stmt->newname, RelationGetRelationName(target_table))));
1262
1263 systable_endscan(sscan);
1264
1265 /* Second pass -- find existing policy and update */
1266 /* Add key - policy's relation id. */
1267 ScanKeyInit(&skey[0],
1268 Anum_pg_policy_polrelid,
1269 BTEqualStrategyNumber, F_OIDEQ,
1270 ObjectIdGetDatum(table_id));
1271
1272 /* Add key - policy's name. */
1273 ScanKeyInit(&skey[1],
1274 Anum_pg_policy_polname,
1275 BTEqualStrategyNumber, F_NAMEEQ,
1276 CStringGetDatum(stmt->subname));
1277
1278 sscan = systable_beginscan(pg_policy_rel,
1279 PolicyPolrelidPolnameIndexId, true, NULL, 2,
1280 skey);
1281
1282 policy_tuple = systable_getnext(sscan);
1283
1284 /* Complain if we did not find the policy */
1285 if (!HeapTupleIsValid(policy_tuple))
1286 ereport(ERROR,
1287 (errcode(ERRCODE_UNDEFINED_OBJECT),
1288 errmsg("policy \"%s\" for table \"%s\" does not exist",
1289 stmt->subname, RelationGetRelationName(target_table))));
1290
1291 opoloid = ((Form_pg_policy) GETSTRUCT(policy_tuple))->oid;
1292
1293 policy_tuple = heap_copytuple(policy_tuple);
1294
1295 namestrcpy(&((Form_pg_policy) GETSTRUCT(policy_tuple))->polname,
1296 stmt->newname);
1297
1298 CatalogTupleUpdate(pg_policy_rel, &policy_tuple->t_self, policy_tuple);
1299
1300 InvokeObjectPostAlterHook(PolicyRelationId, opoloid, 0);
1301
1302 ObjectAddressSet(address, PolicyRelationId, opoloid);
1303
1304 /*
1305 * Invalidate relation's relcache entry so that other backends (and this
1306 * one too!) are sent SI message to make them rebuild relcache entries.
1307 * (Ideally this should happen automatically...)
1308 */
1309 CacheInvalidateRelcache(target_table);
1310
1311 /* Clean up. */
1312 systable_endscan(sscan);
1313 table_close(pg_policy_rel, RowExclusiveLock);
1314 relation_close(target_table, NoLock);
1315
1316 return address;
1317}
1318
1319/*
1320 * get_relation_policy_oid - Look up a policy by name to find its OID
1321 *
1322 * If missing_ok is false, throw an error if policy not found. If
1323 * true, just return InvalidOid.
1324 */
1325Oid
1326get_relation_policy_oid(Oid relid, const char *policy_name, bool missing_ok)
1327{
1328 Relation pg_policy_rel;
1329 ScanKeyData skey[2];
1330 SysScanDesc sscan;
1331 HeapTuple policy_tuple;
1332 Oid policy_oid;
1333
1334 pg_policy_rel = table_open(PolicyRelationId, AccessShareLock);
1335
1336 /* Add key - policy's relation id. */
1337 ScanKeyInit(&skey[0],
1338 Anum_pg_policy_polrelid,
1339 BTEqualStrategyNumber, F_OIDEQ,
1340 ObjectIdGetDatum(relid));
1341
1342 /* Add key - policy's name. */
1343 ScanKeyInit(&skey[1],
1344 Anum_pg_policy_polname,
1345 BTEqualStrategyNumber, F_NAMEEQ,
1346 CStringGetDatum(policy_name));
1347
1348 sscan = systable_beginscan(pg_policy_rel,
1349 PolicyPolrelidPolnameIndexId, true, NULL, 2,
1350 skey);
1351
1352 policy_tuple = systable_getnext(sscan);
1353
1354 if (!HeapTupleIsValid(policy_tuple))
1355 {
1356 if (!missing_ok)
1357 ereport(ERROR,
1358 (errcode(ERRCODE_UNDEFINED_OBJECT),
1359 errmsg("policy \"%s\" for table \"%s\" does not exist",
1360 policy_name, get_rel_name(relid))));
1361
1362 policy_oid = InvalidOid;
1363 }
1364 else
1365 policy_oid = ((Form_pg_policy) GETSTRUCT(policy_tuple))->oid;
1366
1367 /* Clean up. */
1368 systable_endscan(sscan);
1369 table_close(pg_policy_rel, AccessShareLock);
1370
1371 return policy_oid;
1372}
1373
1374/*
1375 * relation_has_policies - Determine if relation has any policies
1376 */
1377bool
1378relation_has_policies(Relation rel)
1379{
1380 Relation catalog;
1381 ScanKeyData skey;
1382 SysScanDesc sscan;
1383 HeapTuple policy_tuple;
1384 bool ret = false;
1385
1386 catalog = table_open(PolicyRelationId, AccessShareLock);
1387 ScanKeyInit(&skey,
1388 Anum_pg_policy_polrelid,
1389 BTEqualStrategyNumber, F_OIDEQ,
1390 ObjectIdGetDatum(RelationGetRelid(rel)));
1391 sscan = systable_beginscan(catalog, PolicyPolrelidPolnameIndexId, true,
1392 NULL, 1, &skey);
1393 policy_tuple = systable_getnext(sscan);
1394 if (HeapTupleIsValid(policy_tuple))
1395 ret = true;
1396
1397 systable_endscan(sscan);
1398 table_close(catalog, AccessShareLock);
1399
1400 return ret;
1401}
1402