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

Generated by: LCOV version 1.13