diff --git a/doc/src/sgml/ref/allfiles.sgml b/doc/src/sgml/ref/allfiles.sgml
index c08b09b67d4b13404d8d26543f13fb5b094d6563..07dd0b0ee7453e8b6963480d2fed100450e4ff26 100644
--- a/doc/src/sgml/ref/allfiles.sgml
+++ b/doc/src/sgml/ref/allfiles.sgml
@@ -1,5 +1,5 @@
 <!--
-$PostgreSQL: pgsql/doc/src/sgml/ref/allfiles.sgml,v 1.66 2005/07/29 15:13:11 momjian Exp $
+$PostgreSQL: pgsql/doc/src/sgml/ref/allfiles.sgml,v 1.67 2005/11/21 12:49:30 alvherre Exp $
 PostgreSQL documentation
 Complete list of usable sgml source files in this directory.
 -->
@@ -70,6 +70,7 @@ Complete list of usable sgml source files in this directory.
 <!entity dropLanguage       system "drop_language.sgml">
 <!entity dropOperator       system "drop_operator.sgml">
 <!entity dropOperatorClass  system "drop_opclass.sgml">
+<!entity dropOwned          system "drop_owned.sgml">
 <!entity dropRole           system "drop_role.sgml">
 <!entity dropRule           system "drop_rule.sgml">
 <!entity dropSchema         system "drop_schema.sgml">
@@ -93,6 +94,7 @@ Complete list of usable sgml source files in this directory.
 <!entity notify             system "notify.sgml">
 <!entity prepare            system "prepare.sgml">
 <!entity prepareTransaction system "prepare_transaction.sgml">
+<!entity reassignOwned      system "reassign_owned.sgml">
 <!entity reindex            system "reindex.sgml">
 <!entity releaseSavepoint   system "release_savepoint.sgml">
 <!entity reset              system "reset.sgml">
diff --git a/doc/src/sgml/ref/drop_owned.sgml b/doc/src/sgml/ref/drop_owned.sgml
new file mode 100644
index 0000000000000000000000000000000000000000..dc8b01fe984bf45cc54165b251a0a8652627033e
--- /dev/null
+++ b/doc/src/sgml/ref/drop_owned.sgml
@@ -0,0 +1,99 @@
+<!--
+$PostgreSQL: pgsql/doc/src/sgml/ref/drop_owned.sgml,v 1.1 2005/11/21 12:49:30 alvherre Exp $
+PostgreSQL documentation
+-->
+
+<refentry id="SQL-DROP-OWNED">
+ <refmeta>
+  <refentrytitle id="SQL-DROP-OWNED-TITLE">DROP OWNED</refentrytitle>
+  <refmiscinfo>SQL - Language Statements</refmiscinfo>
+ </refmeta>
+
+ <refnamediv>
+  <refname>DROP OWNED</refname>
+  <refpurpose>remove database objects owned by a database role</refpurpose>
+ </refnamediv>
+
+ <indexterm zone="sql-drop-owned">
+  <primary>DROP OWNED</primary>
+ </indexterm>
+
+ <refsynopsisdiv>
+<synopsis>
+DROP OWNED <replaceable class="PARAMETER">name</replaceable> [, ...] [ RESTRICT | CASCADE ]
+</synopsis>
+ </refsynopsisdiv>
+
+ <refsect1>
+  <title>Description</title>
+
+  <para>
+   The <command>DROP OWNED</command> instructs the system to drop those
+   database objects owned by one of the given roles which reside on the
+   current database.  All privileges granted to the given roles will also be
+   revoked.
+  </para>
+
+  <para>
+   If <literal>CASCADE</> is specified, <command>DROP OWNED</command>
+   will behave like a <command>DROP ... CASCADE</command> was issued
+   for each object, that is, objects dependent on the ones owned by the
+   given users will be dropped as well.  
+  </para>
+ </refsect1>
+
+ <refsect1>
+  <title>Notes</title>
+  <para>
+   The <command>DROP OWNED</command> command is mostly used in preparation to
+   drop the roles.  It may be necessary to issue the command in more than one
+   database.
+  </para>
+
+  <para>
+   Using the <literal>CASCADE</literal> option may make the command recurse to
+   objects owned by other users.
+  </para>
+
+  <para>
+   See the <command>REASSIGN OWNED</command> command for an alternative that
+   gives the objects away to another role.
+  </para>
+ </refsect1>
+
+ <refsect1>
+  <title>Compatibility</title>
+
+  <para>
+   The <command>DROP OWNED</command> statement is a
+   <productname>PostgreSQL</productname> extension.
+  </para>
+ </refsect1>
+
+ <refsect1>
+  <title>See Also</title>
+
+  <simplelist type="inline">
+   <member><xref linkend="sql-reassign-owned" endterm="sql-reassign-owned-title"></member>
+   <member><xref linkend="sql-droprole" endterm="sql-droprole-title"></member>
+  </simplelist>
+ </refsect1>
+
+</refentry>
+
+<!-- Keep this comment at the end of the file
+Local variables:
+mode: sgml
+sgml-omittag:nil
+sgml-shorttag:t
+sgml-minimize-attributes:nil
+sgml-always-quote-attributes:t
+sgml-indent-step:1
+sgml-indent-data:t
+sgml-parent-document:nil
+sgml-default-dtd-file:"../reference.ced"
+sgml-exposed-tags:nil
+sgml-local-catalogs:"/usr/lib/sgml/catalog"
+sgml-local-ecat-files:nil
+End:
+-->
diff --git a/doc/src/sgml/ref/reassign_owned.sgml b/doc/src/sgml/ref/reassign_owned.sgml
new file mode 100644
index 0000000000000000000000000000000000000000..a54e4c82698d86c1269b8b00b298a4a20c7087e5
--- /dev/null
+++ b/doc/src/sgml/ref/reassign_owned.sgml
@@ -0,0 +1,89 @@
+<!--
+$PostgreSQL: pgsql/doc/src/sgml/ref/reassign_owned.sgml,v 1.1 2005/11/21 12:49:30 alvherre Exp $
+PostgreSQL documentation
+-->
+
+<refentry id="SQL-REASSIGN-OWNED">
+ <refmeta>
+  <refentrytitle id="SQL-REASSIGN-OWNED-TITLE">REASSIGN OWNED</refentrytitle>
+  <refmiscinfo>SQL - Language Statements</refmiscinfo>
+ </refmeta>
+
+ <refnamediv>
+  <refname>REASSIGN OWNED</refname>
+  <refpurpose>change ownership of database objects owned by a database role</refpurpose>
+ </refnamediv>
+
+ <indexterm zone="sql-reassign-owned">
+  <primary>REASSIGN OWNED</primary>
+ </indexterm>
+
+ <refsynopsisdiv>
+<synopsis>
+REASSIGN OWNED <replaceable class="PARAMETER">old_role</replaceable> [, ...] TO <replaceable class="PARAMETER">new_role</replaceable>
+</synopsis>
+ </refsynopsisdiv>
+
+ <refsect1>
+  <title>Description</title>
+
+  <para>
+   The <command>REASSIGN OWNED</command> instructs the system to change
+   the ownership of the database objects owned by one of the old_roles,
+   to new_role.
+  </para>
+ </refsect1>
+
+ <refsect1>
+  <title>Notes</title>
+
+  <para>
+   The <command>REASSIGN OWNED</command> command is mostly used in preparation to
+   drop the roles.  See the <command>DROP OWNED</command> command for an
+   alternative that drops the objects.
+  </para>
+
+  <para>
+   The <command>REASSIGN OWNED</command> command does not affect the privileges
+   granted to the old_roles in objects not owned by them.  Use
+   <command>DROP OWNED</command> to remove them.
+  </para>
+
+ </refsect1>
+
+ <refsect1>
+  <title>Compatibility</title>
+
+  <para>
+   The <command>REASSIGN OWNED</command> statement is a
+   <productname>PostgreSQL</productname> extension.
+  </para>
+ </refsect1>
+
+ <refsect1>
+  <title>See Also</title>
+
+  <simplelist type="inline">
+   <member><xref linkend="sql-drop-owned" endterm="sql-drop-owned-title"></member>
+   <member><xref linkend="sql-droprole" endterm="sql-droprole-title"></member>
+  </simplelist>
+ </refsect1>
+
+</refentry>
+
+<!-- Keep this comment at the end of the file
+Local variables:
+mode: sgml
+sgml-omittag:nil
+sgml-shorttag:t
+sgml-minimize-attributes:nil
+sgml-always-quote-attributes:t
+sgml-indent-step:1
+sgml-indent-data:t
+sgml-parent-document:nil
+sgml-default-dtd-file:"../reference.ced"
+sgml-exposed-tags:nil
+sgml-local-catalogs:"/usr/lib/sgml/catalog"
+sgml-local-ecat-files:nil
+End:
+-->
diff --git a/doc/src/sgml/reference.sgml b/doc/src/sgml/reference.sgml
index 0191ec8b3c31b1b7c2f66532722b0c7c51d10904..f320a132d04b9789f6dbcb33fa6e0a709c728c39 100644
--- a/doc/src/sgml/reference.sgml
+++ b/doc/src/sgml/reference.sgml
@@ -1,5 +1,5 @@
 <!-- reference.sgml
-$PostgreSQL: pgsql/doc/src/sgml/reference.sgml,v 1.56 2005/07/29 15:13:11 momjian Exp $
+$PostgreSQL: pgsql/doc/src/sgml/reference.sgml,v 1.57 2005/11/21 12:49:30 alvherre Exp $
 
 PostgreSQL Reference Manual
 -->
@@ -102,6 +102,7 @@ PostgreSQL Reference Manual
    &dropLanguage;
    &dropOperator;
    &dropOperatorClass;
+   &dropOwned;
    &dropRole;
    &dropRule;
    &dropSchema;
@@ -125,6 +126,7 @@ PostgreSQL Reference Manual
    &notify;
    &prepare;
    &prepareTransaction;
+   &reassignOwned;
    &reindex;
    &releaseSavepoint;
    &reset;
diff --git a/src/backend/catalog/aclchk.c b/src/backend/catalog/aclchk.c
index 15a197af81b123d9725ee1f802634171fe52d768..b0bc8e28eaf790345f3a6b9a34da4eb5c07afbfb 100644
--- a/src/backend/catalog/aclchk.c
+++ b/src/backend/catalog/aclchk.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/catalog/aclchk.c,v 1.120 2005/10/15 02:49:12 momjian Exp $
+ *	  $PostgreSQL: pgsql/src/backend/catalog/aclchk.c,v 1.121 2005/11/21 12:49:30 alvherre Exp $
  *
  * NOTES
  *	  See acl.h.
@@ -17,6 +17,7 @@
  */
 #include "postgres.h"
 
+#include "access/genam.h"
 #include "access/heapam.h"
 #include "catalog/catalog.h"
 #include "catalog/dependency.h"
@@ -41,12 +42,25 @@
 #include "utils/syscache.h"
 
 
-static void ExecuteGrantStmt_Relation(GrantStmt *stmt);
-static void ExecuteGrantStmt_Database(GrantStmt *stmt);
-static void ExecuteGrantStmt_Function(GrantStmt *stmt);
-static void ExecuteGrantStmt_Language(GrantStmt *stmt);
-static void ExecuteGrantStmt_Namespace(GrantStmt *stmt);
-static void ExecuteGrantStmt_Tablespace(GrantStmt *stmt);
+static void ExecGrant_Relation(bool is_grant, List *objects, bool all_privs,
+				   AclMode privileges, List *grantees, bool grant_option,
+				   DropBehavior behavior);
+static void ExecGrant_Database(bool is_grant, List *objects, bool all_privs,
+				   AclMode privileges, List *grantees, bool grant_option,
+				   DropBehavior behavior);
+static void ExecGrant_Function(bool is_grant, List *objects, bool all_privs,
+				   AclMode privileges, List *grantees, bool grant_option,
+				   DropBehavior behavior);
+static void ExecGrant_Language(bool is_grant, List *objects, bool all_privs,
+				   AclMode privileges, List *grantees, bool grant_option,
+				   DropBehavior behavior);
+static void ExecGrant_Namespace(bool is_grant, List *objects, bool all_privs,
+					AclMode privileges, List *grantees, bool grant_option,
+					DropBehavior behavior);
+static void ExecGrant_Tablespace(bool is_grant, List *objects, bool all_privs,
+					 AclMode privileges, List *grantees, bool grant_option,
+					 DropBehavior behavior);
+static List *objectNamesToOids(GrantObjectType objtype, List *objnames);
 
 static AclMode string_to_privilege(const char *privname);
 static const char *privilege_to_string(AclMode privilege);
@@ -96,15 +110,10 @@ merge_acl_with_grant(Acl *old_acl, bool is_grant,
 
 	foreach(j, grantees)
 	{
-		PrivGrantee *grantee = (PrivGrantee *) lfirst(j);
-		AclItem aclitem;
+		AclItem		aclitem;
 		Acl		   *newer_acl;
 
-		if (grantee->rolname)
-			aclitem.	ai_grantee = get_roleid_checked(grantee->rolname);
-
-		else
-			aclitem.	ai_grantee = ACL_ID_PUBLIC;
+		aclitem.ai_grantee = lfirst_oid(j);
 
 		/*
 		 * Grant options can only be granted to individual roles, not PUBLIC.
@@ -151,71 +160,307 @@ merge_acl_with_grant(Acl *old_acl, bool is_grant,
 void
 ExecuteGrantStmt(GrantStmt *stmt)
 {
+	List	   *objects;
+	List	   *grantees = NIL;
+	AclMode		privileges;
+	ListCell   *cell;
+	bool		all_privs;
+	AclMode all_privileges = (AclMode) 0;
+	char	*errormsg = NULL;
+
+	/*
+	 * Convert the PrivGrantee list into an Oid list.  Note that at this point
+	 * we insert an ACL_ID_PUBLIC into the list if an empty role name is
+	 * detected (which is what the grammar uses if PUBLIC is found), so
+	 * downstream there shouldn't be any additional work needed to support this
+	 * case.
+	 */
+	foreach(cell, stmt->grantees)
+	{
+		PrivGrantee *grantee = (PrivGrantee *) lfirst(cell);
+
+		if (grantee->rolname == NULL)
+			grantees = lappend_oid(grantees, ACL_ID_PUBLIC);
+		else
+			grantees = lappend_oid(grantees,
+								   get_roleid_checked(grantee->rolname));
+	}
+
+	/*
+	 * Convert stmt->privileges, a textual list, into an AclMode bitmask
+	 * appropiate for the given object class.
+	 */
 	switch (stmt->objtype)
 	{
 		case ACL_OBJECT_RELATION:
-			ExecuteGrantStmt_Relation(stmt);
+			all_privileges = ACL_ALL_RIGHTS_RELATION;
+			errormsg = _("invalid privilege type %s for table");
 			break;
 		case ACL_OBJECT_DATABASE:
-			ExecuteGrantStmt_Database(stmt);
+			all_privileges = ACL_ALL_RIGHTS_DATABASE;
+			errormsg = _("invalid privilege type %s for database");
 			break;
 		case ACL_OBJECT_FUNCTION:
-			ExecuteGrantStmt_Function(stmt);
+			all_privileges = ACL_ALL_RIGHTS_FUNCTION;
+			errormsg = _("invalid privilege type %s for function");
 			break;
 		case ACL_OBJECT_LANGUAGE:
-			ExecuteGrantStmt_Language(stmt);
+			all_privileges = ACL_ALL_RIGHTS_LANGUAGE;
+			errormsg = _("invalid privilege type %s for language");
 			break;
 		case ACL_OBJECT_NAMESPACE:
-			ExecuteGrantStmt_Namespace(stmt);
+			all_privileges = ACL_ALL_RIGHTS_NAMESPACE;
+			errormsg = _("invalid privilege type %s for namespace");
 			break;
 		case ACL_OBJECT_TABLESPACE:
-			ExecuteGrantStmt_Tablespace(stmt);
+			all_privileges = ACL_ALL_RIGHTS_TABLESPACE;
+			errormsg = _("invalid privilege type %s for tablespace");
 			break;
 		default:
 			elog(ERROR, "unrecognized GrantStmt.objtype: %d",
 				 (int) stmt->objtype);
 	}
-}
-
-
-static void
-ExecuteGrantStmt_Relation(GrantStmt *stmt)
-{
-	AclMode		privileges;
-	bool		all_privs;
-	ListCell   *i;
 
 	if (stmt->privileges == NIL)
 	{
 		all_privs = true;
-		privileges = ACL_ALL_RIGHTS_RELATION;
+		privileges = all_privileges;
 	}
 	else
 	{
 		all_privs = false;
 		privileges = ACL_NO_RIGHTS;
-		foreach(i, stmt->privileges)
+		foreach(cell, stmt->privileges)
 		{
-			char	   *privname = strVal(lfirst(i));
+			char	   *privname = strVal(lfirst(cell));
 			AclMode		priv = string_to_privilege(privname);
 
-			if (priv & ~((AclMode) ACL_ALL_RIGHTS_RELATION))
+			if (priv & ~((AclMode) all_privileges))
 				ereport(ERROR,
 						(errcode(ERRCODE_INVALID_GRANT_OPERATION),
-						 errmsg("invalid privilege type %s for table",
+						 errmsg(errormsg,
 								privilege_to_string(priv))));
+
 			privileges |= priv;
 		}
 	}
 
-	foreach(i, stmt->objects)
+	/* Turn the list of object names into an Oid list */
+	objects = objectNamesToOids(stmt->objtype, stmt->objects);
+
+	ExecGrantStmt_oids(stmt->is_grant, stmt->objtype, objects, all_privs,
+					   privileges, grantees, stmt->grant_option,
+					   stmt->behavior);
+}
+
+/*
+ * ExecGrantStmt_oids
+ *
+ * "Internal" entrypoint for granting and revoking privileges.  The arguments
+ * it receives are lists of Oids or have been otherwise converted from text
+ * format to internal format.
+ */
+void
+ExecGrantStmt_oids(bool is_grant, GrantObjectType objtype, List *objects,
+				   bool all_privs, AclMode privileges, List *grantees,
+				   bool grant_option, DropBehavior behavior)
+{
+	switch (objtype)
 	{
-		RangeVar   *relvar = (RangeVar *) lfirst(i);
-		Oid			relOid;
-		Relation	relation;
-		HeapTuple	tuple;
-		Form_pg_class pg_class_tuple;
+		case ACL_OBJECT_RELATION:
+			ExecGrant_Relation(is_grant, objects, all_privs, privileges,
+							   grantees, grant_option, behavior);
+			break;
+		case ACL_OBJECT_DATABASE:
+			ExecGrant_Database(is_grant, objects, all_privs, privileges,
+							   grantees, grant_option, behavior);
+			break;
+		case ACL_OBJECT_FUNCTION:
+			ExecGrant_Function(is_grant, objects, all_privs, privileges,
+							   grantees, grant_option, behavior);
+			break;
+		case ACL_OBJECT_LANGUAGE:
+			ExecGrant_Language(is_grant, objects, all_privs, privileges,
+							   grantees, grant_option, behavior);
+			break;
+		case ACL_OBJECT_NAMESPACE:
+			ExecGrant_Namespace(is_grant, objects, all_privs,
+								privileges, grantees, grant_option,
+								behavior);
+			break;
+		case ACL_OBJECT_TABLESPACE:
+			ExecGrant_Tablespace(is_grant, objects, all_privs,
+								 privileges, grantees, grant_option,
+								 behavior);
+			break;
+		default:
+			elog(ERROR, "unrecognized GrantStmt.objtype: %d",
+				 (int) objtype);
+	}
+}
+
+/*
+ * objectNamesToOids
+ *
+ * Turn a list of object names of a given type into an Oid list.
+ */
+static List *
+objectNamesToOids(GrantObjectType objtype, List *objnames)
+{
+	List	 *objects = NIL;
+	ListCell *cell;
+
+	Assert(objnames != NIL);
+
+	switch (objtype)
+	{
+		case ACL_OBJECT_RELATION:
+			foreach(cell, objnames)
+			{
+				Oid			relOid;
+				RangeVar   *relvar = (RangeVar *) lfirst(cell);
+
+				relOid = RangeVarGetRelid(relvar, false);
+				objects = lappend_oid(objects, relOid);
+			}
+			break;
+		case ACL_OBJECT_DATABASE:
+			foreach(cell, objnames)
+			{
+				char	   *dbname = strVal(lfirst(cell));
+				ScanKeyData	entry[1];
+				HeapScanDesc scan;
+				HeapTuple	tuple;
+				Relation	relation;
+
+				relation = heap_open(DatabaseRelationId, AccessShareLock);
+
+				/*
+				 * There's no syscache for pg_database, so we must
+				 * look the hard way.
+				 */
+				ScanKeyInit(&entry[0],
+							Anum_pg_database_datname,
+							BTEqualStrategyNumber, F_NAMEEQ,
+							CStringGetDatum(dbname));
+				scan = heap_beginscan(relation, SnapshotNow, 1, entry);
+				tuple = heap_getnext(scan, ForwardScanDirection);
+				if (!HeapTupleIsValid(tuple))
+					ereport(ERROR,
+							(errcode(ERRCODE_UNDEFINED_DATABASE),
+							 errmsg("database \"%s\" does not exist", dbname)));
+				objects = lappend_oid(objects, HeapTupleGetOid(tuple));
+
+				heap_close(relation, AccessShareLock);
+
+				heap_endscan(scan);
+			}
+			break;
+		case ACL_OBJECT_FUNCTION:
+			foreach(cell, objnames)
+			{
+				FuncWithArgs *func = (FuncWithArgs *) lfirst(cell);
+				Oid			funcid;
+
+				funcid = LookupFuncNameTypeNames(func->funcname,
+												 func->funcargs, false);
+				objects = lappend_oid(objects, funcid);
+			}
+			break;
+		case ACL_OBJECT_LANGUAGE:
+			foreach(cell, objnames)
+			{
+				char	*langname = strVal(lfirst(cell));
+				HeapTuple tuple;
+
+				tuple = SearchSysCache(LANGNAME,
+									   PointerGetDatum(langname),
+									   0, 0, 0);
+				if (!HeapTupleIsValid(tuple))
+					ereport(ERROR,
+							(errcode(ERRCODE_UNDEFINED_OBJECT),
+							 errmsg("language \"%s\" does not exist", langname)));
+
+				objects = lappend_oid(objects, HeapTupleGetOid(tuple));
+
+				ReleaseSysCache(tuple);
+			}
+			break;
+		case ACL_OBJECT_NAMESPACE:
+			foreach (cell, objnames)
+			{
+				char	   *nspname = strVal(lfirst(cell));
+				HeapTuple	tuple;
+
+				tuple = SearchSysCache(NAMESPACENAME,
+									   CStringGetDatum(nspname),
+									   0, 0, 0);
+				if (!HeapTupleIsValid(tuple))
+					ereport(ERROR,
+							(errcode(ERRCODE_UNDEFINED_SCHEMA),
+							 errmsg("schema \"%s\" does not exist", nspname)));
+
+				objects = lappend_oid(objects, HeapTupleGetOid(tuple));
+
+				ReleaseSysCache(tuple);
+			}
+			break;
+		case ACL_OBJECT_TABLESPACE:
+			foreach (cell, objnames)
+			{
+				char		   *spcname = strVal(lfirst(cell));
+				ScanKeyData		entry[1];
+				HeapScanDesc	scan;
+				HeapTuple		tuple;
+				Relation		relation;
+
+				relation = heap_open(TableSpaceRelationId, AccessShareLock);
+
+				ScanKeyInit(&entry[0],
+							Anum_pg_tablespace_spcname,
+							BTEqualStrategyNumber, F_NAMEEQ,
+							CStringGetDatum(spcname));
+
+				scan = heap_beginscan(relation, SnapshotNow, 1, entry);
+				tuple = heap_getnext(scan, ForwardScanDirection);
+				if (!HeapTupleIsValid(tuple))
+					ereport(ERROR,
+							(errcode(ERRCODE_UNDEFINED_OBJECT),
+							 errmsg("tablespace \"%s\" does not exist", spcname)));
+
+				objects = lappend_oid(objects, HeapTupleGetOid(tuple));
+
+				heap_endscan(scan);
+
+				heap_close(relation, AccessShareLock);
+			}
+			break;
+		default:
+			elog(ERROR, "unrecognized GrantStmt.objtype: %d",
+				 (int) objtype);
+	}
+
+	return objects;
+}
+
+static void
+ExecGrant_Relation(bool is_grant, List *objects, bool all_privs,
+				   AclMode privileges, List *grantees, bool grant_option,
+				   DropBehavior behavior)
+{
+	Relation	relation;
+	ListCell   *cell;
+
+	if (all_privs && privileges == ACL_NO_RIGHTS)
+		privileges = ACL_ALL_RIGHTS_RELATION;
+
+	relation = heap_open(RelationRelationId, RowExclusiveLock);
+
+	foreach (cell, objects)
+	{
+		Oid			relOid = lfirst_oid(cell);
 		Datum		aclDatum;
+		Form_pg_class pg_class_tuple;
 		bool		isNull;
 		AclMode		avail_goptions;
 		AclMode		this_privileges;
@@ -223,6 +468,7 @@ ExecuteGrantStmt_Relation(GrantStmt *stmt)
 		Acl		   *new_acl;
 		Oid			grantorId;
 		Oid			ownerId;
+		HeapTuple	tuple;
 		HeapTuple	newtuple;
 		Datum		values[Natts_pg_class];
 		char		nulls[Natts_pg_class];
@@ -232,9 +478,6 @@ ExecuteGrantStmt_Relation(GrantStmt *stmt)
 		Oid		   *oldmembers;
 		Oid		   *newmembers;
 
-		/* open pg_class */
-		relation = heap_open(RelationRelationId, RowExclusiveLock);
-		relOid = RangeVarGetRelid(relvar, false);
 		tuple = SearchSysCache(RELOID,
 							   ObjectIdGetDatum(relOid),
 							   0, 0, 0);
@@ -247,15 +490,14 @@ ExecuteGrantStmt_Relation(GrantStmt *stmt)
 			ereport(ERROR,
 					(errcode(ERRCODE_WRONG_OBJECT_TYPE),
 					 errmsg("\"%s\" is an index",
-							relvar->relname)));
+							NameStr(pg_class_tuple->relname))));
 
 		/* Composite types aren't tables either */
 		if (pg_class_tuple->relkind == RELKIND_COMPOSITE_TYPE)
 			ereport(ERROR,
 					(errcode(ERRCODE_WRONG_OBJECT_TYPE),
 					 errmsg("\"%s\" is a composite type",
-							relvar->relname)));
-
+							NameStr(pg_class_tuple->relname))));
 		/*
 		 * Get owner ID and working copy of existing ACL. If there's no ACL,
 		 * substitute the proper default.
@@ -285,7 +527,7 @@ ExecuteGrantStmt_Relation(GrantStmt *stmt)
 								 ACL_ALL_RIGHTS_RELATION | ACL_GRANT_OPTION_FOR(ACL_ALL_RIGHTS_RELATION),
 								 ACLMASK_ANY) == ACL_NO_RIGHTS)
 				aclcheck_error(ACLCHECK_NO_PRIV, ACL_KIND_CLASS,
-							   relvar->relname);
+							   NameStr(pg_class_tuple->relname));
 		}
 
 		/*
@@ -297,7 +539,7 @@ ExecuteGrantStmt_Relation(GrantStmt *stmt)
 		 * GRANT case.)
 		 */
 		this_privileges = privileges & ACL_OPTION_TO_PRIVS(avail_goptions);
-		if (stmt->is_grant)
+		if (is_grant)
 		{
 			if (this_privileges == 0)
 				ereport(WARNING,
@@ -328,9 +570,9 @@ ExecuteGrantStmt_Relation(GrantStmt *stmt)
 		 */
 		noldmembers = aclmembers(old_acl, &oldmembers);
 
-		new_acl = merge_acl_with_grant(old_acl, stmt->is_grant,
-									   stmt->grant_option, stmt->behavior,
-									   stmt->grantees, this_privileges,
+		new_acl = merge_acl_with_grant(old_acl, is_grant,
+									   grant_option, behavior,
+									   grantees, this_privileges,
 									   grantorId, ownerId);
 
 		nnewmembers = aclmembers(new_acl, &newmembers);
@@ -352,7 +594,7 @@ ExecuteGrantStmt_Relation(GrantStmt *stmt)
 
 		/* Update the shared dependency ACL info */
 		updateAclDependencies(RelationRelationId, relOid,
-							  ownerId, stmt->is_grant,
+							  ownerId, is_grant,
 							  noldmembers, oldmembers,
 							  nnewmembers, newmembers);
 
@@ -360,50 +602,29 @@ ExecuteGrantStmt_Relation(GrantStmt *stmt)
 
 		pfree(new_acl);
 
-		heap_close(relation, RowExclusiveLock);
-
 		/* prevent error when processing duplicate objects */
 		CommandCounterIncrement();
 	}
+
+	heap_close(relation, RowExclusiveLock);
 }
 
 static void
-ExecuteGrantStmt_Database(GrantStmt *stmt)
+ExecGrant_Database(bool is_grant, List *objects, bool all_privs,
+				   AclMode privileges, List *grantees, bool grant_option,
+				   DropBehavior behavior)
 {
-	AclMode		privileges;
-	bool		all_privs;
-	ListCell   *i;
+	Relation	relation;
+	ListCell   *cell;
 
-	if (stmt->privileges == NIL)
-	{
-		all_privs = true;
+	if (all_privs && privileges == ACL_NO_RIGHTS)
 		privileges = ACL_ALL_RIGHTS_DATABASE;
-	}
-	else
-	{
-		all_privs = false;
-		privileges = ACL_NO_RIGHTS;
-		foreach(i, stmt->privileges)
-		{
-			char	   *privname = strVal(lfirst(i));
-			AclMode		priv = string_to_privilege(privname);
 
-			if (priv & ~((AclMode) ACL_ALL_RIGHTS_DATABASE))
-				ereport(ERROR,
-						(errcode(ERRCODE_INVALID_GRANT_OPERATION),
-						 errmsg("invalid privilege type %s for database",
-								privilege_to_string(priv))));
-			privileges |= priv;
-		}
-	}
+	relation = heap_open(DatabaseRelationId, RowExclusiveLock);
 
-	foreach(i, stmt->objects)
+	foreach (cell, objects)
 	{
-		char	   *dbname = strVal(lfirst(i));
-		Relation	relation;
-		ScanKeyData entry[1];
-		HeapScanDesc scan;
-		HeapTuple	tuple;
+		Oid			datId = lfirst_oid(cell);
 		Form_pg_database pg_database_tuple;
 		Datum		aclDatum;
 		bool		isNull;
@@ -421,18 +642,23 @@ ExecuteGrantStmt_Database(GrantStmt *stmt)
 		int			nnewmembers;
 		Oid		   *oldmembers;
 		Oid		   *newmembers;
+		ScanKeyData entry[1];
+		SysScanDesc scan;
+		HeapTuple	tuple;
 
-		relation = heap_open(DatabaseRelationId, RowExclusiveLock);
+		/* There's no syscache for pg_database, so must look the hard way */
 		ScanKeyInit(&entry[0],
-					Anum_pg_database_datname,
-					BTEqualStrategyNumber, F_NAMEEQ,
-					CStringGetDatum(dbname));
-		scan = heap_beginscan(relation, SnapshotNow, 1, entry);
-		tuple = heap_getnext(scan, ForwardScanDirection);
+					ObjectIdAttributeNumber,
+					BTEqualStrategyNumber, F_OIDEQ,
+					ObjectIdGetDatum(datId));
+		scan = systable_beginscan(relation, DatabaseOidIndexId, true,
+								  SnapshotNow, 1, entry);
+
+		tuple = systable_getnext(scan);
+
 		if (!HeapTupleIsValid(tuple))
-			ereport(ERROR,
-					(errcode(ERRCODE_UNDEFINED_DATABASE),
-					 errmsg("database \"%s\" does not exist", dbname)));
+			elog(ERROR, "could not find tuple for database %u", datId);
+
 		pg_database_tuple = (Form_pg_database) GETSTRUCT(tuple);
 
 		/*
@@ -476,7 +702,7 @@ ExecuteGrantStmt_Database(GrantStmt *stmt)
 		 * GRANT case.)
 		 */
 		this_privileges = privileges & ACL_OPTION_TO_PRIVS(avail_goptions);
-		if (stmt->is_grant)
+		if (is_grant)
 		{
 			if (this_privileges == 0)
 				ereport(WARNING,
@@ -507,9 +733,9 @@ ExecuteGrantStmt_Database(GrantStmt *stmt)
 		 */
 		noldmembers = aclmembers(old_acl, &oldmembers);
 
-		new_acl = merge_acl_with_grant(old_acl, stmt->is_grant,
-									   stmt->grant_option, stmt->behavior,
-									   stmt->grantees, this_privileges,
+		new_acl = merge_acl_with_grant(old_acl, is_grant,
+									   grant_option, behavior,
+									   grantees, this_privileges,
 									   grantorId, ownerId);
 
 		nnewmembers = aclmembers(new_acl, &newmembers);
@@ -522,7 +748,8 @@ ExecuteGrantStmt_Database(GrantStmt *stmt)
 		replaces[Anum_pg_database_datacl - 1] = 'r';
 		values[Anum_pg_database_datacl - 1] = PointerGetDatum(new_acl);
 
-		newtuple = heap_modifytuple(tuple, RelationGetDescr(relation), values, nulls, replaces);
+		newtuple = heap_modifytuple(tuple, RelationGetDescr(relation), values,
+									nulls, replaces);
 
 		simple_heap_update(relation, &newtuple->t_self, newtuple);
 
@@ -531,57 +758,37 @@ ExecuteGrantStmt_Database(GrantStmt *stmt)
 
 		/* Update the shared dependency ACL info */
 		updateAclDependencies(DatabaseRelationId, HeapTupleGetOid(tuple),
-							  ownerId, stmt->is_grant,
+							  ownerId, is_grant,
 							  noldmembers, oldmembers,
 							  nnewmembers, newmembers);
 
-		pfree(new_acl);
-
-		heap_endscan(scan);
+		systable_endscan(scan);
 
-		heap_close(relation, RowExclusiveLock);
+		pfree(new_acl);
 
 		/* prevent error when processing duplicate objects */
 		CommandCounterIncrement();
 	}
+
+	heap_close(relation, RowExclusiveLock);
 }
 
 static void
-ExecuteGrantStmt_Function(GrantStmt *stmt)
+ExecGrant_Function(bool is_grant, List *objects, bool all_privs,
+				   AclMode privileges, List *grantees, bool grant_option,
+				   DropBehavior behavior)
 {
-	AclMode		privileges;
-	bool		all_privs;
-	ListCell   *i;
+	Relation	relation;
+	ListCell   *cell;
 
-	if (stmt->privileges == NIL)
-	{
-		all_privs = true;
+	if (all_privs && privileges == ACL_NO_RIGHTS)
 		privileges = ACL_ALL_RIGHTS_FUNCTION;
-	}
-	else
-	{
-		all_privs = false;
-		privileges = ACL_NO_RIGHTS;
-		foreach(i, stmt->privileges)
-		{
-			char	   *privname = strVal(lfirst(i));
-			AclMode		priv = string_to_privilege(privname);
 
-			if (priv & ~((AclMode) ACL_ALL_RIGHTS_FUNCTION))
-				ereport(ERROR,
-						(errcode(ERRCODE_INVALID_GRANT_OPERATION),
-						 errmsg("invalid privilege type %s for function",
-								privilege_to_string(priv))));
-			privileges |= priv;
-		}
-	}
+	relation = heap_open(ProcedureRelationId, RowExclusiveLock);
 
-	foreach(i, stmt->objects)
+	foreach (cell, objects)
 	{
-		FuncWithArgs *func = (FuncWithArgs *) lfirst(i);
-		Oid			oid;
-		Relation	relation;
-		HeapTuple	tuple;
+		Oid			funcId = lfirst_oid(cell);
 		Form_pg_proc pg_proc_tuple;
 		Datum		aclDatum;
 		bool		isNull;
@@ -591,6 +798,7 @@ ExecuteGrantStmt_Function(GrantStmt *stmt)
 		Acl		   *new_acl;
 		Oid			grantorId;
 		Oid			ownerId;
+		HeapTuple	tuple;
 		HeapTuple	newtuple;
 		Datum		values[Natts_pg_proc];
 		char		nulls[Natts_pg_proc];
@@ -600,14 +808,12 @@ ExecuteGrantStmt_Function(GrantStmt *stmt)
 		Oid		   *oldmembers;
 		Oid		   *newmembers;
 
-		oid = LookupFuncNameTypeNames(func->funcname, func->funcargs, false);
-
-		relation = heap_open(ProcedureRelationId, RowExclusiveLock);
 		tuple = SearchSysCache(PROCOID,
-							   ObjectIdGetDatum(oid),
+							   ObjectIdGetDatum(funcId),
 							   0, 0, 0);
 		if (!HeapTupleIsValid(tuple))
-			elog(ERROR, "cache lookup failed for function %u", oid);
+			elog(ERROR, "cache lookup failed for function %u", funcId);
+
 		pg_proc_tuple = (Form_pg_proc) GETSTRUCT(tuple);
 
 		/*
@@ -634,7 +840,7 @@ ExecuteGrantStmt_Function(GrantStmt *stmt)
 		 */
 		if (avail_goptions == ACL_NO_RIGHTS)
 		{
-			if (pg_proc_aclmask(oid,
+			if (pg_proc_aclmask(funcId,
 								grantorId,
 								ACL_ALL_RIGHTS_FUNCTION | ACL_GRANT_OPTION_FOR(ACL_ALL_RIGHTS_FUNCTION),
 								ACLMASK_ANY) == ACL_NO_RIGHTS)
@@ -651,7 +857,7 @@ ExecuteGrantStmt_Function(GrantStmt *stmt)
 		 * GRANT case.)
 		 */
 		this_privileges = privileges & ACL_OPTION_TO_PRIVS(avail_goptions);
-		if (stmt->is_grant)
+		if (is_grant)
 		{
 			if (this_privileges == 0)
 				ereport(WARNING,
@@ -682,9 +888,9 @@ ExecuteGrantStmt_Function(GrantStmt *stmt)
 		 */
 		noldmembers = aclmembers(old_acl, &oldmembers);
 
-		new_acl = merge_acl_with_grant(old_acl, stmt->is_grant,
-									   stmt->grant_option, stmt->behavior,
-									   stmt->grantees, this_privileges,
+		new_acl = merge_acl_with_grant(old_acl, is_grant,
+									   grant_option, behavior,
+									   grantees, this_privileges,
 									   grantorId, ownerId);
 
 		nnewmembers = aclmembers(new_acl, &newmembers);
@@ -697,7 +903,8 @@ ExecuteGrantStmt_Function(GrantStmt *stmt)
 		replaces[Anum_pg_proc_proacl - 1] = 'r';
 		values[Anum_pg_proc_proacl - 1] = PointerGetDatum(new_acl);
 
-		newtuple = heap_modifytuple(tuple, RelationGetDescr(relation), values, nulls, replaces);
+		newtuple = heap_modifytuple(tuple, RelationGetDescr(relation), values,
+									nulls, replaces);
 
 		simple_heap_update(relation, &newtuple->t_self, newtuple);
 
@@ -705,8 +912,8 @@ ExecuteGrantStmt_Function(GrantStmt *stmt)
 		CatalogUpdateIndexes(relation, newtuple);
 
 		/* Update the shared dependency ACL info */
-		updateAclDependencies(ProcedureRelationId, oid,
-							  ownerId, stmt->is_grant,
+		updateAclDependencies(ProcedureRelationId, funcId,	
+							  ownerId, is_grant,
 							  noldmembers, oldmembers,
 							  nnewmembers, newmembers);
 
@@ -714,48 +921,29 @@ ExecuteGrantStmt_Function(GrantStmt *stmt)
 
 		pfree(new_acl);
 
-		heap_close(relation, RowExclusiveLock);
-
 		/* prevent error when processing duplicate objects */
 		CommandCounterIncrement();
 	}
+
+	heap_close(relation, RowExclusiveLock);
 }
 
 static void
-ExecuteGrantStmt_Language(GrantStmt *stmt)
+ExecGrant_Language(bool is_grant, List *objects, bool all_privs,
+				   AclMode privileges, List *grantees, bool grant_option,
+				   DropBehavior behavior)
 {
-	AclMode		privileges;
-	bool		all_privs;
-	ListCell   *i;
+	Relation	relation;
+	ListCell   *cell;
 
-	if (stmt->privileges == NIL)
-	{
-		all_privs = true;
+	if (all_privs && privileges == ACL_NO_RIGHTS)
 		privileges = ACL_ALL_RIGHTS_LANGUAGE;
-	}
-	else
-	{
-		all_privs = false;
-		privileges = ACL_NO_RIGHTS;
-		foreach(i, stmt->privileges)
-		{
-			char	   *privname = strVal(lfirst(i));
-			AclMode		priv = string_to_privilege(privname);
 
-			if (priv & ~((AclMode) ACL_ALL_RIGHTS_LANGUAGE))
-				ereport(ERROR,
-						(errcode(ERRCODE_INVALID_GRANT_OPERATION),
-						 errmsg("invalid privilege type %s for language",
-								privilege_to_string(priv))));
-			privileges |= priv;
-		}
-	}
+	relation = heap_open(LanguageRelationId, RowExclusiveLock);
 
-	foreach(i, stmt->objects)
+	foreach (cell, objects)
 	{
-		char	   *langname = strVal(lfirst(i));
-		Relation	relation;
-		HeapTuple	tuple;
+		Oid			langid = lfirst_oid(cell);
 		Form_pg_language pg_language_tuple;
 		Datum		aclDatum;
 		bool		isNull;
@@ -765,6 +953,7 @@ ExecuteGrantStmt_Language(GrantStmt *stmt)
 		Acl		   *new_acl;
 		Oid			grantorId;
 		Oid			ownerId;
+		HeapTuple	tuple;
 		HeapTuple	newtuple;
 		Datum		values[Natts_pg_language];
 		char		nulls[Natts_pg_language];
@@ -774,21 +963,20 @@ ExecuteGrantStmt_Language(GrantStmt *stmt)
 		Oid		   *oldmembers;
 		Oid		   *newmembers;
 
-		relation = heap_open(LanguageRelationId, RowExclusiveLock);
-		tuple = SearchSysCache(LANGNAME,
-							   PointerGetDatum(langname),
+		tuple = SearchSysCache(LANGOID,
+							   ObjectIdGetDatum(langid),
 							   0, 0, 0);
 		if (!HeapTupleIsValid(tuple))
-			ereport(ERROR,
-					(errcode(ERRCODE_UNDEFINED_OBJECT),
-					 errmsg("language \"%s\" does not exist", langname)));
+			elog(ERROR, "cache lookup failed for language %u", langid);
+
 		pg_language_tuple = (Form_pg_language) GETSTRUCT(tuple);
 
 		if (!pg_language_tuple->lanpltrusted)
 			ereport(ERROR,
 					(errcode(ERRCODE_WRONG_OBJECT_TYPE),
-					 errmsg("language \"%s\" is not trusted", langname),
-				   errhint("Only superusers may use untrusted languages.")));
+					 errmsg("language \"%s\" is not trusted",
+							NameStr(pg_language_tuple->lanname)),
+					 errhint("Only superusers may use untrusted languages.")));
 
 		/*
 		 * Get owner ID and working copy of existing ACL. If there's no ACL,
@@ -834,7 +1022,7 @@ ExecuteGrantStmt_Language(GrantStmt *stmt)
 		 * GRANT case.)
 		 */
 		this_privileges = privileges & ACL_OPTION_TO_PRIVS(avail_goptions);
-		if (stmt->is_grant)
+		if (is_grant)
 		{
 			if (this_privileges == 0)
 				ereport(WARNING,
@@ -865,9 +1053,9 @@ ExecuteGrantStmt_Language(GrantStmt *stmt)
 		 */
 		noldmembers = aclmembers(old_acl, &oldmembers);
 
-		new_acl = merge_acl_with_grant(old_acl, stmt->is_grant,
-									   stmt->grant_option, stmt->behavior,
-									   stmt->grantees, this_privileges,
+		new_acl = merge_acl_with_grant(old_acl, is_grant,
+									   grant_option, behavior,
+									   grantees, this_privileges,
 									   grantorId, ownerId);
 
 		nnewmembers = aclmembers(new_acl, &newmembers);
@@ -880,7 +1068,8 @@ ExecuteGrantStmt_Language(GrantStmt *stmt)
 		replaces[Anum_pg_language_lanacl - 1] = 'r';
 		values[Anum_pg_language_lanacl - 1] = PointerGetDatum(new_acl);
 
-		newtuple = heap_modifytuple(tuple, RelationGetDescr(relation), values, nulls, replaces);
+		newtuple = heap_modifytuple(tuple, RelationGetDescr(relation), values,
+									nulls, replaces);
 
 		simple_heap_update(relation, &newtuple->t_self, newtuple);
 
@@ -889,7 +1078,7 @@ ExecuteGrantStmt_Language(GrantStmt *stmt)
 
 		/* Update the shared dependency ACL info */
 		updateAclDependencies(LanguageRelationId, HeapTupleGetOid(tuple),
-							  ownerId, stmt->is_grant,
+							  ownerId, is_grant,
 							  noldmembers, oldmembers,
 							  nnewmembers, newmembers);
 
@@ -897,48 +1086,29 @@ ExecuteGrantStmt_Language(GrantStmt *stmt)
 
 		pfree(new_acl);
 
-		heap_close(relation, RowExclusiveLock);
-
 		/* prevent error when processing duplicate objects */
 		CommandCounterIncrement();
 	}
+
+	heap_close(relation, RowExclusiveLock);
 }
 
 static void
-ExecuteGrantStmt_Namespace(GrantStmt *stmt)
+ExecGrant_Namespace(bool is_grant, List *objects, bool all_privs,
+ 					AclMode privileges, List *grantees, bool grant_option,
+ 					DropBehavior behavior)
 {
-	AclMode		privileges;
-	bool		all_privs;
-	ListCell   *i;
+	Relation	relation;
+	ListCell   *cell;
 
-	if (stmt->privileges == NIL)
-	{
-		all_privs = true;
+	if (all_privs && privileges == ACL_NO_RIGHTS)
 		privileges = ACL_ALL_RIGHTS_NAMESPACE;
-	}
-	else
-	{
-		all_privs = false;
-		privileges = ACL_NO_RIGHTS;
-		foreach(i, stmt->privileges)
-		{
-			char	   *privname = strVal(lfirst(i));
-			AclMode		priv = string_to_privilege(privname);
 
-			if (priv & ~((AclMode) ACL_ALL_RIGHTS_NAMESPACE))
-				ereport(ERROR,
-						(errcode(ERRCODE_INVALID_GRANT_OPERATION),
-						 errmsg("invalid privilege type %s for schema",
-								privilege_to_string(priv))));
-			privileges |= priv;
-		}
-	}
+	relation = heap_open(NamespaceRelationId, RowExclusiveLock);
 
-	foreach(i, stmt->objects)
+	foreach(cell, objects)
 	{
-		char	   *nspname = strVal(lfirst(i));
-		Relation	relation;
-		HeapTuple	tuple;
+		Oid			nspid = lfirst_oid(cell);
 		Form_pg_namespace pg_namespace_tuple;
 		Datum		aclDatum;
 		bool		isNull;
@@ -948,6 +1118,7 @@ ExecuteGrantStmt_Namespace(GrantStmt *stmt)
 		Acl		   *new_acl;
 		Oid			grantorId;
 		Oid			ownerId;
+		HeapTuple	tuple;
 		HeapTuple	newtuple;
 		Datum		values[Natts_pg_namespace];
 		char		nulls[Natts_pg_namespace];
@@ -957,14 +1128,12 @@ ExecuteGrantStmt_Namespace(GrantStmt *stmt)
 		Oid		   *oldmembers;
 		Oid		   *newmembers;
 
-		relation = heap_open(NamespaceRelationId, RowExclusiveLock);
-		tuple = SearchSysCache(NAMESPACENAME,
-							   CStringGetDatum(nspname),
+		tuple = SearchSysCache(NAMESPACEOID,
+							   ObjectIdGetDatum(nspid),
 							   0, 0, 0);
 		if (!HeapTupleIsValid(tuple))
-			ereport(ERROR,
-					(errcode(ERRCODE_UNDEFINED_SCHEMA),
-					 errmsg("schema \"%s\" does not exist", nspname)));
+			elog(ERROR, "cache lookup failed for namespace %u", nspid);
+
 		pg_namespace_tuple = (Form_pg_namespace) GETSTRUCT(tuple);
 
 		/*
@@ -997,7 +1166,7 @@ ExecuteGrantStmt_Namespace(GrantStmt *stmt)
 									 ACL_ALL_RIGHTS_NAMESPACE | ACL_GRANT_OPTION_FOR(ACL_ALL_RIGHTS_NAMESPACE),
 									 ACLMASK_ANY) == ACL_NO_RIGHTS)
 				aclcheck_error(ACLCHECK_NO_PRIV, ACL_KIND_NAMESPACE,
-							   nspname);
+							   NameStr(pg_namespace_tuple->nspname));
 		}
 
 		/*
@@ -1009,7 +1178,7 @@ ExecuteGrantStmt_Namespace(GrantStmt *stmt)
 		 * GRANT case.)
 		 */
 		this_privileges = privileges & ACL_OPTION_TO_PRIVS(avail_goptions);
-		if (stmt->is_grant)
+		if (is_grant)
 		{
 			if (this_privileges == 0)
 				ereport(WARNING,
@@ -1040,9 +1209,9 @@ ExecuteGrantStmt_Namespace(GrantStmt *stmt)
 		 */
 		noldmembers = aclmembers(old_acl, &oldmembers);
 
-		new_acl = merge_acl_with_grant(old_acl, stmt->is_grant,
-									   stmt->grant_option, stmt->behavior,
-									   stmt->grantees, this_privileges,
+		new_acl = merge_acl_with_grant(old_acl, is_grant,
+									   grant_option, behavior,
+									   grantees, this_privileges,
 									   grantorId, ownerId);
 
 		nnewmembers = aclmembers(new_acl, &newmembers);
@@ -1055,7 +1224,8 @@ ExecuteGrantStmt_Namespace(GrantStmt *stmt)
 		replaces[Anum_pg_namespace_nspacl - 1] = 'r';
 		values[Anum_pg_namespace_nspacl - 1] = PointerGetDatum(new_acl);
 
-		newtuple = heap_modifytuple(tuple, RelationGetDescr(relation), values, nulls, replaces);
+		newtuple = heap_modifytuple(tuple, RelationGetDescr(relation), values,
+									nulls, replaces);
 
 		simple_heap_update(relation, &newtuple->t_self, newtuple);
 
@@ -1064,7 +1234,7 @@ ExecuteGrantStmt_Namespace(GrantStmt *stmt)
 
 		/* Update the shared dependency ACL info */
 		updateAclDependencies(NamespaceRelationId, HeapTupleGetOid(tuple),
-							  ownerId, stmt->is_grant,
+							  ownerId, is_grant,
 							  noldmembers, oldmembers,
 							  nnewmembers, newmembers);
 
@@ -1072,50 +1242,29 @@ ExecuteGrantStmt_Namespace(GrantStmt *stmt)
 
 		pfree(new_acl);
 
-		heap_close(relation, RowExclusiveLock);
-
 		/* prevent error when processing duplicate objects */
 		CommandCounterIncrement();
 	}
+
+	heap_close(relation, RowExclusiveLock);
 }
 
 static void
-ExecuteGrantStmt_Tablespace(GrantStmt *stmt)
+ExecGrant_Tablespace(bool is_grant, List *objects, bool all_privs,
+					 AclMode privileges, List *grantees, bool grant_option,
+					 DropBehavior behavior)
 {
-	AclMode		privileges;
-	bool		all_privs;
-	ListCell   *i;
+	Relation	relation;
+	ListCell   *cell;
 
-	if (stmt->privileges == NIL)
-	{
-		all_privs = true;
+	if (all_privs && privileges == ACL_NO_RIGHTS)
 		privileges = ACL_ALL_RIGHTS_TABLESPACE;
-	}
-	else
-	{
-		all_privs = false;
-		privileges = ACL_NO_RIGHTS;
-		foreach(i, stmt->privileges)
-		{
-			char	   *privname = strVal(lfirst(i));
-			AclMode		priv = string_to_privilege(privname);
 
-			if (priv & ~((AclMode) ACL_ALL_RIGHTS_TABLESPACE))
-				ereport(ERROR,
-						(errcode(ERRCODE_INVALID_GRANT_OPERATION),
-						 errmsg("invalid privilege type %s for tablespace",
-								privilege_to_string(priv))));
-			privileges |= priv;
-		}
-	}
+	relation = heap_open(TableSpaceRelationId, RowExclusiveLock);
 
-	foreach(i, stmt->objects)
+	foreach(cell, objects)
 	{
-		char	   *spcname = strVal(lfirst(i));
-		Relation	relation;
-		ScanKeyData entry[1];
-		HeapScanDesc scan;
-		HeapTuple	tuple;
+		Oid			tblId = lfirst_oid(cell);
 		Form_pg_tablespace pg_tablespace_tuple;
 		Datum		aclDatum;
 		bool		isNull;
@@ -1133,18 +1282,21 @@ ExecuteGrantStmt_Tablespace(GrantStmt *stmt)
 		int			nnewmembers;
 		Oid		   *oldmembers;
 		Oid		   *newmembers;
+		ScanKeyData	entry[1];
+		SysScanDesc scan;
+		HeapTuple	tuple;
 
-		relation = heap_open(TableSpaceRelationId, RowExclusiveLock);
+		/* There's no syscache for pg_tablespace, so must look the hard way */
 		ScanKeyInit(&entry[0],
-					Anum_pg_tablespace_spcname,
-					BTEqualStrategyNumber, F_NAMEEQ,
-					CStringGetDatum(spcname));
-		scan = heap_beginscan(relation, SnapshotNow, 1, entry);
-		tuple = heap_getnext(scan, ForwardScanDirection);
+					ObjectIdAttributeNumber,
+					BTEqualStrategyNumber, F_OIDEQ,
+					ObjectIdGetDatum(tblId));
+		scan = systable_beginscan(relation, TablespaceOidIndexId, true,
+								  SnapshotNow, 1, entry);
+		tuple = systable_getnext(scan);
 		if (!HeapTupleIsValid(tuple))
-			ereport(ERROR,
-					(errcode(ERRCODE_UNDEFINED_OBJECT),
-					 errmsg("tablespace \"%s\" does not exist", spcname)));
+			elog(ERROR, "cache lookup failed for tablespace %u", tblId);
+
 		pg_tablespace_tuple = (Form_pg_tablespace) GETSTRUCT(tuple);
 
 		/*
@@ -1176,7 +1328,7 @@ ExecuteGrantStmt_Tablespace(GrantStmt *stmt)
 									  ACL_ALL_RIGHTS_TABLESPACE | ACL_GRANT_OPTION_FOR(ACL_ALL_RIGHTS_TABLESPACE),
 									  ACLMASK_ANY) == ACL_NO_RIGHTS)
 				aclcheck_error(ACLCHECK_NO_PRIV, ACL_KIND_TABLESPACE,
-							   spcname);
+							   NameStr(pg_tablespace_tuple->spcname));
 		}
 
 		/*
@@ -1188,7 +1340,7 @@ ExecuteGrantStmt_Tablespace(GrantStmt *stmt)
 		 * GRANT case.)
 		 */
 		this_privileges = privileges & ACL_OPTION_TO_PRIVS(avail_goptions);
-		if (stmt->is_grant)
+		if (is_grant)
 		{
 			if (this_privileges == 0)
 				ereport(WARNING,
@@ -1219,9 +1371,9 @@ ExecuteGrantStmt_Tablespace(GrantStmt *stmt)
 		 */
 		noldmembers = aclmembers(old_acl, &oldmembers);
 
-		new_acl = merge_acl_with_grant(old_acl, stmt->is_grant,
-									   stmt->grant_option, stmt->behavior,
-									   stmt->grantees, this_privileges,
+		new_acl = merge_acl_with_grant(old_acl, is_grant,
+									   grant_option, behavior,
+									   grantees, this_privileges,
 									   grantorId, ownerId);
 
 		nnewmembers = aclmembers(new_acl, &newmembers);
@@ -1234,7 +1386,8 @@ ExecuteGrantStmt_Tablespace(GrantStmt *stmt)
 		replaces[Anum_pg_tablespace_spcacl - 1] = 'r';
 		values[Anum_pg_tablespace_spcacl - 1] = PointerGetDatum(new_acl);
 
-		newtuple = heap_modifytuple(tuple, RelationGetDescr(relation), values, nulls, replaces);
+		newtuple = heap_modifytuple(tuple, RelationGetDescr(relation), values,
+									nulls, replaces);
 
 		simple_heap_update(relation, &newtuple->t_self, newtuple);
 
@@ -1242,19 +1395,20 @@ ExecuteGrantStmt_Tablespace(GrantStmt *stmt)
 		CatalogUpdateIndexes(relation, newtuple);
 
 		/* Update the shared dependency ACL info */
-		updateAclDependencies(TableSpaceRelationId, HeapTupleGetOid(tuple),
-							  ownerId, stmt->is_grant,
+		updateAclDependencies(TableSpaceRelationId, tblId,
+							  ownerId, is_grant,
 							  noldmembers, oldmembers,
 							  nnewmembers, newmembers);
 
-		pfree(new_acl);
+		systable_endscan(scan);
 
-		heap_endscan(scan);
-		heap_close(relation, RowExclusiveLock);
+		pfree(new_acl);
 
 		/* prevent error when processing duplicate objects */
 		CommandCounterIncrement();
 	}
+
+	heap_close(relation, RowExclusiveLock);
 }
 
 
@@ -1537,7 +1691,7 @@ pg_database_aclmask(Oid db_oid, Oid roleid,
 	AclMode		result;
 	Relation	pg_database;
 	ScanKeyData entry[1];
-	HeapScanDesc scan;
+	SysScanDesc	scan;
 	HeapTuple	tuple;
 	Datum		aclDatum;
 	bool		isNull;
@@ -1558,8 +1712,9 @@ pg_database_aclmask(Oid db_oid, Oid roleid,
 				ObjectIdAttributeNumber,
 				BTEqualStrategyNumber, F_OIDEQ,
 				ObjectIdGetDatum(db_oid));
-	scan = heap_beginscan(pg_database, SnapshotNow, 1, entry);
-	tuple = heap_getnext(scan, ForwardScanDirection);
+	scan = systable_beginscan(pg_database, DatabaseOidIndexId, true,
+							  SnapshotNow, 1, entry);
+	tuple = systable_getnext(scan);
 	if (!HeapTupleIsValid(tuple))
 		ereport(ERROR,
 				(errcode(ERRCODE_UNDEFINED_DATABASE),
@@ -1588,7 +1743,7 @@ pg_database_aclmask(Oid db_oid, Oid roleid,
 	if (acl && (Pointer) acl != DatumGetPointer(aclDatum))
 		pfree(acl);
 
-	heap_endscan(scan);
+	systable_endscan(scan);
 	heap_close(pg_database, AccessShareLock);
 
 	return result;
@@ -1801,7 +1956,7 @@ pg_tablespace_aclmask(Oid spc_oid, Oid roleid,
 	AclMode		result;
 	Relation	pg_tablespace;
 	ScanKeyData entry[1];
-	HeapScanDesc scan;
+	SysScanDesc	scan;
 	HeapTuple	tuple;
 	Datum		aclDatum;
 	bool		isNull;
@@ -1829,8 +1984,9 @@ pg_tablespace_aclmask(Oid spc_oid, Oid roleid,
 				ObjectIdAttributeNumber,
 				BTEqualStrategyNumber, F_OIDEQ,
 				ObjectIdGetDatum(spc_oid));
-	scan = heap_beginscan(pg_tablespace, SnapshotNow, 1, entry);
-	tuple = heap_getnext(scan, ForwardScanDirection);
+	scan = systable_beginscan(pg_tablespace, TablespaceOidIndexId, true,
+							  SnapshotNow, 1, entry);
+	tuple = systable_getnext(scan);
 	if (!HeapTupleIsValid(tuple))
 		ereport(ERROR,
 				(errcode(ERRCODE_UNDEFINED_OBJECT),
@@ -1859,7 +2015,7 @@ pg_tablespace_aclmask(Oid spc_oid, Oid roleid,
 	if (acl && (Pointer) acl != DatumGetPointer(aclDatum))
 		pfree(acl);
 
-	heap_endscan(scan);
+	systable_endscan(scan);
 	heap_close(pg_tablespace, AccessShareLock);
 
 	return result;
@@ -2091,7 +2247,7 @@ pg_tablespace_ownercheck(Oid spc_oid, Oid roleid)
 {
 	Relation	pg_tablespace;
 	ScanKeyData entry[1];
-	HeapScanDesc scan;
+	SysScanDesc	scan;
 	HeapTuple	spctuple;
 	Oid			spcowner;
 
@@ -2105,9 +2261,10 @@ pg_tablespace_ownercheck(Oid spc_oid, Oid roleid)
 				ObjectIdAttributeNumber,
 				BTEqualStrategyNumber, F_OIDEQ,
 				ObjectIdGetDatum(spc_oid));
-	scan = heap_beginscan(pg_tablespace, SnapshotNow, 1, entry);
+	scan = systable_beginscan(pg_tablespace, TablespaceOidIndexId, true,
+							  SnapshotNow, 1, entry);
 
-	spctuple = heap_getnext(scan, ForwardScanDirection);
+	spctuple = systable_getnext(scan);
 
 	if (!HeapTupleIsValid(spctuple))
 		ereport(ERROR,
@@ -2116,7 +2273,7 @@ pg_tablespace_ownercheck(Oid spc_oid, Oid roleid)
 
 	spcowner = ((Form_pg_tablespace) GETSTRUCT(spctuple))->spcowner;
 
-	heap_endscan(scan);
+	systable_endscan(scan);
 	heap_close(pg_tablespace, AccessShareLock);
 
 	return has_privs_of_role(roleid, spcowner);
@@ -2159,7 +2316,7 @@ pg_database_ownercheck(Oid db_oid, Oid roleid)
 {
 	Relation	pg_database;
 	ScanKeyData entry[1];
-	HeapScanDesc scan;
+	SysScanDesc	scan;
 	HeapTuple	dbtuple;
 	Oid			dba;
 
@@ -2173,9 +2330,10 @@ pg_database_ownercheck(Oid db_oid, Oid roleid)
 				ObjectIdAttributeNumber,
 				BTEqualStrategyNumber, F_OIDEQ,
 				ObjectIdGetDatum(db_oid));
-	scan = heap_beginscan(pg_database, SnapshotNow, 1, entry);
+	scan = systable_beginscan(pg_database, DatabaseOidIndexId, true,
+							  SnapshotNow, 1, entry);
 
-	dbtuple = heap_getnext(scan, ForwardScanDirection);
+	dbtuple = systable_getnext(scan);
 
 	if (!HeapTupleIsValid(dbtuple))
 		ereport(ERROR,
@@ -2184,7 +2342,7 @@ pg_database_ownercheck(Oid db_oid, Oid roleid)
 
 	dba = ((Form_pg_database) GETSTRUCT(dbtuple))->datdba;
 
-	heap_endscan(scan);
+	systable_endscan(scan);
 	heap_close(pg_database, AccessShareLock);
 
 	return has_privs_of_role(roleid, dba);
diff --git a/src/backend/catalog/pg_depend.c b/src/backend/catalog/pg_depend.c
index c8f9e53212d1be5498ac7b7f99c83e4b7508ec54..848183dae5dc558448987d7a80ba86ad703f2d6d 100644
--- a/src/backend/catalog/pg_depend.c
+++ b/src/backend/catalog/pg_depend.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/catalog/pg_depend.c,v 1.15 2005/10/15 02:49:14 momjian Exp $
+ *	  $PostgreSQL: pgsql/src/backend/catalog/pg_depend.c,v 1.16 2005/11/21 12:49:30 alvherre Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -162,6 +162,58 @@ deleteDependencyRecordsFor(Oid classId, Oid objectId)
 	return count;
 }
 
+/*
+ * objectIsInternalDependency -- return whether the specified object
+ * is listed as an internal dependency for some other object.
+ *
+ * This is used to implement DROP/REASSIGN OWNED.  We cannot invoke
+ * performDeletion blindly, because it may try to drop or modify an internal-
+ * dependent object before the "main" object, so we need to skip the first
+ * object and expect it to be automatically dropped when the main object is
+ * dropped.
+ */
+bool
+objectIsInternalDependency(Oid classId, Oid objectId)
+{
+	Relation	depRel;
+	ScanKeyData	key[2];
+	SysScanDesc	scan;
+	HeapTuple	tup;
+	bool		isdep = false;
+
+	depRel = heap_open(DependRelationId, AccessShareLock);
+
+	ScanKeyInit(&key[0],
+				Anum_pg_depend_classid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(classId));
+	ScanKeyInit(&key[1],
+				Anum_pg_depend_objid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(objectId));
+
+	scan = systable_beginscan(depRel, DependDependerIndexId, true,
+							  SnapshotNow, 2, key);
+
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_depend depForm = (Form_pg_depend) GETSTRUCT(tup);
+
+		if (depForm->deptype == DEPENDENCY_INTERNAL)
+		{
+			/* No need to keep scanning */
+			isdep = true;
+			break;
+		}
+	}
+
+	systable_endscan(scan);
+
+	heap_close(depRel, AccessShareLock);
+
+	return isdep;
+}
+
 /*
  * Adjust dependency record(s) to point to a different object of the same type
  *
diff --git a/src/backend/catalog/pg_shdepend.c b/src/backend/catalog/pg_shdepend.c
index 4cce7ba13cff36c37f8367b2c3fdef31a41a380a..6bd69cdef6c1b85ba98c3cb69d4991b65e5fe29f 100644
--- a/src/backend/catalog/pg_shdepend.c
+++ b/src/backend/catalog/pg_shdepend.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/catalog/pg_shdepend.c,v 1.3 2005/10/15 02:49:14 momjian Exp $
+ *	  $PostgreSQL: pgsql/src/backend/catalog/pg_shdepend.c,v 1.4 2005/11/21 12:49:30 alvherre Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -16,11 +16,24 @@
 
 #include "access/genam.h"
 #include "access/heapam.h"
+#include "utils/acl.h"
 #include "catalog/dependency.h"
 #include "catalog/indexing.h"
 #include "catalog/pg_authid.h"
+#include "catalog/pg_conversion.h"
 #include "catalog/pg_database.h"
+#include "catalog/pg_language.h"
+#include "catalog/pg_namespace.h"
+#include "catalog/pg_operator.h"
+#include "catalog/pg_proc.h"
 #include "catalog/pg_shdepend.h"
+#include "catalog/pg_tablespace.h"
+#include "catalog/pg_type.h"
+#include "commands/conversioncmds.h"
+#include "commands/defrem.h"
+#include "commands/schemacmds.h"
+#include "commands/tablecmds.h"
+#include "commands/typecmds.h"
 #include "lib/stringinfo.h"
 #include "miscadmin.h"
 #include "utils/fmgroids.h"
@@ -1042,3 +1055,251 @@ isSharedObjectPinned(Oid classId, Oid objectId, Relation sdepRel)
 
 	return result;
 }
+
+/*
+ * shdepDropOwned
+ *
+ * Drop the objects owned by any one of the given RoleIds.  If a role has
+ * access to an object, the grant will be removed as well (but the object
+ * will not, of course.)
+ */
+void
+shdepDropOwned(List *roleids, DropBehavior behavior)
+{
+	Relation	sdepRel;
+	ListCell   *cell;
+
+	sdepRel = heap_open(SharedDependRelationId, AccessExclusiveLock);
+
+	/*
+	 * For each role, find the dependent objects and drop them using the
+	 * regular (non-shared) dependency management.
+	 */
+	foreach(cell, roleids)
+	{
+		Oid			roleid = lfirst_oid(cell);
+		ScanKeyData	key[2];
+		SysScanDesc	scan;
+		HeapTuple	tuple;
+
+		/* Doesn't work for pinned objects */
+		if (isSharedObjectPinned(AuthIdRelationId, roleid, sdepRel))
+		{
+			ObjectAddress obj;
+
+			obj.classId = AuthIdRelationId;
+			obj.objectId = roleid;
+			obj.objectSubId = 0;
+
+			ereport(ERROR,
+					(errcode(ERRCODE_DEPENDENT_OBJECTS_STILL_EXIST),
+					 errmsg("cannot drop objects owned by %s because they are "
+							"required by the database system",
+							getObjectDescription(&obj))));
+		}
+
+		ScanKeyInit(&key[0],
+					Anum_pg_shdepend_refclassid,
+					BTEqualStrategyNumber, F_OIDEQ,
+					ObjectIdGetDatum(AuthIdRelationId));
+		ScanKeyInit(&key[1],
+					Anum_pg_shdepend_refobjid,
+					BTEqualStrategyNumber, F_OIDEQ,
+					ObjectIdGetDatum(roleid));
+
+		scan = systable_beginscan(sdepRel, SharedDependReferenceIndexId, true,
+								  SnapshotNow, 2, key);
+
+		while ((tuple = systable_getnext(scan)) != NULL)
+		{
+			Form_pg_shdepend sdepForm = (Form_pg_shdepend) GETSTRUCT(tuple);
+
+			/* We only operate on objects on the current database */
+			if (sdepForm->dbid != MyDatabaseId)
+				continue;
+
+			switch (sdepForm->deptype)
+			{
+				ObjectAddress	obj;
+				GrantObjectType	objtype;
+
+				/* Shouldn't happen */
+				case SHARED_DEPENDENCY_PIN:
+				case SHARED_DEPENDENCY_INVALID:
+					elog(ERROR, "unexpected dependency type");
+					break;
+				case SHARED_DEPENDENCY_ACL:
+					switch (sdepForm->classid)
+					{
+						case RelationRelationId:
+							objtype = ACL_OBJECT_RELATION;
+							break;
+						case DatabaseRelationId:
+							objtype = ACL_OBJECT_DATABASE;
+							break;
+						case ProcedureRelationId:
+							objtype = ACL_OBJECT_FUNCTION;
+							break;
+						case LanguageRelationId:
+							objtype = ACL_OBJECT_LANGUAGE;
+							break;
+						case NamespaceRelationId:
+							objtype = ACL_OBJECT_NAMESPACE;
+							break;
+						case TableSpaceRelationId:
+							objtype = ACL_OBJECT_TABLESPACE;
+							break;
+						default:
+							elog(ERROR, "unexpected object type %d",
+								 sdepForm->classid);
+							/* keep compiler quiet */
+							objtype = (GrantObjectType) 0;
+							break;
+					}
+
+					ExecGrantStmt_oids(false, objtype,
+									   list_make1_oid(sdepForm->objid), true,
+									   ACL_NO_RIGHTS, list_make1_oid(roleid),
+									   false, DROP_CASCADE);
+					break;
+				case SHARED_DEPENDENCY_OWNER:
+					/*
+					 * If there's a regular (non-shared) dependency on this
+					 * object marked with DEPENDENCY_INTERNAL, skip this
+					 * object.  We will drop the referencer object instead.
+					 */
+					if (objectIsInternalDependency(sdepForm->classid, sdepForm->objid))
+						continue;
+
+					/* Drop the object */
+					obj.classId = sdepForm->classid;
+					obj.objectId = sdepForm->objid;
+					obj.objectSubId = 0;
+					performDeletion(&obj, behavior);
+					break;
+			}
+		}
+
+		systable_endscan(scan);
+	}
+
+	heap_close(sdepRel, AccessExclusiveLock);
+}
+
+/*
+ * shdepReassignOwned
+ *
+ * Change the owner of objects owned by any of the roles in roleids to
+ * newrole.  Grants are not touched.
+ */
+void
+shdepReassignOwned(List *roleids, Oid newrole)
+{
+	Relation sdepRel;
+	ListCell *cell;
+
+	sdepRel = heap_open(SharedDependRelationId, AccessShareLock);
+
+	foreach(cell, roleids)
+	{
+		SysScanDesc scan;
+		ScanKeyData key[2];
+		HeapTuple	tuple;
+		Oid			roleid = lfirst_oid(cell);
+
+		/* Refuse to work on pinned roles */
+		if (isSharedObjectPinned(AuthIdRelationId, roleid, sdepRel))
+		{
+			ObjectAddress obj;
+
+			obj.classId = AuthIdRelationId;
+			obj.objectId = roleid;
+			obj.objectSubId = 0;
+
+			ereport(ERROR,
+					(errcode(ERRCODE_DEPENDENT_OBJECTS_STILL_EXIST),
+					 errmsg("cannot drop objects owned by %s because they are "
+							"required by the database system",
+							getObjectDescription(&obj))));
+			/*
+			 * There's no need to tell the whole truth, which is that we
+			 * didn't track these dependencies at all ...
+			 */
+		}
+
+		ScanKeyInit(&key[0],
+					Anum_pg_shdepend_refclassid,
+					BTEqualStrategyNumber, F_OIDEQ,
+					ObjectIdGetDatum(AuthIdRelationId));
+		ScanKeyInit(&key[1],
+					Anum_pg_shdepend_refobjid,
+					BTEqualStrategyNumber, F_OIDEQ,
+					ObjectIdGetDatum(roleid));
+		
+		scan = systable_beginscan(sdepRel, SharedDependReferenceIndexId, true,
+								  SnapshotNow, 2, key);
+
+		while ((tuple = systable_getnext(scan)) != NULL)
+		{
+			Form_pg_shdepend sdepForm = (Form_pg_shdepend) GETSTRUCT(tuple);
+
+			/* We only operate on objects on the current database */
+			if (sdepForm->dbid != MyDatabaseId)
+				continue;
+
+			/* Unexpected because we checked for pins above */
+			if (sdepForm->deptype == SHARED_DEPENDENCY_PIN)
+				elog(ERROR, "unexpected shared pin");
+
+			/* We leave non-owner dependencies alone */
+			if (sdepForm->deptype != SHARED_DEPENDENCY_OWNER)
+				continue;
+
+			/*
+			 * If there's a regular (non-shared) dependency on this
+			 * object marked with DEPENDENCY_INTERNAL, skip this
+			 * object.  We will alter the referencer object instead.
+			 */
+			if (objectIsInternalDependency(sdepForm->classid, sdepForm->objid))
+				continue;
+
+			/* Issue the appropiate ALTER OWNER call */
+			switch (sdepForm->classid)
+			{
+				case ConversionRelationId:
+					AlterConversionOwner_oid(sdepForm->objid, newrole);
+					break;
+
+				case TypeRelationId:
+					AlterTypeOwnerInternal(sdepForm->objid, newrole);
+					break;
+
+				case OperatorRelationId:
+					AlterOperatorOwner_oid(sdepForm->objid, newrole);
+					break;
+
+				case NamespaceRelationId:
+					AlterSchemaOwner_oid(sdepForm->objid, newrole);
+					break;
+
+				case RelationRelationId:
+					ATExecChangeOwner(sdepForm->objid, newrole, false);
+					break;
+
+				case ProcedureRelationId:
+					AlterFunctionOwner_oid(sdepForm->objid, newrole);
+					break;
+
+				default:
+					elog(ERROR, "unexpected classid %d", sdepForm->classid);
+					break;
+			}
+			/* Make sure the next iteration will see my changes */
+			CommandCounterIncrement();
+		}
+
+		systable_endscan(scan);
+	}
+
+	heap_close(sdepRel, AccessShareLock);
+}
diff --git a/src/backend/commands/conversioncmds.c b/src/backend/commands/conversioncmds.c
index 97abc9fc77410e29b5dc26de479dee0ddc37efc1..42bb0853a10b9e13c671333300426dac291e1f09 100644
--- a/src/backend/commands/conversioncmds.c
+++ b/src/backend/commands/conversioncmds.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/commands/conversioncmds.c,v 1.24 2005/11/19 17:39:44 adunstan Exp $
+ *	  $PostgreSQL: pgsql/src/backend/commands/conversioncmds.c,v 1.25 2005/11/21 12:49:30 alvherre Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -30,6 +30,8 @@
 #include "utils/lsyscache.h"
 #include "utils/syscache.h"
 
+static void AlterConversionOwner_internal(Relation rel, Oid conversionOid,
+									 Oid newOwnerId);
 
 /*
  * CREATE CONVERSION
@@ -185,16 +187,13 @@ RenameConversion(List *name, const char *newname)
 }
 
 /*
- * Change conversion owner
+ * Change conversion owner, by name
  */
 void
 AlterConversionOwner(List *name, Oid newOwnerId)
 {
 	Oid			conversionOid;
-	HeapTuple	tup;
 	Relation	rel;
-	Form_pg_conversion convForm;
-	AclResult	aclresult;
 
 	rel = heap_open(ConversionRelationId, RowExclusiveLock);
 
@@ -205,6 +204,40 @@ AlterConversionOwner(List *name, Oid newOwnerId)
 				 errmsg("conversion \"%s\" does not exist",
 						NameListToString(name))));
 
+	AlterConversionOwner_internal(rel, conversionOid, newOwnerId);
+
+	heap_close(rel, NoLock);
+}
+
+/*
+ * Change conversion owner, by oid
+ */
+void
+AlterConversionOwner_oid(Oid conversionOid, Oid newOwnerId)
+{
+	Relation	rel;
+
+	rel = heap_open(ConversionRelationId, RowExclusiveLock);
+	
+	AlterConversionOwner_internal(rel, conversionOid, newOwnerId);
+
+	heap_close(rel, NoLock);
+}
+
+/*
+ * AlterConversionOwner_internal
+ *
+ * Internal routine for changing the owner.  rel must be pg_conversion, already
+ * open and suitably locked; it will not be closed.
+ */
+static void
+AlterConversionOwner_internal(Relation rel, Oid conversionOid, Oid newOwnerId)
+{
+	Form_pg_conversion convForm;
+	HeapTuple		tup;
+
+	Assert(RelationGetRelid(rel) == ConversionRelationId);
+
 	tup = SearchSysCacheCopy(CONOID,
 							 ObjectIdGetDatum(conversionOid),
 							 0, 0, 0);
@@ -219,13 +252,15 @@ AlterConversionOwner(List *name, Oid newOwnerId)
 	 */
 	if (convForm->conowner != newOwnerId)
 	{
+		AclResult	aclresult;
+
 		/* Superusers can always do it */
 		if (!superuser())
 		{
 			/* Otherwise, must be owner of the existing object */
 			if (!pg_conversion_ownercheck(HeapTupleGetOid(tup), GetUserId()))
 				aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_CONVERSION,
-							   NameListToString(name));
+							   NameStr(convForm->conname));
 
 			/* Must be able to become new owner */
 			check_is_member_of_role(GetUserId(), newOwnerId);
@@ -253,6 +288,5 @@ AlterConversionOwner(List *name, Oid newOwnerId)
 								newOwnerId);
 	}
 
-	heap_close(rel, NoLock);
 	heap_freetuple(tup);
 }
diff --git a/src/backend/commands/functioncmds.c b/src/backend/commands/functioncmds.c
index f4d6164775ee48e636aa78c819b9755c1a16a96d..77f892fe501f8e77e3df70cdd0e45a287ebdfb67 100644
--- a/src/backend/commands/functioncmds.c
+++ b/src/backend/commands/functioncmds.c
@@ -10,7 +10,7 @@
  *
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/commands/functioncmds.c,v 1.69 2005/10/15 02:49:15 momjian Exp $
+ *	  $PostgreSQL: pgsql/src/backend/commands/functioncmds.c,v 1.70 2005/11/21 12:49:31 alvherre Exp $
  *
  * DESCRIPTION
  *	  These routines take the parse tree and pick out the
@@ -55,6 +55,7 @@
 #include "utils/lsyscache.h"
 #include "utils/syscache.h"
 
+static void AlterFunctionOwner_internal(Relation rel, HeapTuple tup, Oid newOwnerId);
 
 /*
  *	 Examine the RETURNS clause of the CREATE FUNCTION statement
@@ -853,16 +854,14 @@ RenameFunction(List *name, List *argtypes, const char *newname)
 }
 
 /*
- * Change function owner
+ * Change function owner by name and args
  */
 void
 AlterFunctionOwner(List *name, List *argtypes, Oid newOwnerId)
 {
+	Relation	rel;
 	Oid			procOid;
 	HeapTuple	tup;
-	Form_pg_proc procForm;
-	Relation	rel;
-	AclResult	aclresult;
 
 	rel = heap_open(ProcedureRelationId, RowExclusiveLock);
 
@@ -873,15 +872,53 @@ AlterFunctionOwner(List *name, List *argtypes, Oid newOwnerId)
 						 0, 0, 0);
 	if (!HeapTupleIsValid(tup)) /* should not happen */
 		elog(ERROR, "cache lookup failed for function %u", procOid);
-	procForm = (Form_pg_proc) GETSTRUCT(tup);
 
-	if (procForm->proisagg)
+	if (((Form_pg_proc) GETSTRUCT(tup))->proisagg)
 		ereport(ERROR,
 				(errcode(ERRCODE_WRONG_OBJECT_TYPE),
 				 errmsg("\"%s\" is an aggregate function",
 						NameListToString(name)),
 				 errhint("Use ALTER AGGREGATE to change owner of aggregate functions.")));
 
+	AlterFunctionOwner_internal(rel, tup, newOwnerId);
+
+	heap_close(rel, NoLock);
+}
+
+/*
+ * Change function owner by Oid
+ */
+void
+AlterFunctionOwner_oid(Oid procOid, Oid newOwnerId)
+{
+	Relation	rel;
+	HeapTuple	tup;
+
+	rel = heap_open(ProcedureRelationId, RowExclusiveLock);
+
+	tup = SearchSysCache(PROCOID,
+						 ObjectIdGetDatum(procOid),
+						 0, 0, 0);
+	if (!HeapTupleIsValid(tup)) /* should not happen */
+		elog(ERROR, "cache lookup failed for function %u", procOid);
+	AlterFunctionOwner_internal(rel, tup, newOwnerId);
+
+	heap_close(rel, NoLock);
+}
+
+static void
+AlterFunctionOwner_internal(Relation rel, HeapTuple tup, Oid newOwnerId)
+{
+	Form_pg_proc procForm;
+	AclResult	aclresult;
+	Oid			procOid;
+
+	Assert(RelationGetRelid(rel) == ProcedureRelationId);
+	Assert(tup->t_tableOid == ProcedureRelationId);
+
+	procForm = (Form_pg_proc) GETSTRUCT(tup);
+	procOid = HeapTupleGetOid(tup);
+
 	/*
 	 * If the new owner is the same as the existing owner, consider the
 	 * command to have succeeded.  This is for dump restoration purposes.
@@ -902,7 +939,7 @@ AlterFunctionOwner(List *name, List *argtypes, Oid newOwnerId)
 			/* Otherwise, must be owner of the existing object */
 			if (!pg_proc_ownercheck(procOid, GetUserId()))
 				aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_PROC,
-							   NameListToString(name));
+							   NameStr(procForm->proname));
 
 			/* Must be able to become new owner */
 			check_is_member_of_role(GetUserId(), newOwnerId);
@@ -937,7 +974,8 @@ AlterFunctionOwner(List *name, List *argtypes, Oid newOwnerId)
 			repl_val[Anum_pg_proc_proacl - 1] = PointerGetDatum(newAcl);
 		}
 
-		newtuple = heap_modifytuple(tup, RelationGetDescr(rel), repl_val, repl_null, repl_repl);
+		newtuple = heap_modifytuple(tup, RelationGetDescr(rel), repl_val,
+									repl_null, repl_repl);
 
 		simple_heap_update(rel, &newtuple->t_self, newtuple);
 		CatalogUpdateIndexes(rel, newtuple);
@@ -949,7 +987,6 @@ AlterFunctionOwner(List *name, List *argtypes, Oid newOwnerId)
 	}
 
 	ReleaseSysCache(tup);
-	heap_close(rel, NoLock);
 }
 
 /*
diff --git a/src/backend/commands/opclasscmds.c b/src/backend/commands/opclasscmds.c
index ea8afcfccbf8bbf249609169b5bc176e85a7b9da..10745032de2f3d460014dbce74bcca44b82ed4b0 100644
--- a/src/backend/commands/opclasscmds.c
+++ b/src/backend/commands/opclasscmds.c
@@ -9,7 +9,7 @@
  *
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/commands/opclasscmds.c,v 1.38 2005/10/15 02:49:15 momjian Exp $
+ *	  $PostgreSQL: pgsql/src/backend/commands/opclasscmds.c,v 1.39 2005/11/21 12:49:31 alvherre Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -58,6 +58,8 @@ static Oid	assignProcSubtype(Oid amoid, Oid typeoid, Oid procOid);
 static void addClassMember(List **list, OpClassMember *member, bool isProc);
 static void storeOperators(Oid opclassoid, List *operators);
 static void storeProcedures(Oid opclassoid, List *procedures);
+static void AlterOpClassOwner_internal(Relation rel, HeapTuple tuple,
+								  Oid newOwnerId);
 
 
 /*
@@ -879,20 +881,39 @@ RenameOpClass(List *name, const char *access_method, const char *newname)
 }
 
 /*
- * Change opclass owner
+ * Change opclass owner by oid
+ */
+void
+AlterOpClassOwner_oid(Oid opcOid, Oid newOwnerId)
+{
+	Relation	rel;
+	HeapTuple	tup;
+
+	rel = heap_open(OperatorClassRelationId, RowExclusiveLock);
+
+	tup = SearchSysCacheCopy(CLAOID,
+							 ObjectIdGetDatum(opcOid),
+							 0, 0, 0);
+	if (!HeapTupleIsValid(tup))		/* shouldn't happen */
+		elog(ERROR, "cache lookup failed for opclass %u", opcOid);
+
+	AlterOpClassOwner_internal(rel, tup, newOwnerId);
+
+	heap_freetuple(tup);
+	heap_close(rel, NoLock);
+}
+
+/*
+ * Change opclass owner by name
  */
 void
 AlterOpClassOwner(List *name, const char *access_method, Oid newOwnerId)
 {
-	Oid			opcOid;
 	Oid			amOid;
-	Oid			namespaceOid;
-	char	   *schemaname;
-	char	   *opcname;
-	HeapTuple	tup;
 	Relation	rel;
-	AclResult	aclresult;
-	Form_pg_opclass opcForm;
+	HeapTuple	tup;
+	char	   *opcname;
+	char	   *schemaname;
 
 	amOid = GetSysCacheOid(AMNAME,
 						   CStringGetDatum(access_method),
@@ -912,6 +933,8 @@ AlterOpClassOwner(List *name, const char *access_method, Oid newOwnerId)
 
 	if (schemaname)
 	{
+		Oid		namespaceOid;
+
 		namespaceOid = LookupExplicitNamespace(schemaname);
 
 		tup = SearchSysCacheCopy(CLAAMNAMENSP,
@@ -924,11 +947,11 @@ AlterOpClassOwner(List *name, const char *access_method, Oid newOwnerId)
 					(errcode(ERRCODE_UNDEFINED_OBJECT),
 					 errmsg("operator class \"%s\" does not exist for access method \"%s\"",
 							opcname, access_method)));
-
-		opcOid = HeapTupleGetOid(tup);
 	}
 	else
 	{
+		Oid		opcOid;
+
 		opcOid = OpclassnameGetOpcid(amOid, opcname);
 		if (!OidIsValid(opcOid))
 			ereport(ERROR,
@@ -941,10 +964,32 @@ AlterOpClassOwner(List *name, const char *access_method, Oid newOwnerId)
 								 0, 0, 0);
 		if (!HeapTupleIsValid(tup))		/* should not happen */
 			elog(ERROR, "cache lookup failed for opclass %u", opcOid);
-		namespaceOid = ((Form_pg_opclass) GETSTRUCT(tup))->opcnamespace;
 	}
+
+	AlterOpClassOwner_internal(rel, tup, newOwnerId);
+
+	heap_freetuple(tup);
+	heap_close(rel, NoLock);
+}
+
+/*
+ * The first parameter is pg_opclass, opened and suitably locked.  The second
+ * parameter is the tuple from pg_opclass we want to modify.
+ */
+static void
+AlterOpClassOwner_internal(Relation rel, HeapTuple tup, Oid newOwnerId)
+{
+	Oid			namespaceOid;
+	AclResult	aclresult;
+	Form_pg_opclass opcForm;
+
+	Assert(tup->t_tableOid == OperatorClassRelationId);
+	Assert(RelationGetRelid(rel) == OperatorClassRelationId);
+
 	opcForm = (Form_pg_opclass) GETSTRUCT(tup);
 
+	namespaceOid = opcForm->opcnamespace;
+
 	/*
 	 * If the new owner is the same as the existing owner, consider the
 	 * command to have succeeded.  This is for dump restoration purposes.
@@ -957,7 +1002,7 @@ AlterOpClassOwner(List *name, const char *access_method, Oid newOwnerId)
 			/* Otherwise, must be owner of the existing object */
 			if (!pg_opclass_ownercheck(HeapTupleGetOid(tup), GetUserId()))
 				aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_OPCLASS,
-							   NameListToString(name));
+							   NameStr(opcForm->opcname));
 
 			/* Must be able to become new owner */
 			check_is_member_of_role(GetUserId(), newOwnerId);
@@ -980,9 +1025,7 @@ AlterOpClassOwner(List *name, const char *access_method, Oid newOwnerId)
 		CatalogUpdateIndexes(rel, tup);
 
 		/* Update owner dependency reference */
-		changeDependencyOnOwner(OperatorClassRelationId, opcOid, newOwnerId);
+		changeDependencyOnOwner(OperatorClassRelationId, HeapTupleGetOid(tup),
+								newOwnerId);
 	}
-
-	heap_close(rel, NoLock);
-	heap_freetuple(tup);
 }
diff --git a/src/backend/commands/operatorcmds.c b/src/backend/commands/operatorcmds.c
index 07877962e3f152bdcc9f54e62beae8b74ddc5bdc..bcc2abe5fb718898f7103eca42594f44221d17e3 100644
--- a/src/backend/commands/operatorcmds.c
+++ b/src/backend/commands/operatorcmds.c
@@ -9,7 +9,7 @@
  *
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/commands/operatorcmds.c,v 1.26 2005/10/15 02:49:15 momjian Exp $
+ *	  $PostgreSQL: pgsql/src/backend/commands/operatorcmds.c,v 1.27 2005/11/21 12:49:31 alvherre Exp $
  *
  * DESCRIPTION
  *	  The "DefineFoo" routines take the parse tree and pick out the
@@ -48,6 +48,8 @@
 #include "utils/syscache.h"
 
 
+static void AlterOperatorOwner_internal(Relation rel, Oid operOid, Oid newOwnerId);
+
 /*
  * DefineOperator
  *		this function extracts all the information from the
@@ -260,6 +262,18 @@ RemoveOperatorById(Oid operOid)
 	heap_close(relation, RowExclusiveLock);
 }
 
+void
+AlterOperatorOwner_oid(Oid operOid, Oid newOwnerId)
+{
+	Relation	rel;
+
+	rel = heap_open(OperatorRelationId, RowExclusiveLock);
+
+	AlterOperatorOwner_internal(rel, operOid, newOwnerId);
+
+	heap_close(rel, NoLock);
+}
+
 /*
  * change operator owner
  */
@@ -268,16 +282,27 @@ AlterOperatorOwner(List *name, TypeName *typeName1, TypeName *typeName2,
 				   Oid newOwnerId)
 {
 	Oid			operOid;
-	HeapTuple	tup;
 	Relation	rel;
-	AclResult	aclresult;
-	Form_pg_operator oprForm;
 
 	rel = heap_open(OperatorRelationId, RowExclusiveLock);
 
 	operOid = LookupOperNameTypeNames(name, typeName1, typeName2,
 									  false);
 
+	AlterOperatorOwner_internal(rel, operOid, newOwnerId);
+
+	heap_close(rel, NoLock);
+}
+
+static void
+AlterOperatorOwner_internal(Relation rel, Oid operOid, Oid newOwnerId)
+{
+	HeapTuple	tup;
+	AclResult	aclresult;
+	Form_pg_operator oprForm;
+
+	Assert(RelationGetRelid(rel) == OperatorRelationId);
+
 	tup = SearchSysCacheCopy(OPEROID,
 							 ObjectIdGetDatum(operOid),
 							 0, 0, 0);
@@ -298,7 +323,7 @@ AlterOperatorOwner(List *name, TypeName *typeName1, TypeName *typeName2,
 			/* Otherwise, must be owner of the existing object */
 			if (!pg_oper_ownercheck(operOid, GetUserId()))
 				aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_OPER,
-							   NameListToString(name));
+							   NameStr(oprForm->oprname));
 
 			/* Must be able to become new owner */
 			check_is_member_of_role(GetUserId(), newOwnerId);
@@ -325,7 +350,5 @@ AlterOperatorOwner(List *name, TypeName *typeName1, TypeName *typeName2,
 		changeDependencyOnOwner(OperatorRelationId, operOid, newOwnerId);
 	}
 
-	heap_close(rel, NoLock);
 	heap_freetuple(tup);
-
 }
diff --git a/src/backend/commands/schemacmds.c b/src/backend/commands/schemacmds.c
index caa336d2c455395f12b4a830e96e6203caba0e56..7a7b930ce055c0496f5622a8d195fd3798c41709 100644
--- a/src/backend/commands/schemacmds.c
+++ b/src/backend/commands/schemacmds.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/commands/schemacmds.c,v 1.36 2005/11/19 17:39:44 adunstan Exp $
+ *	  $PostgreSQL: pgsql/src/backend/commands/schemacmds.c,v 1.37 2005/11/21 12:49:31 alvherre Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -31,6 +31,8 @@
 #include "utils/syscache.h"
 
 
+static void AlterSchemaOwner_internal(HeapTuple tup, Relation rel, Oid newOwnerId);
+
 /*
  * CREATE SCHEMA
  */
@@ -277,6 +279,28 @@ RenameSchema(const char *oldname, const char *newname)
 	heap_freetuple(tup);
 }
 
+void
+AlterSchemaOwner_oid(Oid oid, Oid newOwnerId)
+{
+	HeapTuple	tup;
+	Relation	rel;
+
+	rel = heap_open(NamespaceRelationId, RowExclusiveLock);
+
+	tup = SearchSysCache(NAMESPACEOID,
+						 ObjectIdGetDatum(oid),
+						 0, 0, 0);
+	if (!HeapTupleIsValid(tup))
+		elog(ERROR, "cache lookup failed for schema %u", oid);
+
+	AlterSchemaOwner_internal(tup, rel, newOwnerId);
+
+	ReleaseSysCache(tup);
+
+	heap_close(rel, RowExclusiveLock);
+}
+
+
 /*
  * Change schema owner
  */
@@ -285,7 +309,6 @@ AlterSchemaOwner(const char *name, Oid newOwnerId)
 {
 	HeapTuple	tup;
 	Relation	rel;
-	Form_pg_namespace nspForm;
 
 	rel = heap_open(NamespaceRelationId, RowExclusiveLock);
 
@@ -296,6 +319,22 @@ AlterSchemaOwner(const char *name, Oid newOwnerId)
 		ereport(ERROR,
 				(errcode(ERRCODE_UNDEFINED_SCHEMA),
 				 errmsg("schema \"%s\" does not exist", name)));
+
+	AlterSchemaOwner_internal(tup, rel, newOwnerId);
+
+	ReleaseSysCache(tup);
+
+	heap_close(rel, RowExclusiveLock);
+}
+
+static void
+AlterSchemaOwner_internal(HeapTuple tup, Relation rel, Oid newOwnerId)
+{
+	Form_pg_namespace nspForm;
+
+	Assert(tup->t_tableOid == NamespaceRelationId);
+	Assert(RelationGetRelid(rel) == NamespaceRelationId);
+
 	nspForm = (Form_pg_namespace) GETSTRUCT(tup);
 
 	/*
@@ -316,7 +355,7 @@ AlterSchemaOwner(const char *name, Oid newOwnerId)
 		/* Otherwise, must be owner of the existing object */
 		if (!pg_namespace_ownercheck(HeapTupleGetOid(tup), GetUserId()))
 			aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_NAMESPACE,
-						   name);
+						   NameStr(nspForm->nspname));
 
 		/* Must be able to become new owner */
 		check_is_member_of_role(GetUserId(), newOwnerId);
@@ -369,6 +408,4 @@ AlterSchemaOwner(const char *name, Oid newOwnerId)
 								newOwnerId);
 	}
 
-	ReleaseSysCache(tup);
-	heap_close(rel, NoLock);
 }
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index abec1a835d1b6352846dcc08dd7fcf1cc3041f0e..09161e0d4b914567413b0e34a668ccafb3b7865b 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/commands/tablecmds.c,v 1.174 2005/10/15 02:49:15 momjian Exp $
+ *	  $PostgreSQL: pgsql/src/backend/commands/tablecmds.c,v 1.175 2005/11/21 12:49:31 alvherre Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -236,7 +236,6 @@ static void ATExecAlterColumnType(AlteredTableInfo *tab, Relation rel,
 					  const char *colName, TypeName *typename);
 static void ATPostAlterTypeCleanup(List **wqueue, AlteredTableInfo *tab);
 static void ATPostAlterTypeParse(char *cmd, List **wqueue);
-static void ATExecChangeOwner(Oid relationOid, Oid newOwnerId, bool recursing);
 static void change_owner_recurse_to_sequences(Oid relationOid,
 								  Oid newOwnerId);
 static void ATExecClusterOn(Relation rel, const char *indexName);
@@ -5264,7 +5263,7 @@ ATPostAlterTypeParse(char *cmd, List **wqueue)
  * checks (this is necessary not just an optimization, else we'd fail to
  * handle toast tables properly).
  */
-static void
+void
 ATExecChangeOwner(Oid relationOid, Oid newOwnerId, bool recursing)
 {
 	Relation	target_rel;
diff --git a/src/backend/commands/typecmds.c b/src/backend/commands/typecmds.c
index 475c251b2fc17100e8572f4ce1ebe385aacccf80..30044f10bf129b8c4160ddf68ab3d466d584fc0c 100644
--- a/src/backend/commands/typecmds.c
+++ b/src/backend/commands/typecmds.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/commands/typecmds.c,v 1.83 2005/11/19 17:39:44 adunstan Exp $
+ *	  $PostgreSQL: pgsql/src/backend/commands/typecmds.c,v 1.84 2005/11/21 12:49:31 alvherre Exp $
  *
  * DESCRIPTION
  *	  The "DefineFoo" routines take the parse tree and pick out the
@@ -2122,7 +2122,8 @@ AlterTypeOwner(List *names, Oid newOwnerId)
  * AlterTypeOwnerInternal - change type owner unconditionally
  *
  * This is currently only used to propagate ALTER TABLE OWNER to the
- * table's rowtype.  It assumes the caller has done all needed checks.
+ * table's rowtype, and to implement REASSIGN OWNED BY.  It assumes the
+ * caller has done all needed checks.
  */
 void
 AlterTypeOwnerInternal(Oid typeOid, Oid newOwnerId)
diff --git a/src/backend/commands/user.c b/src/backend/commands/user.c
index 91befbc6aba3966277b216d59f406876c37780de..9ac3c8a97ef5a6805a1ea11c7ceac7c3ccb62514 100644
--- a/src/backend/commands/user.c
+++ b/src/backend/commands/user.c
@@ -6,7 +6,7 @@
  * Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/backend/commands/user.c,v 1.164 2005/11/04 17:25:15 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/commands/user.c,v 1.165 2005/11/21 12:49:31 alvherre Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -1118,6 +1118,67 @@ GrantRole(GrantRoleStmt *stmt)
 	auth_file_update_needed();
 }
 
+/*
+ * DropOwnedObjects
+ *
+ * Drop the objects owned by a given list of roles.
+ */
+void
+DropOwnedObjects(DropOwnedStmt *stmt)
+{
+	List	*role_ids = roleNamesToIds(stmt->roles);
+	ListCell *cell;
+
+	/* Check privileges */
+	foreach (cell, role_ids)
+	{
+		Oid	roleid = lfirst_oid(cell);
+
+		if (!has_privs_of_role(GetUserId(), roleid))
+			ereport(ERROR,
+					(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+					 errmsg("permission denied to drop objects")));
+	}
+
+	/* Ok, do it */
+	shdepDropOwned(role_ids, stmt->behavior);
+}
+
+/*
+ * ReassignOwnedObjects
+ *
+ * Give the objects owned by a given list of roles away to another user.
+ */
+void
+ReassignOwnedObjects(ReassignOwnedStmt *stmt)
+{
+	List	   *role_ids = roleNamesToIds(stmt->roles);
+	ListCell   *cell;
+	Oid			newrole;
+
+	/* Check privileges */
+	foreach (cell, role_ids)
+	{
+		Oid	roleid = lfirst_oid(cell);
+
+		if (!has_privs_of_role(GetUserId(), roleid))
+			ereport(ERROR,
+					(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+					 errmsg("permission denied to reassign objects")));
+	}
+
+	/* Must have privileges on the receiving side too */
+	newrole = get_roleid_checked(stmt->newrole);
+
+	if (!has_privs_of_role(GetUserId(), newrole))
+			ereport(ERROR,
+					(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+					 errmsg("permission denied to reassign objects")));
+
+	/* Ok, do it */
+	shdepReassignOwned(role_ids, newrole);
+}
+
 /*
  * roleNamesToIds
  *
diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c
index 1b3c6076fc049fb4388d5e5ea1e55483693ff36e..bfce72b4057bde211d0786a8c3ef15ed39fd6141 100644
--- a/src/backend/nodes/copyfuncs.c
+++ b/src/backend/nodes/copyfuncs.c
@@ -15,7 +15,7 @@
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/nodes/copyfuncs.c,v 1.318 2005/11/20 23:24:12 tgl Exp $
+ *	  $PostgreSQL: pgsql/src/backend/nodes/copyfuncs.c,v 1.319 2005/11/21 12:49:31 alvherre Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -2595,6 +2595,27 @@ _copyDeallocateStmt(DeallocateStmt *from)
 	return newnode;
 }
 
+static DropOwnedStmt *
+_copyDropOwnedStmt(DropOwnedStmt *from)
+{
+	DropOwnedStmt *newnode = makeNode(DropOwnedStmt);
+
+	COPY_NODE_FIELD(roles);
+	COPY_SCALAR_FIELD(behavior);
+	
+	return newnode;
+}
+
+static ReassignOwnedStmt *
+_copyReassignOwnedStmt(ReassignOwnedStmt *from)
+{
+	ReassignOwnedStmt *newnode = makeNode(ReassignOwnedStmt);
+
+	COPY_NODE_FIELD(roles);
+	COPY_SCALAR_FIELD(newrole);
+
+	return newnode;
+}
 
 /* ****************************************************************
  *					pg_list.h copy functions
@@ -3146,6 +3167,12 @@ copyObject(void *from)
 		case T_DeallocateStmt:
 			retval = _copyDeallocateStmt(from);
 			break;
+		case T_DropOwnedStmt:
+			retval = _copyDropOwnedStmt(from);
+			break;
+		case T_ReassignOwnedStmt:
+			retval = _copyReassignOwnedStmt(from);
+			break;
 
 		case T_A_Expr:
 			retval = _copyAExpr(from);
diff --git a/src/backend/nodes/equalfuncs.c b/src/backend/nodes/equalfuncs.c
index bc9c8f3f2a5ebf65d7bc0541d2a0b91f20256980..fa27e5871b986a06ee9cd74cd6175d4db4c49bde 100644
--- a/src/backend/nodes/equalfuncs.c
+++ b/src/backend/nodes/equalfuncs.c
@@ -18,7 +18,7 @@
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/nodes/equalfuncs.c,v 1.255 2005/11/20 23:24:12 tgl Exp $
+ *	  $PostgreSQL: pgsql/src/backend/nodes/equalfuncs.c,v 1.256 2005/11/21 12:49:31 alvherre Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -1469,10 +1469,23 @@ _equalDeallocateStmt(DeallocateStmt *a, DeallocateStmt *b)
 	return true;
 }
 
+static bool
+_equalDropOwnedStmt(DropOwnedStmt *a, DropOwnedStmt *b)
+{
+	COMPARE_NODE_FIELD(roles);
+	COMPARE_SCALAR_FIELD(behavior);
 
-/*
- * stuff from parsenodes.h
- */
+	return true;
+}
+
+static bool
+_equalReassignOwnedStmt(ReassignOwnedStmt *a, ReassignOwnedStmt *b)
+{
+	COMPARE_NODE_FIELD(roles);
+	COMPARE_NODE_FIELD(newrole);
+
+	return true;
+}
 
 static bool
 _equalAExpr(A_Expr *a, A_Expr *b)
@@ -2188,6 +2201,13 @@ equal(void *a, void *b)
 		case T_DeallocateStmt:
 			retval = _equalDeallocateStmt(a, b);
 			break;
+		case T_DropOwnedStmt:
+			retval = _equalDropOwnedStmt(a, b);
+			break;
+
+		case T_ReassignOwnedStmt:
+			retval = _equalReassignOwnedStmt(a, b);
+			break;
 
 		case T_A_Expr:
 			retval = _equalAExpr(a, b);
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index c91100abe4e0cf0ea30d996dbbff9ed47bef9902..15b079ca6ff417aa2c0eb734ef272b8a9ebb229b 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -11,7 +11,7 @@
  *
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/parser/gram.y,v 2.513 2005/11/19 17:39:44 adunstan Exp $
+ *	  $PostgreSQL: pgsql/src/backend/parser/gram.y,v 2.514 2005/11/21 12:49:31 alvherre Exp $
  *
  * HISTORY
  *	  AUTHOR			DATE			MAJOR EVENT
@@ -153,6 +153,7 @@ static void doNegateFloat(Value *v);
 		VariableResetStmt VariableSetStmt VariableShowStmt
 		ViewStmt CheckPointStmt CreateConversionStmt
 		DeallocateStmt PrepareStmt ExecuteStmt
+		DropOwnedStmt ReassignOwnedStmt
 
 %type <node>	select_no_parens select_with_parens select_clause
 				simple_select
@@ -382,7 +383,7 @@ static void doNegateFloat(Value *v);
 	NOT NOTHING NOTIFY NOTNULL NOWAIT NULL_P NULLIF NUMERIC
 
 	OBJECT_P OF OFF OFFSET OIDS OLD ON ONLY OPERATOR OPTION OR
-	ORDER OUT_P OUTER_P OVERLAPS OVERLAY OWNER
+	ORDER OUT_P OUTER_P OVERLAPS OVERLAY OWNED OWNER
 
 	PARTIAL PASSWORD PLACING POSITION
 	PRECISION PRESERVE PREPARE PREPARED PRIMARY
@@ -390,7 +391,7 @@ static void doNegateFloat(Value *v);
 
 	QUOTE
 
-	READ REAL RECHECK REFERENCES REINDEX RELATIVE_P RELEASE RENAME
+	READ REAL REASSIGN RECHECK REFERENCES REINDEX RELATIVE_P RELEASE RENAME
 	REPEATABLE REPLACE RESET RESTART RESTRICT RETURNS REVOKE RIGHT
 	ROLE ROLLBACK ROW ROWS RULE
 
@@ -533,6 +534,7 @@ stmt :
 			| DropCastStmt
 			| DropGroupStmt
 			| DropOpClassStmt
+			| DropOwnedStmt
 			| DropPLangStmt
 			| DropRuleStmt
 			| DropStmt
@@ -553,6 +555,7 @@ stmt :
 			| LockStmt
 			| NotifyStmt
 			| PrepareStmt
+			| ReassignOwnedStmt
 			| ReindexStmt
 			| RemoveAggrStmt
 			| RemoveFuncStmt
@@ -2813,6 +2816,33 @@ DropOpClassStmt:
 				}
 		;
 
+/*****************************************************************************
+ *
+ *		QUERY:
+ *
+ *		DROP OWNED BY username [, username ...] [ RESTRICT | CASCADE ]
+ *		REASSIGN OWNED BY username [, username ...] TO username
+ *
+ *****************************************************************************/
+DropOwnedStmt:
+			DROP OWNED BY name_list opt_drop_behavior
+			 	{
+					DropOwnedStmt *n = makeNode(DropOwnedStmt);
+					n->roles = $4;
+					n->behavior = $5;
+					$$ = (Node *)n;
+				}
+		;
+
+ReassignOwnedStmt:
+			REASSIGN OWNED BY name_list TO name
+				{
+					ReassignOwnedStmt *n = makeNode(ReassignOwnedStmt);
+					n->roles = $4;
+					n->newrole = $6;
+					$$ = (Node *)n;
+				}
+		;
 
 /*****************************************************************************
  *
@@ -8222,6 +8252,7 @@ unreserved_keyword:
 			| OIDS
 			| OPERATOR
 			| OPTION
+			| OWNED
 			| OWNER
 			| PARTIAL
 			| PASSWORD
@@ -8234,6 +8265,7 @@ unreserved_keyword:
 			| PROCEDURE
 			| QUOTE
 			| READ
+			| REASSIGN
 			| RECHECK
 			| REINDEX
 			| RELATIVE_P
diff --git a/src/backend/parser/keywords.c b/src/backend/parser/keywords.c
index 797ea610db5ac22f700eae06ec681e4e53e67f87..639c260b69ba894127b1bf6c24ac2fce4760c69a 100644
--- a/src/backend/parser/keywords.c
+++ b/src/backend/parser/keywords.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/parser/keywords.c,v 1.167 2005/11/19 17:39:44 adunstan Exp $
+ *	  $PostgreSQL: pgsql/src/backend/parser/keywords.c,v 1.168 2005/11/21 12:49:32 alvherre Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -252,6 +252,7 @@ static const ScanKeyword ScanKeywords[] = {
 	{"outer", OUTER_P},
 	{"overlaps", OVERLAPS},
 	{"overlay", OVERLAY},
+	{"owned", OWNED},
 	{"owner", OWNER},
 	{"partial", PARTIAL},
 	{"password", PASSWORD},
@@ -269,6 +270,7 @@ static const ScanKeyword ScanKeywords[] = {
 	{"quote", QUOTE},
 	{"read", READ},
 	{"real", REAL},
+	{"reassign", REASSIGN},
 	{"recheck", RECHECK},
 	{"references", REFERENCES},
 	{"reindex", REINDEX},
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index 75458bfce352f331b0336ef3781d9508ba2644aa..a17021eb2916550000bf24d7112652eb31b909d5 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -10,7 +10,7 @@
  *
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/tcop/utility.c,v 1.246 2005/11/19 17:39:45 adunstan Exp $
+ *	  $PostgreSQL: pgsql/src/backend/tcop/utility.c,v 1.247 2005/11/21 12:49:32 alvherre Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -347,6 +347,8 @@ check_xact_readonly(Node *parsetree)
 		case T_GrantStmt:
 		case T_GrantRoleStmt:
 		case T_TruncateStmt:
+		case T_DropOwnedStmt:
+		case T_ReassignOwnedStmt:
 			ereport(ERROR,
 					(errcode(ERRCODE_READ_ONLY_SQL_TRANSACTION),
 					 errmsg("transaction is read-only")));
@@ -725,8 +727,7 @@ ProcessUtility(Node *parsetree,
 			break;
 
 			/*
-			 * ******************************** object creation / destruction ********************************
-			 *
+			 * **************** object creation / destruction ******************
 			 */
 		case T_DefineStmt:
 			{
@@ -1019,6 +1020,14 @@ ProcessUtility(Node *parsetree,
 			DropRole((DropRoleStmt *) parsetree);
 			break;
 
+		case T_DropOwnedStmt:
+			DropOwnedObjects((DropOwnedStmt *) parsetree);
+			break;
+
+		case T_ReassignOwnedStmt:
+			ReassignOwnedObjects((ReassignOwnedStmt *) parsetree);
+			break;
+
 		case T_LockStmt:
 			LockTableCommand((LockStmt *) parsetree);
 			break;
@@ -1677,6 +1686,14 @@ CreateCommandTag(Node *parsetree)
 			tag = "DROP ROLE";
 			break;
 
+		case T_DropOwnedStmt:
+			tag = "DROP OWNED";
+			break;
+
+		case T_ReassignOwnedStmt:
+			tag = "REASSIGN OWNED";
+			break;
+
 		case T_LockStmt:
 			tag = "LOCK TABLE";
 			break;
diff --git a/src/include/catalog/dependency.h b/src/include/catalog/dependency.h
index 5c5e87c746e952c511e7ceae2c1ba197d8c1b376..c91406390b015f96fecdc1a551856e967320d6ce 100644
--- a/src/include/catalog/dependency.h
+++ b/src/include/catalog/dependency.h
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/catalog/dependency.h,v 1.17 2005/10/15 02:49:42 momjian Exp $
+ * $PostgreSQL: pgsql/src/include/catalog/dependency.h,v 1.18 2005/11/21 12:49:32 alvherre Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -177,6 +177,8 @@ extern long changeDependencyFor(Oid classId, Oid objectId,
 					Oid refClassId, Oid oldRefObjectId,
 					Oid newRefObjectId);
 
+extern bool objectIsInternalDependency(Oid classId, Oid objectId);
+
 /* in pg_shdepend.c */
 
 extern void recordSharedDependencyOn(ObjectAddress *depender,
@@ -201,4 +203,8 @@ extern void copyTemplateDependencies(Oid templateDbId, Oid newDbId);
 
 extern void dropDatabaseDependencies(Oid databaseId);
 
+extern void shdepDropOwned(List *relids, DropBehavior behavior);
+
+extern void shdepReassignOwned(List *relids, Oid newrole);
+
 #endif   /* DEPENDENCY_H */
diff --git a/src/include/commands/conversioncmds.h b/src/include/commands/conversioncmds.h
index f484ceaff2cf112dd1f212be88c973941486bf0b..05d30a31cba3ebe3bbccc31d0b3b8b8b7cdce911 100644
--- a/src/include/commands/conversioncmds.h
+++ b/src/include/commands/conversioncmds.h
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/commands/conversioncmds.h,v 1.11 2005/11/19 17:39:45 adunstan Exp $
+ * $PostgreSQL: pgsql/src/include/commands/conversioncmds.h,v 1.12 2005/11/21 12:49:32 alvherre Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -22,5 +22,6 @@ extern void DropConversionCommand(List *conversion_name,
 								  DropBehavior behavior, bool missing_ok);
 extern void RenameConversion(List *name, const char *newname);
 extern void AlterConversionOwner(List *name, Oid newOwnerId);
+extern void AlterConversionOwner_oid(Oid conversionOid, Oid newOwnerId);
 
 #endif   /* CONVERSIONCMDS_H */
diff --git a/src/include/commands/defrem.h b/src/include/commands/defrem.h
index ad7c6a178a02c8d8650a0b28d733b665f581567d..39603cf31f27bd7de9e9218da54b9f36bb3ae3d0 100644
--- a/src/include/commands/defrem.h
+++ b/src/include/commands/defrem.h
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/commands/defrem.h,v 1.68 2005/10/15 02:49:44 momjian Exp $
+ * $PostgreSQL: pgsql/src/include/commands/defrem.h,v 1.69 2005/11/21 12:49:32 alvherre Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -51,6 +51,7 @@ extern void SetFunctionReturnType(Oid funcOid, Oid newRetType);
 extern void SetFunctionArgType(Oid funcOid, int argIndex, Oid newArgType);
 extern void RenameFunction(List *name, List *argtypes, const char *newname);
 extern void AlterFunctionOwner(List *name, List *argtypes, Oid newOwnerId);
+extern void AlterFunctionOwner_oid(Oid procOid, Oid newOwnerId);
 extern void AlterFunction(AlterFunctionStmt *stmt);
 extern void CreateCast(CreateCastStmt *stmt);
 extern void DropCast(DropCastStmt *stmt);
@@ -64,6 +65,7 @@ extern void RemoveOperator(RemoveOperStmt *stmt);
 extern void RemoveOperatorById(Oid operOid);
 extern void AlterOperatorOwner(List *name, TypeName *typeName1,
 				   TypeName *typename2, Oid newOwnerId);
+extern void AlterOperatorOwner_oid(Oid operOid, Oid newOwnerId);
 
 /* commands/aggregatecmds.c */
 extern void DefineAggregate(List *names, List *parameters);
@@ -77,6 +79,7 @@ extern void RemoveOpClass(RemoveOpClassStmt *stmt);
 extern void RemoveOpClassById(Oid opclassOid);
 extern void RenameOpClass(List *name, const char *access_method, const char *newname);
 extern void AlterOpClassOwner(List *name, const char *access_method, Oid newOwnerId);
+extern void AlterOpClassOwner_oid(Oid opcOid, Oid newOwnerId);
 
 /* support routines in commands/define.c */
 
diff --git a/src/include/commands/schemacmds.h b/src/include/commands/schemacmds.h
index f47baf6ef3b71a65450ad48d1e59092d14c2167c..6611364bf75076313a11d1be0977af5491f8f943 100644
--- a/src/include/commands/schemacmds.h
+++ b/src/include/commands/schemacmds.h
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/commands/schemacmds.h,v 1.11 2005/11/19 17:39:45 adunstan Exp $
+ * $PostgreSQL: pgsql/src/include/commands/schemacmds.h,v 1.12 2005/11/21 12:49:32 alvherre Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -24,5 +24,6 @@ extern void RemoveSchemaById(Oid schemaOid);
 
 extern void RenameSchema(const char *oldname, const char *newname);
 extern void AlterSchemaOwner(const char *name, Oid newOwnerId);
+extern void AlterSchemaOwner_oid(const Oid schemaOid, Oid newOwnerId);
 
 #endif   /* SCHEMACMDS_H */
diff --git a/src/include/commands/tablecmds.h b/src/include/commands/tablecmds.h
index 077416484486489358765bb59b91bd2f13a03c7e..b6e30f6f699b27f9268d3b235cbac9bf379d3bc4 100644
--- a/src/include/commands/tablecmds.h
+++ b/src/include/commands/tablecmds.h
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/commands/tablecmds.h,v 1.24 2005/10/15 02:49:44 momjian Exp $
+ * $PostgreSQL: pgsql/src/include/commands/tablecmds.h,v 1.25 2005/11/21 12:49:32 alvherre Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -24,6 +24,8 @@ extern void RemoveRelation(const RangeVar *relation, DropBehavior behavior);
 
 extern void AlterTable(AlterTableStmt *stmt);
 
+extern void ATExecChangeOwner(Oid relationOid, Oid newOwnerId, bool recursing);
+
 extern void AlterTableInternal(Oid relid, List *cmds, bool recurse);
 
 extern void AlterTableCreateToastTable(Oid relOid, bool silent);
diff --git a/src/include/commands/user.h b/src/include/commands/user.h
index ab2829a266bba88730e1ff4892103fd6784ec878..a8fba2f874d349468bf155484cdb1186b9354ab4 100644
--- a/src/include/commands/user.h
+++ b/src/include/commands/user.h
@@ -4,7 +4,7 @@
  *	  Commands for manipulating roles (formerly called users).
  *
  *
- * $PostgreSQL: pgsql/src/include/commands/user.h,v 1.27 2005/06/28 05:09:12 tgl Exp $
+ * $PostgreSQL: pgsql/src/include/commands/user.h,v 1.28 2005/11/21 12:49:32 alvherre Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -20,5 +20,7 @@ extern void AlterRoleSet(AlterRoleSetStmt *stmt);
 extern void DropRole(DropRoleStmt *stmt);
 extern void GrantRole(GrantRoleStmt *stmt);
 extern void RenameRole(const char *oldname, const char *newname);
+extern void DropOwnedObjects(DropOwnedStmt *stmt);
+extern void ReassignOwnedObjects(ReassignOwnedStmt *stmt);
 
 #endif   /* USER_H */
diff --git a/src/include/nodes/nodes.h b/src/include/nodes/nodes.h
index 327e4301ff9549be2b5fb96f68b5e2b4e76282e2..919753ea92fc9b269dca3934c12fb6ebd16aa814 100644
--- a/src/include/nodes/nodes.h
+++ b/src/include/nodes/nodes.h
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/nodes/nodes.h,v 1.176 2005/10/15 02:49:45 momjian Exp $
+ * $PostgreSQL: pgsql/src/include/nodes/nodes.h,v 1.177 2005/11/21 12:49:32 alvherre Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -286,6 +286,8 @@ typedef enum NodeTag
 	T_DropTableSpaceStmt,
 	T_AlterObjectSchemaStmt,
 	T_AlterOwnerStmt,
+	T_DropOwnedStmt,
+	T_ReassignOwnedStmt,
 
 	T_A_Expr = 800,
 	T_ColumnRef,
diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h
index 8baed3e4491e5d895076f487e9192a429df4bf22..a482abfb351e9c5d64605a34a9944e568e63117c 100644
--- a/src/include/nodes/parsenodes.h
+++ b/src/include/nodes/parsenodes.h
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/nodes/parsenodes.h,v 1.293 2005/11/19 17:39:45 adunstan Exp $
+ * $PostgreSQL: pgsql/src/include/nodes/parsenodes.h,v 1.294 2005/11/21 12:49:32 alvherre Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -1875,4 +1875,24 @@ typedef struct DeallocateStmt
 	char	   *name;			/* The name of the plan to remove */
 } DeallocateStmt;
 
+/*
+ * 		DROP OWNED statement
+ */
+typedef struct DropOwnedStmt
+{
+	NodeTag		type;
+	List	   *roles;
+	DropBehavior behavior;
+} DropOwnedStmt;
+
+/*
+ * 		REASSIGN OWNED statement
+ */
+typedef struct ReassignOwnedStmt
+{
+	NodeTag		type;
+	List	   *roles;
+	char	   *newrole;
+} ReassignOwnedStmt;
+
 #endif   /* PARSENODES_H */
diff --git a/src/include/utils/acl.h b/src/include/utils/acl.h
index 8699913eb95cf1a1e7be656e3ccd42846c36d4df..7b2122b9a9f1f47923265f1ac1a02f72579e99ee 100644
--- a/src/include/utils/acl.h
+++ b/src/include/utils/acl.h
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/utils/acl.h,v 1.88 2005/11/18 02:38:24 tgl Exp $
+ * $PostgreSQL: pgsql/src/include/utils/acl.h,v 1.89 2005/11/21 12:49:33 alvherre Exp $
  *
  * NOTES
  *	  An ACL array is simply an array of AclItems, representing the union
@@ -221,6 +221,9 @@ extern Datum hash_aclitem(PG_FUNCTION_ARGS);
  * prototypes for functions in aclchk.c
  */
 extern void ExecuteGrantStmt(GrantStmt *stmt);
+extern void ExecGrantStmt_oids(bool is_grant, GrantObjectType objtype,
+				   List *objects, bool all_privs, AclMode privileges,
+				   List *grantees, bool grant_option, DropBehavior behavior);
 
 extern AclMode pg_class_aclmask(Oid table_oid, Oid roleid,
 				 AclMode mask, AclMaskHow how);
diff --git a/src/test/regress/expected/dependency.out b/src/test/regress/expected/dependency.out
index 2c31e581bfe84353c63dc1dbf2ec2d0269992f06..4781b5f9f29841abe3b82f175ffb75244dce8d61 100644
--- a/src/test/regress/expected/dependency.out
+++ b/src/test/regress/expected/dependency.out
@@ -38,6 +38,76 @@ DROP USER regression_user2;
 ALTER TABLE deptest OWNER TO regression_user3;
 DROP USER regression_user3;
 ERROR:  role "regression_user3" cannot be dropped because some objects depend on it
+\set VERBOSITY default
 -- if we drop the object, we can drop the user too
 DROP TABLE deptest;
 DROP USER regression_user3;
+-- Test DROP OWNED
+CREATE USER regression_user0;
+CREATE USER regression_user1;
+CREATE USER regression_user2;
+SET SESSION AUTHORIZATION regression_user0;
+-- permission denied
+DROP OWNED BY regression_user1;
+ERROR:  permission denied to drop objects
+DROP OWNED BY regression_user0, regression_user2;
+ERROR:  permission denied to drop objects
+REASSIGN OWNED BY regression_user0 TO regression_user1;
+ERROR:  permission denied to reassign objects
+REASSIGN OWNED BY regression_user1 TO regression_user0;
+ERROR:  permission denied to reassign objects
+-- this one is allowed
+DROP OWNED BY regression_user0;
+CREATE TABLE deptest1 ();
+GRANT ALL ON deptest1 TO regression_user1 WITH GRANT OPTION;
+SET SESSION AUTHORIZATION regression_user1;
+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"
+GRANT ALL ON deptest1 TO regression_user2;
+RESET SESSION AUTHORIZATION;
+\z deptest1
+                                                            Access privileges for database "regression"
+ Schema |   Name   | Type  |                                                           Access privileges                                                            
+--------+----------+-------+----------------------------------------------------------------------------------------------------------------------------------------
+ public | deptest1 | table | {regression_user0=arwdRxt/regression_user0,regression_user1=a*r*w*d*R*x*t*/regression_user0,regression_user2=arwdRxt/regression_user1}
+(1 row)
+
+DROP OWNED BY regression_user1;
+-- all grants revoked
+\z deptest1
+               Access privileges for database "regression"
+ Schema |   Name   | Type  |              Access privileges              
+--------+----------+-------+---------------------------------------------
+ public | deptest1 | table | {regression_user0=arwdRxt/regression_user0}
+(1 row)
+
+-- table was dropped
+\d deptest
+-- Test REASSIGN OWNED
+GRANT ALL ON deptest1 TO regression_user1;
+SET SESSION AUTHORIZATION regression_user1;
+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"
+RESET SESSION AUTHORIZATION;
+REASSIGN OWNED BY regression_user1 TO regression_user2;
+\dt deptest
+              List of relations
+ Schema |  Name   | Type  |      Owner       
+--------+---------+-------+------------------
+ public | deptest | table | regression_user2
+(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:  access to table deptest1
+DROP OWNED BY regression_user1;
+DROP USER regression_user1;
+\set VERBOSITY terse
+DROP USER regression_user2;
+ERROR:  role "regression_user2" cannot be dropped because some objects depend on it
+DROP OWNED BY regression_user2, regression_user0;
+DROP USER regression_user2;
+DROP USER regression_user0;
diff --git a/src/test/regress/sql/dependency.sql b/src/test/regress/sql/dependency.sql
index 3e4a232ea716654908b9e0d9eb1f50c250c92644..c1b189f527894d77e32631710ec9b9d01dc90a75 100644
--- a/src/test/regress/sql/dependency.sql
+++ b/src/test/regress/sql/dependency.sql
@@ -39,6 +39,55 @@ DROP USER regression_user2;
 ALTER TABLE deptest OWNER TO regression_user3;
 DROP USER regression_user3;
 
+\set VERBOSITY default
 -- if we drop the object, we can drop the user too
 DROP TABLE deptest;
 DROP USER regression_user3;
+
+-- Test DROP OWNED
+CREATE USER regression_user0;
+CREATE USER regression_user1;
+CREATE USER regression_user2;
+SET SESSION AUTHORIZATION regression_user0;
+-- permission denied
+DROP OWNED BY regression_user1;
+DROP OWNED BY regression_user0, regression_user2;
+REASSIGN OWNED BY regression_user0 TO regression_user1;
+REASSIGN OWNED BY regression_user1 TO regression_user0;
+-- this one is allowed
+DROP OWNED BY regression_user0;
+
+CREATE TABLE deptest1 ();
+GRANT ALL ON deptest1 TO regression_user1 WITH GRANT OPTION;
+
+SET SESSION AUTHORIZATION regression_user1;
+CREATE TABLE deptest (a serial primary key, b text);
+GRANT ALL ON deptest1 TO regression_user2;
+RESET SESSION AUTHORIZATION;
+\z deptest1
+
+DROP OWNED BY regression_user1;
+-- all grants revoked
+\z deptest1
+-- table was dropped
+\d deptest
+
+-- Test REASSIGN OWNED
+GRANT ALL ON deptest1 TO regression_user1;
+
+SET SESSION AUTHORIZATION regression_user1;
+CREATE TABLE deptest (a serial primary key, b text);
+RESET SESSION AUTHORIZATION;
+
+REASSIGN OWNED BY regression_user1 TO regression_user2;
+\dt deptest
+-- doesn't work: grant still exists
+DROP USER regression_user1;
+DROP OWNED BY regression_user1;
+DROP USER regression_user1;
+
+\set VERBOSITY terse
+DROP USER regression_user2;
+DROP OWNED BY regression_user2, regression_user0;
+DROP USER regression_user2;
+DROP USER regression_user0;