Improve tracking of role dependencies of pg_init_privs entries.

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
This commit is contained in:
Tom Lane 2024-06-17 12:55:10 -04:00
parent 653d3969bb
commit 35dd40d34c
9 changed files with 666 additions and 111 deletions

View 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>

View 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);

View 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);
case SHARED_DEPENDENCY_OWNER:
shdepReassignOwned_Owner(sdepForm, newrole);
break;
case NamespaceRelationId:
AlterSchemaOwner_oid(sdepForm->objid, newrole);
case SHARED_DEPENDENCY_INITACL:
shdepReassignOwned_InitAcl(sdepForm, roleid, 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);
case SHARED_DEPENDENCY_ACL:
case SHARED_DEPENDENCY_POLICY:
case SHARED_DEPENDENCY_TABLESPACE:
/* Nothing to do for these entry types */
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);
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);
}

View 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)

View file

@ -57,6 +57,6 @@
*/
/* yyyymmddN */
#define CATALOG_VERSION_NO 202406141
#define CATALOG_VERSION_NO 202406171
#endif

View 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);

View 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);

View 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;

View 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;