LCOV - code coverage report
Current view: top level - src/backend/commands - policy.c (source / functions) Coverage Total Hit
Test: PostgreSQL 20devel Lines: 87.5 % 399 349
Test Date: 2026-07-03 19:57:34 Functions: 90.9 % 11 10
Legend: Lines:     hit not hit
Branches: + taken - not taken # not executed
Branches: 61.1 % 190 116

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

Generated by: LCOV version 2.0-1