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