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

Generated by: LCOV version 1.11