LCOV - code coverage report
Current view: top level - src/backend/commands - policy.c (source / functions) Coverage Total Hit
Test: PostgreSQL 19devel Lines: 87.4 % 397 347
Test Date: 2026-02-17 17:20:33 Functions: 90.9 % 11 10
Legend: Lines:     hit not hit

            Line data    Source code
       1              : /*-------------------------------------------------------------------------
       2              :  *
       3              :  * policy.c
       4              :  *    Commands for manipulating policies.
       5              :  *
       6              :  * Portions Copyright (c) 1996-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          464 : RangeVarCallbackForPolicy(const RangeVar *rv, Oid relid, Oid oldrelid,
      65              :                           void *arg)
      66              : {
      67              :     HeapTuple   tuple;
      68              :     Form_pg_class classform;
      69              :     char        relkind;
      70              : 
      71          464 :     tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relid));
      72          464 :     if (!HeapTupleIsValid(tuple))
      73            0 :         return;
      74              : 
      75          464 :     classform = (Form_pg_class) GETSTRUCT(tuple);
      76          464 :     relkind = classform->relkind;
      77              : 
      78              :     /* Must own relation. */
      79          464 :     if (!object_ownercheck(RelationRelationId, relid, GetUserId()))
      80            6 :         aclcheck_error(ACLCHECK_NOT_OWNER, get_relkind_objtype(get_rel_relkind(relid)), rv->relname);
      81              : 
      82              :     /* No system table modifications unless explicitly allowed. */
      83          458 :     if (!allowSystemTableMods && IsSystemClass(relid, classform))
      84            1 :         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          457 :     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          457 :     ReleaseSysCache(tuple);
      96              : }
      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          395 : parse_policy_command(const char *cmd_name)
     109              : {
     110              :     char        polcmd;
     111              : 
     112          395 :     if (!cmd_name)
     113            0 :         elog(ERROR, "unrecognized policy command");
     114              : 
     115          395 :     if (strcmp(cmd_name, "all") == 0)
     116          230 :         polcmd = '*';
     117          165 :     else if (strcmp(cmd_name, "select") == 0)
     118           65 :         polcmd = ACL_SELECT_CHR;
     119          100 :     else if (strcmp(cmd_name, "insert") == 0)
     120           28 :         polcmd = ACL_INSERT_CHR;
     121           72 :     else if (strcmp(cmd_name, "update") == 0)
     122           48 :         polcmd = ACL_UPDATE_CHR;
     123           24 :     else if (strcmp(cmd_name, "delete") == 0)
     124           24 :         polcmd = ACL_DELETE_CHR;
     125              :     else
     126            0 :         elog(ERROR, "unrecognized policy command");
     127              : 
     128          395 :     return polcmd;
     129              : }
     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          401 : policy_role_list_to_array(List *roles, int *num_roles)
     138              : {
     139              :     Datum      *role_oids;
     140              :     ListCell   *cell;
     141          401 :     int         i = 0;
     142              : 
     143              :     /* Handle no roles being passed in as being for public */
     144          401 :     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          401 :     *num_roles = list_length(roles);
     154          401 :     role_oids = (Datum *) palloc(*num_roles * sizeof(Datum));
     155              : 
     156          484 :     foreach(cell, roles)
     157              :     {
     158          422 :         RoleSpec   *spec = lfirst(cell);
     159              : 
     160              :         /*
     161              :          * PUBLIC covers all roles, so it only makes sense alone.
     162              :          */
     163          422 :         if (spec->roletype == ROLESPEC_PUBLIC)
     164              :         {
     165          339 :             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              :             }
     173          339 :             role_oids[0] = ObjectIdGetDatum(ACL_ID_PUBLIC);
     174              : 
     175          339 :             return role_oids;
     176              :         }
     177              :         else
     178           83 :             role_oids[i++] =
     179           83 :                 ObjectIdGetDatum(get_rolespec_oid(spec, false));
     180              :     }
     181              : 
     182           62 :     return role_oids;
     183              : }
     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         1220 : RelationBuildRowSecurity(Relation relation)
     194              : {
     195              :     MemoryContext rscxt;
     196         1220 :     MemoryContext oldcxt = CurrentMemoryContext;
     197              :     RowSecurityDesc *rsdesc;
     198              :     Relation    catalog;
     199              :     ScanKeyData skey;
     200              :     SysScanDesc sscan;
     201              :     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         1220 :     rscxt = AllocSetContextCreate(CurrentMemoryContext,
     210              :                                   "row security descriptor",
     211              :                                   ALLOCSET_SMALL_SIZES);
     212         1220 :     MemoryContextCopyAndSetIdentifier(rscxt,
     213              :                                       RelationGetRelationName(relation));
     214              : 
     215         1220 :     rsdesc = MemoryContextAllocZero(rscxt, sizeof(RowSecurityDesc));
     216         1220 :     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         1220 :     catalog = table_open(PolicyRelationId, AccessShareLock);
     225              : 
     226         1220 :     ScanKeyInit(&skey,
     227              :                 Anum_pg_policy_polrelid,
     228              :                 BTEqualStrategyNumber, F_OIDEQ,
     229              :                 ObjectIdGetDatum(RelationGetRelid(relation)));
     230              : 
     231         1220 :     sscan = systable_beginscan(catalog, PolicyPolrelidPolnameIndexId, true,
     232              :                                NULL, 1, &skey);
     233              : 
     234         2938 :     while (HeapTupleIsValid(tuple = systable_getnext(sscan)))
     235              :     {
     236         1718 :         Form_pg_policy policy_form = (Form_pg_policy) GETSTRUCT(tuple);
     237              :         RowSecurityPolicy *policy;
     238              :         Datum       datum;
     239              :         bool        isnull;
     240              :         char       *str_value;
     241              : 
     242         1718 :         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         1718 :         policy->polcmd = policy_form->polcmd;
     252              : 
     253              :         /* Get policy, permissive or restrictive */
     254         1718 :         policy->permissive = policy_form->polpermissive;
     255              : 
     256              :         /* Get policy name */
     257         1718 :         policy->policy_name =
     258         1718 :             MemoryContextStrdup(rscxt, NameStr(policy_form->polname));
     259              : 
     260              :         /* Get policy roles */
     261         1718 :         datum = heap_getattr(tuple, Anum_pg_policy_polroles,
     262              :                              RelationGetDescr(catalog), &isnull);
     263              :         /* shouldn't be null, but let's check for luck */
     264         1718 :         if (isnull)
     265            0 :             elog(ERROR, "unexpected null value in pg_policy.polroles");
     266         1718 :         MemoryContextSwitchTo(rscxt);
     267         1718 :         policy->roles = DatumGetArrayTypePCopy(datum);
     268         1718 :         MemoryContextSwitchTo(oldcxt);
     269              : 
     270              :         /* Get policy qual */
     271         1718 :         datum = heap_getattr(tuple, Anum_pg_policy_polqual,
     272              :                              RelationGetDescr(catalog), &isnull);
     273         1718 :         if (!isnull)
     274              :         {
     275         1505 :             str_value = TextDatumGetCString(datum);
     276         1505 :             MemoryContextSwitchTo(rscxt);
     277         1505 :             policy->qual = (Expr *) stringToNode(str_value);
     278         1505 :             MemoryContextSwitchTo(oldcxt);
     279         1505 :             pfree(str_value);
     280              :         }
     281              :         else
     282          213 :             policy->qual = NULL;
     283              : 
     284              :         /* Get WITH CHECK qual */
     285         1718 :         datum = heap_getattr(tuple, Anum_pg_policy_polwithcheck,
     286              :                              RelationGetDescr(catalog), &isnull);
     287         1718 :         if (!isnull)
     288              :         {
     289          518 :             str_value = TextDatumGetCString(datum);
     290          518 :             MemoryContextSwitchTo(rscxt);
     291          518 :             policy->with_check_qual = (Expr *) stringToNode(str_value);
     292          518 :             MemoryContextSwitchTo(oldcxt);
     293          518 :             pfree(str_value);
     294              :         }
     295              :         else
     296         1200 :             policy->with_check_qual = NULL;
     297              : 
     298              :         /* We want to cache whether there are SubLinks in these expressions */
     299         3280 :         policy->hassublinks = checkExprHasSubLink((Node *) policy->qual) ||
     300         1562 :             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         1718 :         MemoryContextSwitchTo(rscxt);
     307         1718 :         rsdesc->policies = lcons(policy, rsdesc->policies);
     308         1718 :         MemoryContextSwitchTo(oldcxt);
     309              :     }
     310              : 
     311         1220 :     systable_endscan(sscan);
     312         1220 :     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         1220 :     MemoryContextSetParent(rscxt, CacheMemoryContext);
     320              : 
     321         1220 :     relation->rd_rsdesc = rsdesc;
     322         1220 : }
     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          336 : RemovePolicyById(Oid policy_id)
     333              : {
     334              :     Relation    pg_policy_rel;
     335              :     SysScanDesc sscan;
     336              :     ScanKeyData skey[1];
     337              :     HeapTuple   tuple;
     338              :     Oid         relid;
     339              :     Relation    rel;
     340              : 
     341          336 :     pg_policy_rel = table_open(PolicyRelationId, RowExclusiveLock);
     342              : 
     343              :     /*
     344              :      * Find the policy to delete.
     345              :      */
     346          336 :     ScanKeyInit(&skey[0],
     347              :                 Anum_pg_policy_oid,
     348              :                 BTEqualStrategyNumber, F_OIDEQ,
     349              :                 ObjectIdGetDatum(policy_id));
     350              : 
     351          336 :     sscan = systable_beginscan(pg_policy_rel, PolicyOidIndexId, true,
     352              :                                NULL, 1, skey);
     353              : 
     354          336 :     tuple = systable_getnext(sscan);
     355              : 
     356              :     /* If the policy exists, then remove it, otherwise raise an error. */
     357          336 :     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          336 :     relid = ((Form_pg_policy) GETSTRUCT(tuple))->polrelid;
     367              : 
     368          336 :     rel = table_open(relid, AccessExclusiveLock);
     369          336 :     if (rel->rd_rel->relkind != RELKIND_RELATION &&
     370           27 :         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          336 :     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          336 :     CatalogTupleDelete(pg_policy_rel, &tuple->t_self);
     383              : 
     384          336 :     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          336 :     CacheInvalidateRelcache(rel);
     395              : 
     396          336 :     table_close(rel, NoLock);
     397              : 
     398              :     /* Clean up */
     399          336 :     table_close(pg_policy_rel, RowExclusiveLock);
     400          336 : }
     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           22 : RemoveRoleFromObjectPolicy(Oid roleid, Oid classid, Oid policy_id)
     417              : {
     418              :     Relation    pg_policy_rel;
     419              :     SysScanDesc sscan;
     420              :     ScanKeyData skey[1];
     421              :     HeapTuple   tuple;
     422              :     Oid         relid;
     423              :     ArrayType  *policy_roles;
     424              :     Datum       roles_datum;
     425              :     Oid        *roles;
     426              :     int         num_roles;
     427              :     Datum      *role_oids;
     428              :     bool        attr_isnull;
     429           22 :     bool        keep_policy = true;
     430              :     int         i,
     431              :                 j;
     432              : 
     433              :     Assert(classid == PolicyRelationId);
     434              : 
     435           22 :     pg_policy_rel = table_open(PolicyRelationId, RowExclusiveLock);
     436              : 
     437              :     /*
     438              :      * Find the policy to update.
     439              :      */
     440           22 :     ScanKeyInit(&skey[0],
     441              :                 Anum_pg_policy_oid,
     442              :                 BTEqualStrategyNumber, F_OIDEQ,
     443              :                 ObjectIdGetDatum(policy_id));
     444              : 
     445           22 :     sscan = systable_beginscan(pg_policy_rel, PolicyOidIndexId, true,
     446              :                                NULL, 1, skey);
     447              : 
     448           22 :     tuple = systable_getnext(sscan);
     449              : 
     450              :     /* Raise an error if we don't find the policy. */
     451           22 :     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           22 :     relid = ((Form_pg_policy) GETSTRUCT(tuple))->polrelid;
     456              : 
     457              :     /* Get the current set of roles */
     458           22 :     roles_datum = heap_getattr(tuple,
     459              :                                Anum_pg_policy_polroles,
     460              :                                RelationGetDescr(pg_policy_rel),
     461              :                                &attr_isnull);
     462              : 
     463              :     Assert(!attr_isnull);
     464              : 
     465           22 :     policy_roles = DatumGetArrayTypePCopy(roles_datum);
     466           22 :     roles = (Oid *) ARR_DATA_PTR(policy_roles);
     467           22 :     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           22 :     role_oids = palloc_array(Datum, num_roles);
     475           62 :     for (i = 0, j = 0; i < num_roles; i++)
     476              :     {
     477           40 :         if (roles[i] != roleid)
     478           12 :             role_oids[j++] = ObjectIdGetDatum(roles[i]);
     479              :     }
     480           22 :     num_roles = j;
     481              : 
     482              :     /* If any roles remain, update the policy entry. */
     483           22 :     if (num_roles > 0)
     484              :     {
     485              :         ArrayType  *role_ids;
     486              :         Datum       values[Natts_pg_policy];
     487              :         bool        isnull[Natts_pg_policy];
     488              :         bool        replaces[Natts_pg_policy];
     489              :         HeapTuple   new_tuple;
     490              :         HeapTuple   reltup;
     491              :         ObjectAddress target;
     492              :         ObjectAddress myself;
     493              : 
     494              :         /* zero-clear */
     495           12 :         memset(values, 0, sizeof(values));
     496           12 :         memset(replaces, 0, sizeof(replaces));
     497           12 :         memset(isnull, 0, sizeof(isnull));
     498              : 
     499              :         /* This is the array for the new tuple */
     500           12 :         role_ids = construct_array_builtin(role_oids, num_roles, OIDOID);
     501              : 
     502           12 :         replaces[Anum_pg_policy_polroles - 1] = true;
     503           12 :         values[Anum_pg_policy_polroles - 1] = PointerGetDatum(role_ids);
     504              : 
     505           12 :         new_tuple = heap_modify_tuple(tuple,
     506              :                                       RelationGetDescr(pg_policy_rel),
     507              :                                       values, isnull, replaces);
     508           12 :         CatalogTupleUpdate(pg_policy_rel, &new_tuple->t_self, new_tuple);
     509              : 
     510              :         /* Remove all the old shared dependencies (roles) */
     511           12 :         deleteSharedDependencyRecordsFor(PolicyRelationId, policy_id, 0);
     512              : 
     513              :         /* Record the new shared dependencies (roles) */
     514           12 :         myself.classId = PolicyRelationId;
     515           12 :         myself.objectId = policy_id;
     516           12 :         myself.objectSubId = 0;
     517              : 
     518           12 :         target.classId = AuthIdRelationId;
     519           12 :         target.objectSubId = 0;
     520           24 :         for (i = 0; i < num_roles; i++)
     521              :         {
     522           12 :             target.objectId = DatumGetObjectId(role_oids[i]);
     523              :             /* no need for dependency on the public role */
     524           12 :             if (target.objectId != ACL_ID_PUBLIC)
     525           12 :                 recordSharedDependencyOn(&myself, &target,
     526              :                                          SHARED_DEPENDENCY_POLICY);
     527              :         }
     528              : 
     529           12 :         InvokeObjectPostAlterHook(PolicyRelationId, policy_id, 0);
     530              : 
     531           12 :         heap_freetuple(new_tuple);
     532              : 
     533              :         /* Make updates visible */
     534           12 :         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           12 :         reltup = SearchSysCache1(RELOID, ObjectIdGetDatum(relid));
     542           12 :         if (HeapTupleIsValid(reltup))
     543              :         {
     544           12 :             CacheInvalidateRelcacheByTuple(reltup);
     545           12 :             ReleaseSysCache(reltup);
     546              :         }
     547              :     }
     548              :     else
     549              :     {
     550              :         /* No roles would remain, so drop the policy instead. */
     551           10 :         keep_policy = false;
     552              :     }
     553              : 
     554              :     /* Clean up. */
     555           22 :     systable_endscan(sscan);
     556              : 
     557           22 :     table_close(pg_policy_rel, RowExclusiveLock);
     558              : 
     559           22 :     return keep_policy;
     560              : }
     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          395 : CreatePolicy(CreatePolicyStmt *stmt)
     570              : {
     571              :     Relation    pg_policy_rel;
     572              :     Oid         policy_id;
     573              :     Relation    target_table;
     574              :     Oid         table_id;
     575              :     char        polcmd;
     576              :     Datum      *role_oids;
     577          395 :     int         nitems = 0;
     578              :     ArrayType  *role_ids;
     579              :     ParseState *qual_pstate;
     580              :     ParseState *with_check_pstate;
     581              :     ParseNamespaceItem *nsitem;
     582              :     Node       *qual;
     583              :     Node       *with_check_qual;
     584              :     ScanKeyData skey[2];
     585              :     SysScanDesc sscan;
     586              :     HeapTuple   policy_tuple;
     587              :     Datum       values[Natts_pg_policy];
     588              :     bool        isnull[Natts_pg_policy];
     589              :     ObjectAddress target;
     590              :     ObjectAddress myself;
     591              :     int         i;
     592              : 
     593              :     /* Parse command */
     594          395 :     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          395 :     if ((polcmd == ACL_SELECT_CHR || polcmd == ACL_DELETE_CHR)
     600           89 :         && 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          395 :     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          395 :     role_oids = policy_role_list_to_array(stmt->roles, &nitems);
     616          395 :     role_ids = construct_array_builtin(role_oids, nitems, OIDOID);
     617              : 
     618              :     /* Parse the supplied clause */
     619          395 :     qual_pstate = make_parsestate(NULL);
     620          395 :     with_check_pstate = make_parsestate(NULL);
     621              : 
     622              :     /* zero-clear */
     623          395 :     memset(values, 0, sizeof(values));
     624          395 :     memset(isnull, 0, sizeof(isnull));
     625              : 
     626              :     /* Get id of table.  Also handles permissions checks. */
     627          395 :     table_id = RangeVarGetRelidExtended(stmt->table, AccessExclusiveLock,
     628              :                                         0,
     629              :                                         RangeVarCallbackForPolicy,
     630              :                                         stmt);
     631              : 
     632              :     /* Open target_table to build quals. No additional lock is necessary. */
     633          394 :     target_table = relation_open(table_id, NoLock);
     634              : 
     635              :     /* Add for the regular security quals */
     636          394 :     nsitem = addRangeTableEntryForRelation(qual_pstate, target_table,
     637              :                                            AccessShareLock,
     638              :                                            NULL, false, false);
     639          394 :     addNSItemToQuery(qual_pstate, nsitem, false, true, true);
     640              : 
     641              :     /* Add for the with-check quals */
     642          394 :     nsitem = addRangeTableEntryForRelation(with_check_pstate, target_table,
     643              :                                            AccessShareLock,
     644              :                                            NULL, false, false);
     645          394 :     addNSItemToQuery(with_check_pstate, nsitem, false, true, true);
     646              : 
     647          394 :     qual = transformWhereClause(qual_pstate,
     648              :                                 stmt->qual,
     649              :                                 EXPR_KIND_POLICY,
     650              :                                 "POLICY");
     651              : 
     652          391 :     with_check_qual = transformWhereClause(with_check_pstate,
     653              :                                            stmt->with_check,
     654              :                                            EXPR_KIND_POLICY,
     655              :                                            "POLICY");
     656              : 
     657              :     /* Fix up collation information */
     658          391 :     assign_expr_collations(qual_pstate, qual);
     659          391 :     assign_expr_collations(with_check_pstate, with_check_qual);
     660              : 
     661              :     /* Open pg_policy catalog */
     662          391 :     pg_policy_rel = table_open(PolicyRelationId, RowExclusiveLock);
     663              : 
     664              :     /* Set key - policy's relation id. */
     665          391 :     ScanKeyInit(&skey[0],
     666              :                 Anum_pg_policy_polrelid,
     667              :                 BTEqualStrategyNumber, F_OIDEQ,
     668              :                 ObjectIdGetDatum(table_id));
     669              : 
     670              :     /* Set key - policy's name. */
     671          391 :     ScanKeyInit(&skey[1],
     672              :                 Anum_pg_policy_polname,
     673              :                 BTEqualStrategyNumber, F_NAMEEQ,
     674          391 :                 CStringGetDatum(stmt->policy_name));
     675              : 
     676          391 :     sscan = systable_beginscan(pg_policy_rel,
     677              :                                PolicyPolrelidPolnameIndexId, true, NULL, 2,
     678              :                                skey);
     679              : 
     680          391 :     policy_tuple = systable_getnext(sscan);
     681              : 
     682              :     /* Complain if the policy name already exists for the table */
     683          391 :     if (HeapTupleIsValid(policy_tuple))
     684            3 :         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          388 :     policy_id = GetNewOidWithIndex(pg_policy_rel, PolicyOidIndexId,
     690              :                                    Anum_pg_policy_oid);
     691          388 :     values[Anum_pg_policy_oid - 1] = ObjectIdGetDatum(policy_id);
     692          388 :     values[Anum_pg_policy_polrelid - 1] = ObjectIdGetDatum(table_id);
     693          388 :     values[Anum_pg_policy_polname - 1] = DirectFunctionCall1(namein,
     694              :                                                              CStringGetDatum(stmt->policy_name));
     695          388 :     values[Anum_pg_policy_polcmd - 1] = CharGetDatum(polcmd);
     696          388 :     values[Anum_pg_policy_polpermissive - 1] = BoolGetDatum(stmt->permissive);
     697          388 :     values[Anum_pg_policy_polroles - 1] = PointerGetDatum(role_ids);
     698              : 
     699              :     /* Add qual if present. */
     700          388 :     if (qual)
     701          356 :         values[Anum_pg_policy_polqual - 1] = CStringGetTextDatum(nodeToString(qual));
     702              :     else
     703           32 :         isnull[Anum_pg_policy_polqual - 1] = true;
     704              : 
     705              :     /* Add WITH CHECK qual if present */
     706          388 :     if (with_check_qual)
     707           76 :         values[Anum_pg_policy_polwithcheck - 1] = CStringGetTextDatum(nodeToString(with_check_qual));
     708              :     else
     709          312 :         isnull[Anum_pg_policy_polwithcheck - 1] = true;
     710              : 
     711          388 :     policy_tuple = heap_form_tuple(RelationGetDescr(pg_policy_rel), values,
     712              :                                    isnull);
     713              : 
     714          388 :     CatalogTupleInsert(pg_policy_rel, policy_tuple);
     715              : 
     716              :     /* Record Dependencies */
     717          388 :     target.classId = RelationRelationId;
     718          388 :     target.objectId = table_id;
     719          388 :     target.objectSubId = 0;
     720              : 
     721          388 :     myself.classId = PolicyRelationId;
     722          388 :     myself.objectId = policy_id;
     723          388 :     myself.objectSubId = 0;
     724              : 
     725          388 :     recordDependencyOn(&myself, &target, DEPENDENCY_AUTO);
     726              : 
     727          388 :     recordDependencyOnExpr(&myself, qual, qual_pstate->p_rtable,
     728              :                            DEPENDENCY_NORMAL);
     729              : 
     730          388 :     recordDependencyOnExpr(&myself, with_check_qual,
     731              :                            with_check_pstate->p_rtable, DEPENDENCY_NORMAL);
     732              : 
     733              :     /* Register role dependencies */
     734          388 :     target.classId = AuthIdRelationId;
     735          388 :     target.objectSubId = 0;
     736          794 :     for (i = 0; i < nitems; i++)
     737              :     {
     738          406 :         target.objectId = DatumGetObjectId(role_oids[i]);
     739              :         /* no dependency if public */
     740          406 :         if (target.objectId != ACL_ID_PUBLIC)
     741           74 :             recordSharedDependencyOn(&myself, &target,
     742              :                                      SHARED_DEPENDENCY_POLICY);
     743              :     }
     744              : 
     745          388 :     InvokeObjectPostCreateHook(PolicyRelationId, policy_id, 0);
     746              : 
     747              :     /* Invalidate Relation Cache */
     748          388 :     CacheInvalidateRelcache(target_table);
     749              : 
     750              :     /* Clean up. */
     751          388 :     heap_freetuple(policy_tuple);
     752          388 :     free_parsestate(qual_pstate);
     753          388 :     free_parsestate(with_check_pstate);
     754          388 :     systable_endscan(sscan);
     755          388 :     relation_close(target_table, NoLock);
     756          388 :     table_close(pg_policy_rel, RowExclusiveLock);
     757              : 
     758          388 :     return myself;
     759              : }
     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           42 : AlterPolicy(AlterPolicyStmt *stmt)
     769              : {
     770              :     Relation    pg_policy_rel;
     771              :     Oid         policy_id;
     772              :     Relation    target_table;
     773              :     Oid         table_id;
     774           42 :     Datum      *role_oids = NULL;
     775           42 :     int         nitems = 0;
     776           42 :     ArrayType  *role_ids = NULL;
     777           42 :     List       *qual_parse_rtable = NIL;
     778           42 :     List       *with_check_parse_rtable = NIL;
     779           42 :     Node       *qual = NULL;
     780           42 :     Node       *with_check_qual = NULL;
     781              :     ScanKeyData skey[2];
     782              :     SysScanDesc sscan;
     783              :     HeapTuple   policy_tuple;
     784              :     HeapTuple   new_tuple;
     785              :     Datum       values[Natts_pg_policy];
     786              :     bool        isnull[Natts_pg_policy];
     787              :     bool        replaces[Natts_pg_policy];
     788              :     ObjectAddress target;
     789              :     ObjectAddress myself;
     790              :     Datum       polcmd_datum;
     791              :     char        polcmd;
     792              :     bool        polcmd_isnull;
     793              :     int         i;
     794              : 
     795              :     /* Parse role_ids */
     796           42 :     if (stmt->roles != NULL)
     797              :     {
     798            6 :         role_oids = policy_role_list_to_array(stmt->roles, &nitems);
     799            6 :         role_ids = construct_array_builtin(role_oids, nitems, OIDOID);
     800              :     }
     801              : 
     802              :     /* Get id of table.  Also handles permissions checks. */
     803           42 :     table_id = RangeVarGetRelidExtended(stmt->table, AccessExclusiveLock,
     804              :                                         0,
     805              :                                         RangeVarCallbackForPolicy,
     806              :                                         stmt);
     807              : 
     808           36 :     target_table = relation_open(table_id, NoLock);
     809              : 
     810              :     /* Parse the using policy clause */
     811           36 :     if (stmt->qual)
     812              :     {
     813              :         ParseNamespaceItem *nsitem;
     814           33 :         ParseState *qual_pstate = make_parsestate(NULL);
     815              : 
     816           33 :         nsitem = addRangeTableEntryForRelation(qual_pstate, target_table,
     817              :                                                AccessShareLock,
     818              :                                                NULL, false, false);
     819              : 
     820           33 :         addNSItemToQuery(qual_pstate, nsitem, false, true, true);
     821              : 
     822           33 :         qual = transformWhereClause(qual_pstate, stmt->qual,
     823              :                                     EXPR_KIND_POLICY,
     824              :                                     "POLICY");
     825              : 
     826              :         /* Fix up collation information */
     827           33 :         assign_expr_collations(qual_pstate, qual);
     828              : 
     829           33 :         qual_parse_rtable = qual_pstate->p_rtable;
     830           33 :         free_parsestate(qual_pstate);
     831              :     }
     832              : 
     833              :     /* Parse the with-check policy clause */
     834           36 :     if (stmt->with_check)
     835              :     {
     836              :         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              :                                                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              :     }
     856              : 
     857              :     /* zero-clear */
     858           36 :     memset(values, 0, sizeof(values));
     859           36 :     memset(replaces, 0, sizeof(replaces));
     860           36 :     memset(isnull, 0, sizeof(isnull));
     861              : 
     862              :     /* Find policy to update. */
     863           36 :     pg_policy_rel = table_open(PolicyRelationId, RowExclusiveLock);
     864              : 
     865              :     /* Set key - policy's relation id. */
     866           36 :     ScanKeyInit(&skey[0],
     867              :                 Anum_pg_policy_polrelid,
     868              :                 BTEqualStrategyNumber, F_OIDEQ,
     869              :                 ObjectIdGetDatum(table_id));
     870              : 
     871              :     /* Set key - policy's name. */
     872           36 :     ScanKeyInit(&skey[1],
     873              :                 Anum_pg_policy_polname,
     874              :                 BTEqualStrategyNumber, F_NAMEEQ,
     875           36 :                 CStringGetDatum(stmt->policy_name));
     876              : 
     877           36 :     sscan = systable_beginscan(pg_policy_rel,
     878              :                                PolicyPolrelidPolnameIndexId, true, NULL, 2,
     879              :                                skey);
     880              : 
     881           36 :     policy_tuple = systable_getnext(sscan);
     882              : 
     883              :     /* Check that the policy is found, raise an error if not. */
     884           36 :     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           36 :     polcmd_datum = heap_getattr(policy_tuple, Anum_pg_policy_polcmd,
     893              :                                 RelationGetDescr(pg_policy_rel),
     894              :                                 &polcmd_isnull);
     895              :     Assert(!polcmd_isnull);
     896           36 :     polcmd = DatumGetChar(polcmd_datum);
     897              : 
     898              :     /*
     899              :      * If the command is SELECT or DELETE then WITH CHECK should be NULL.
     900              :      */
     901           36 :     if ((polcmd == ACL_SELECT_CHR || polcmd == ACL_DELETE_CHR)
     902            0 :         && 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           36 :     if ((polcmd == ACL_INSERT_CHR)
     912            0 :         && stmt->qual != NULL)
     913            0 :         ereport(ERROR,
     914              :                 (errcode(ERRCODE_SYNTAX_ERROR),
     915              :                  errmsg("only WITH CHECK expression allowed for INSERT")));
     916              : 
     917           36 :     policy_id = ((Form_pg_policy) GETSTRUCT(policy_tuple))->oid;
     918              : 
     919           36 :     if (role_ids != NULL)
     920              :     {
     921            6 :         replaces[Anum_pg_policy_polroles - 1] = true;
     922            6 :         values[Anum_pg_policy_polroles - 1] = PointerGetDatum(role_ids);
     923              :     }
     924              :     else
     925              :     {
     926              :         Oid        *roles;
     927              :         Datum       roles_datum;
     928              :         bool        attr_isnull;
     929              :         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           30 :         roles_datum = heap_getattr(policy_tuple, Anum_pg_policy_polroles,
     938              :                                    RelationGetDescr(pg_policy_rel),
     939              :                                    &attr_isnull);
     940              :         Assert(!attr_isnull);
     941              : 
     942           30 :         policy_roles = DatumGetArrayTypePCopy(roles_datum);
     943              : 
     944           30 :         roles = (Oid *) ARR_DATA_PTR(policy_roles);
     945              : 
     946           30 :         nitems = ARR_DIMS(policy_roles)[0];
     947              : 
     948           30 :         role_oids = palloc_array(Datum, nitems);
     949              : 
     950           63 :         for (i = 0; i < nitems; i++)
     951           33 :             role_oids[i] = ObjectIdGetDatum(roles[i]);
     952              :     }
     953              : 
     954           36 :     if (qual != NULL)
     955              :     {
     956           33 :         replaces[Anum_pg_policy_polqual - 1] = true;
     957              :         values[Anum_pg_policy_polqual - 1]
     958           33 :             = CStringGetTextDatum(nodeToString(qual));
     959              :     }
     960              :     else
     961              :     {
     962              :         Datum       value_datum;
     963              :         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            3 :         value_datum = heap_getattr(policy_tuple, Anum_pg_policy_polqual,
     973              :                                    RelationGetDescr(pg_policy_rel),
     974              :                                    &attr_isnull);
     975            3 :         if (!attr_isnull)
     976              :         {
     977              :             char       *qual_value;
     978              :             ParseState *qual_pstate;
     979              : 
     980              :             /* parsestate is built just to build the range table */
     981            3 :             qual_pstate = make_parsestate(NULL);
     982              : 
     983            3 :             qual_value = TextDatumGetCString(value_datum);
     984            3 :             qual = stringToNode(qual_value);
     985              : 
     986              :             /* Add this rel to the parsestate's rangetable, for dependencies */
     987            3 :             (void) addRangeTableEntryForRelation(qual_pstate, target_table,
     988              :                                                  AccessShareLock,
     989              :                                                  NULL, false, false);
     990              : 
     991            3 :             qual_parse_rtable = qual_pstate->p_rtable;
     992            3 :             free_parsestate(qual_pstate);
     993              :         }
     994              :     }
     995              : 
     996           36 :     if (with_check_qual != NULL)
     997              :     {
     998            0 :         replaces[Anum_pg_policy_polwithcheck - 1] = true;
     999              :         values[Anum_pg_policy_polwithcheck - 1]
    1000            0 :             = CStringGetTextDatum(nodeToString(with_check_qual));
    1001              :     }
    1002              :     else
    1003              :     {
    1004              :         Datum       value_datum;
    1005              :         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           36 :         value_datum = heap_getattr(policy_tuple, Anum_pg_policy_polwithcheck,
    1015              :                                    RelationGetDescr(pg_policy_rel),
    1016              :                                    &attr_isnull);
    1017           36 :         if (!attr_isnull)
    1018              :         {
    1019              :             char       *with_check_value;
    1020              :             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              :                                                  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              :         }
    1037              :     }
    1038              : 
    1039           36 :     new_tuple = heap_modify_tuple(policy_tuple,
    1040              :                                   RelationGetDescr(pg_policy_rel),
    1041              :                                   values, isnull, replaces);
    1042           36 :     CatalogTupleUpdate(pg_policy_rel, &new_tuple->t_self, new_tuple);
    1043              : 
    1044              :     /* Update Dependencies. */
    1045           36 :     deleteDependencyRecordsFor(PolicyRelationId, policy_id, false);
    1046              : 
    1047              :     /* Record Dependencies */
    1048           36 :     target.classId = RelationRelationId;
    1049           36 :     target.objectId = table_id;
    1050           36 :     target.objectSubId = 0;
    1051              : 
    1052           36 :     myself.classId = PolicyRelationId;
    1053           36 :     myself.objectId = policy_id;
    1054           36 :     myself.objectSubId = 0;
    1055              : 
    1056           36 :     recordDependencyOn(&myself, &target, DEPENDENCY_AUTO);
    1057              : 
    1058           36 :     recordDependencyOnExpr(&myself, qual, qual_parse_rtable, DEPENDENCY_NORMAL);
    1059              : 
    1060           36 :     recordDependencyOnExpr(&myself, with_check_qual, with_check_parse_rtable,
    1061              :                            DEPENDENCY_NORMAL);
    1062              : 
    1063              :     /* Register role dependencies */
    1064           36 :     deleteSharedDependencyRecordsFor(PolicyRelationId, policy_id, 0);
    1065           36 :     target.classId = AuthIdRelationId;
    1066           36 :     target.objectSubId = 0;
    1067           78 :     for (i = 0; i < nitems; i++)
    1068              :     {
    1069           42 :         target.objectId = DatumGetObjectId(role_oids[i]);
    1070              :         /* no dependency if public */
    1071           42 :         if (target.objectId != ACL_ID_PUBLIC)
    1072           15 :             recordSharedDependencyOn(&myself, &target,
    1073              :                                      SHARED_DEPENDENCY_POLICY);
    1074              :     }
    1075              : 
    1076           36 :     InvokeObjectPostAlterHook(PolicyRelationId, policy_id, 0);
    1077              : 
    1078           36 :     heap_freetuple(new_tuple);
    1079              : 
    1080              :     /* Invalidate Relation Cache */
    1081           36 :     CacheInvalidateRelcache(target_table);
    1082              : 
    1083              :     /* Clean up. */
    1084           36 :     systable_endscan(sscan);
    1085           36 :     relation_close(target_table, NoLock);
    1086           36 :     table_close(pg_policy_rel, RowExclusiveLock);
    1087              : 
    1088           36 :     return myself;
    1089              : }
    1090              : 
    1091              : /*
    1092              :  * rename_policy -
    1093              :  *   change the name of a policy on a relation
    1094              :  */
    1095              : ObjectAddress
    1096            9 : rename_policy(RenameStmt *stmt)
    1097              : {
    1098              :     Relation    pg_policy_rel;
    1099              :     Relation    target_table;
    1100              :     Oid         table_id;
    1101              :     Oid         opoloid;
    1102              :     ScanKeyData skey[2];
    1103              :     SysScanDesc sscan;
    1104              :     HeapTuple   policy_tuple;
    1105              :     ObjectAddress address;
    1106              : 
    1107              :     /* Get id of table.  Also handles permissions checks. */
    1108            9 :     table_id = RangeVarGetRelidExtended(stmt->relation, AccessExclusiveLock,
    1109              :                                         0,
    1110              :                                         RangeVarCallbackForPolicy,
    1111              :                                         stmt);
    1112              : 
    1113            9 :     target_table = relation_open(table_id, NoLock);
    1114              : 
    1115            9 :     pg_policy_rel = table_open(PolicyRelationId, RowExclusiveLock);
    1116              : 
    1117              :     /* First pass -- check for conflict */
    1118              : 
    1119              :     /* Add key - policy's relation id. */
    1120            9 :     ScanKeyInit(&skey[0],
    1121              :                 Anum_pg_policy_polrelid,
    1122              :                 BTEqualStrategyNumber, F_OIDEQ,
    1123              :                 ObjectIdGetDatum(table_id));
    1124              : 
    1125              :     /* Add key - policy's name. */
    1126            9 :     ScanKeyInit(&skey[1],
    1127              :                 Anum_pg_policy_polname,
    1128              :                 BTEqualStrategyNumber, F_NAMEEQ,
    1129            9 :                 CStringGetDatum(stmt->newname));
    1130              : 
    1131            9 :     sscan = systable_beginscan(pg_policy_rel,
    1132              :                                PolicyPolrelidPolnameIndexId, true, NULL, 2,
    1133              :                                skey);
    1134              : 
    1135            9 :     if (HeapTupleIsValid(systable_getnext(sscan)))
    1136            3 :         ereport(ERROR,
    1137              :                 (errcode(ERRCODE_DUPLICATE_OBJECT),
    1138              :                  errmsg("policy \"%s\" for table \"%s\" already exists",
    1139              :                         stmt->newname, RelationGetRelationName(target_table))));
    1140              : 
    1141            6 :     systable_endscan(sscan);
    1142              : 
    1143              :     /* Second pass -- find existing policy and update */
    1144              :     /* Add key - policy's relation id. */
    1145            6 :     ScanKeyInit(&skey[0],
    1146              :                 Anum_pg_policy_polrelid,
    1147              :                 BTEqualStrategyNumber, F_OIDEQ,
    1148              :                 ObjectIdGetDatum(table_id));
    1149              : 
    1150              :     /* Add key - policy's name. */
    1151            6 :     ScanKeyInit(&skey[1],
    1152              :                 Anum_pg_policy_polname,
    1153              :                 BTEqualStrategyNumber, F_NAMEEQ,
    1154            6 :                 CStringGetDatum(stmt->subname));
    1155              : 
    1156            6 :     sscan = systable_beginscan(pg_policy_rel,
    1157              :                                PolicyPolrelidPolnameIndexId, true, NULL, 2,
    1158              :                                skey);
    1159              : 
    1160            6 :     policy_tuple = systable_getnext(sscan);
    1161              : 
    1162              :     /* Complain if we did not find the policy */
    1163            6 :     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            6 :     opoloid = ((Form_pg_policy) GETSTRUCT(policy_tuple))->oid;
    1170              : 
    1171            6 :     policy_tuple = heap_copytuple(policy_tuple);
    1172              : 
    1173            6 :     namestrcpy(&((Form_pg_policy) GETSTRUCT(policy_tuple))->polname,
    1174            6 :                stmt->newname);
    1175              : 
    1176            6 :     CatalogTupleUpdate(pg_policy_rel, &policy_tuple->t_self, policy_tuple);
    1177              : 
    1178            6 :     InvokeObjectPostAlterHook(PolicyRelationId, opoloid, 0);
    1179              : 
    1180            6 :     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            6 :     CacheInvalidateRelcache(target_table);
    1188              : 
    1189              :     /* Clean up. */
    1190            6 :     systable_endscan(sscan);
    1191            6 :     table_close(pg_policy_rel, RowExclusiveLock);
    1192            6 :     relation_close(target_table, NoLock);
    1193              : 
    1194            6 :     return address;
    1195              : }
    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           98 : get_relation_policy_oid(Oid relid, const char *policy_name, bool missing_ok)
    1205              : {
    1206              :     Relation    pg_policy_rel;
    1207              :     ScanKeyData skey[2];
    1208              :     SysScanDesc sscan;
    1209              :     HeapTuple   policy_tuple;
    1210              :     Oid         policy_oid;
    1211              : 
    1212           98 :     pg_policy_rel = table_open(PolicyRelationId, AccessShareLock);
    1213              : 
    1214              :     /* Add key - policy's relation id. */
    1215           98 :     ScanKeyInit(&skey[0],
    1216              :                 Anum_pg_policy_polrelid,
    1217              :                 BTEqualStrategyNumber, F_OIDEQ,
    1218              :                 ObjectIdGetDatum(relid));
    1219              : 
    1220              :     /* Add key - policy's name. */
    1221           98 :     ScanKeyInit(&skey[1],
    1222              :                 Anum_pg_policy_polname,
    1223              :                 BTEqualStrategyNumber, F_NAMEEQ,
    1224              :                 CStringGetDatum(policy_name));
    1225              : 
    1226           98 :     sscan = systable_beginscan(pg_policy_rel,
    1227              :                                PolicyPolrelidPolnameIndexId, true, NULL, 2,
    1228              :                                skey);
    1229              : 
    1230           98 :     policy_tuple = systable_getnext(sscan);
    1231              : 
    1232           98 :     if (!HeapTupleIsValid(policy_tuple))
    1233              :     {
    1234            6 :         if (!missing_ok)
    1235            6 :             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              :     }
    1242              :     else
    1243           92 :         policy_oid = ((Form_pg_policy) GETSTRUCT(policy_tuple))->oid;
    1244              : 
    1245              :     /* Clean up. */
    1246           92 :     systable_endscan(sscan);
    1247           92 :     table_close(pg_policy_rel, AccessShareLock);
    1248              : 
    1249           92 :     return policy_oid;
    1250              : }
    1251              : 
    1252              : /*
    1253              :  * relation_has_policies - Determine if relation has any policies
    1254              :  */
    1255              : bool
    1256            0 : relation_has_policies(Relation rel)
    1257              : {
    1258              :     Relation    catalog;
    1259              :     ScanKeyData skey;
    1260              :     SysScanDesc sscan;
    1261              :     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              :                 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              : }
        

Generated by: LCOV version 2.0-1