It seems that ALTER TABLE ... OWNER TO does not change the ownership of
any inheriting tables. The documentation states:

> If ONLY is specified, only that table is altered. If ONLY is not
> specified, the table and any descendant tables are altered.

Which to me indicates that ownership should be altered on the target table and
any descendant tables as well.

Here is a small test case to reproduce the problem:

create table parent (id int);
create table child () inherits (parent);
create role new_owner;
alter table parent owner to new_owner;

After performing this sequence of commands, \d looks like:
 Schema |     Name      |   Type   |   Owner 
--------+---------------+----------+-----------
 public | child         | table    | ryan
 public | parent        | table    | new_owner

But I would expect that without specifying only, I would get:
 Schema |     Name      |   Type   |   Owner   
--------+---------------+----------+-----------
 public | child         | table    | new_owner
 public | parent        | table    | new_owner

Attached is a patch which fixes this issue. It is based off of REL8_4_11.

Version: PostgreSQL 8.4.11 on i386-apple-darwin10.8.0, compiled by GCC 
i686-apple-darwin10-gcc-4.2.1 (GCC) 4.2.1 (Apple Inc. build 5666) (dot 3), 
64-bit

-Ryan Kelly
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 963f6b4..ba97baf 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -312,7 +312,7 @@ static void ATPostAlterTypeParse(char *cmd, List **wqueue);
 static void change_owner_fix_column_acls(Oid relationOid,
 							 Oid oldOwnerId, Oid newOwnerId);
 static void change_owner_recurse_to_sequences(Oid relationOid,
-								  Oid newOwnerId);
+								  Oid newOwnerId, bool isOnly);
 static void ATExecClusterOn(Relation rel, const char *indexName);
 static void ATExecDropCluster(Relation rel);
 static void ATPrepSetTableSpace(AlteredTableInfo *tab, Relation rel,
@@ -2457,8 +2457,10 @@ ATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd,
 			pass = AT_PASS_ALTER_TYPE;
 			break;
 		case AT_ChangeOwner:	/* ALTER OWNER */
-			/* This command never recurses */
-			/* No command-specific prep needed */
+			/* Recursion occurs during execution phase */
+			/* No command-specific prep needed except saving recurse flag */
+			if (recurse)
+				cmd->subtype = AT_ChangeOwnerRecurse;
 			pass = AT_PASS_MISC;
 			break;
 		case AT_ClusterOn:		/* CLUSTER ON */
@@ -2663,6 +2665,13 @@ ATExecCmd(List **wqueue, AlteredTableInfo *tab, Relation rel,
 		case AT_ChangeOwner:	/* ALTER OWNER */
 			ATExecChangeOwner(RelationGetRelid(rel),
 							  get_roleid_checked(cmd->name),
+							  false,
+							  true);
+			break;
+		case AT_ChangeOwnerRecurse:	/* ALTER OWNER with recursion */
+			ATExecChangeOwner(RelationGetRelid(rel),
+							  get_roleid_checked(cmd->name),
+							  false,
 							  false);
 			break;
 		case AT_ClusterOn:		/* CLUSTER ON */
@@ -6217,9 +6226,13 @@ ATPostAlterTypeParse(char *cmd, List **wqueue)
  *
  * recursing is also true if ALTER TYPE OWNER is calling us to fix up a
  * free-standing composite type.
+ *
+ * isOnly is true if we are altering this table only (including any sequences
+ * and indexes).  If it is false, this means we should recurse to any tables
+ * which inherit from this one.
  */
 void
-ATExecChangeOwner(Oid relationOid, Oid newOwnerId, bool recursing)
+ATExecChangeOwner(Oid relationOid, Oid newOwnerId, bool recursing, bool isOnly)
 {
 	Relation	target_rel;
 	Relation	class_rel;
@@ -6310,8 +6323,10 @@ ATExecChangeOwner(Oid relationOid, Oid newOwnerId, bool recursing)
 	/*
 	 * If the new owner is the same as the existing owner, consider the
 	 * command to have succeeded.  This is for dump restoration purposes.
+	 * However, if we are potentially altering any child tables, we need
+	 * to keep going.
 	 */
-	if (tuple_class->relowner != newOwnerId)
+	if (tuple_class->relowner != newOwnerId || !isOnly)
 	{
 		Datum		repl_val[Natts_pg_class];
 		bool		repl_null[Natts_pg_class];
@@ -6417,7 +6432,7 @@ ATExecChangeOwner(Oid relationOid, Oid newOwnerId, bool recursing)
 
 			/* For each index, recursively change its ownership */
 			foreach(i, index_oid_list)
-				ATExecChangeOwner(lfirst_oid(i), newOwnerId, true);
+				ATExecChangeOwner(lfirst_oid(i), newOwnerId, true, isOnly);
 
 			list_free(index_oid_list);
 		}
@@ -6427,10 +6442,26 @@ ATExecChangeOwner(Oid relationOid, Oid newOwnerId, bool recursing)
 			/* If it has a toast table, recurse to change its ownership */
 			if (tuple_class->reltoastrelid != InvalidOid)
 				ATExecChangeOwner(tuple_class->reltoastrelid, newOwnerId,
-								  true);
+								  true, isOnly);
 
 			/* If it has dependent sequences, recurse to change them too */
-			change_owner_recurse_to_sequences(relationOid, newOwnerId);
+			change_owner_recurse_to_sequences(relationOid, newOwnerId, isOnly);
+
+			/* If it has child tables, recurse to change them too */
+			if (!isOnly) {
+				List	   *inheritingOidList;
+				ListCell   *i;
+
+				/* Find all the inheriting children of this relation */
+				inheritingOidList = find_inheritance_children(relationOid,
+															  AccessExclusiveLock);
+
+				/* For each child, recursively change its ownership */
+				foreach(i, inheritingOidList)
+					ATExecChangeOwner(lfirst_oid(i), newOwnerId, true, isOnly);
+
+				list_free(inheritingOidList);
+			}
 		}
 	}
 
@@ -6512,7 +6543,7 @@ change_owner_fix_column_acls(Oid relationOid, Oid oldOwnerId, Oid newOwnerId)
  * ownership.
  */
 static void
-change_owner_recurse_to_sequences(Oid relationOid, Oid newOwnerId)
+change_owner_recurse_to_sequences(Oid relationOid, Oid newOwnerId, bool isOnly)
 {
 	Relation	depRel;
 	SysScanDesc scan;
@@ -6562,7 +6593,7 @@ change_owner_recurse_to_sequences(Oid relationOid, Oid newOwnerId)
 		}
 
 		/* We don't need to close the sequence while we alter it. */
-		ATExecChangeOwner(depForm->objid, newOwnerId, true);
+		ATExecChangeOwner(depForm->objid, newOwnerId, true, isOnly);
 
 		/* Now we can close it.  Keep the lock till end of transaction. */
 		relation_close(seqRel, NoLock);
diff --git a/src/include/commands/tablecmds.h b/src/include/commands/tablecmds.h
index 82c9813..33e7891 100644
--- a/src/include/commands/tablecmds.h
+++ b/src/include/commands/tablecmds.h
@@ -24,7 +24,8 @@ extern void RemoveRelations(DropStmt *drop);
 
 extern void AlterTable(AlterTableStmt *stmt);
 
-extern void ATExecChangeOwner(Oid relationOid, Oid newOwnerId, bool recursing);
+extern void ATExecChangeOwner(Oid relationOid, Oid newOwnerId, bool recursing,
+							  bool isOnly);
 
 extern void AlterTableInternal(Oid relid, List *cmds, bool recurse);
 
diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h
index f27d9a2..78254e1 100644
--- a/src/include/nodes/parsenodes.h
+++ b/src/include/nodes/parsenodes.h
@@ -1131,7 +1131,8 @@ typedef enum AlterTableType
 	AT_EnableReplicaRule,		/* ENABLE REPLICA RULE name */
 	AT_DisableRule,				/* DISABLE RULE name */
 	AT_AddInherit,				/* INHERIT parent */
-	AT_DropInherit				/* NO INHERIT parent */
+	AT_DropInherit,				/* NO INHERIT parent */
+	AT_ChangeOwnerRecurse		/* internal to commands/tablecmds.c */
 } AlterTableType;
 
 typedef struct AlterTableCmd	/* one subcommand of an ALTER TABLE */
-- 
Sent via pgsql-bugs mailing list (pgsql-bugs@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-bugs

Reply via email to