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

Generated by: LCOV version 1.14