diff --git a/src/backend/catalog/pg_shdepend.c b/src/backend/catalog/pg_shdepend.c index 1edf950c56..7474800e7e 100644 --- a/src/backend/catalog/pg_shdepend.c +++ b/src/backend/catalog/pg_shdepend.c @@ -25,6 +25,7 @@ #include "catalog/pg_conversion.h" #include "catalog/pg_database.h" #include "catalog/pg_default_acl.h" +#include "catalog/pg_extension.h" #include "catalog/pg_foreign_data_wrapper.h" #include "catalog/pg_foreign_server.h" #include "catalog/pg_language.h" @@ -1392,6 +1393,10 @@ shdepReassignOwned(List *roleids, Oid newrole) AlterForeignDataWrapperOwner_oid(sdepForm->objid, newrole); break; + case ExtensionRelationId: + AlterExtensionOwner_oid(sdepForm->objid, newrole); + break; + default: elog(ERROR, "unexpected classid %u", sdepForm->classid); break; diff --git a/src/backend/commands/extension.c b/src/backend/commands/extension.c index cde3d60ee8..5d6bc7a118 100644 --- a/src/backend/commands/extension.c +++ b/src/backend/commands/extension.c @@ -2724,3 +2724,95 @@ ExecAlterExtensionContentsStmt(AlterExtensionContentsStmt *stmt) if (relation != NULL) relation_close(relation, NoLock); } + +/* + * AlterExtensionOwner_internal + * + * Internal routine for changing the owner of an extension. rel must be + * pg_extension, already open and suitably locked; it will not be closed. + * + * Note that this only changes ownership of the extension itself; it doesn't + * change the ownership of objects it contains. Since this function is + * currently only called from REASSIGN OWNED, this restriction is okay because + * said objects would also be affected by our caller. But it's not enough for + * a full-fledged ALTER OWNER implementation, so beware. + */ +static void +AlterExtensionOwner_internal(Relation rel, Oid extensionOid, Oid newOwnerId) +{ + Form_pg_extension extForm; + HeapTuple tup; + SysScanDesc scandesc; + ScanKeyData entry[1]; + + Assert(RelationGetRelid(rel) == ExtensionRelationId); + + ScanKeyInit(&entry[0], + ObjectIdAttributeNumber, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(extensionOid)); + + scandesc = systable_beginscan(rel, ExtensionOidIndexId, true, + SnapshotNow, 1, entry); + + /* We assume that there can be at most one matching tuple */ + tup = systable_getnext(scandesc); + if (!HeapTupleIsValid(tup)) /* should not happen */ + elog(ERROR, "cache lookup failed for extension %u", extensionOid); + + tup = heap_copytuple(tup); + systable_endscan(scandesc); + + extForm = (Form_pg_extension) GETSTRUCT(tup); + + /* + * If the new owner is the same as the existing owner, consider the + * command to have succeeded. This is for dump restoration purposes. + */ + if (extForm->extowner != newOwnerId) + { + /* Superusers can always do it */ + if (!superuser()) + { + /* Otherwise, must be owner of the existing object */ + if (!pg_extension_ownercheck(HeapTupleGetOid(tup), GetUserId())) + aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_EXTENSION, + NameStr(extForm->extname)); + + /* Must be able to become new owner */ + check_is_member_of_role(GetUserId(), newOwnerId); + + /* no privilege checks on namespace are required */ + } + + /* + * Modify the owner --- okay to scribble on tup because it's a copy + */ + extForm->extowner = newOwnerId; + + simple_heap_update(rel, &tup->t_self, tup); + + CatalogUpdateIndexes(rel, tup); + + /* Update owner dependency reference */ + changeDependencyOnOwner(ExtensionRelationId, extensionOid, + newOwnerId); + } + + heap_freetuple(tup); +} + +/* + * Change extension owner, by OID + */ +void +AlterExtensionOwner_oid(Oid extensionOid, Oid newOwnerId) +{ + Relation rel; + + rel = heap_open(ExtensionRelationId, RowExclusiveLock); + + AlterExtensionOwner_internal(rel, extensionOid, newOwnerId); + + heap_close(rel, NoLock); +} diff --git a/src/include/commands/extension.h b/src/include/commands/extension.h index 7fc8a927d3..f0847ff3fb 100644 --- a/src/include/commands/extension.h +++ b/src/include/commands/extension.h @@ -45,4 +45,6 @@ extern char *get_extension_name(Oid ext_oid); extern void AlterExtensionNamespace(List *names, const char *newschema); +extern void AlterExtensionOwner_oid(Oid extensionOid, Oid newOwnerId); + #endif /* EXTENSION_H */