LCOV - code coverage report
Current view: top level - src/backend/commands - policy.c (source / functions) Hit Total Coverage
Test: PostgreSQL 17devel Lines: 347 397 87.4 %
Date: 2024-04-13 09:11:47 Functions: 10 11 90.9 %
Legend: Lines: hit not hit

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

Generated by: LCOV version 1.14