LCOV - differential code coverage report
Current view: top level - src/backend/commands - policy.c (source / functions) Coverage Total Hit LBC UIC UBC GBC GIC GNC CBC EUB ECB DCB
Current: Differential Code Coverage HEAD vs 15 Lines: 87.5 % 401 351 1 26 23 3 143 4 201 24 147 2
Current Date: 2023-04-08 15:15:32 Functions: 90.9 % 11 10 1 4 2 4 1 4
Baseline: 15
Baseline Date: 2023-04-08 15:09:40
Legend: Lines: hit not hit

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

Generated by: LCOV version v1.16-55-g56c0a2a