diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 65a28bfb9a..8473c9e7f4 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -357,6 +357,8 @@ static void ATPostAlterTypeCleanup(List **wqueue, AlteredTableInfo *tab, LOCKMOD static void ATPostAlterTypeParse(Oid oldId, char *cmd, List **wqueue, LOCKMODE lockmode, bool rewrite); static void TryReuseIndex(Oid oldId, IndexStmt *stmt); +static void change_owner_fix_column_acls(Oid relationOid, + Oid oldOwnerId, Oid newOwnerId); static void change_owner_recurse_to_sequences(Oid relationOid, Oid newOwnerId, LOCKMODE lockmode); static void ATExecClusterOn(Relation rel, const char *indexName, LOCKMODE lockmode); @@ -8008,6 +8010,14 @@ ATExecChangeOwner(Oid relationOid, Oid newOwnerId, bool recursing, LOCKMODE lock heap_freetuple(newtuple); + /* + * We must similarly update any per-column ACLs to reflect the new + * owner; for neatness reasons that's split out as a subroutine. + */ + change_owner_fix_column_acls(relationOid, + tuple_class->relowner, + newOwnerId); + /* * Update owner dependency reference, if any. A composite type has * none, because it's tracked for the pg_type entry instead of here; @@ -8064,6 +8074,71 @@ ATExecChangeOwner(Oid relationOid, Oid newOwnerId, bool recursing, LOCKMODE lock relation_close(target_rel, NoLock); } +/* + * change_owner_fix_column_acls + * + * Helper function for ATExecChangeOwner. Scan the columns of the table + * and fix any non-null column ACLs to reflect the new owner. + */ +static void +change_owner_fix_column_acls(Oid relationOid, Oid oldOwnerId, Oid newOwnerId) +{ + Relation attRelation; + SysScanDesc scan; + ScanKeyData key[1]; + HeapTuple attributeTuple; + + attRelation = heap_open(AttributeRelationId, RowExclusiveLock); + ScanKeyInit(&key[0], + Anum_pg_attribute_attrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(relationOid)); + scan = systable_beginscan(attRelation, AttributeRelidNumIndexId, + true, SnapshotNow, 1, key); + while (HeapTupleIsValid(attributeTuple = systable_getnext(scan))) + { + Form_pg_attribute att = (Form_pg_attribute) GETSTRUCT(attributeTuple); + Datum repl_val[Natts_pg_attribute]; + bool repl_null[Natts_pg_attribute]; + bool repl_repl[Natts_pg_attribute]; + Acl *newAcl; + Datum aclDatum; + bool isNull; + HeapTuple newtuple; + + /* Ignore dropped columns */ + if (att->attisdropped) + continue; + + aclDatum = heap_getattr(attributeTuple, + Anum_pg_attribute_attacl, + RelationGetDescr(attRelation), + &isNull); + /* Null ACLs do not require changes */ + if (isNull) + continue; + + memset(repl_null, false, sizeof(repl_null)); + memset(repl_repl, false, sizeof(repl_repl)); + + newAcl = aclnewowner(DatumGetAclP(aclDatum), + oldOwnerId, newOwnerId); + repl_repl[Anum_pg_attribute_attacl - 1] = true; + repl_val[Anum_pg_attribute_attacl - 1] = PointerGetDatum(newAcl); + + newtuple = heap_modify_tuple(attributeTuple, + RelationGetDescr(attRelation), + repl_val, repl_null, repl_repl); + + simple_heap_update(attRelation, &newtuple->t_self, newtuple); + CatalogUpdateIndexes(attRelation, newtuple); + + heap_freetuple(newtuple); + } + systable_endscan(scan); + heap_close(attRelation, RowExclusiveLock); +} + /* * change_owner_recurse_to_sequences *