LCOV - code coverage report
Current view: top level - src/backend/commands - policy.c (source / functions) Coverage Total Hit
Test: Code coverage Lines: 88.6 % 577 511
Test Date: 2026-01-26 10:56:24 Functions: 90.9 % 11 10
Legend: Lines:     hit not hit
Branches: + taken - not taken # not executed
Branches: 50.9 % 232 118

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

Generated by: LCOV version 2.3.2-1