From daec989f6a763c9353ba2912b92f5b03c0246c1f Mon Sep 17 00:00:00 2001
From: Tom Lane <tgl@sss.pgh.pa.us>
Date: Mon, 6 Jan 2003 00:31:45 +0000
Subject: [PATCH] ALTER DOMAIN OWNER, from Rod Taylor.

---
 doc/src/sgml/ref/alter_domain.sgml | 38 ++++++++++++++-----
 src/backend/commands/typecmds.c    | 60 +++++++++++++++++++++++++++++-
 src/backend/parser/gram.y          | 11 +++++-
 src/backend/tcop/utility.c         | 20 +++++++---
 src/include/commands/typecmds.h    |  6 ++-
 src/include/nodes/parsenodes.h     |  4 +-
 6 files changed, 120 insertions(+), 19 deletions(-)

diff --git a/doc/src/sgml/ref/alter_domain.sgml b/doc/src/sgml/ref/alter_domain.sgml
index 4735d424059..fdbf08e6d48 100644
--- a/doc/src/sgml/ref/alter_domain.sgml
+++ b/doc/src/sgml/ref/alter_domain.sgml
@@ -1,5 +1,5 @@
 <!--
-$Header: /cvsroot/pgsql/doc/src/sgml/ref/alter_domain.sgml,v 1.6 2002/12/06 16:40:13 momjian Exp $
+$Header: /cvsroot/pgsql/doc/src/sgml/ref/alter_domain.sgml,v 1.7 2003/01/06 00:31:44 tgl Exp $
 PostgreSQL documentation
 -->
 
@@ -29,6 +29,8 @@ ALTER DOMAIN <replaceable class="PARAMETER">domain</replaceable>
     ADD <replaceable class="PARAMETER">domain_constraint</replaceable>
 ALTER DOMAIN <replaceable class="PARAMETER">domain</replaceable>
     DROP CONSTRAINT <replaceable class="PARAMETER">constraint_name</replaceable> [ RESTRICT | CASCADE ]
+ALTER DOMAIN <replaceable class="PARAMETER">domain</replaceable>
+    OWNER TO <replaceable class="PARAMETER">new_owner</replaceable> 
   </synopsis>
 
   <refsect2 id="R2-SQL-ALTERDOMAIN-1">
@@ -73,7 +75,7 @@ ALTER DOMAIN <replaceable class="PARAMETER">domain</replaceable>
       <term>CASCADE</term>
       <listitem>
        <para>
-        Automatically drop objects that depend constraint.
+        Automatically drop objects that depend on the constraint.
        </para>
       </listitem>
      </varlistentry>
@@ -88,6 +90,15 @@ ALTER DOMAIN <replaceable class="PARAMETER">domain</replaceable>
       </listitem>
      </varlistentry>
 
+     <varlistentry>
+      <term><replaceable class="PARAMETER">new_owner</replaceable></term>
+      <listitem>
+       <para>
+	The user name of the new owner of the domain.
+       </para>
+      </listitem>
+     </varlistentry>
+
     </variablelist>
    </para>
   </refsect2>
@@ -141,9 +152,9 @@ ALTER DOMAIN <replaceable class="PARAMETER">domain</replaceable>
     <term>SET/DROP DEFAULT</term>
     <listitem>
      <para>
-      These forms set or remove the default value for a column. Note
+      These forms set or remove the default value for a domain. Note
       that defaults only apply to subsequent <command>INSERT</command>
-      commands; they do not cause rows already in a table using the domain.
+      commands; they do not affect rows already in a table using the domain.
      </para>
     </listitem>
    </varlistentry>
@@ -154,8 +165,7 @@ ALTER DOMAIN <replaceable class="PARAMETER">domain</replaceable>
      <para>
       These forms change whether a domain is marked to allow NULL
       values or to reject NULL values.  You may only <literal>SET NOT NULL</>
-      when the tables using the domain contain no null values in the domain
-      based column.
+      when the columns using the domain contain no null values.
      </para>
     </listitem>
    </varlistentry>
@@ -164,8 +174,10 @@ ALTER DOMAIN <replaceable class="PARAMETER">domain</replaceable>
     <term>ADD <replaceable class="PARAMETER">domain_constraint</replaceable></term>
     <listitem>
      <para>
-      This form adds a new constraint to a table using the same syntax as
-      <xref linkend="SQL-CREATEDOMAIN" endterm="SQL-CREATEDOMAIN-TITLE">. 
+      This form adds a new constraint to a domain using the same syntax as
+      <xref linkend="SQL-CREATEDOMAIN" endterm="SQL-CREATEDOMAIN-TITLE">.
+      This will only succeed if all columns using the domain satisfy the
+      new constraint.
      </para>
     </listitem>
    </varlistentry>
@@ -179,11 +191,19 @@ ALTER DOMAIN <replaceable class="PARAMETER">domain</replaceable>
     </listitem>
    </varlistentry>
 
+   <varlistentry>
+    <term>OWNER</term>
+    <listitem>
+     <para>
+      This form changes the owner of the domain to the specified user.
+     </para>
+    </listitem>
+   </varlistentry>
   </variablelist>
 
   <para>
    You must own the domain to use <command>ALTER DOMAIN</>; except for
-   <command>ALTER TABLE OWNER</>, which may only be executed by a superuser.
+   <command>ALTER DOMAIN OWNER</>, which may only be executed by a superuser.
   </para>
  </refsect1>
 
diff --git a/src/backend/commands/typecmds.c b/src/backend/commands/typecmds.c
index 33f2fa7c2bf..bd29033c729 100644
--- a/src/backend/commands/typecmds.c
+++ b/src/backend/commands/typecmds.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/commands/typecmds.c,v 1.26 2003/01/04 00:46:08 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/commands/typecmds.c,v 1.27 2003/01/06 00:31:44 tgl Exp $
  *
  * DESCRIPTION
  *	  The "DefineFoo" routines take the parse tree and pick out the
@@ -1673,3 +1673,61 @@ domainAddConstraint(Oid domainOid, Oid domainNamespace, Oid baseTypeOid,
 	 */
 	return ccbin;
 }
+
+/*
+ * ALTER DOMAIN .. OWNER TO
+ *
+ * Eventually this should allow changing ownership of other kinds of types,
+ * but some thought must be given to handling complex types.  (A table's
+ * rowtype probably shouldn't be allowed as target, but what of a standalone
+ * composite type?)
+ *
+ * Assumes that permission checks have been completed earlier.
+ */
+void
+AlterTypeOwner(List *names, AclId newOwnerSysId)
+{
+	TypeName   *typename;
+	Oid			typeOid;
+	Relation	rel;
+	HeapTuple	tup;
+	Form_pg_type	typTup;
+
+	/* Make a TypeName so we can use standard type lookup machinery */
+	typename = makeNode(TypeName);
+	typename->names = names;
+	typename->typmod = -1;
+	typename->arrayBounds = NIL;
+
+	/* Lock the type table */
+	rel = heap_openr(TypeRelationName, RowExclusiveLock);
+
+	/* Use LookupTypeName here so that shell types can be processed (why?) */
+	typeOid = LookupTypeName(typename);
+	if (!OidIsValid(typeOid))
+		elog(ERROR, "Type \"%s\" does not exist",
+			 TypeNameToString(typename));
+
+	tup = SearchSysCacheCopy(TYPEOID,
+							 ObjectIdGetDatum(typeOid),
+							 0, 0, 0);
+	if (!HeapTupleIsValid(tup))
+		elog(ERROR, "AlterDomain: type \"%s\" does not exist",
+			 TypeNameToString(typename));
+	typTup = (Form_pg_type) GETSTRUCT(tup);
+
+	/* Check that this is actually a domain */
+	if (typTup->typtype != 'd')
+		elog(ERROR, "%s is not a domain",
+			 TypeNameToString(typename));
+
+	/* Modify the owner --- okay to scribble on typTup because it's a copy */
+	typTup->typowner = newOwnerSysId;
+
+	simple_heap_update(rel, &tup->t_self, tup);
+
+	CatalogUpdateIndexes(rel, tup);
+
+	/* Clean up */
+	heap_close(rel, RowExclusiveLock);
+}
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index c19673fc422..921099b7926 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -11,7 +11,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/parser/gram.y,v 2.389 2002/12/30 15:31:47 momjian Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/parser/gram.y,v 2.390 2003/01/06 00:31:44 tgl Exp $
  *
  * HISTORY
  *	  AUTHOR			DATE			MAJOR EVENT
@@ -3764,6 +3764,15 @@ AlterDomainStmt:
 					n->behavior = $7;
 					$$ = (Node *)n;
 				}
+			/* ALTER DOMAIN <domain> OWNER TO UserId */
+			| ALTER DOMAIN_P any_name OWNER TO UserId
+				{
+					AlterDomainStmt *n = makeNode(AlterDomainStmt);
+					n->subtype = 'U';
+					n->typename = $3;
+					n->name = $6;
+					$$ = (Node *)n;
+				}
 			;
 
 opt_as:		AS										{}
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index 962c5311500..68c02c5f62d 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -10,7 +10,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/tcop/utility.c,v 1.187 2002/12/30 18:42:16 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/tcop/utility.c,v 1.188 2003/01/06 00:31:44 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -439,11 +439,11 @@ ProcessUtility(Node *parsetree,
 								   stmt->newname);		/* new att name */
 						break;
 					case RENAME_RULE:
-						elog(ERROR, "ProcessUtility: Invalid target for RENAME: %d",
+						elog(ERROR, "ProcessUtility: Invalid type for RENAME: %d",
 							 stmt->renameType);
 						break;
 					default:
-						elog(ERROR, "ProcessUtility: Invalid target for RENAME: %d",
+						elog(ERROR, "ProcessUtility: Invalid type for RENAME: %d",
 							 stmt->renameType);
 				}
 			}
@@ -553,7 +553,8 @@ ProcessUtility(Node *parsetree,
 										get_usesysid(stmt->name));
 						break;
 					default:	/* oops */
-						elog(ERROR, "T_AlterTableStmt: unknown subtype");
+						elog(ERROR, "ProcessUtility: Invalid type for AlterTableStmt: %d",
+							 stmt->subtype);
 						break;
 				}
 			}
@@ -595,8 +596,17 @@ ProcessUtility(Node *parsetree,
 												  stmt->name,
 												  stmt->behavior);
 						break;
+					case 'U':	/* OWNER TO */
+						/* check that we are the superuser */
+						if (!superuser())
+							elog(ERROR, "ALTER DOMAIN: permission denied");
+						/* get_usesysid raises an error if no such user */
+						AlterTypeOwner(stmt->typename,
+									   get_usesysid(stmt->name));
+						break;
 					default:	/* oops */
-						elog(ERROR, "T_AlterDomainStmt: unknown subtype");
+						elog(ERROR, "ProcessUtility: Invalid type for AlterDomainStmt: %d",
+							 stmt->subtype);
 						break;
 				}
 			}
diff --git a/src/include/commands/typecmds.h b/src/include/commands/typecmds.h
index 2757d54276d..8f21aef7f41 100644
--- a/src/include/commands/typecmds.h
+++ b/src/include/commands/typecmds.h
@@ -7,15 +7,17 @@
  * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: typecmds.h,v 1.2 2002/12/10 16:12:53 tgl Exp $
+ * $Id: typecmds.h,v 1.3 2003/01/06 00:31:44 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
 #ifndef TYPECMDS_H
 #define TYPECMDS_H
 
+#include "miscadmin.h"
 #include "nodes/parsenodes.h"
 
+
 #define DEFAULT_TYPDELIM		','
 
 extern void DefineType(List *names, List *parameters);
@@ -31,4 +33,6 @@ extern void AlterDomainAddConstraint(List *names, Node *constr);
 extern void AlterDomainDropConstraint(List *names, const char *constrName,
 									  DropBehavior behavior);
 
+extern void AlterTypeOwner(List *names, AclId newOwnerSysId);
+
 #endif   /* TYPECMDS_H */
diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h
index 0f9b7613a00..6ca3894b0da 100644
--- a/src/include/nodes/parsenodes.h
+++ b/src/include/nodes/parsenodes.h
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: parsenodes.h,v 1.224 2002/12/30 15:31:51 momjian Exp $
+ * $Id: parsenodes.h,v 1.225 2003/01/06 00:31:45 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -711,7 +711,7 @@ typedef struct AlterTableStmt
  *	Alter Domain
  *
  * The fields are used in different ways by the different variants of
- * this command. Subtypes should match AlterTable subtypes
+ * this command. Subtypes should match AlterTable subtypes where possible.
  * ----------------------
  */
 typedef struct AlterDomainStmt
-- 
GitLab