diff --git a/src/backend/catalog/pg_shdepend.c b/src/backend/catalog/pg_shdepend.c index 4e052d3cb0d97cef73ff8eb1fd2b189f005785a6..0c61d7bf632e6966b655c79f915b189a6b660c26 100644 --- a/src/backend/catalog/pg_shdepend.c +++ b/src/backend/catalog/pg_shdepend.c @@ -1349,7 +1349,7 @@ shdepReassignOwned(List *roleids, Oid newrole) break; case TypeRelationId: - AlterTypeOwnerInternal(sdepForm->objid, newrole, true); + AlterTypeOwner_oid(sdepForm->objid, newrole, true); break; case OperatorRelationId: diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 36b252340a606aafbdc35545c9126b97513739be..0c89d81707ba32eaa29b32503ac0051af8f4a8d8 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -8310,8 +8310,7 @@ ATExecChangeOwner(Oid relationOid, Oid newOwnerId, bool recursing, LOCKMODE lock * Also change the ownership of the table's row type, if it has one */ if (tuple_class->relkind != RELKIND_INDEX) - AlterTypeOwnerInternal(tuple_class->reltype, newOwnerId, - tuple_class->relkind == RELKIND_COMPOSITE_TYPE); + AlterTypeOwnerInternal(tuple_class->reltype, newOwnerId); /* * If we are operating on a table, also change the ownership of any diff --git a/src/backend/commands/typecmds.c b/src/backend/commands/typecmds.c index c6ec67dcbd43bb50a93d805fd419e76f5447550f..b3b3c6bdf07f4ee07ccc559a4cdde96ac125dbc0 100644 --- a/src/backend/commands/typecmds.c +++ b/src/backend/commands/typecmds.c @@ -3254,77 +3254,64 @@ AlterTypeOwner(List *names, Oid newOwnerId, ObjectType objecttype) get_namespace_name(typTup->typnamespace)); } - /* - * If it's a composite type, invoke ATExecChangeOwner so that we fix - * up the pg_class entry properly. That will call back to - * AlterTypeOwnerInternal to take care of the pg_type entry(s). - */ - if (typTup->typtype == TYPTYPE_COMPOSITE) - ATExecChangeOwner(typTup->typrelid, newOwnerId, true, AccessExclusiveLock); - else - { - Datum repl_val[Natts_pg_type]; - bool repl_null[Natts_pg_type]; - bool repl_repl[Natts_pg_type]; - Acl *newAcl; - Datum aclDatum; - bool isNull; - - memset(repl_null, false, sizeof(repl_null)); - memset(repl_repl, false, sizeof(repl_repl)); - - repl_repl[Anum_pg_type_typowner - 1] = true; - repl_val[Anum_pg_type_typowner - 1] = ObjectIdGetDatum(newOwnerId); + AlterTypeOwner_oid(typeOid, newOwnerId, true); + } - aclDatum = heap_getattr(tup, - Anum_pg_type_typacl, - RelationGetDescr(rel), - &isNull); - /* Null ACLs do not require changes */ - if (!isNull) - { - newAcl = aclnewowner(DatumGetAclP(aclDatum), - typTup->typowner, newOwnerId); - repl_repl[Anum_pg_type_typacl - 1] = true; - repl_val[Anum_pg_type_typacl - 1] = PointerGetDatum(newAcl); - } + /* Clean up */ + heap_close(rel, RowExclusiveLock); +} - tup = heap_modify_tuple(tup, RelationGetDescr(rel), repl_val, repl_null, - repl_repl); +/* + * AlterTypeOwner_oid - change type owner unconditionally + * + * This function recurses to handle a pg_class entry, if necessary. It + * invokes any necessary access object hooks. If hasDependEntry is TRUE, this + * function modifies the pg_shdepend entry appropriately (this should be + * passed as FALSE only for table rowtypes and array types). + * + * This is used by ALTER TABLE/TYPE OWNER commands, as well as by REASSIGN + * OWNED BY. It assumes the caller has done all needed check. + */ +void +AlterTypeOwner_oid(Oid typeOid, Oid newOwnerId, bool hasDependEntry) +{ + Relation rel; + HeapTuple tup; + Form_pg_type typTup; - simple_heap_update(rel, &tup->t_self, tup); + rel = heap_open(TypeRelationId, RowExclusiveLock); - CatalogUpdateIndexes(rel, tup); + tup = SearchSysCache1(TYPEOID, ObjectIdGetDatum(typeOid)); + if (!HeapTupleIsValid(tup)) + elog(ERROR, "cache lookup failed for type %u", typeOid); + typTup = (Form_pg_type) GETSTRUCT(tup); - /* Update owner dependency reference */ - changeDependencyOnOwner(TypeRelationId, typeOid, newOwnerId); + /* + * If it's a composite type, invoke ATExecChangeOwner so that we fix up the + * pg_class entry properly. That will call back to AlterTypeOwnerInternal + * to take care of the pg_type entry(s). + */ + if (typTup->typtype == TYPTYPE_COMPOSITE) + ATExecChangeOwner(typTup->typrelid, newOwnerId, true, AccessExclusiveLock); + else + AlterTypeOwnerInternal(typeOid, newOwnerId); - /* If it has an array type, update that too */ - if (OidIsValid(typTup->typarray)) - AlterTypeOwnerInternal(typTup->typarray, newOwnerId, false); - } - } + /* Update owner dependency reference */ + if (hasDependEntry) + changeDependencyOnOwner(TypeRelationId, typeOid, newOwnerId); - /* Clean up */ + ReleaseSysCache(tup); heap_close(rel, RowExclusiveLock); } /* - * AlterTypeOwnerInternal - change type owner unconditionally + * AlterTypeOwnerInternal - bare-bones type owner change. * - * This is currently only used to propagate ALTER TABLE/TYPE OWNER to a - * table's rowtype or an array type, and to implement REASSIGN OWNED BY. - * It assumes the caller has done all needed checks. The function will - * automatically recurse to an array type if the type has one. - * - * hasDependEntry should be TRUE if type is expected to have a pg_shdepend - * entry (ie, it's not a table rowtype nor an array type). - * is_primary_ops should be TRUE if this function is invoked with user's - * direct operation (e.g, shdepReassignOwned). Elsewhere, + * This routine simply modifies the owner of a pg_type entry, and recurses + * to handle a possible array type. */ void -AlterTypeOwnerInternal(Oid typeOid, Oid newOwnerId, - bool hasDependEntry) +AlterTypeOwnerInternal(Oid typeOid, Oid newOwnerId) { Relation rel; HeapTuple tup; @@ -3369,15 +3356,9 @@ AlterTypeOwnerInternal(Oid typeOid, Oid newOwnerId, CatalogUpdateIndexes(rel, tup); - /* Update owner dependency reference, if it has one */ - if (hasDependEntry) - changeDependencyOnOwner(TypeRelationId, typeOid, newOwnerId); - /* If it has an array type, update that too */ if (OidIsValid(typTup->typarray)) - AlterTypeOwnerInternal(typTup->typarray, newOwnerId, false); - - InvokeObjectPostAlterHook(TypeRelationId, typeOid, 0); + AlterTypeOwnerInternal(typTup->typarray, newOwnerId); /* Clean up */ heap_close(rel, RowExclusiveLock); diff --git a/src/include/commands/typecmds.h b/src/include/commands/typecmds.h index 2351024c2212199dac3563c222aa171a3dacc8cd..65c19a0bf9169b3960d2fb4f98b980e63b14738d 100644 --- a/src/include/commands/typecmds.h +++ b/src/include/commands/typecmds.h @@ -43,8 +43,8 @@ extern List *GetDomainConstraints(Oid typeOid); extern void RenameType(RenameStmt *stmt); extern void AlterTypeOwner(List *names, Oid newOwnerId, ObjectType objecttype); -extern void AlterTypeOwnerInternal(Oid typeOid, Oid newOwnerId, - bool hasDependEntry); +extern void AlterTypeOwner_oid(Oid typeOid, Oid newOwnerId, bool hasDependEntry); +extern void AlterTypeOwnerInternal(Oid typeOid, Oid newOwnerId); extern void AlterTypeNamespace(List *names, const char *newschema, ObjectType objecttype); extern Oid AlterTypeNamespace_oid(Oid typeOid, Oid nspOid, ObjectAddresses *objsMoved); extern Oid AlterTypeNamespaceInternal(Oid typeOid, Oid nspOid, diff --git a/src/test/regress/expected/dependency.out b/src/test/regress/expected/dependency.out index 700def75b1fbce446925897e932886609e9390d6..c687342df29190b260a64cfe9513dd3b7630e676 100644 --- a/src/test/regress/expected/dependency.out +++ b/src/test/regress/expected/dependency.out @@ -89,15 +89,33 @@ DROP OWNED BY regression_user1; \d deptest -- Test REASSIGN OWNED GRANT ALL ON deptest1 TO regression_user1; +GRANT CREATE ON DATABASE regression TO regression_user1; SET SESSION AUTHORIZATION regression_user1; +CREATE SCHEMA deptest; CREATE TABLE deptest (a serial primary key, b text); NOTICE: CREATE TABLE will create implicit sequence "deptest_a_seq" for serial column "deptest.a" NOTICE: CREATE TABLE / PRIMARY KEY will create implicit index "deptest_pkey" for table "deptest" +ALTER DEFAULT PRIVILEGES FOR ROLE regression_user1 IN SCHEMA deptest + GRANT ALL ON TABLES TO regression_user2; +CREATE FUNCTION deptest_func() RETURNS void LANGUAGE plpgsql + AS $$ BEGIN END; $$; +CREATE TYPE deptest_enum AS ENUM ('red'); +CREATE TYPE deptest_range AS RANGE (SUBTYPE = int4); CREATE TABLE deptest2 (f1 int); -- make a serial column the hard way CREATE SEQUENCE ss1; ALTER TABLE deptest2 ALTER f1 SET DEFAULT nextval('ss1'); ALTER SEQUENCE ss1 OWNED BY deptest2.f1; +-- When reassigning ownership of a composite type, its pg_class entry +-- should match +CREATE TYPE deptest_t AS (a int); +SELECT typowner = relowner +FROM pg_type JOIN pg_class c ON typrelid = c.oid WHERE typname = 'deptest_t'; + ?column? +---------- + t +(1 row) + RESET SESSION AUTHORIZATION; REASSIGN OWNED BY regression_user1 TO regression_user2; \dt deptest @@ -107,10 +125,19 @@ REASSIGN OWNED BY regression_user1 TO regression_user2; public | deptest | table | regression_user2 (1 row) +SELECT typowner = relowner +FROM pg_type JOIN pg_class c ON typrelid = c.oid WHERE typname = 'deptest_t'; + ?column? +---------- + t +(1 row) + -- doesn't work: grant still exists DROP USER regression_user1; ERROR: role "regression_user1" cannot be dropped because some objects depend on it -DETAIL: privileges for table deptest1 +DETAIL: owner of default privileges on new relations belonging to role regression_user1 in schema deptest +privileges for database regression +privileges for table deptest1 DROP OWNED BY regression_user1; DROP USER regression_user1; \set VERBOSITY terse diff --git a/src/test/regress/sql/dependency.sql b/src/test/regress/sql/dependency.sql index c1d81569c69136fa4b0c610b1ff30d8f0e0f30f6..599a359b4fed985ca248d6c1a94b4ebe527a279f 100644 --- a/src/test/regress/sql/dependency.sql +++ b/src/test/regress/sql/dependency.sql @@ -74,20 +74,37 @@ DROP OWNED BY regression_user1; -- Test REASSIGN OWNED GRANT ALL ON deptest1 TO regression_user1; +GRANT CREATE ON DATABASE regression TO regression_user1; SET SESSION AUTHORIZATION regression_user1; +CREATE SCHEMA deptest; CREATE TABLE deptest (a serial primary key, b text); +ALTER DEFAULT PRIVILEGES FOR ROLE regression_user1 IN SCHEMA deptest + GRANT ALL ON TABLES TO regression_user2; +CREATE FUNCTION deptest_func() RETURNS void LANGUAGE plpgsql + AS $$ BEGIN END; $$; +CREATE TYPE deptest_enum AS ENUM ('red'); +CREATE TYPE deptest_range AS RANGE (SUBTYPE = int4); CREATE TABLE deptest2 (f1 int); -- make a serial column the hard way CREATE SEQUENCE ss1; ALTER TABLE deptest2 ALTER f1 SET DEFAULT nextval('ss1'); ALTER SEQUENCE ss1 OWNED BY deptest2.f1; -RESET SESSION AUTHORIZATION; +-- When reassigning ownership of a composite type, its pg_class entry +-- should match +CREATE TYPE deptest_t AS (a int); +SELECT typowner = relowner +FROM pg_type JOIN pg_class c ON typrelid = c.oid WHERE typname = 'deptest_t'; + +RESET SESSION AUTHORIZATION; REASSIGN OWNED BY regression_user1 TO regression_user2; \dt deptest +SELECT typowner = relowner +FROM pg_type JOIN pg_class c ON typrelid = c.oid WHERE typname = 'deptest_t'; + -- doesn't work: grant still exists DROP USER regression_user1; DROP OWNED BY regression_user1;