Re: DROP OWNED BY fails to clean out pg_init_privs grants - Mailing list pgsql-hackers

From Tom Lane
Subject Re: DROP OWNED BY fails to clean out pg_init_privs grants
Date
Msg-id 378162.1714257933@sss.pgh.pa.us
Whole thread Raw
In response to Re: DROP OWNED BY fails to clean out pg_init_privs grants  (Tom Lane <tgl@sss.pgh.pa.us>)
Responses Re: DROP OWNED BY fails to clean out pg_init_privs grants
List pgsql-hackers
I wrote:
> A bigger problem though is that I think you are addressing the
> original complaint from the older thread, which while it's a fine
> thing to fix seems orthogonal to the failure we're seeing in the
> buildfarm.  The buildfarm's problem is not that we're recording
> incorrect pg_init_privs entries, it's that when we do create such
> entries we're failing to show their dependency on the grantee role
> in pg_shdepend.  We've missed spotting that so far because it's
> so seldom that pg_init_privs entries reference any but built-in
> roles (or at least roles that'd likely outlive the extension).

Here's a draft patch that attacks that.  It seems to fix the
problem with test_pg_dump: no dangling pg_init_privs grants
are left behind.

A lot of the changes here are just involved with needing to pass the
object's owner OID to recordExtensionInitPriv so that it can be passed
to updateAclDependencies.  One thing I'm a bit worried about is that
some of the new code assumes that all object types that are of
interest here will have catcaches on OID, so that it's possible to
fetch the owner OID for a generic object-with-privileges using the
catcache and objectaddress.c's tables of object properties.  That
assumption seems to exist already, eg ExecGrant_common also assumes
it, but it's not obvious that it must be so.

            regards, tom lane

diff --git a/doc/src/sgml/catalogs.sgml b/doc/src/sgml/catalogs.sgml
index 2907079e2a..c8cb46c5b9 100644
--- a/doc/src/sgml/catalogs.sgml
+++ b/doc/src/sgml/catalogs.sgml
@@ -7184,6 +7184,20 @@ SCRAM-SHA-256$<replaceable><iteration count></replaceable>:<replaceable>&l
      </listitem>
     </varlistentry>

+    <varlistentry>
+     <term><symbol>SHARED_DEPENDENCY_INITACL</symbol> (<literal>i</literal>)</term>
+     <listitem>
+      <para>
+       The referenced object (which must be a role) is mentioned in a
+       <link linkend="catalog-pg-init-privs"><structname>pg_init_privs</structname></link>
+       entry for the dependent object.
+       (A <symbol>SHARED_DEPENDENCY_INITACL</symbol> entry is not made for
+       the owner of the object, since the owner will have
+       a <symbol>SHARED_DEPENDENCY_OWNER</symbol> entry anyway.)
+      </para>
+     </listitem>
+    </varlistentry>
+
     <varlistentry>
      <term><symbol>SHARED_DEPENDENCY_POLICY</symbol> (<literal>r</literal>)</term>
      <listitem>
diff --git a/src/backend/catalog/aclchk.c b/src/backend/catalog/aclchk.c
index 7abf3c2a74..04c41c0c14 100644
--- a/src/backend/catalog/aclchk.c
+++ b/src/backend/catalog/aclchk.c
@@ -165,9 +165,9 @@ static AclMode pg_type_aclmask_ext(Oid type_oid, Oid roleid,
                                    AclMode mask, AclMaskHow how,
                                    bool *is_missing);
 static void recordExtensionInitPriv(Oid objoid, Oid classoid, int objsubid,
-                                    Acl *new_acl);
+                                    Oid ownerId, Acl *new_acl);
 static void recordExtensionInitPrivWorker(Oid objoid, Oid classoid, int objsubid,
-                                          Acl *new_acl);
+                                          Oid ownerId, Acl *new_acl);


 /*
@@ -1790,7 +1790,7 @@ ExecGrant_Attribute(InternalGrant *istmt, Oid relOid, const char *relname,
         CatalogTupleUpdate(attRelation, &newtuple->t_self, newtuple);

         /* Update initial privileges for extensions */
-        recordExtensionInitPriv(relOid, RelationRelationId, attnum,
+        recordExtensionInitPriv(relOid, RelationRelationId, attnum, ownerId,
                                 ACL_NUM(new_acl) > 0 ? new_acl : NULL);

         /* Update the shared dependency ACL info */
@@ -2050,7 +2050,8 @@ ExecGrant_Relation(InternalGrant *istmt)
             CatalogTupleUpdate(relation, &newtuple->t_self, newtuple);

             /* Update initial privileges for extensions */
-            recordExtensionInitPriv(relOid, RelationRelationId, 0, new_acl);
+            recordExtensionInitPriv(relOid, RelationRelationId, 0,
+                                    ownerId, new_acl);

             /* Update the shared dependency ACL info */
             updateAclDependencies(RelationRelationId, relOid, 0,
@@ -2251,7 +2252,7 @@ ExecGrant_common(InternalGrant *istmt, Oid classid, AclMode default_privs,
         CatalogTupleUpdate(relation, &newtuple->t_self, newtuple);

         /* Update initial privileges for extensions */
-        recordExtensionInitPriv(objectid, classid, 0, new_acl);
+        recordExtensionInitPriv(objectid, classid, 0, ownerId, new_acl);

         /* Update the shared dependency ACL info */
         updateAclDependencies(classid,
@@ -2403,7 +2404,8 @@ ExecGrant_Largeobject(InternalGrant *istmt)
         CatalogTupleUpdate(relation, &newtuple->t_self, newtuple);

         /* Update initial privileges for extensions */
-        recordExtensionInitPriv(loid, LargeObjectRelationId, 0, new_acl);
+        recordExtensionInitPriv(loid, LargeObjectRelationId, 0,
+                                ownerId, new_acl);

         /* Update the shared dependency ACL info */
         updateAclDependencies(LargeObjectRelationId,
@@ -2575,7 +2577,7 @@ ExecGrant_Parameter(InternalGrant *istmt)

         /* Update initial privileges for extensions */
         recordExtensionInitPriv(parameterId, ParameterAclRelationId, 0,
-                                new_acl);
+                                ownerId, new_acl);

         /* Update the shared dependency ACL info */
         updateAclDependencies(ParameterAclRelationId, parameterId, 0,
@@ -4463,6 +4465,7 @@ recordExtObjInitPriv(Oid objoid, Oid classoid)
                 }

                 recordExtensionInitPrivWorker(objoid, classoid, curr_att,
+                                              pg_class_tuple->relowner,
                                               DatumGetAclP(attaclDatum));

                 ReleaseSysCache(attTuple);
@@ -4475,6 +4478,7 @@ recordExtObjInitPriv(Oid objoid, Oid classoid)
         /* Add the record, if any, for the top-level object */
         if (!isNull)
             recordExtensionInitPrivWorker(objoid, classoid, 0,
+                                          pg_class_tuple->relowner,
                                           DatumGetAclP(aclDatum));

         ReleaseSysCache(tuple);
@@ -4485,6 +4489,7 @@ recordExtObjInitPriv(Oid objoid, Oid classoid)
         Datum        aclDatum;
         bool        isNull;
         HeapTuple    tuple;
+        Form_pg_largeobject_metadata form_lo_meta;
         ScanKeyData entry[1];
         SysScanDesc scan;
         Relation    relation;
@@ -4509,6 +4514,7 @@ recordExtObjInitPriv(Oid objoid, Oid classoid)
         tuple = systable_getnext(scan);
         if (!HeapTupleIsValid(tuple))
             elog(ERROR, "could not find tuple for large object %u", objoid);
+        form_lo_meta = (Form_pg_largeobject_metadata) GETSTRUCT(tuple);

         aclDatum = heap_getattr(tuple,
                                 Anum_pg_largeobject_metadata_lomacl,
@@ -4517,6 +4523,7 @@ recordExtObjInitPriv(Oid objoid, Oid classoid)
         /* Add the record, if any, for the top-level object */
         if (!isNull)
             recordExtensionInitPrivWorker(objoid, classoid, 0,
+                                          form_lo_meta->lomowner,
                                           DatumGetAclP(aclDatum));

         systable_endscan(scan);
@@ -4524,24 +4531,29 @@ recordExtObjInitPriv(Oid objoid, Oid classoid)
     /* This will error on unsupported classoid. */
     else if (get_object_attnum_acl(classoid) != InvalidAttrNumber)
     {
+        int            cacheid;
+        Oid            ownerId;
         Datum        aclDatum;
         bool        isNull;
         HeapTuple    tuple;

-        tuple = SearchSysCache1(get_object_catcache_oid(classoid),
-                                ObjectIdGetDatum(objoid));
+        cacheid = get_object_catcache_oid(classoid);
+        tuple = SearchSysCache1(cacheid, ObjectIdGetDatum(objoid));
         if (!HeapTupleIsValid(tuple))
             elog(ERROR, "cache lookup failed for %s %u",
                  get_object_class_descr(classoid), objoid);

-        aclDatum = SysCacheGetAttr(get_object_catcache_oid(classoid), tuple,
+        ownerId = DatumGetObjectId(SysCacheGetAttrNotNull(cacheid,
+                                                          tuple,
+                                                          get_object_attnum_owner(classoid)));
+        aclDatum = SysCacheGetAttr(cacheid, tuple,
                                    get_object_attnum_acl(classoid),
                                    &isNull);

         /* Add the record, if any, for the top-level object */
         if (!isNull)
             recordExtensionInitPrivWorker(objoid, classoid, 0,
-                                          DatumGetAclP(aclDatum));
+                                          ownerId, DatumGetAclP(aclDatum));

         ReleaseSysCache(tuple);
     }
@@ -4554,6 +4566,8 @@ recordExtObjInitPriv(Oid objoid, Oid classoid)
 void
 removeExtObjInitPriv(Oid objoid, Oid classoid)
 {
+    Oid            ownerId;
+
     /*
      * If this is a relation then we need to see if there are any sub-objects
      * (eg: columns) for it and, if so, be sure to call
@@ -4568,6 +4582,7 @@ removeExtObjInitPriv(Oid objoid, Oid classoid)
         if (!HeapTupleIsValid(tuple))
             elog(ERROR, "cache lookup failed for relation %u", objoid);
         pg_class_tuple = (Form_pg_class) GETSTRUCT(tuple);
+        ownerId = pg_class_tuple->relowner;

         /*
          * Indexes don't have permissions, neither do the pg_class rows for
@@ -4604,7 +4619,8 @@ removeExtObjInitPriv(Oid objoid, Oid classoid)

                 /* when removing, remove all entries, even dropped columns */

-                recordExtensionInitPrivWorker(objoid, classoid, curr_att, NULL);
+                recordExtensionInitPrivWorker(objoid, classoid, curr_att,
+                                              ownerId, NULL);

                 ReleaseSysCache(attTuple);
             }
@@ -4612,9 +4628,35 @@ removeExtObjInitPriv(Oid objoid, Oid classoid)

         ReleaseSysCache(tuple);
     }
+    else
+    {
+        /* Must find out the owner's OID the hard way */
+        AttrNumber    ownerattnum;
+        int            cacheid;
+        HeapTuple    tuple;
+
+        /*
+         * If the object is of a type that has no owner, it should not have
+         * any pg_init_privs entry either.
+         */
+        ownerattnum = get_object_attnum_owner(classoid);
+        if (ownerattnum == InvalidAttrNumber)
+            return;
+        cacheid = get_object_catcache_oid(classoid);
+        tuple = SearchSysCache1(cacheid, ObjectIdGetDatum(objoid));
+        if (!HeapTupleIsValid(tuple))
+            elog(ERROR, "cache lookup failed for %s %u",
+                 get_object_class_descr(classoid), objoid);
+
+        ownerId = DatumGetObjectId(SysCacheGetAttrNotNull(cacheid,
+                                                          tuple,
+                                                          ownerattnum));
+
+        ReleaseSysCache(tuple);
+    }

     /* Remove the record, if any, for the top-level object */
-    recordExtensionInitPrivWorker(objoid, classoid, 0, NULL);
+    recordExtensionInitPrivWorker(objoid, classoid, 0, ownerId, NULL);
 }

 /*
@@ -4626,7 +4668,8 @@ removeExtObjInitPriv(Oid objoid, Oid classoid)
  * Pass in the object OID, the OID of the class (the OID of the table which
  * the object is defined in) and the 'sub' id of the object (objsubid), if
  * any.  If there is no 'sub' id (they are currently only used for columns of
- * tables) then pass in '0'.  Finally, pass in the complete ACL to store.
+ * tables) then pass in '0'.  Also pass the OID of the object's owner.
+ * Finally, pass in the complete ACL to store.
  *
  * If an ACL already exists for this object/sub-object then we will replace
  * it with what is passed in.
@@ -4635,7 +4678,8 @@ removeExtObjInitPriv(Oid objoid, Oid classoid)
  * removed, if one is found.
  */
 static void
-recordExtensionInitPriv(Oid objoid, Oid classoid, int objsubid, Acl *new_acl)
+recordExtensionInitPriv(Oid objoid, Oid classoid, int objsubid,
+                        Oid ownerId, Acl *new_acl)
 {
     /*
      * Generally, we only record the initial privileges when an extension is
@@ -4648,7 +4692,7 @@ recordExtensionInitPriv(Oid objoid, Oid classoid, int objsubid, Acl *new_acl)
     if (!creating_extension && !binary_upgrade_record_init_privs)
         return;

-    recordExtensionInitPrivWorker(objoid, classoid, objsubid, new_acl);
+    recordExtensionInitPrivWorker(objoid, classoid, objsubid, ownerId, new_acl);
 }

 /*
@@ -4664,14 +4708,23 @@ recordExtensionInitPriv(Oid objoid, Oid classoid, int objsubid, Acl *new_acl)
  * EXTENSION ... ADD/DROP.
  */
 static void
-recordExtensionInitPrivWorker(Oid objoid, Oid classoid, int objsubid, Acl *new_acl)
+recordExtensionInitPrivWorker(Oid objoid, Oid classoid, int objsubid,
+                              Oid ownerId, Acl *new_acl)
 {
     Relation    relation;
     ScanKeyData key[3];
     SysScanDesc scan;
     HeapTuple    tuple;
     HeapTuple    oldtuple;
+    int            noldmembers;
+    int            nnewmembers;
+    Oid           *oldmembers;
+    Oid           *newmembers;

+    /* We'll need the role membership of the new ACL. */
+    nnewmembers = aclmembers(new_acl, &newmembers);
+
+    /* Search pg_init_privs for an existing entry. */
     relation = table_open(InitPrivsRelationId, RowExclusiveLock);

     ScanKeyInit(&key[0],
@@ -4699,6 +4752,23 @@ recordExtensionInitPrivWorker(Oid objoid, Oid classoid, int objsubid, Acl *new_a
         Datum        values[Natts_pg_init_privs] = {0};
         bool        nulls[Natts_pg_init_privs] = {0};
         bool        replace[Natts_pg_init_privs] = {0};
+        Datum        oldAclDatum;
+        bool        isNull;
+        Acl           *old_acl;
+
+        /* Update pg_shdepend for roles mentioned in the old/new ACLs. */
+        oldAclDatum = heap_getattr(oldtuple, Anum_pg_init_privs_initprivs,
+                                   RelationGetDescr(relation), &isNull);
+        if (!isNull)
+            old_acl = DatumGetAclP(oldAclDatum);
+        else
+            old_acl = NULL;        /* this case shouldn't happen, probably */
+        noldmembers = aclmembers(old_acl, &oldmembers);
+
+        updateInitAclDependencies(classoid, objoid, objsubid,
+                                  ownerId,
+                                  noldmembers, oldmembers,
+                                  nnewmembers, newmembers);

         /* If we have a new ACL to set, then update the row with it. */
         if (new_acl)
@@ -4744,6 +4814,15 @@ recordExtensionInitPrivWorker(Oid objoid, Oid classoid, int objsubid, Acl *new_a
             tuple = heap_form_tuple(RelationGetDescr(relation), values, nulls);

             CatalogTupleInsert(relation, tuple);
+
+            /* Update pg_shdepend, too. */
+            noldmembers = 0;
+            oldmembers = NULL;
+
+            updateInitAclDependencies(classoid, objoid, objsubid,
+                                      ownerId,
+                                      noldmembers, oldmembers,
+                                      nnewmembers, newmembers);
         }
     }

@@ -4754,3 +4833,138 @@ recordExtensionInitPrivWorker(Oid objoid, Oid classoid, int objsubid, Acl *new_a

     table_close(relation, RowExclusiveLock);
 }
+
+/*
+ * RemoveRoleFromInitPriv
+ *
+ * Used by shdepDropOwned to remove mentions of a role in pg_init_privs.
+ */
+void
+RemoveRoleFromInitPriv(Oid roleid, Oid classid, Oid objid, int32 objsubid)
+{
+    Relation    rel;
+    ScanKeyData key[3];
+    SysScanDesc scan;
+    HeapTuple    oldtuple;
+    int            cacheid;
+    HeapTuple    objtuple;
+    Oid            ownerId;
+    Datum        oldAclDatum;
+    bool        isNull;
+    Acl           *old_acl;
+    Acl           *new_acl;
+    HeapTuple    newtuple;
+    int            noldmembers;
+    int            nnewmembers;
+    Oid           *oldmembers;
+    Oid           *newmembers;
+
+    /* Search for existing pg_init_privs entry for the target object. */
+    rel = table_open(InitPrivsRelationId, RowExclusiveLock);
+
+    ScanKeyInit(&key[0],
+                Anum_pg_init_privs_objoid,
+                BTEqualStrategyNumber, F_OIDEQ,
+                ObjectIdGetDatum(objid));
+    ScanKeyInit(&key[1],
+                Anum_pg_init_privs_classoid,
+                BTEqualStrategyNumber, F_OIDEQ,
+                ObjectIdGetDatum(classid));
+    ScanKeyInit(&key[2],
+                Anum_pg_init_privs_objsubid,
+                BTEqualStrategyNumber, F_INT4EQ,
+                Int32GetDatum(objsubid));
+
+    scan = systable_beginscan(rel, InitPrivsObjIndexId, true,
+                              NULL, 3, key);
+
+    /* There should exist only one entry or none. */
+    oldtuple = systable_getnext(scan);
+
+    if (!HeapTupleIsValid(oldtuple))
+    {
+        /*
+         * Hmm, why are we here if there's no entry?  But pack up and go away
+         * quietly.
+         */
+        systable_endscan(scan);
+        table_close(rel, RowExclusiveLock);
+        return;
+    }
+
+    /* Get a writable copy of the existing ACL. */
+    oldAclDatum = heap_getattr(oldtuple, Anum_pg_init_privs_initprivs,
+                               RelationGetDescr(rel), &isNull);
+    if (!isNull)
+        old_acl = DatumGetAclPCopy(oldAclDatum);
+    else
+        old_acl = NULL;            /* this case shouldn't happen, probably */
+
+    /*
+     * We need the members of both old and new ACLs so we can correct the
+     * shared dependency information.  Collect data before
+     * merge_acl_with_grant throws away old_acl.
+     */
+    noldmembers = aclmembers(old_acl, &oldmembers);
+
+    /* Must find out the owner's OID the hard way. */
+    cacheid = get_object_catcache_oid(classid);
+    objtuple = SearchSysCache1(cacheid, ObjectIdGetDatum(objid));
+    if (!HeapTupleIsValid(objtuple))
+        elog(ERROR, "cache lookup failed for %s %u",
+             get_object_class_descr(classid), objid);
+
+    ownerId = DatumGetObjectId(SysCacheGetAttrNotNull(cacheid,
+                                                      objtuple,
+                                                      get_object_attnum_owner(classid)));
+    ReleaseSysCache(objtuple);
+
+    /*
+     * Generate new ACL.  Grantor of rights is always the same as the owner.
+     */
+    new_acl = merge_acl_with_grant(old_acl,
+                                   false,    /* is_grant */
+                                   false,    /* grant_option */
+                                   DROP_RESTRICT,
+                                   list_make1_oid(roleid),
+                                   ACLITEM_ALL_PRIV_BITS,
+                                   ownerId,
+                                   ownerId);
+
+    /* If we end with an empty ACL, delete the pg_init_privs entry. */
+    if (new_acl == NULL || ACL_NUM(new_acl) == 0)
+    {
+        CatalogTupleDelete(rel, &oldtuple->t_self);
+    }
+    else
+    {
+        Datum        values[Natts_pg_init_privs] = {0};
+        bool        nulls[Natts_pg_init_privs] = {0};
+        bool        replaces[Natts_pg_init_privs] = {0};
+
+        /* Update existing entry. */
+        values[Anum_pg_init_privs_initprivs - 1] = PointerGetDatum(new_acl);
+        replaces[Anum_pg_init_privs_initprivs - 1] = true;
+
+        newtuple = heap_modify_tuple(oldtuple, RelationGetDescr(rel),
+                                     values, nulls, replaces);
+        CatalogTupleUpdate(rel, &newtuple->t_self, newtuple);
+    }
+
+    /*
+     * Update the shared dependency ACL info.
+     */
+    nnewmembers = aclmembers(new_acl, &newmembers);
+
+    updateInitAclDependencies(classid, objid, objsubid,
+                              ownerId,
+                              noldmembers, oldmembers,
+                              nnewmembers, newmembers);
+
+    systable_endscan(scan);
+
+    /* prevent error when processing objects multiple times */
+    CommandCounterIncrement();
+
+    table_close(rel, RowExclusiveLock);
+}
diff --git a/src/backend/catalog/pg_shdepend.c b/src/backend/catalog/pg_shdepend.c
index cb31590339..20bcfd779b 100644
--- a/src/backend/catalog/pg_shdepend.c
+++ b/src/backend/catalog/pg_shdepend.c
@@ -84,6 +84,11 @@ static void shdepChangeDep(Relation sdepRel,
                            Oid classid, Oid objid, int32 objsubid,
                            Oid refclassid, Oid refobjid,
                            SharedDependencyType deptype);
+static void updateAclDependenciesWorker(Oid classId, Oid objectId,
+                                        int32 objsubId, Oid ownerId,
+                                        SharedDependencyType deptype,
+                                        int noldmembers, Oid *oldmembers,
+                                        int nnewmembers, Oid *newmembers);
 static void shdepAddDependency(Relation sdepRel,
                                Oid classId, Oid objectId, int32 objsubId,
                                Oid refclassId, Oid refobjId,
@@ -340,6 +345,11 @@ changeDependencyOnOwner(Oid classId, Oid objectId, Oid newOwnerId)
                         AuthIdRelationId, newOwnerId,
                         SHARED_DEPENDENCY_ACL);

+    /* The same applies to SHARED_DEPENDENCY_INITACL */
+    shdepDropDependency(sdepRel, classId, objectId, 0, true,
+                        AuthIdRelationId, newOwnerId,
+                        SHARED_DEPENDENCY_INITACL);
+
     table_close(sdepRel, RowExclusiveLock);
 }

@@ -478,6 +488,38 @@ updateAclDependencies(Oid classId, Oid objectId, int32 objsubId,
                       Oid ownerId,
                       int noldmembers, Oid *oldmembers,
                       int nnewmembers, Oid *newmembers)
+{
+    updateAclDependenciesWorker(classId, objectId, objsubId,
+                                ownerId, SHARED_DEPENDENCY_ACL,
+                                noldmembers, oldmembers,
+                                nnewmembers, newmembers);
+}
+
+/*
+ * updateInitAclDependencies
+ *        Update the pg_shdepend info for a pg_init_privs entry.
+ *
+ * Exactly like updateAclDependencies, except we are considering a
+ * pg_init_privs ACL for the specified object.
+ */
+void
+updateInitAclDependencies(Oid classId, Oid objectId, int32 objsubId,
+                          Oid ownerId,
+                          int noldmembers, Oid *oldmembers,
+                          int nnewmembers, Oid *newmembers)
+{
+    updateAclDependenciesWorker(classId, objectId, objsubId,
+                                ownerId, SHARED_DEPENDENCY_INITACL,
+                                noldmembers, oldmembers,
+                                nnewmembers, newmembers);
+}
+
+/* Common code for the above two functions */
+static void
+updateAclDependenciesWorker(Oid classId, Oid objectId, int32 objsubId,
+                            Oid ownerId, SharedDependencyType deptype,
+                            int noldmembers, Oid *oldmembers,
+                            int nnewmembers, Oid *newmembers)
 {
     Relation    sdepRel;
     int            i;
@@ -513,7 +555,7 @@ updateAclDependencies(Oid classId, Oid objectId, int32 objsubId,

             shdepAddDependency(sdepRel, classId, objectId, objsubId,
                                AuthIdRelationId, roleid,
-                               SHARED_DEPENDENCY_ACL);
+                               deptype);
         }

         /* Drop no-longer-used old dependencies */
@@ -532,7 +574,7 @@ updateAclDependencies(Oid classId, Oid objectId, int32 objsubId,
             shdepDropDependency(sdepRel, classId, objectId, objsubId,
                                 false,    /* exact match on objsubId */
                                 AuthIdRelationId, roleid,
-                                SHARED_DEPENDENCY_ACL);
+                                deptype);
         }

         table_close(sdepRel, RowExclusiveLock);
@@ -1249,6 +1291,8 @@ storeObjectDescription(StringInfo descs,
                 appendStringInfo(descs, _("owner of %s"), objdesc);
             else if (deptype == SHARED_DEPENDENCY_ACL)
                 appendStringInfo(descs, _("privileges for %s"), objdesc);
+            else if (deptype == SHARED_DEPENDENCY_INITACL)
+                appendStringInfo(descs, _("initial privileges for %s"), objdesc);
             else if (deptype == SHARED_DEPENDENCY_POLICY)
                 appendStringInfo(descs, _("target of %s"), objdesc);
             else if (deptype == SHARED_DEPENDENCY_TABLESPACE)
@@ -1431,6 +1475,14 @@ shdepDropOwned(List *roleids, DropBehavior behavior)
                         add_exact_object_address(&obj, deleteobjs);
                     }
                     break;
+                case SHARED_DEPENDENCY_INITACL:
+                    /* Shouldn't see a role grant here */
+                    Assert(sdepForm->classid != AuthMemRelationId);
+                    RemoveRoleFromInitPriv(roleid,
+                                           sdepForm->classid,
+                                           sdepForm->objid,
+                                           sdepForm->objsubid);
+                    break;
             }
         }

diff --git a/src/include/catalog/dependency.h b/src/include/catalog/dependency.h
index ec654010d4..e0dcc0b069 100644
--- a/src/include/catalog/dependency.h
+++ b/src/include/catalog/dependency.h
@@ -56,11 +56,17 @@ typedef enum DependencyType
  * created for the owner of an object; hence two objects may be linked by
  * one or the other, but not both, of these dependency types.)
  *
- * (c) a SHARED_DEPENDENCY_POLICY entry means that the referenced object is
+ * (c) a SHARED_DEPENDENCY_INITACL entry means that the referenced object is
+ * a role mentioned in a pg_init_privs entry for the dependent object.  The
+ * referenced object must be a pg_authid entry.  (SHARED_DEPENDENCY_INITACL
+ * entries are not created for the owner of an object; hence two objects may
+ * be linked by one or the other, but not both, of these dependency types.)
+ *
+ * (d) a SHARED_DEPENDENCY_POLICY entry means that the referenced object is
  * a role mentioned in a policy object.  The referenced object must be a
  * pg_authid entry.
  *
- * (d) a SHARED_DEPENDENCY_TABLESPACE entry means that the referenced
+ * (e) a SHARED_DEPENDENCY_TABLESPACE entry means that the referenced
  * object is a tablespace mentioned in a relation without storage.  The
  * referenced object must be a pg_tablespace entry.  (Relations that have
  * storage don't need this: they are protected by the existence of a physical
@@ -73,6 +79,7 @@ typedef enum SharedDependencyType
 {
     SHARED_DEPENDENCY_OWNER = 'o',
     SHARED_DEPENDENCY_ACL = 'a',
+    SHARED_DEPENDENCY_INITACL = 'i',
     SHARED_DEPENDENCY_POLICY = 'r',
     SHARED_DEPENDENCY_TABLESPACE = 't',
     SHARED_DEPENDENCY_INVALID = 0,
@@ -201,6 +208,11 @@ extern void updateAclDependencies(Oid classId, Oid objectId, int32 objsubId,
                                   int noldmembers, Oid *oldmembers,
                                   int nnewmembers, Oid *newmembers);

+extern void updateInitAclDependencies(Oid classId, Oid objectId, int32 objsubId,
+                                      Oid ownerId,
+                                      int noldmembers, Oid *oldmembers,
+                                      int nnewmembers, Oid *newmembers);
+
 extern bool checkSharedDependencies(Oid classId, Oid objectId,
                                     char **detail_msg, char **detail_log_msg);

diff --git a/src/include/utils/acl.h b/src/include/utils/acl.h
index 3a0baf3039..1a554c6699 100644
--- a/src/include/utils/acl.h
+++ b/src/include/utils/acl.h
@@ -276,6 +276,8 @@ extern void aclcheck_error_type(AclResult aclerr, Oid typeOid);

 extern void recordExtObjInitPriv(Oid objoid, Oid classoid);
 extern void removeExtObjInitPriv(Oid objoid, Oid classoid);
+extern void RemoveRoleFromInitPriv(Oid roleid,
+                                   Oid classid, Oid objid, int32 objsubid);


 /* ownercheck routines just return true (owner) or false (not) */

pgsql-hackers by date:

Previous
From: "Imseih (AWS), Sami"
Date:
Subject: Re: query_id, pg_stat_activity, extended query protocol
Next
From: jian he
Date:
Subject: Re: documentation structure