Improve tracking of role dependencies of pg_init_privs entries.
authorTom Lane <[email protected]>
Mon, 17 Jun 2024 16:55:10 +0000 (12:55 -0400)
committerTom Lane <[email protected]>
Mon, 17 Jun 2024 16:55:10 +0000 (12:55 -0400)
Commit 534287403 invented SHARED_DEPENDENCY_INITACL entries in
pg_shdepend, but installed them only for non-owner roles mentioned
in a pg_init_privs entry.  This turns out to be the wrong thing,
because there is nothing to cue REASSIGN OWNED to go and update
pg_init_privs entries when the object's ownership is reassigned.
That leads to leaving dangling entries in pg_init_privs, as
reported by Hannu Krosing.  Instead, install INITACL entries for
all roles mentioned in pg_init_privs entries (except pinned roles),
and change ALTER OWNER to not touch them, just as it doesn't
touch pg_init_privs entries.

REASSIGN OWNED will now substitute the new owner OID for the old
in pg_init_privs entries.  This feels like perhaps not quite the
right thing, since pg_init_privs ought to be a historical record
of the state of affairs just after CREATE EXTENSION.  However,
it's hard to see what else to do, if we don't want to disallow
dropping the object's original owner.  In any case this is
better than the previous do-nothing behavior, and we're unlikely
to come up with a superior solution in time for v17.

While here, tighten up some coding rules about how ACLs in
pg_init_privs should never be null or empty.  There's not any
obvious reason to allow that, and perhaps asserting that it's
not so will catch some bugs.  (We were previously inconsistent
on the point, with some code paths taking care not to store
empty ACLs and others not.)

This leaves recordExtensionInitPrivWorker not doing anything
with its ownerId argument, but we'll deal with that separately.

catversion bump forced because of change of expected contents
of pg_shdepend when pg_init_privs entries exist.

Discussion: https://postgr.es/m/CAMT0RQSVgv48G5GArUvOVhottWqZLrvC5wBzBa4HrUdXe9VRXw@mail.gmail.com

doc/src/sgml/catalogs.sgml
src/backend/catalog/aclchk.c
src/backend/catalog/pg_shdepend.c
src/backend/utils/adt/acl.c
src/include/catalog/catversion.h
src/include/catalog/dependency.h
src/include/utils/acl.h
src/test/modules/test_pg_dump/expected/test_pg_dump.out
src/test/modules/test_pg_dump/sql/test_pg_dump.sql

index 2f52e70b48e8d5ee89d08afcdc984cc98bad73ae..a63cc71efa2f1f36a8ec787c3c86ee12239582a2 100644 (file)
@@ -7182,9 +7182,6 @@ SCRAM-SHA-256$<replaceable>&lt;iteration count&gt;</replaceable>:<replaceable>&l
        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>
index 143876b77ff8e0f5ea6302db1d358ed38e50c7bb..fe90102e6eb36e9ff5040ee3e4e948d633fbf0e8 100644 (file)
@@ -4771,19 +4771,16 @@ recordExtensionInitPrivWorker(Oid objoid, Oid classoid, int objsubid,
        /* 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 */
+       Assert(!isNull);
+       old_acl = DatumGetAclP(oldAclDatum);
        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)
+       if (new_acl && ACL_NUM(new_acl) != 0)
        {
            values[Anum_pg_init_privs_initprivs - 1] = PointerGetDatum(new_acl);
            replace[Anum_pg_init_privs_initprivs - 1] = true;
@@ -4795,7 +4792,7 @@ recordExtensionInitPrivWorker(Oid objoid, Oid classoid, int objsubid,
        }
        else
        {
-           /* new_acl is NULL, so delete the entry we found. */
+           /* new_acl is NULL/empty, so delete the entry we found. */
            CatalogTupleDelete(relation, &oldtuple->t_self);
        }
    }
@@ -4810,7 +4807,7 @@ recordExtensionInitPrivWorker(Oid objoid, Oid classoid, int objsubid,
         * If we are passed in a NULL ACL and no entry exists, we can just
         * fall through and do nothing.
         */
-       if (new_acl)
+       if (new_acl && ACL_NUM(new_acl) != 0)
        {
            /* No entry found, so add it. */
            values[Anum_pg_init_privs_objoid - 1] = ObjectIdGetDatum(objoid);
@@ -4832,7 +4829,6 @@ recordExtensionInitPrivWorker(Oid objoid, Oid classoid, int objsubid,
            oldmembers = NULL;
 
            updateInitAclDependencies(classoid, objoid, objsubid,
-                                     ownerId,
                                      noldmembers, oldmembers,
                                      nnewmembers, newmembers);
        }
@@ -4846,6 +4842,115 @@ recordExtensionInitPrivWorker(Oid objoid, Oid classoid, int objsubid,
    table_close(relation, RowExclusiveLock);
 }
 
+/*
+ * ReplaceRoleInInitPriv
+ *
+ * Used by shdepReassignOwned to replace mentions of a role in pg_init_privs.
+ */
+void
+ReplaceRoleInInitPriv(Oid oldroleid, Oid newroleid,
+                     Oid classid, Oid objid, int32 objsubid)
+{
+   Relation    rel;
+   ScanKeyData key[3];
+   SysScanDesc scan;
+   HeapTuple   oldtuple;
+   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);
+   Assert(!isNull);
+   old_acl = DatumGetAclPCopy(oldAclDatum);
+
+   /*
+    * Generate new ACL.  This usage of aclnewowner is a bit off-label when
+    * oldroleid isn't the owner; but it does the job fine.
+    */
+   new_acl = aclnewowner(old_acl, oldroleid, newroleid);
+
+   /*
+    * If we end with an empty ACL, delete the pg_init_privs entry.  (That
+    * probably can't happen here, but we may as well cover the case.)
+    */
+   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.
+    */
+   noldmembers = aclmembers(old_acl, &oldmembers);
+   nnewmembers = aclmembers(new_acl, &newmembers);
+
+   updateInitAclDependencies(classid, objid, objsubid,
+                             noldmembers, oldmembers,
+                             nnewmembers, newmembers);
+
+   systable_endscan(scan);
+
+   /* prevent error when processing objects multiple times */
+   CommandCounterIncrement();
+
+   table_close(rel, RowExclusiveLock);
+}
+
 /*
  * RemoveRoleFromInitPriv
  *
@@ -4907,10 +5012,8 @@ RemoveRoleFromInitPriv(Oid roleid, Oid classid, Oid objid, int32 objsubid)
    /* 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 */
+   Assert(!isNull);
+   old_acl = DatumGetAclPCopy(oldAclDatum);
 
    /*
     * We need the members of both old and new ACLs so we can correct the
@@ -4972,7 +5075,6 @@ RemoveRoleFromInitPriv(Oid roleid, Oid classid, Oid objid, int32 objsubid)
    nnewmembers = aclmembers(new_acl, &newmembers);
 
    updateInitAclDependencies(classid, objid, objsubid,
-                             ownerId,
                              noldmembers, oldmembers,
                              nnewmembers, newmembers);
 
index 20bcfd779b969dbc42748f49b7ebac26730c747d..753afb88453dc8ac6ced9df52b8eba5190b23132 100644 (file)
@@ -103,6 +103,9 @@ static void storeObjectDescription(StringInfo descs,
                                   ObjectAddress *object,
                                   SharedDependencyType deptype,
                                   int count);
+static void shdepReassignOwned_Owner(Form_pg_shdepend sdepForm, Oid newrole);
+static void shdepReassignOwned_InitAcl(Form_pg_shdepend sdepForm,
+                                      Oid oldrole, Oid newrole);
 
 
 /*
@@ -345,10 +348,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);
+   /*
+    * However, nothing need be done about SHARED_DEPENDENCY_INITACL entries,
+    * since those exist whether or not the role is the object's owner, and
+    * ALTER OWNER does not modify the underlying pg_init_privs entry.
+    */
 
    table_close(sdepRel, RowExclusiveLock);
 }
@@ -500,16 +504,18 @@ updateAclDependencies(Oid classId, Oid objectId, int32 objsubId,
  *     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.
+ * pg_init_privs ACL for the specified object.  Since recording of
+ * pg_init_privs role dependencies is the same for owners and non-owners,
+ * we do not need an ownerId argument.
  */
 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,
+                               InvalidOid, /* ownerId will not be consulted */
+                               SHARED_DEPENDENCY_INITACL,
                                noldmembers, oldmembers,
                                nnewmembers, newmembers);
 }
@@ -542,11 +548,13 @@ updateAclDependenciesWorker(Oid classId, Oid objectId, int32 objsubId,
            Oid         roleid = newmembers[i];
 
            /*
-            * Skip the owner: he has an OWNER shdep entry instead. (This is
-            * not just a space optimization; it makes ALTER OWNER easier. See
-            * notes in changeDependencyOnOwner.)
+            * For SHARED_DEPENDENCY_ACL entries, skip the owner: she has an
+            * OWNER shdep entry instead.  (This is not just a space
+            * optimization; it makes ALTER OWNER easier.  See notes in
+            * changeDependencyOnOwner.)  But for INITACL entries, we record
+            * the owner too.
             */
-           if (roleid == ownerId)
+           if (deptype == SHARED_DEPENDENCY_ACL && roleid == ownerId)
                continue;
 
            /* Skip pinned roles; they don't need dependency entries */
@@ -563,8 +571,8 @@ updateAclDependenciesWorker(Oid classId, Oid objectId, int32 objsubId,
        {
            Oid         roleid = oldmembers[i];
 
-           /* Skip the owner, same as above */
-           if (roleid == ownerId)
+           /* Skip the owner for ACL entries, same as above */
+           if (deptype == SHARED_DEPENDENCY_ACL && roleid == ownerId)
                continue;
 
            /* Skip pinned roles */
@@ -1476,6 +1484,13 @@ shdepDropOwned(List *roleids, DropBehavior behavior)
                    }
                    break;
                case SHARED_DEPENDENCY_INITACL:
+
+                   /*
+                    * Any mentions of the role that remain in pg_init_privs
+                    * entries are just dropped.  This is the same policy as
+                    * we apply to regular ACLs.
+                    */
+
                    /* Shouldn't see a role grant here */
                    Assert(sdepForm->classid != AuthMemRelationId);
                    RemoveRoleFromInitPriv(roleid,
@@ -1577,12 +1592,8 @@ shdepReassignOwned(List *roleids, Oid newrole)
                sdepForm->dbid != InvalidOid)
                continue;
 
-           /* We leave non-owner dependencies alone */
-           if (sdepForm->deptype != SHARED_DEPENDENCY_OWNER)
-               continue;
-
            /*
-            * The various ALTER OWNER routines tend to leak memory in
+            * The various DDL routines called here tend to leak memory in
             * CurrentMemoryContext.  That's not a problem when they're only
             * called once per command; but in this usage where we might be
             * touching many objects, it can amount to a serious memory leak.
@@ -1593,81 +1604,23 @@ shdepReassignOwned(List *roleids, Oid newrole)
                                        ALLOCSET_DEFAULT_SIZES);
            oldcxt = MemoryContextSwitchTo(cxt);
 
-           /* Issue the appropriate ALTER OWNER call */
-           switch (sdepForm->classid)
+           /* Perform the appropriate processing */
+           switch (sdepForm->deptype)
            {
-               case TypeRelationId:
-                   AlterTypeOwner_oid(sdepForm->objid, newrole, true);
-                   break;
-
-               case NamespaceRelationId:
-                   AlterSchemaOwner_oid(sdepForm->objid, newrole);
-                   break;
-
-               case RelationRelationId:
-
-                   /*
-                    * Pass recursing = true so that we don't fail on indexes,
-                    * owned sequences, etc when we happen to visit them
-                    * before their parent table.
-                    */
-                   ATExecChangeOwner(sdepForm->objid, newrole, true, AccessExclusiveLock);
-                   break;
-
-               case DefaultAclRelationId:
-
-                   /*
-                    * Ignore default ACLs; they should be handled by DROP
-                    * OWNED, not REASSIGN OWNED.
-                    */
-                   break;
-
-               case UserMappingRelationId:
-                   /* ditto */
-                   break;
-
-               case ForeignServerRelationId:
-                   AlterForeignServerOwner_oid(sdepForm->objid, newrole);
-                   break;
-
-               case ForeignDataWrapperRelationId:
-                   AlterForeignDataWrapperOwner_oid(sdepForm->objid, newrole);
-                   break;
-
-               case EventTriggerRelationId:
-                   AlterEventTriggerOwner_oid(sdepForm->objid, newrole);
-                   break;
-
-               case PublicationRelationId:
-                   AlterPublicationOwner_oid(sdepForm->objid, newrole);
+               case SHARED_DEPENDENCY_OWNER:
+                   shdepReassignOwned_Owner(sdepForm, newrole);
                    break;
-
-               case SubscriptionRelationId:
-                   AlterSubscriptionOwner_oid(sdepForm->objid, newrole);
+               case SHARED_DEPENDENCY_INITACL:
+                   shdepReassignOwned_InitAcl(sdepForm, roleid, newrole);
                    break;
-
-                   /* Generic alter owner cases */
-               case CollationRelationId:
-               case ConversionRelationId:
-               case OperatorRelationId:
-               case ProcedureRelationId:
-               case LanguageRelationId:
-               case LargeObjectRelationId:
-               case OperatorFamilyRelationId:
-               case OperatorClassRelationId:
-               case ExtensionRelationId:
-               case StatisticExtRelationId:
-               case TableSpaceRelationId:
-               case DatabaseRelationId:
-               case TSConfigRelationId:
-               case TSDictionaryRelationId:
-                   AlterObjectOwner_internal(sdepForm->classid,
-                                             sdepForm->objid,
-                                             newrole);
+               case SHARED_DEPENDENCY_ACL:
+               case SHARED_DEPENDENCY_POLICY:
+               case SHARED_DEPENDENCY_TABLESPACE:
+                   /* Nothing to do for these entry types */
                    break;
-
                default:
-                   elog(ERROR, "unexpected classid %u", sdepForm->classid);
+                   elog(ERROR, "unrecognized dependency type: %d",
+                        (int) sdepForm->deptype);
                    break;
            }
 
@@ -1684,3 +1637,123 @@ shdepReassignOwned(List *roleids, Oid newrole)
 
    table_close(sdepRel, RowExclusiveLock);
 }
+
+/*
+ * shdepReassignOwned_Owner
+ *
+ * shdepReassignOwned's processing of SHARED_DEPENDENCY_OWNER entries
+ */
+static void
+shdepReassignOwned_Owner(Form_pg_shdepend sdepForm, Oid newrole)
+{
+   /* Issue the appropriate ALTER OWNER call */
+   switch (sdepForm->classid)
+   {
+       case TypeRelationId:
+           AlterTypeOwner_oid(sdepForm->objid, newrole, true);
+           break;
+
+       case NamespaceRelationId:
+           AlterSchemaOwner_oid(sdepForm->objid, newrole);
+           break;
+
+       case RelationRelationId:
+
+           /*
+            * Pass recursing = true so that we don't fail on indexes, owned
+            * sequences, etc when we happen to visit them before their parent
+            * table.
+            */
+           ATExecChangeOwner(sdepForm->objid, newrole, true, AccessExclusiveLock);
+           break;
+
+       case DefaultAclRelationId:
+
+           /*
+            * Ignore default ACLs; they should be handled by DROP OWNED, not
+            * REASSIGN OWNED.
+            */
+           break;
+
+       case UserMappingRelationId:
+           /* ditto */
+           break;
+
+       case ForeignServerRelationId:
+           AlterForeignServerOwner_oid(sdepForm->objid, newrole);
+           break;
+
+       case ForeignDataWrapperRelationId:
+           AlterForeignDataWrapperOwner_oid(sdepForm->objid, newrole);
+           break;
+
+       case EventTriggerRelationId:
+           AlterEventTriggerOwner_oid(sdepForm->objid, newrole);
+           break;
+
+       case PublicationRelationId:
+           AlterPublicationOwner_oid(sdepForm->objid, newrole);
+           break;
+
+       case SubscriptionRelationId:
+           AlterSubscriptionOwner_oid(sdepForm->objid, newrole);
+           break;
+
+           /* Generic alter owner cases */
+       case CollationRelationId:
+       case ConversionRelationId:
+       case OperatorRelationId:
+       case ProcedureRelationId:
+       case LanguageRelationId:
+       case LargeObjectRelationId:
+       case OperatorFamilyRelationId:
+       case OperatorClassRelationId:
+       case ExtensionRelationId:
+       case StatisticExtRelationId:
+       case TableSpaceRelationId:
+       case DatabaseRelationId:
+       case TSConfigRelationId:
+       case TSDictionaryRelationId:
+           AlterObjectOwner_internal(sdepForm->classid,
+                                     sdepForm->objid,
+                                     newrole);
+           break;
+
+       default:
+           elog(ERROR, "unexpected classid %u", sdepForm->classid);
+           break;
+   }
+}
+
+/*
+ * shdepReassignOwned_InitAcl
+ *
+ * shdepReassignOwned's processing of SHARED_DEPENDENCY_INITACL entries
+ */
+static void
+shdepReassignOwned_InitAcl(Form_pg_shdepend sdepForm, Oid oldrole, Oid newrole)
+{
+   /*
+    * Currently, REASSIGN OWNED replaces mentions of oldrole with newrole in
+    * pg_init_privs entries, just as it does in the object's regular ACL.
+    * This is less than ideal, since pg_init_privs ought to retain a
+    * historical record of the situation at the end of CREATE EXTENSION.
+    * However, there are two big stumbling blocks to doing something
+    * different:
+    *
+    * 1. If we don't replace the references, what is to happen if the old
+    * role gets dropped?  (DROP OWNED's current answer is to just delete the
+    * pg_init_privs entry, which is surely ahistorical.)
+    *
+    * 2. It's unlikely that pg_dump will cope nicely with pg_init_privs
+    * entries that are based on a different owner than the object now has ---
+    * the more so given that pg_init_privs doesn't record the original owner
+    * explicitly.  (This problem actually exists anyway given that a bare
+    * ALTER OWNER won't update pg_init_privs, but we don't need REASSIGN
+    * OWNED making it worse.)
+    */
+   ReplaceRoleInInitPriv(oldrole, newrole,
+                         sdepForm->classid,
+                         sdepForm->objid,
+                         sdepForm->objsubid);
+}
index dc10b4a4839b8c4991e3123531b7073c1b15bdb0..d7b39140b3d2210273a1e74d6ca69acac4e6a38e 100644 (file)
@@ -1091,6 +1091,12 @@ aclupdate(const Acl *old_acl, const AclItem *mod_aip,
  * The result is a modified copy; the input object is not changed.
  *
  * NB: caller is responsible for having detoasted the input ACL, if needed.
+ *
+ * Note: the name of this function is a bit of a misnomer, since it will
+ * happily make the specified role substitution whether the old role is
+ * really the owner of the parent object or merely mentioned in its ACL.
+ * But the vast majority of callers use it in connection with ALTER OWNER
+ * operations, so we'll keep the name.
  */
 Acl *
 aclnewowner(const Acl *old_acl, Oid oldOwnerId, Oid newOwnerId)
index 542213db380f0493e735b2f9b02d3052101d229a..b3322e8d67551dd515891bbbf419e9bf576da236 100644 (file)
@@ -57,6 +57,6 @@
  */
 
 /*                         yyyymmddN */
-#define CATALOG_VERSION_NO 202406141
+#define CATALOG_VERSION_NO 202406171
 
 #endif
index 7eee66f8106a15599af7ab5b9c1b9f3ea0bec5a8..6908ca7180ae120dc43d87a95a89ff689890fbe3 100644 (file)
@@ -57,10 +57,10 @@ typedef enum DependencyType
  * one or the other, but not both, of these dependency types.)
  *
  * (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.)
+ * a role mentioned in a pg_init_privs entry for the dependent object.
+ * The referenced object must be a pg_authid entry.  (Unlike the case for
+ * SHARED_DEPENDENCY_ACL, we make an entry for such a role whether or not
+ * it is the object's owner.)
  *
  * (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
@@ -209,7 +209,6 @@ extern void updateAclDependencies(Oid classId, Oid objectId, int32 objsubId,
                                  int nnewmembers, Oid *newmembers);
 
 extern void updateInitAclDependencies(Oid classId, Oid objectId, int32 objsubId,
-                                     Oid ownerId,
                                      int noldmembers, Oid *oldmembers,
                                      int nnewmembers, Oid *newmembers);
 
index 1a554c669972118522616af8b1ff5fa71e3b61af..731d84b2a939c3b79226576258c436c991d77b0a 100644 (file)
@@ -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 ReplaceRoleInInitPriv(Oid oldroleid, Oid newroleid,
+                                 Oid classid, Oid objid, int32 objsubid);
 extern void RemoveRoleFromInitPriv(Oid roleid,
                                   Oid classid, Oid objid, int32 objsubid);
 
index 3cd7db97aa47e9bd7f17588681817e91e503d50b..3536d636d8ea2da756946559f07a74ae7790f995 100644 (file)
@@ -283,3 +283,308 @@ SELECT * FROM pg_init_privs WHERE privtype = 'e';
 --------+----------+----------+----------+-----------
 (0 rows)
 
+CREATE ROLE regress_dump_test_role;
+CREATE ROLE regress_dump_test_super SUPERUSER;
+SET ROLE regress_dump_test_super;
+CREATE EXTENSION test_pg_dump;
+RESET ROLE;
+-- Substitute for current user's name to keep test output consistent
+SELECT s.obj,
+  CASE WHEN a.grantor::regrole::name = quote_ident(current_user) THEN 'postgres'
+    ELSE a.grantor::regrole::name END,
+  CASE WHEN a.grantee::regrole::name = quote_ident(current_user) THEN 'postgres'
+    ELSE a.grantee::regrole::name END,
+  a.privilege_type, a.is_grantable
+FROM
+  (SELECT pg_describe_object(classoid,objoid,objsubid) COLLATE "C" AS obj, initprivs
+   FROM pg_init_privs WHERE privtype = 'e' ORDER BY 1) s,
+  aclexplode(s.initprivs) a;
+                        obj                         |         grantor         |         grantee         | privilege_type | is_grantable 
+----------------------------------------------------+-------------------------+-------------------------+----------------+--------------
+ column col1 of table regress_pg_dump_table         | regress_dump_test_super | -                       | SELECT         | f
+ function regress_pg_dump_schema.test_agg(smallint) | regress_dump_test_super | -                       | EXECUTE        | f
+ function regress_pg_dump_schema.test_agg(smallint) | regress_dump_test_super | regress_dump_test_super | EXECUTE        | f
+ function regress_pg_dump_schema.test_agg(smallint) | regress_dump_test_super | regress_dump_test_role  | EXECUTE        | f
+ function regress_pg_dump_schema.test_func()        | regress_dump_test_super | -                       | EXECUTE        | f
+ function regress_pg_dump_schema.test_func()        | regress_dump_test_super | regress_dump_test_super | EXECUTE        | f
+ function regress_pg_dump_schema.test_func()        | regress_dump_test_super | regress_dump_test_role  | EXECUTE        | f
+ function wgo_then_no_access()                      | regress_dump_test_super | -                       | EXECUTE        | f
+ function wgo_then_no_access()                      | regress_dump_test_super | regress_dump_test_super | EXECUTE        | f
+ function wgo_then_no_access()                      | regress_dump_test_super | pg_signal_backend       | EXECUTE        | t
+ sequence regress_pg_dump_schema.test_seq           | regress_dump_test_super | regress_dump_test_super | SELECT         | f
+ sequence regress_pg_dump_schema.test_seq           | regress_dump_test_super | regress_dump_test_super | UPDATE         | f
+ sequence regress_pg_dump_schema.test_seq           | regress_dump_test_super | regress_dump_test_super | USAGE          | f
+ sequence regress_pg_dump_schema.test_seq           | regress_dump_test_super | regress_dump_test_role  | USAGE          | f
+ sequence regress_pg_dump_seq                       | regress_dump_test_super | regress_dump_test_super | SELECT         | f
+ sequence regress_pg_dump_seq                       | regress_dump_test_super | regress_dump_test_super | UPDATE         | f
+ sequence regress_pg_dump_seq                       | regress_dump_test_super | regress_dump_test_super | USAGE          | f
+ sequence regress_pg_dump_seq                       | regress_dump_test_super | regress_dump_test_role  | USAGE          | f
+ sequence regress_seq_dumpable                      | regress_dump_test_super | regress_dump_test_super | SELECT         | f
+ sequence regress_seq_dumpable                      | regress_dump_test_super | regress_dump_test_super | UPDATE         | f
+ sequence regress_seq_dumpable                      | regress_dump_test_super | regress_dump_test_super | USAGE          | f
+ sequence regress_seq_dumpable                      | regress_dump_test_super | -                       | SELECT         | f
+ sequence wgo_then_regular                          | regress_dump_test_super | regress_dump_test_super | SELECT         | f
+ sequence wgo_then_regular                          | regress_dump_test_super | regress_dump_test_super | UPDATE         | f
+ sequence wgo_then_regular                          | regress_dump_test_super | regress_dump_test_super | USAGE          | f
+ sequence wgo_then_regular                          | regress_dump_test_super | pg_signal_backend       | SELECT         | f
+ sequence wgo_then_regular                          | regress_dump_test_super | pg_signal_backend       | UPDATE         | t
+ sequence wgo_then_regular                          | regress_dump_test_super | pg_signal_backend       | USAGE          | t
+ table regress_pg_dump_schema.test_table            | regress_dump_test_super | regress_dump_test_super | INSERT         | f
+ table regress_pg_dump_schema.test_table            | regress_dump_test_super | regress_dump_test_super | SELECT         | f
+ table regress_pg_dump_schema.test_table            | regress_dump_test_super | regress_dump_test_super | UPDATE         | f
+ table regress_pg_dump_schema.test_table            | regress_dump_test_super | regress_dump_test_super | DELETE         | f
+ table regress_pg_dump_schema.test_table            | regress_dump_test_super | regress_dump_test_super | TRUNCATE       | f
+ table regress_pg_dump_schema.test_table            | regress_dump_test_super | regress_dump_test_super | REFERENCES     | f
+ table regress_pg_dump_schema.test_table            | regress_dump_test_super | regress_dump_test_super | TRIGGER        | f
+ table regress_pg_dump_schema.test_table            | regress_dump_test_super | regress_dump_test_super | MAINTAIN       | f
+ table regress_pg_dump_schema.test_table            | regress_dump_test_super | regress_dump_test_role  | SELECT         | f
+ table regress_pg_dump_table                        | regress_dump_test_super | regress_dump_test_super | INSERT         | f
+ table regress_pg_dump_table                        | regress_dump_test_super | regress_dump_test_super | SELECT         | f
+ table regress_pg_dump_table                        | regress_dump_test_super | regress_dump_test_super | UPDATE         | f
+ table regress_pg_dump_table                        | regress_dump_test_super | regress_dump_test_super | DELETE         | f
+ table regress_pg_dump_table                        | regress_dump_test_super | regress_dump_test_super | TRUNCATE       | f
+ table regress_pg_dump_table                        | regress_dump_test_super | regress_dump_test_super | REFERENCES     | f
+ table regress_pg_dump_table                        | regress_dump_test_super | regress_dump_test_super | TRIGGER        | f
+ table regress_pg_dump_table                        | regress_dump_test_super | regress_dump_test_super | MAINTAIN       | f
+ table regress_pg_dump_table                        | regress_dump_test_super | regress_dump_test_role  | SELECT         | f
+ table regress_table_dumpable                       | regress_dump_test_super | regress_dump_test_super | INSERT         | f
+ table regress_table_dumpable                       | regress_dump_test_super | regress_dump_test_super | SELECT         | f
+ table regress_table_dumpable                       | regress_dump_test_super | regress_dump_test_super | UPDATE         | f
+ table regress_table_dumpable                       | regress_dump_test_super | regress_dump_test_super | DELETE         | f
+ table regress_table_dumpable                       | regress_dump_test_super | regress_dump_test_super | TRUNCATE       | f
+ table regress_table_dumpable                       | regress_dump_test_super | regress_dump_test_super | REFERENCES     | f
+ table regress_table_dumpable                       | regress_dump_test_super | regress_dump_test_super | TRIGGER        | f
+ table regress_table_dumpable                       | regress_dump_test_super | regress_dump_test_super | MAINTAIN       | f
+ table regress_table_dumpable                       | regress_dump_test_super | -                       | SELECT         | f
+ type regress_pg_dump_schema.test_type              | regress_dump_test_super | -                       | USAGE          | f
+ type regress_pg_dump_schema.test_type              | regress_dump_test_super | regress_dump_test_super | USAGE          | f
+ type regress_pg_dump_schema.test_type              | regress_dump_test_super | regress_dump_test_role  | USAGE          | f
+(58 rows)
+
+SELECT pg_describe_object(classid,objid,objsubid) COLLATE "C" AS obj,
+  pg_describe_object(refclassid,refobjid,0) AS refobj,
+  deptype
+  FROM pg_shdepend JOIN pg_database d ON dbid = d.oid
+  WHERE d.datname = current_database()
+  ORDER BY 1, 2, 3;
+                        obj                         |            refobj            | deptype 
+----------------------------------------------------+------------------------------+---------
+ column col1 of table regress_pg_dump_table         | role regress_dump_test_super | i
+ extension test_pg_dump                             | role regress_dump_test_super | o
+ function regress_pg_dump_schema.test_agg(smallint) | role regress_dump_test_role  | a
+ function regress_pg_dump_schema.test_agg(smallint) | role regress_dump_test_role  | i
+ function regress_pg_dump_schema.test_agg(smallint) | role regress_dump_test_super | i
+ function regress_pg_dump_schema.test_agg(smallint) | role regress_dump_test_super | o
+ function regress_pg_dump_schema.test_func()        | role regress_dump_test_role  | a
+ function regress_pg_dump_schema.test_func()        | role regress_dump_test_role  | i
+ function regress_pg_dump_schema.test_func()        | role regress_dump_test_super | i
+ function regress_pg_dump_schema.test_func()        | role regress_dump_test_super | o
+ function wgo_then_no_access()                      | role regress_dump_test_super | i
+ function wgo_then_no_access()                      | role regress_dump_test_super | o
+ schema regress_pg_dump_schema                      | role regress_dump_test_super | o
+ sequence regress_pg_dump_schema.test_seq           | role regress_dump_test_role  | a
+ sequence regress_pg_dump_schema.test_seq           | role regress_dump_test_role  | i
+ sequence regress_pg_dump_schema.test_seq           | role regress_dump_test_super | i
+ sequence regress_pg_dump_schema.test_seq           | role regress_dump_test_super | o
+ sequence regress_pg_dump_seq                       | role regress_dump_test_role  | a
+ sequence regress_pg_dump_seq                       | role regress_dump_test_role  | i
+ sequence regress_pg_dump_seq                       | role regress_dump_test_super | i
+ sequence regress_pg_dump_seq                       | role regress_dump_test_super | o
+ sequence regress_pg_dump_table_col1_seq            | role regress_dump_test_super | o
+ sequence regress_seq_dumpable                      | role regress_dump_test_super | i
+ sequence regress_seq_dumpable                      | role regress_dump_test_super | o
+ sequence wgo_then_regular                          | role regress_dump_test_super | i
+ sequence wgo_then_regular                          | role regress_dump_test_super | o
+ table regress_pg_dump_schema.test_table            | role regress_dump_test_role  | a
+ table regress_pg_dump_schema.test_table            | role regress_dump_test_role  | i
+ table regress_pg_dump_schema.test_table            | role regress_dump_test_super | i
+ table regress_pg_dump_schema.test_table            | role regress_dump_test_super | o
+ table regress_pg_dump_table                        | role regress_dump_test_role  | a
+ table regress_pg_dump_table                        | role regress_dump_test_role  | i
+ table regress_pg_dump_table                        | role regress_dump_test_super | i
+ table regress_pg_dump_table                        | role regress_dump_test_super | o
+ table regress_table_dumpable                       | role regress_dump_test_super | i
+ table regress_table_dumpable                       | role regress_dump_test_super | o
+ type regress_pg_dump_schema.test_type              | role regress_dump_test_role  | a
+ type regress_pg_dump_schema.test_type              | role regress_dump_test_role  | i
+ type regress_pg_dump_schema.test_type              | role regress_dump_test_super | i
+ type regress_pg_dump_schema.test_type              | role regress_dump_test_super | o
+(40 rows)
+
+REASSIGN OWNED BY regress_dump_test_super TO CURRENT_ROLE;
+-- Substitute for current user's name to keep test output consistent
+SELECT s.obj,
+  CASE WHEN a.grantor::regrole::name = quote_ident(current_user) THEN 'postgres'
+    ELSE a.grantor::regrole::name END,
+  CASE WHEN a.grantee::regrole::name = quote_ident(current_user) THEN 'postgres'
+    ELSE a.grantee::regrole::name END,
+  a.privilege_type, a.is_grantable
+FROM
+  (SELECT pg_describe_object(classoid,objoid,objsubid) COLLATE "C" AS obj, initprivs
+   FROM pg_init_privs WHERE privtype = 'e' ORDER BY 1) s,
+  aclexplode(s.initprivs) a;
+                        obj                         | grantor  |        grantee         | privilege_type | is_grantable 
+----------------------------------------------------+----------+------------------------+----------------+--------------
+ column col1 of table regress_pg_dump_table         | postgres | -                      | SELECT         | f
+ function regress_pg_dump_schema.test_agg(smallint) | postgres | -                      | EXECUTE        | f
+ function regress_pg_dump_schema.test_agg(smallint) | postgres | postgres               | EXECUTE        | f
+ function regress_pg_dump_schema.test_agg(smallint) | postgres | regress_dump_test_role | EXECUTE        | f
+ function regress_pg_dump_schema.test_func()        | postgres | -                      | EXECUTE        | f
+ function regress_pg_dump_schema.test_func()        | postgres | postgres               | EXECUTE        | f
+ function regress_pg_dump_schema.test_func()        | postgres | regress_dump_test_role | EXECUTE        | f
+ function wgo_then_no_access()                      | postgres | -                      | EXECUTE        | f
+ function wgo_then_no_access()                      | postgres | postgres               | EXECUTE        | f
+ function wgo_then_no_access()                      | postgres | pg_signal_backend      | EXECUTE        | t
+ sequence regress_pg_dump_schema.test_seq           | postgres | postgres               | SELECT         | f
+ sequence regress_pg_dump_schema.test_seq           | postgres | postgres               | UPDATE         | f
+ sequence regress_pg_dump_schema.test_seq           | postgres | postgres               | USAGE          | f
+ sequence regress_pg_dump_schema.test_seq           | postgres | regress_dump_test_role | USAGE          | f
+ sequence regress_pg_dump_seq                       | postgres | postgres               | SELECT         | f
+ sequence regress_pg_dump_seq                       | postgres | postgres               | UPDATE         | f
+ sequence regress_pg_dump_seq                       | postgres | postgres               | USAGE          | f
+ sequence regress_pg_dump_seq                       | postgres | regress_dump_test_role | USAGE          | f
+ sequence regress_seq_dumpable                      | postgres | postgres               | SELECT         | f
+ sequence regress_seq_dumpable                      | postgres | postgres               | UPDATE         | f
+ sequence regress_seq_dumpable                      | postgres | postgres               | USAGE          | f
+ sequence regress_seq_dumpable                      | postgres | -                      | SELECT         | f
+ sequence wgo_then_regular                          | postgres | postgres               | SELECT         | f
+ sequence wgo_then_regular                          | postgres | postgres               | UPDATE         | f
+ sequence wgo_then_regular                          | postgres | postgres               | USAGE          | f
+ sequence wgo_then_regular                          | postgres | pg_signal_backend      | SELECT         | f
+ sequence wgo_then_regular                          | postgres | pg_signal_backend      | UPDATE         | t
+ sequence wgo_then_regular                          | postgres | pg_signal_backend      | USAGE          | t
+ table regress_pg_dump_schema.test_table            | postgres | postgres               | INSERT         | f
+ table regress_pg_dump_schema.test_table            | postgres | postgres               | SELECT         | f
+ table regress_pg_dump_schema.test_table            | postgres | postgres               | UPDATE         | f
+ table regress_pg_dump_schema.test_table            | postgres | postgres               | DELETE         | f
+ table regress_pg_dump_schema.test_table            | postgres | postgres               | TRUNCATE       | f
+ table regress_pg_dump_schema.test_table            | postgres | postgres               | REFERENCES     | f
+ table regress_pg_dump_schema.test_table            | postgres | postgres               | TRIGGER        | f
+ table regress_pg_dump_schema.test_table            | postgres | postgres               | MAINTAIN       | f
+ table regress_pg_dump_schema.test_table            | postgres | regress_dump_test_role | SELECT         | f
+ table regress_pg_dump_table                        | postgres | postgres               | INSERT         | f
+ table regress_pg_dump_table                        | postgres | postgres               | SELECT         | f
+ table regress_pg_dump_table                        | postgres | postgres               | UPDATE         | f
+ table regress_pg_dump_table                        | postgres | postgres               | DELETE         | f
+ table regress_pg_dump_table                        | postgres | postgres               | TRUNCATE       | f
+ table regress_pg_dump_table                        | postgres | postgres               | REFERENCES     | f
+ table regress_pg_dump_table                        | postgres | postgres               | TRIGGER        | f
+ table regress_pg_dump_table                        | postgres | postgres               | MAINTAIN       | f
+ table regress_pg_dump_table                        | postgres | regress_dump_test_role | SELECT         | f
+ table regress_table_dumpable                       | postgres | postgres               | INSERT         | f
+ table regress_table_dumpable                       | postgres | postgres               | SELECT         | f
+ table regress_table_dumpable                       | postgres | postgres               | UPDATE         | f
+ table regress_table_dumpable                       | postgres | postgres               | DELETE         | f
+ table regress_table_dumpable                       | postgres | postgres               | TRUNCATE       | f
+ table regress_table_dumpable                       | postgres | postgres               | REFERENCES     | f
+ table regress_table_dumpable                       | postgres | postgres               | TRIGGER        | f
+ table regress_table_dumpable                       | postgres | postgres               | MAINTAIN       | f
+ table regress_table_dumpable                       | postgres | -                      | SELECT         | f
+ type regress_pg_dump_schema.test_type              | postgres | -                      | USAGE          | f
+ type regress_pg_dump_schema.test_type              | postgres | postgres               | USAGE          | f
+ type regress_pg_dump_schema.test_type              | postgres | regress_dump_test_role | USAGE          | f
+(58 rows)
+
+SELECT pg_describe_object(classid,objid,objsubid) COLLATE "C" AS obj,
+  pg_describe_object(refclassid,refobjid,0) AS refobj,
+  deptype
+  FROM pg_shdepend JOIN pg_database d ON dbid = d.oid
+  WHERE d.datname = current_database()
+  ORDER BY 1, 2, 3;
+                        obj                         |           refobj            | deptype 
+----------------------------------------------------+-----------------------------+---------
+ function regress_pg_dump_schema.test_agg(smallint) | role regress_dump_test_role | a
+ function regress_pg_dump_schema.test_agg(smallint) | role regress_dump_test_role | i
+ function regress_pg_dump_schema.test_func()        | role regress_dump_test_role | a
+ function regress_pg_dump_schema.test_func()        | role regress_dump_test_role | i
+ sequence regress_pg_dump_schema.test_seq           | role regress_dump_test_role | a
+ sequence regress_pg_dump_schema.test_seq           | role regress_dump_test_role | i
+ sequence regress_pg_dump_seq                       | role regress_dump_test_role | a
+ sequence regress_pg_dump_seq                       | role regress_dump_test_role | i
+ table regress_pg_dump_schema.test_table            | role regress_dump_test_role | a
+ table regress_pg_dump_schema.test_table            | role regress_dump_test_role | i
+ table regress_pg_dump_table                        | role regress_dump_test_role | a
+ table regress_pg_dump_table                        | role regress_dump_test_role | i
+ type regress_pg_dump_schema.test_type              | role regress_dump_test_role | a
+ type regress_pg_dump_schema.test_type              | role regress_dump_test_role | i
+(14 rows)
+
+DROP OWNED BY regress_dump_test_role RESTRICT;
+-- Substitute for current user's name to keep test output consistent
+SELECT s.obj,
+  CASE WHEN a.grantor::regrole::name = quote_ident(current_user) THEN 'postgres'
+    ELSE a.grantor::regrole::name END,
+  CASE WHEN a.grantee::regrole::name = quote_ident(current_user) THEN 'postgres'
+    ELSE a.grantee::regrole::name END,
+  a.privilege_type, a.is_grantable
+FROM
+  (SELECT pg_describe_object(classoid,objoid,objsubid) COLLATE "C" AS obj, initprivs
+   FROM pg_init_privs WHERE privtype = 'e' ORDER BY 1) s,
+  aclexplode(s.initprivs) a;
+                        obj                         | grantor  |      grantee      | privilege_type | is_grantable 
+----------------------------------------------------+----------+-------------------+----------------+--------------
+ column col1 of table regress_pg_dump_table         | postgres | -                 | SELECT         | f
+ function regress_pg_dump_schema.test_agg(smallint) | postgres | -                 | EXECUTE        | f
+ function regress_pg_dump_schema.test_agg(smallint) | postgres | postgres          | EXECUTE        | f
+ function regress_pg_dump_schema.test_func()        | postgres | -                 | EXECUTE        | f
+ function regress_pg_dump_schema.test_func()        | postgres | postgres          | EXECUTE        | f
+ function wgo_then_no_access()                      | postgres | -                 | EXECUTE        | f
+ function wgo_then_no_access()                      | postgres | postgres          | EXECUTE        | f
+ function wgo_then_no_access()                      | postgres | pg_signal_backend | EXECUTE        | t
+ sequence regress_pg_dump_schema.test_seq           | postgres | postgres          | SELECT         | f
+ sequence regress_pg_dump_schema.test_seq           | postgres | postgres          | UPDATE         | f
+ sequence regress_pg_dump_schema.test_seq           | postgres | postgres          | USAGE          | f
+ sequence regress_pg_dump_seq                       | postgres | postgres          | SELECT         | f
+ sequence regress_pg_dump_seq                       | postgres | postgres          | UPDATE         | f
+ sequence regress_pg_dump_seq                       | postgres | postgres          | USAGE          | f
+ sequence regress_seq_dumpable                      | postgres | postgres          | SELECT         | f
+ sequence regress_seq_dumpable                      | postgres | postgres          | UPDATE         | f
+ sequence regress_seq_dumpable                      | postgres | postgres          | USAGE          | f
+ sequence regress_seq_dumpable                      | postgres | -                 | SELECT         | f
+ sequence wgo_then_regular                          | postgres | postgres          | SELECT         | f
+ sequence wgo_then_regular                          | postgres | postgres          | UPDATE         | f
+ sequence wgo_then_regular                          | postgres | postgres          | USAGE          | f
+ sequence wgo_then_regular                          | postgres | pg_signal_backend | SELECT         | f
+ sequence wgo_then_regular                          | postgres | pg_signal_backend | UPDATE         | t
+ sequence wgo_then_regular                          | postgres | pg_signal_backend | USAGE          | t
+ table regress_pg_dump_schema.test_table            | postgres | postgres          | INSERT         | f
+ table regress_pg_dump_schema.test_table            | postgres | postgres          | SELECT         | f
+ table regress_pg_dump_schema.test_table            | postgres | postgres          | UPDATE         | f
+ table regress_pg_dump_schema.test_table            | postgres | postgres          | DELETE         | f
+ table regress_pg_dump_schema.test_table            | postgres | postgres          | TRUNCATE       | f
+ table regress_pg_dump_schema.test_table            | postgres | postgres          | REFERENCES     | f
+ table regress_pg_dump_schema.test_table            | postgres | postgres          | TRIGGER        | f
+ table regress_pg_dump_schema.test_table            | postgres | postgres          | MAINTAIN       | f
+ table regress_pg_dump_table                        | postgres | postgres          | INSERT         | f
+ table regress_pg_dump_table                        | postgres | postgres          | SELECT         | f
+ table regress_pg_dump_table                        | postgres | postgres          | UPDATE         | f
+ table regress_pg_dump_table                        | postgres | postgres          | DELETE         | f
+ table regress_pg_dump_table                        | postgres | postgres          | TRUNCATE       | f
+ table regress_pg_dump_table                        | postgres | postgres          | REFERENCES     | f
+ table regress_pg_dump_table                        | postgres | postgres          | TRIGGER        | f
+ table regress_pg_dump_table                        | postgres | postgres          | MAINTAIN       | f
+ table regress_table_dumpable                       | postgres | postgres          | INSERT         | f
+ table regress_table_dumpable                       | postgres | postgres          | SELECT         | f
+ table regress_table_dumpable                       | postgres | postgres          | UPDATE         | f
+ table regress_table_dumpable                       | postgres | postgres          | DELETE         | f
+ table regress_table_dumpable                       | postgres | postgres          | TRUNCATE       | f
+ table regress_table_dumpable                       | postgres | postgres          | REFERENCES     | f
+ table regress_table_dumpable                       | postgres | postgres          | TRIGGER        | f
+ table regress_table_dumpable                       | postgres | postgres          | MAINTAIN       | f
+ table regress_table_dumpable                       | postgres | -                 | SELECT         | f
+ type regress_pg_dump_schema.test_type              | postgres | -                 | USAGE          | f
+ type regress_pg_dump_schema.test_type              | postgres | postgres          | USAGE          | f
+(51 rows)
+
+SELECT pg_describe_object(classid,objid,objsubid) COLLATE "C" AS obj,
+  pg_describe_object(refclassid,refobjid,0) AS refobj,
+  deptype
+  FROM pg_shdepend JOIN pg_database d ON dbid = d.oid
+  WHERE d.datname = current_database()
+  ORDER BY 1, 2, 3;
+ obj | refobj | deptype 
+-----+--------+---------
+(0 rows)
+
+DROP ROLE regress_dump_test_super;
+DROP ROLE regress_dump_test_role;
index 2110ac71633c332622be7115998d527c140844d6..87e66cae6e3c1f2a880ad56d8e3e1a9539c1b3dd 100644 (file)
@@ -151,3 +151,74 @@ DROP EXTENSION test_pg_dump;
 
 -- shouldn't be anything left in pg_init_privs
 SELECT * FROM pg_init_privs WHERE privtype = 'e';
+
+CREATE ROLE regress_dump_test_role;
+CREATE ROLE regress_dump_test_super SUPERUSER;
+
+SET ROLE regress_dump_test_super;
+
+CREATE EXTENSION test_pg_dump;
+
+RESET ROLE;
+
+-- Substitute for current user's name to keep test output consistent
+SELECT s.obj,
+  CASE WHEN a.grantor::regrole::name = quote_ident(current_user) THEN 'postgres'
+    ELSE a.grantor::regrole::name END,
+  CASE WHEN a.grantee::regrole::name = quote_ident(current_user) THEN 'postgres'
+    ELSE a.grantee::regrole::name END,
+  a.privilege_type, a.is_grantable
+FROM
+  (SELECT pg_describe_object(classoid,objoid,objsubid) COLLATE "C" AS obj, initprivs
+   FROM pg_init_privs WHERE privtype = 'e' ORDER BY 1) s,
+  aclexplode(s.initprivs) a;
+SELECT pg_describe_object(classid,objid,objsubid) COLLATE "C" AS obj,
+  pg_describe_object(refclassid,refobjid,0) AS refobj,
+  deptype
+  FROM pg_shdepend JOIN pg_database d ON dbid = d.oid
+  WHERE d.datname = current_database()
+  ORDER BY 1, 2, 3;
+
+REASSIGN OWNED BY regress_dump_test_super TO CURRENT_ROLE;
+
+-- Substitute for current user's name to keep test output consistent
+SELECT s.obj,
+  CASE WHEN a.grantor::regrole::name = quote_ident(current_user) THEN 'postgres'
+    ELSE a.grantor::regrole::name END,
+  CASE WHEN a.grantee::regrole::name = quote_ident(current_user) THEN 'postgres'
+    ELSE a.grantee::regrole::name END,
+  a.privilege_type, a.is_grantable
+FROM
+  (SELECT pg_describe_object(classoid,objoid,objsubid) COLLATE "C" AS obj, initprivs
+   FROM pg_init_privs WHERE privtype = 'e' ORDER BY 1) s,
+  aclexplode(s.initprivs) a;
+SELECT pg_describe_object(classid,objid,objsubid) COLLATE "C" AS obj,
+  pg_describe_object(refclassid,refobjid,0) AS refobj,
+  deptype
+  FROM pg_shdepend JOIN pg_database d ON dbid = d.oid
+  WHERE d.datname = current_database()
+  ORDER BY 1, 2, 3;
+
+DROP OWNED BY regress_dump_test_role RESTRICT;
+
+-- Substitute for current user's name to keep test output consistent
+SELECT s.obj,
+  CASE WHEN a.grantor::regrole::name = quote_ident(current_user) THEN 'postgres'
+    ELSE a.grantor::regrole::name END,
+  CASE WHEN a.grantee::regrole::name = quote_ident(current_user) THEN 'postgres'
+    ELSE a.grantee::regrole::name END,
+  a.privilege_type, a.is_grantable
+FROM
+  (SELECT pg_describe_object(classoid,objoid,objsubid) COLLATE "C" AS obj, initprivs
+   FROM pg_init_privs WHERE privtype = 'e' ORDER BY 1) s,
+  aclexplode(s.initprivs) a;
+SELECT pg_describe_object(classid,objid,objsubid) COLLATE "C" AS obj,
+  pg_describe_object(refclassid,refobjid,0) AS refobj,
+  deptype
+  FROM pg_shdepend JOIN pg_database d ON dbid = d.oid
+  WHERE d.datname = current_database()
+  ORDER BY 1, 2, 3;
+
+DROP ROLE regress_dump_test_super;
+
+DROP ROLE regress_dump_test_role;