diff --git a/doc/src/sgml/catalogs.sgml b/doc/src/sgml/catalogs.sgml index 2f52e70b48..a63cc71efa 100644 --- a/doc/src/sgml/catalogs.sgml +++ b/doc/src/sgml/catalogs.sgml @@ -7182,9 +7182,6 @@ SCRAM-SHA-256$<iteration count>:&l The referenced object (which must be a role) is mentioned in a pg_init_privs entry for the dependent object. - (A SHARED_DEPENDENCY_INITACL entry is not made for - the owner of the object, since the owner will have - a SHARED_DEPENDENCY_OWNER entry anyway.) diff --git a/src/backend/catalog/aclchk.c b/src/backend/catalog/aclchk.c index 143876b77f..fe90102e6e 100644 --- a/src/backend/catalog/aclchk.c +++ b/src/backend/catalog/aclchk.c @@ -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); diff --git a/src/backend/catalog/pg_shdepend.c b/src/backend/catalog/pg_shdepend.c index 20bcfd779b..753afb8845 100644 --- a/src/backend/catalog/pg_shdepend.c +++ b/src/backend/catalog/pg_shdepend.c @@ -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); +} diff --git a/src/backend/utils/adt/acl.c b/src/backend/utils/adt/acl.c index dc10b4a483..d7b39140b3 100644 --- a/src/backend/utils/adt/acl.c +++ b/src/backend/utils/adt/acl.c @@ -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) diff --git a/src/include/catalog/catversion.h b/src/include/catalog/catversion.h index 542213db38..b3322e8d67 100644 --- a/src/include/catalog/catversion.h +++ b/src/include/catalog/catversion.h @@ -57,6 +57,6 @@ */ /* yyyymmddN */ -#define CATALOG_VERSION_NO 202406141 +#define CATALOG_VERSION_NO 202406171 #endif diff --git a/src/include/catalog/dependency.h b/src/include/catalog/dependency.h index 7eee66f810..6908ca7180 100644 --- a/src/include/catalog/dependency.h +++ b/src/include/catalog/dependency.h @@ -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); diff --git a/src/include/utils/acl.h b/src/include/utils/acl.h index 1a554c6699..731d84b2a9 100644 --- a/src/include/utils/acl.h +++ b/src/include/utils/acl.h @@ -276,6 +276,8 @@ extern void aclcheck_error_type(AclResult aclerr, Oid typeOid); extern void recordExtObjInitPriv(Oid objoid, Oid classoid); extern void removeExtObjInitPriv(Oid objoid, Oid classoid); +extern void ReplaceRoleInInitPriv(Oid oldroleid, Oid newroleid, + Oid classid, Oid objid, int32 objsubid); extern void RemoveRoleFromInitPriv(Oid roleid, Oid classid, Oid objid, int32 objsubid); diff --git a/src/test/modules/test_pg_dump/expected/test_pg_dump.out b/src/test/modules/test_pg_dump/expected/test_pg_dump.out index 3cd7db97aa..3536d636d8 100644 --- a/src/test/modules/test_pg_dump/expected/test_pg_dump.out +++ b/src/test/modules/test_pg_dump/expected/test_pg_dump.out @@ -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; diff --git a/src/test/modules/test_pg_dump/sql/test_pg_dump.sql b/src/test/modules/test_pg_dump/sql/test_pg_dump.sql index 2110ac7163..87e66cae6e 100644 --- a/src/test/modules/test_pg_dump/sql/test_pg_dump.sql +++ b/src/test/modules/test_pg_dump/sql/test_pg_dump.sql @@ -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;