diff --git a/src/backend/catalog/pg_proc.c b/src/backend/catalog/pg_proc.c
index 98d0cdca0c4153f6a922094365b2ea9aba03bf02..26861047b24420126b65443c35c6271322470afe 100644
--- a/src/backend/catalog/pg_proc.c
+++ b/src/backend/catalog/pg_proc.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/catalog/pg_proc.c,v 1.48 2000/08/21 20:55:31 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/catalog/pg_proc.c,v 1.49 2000/10/07 00:58:15 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -93,35 +93,34 @@ ProcedureCreate(char *procedureName,
 	MemSet(typev, 0, FUNC_MAX_ARGS * sizeof(Oid));
 	foreach(x, argList)
 	{
-		Value	   *t = lfirst(x);
+		TypeName   *t = (TypeName *) lfirst(x);
+		char	   *typnam = TypeNameToInternalName(t);
 
 		if (parameterCount >= FUNC_MAX_ARGS)
 			elog(ERROR, "Procedures cannot take more than %d arguments",
 				 FUNC_MAX_ARGS);
 
-		if (strcmp(strVal(t), "opaque") == 0)
+		if (strcmp(typnam, "opaque") == 0)
 		{
 			if (languageObjectId == SQLlanguageId)
 				elog(ERROR, "ProcedureCreate: sql functions cannot take type \"opaque\"");
-			toid = 0;
+			toid = InvalidOid;
 		}
 		else
 		{
-			toid = TypeGet(strVal(t), &defined);
+			toid = TypeGet(typnam, &defined);
 
 			if (!OidIsValid(toid))
-			{
 				elog(ERROR, "ProcedureCreate: arg type '%s' is not defined",
-					 strVal(t));
-			}
-
+					 typnam);
 			if (!defined)
-			{
 				elog(NOTICE, "ProcedureCreate: arg type '%s' is only a shell",
-					 strVal(t));
-			}
+					 typnam);
 		}
 
+		if (t->setof)
+			elog(ERROR, "ProcedureCreate: functions cannot accept set arguments");
+
 		typev[parameterCount++] = toid;
 	}
 
@@ -178,7 +177,7 @@ ProcedureCreate(char *procedureName,
 	{
 		if (languageObjectId == SQLlanguageId)
 			elog(ERROR, "ProcedureCreate: sql functions cannot return type \"opaque\"");
-		typeObjectId = 0;
+		typeObjectId = InvalidOid;
 	}
 	else
 	{
@@ -194,10 +193,8 @@ ProcedureCreate(char *procedureName,
 					 returnTypeName);
 		}
 		else if (!defined)
-		{
 			elog(NOTICE, "ProcedureCreate: return type '%s' is only a shell",
 				 returnTypeName);
-		}
 	}
 
 	/*
@@ -212,7 +209,6 @@ ProcedureCreate(char *procedureName,
 		elog(ERROR, "method %s already an attribute of type %s",
 			 procedureName, strVal(lfirst(argList)));
 
-
 	/*
 	 * If this is a postquel procedure, we parse it here in order to be
 	 * sure that it contains no syntax errors.	We should store the plan
diff --git a/src/backend/commands/comment.c b/src/backend/commands/comment.c
index 51832bc2c7bb442a78f261801d130ee8d37dc9fc..e7b13a80788340cafaa7e67d86ed3fc4d31b7cd0 100644
--- a/src/backend/commands/comment.c
+++ b/src/backend/commands/comment.c
@@ -25,6 +25,8 @@
 #include "commands/comment.h"
 #include "miscadmin.h"
 #include "parser/parse.h"
+#include "parser/parse_expr.h"
+#include "parser/parse_func.h"
 #include "rewrite/rewriteRemove.h"
 #include "utils/acl.h"
 #include "utils/fmgroids.h"
@@ -46,7 +48,7 @@ static void CommentAttribute(char *relation, char *attrib, char *comment);
 static void CommentDatabase(char *database, char *comment);
 static void CommentRewrite(char *rule, char *comment);
 static void CommentType(char *type, char *comment);
-static void CommentAggregate(char *aggregate, char *aggtype, char *comment);
+static void CommentAggregate(char *aggregate, List *arguments, char *comment);
 static void CommentProc(char *function, List *arguments, char *comment);
 static void CommentOperator(char *opname, List *arguments, char *comment);
 static void CommentTrigger(char *trigger, char *relation, char *comments);
@@ -92,7 +94,7 @@ CommentObject(int objtype, char *objname, char *objproperty,
 			CommentType(objname, comment);
 			break;
 		case (AGGREGATE):
-			CommentAggregate(objname, objproperty, comment);
+			CommentAggregate(objname, objlist, comment);
 			break;
 		case (FUNCTION):
 			CommentProc(objname, objlist, comment);
@@ -544,9 +546,10 @@ CommentType(char *type, char *comment)
 */
 
 static void
-CommentAggregate(char *aggregate, char *argument, char *comment)
+CommentAggregate(char *aggregate, List *arguments, char *comment)
 {
-
+	TypeName   *aggtype = (TypeName *) lfirst(arguments);
+	char	   *aggtypename = NULL;
 	HeapTuple	aggtuple;
 	Oid			baseoid,
 				oid;
@@ -554,11 +557,12 @@ CommentAggregate(char *aggregate, char *argument, char *comment)
 
 	/*** First, attempt to determine the base aggregate oid ***/
 
-	if (argument)
+	if (aggtype)
 	{
-		baseoid = TypeGet(argument, &defined);
+		aggtypename = TypeNameToInternalName(aggtype);
+		baseoid = TypeGet(aggtypename, &defined);
 		if (!OidIsValid(baseoid))
-			elog(ERROR, "aggregate type '%s' does not exist", argument);
+			elog(ERROR, "type '%s' does not exist", aggtypename);
 	}
 	else
 		baseoid = 0;
@@ -568,10 +572,10 @@ CommentAggregate(char *aggregate, char *argument, char *comment)
 #ifndef NO_SECURITY
 	if (!pg_aggr_ownercheck(GetUserId(), aggregate, baseoid))
 	{
-		if (argument)
+		if (aggtypename)
 		{
 			elog(ERROR, "you are not permitted to comment on aggregate '%s' %s '%s'",
-				 aggregate, "with type", argument);
+				 aggregate, "with type", aggtypename);
 		}
 		else
 		{
@@ -587,10 +591,10 @@ CommentAggregate(char *aggregate, char *argument, char *comment)
 								   ObjectIdGetDatum(baseoid), 0, 0);
 	if (!HeapTupleIsValid(aggtuple))
 	{
-		if (argument)
+		if (aggtypename)
 		{
 			elog(ERROR, "aggregate type '%s' does not exist for aggregate '%s'",
-				 argument, aggregate);
+				 aggtypename, aggregate);
 		}
 		else
 			elog(ERROR, "aggregate '%s' does not exist", aggregate);
@@ -622,7 +626,6 @@ CommentProc(char *function, List *arguments, char *comment)
 				functuple;
 	Oid			oid,
 				argoids[FUNC_MAX_ARGS];
-	char	   *argument;
 	int			i,
 				argcount;
 
@@ -635,18 +638,20 @@ CommentProc(char *function, List *arguments, char *comment)
 			 FUNC_MAX_ARGS);
 	for (i = 0; i < argcount; i++)
 	{
-		argument = strVal(lfirst(arguments));
+		TypeName   *t = (TypeName *) lfirst(arguments);
+		char	   *typnam = TypeNameToInternalName(t);
+
 		arguments = lnext(arguments);
-		if (strcmp(argument, "opaque") == 0)
-			argoids[i] = 0;
+
+		if (strcmp(typnam, "opaque") == 0)
+			argoids[i] = InvalidOid;
 		else
 		{
 			argtuple = SearchSysCacheTuple(TYPENAME,
-										   PointerGetDatum(argument),
+										   PointerGetDatum(typnam),
 										   0, 0, 0);
 			if (!HeapTupleIsValid(argtuple))
-				elog(ERROR, "function argument type '%s' does not exist",
-					 argument);
+				elog(ERROR, "CommentProc: type '%s' not found", typnam);
 			argoids[i] = argtuple->t_data->t_oid;
 		}
 	}
@@ -664,10 +669,9 @@ CommentProc(char *function, List *arguments, char *comment)
 	functuple = SearchSysCacheTuple(PROCNAME, PointerGetDatum(function),
 									Int32GetDatum(argcount),
 									PointerGetDatum(argoids), 0);
-
 	if (!HeapTupleIsValid(functuple))
-		elog(ERROR, "function '%s' with the supplied %s does not exist",
-			 function, "argument list");
+		func_error("CommentProc", function, argcount, argoids, NULL);
+
 	oid = functuple->t_data->t_oid;
 
 	/*** Call CreateComments() to create/drop the comments ***/
@@ -691,23 +695,24 @@ CommentProc(char *function, List *arguments, char *comment)
 static void
 CommentOperator(char *opername, List *arguments, char *comment)
 {
-
+	TypeName   *typenode1 = (TypeName *) lfirst(arguments);
+	TypeName   *typenode2 = (TypeName *) lsecond(arguments);
+	char		oprtype = 0,
+			   *lefttype = NULL,
+			   *righttype = NULL;
 	Form_pg_operator data;
 	HeapTuple	optuple;
 	Oid			oid,
 				leftoid = InvalidOid,
 				rightoid = InvalidOid;
 	bool		defined;
-	char		oprtype = 0,
-			   *lefttype = NULL,
-			   *righttype = NULL;
 
 	/*** Initialize our left and right argument types ***/
 
-	if (lfirst(arguments) != NULL)
-		lefttype = strVal(lfirst(arguments));
-	if (lsecond(arguments) != NULL)
-		righttype = strVal(lsecond(arguments));
+	if (typenode1 != NULL)
+		lefttype = TypeNameToInternalName(typenode1);
+	if (typenode2 != NULL)
+		righttype = TypeNameToInternalName(typenode2);
 
 	/*** Attempt to fetch the left oid, if specified ***/
 
@@ -732,9 +737,9 @@ CommentOperator(char *opername, List *arguments, char *comment)
 	if (OidIsValid(leftoid) && (OidIsValid(rightoid)))
 		oprtype = 'b';
 	else if (OidIsValid(leftoid))
-		oprtype = 'l';
-	else if (OidIsValid(rightoid))
 		oprtype = 'r';
+	else if (OidIsValid(rightoid))
+		oprtype = 'l';
 	else
 		elog(ERROR, "operator '%s' is of an illegal type'", opername);
 
@@ -769,7 +774,6 @@ CommentOperator(char *opername, List *arguments, char *comment)
 	/*** Call CreateComments() to create/drop the comments ***/
 
 	CreateComments(oid, comment);
-
 }
 
 /*------------------------------------------------------------------
diff --git a/src/backend/commands/define.c b/src/backend/commands/define.c
index cf31e5edb1ff199fbce4525069d14941cda40edf..a33d155db9faabee49fb018df7440ed552710873 100644
--- a/src/backend/commands/define.c
+++ b/src/backend/commands/define.c
@@ -10,7 +10,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/commands/define.c,v 1.46 2000/07/22 03:34:26 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/commands/define.c,v 1.47 2000/10/07 00:58:16 tgl Exp $
  *
  * DESCRIPTION
  *	  The "DefineFoo" routines take the parse tree and pick out the
@@ -49,6 +49,7 @@
 #include "commands/defrem.h"
 #include "fmgr.h"
 #include "optimizer/cost.h"
+#include "parser/parse_expr.h"
 #include "tcop/dest.h"
 #include "utils/builtins.h"
 #include "utils/syscache.h"
@@ -83,27 +84,15 @@ case_translate_language_name(const char *input, char *output)
 
 
 static void
-compute_return_type(const Node *returnType,
+compute_return_type(TypeName *returnType,
 					char **prorettype_p, bool *returnsSet_p)
 {
 /*---------------------------------------------------------------------------
    Examine the "returns" clause returnType of the CREATE FUNCTION statement
-   and return information about it as **prorettype_p and **returnsSet.
+   and return information about it as *prorettype_p and *returnsSet.
 ----------------------------------------------------------------------------*/
-	if (nodeTag(returnType) == T_TypeName)
-	{
-		/* a set of values */
-		TypeName   *setType = (TypeName *) returnType;
-
-		*prorettype_p = setType->name;
-		*returnsSet_p = setType->setof;
-	}
-	else
-	{
-		/* singleton */
-		*prorettype_p = strVal(returnType);
-		*returnsSet_p = false;
-	}
+	*prorettype_p = TypeNameToInternalName(returnType);
+	*returnsSet_p = returnType->setof;
 }
 
 
@@ -214,7 +203,7 @@ interpret_AS_clause(const char *languageName, const List *as,
 		*prosrc_str_p = strVal(lfirst(as));
 		*probin_str_p = "-";
 
-		if (lnext(as) != NULL)
+		if (lnext(as) != NIL)
 			elog(ERROR, "CREATE FUNCTION: only one AS item needed for %s language",
 				 languageName);
 	}
@@ -231,27 +220,23 @@ void
 CreateFunction(ProcedureStmt *stmt, CommandDest dest)
 {
 	char	   *probin_str;
-
 	/* pathname of executable file that executes this function, if any */
-	char	   *prosrc_str;
 
+	char	   *prosrc_str;
 	/* SQL that executes this function, if any */
-	char	   *prorettype;
 
+	char	   *prorettype;
 	/* Type of return value (or member of set of values) from function */
-	char		languageName[NAMEDATALEN];
 
+	char		languageName[NAMEDATALEN];
 	/*
 	 * name of language of function, with case adjusted: "C", "newC",
 	 * "internal", "newinternal", "sql", etc.
 	 */
 
 	bool		returnsSet;
-
 	/* The function returns a set of values, as opposed to a singleton. */
 
-	bool		lanisPL = false;
-
 	/*
 	 * The following are optional user-supplied attributes of the
 	 * function.
@@ -263,8 +248,12 @@ CreateFunction(ProcedureStmt *stmt, CommandDest dest)
 	bool		canCache,
 				isStrict;
 
+	/* Convert language name to canonical case */
 	case_translate_language_name(stmt->language, languageName);
 
+	/*
+	 * Apply appropriate security checks depending on language.
+	 */
 	if (strcmp(languageName, "C") == 0 ||
 		strcmp(languageName, "newC") == 0 ||
 		strcmp(languageName, "internal") == 0 ||
@@ -301,7 +290,7 @@ CreateFunction(ProcedureStmt *stmt, CommandDest dest)
 
 		/* Check that this language is a PL */
 		languageStruct = (Form_pg_language) GETSTRUCT(languageTuple);
-		if (!(languageStruct->lanispl))
+		if (!languageStruct->lanispl)
 			elog(ERROR,
 				 "Language '%s' isn't defined as PL", languageName);
 
@@ -316,11 +305,15 @@ CreateFunction(ProcedureStmt *stmt, CommandDest dest)
 				 "language.",
 				 languageName);
 		}
-
-		lanisPL = true;
 	}
 
-	compute_return_type(stmt->returnType, &prorettype, &returnsSet);
+	/*
+	 * Convert remaining parameters of CREATE to form wanted by
+	 * ProcedureCreate.
+	 */
+	Assert(IsA(stmt->returnType, TypeName));
+	compute_return_type((TypeName *) stmt->returnType,
+						&prorettype, &returnsSet);
 
 	compute_full_attributes(stmt->withClause,
 							&byte_pct, &perbyte_cpu, &percall_cpu,
@@ -345,7 +338,7 @@ CreateFunction(ProcedureStmt *stmt, CommandDest dest)
 					perbyte_cpu,
 					percall_cpu,
 					outin_ratio,
-					stmt->defArgs,
+					stmt->argTypes,
 					dest);
 }
 
@@ -389,49 +382,43 @@ DefineOperator(char *oprName,
 	{
 		DefElem    *defel = (DefElem *) lfirst(pl);
 
-		if (!strcasecmp(defel->defname, "leftarg"))
+		if (strcasecmp(defel->defname, "leftarg") == 0)
 		{
-			if ((nodeTag(defel->arg) == T_TypeName)
-				&& (((TypeName *) defel->arg)->setof))
-				elog(ERROR, "setof type not implemented for leftarg");
-
 			typeName1 = defGetString(defel);
-			if (typeName1 == NULL)
-				elog(ERROR, "type for leftarg is malformed.");
+			if (IsA(defel->arg, TypeName)
+				&& ((TypeName *) defel->arg)->setof)
+				elog(ERROR, "setof type not implemented for leftarg");
 		}
-		else if (!strcasecmp(defel->defname, "rightarg"))
+		else if (strcasecmp(defel->defname, "rightarg") == 0)
 		{
-			if ((nodeTag(defel->arg) == T_TypeName)
-				&& (((TypeName *) defel->arg)->setof))
-				elog(ERROR, "setof type not implemented for rightarg");
-
 			typeName2 = defGetString(defel);
-			if (typeName2 == NULL)
-				elog(ERROR, "type for rightarg is malformed.");
+			if (IsA(defel->arg, TypeName)
+				&& ((TypeName *) defel->arg)->setof)
+				elog(ERROR, "setof type not implemented for rightarg");
 		}
-		else if (!strcasecmp(defel->defname, "procedure"))
+		else if (strcasecmp(defel->defname, "procedure") == 0)
 			functionName = defGetString(defel);
-		else if (!strcasecmp(defel->defname, "precedence"))
+		else if (strcasecmp(defel->defname, "precedence") == 0)
 		{
 			/* NOT IMPLEMENTED (never worked in v4.2) */
 			elog(NOTICE, "CREATE OPERATOR: precedence not implemented");
 		}
-		else if (!strcasecmp(defel->defname, "associativity"))
+		else if (strcasecmp(defel->defname, "associativity") == 0)
 		{
 			/* NOT IMPLEMENTED (never worked in v4.2) */
 			elog(NOTICE, "CREATE OPERATOR: associativity not implemented");
 		}
-		else if (!strcasecmp(defel->defname, "commutator"))
+		else if (strcasecmp(defel->defname, "commutator") == 0)
 			commutatorName = defGetString(defel);
-		else if (!strcasecmp(defel->defname, "negator"))
+		else if (strcasecmp(defel->defname, "negator") == 0)
 			negatorName = defGetString(defel);
-		else if (!strcasecmp(defel->defname, "restrict"))
+		else if (strcasecmp(defel->defname, "restrict") == 0)
 			restrictionName = defGetString(defel);
-		else if (!strcasecmp(defel->defname, "join"))
+		else if (strcasecmp(defel->defname, "join") == 0)
 			joinName = defGetString(defel);
-		else if (!strcasecmp(defel->defname, "hashes"))
+		else if (strcasecmp(defel->defname, "hashes") == 0)
 			canHash = TRUE;
-		else if (!strcasecmp(defel->defname, "sort1"))
+		else if (strcasecmp(defel->defname, "sort1") == 0)
 		{
 			/* ----------------
 			 * XXX ( ... [ , sort1 = oprname ] [ , sort2 = oprname ] ... )
@@ -441,7 +428,7 @@ DefineOperator(char *oprName,
 			 */
 			sortName1 = defGetString(defel);
 		}
-		else if (!strcasecmp(defel->defname, "sort2"))
+		else if (strcasecmp(defel->defname, "sort2") == 0)
 			sortName2 = defGetString(defel);
 		else
 		{
@@ -562,72 +549,73 @@ DefineType(char *typeName, List *parameters)
 	char		delimiter = DEFAULT_TYPDELIM;
 	char	   *shadow_type;
 	List	   *pl;
-	char		alignment = 'i';/* default alignment */
-	char		storage = 'p'; /* default storage in TOAST */
+	char		alignment = 'i'; /* default alignment */
+	char		storage = 'p';	/* default storage in TOAST */
 
 	/*
-	 * Type names can only be 15 characters long, so that the shadow type
-	 * can be created using the 16th character as necessary.
+	 * Type names must be one character shorter than other names,
+	 * allowing room to create the corresponding array type name with
+	 * prepended "_".
 	 */
-	if (strlen(typeName) >= (NAMEDATALEN - 1))
+	if (strlen(typeName) > (NAMEDATALEN - 2))
 	{
 		elog(ERROR, "DefineType: type names must be %d characters or less",
-			 NAMEDATALEN - 1);
+			 NAMEDATALEN - 2);
 	}
 
 	foreach(pl, parameters)
 	{
 		DefElem    *defel = (DefElem *) lfirst(pl);
 
-		if (!strcasecmp(defel->defname, "internallength"))
+		if (strcasecmp(defel->defname, "internallength") == 0)
 			internalLength = defGetTypeLength(defel);
-		else if (!strcasecmp(defel->defname, "externallength"))
+		else if (strcasecmp(defel->defname, "externallength") == 0)
 			externalLength = defGetTypeLength(defel);
-		else if (!strcasecmp(defel->defname, "input"))
+		else if (strcasecmp(defel->defname, "input") == 0)
 			inputName = defGetString(defel);
-		else if (!strcasecmp(defel->defname, "output"))
+		else if (strcasecmp(defel->defname, "output") == 0)
 			outputName = defGetString(defel);
-		else if (!strcasecmp(defel->defname, "send"))
+		else if (strcasecmp(defel->defname, "send") == 0)
 			sendName = defGetString(defel);
-		else if (!strcasecmp(defel->defname, "delimiter"))
+		else if (strcasecmp(defel->defname, "delimiter") == 0)
 		{
 			char	   *p = defGetString(defel);
 
 			delimiter = p[0];
 		}
-		else if (!strcasecmp(defel->defname, "receive"))
+		else if (strcasecmp(defel->defname, "receive") == 0)
 			receiveName = defGetString(defel);
-		else if (!strcasecmp(defel->defname, "element"))
+		else if (strcasecmp(defel->defname, "element") == 0)
 			elemName = defGetString(defel);
-		else if (!strcasecmp(defel->defname, "default"))
+		else if (strcasecmp(defel->defname, "default") == 0)
 			defaultValue = defGetString(defel);
-		else if (!strcasecmp(defel->defname, "passedbyvalue"))
+		else if (strcasecmp(defel->defname, "passedbyvalue") == 0)
 			byValue = true;
-		else if (!strcasecmp(defel->defname, "alignment"))
+		else if (strcasecmp(defel->defname, "alignment") == 0)
 		{
 			char	   *a = defGetString(defel);
 
-			if (!strcasecmp(a, "double"))
+			if (strcasecmp(a, "double") == 0)
 				alignment = 'd';
-			else if (!strcasecmp(a, "int4"))
+			else if (strcasecmp(a, "int4") == 0)
 				alignment = 'i';
 			else
 			{
-				elog(ERROR, "DefineType: \"%s\" alignment  not recognized",
+				elog(ERROR, "DefineType: \"%s\" alignment not recognized",
 					 a);
 			}
 		}
-		else if (!strcasecmp(defel->defname, "storage"))
+		else if (strcasecmp(defel->defname, "storage") == 0)
 		{
 			char	   *a = defGetString(defel);
 
-			if (!strcasecmp(a, "plain"))
+			if (strcasecmp(a, "plain") == 0)
 				storage = 'p';
-			else if (!strcasecmp(a, "external"))
+			else if (strcasecmp(a, "external") == 0)
 				storage = 'e';
-			else if (!strcasecmp(a, "extended"))
+			else if (strcasecmp(a, "extended") == 0)
 				storage = 'x';
-			else if (!strcasecmp(a, "main"))
+			else if (strcasecmp(a, "main") == 0)
 				storage = 'm';
 			else
 			{
@@ -674,8 +662,8 @@ DefineType(char *typeName, List *parameters)
 			   storage);		/* TOAST strategy */
 
 	/* ----------------
-	 *	When we create a true type (as opposed to a complex type)
-	 *	we need to have an shadow array entry for it in pg_type as well.
+	 *	When we create a base type (as opposed to a complex type)
+	 *	we need to have an array entry for it in pg_type as well.
 	 * ----------------
 	 */
 	shadow_type = makeArrayTypeName(typeName);
@@ -702,19 +690,32 @@ DefineType(char *typeName, List *parameters)
 static char *
 defGetString(DefElem *def)
 {
-	char	   *string;
-
-	if (nodeTag(def->arg) == T_String)
-		string = strVal(def->arg);
-	else if (nodeTag(def->arg) == T_TypeName)
-		string = ((TypeName *) def->arg)->name;
-	else
-		string = NULL;
-#if 0
-	elog(ERROR, "Define: \"%s\" = what?", def->defname);
-#endif
+	if (def->arg == NULL)
+		elog(ERROR, "Define: \"%s\" requires a parameter",
+			 def->defname);
+	switch (nodeTag(def->arg))
+	{
+		case T_Integer:
+		{
+			char *str = palloc(32);
 
-	return string;
+			snprintf(str, 32, "%ld", (long) intVal(def->arg));
+			return str;
+		}
+		case T_Float:
+			/* T_Float values are kept in string form, so this type cheat
+			 * works (and doesn't risk losing precision)
+			 */
+			return strVal(def->arg);
+		case T_String:
+			return strVal(def->arg);
+		case T_TypeName:
+			return TypeNameToInternalName((TypeName *) def->arg);
+		default:
+			elog(ERROR, "Define: cannot interpret argument of \"%s\"",
+				 def->defname);
+	}
+	return NULL;				/* keep compiler quiet */
 }
 
 static double
@@ -739,15 +740,32 @@ defGetNumeric(DefElem *def)
 static int
 defGetTypeLength(DefElem *def)
 {
-	if (nodeTag(def->arg) == T_Integer)
-		return intVal(def->arg);
-	else if (nodeTag(def->arg) == T_String &&
-			!strcasecmp(strVal(def->arg), "variable"))
-		return -1;				/* variable length */
-	else if (nodeTag(def->arg) == T_TypeName &&
-			!strcasecmp(((TypeName *)(def->arg))->name, "variable"))
-		return -1;
-
-	elog(ERROR, "Define: \"%s\" = what?", def->defname);
-	return -1;
+	if (def->arg == NULL)
+		elog(ERROR, "Define: \"%s\" requires a parameter",
+			 def->defname);
+	switch (nodeTag(def->arg))
+	{
+		case T_Integer:
+			return intVal(def->arg);
+		case T_Float:
+			elog(ERROR, "Define: \"%s\" requires an integral value",
+				 def->defname);
+			break;
+		case T_String:
+			if (strcasecmp(strVal(def->arg), "variable") == 0)
+				return -1;		/* variable length */
+			break;
+		case T_TypeName:
+			/* cope if grammar chooses to believe "variable" is a typename */
+			if (strcasecmp(TypeNameToInternalName((TypeName *) def->arg),
+						   "variable") == 0)
+				return -1;		/* variable length */
+			break;
+		default:
+			elog(ERROR, "Define: cannot interpret argument of \"%s\"",
+				 def->defname);
+	}
+	elog(ERROR, "Define: invalid argument for \"%s\"",
+		 def->defname);
+	return 0;					/* keep compiler quiet */
 }
diff --git a/src/backend/commands/remove.c b/src/backend/commands/remove.c
index c5b7968e5fdf930a9d4d7806e2e07e9ceceab3e0..6da32297f6182804d450b3dc972a9a32ae335679 100644
--- a/src/backend/commands/remove.c
+++ b/src/backend/commands/remove.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/commands/Attic/remove.c,v 1.52 2000/09/12 16:48:55 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/commands/Attic/remove.c,v 1.53 2000/10/07 00:58:16 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -22,6 +22,7 @@
 #include "commands/comment.h"
 #include "commands/defrem.h"
 #include "miscadmin.h"
+#include "parser/parse_expr.h"
 #include "parser/parse_func.h"
 #include "utils/acl.h"
 #include "utils/syscache.h"
@@ -39,8 +40,8 @@
  */
 void
 RemoveOperator(char *operatorName,		/* operator name */
-			   char *typeName1, /* first type name */
-			   char *typeName2) /* optional second type name */
+			   char *typeName1,			/* left argument type name */
+			   char *typeName2)			/* right argument type name */
 {
 	Relation	relation;
 	HeapTuple	tup;
@@ -53,28 +54,22 @@ RemoveOperator(char *operatorName,		/* operator name */
 	{
 		typeId1 = TypeGet(typeName1, &defined);
 		if (!OidIsValid(typeId1))
-		{
 			elog(ERROR, "RemoveOperator: type '%s' does not exist", typeName1);
-			return;
-		}
 	}
 
 	if (typeName2)
 	{
 		typeId2 = TypeGet(typeName2, &defined);
 		if (!OidIsValid(typeId2))
-		{
 			elog(ERROR, "RemoveOperator: type '%s' does not exist", typeName2);
-			return;
-		}
 	}
 
 	if (OidIsValid(typeId1) && OidIsValid(typeId2))
 		oprtype = 'b';
 	else if (OidIsValid(typeId1))
-		oprtype = 'l';
-	else
 		oprtype = 'r';
+	else
+		oprtype = 'l';
 
 	relation = heap_openr(OperatorRelationName, RowExclusiveLock);
 
@@ -94,7 +89,6 @@ RemoveOperator(char *operatorName,		/* operator name */
 				 operatorName);
 #endif
 
-
 		/*** Delete any comments associated with this operator ***/
 
 		DeleteComments(tup->t_data->t_oid);
@@ -308,13 +302,12 @@ RemoveType(char *typeName)		/* type name to be removed */
  */
 void
 RemoveFunction(char *functionName,		/* function name to be removed */
-			   int nargs,
-			   List *argNameList /* list of TypeNames */ )
+			   List *argTypes)	/* list of TypeName nodes */
 {
+	int			nargs = length(argTypes);
 	Relation	relation;
 	HeapTuple	tup;
 	Oid			argList[FUNC_MAX_ARGS];
-	char	   *typename;
 	int			i;
 
 	if (nargs > FUNC_MAX_ARGS)
@@ -323,19 +316,20 @@ RemoveFunction(char *functionName,		/* function name to be removed */
 	MemSet(argList, 0, FUNC_MAX_ARGS * sizeof(Oid));
 	for (i = 0; i < nargs; i++)
 	{
-		typename = strVal(lfirst(argNameList));
-		argNameList = lnext(argNameList);
+		TypeName   *t = (TypeName *) lfirst(argTypes);
+		char	   *typnam = TypeNameToInternalName(t);
 
-		if (strcmp(typename, "opaque") == 0)
-			argList[i] = 0;
+		argTypes = lnext(argTypes);
+
+		if (strcmp(typnam, "opaque") == 0)
+			argList[i] = InvalidOid;
 		else
 		{
 			tup = SearchSysCacheTuple(TYPENAME,
-									  PointerGetDatum(typename),
+									  PointerGetDatum(typnam),
 									  0, 0, 0);
-
 			if (!HeapTupleIsValid(tup))
-				elog(ERROR, "RemoveFunction: type '%s' not found", typename);
+				elog(ERROR, "RemoveFunction: type '%s' not found", typnam);
 			argList[i] = tup->t_data->t_oid;
 		}
 	}
diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c
index 1a0f4623978fdf8e519707d214a7cfcec6a4680f..e768d0bd796a8acc158d40cec112bea7a02c169e 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
- *	  $Header: /cvsroot/pgsql/src/backend/nodes/copyfuncs.c,v 1.124 2000/10/05 19:11:27 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/nodes/copyfuncs.c,v 1.125 2000/10/07 00:58:16 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -1854,13 +1854,9 @@ _copyChangeACLStmt(ChangeACLStmt *from)
 {
 	ChangeACLStmt *newnode = makeNode(ChangeACLStmt);
 	
-	if (from->aclitem)
-	{
-		newnode->aclitem = (struct AclItem *) palloc(sizeof(struct AclItem));
-		memcpy(newnode->aclitem, from->aclitem, sizeof(struct AclItem));
-	}
-	newnode->modechg = from->modechg;
 	Node_Copy(from, newnode, relNames);
+	if (from->aclString)
+		newnode->aclString = pstrdup(from->aclString);
 
 	return newnode;
 }
@@ -2033,7 +2029,7 @@ _copyProcedureStmt(ProcedureStmt *from)
 	ProcedureStmt *newnode = makeNode(ProcedureStmt);
 	
 	newnode->funcname = pstrdup(from->funcname);
-	Node_Copy(from, newnode, defArgs);
+	Node_Copy(from, newnode, argTypes);
 	Node_Copy(from, newnode, returnType);
 	Node_Copy(from, newnode, withClause);
 	Node_Copy(from, newnode, as);
@@ -2048,7 +2044,7 @@ _copyRemoveAggrStmt(RemoveAggrStmt *from)
 	RemoveAggrStmt *newnode = makeNode(RemoveAggrStmt);
 	
 	newnode->aggname = pstrdup(from->aggname);
-	newnode->aggtype = pstrdup(from->aggtype);
+	Node_Copy(from, newnode, aggtype);
 
 	return newnode;
 }
diff --git a/src/backend/nodes/equalfuncs.c b/src/backend/nodes/equalfuncs.c
index ab8779cb3755932d2a0643f3d3f819955670db4c..d5b2ff4607e6d8be6dea737af484c9abb599dccb 100644
--- a/src/backend/nodes/equalfuncs.c
+++ b/src/backend/nodes/equalfuncs.c
@@ -20,7 +20,7 @@
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/nodes/equalfuncs.c,v 1.75 2000/10/05 19:11:27 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/nodes/equalfuncs.c,v 1.76 2000/10/07 00:58:16 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -753,24 +753,10 @@ _equalAlterTableStmt(AlterTableStmt *a, AlterTableStmt *b)
 static bool
 _equalChangeACLStmt(ChangeACLStmt *a, ChangeACLStmt *b)
 {
-	if (a->aclitem && b->aclitem)
-	{
-		if (a->aclitem->ai_id != b->aclitem->ai_id)
-			return false;
-		if (a->aclitem->ai_idtype != b->aclitem->ai_idtype)
-			return false;
-		if (a->aclitem->ai_mode != b->aclitem->ai_mode)
-			return false;
-	}
-	else
-	{
-		if (a->aclitem != b->aclitem)
-			return false;		/* one NULL, one not */
-	}
-	if (a->modechg != b->modechg)
-		return false;
 	if (!equal(a->relNames, b->relNames))
 		return false;
+	if (!equalstr(a->aclString, b->aclString))
+		return false;
 
 	return true;
 }
@@ -956,7 +942,7 @@ _equalProcedureStmt(ProcedureStmt *a, ProcedureStmt *b)
 {
 	if (!equalstr(a->funcname, b->funcname))
 		return false;
-	if (!equal(a->defArgs, b->defArgs))
+	if (!equal(a->argTypes, b->argTypes))
 		return false;
 	if (!equal(a->returnType, b->returnType))
 		return false;
@@ -975,7 +961,7 @@ _equalRemoveAggrStmt(RemoveAggrStmt *a, RemoveAggrStmt *b)
 {
 	if (!equalstr(a->aggname, b->aggname))
 		return false;
-	if (!equalstr(a->aggtype, b->aggtype))
+	if (!equal(a->aggtype, b->aggtype))
 		return false;
 
 	return true;
diff --git a/src/backend/parser/analyze.c b/src/backend/parser/analyze.c
index ffedca05ed997062176f6fb56152c4c6b95c1633..ae1f027e47cb7d11b39364e8d48e841dc30cb236 100644
--- a/src/backend/parser/analyze.c
+++ b/src/backend/parser/analyze.c
@@ -6,7 +6,7 @@
  * Portions Copyright (c) 1996-2000, PostgreSQL, Inc
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- *	$Id: analyze.c,v 1.160 2000/10/05 19:11:33 tgl Exp $
+ *	$Id: analyze.c,v 1.161 2000/10/07 00:58:17 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -69,49 +69,45 @@ static List *extras_after;
 
 /*
  * parse_analyze -
- *	  analyze a list of parse trees and transform them if necessary.
- *
- * Returns a list of transformed parse trees. Optimizable statements are
- * all transformed to Query while the rest stays the same.
+ *	  analyze a raw parse tree and transform it to Query form.
  *
+ * The result is a List of Query nodes (we need a list since some commands
+ * produce multiple Queries).  Optimizable statements require considerable
+ * transformation, while many utility-type statements are simply hung off
+ * a dummy CMD_UTILITY Query node.
  */
 List *
-parse_analyze(List *pl, ParseState *parentParseState)
+parse_analyze(Node *parseTree, ParseState *parentParseState)
 {
 	List	   *result = NIL;
+	ParseState *pstate = make_parsestate(parentParseState);
+	Query	   *query;
 
-	while (pl != NIL)
-	{
-		ParseState *pstate = make_parsestate(parentParseState);
-		Query	   *parsetree;
+	extras_before = extras_after = NIL;
 
-		extras_before = extras_after = NIL;
+	query = transformStmt(pstate, parseTree);
+	release_pstate_resources(pstate);
 
-		parsetree = transformStmt(pstate, lfirst(pl));
+	while (extras_before != NIL)
+	{
+		result = lappend(result,
+						 transformStmt(pstate, lfirst(extras_before)));
 		release_pstate_resources(pstate);
+		extras_before = lnext(extras_before);
+	}
 
-		while (extras_before != NIL)
-		{
-			result = lappend(result,
-							 transformStmt(pstate, lfirst(extras_before)));
-			release_pstate_resources(pstate);
-			extras_before = lnext(extras_before);
-		}
-
-		result = lappend(result, parsetree);
+	result = lappend(result, query);
 
-		while (extras_after != NIL)
-		{
-			result = lappend(result,
-							 transformStmt(pstate, lfirst(extras_after)));
-			release_pstate_resources(pstate);
-			extras_after = lnext(extras_after);
-		}
-
-		pfree(pstate);
-		pl = lnext(pl);
+	while (extras_after != NIL)
+	{
+		result = lappend(result,
+						 transformStmt(pstate, lfirst(extras_after)));
+		release_pstate_resources(pstate);
+		extras_after = lnext(extras_after);
 	}
 
+	pfree(pstate);
+
 	return result;
 }
 
@@ -126,8 +122,7 @@ release_pstate_resources(ParseState *pstate)
 
 /*
  * transformStmt -
- *	  transform a Parse tree. If it is an optimizable statement, turn it
- *	  into a Query tree.
+ *	  transform a Parse tree into a Query tree.
  */
 static Query *
 transformStmt(ParseState *pstate, Node *parseTree)
@@ -353,7 +348,7 @@ transformInsertStmt(ParseState *pstate, InsertStmt *stmt)
 		 * from a stand-alone SELECT. (Indeed, Postgres up through 6.5 had
 		 * bugs of just that nature...)
 		 */
-		selectList = parse_analyze(makeList1(stmt->selectStmt), pstate);
+		selectList = parse_analyze(stmt->selectStmt, pstate);
 		Assert(length(selectList) == 1);
 
 		selectQuery = (Query *) lfirst(selectList);
@@ -1933,7 +1928,7 @@ transformSetOperationTree(ParseState *pstate, Node *node)
 		 */
 		save_rtable = pstate->p_rtable;
 		pstate->p_rtable = NIL;
-		selectList = parse_analyze(makeList1(stmt), pstate);
+		selectList = parse_analyze((Node *) stmt, pstate);
 		pstate->p_rtable = save_rtable;
 
 		Assert(length(selectList) == 1);
@@ -2752,6 +2747,7 @@ makeFromExpr(List *fromlist, Node *quals)
 static void
 transformColumnType(ParseState *pstate, ColumnDef *column)
 {
+	TypeName   *typename = column->typename;
 
 	/*
 	 * If the column doesn't have an explicitly specified typmod, check to
@@ -2760,18 +2756,33 @@ transformColumnType(ParseState *pstate, ColumnDef *column)
 	 * Note that we deliberately do NOT look at array or set information
 	 * here; "numeric[]" needs the same default typmod as "numeric".
 	 */
-	if (column->typename->typmod == -1)
+	if (typename->typmod == -1)
 	{
-		switch (typeTypeId(typenameType(column->typename->name)))
+		switch (typeTypeId(typenameType(typename->name)))
 		{
-				case BPCHAROID:
+			case BPCHAROID:
 				/* "char" -> "char(1)" */
-				column->typename->typmod = VARHDRSZ + 1;
+				typename->typmod = VARHDRSZ + 1;
 				break;
 			case NUMERICOID:
-				column->typename->typmod = VARHDRSZ +
+				typename->typmod = VARHDRSZ +
 					((NUMERIC_DEFAULT_PRECISION << 16) | NUMERIC_DEFAULT_SCALE);
 				break;
 		}
 	}
+
+	/*
+	 * Is this the name of a complex type? If so, implement
+	 * it as a set.
+	 *
+	 * XXX this is a hangover from ancient Berkeley code that probably
+	 * doesn't work anymore anyway.
+	 */
+	 if (typeTypeRelid(typenameType(typename->name)) != InvalidOid)
+	 {
+		 /* (Eventually add in here that the set can only
+		  * contain one element.)
+		  */
+		 typename->setof = true;
+	 }
 }
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index f13b942abd87b3d51b032fd017e85ae8a60a0ec9..a596bd6aba1bab11e86b1fe6e422181694b1ce18 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.194 2000/10/05 19:11:33 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/parser/gram.y,v 2.195 2000/10/07 00:58:17 tgl Exp $
  *
  * HISTORY
  *	  AUTHOR			DATE			MAJOR EVENT
@@ -59,7 +59,6 @@
 
 extern List *parsetree;			/* final parse result is delivered here */
 
-static char saved_relname[NAMEDATALEN];  /* need this for complex attributes */
 static bool QueryIsRule = FALSE;
 static Oid	*param_type_info;
 static int	pfunc_num_args;
@@ -161,7 +160,7 @@ static void doNegateFloat(Value *v);
 
 %type <str>		relation_name, copy_file_name, copy_delimiter, copy_null, def_name,
 		database_name, access_method_clause, access_method, attr_name,
-		class, index_name, name, func_name, file_name, aggr_argtype
+		class, index_name, name, func_name, file_name
 
 %type <str>		opt_id,
 		all_Op, MathOp, opt_name,
@@ -183,7 +182,7 @@ static void doNegateFloat(Value *v);
 		def_list, opt_indirection, group_clause, TriggerFuncArgs,
 		opt_select_limit
 
-%type <typnam>	func_arg, func_return
+%type <typnam>	func_arg, func_return, aggr_argtype
 
 %type <boolean>	opt_arg, TriggerForOpt, TriggerForType, OptTemp
 
@@ -1541,7 +1540,7 @@ CreateAsStmt:  CREATE OptTemp TABLE relation_name OptUnder OptCreateAs AS Select
 					n->istemp = $2;
 					n->into = $4;
                     if ($5 != NIL)
-						yyerror("CREATE TABLE/AS SELECT does not support UNDER");
+						elog(ERROR,"CREATE TABLE/AS SELECT does not support UNDER");
 					if ($6 != NIL)
 						mapTargetColumns($6, n->targetList);
 					$$ = $8;
@@ -2009,8 +2008,8 @@ CommentStmt:	COMMENT ON comment_type name IS comment_text
 				CommentStmt *n = makeNode(CommentStmt);
 				n->objtype = $3;
 				n->objname = $4;
-				n->objproperty = $5;
-				n->objlist = NULL;
+				n->objproperty = NULL;
+				n->objlist = makeList1($5);
 				n->comment = $7;
 				$$ = (Node *) n;
 			}
@@ -2309,7 +2308,7 @@ grantee:  PUBLIC
 
 opt_with_grant:  WITH GRANT OPTION
 				{
-					yyerror("WITH GRANT OPTION is not supported.  Only relation owners can set privileges");
+					elog(ERROR,"WITH GRANT OPTION is not supported.  Only relation owners can set privileges");
 				 }
 		| /*EMPTY*/
 		;
@@ -2471,7 +2470,7 @@ ProcedureStmt:	CREATE FUNCTION func_name func_args
 				{
 					ProcedureStmt *n = makeNode(ProcedureStmt);
 					n->funcname = $3;
-					n->defArgs = $4;
+					n->argTypes = $4;
 					n->returnType = (Node *)$6;
 					n->withClause = $11;
 					n->as = $8;
@@ -2488,29 +2487,12 @@ func_args:  '(' func_args_list ')'				{ $$ = $2; }
 		;
 
 func_args_list:  func_arg
-				{	$$ = makeList1(makeString($1->name)); }
+				{	$$ = makeList1($1); }
 		| func_args_list ',' func_arg
-				{	$$ = lappend($1, makeString($3->name)); }
+				{	$$ = lappend($1, $3); }
 		;
 
-/* Would be nice to use the full Typename production for these fields,
- * but that one sometimes dives into the catalogs looking for valid types.
- * Arguments like "opaque" are valid when defining functions,
- * so that won't work here. The only thing we give up is array notation,
- * which isn't meaningful in this context anyway.
- * - thomas 2000-03-25
- * The following productions are difficult, since it is difficult to
- * distinguish between TokenId and SimpleTypename:
-		opt_arg TokenId SimpleTypename
-				{
-					$$ = $3;
-				}
-		| TokenId SimpleTypename
-				{
-					$$ = $2;
-				}
- */
-func_arg:  opt_arg SimpleTypename
+func_arg:  opt_arg Typename
 				{
 					/* We can catch over-specified arguments here if we want to,
 					 * but for now better to silently swallow typmod, etc.
@@ -2518,7 +2500,7 @@ func_arg:  opt_arg SimpleTypename
 					 */
 					$$ = $2;
 				}
-		| SimpleTypename
+		| Typename
 				{
 					$$ = $1;
 				}
@@ -2546,7 +2528,7 @@ func_as: Sconst
 				{ 	$$ = makeList2(makeString($1), makeString($3)); }
 		;
 
-func_return:  SimpleTypename
+func_return:  Typename
 				{
 					/* We can catch over-specified arguments here if we want to,
 					 * but for now better to silently swallow typmod, etc.
@@ -2554,11 +2536,6 @@ func_return:  SimpleTypename
 					 */
 					$$ = $1;
 				}
-		| SETOF SimpleTypename
-				{
-					$$ = $2;
-					$$->setof = TRUE;
-				}
 		;
 
 
@@ -2599,12 +2576,12 @@ RemoveAggrStmt:  DROP AGGREGATE name aggr_argtype
 				{
 						RemoveAggrStmt *n = makeNode(RemoveAggrStmt);
 						n->aggname = $3;
-						n->aggtype = $4;
+						n->aggtype = (Node *) $4;
 						$$ = (Node *)n;
 				}
 		;
 
-aggr_argtype:  name								{ $$ = $1; }
+aggr_argtype:  Typename							{ $$ = $1; }
 		| '*'									{ $$ = NULL; }
 		;
 
@@ -2628,16 +2605,16 @@ RemoveOperStmt:  DROP OPERATOR all_Op '(' oper_argtypes ')'
 				}
 		;
 
-oper_argtypes:	name
+oper_argtypes:	Typename
 				{
 				   elog(ERROR,"parser: argument type missing (use NONE for unary operators)");
 				}
-		| name ',' name
-				{ $$ = makeList2(makeString($1), makeString($3)); }
-		| NONE ',' name			/* left unary */
-				{ $$ = makeList2(NULL, makeString($3)); }
-		| name ',' NONE			/* right unary */
-				{ $$ = makeList2(makeString($1), NULL); }
+		| Typename ',' Typename
+				{ $$ = makeList2($1, $3); }
+		| NONE ',' Typename			/* left unary */
+				{ $$ = makeList2(NULL, $3); }
+		| Typename ',' NONE			/* right unary */
+				{ $$ = makeList2($1, NULL); }
 		;
 
 
@@ -3832,23 +3809,6 @@ Typename:  SimpleTypename opt_array_bounds
 				{
 					$$ = $1;
 					$$->arrayBounds = $2;
-
-					/* Is this the name of a complex type? If so, implement
-					 * it as a set.
-					 */
-					if (strcmp(saved_relname, $$->name) == 0)
-						/* This attr is the same type as the relation
-						 * being defined. The classic example: create
-						 * emp(name=text,mgr=emp)
-						 */
-						$$->setof = TRUE;
-					else if (typeTypeRelid(typenameType($$->name)) != InvalidOid)
-						 /* (Eventually add in here that the set can only
-						  * contain one element.)
-						  */
-						$$->setof = TRUE;
-					else
-						$$->setof = FALSE;
 				}
 		| SETOF SimpleTypename
 				{
@@ -3909,7 +3869,7 @@ Numeric:  FLOAT opt_float
 		| DECIMAL opt_decimal
 				{
 					$$ = makeNode(TypeName);
-					$$->name = xlateSqlType("numeric");
+					$$->name = xlateSqlType("decimal");
 					$$->typmod = $2;
 				}
 		| DEC opt_decimal
@@ -5245,17 +5205,15 @@ update_target_el:  ColId opt_indirection '=' a_expr
 relation_name:	SpecialRuleRelation
 				{
 					$$ = $1;
-					StrNCpy(saved_relname, $1, NAMEDATALEN);
 				}
 		| ColId
 				{
 					/* disallow refs to variable system tables */
 					if (strcmp(LogRelationName, $1) == 0
-					   || strcmp(VariableRelationName, $1) == 0)
+						|| strcmp(VariableRelationName, $1) == 0)
 						elog(ERROR,"%s cannot be accessed by users",$1);
 					else
 						$$ = $1;
-					StrNCpy(saved_relname, $1, NAMEDATALEN);
 				}
 		;
 
@@ -5298,7 +5256,7 @@ AexprConst:  Iconst
 					n->val.val.str = $1;
 					$$ = (Node *)n;
 				}
-		/* The SimpleTypename rule formerly used Typename,
+		/* This rule formerly used Typename,
 		 * but that causes reduce conflicts with subscripted column names.
 		 * Now, separate into ConstTypename and ConstInterval,
 		 * to allow implementing the SQL92 syntax for INTERVAL literals.
@@ -5383,6 +5341,7 @@ ColId:  generic							{ $$ = $1; }
 		| TokenId						{ $$ = $1; }
 		| INTERVAL						{ $$ = "interval"; }
 		| NATIONAL						{ $$ = "national"; }
+		| NONE							{ $$ = "none"; }
 		| PATH_P						{ $$ = "path"; }
 		| SERIAL						{ $$ = "serial"; }
 		| TIME							{ $$ = "time"; }
@@ -5595,7 +5554,6 @@ ColLabel:  ColId						{ $$ = $1; }
 		| NATURAL						{ $$ = "natural"; }
 		| NCHAR							{ $$ = "nchar"; }
 		| NEW							{ $$ = "new"; }
-		| NONE							{ $$ = "none"; }
 		| NOT							{ $$ = "not"; }
 		| NOTNULL						{ $$ = "notnull"; }
 		| NULLIF						{ $$ = "nullif"; }
@@ -5851,7 +5809,6 @@ xlateSqlType(char *name)
 
 void parser_init(Oid *typev, int nargs)
 {
-	saved_relname[0] = '\0';
 	QueryIsRule = FALSE;
 	/*
 	 * Keep enough information around to fill out the type of param nodes
diff --git a/src/backend/parser/parse_clause.c b/src/backend/parser/parse_clause.c
index 20233ed1950b38c29ad2ae346f12e41c4d87c3b6..38dc3ea09764b9dfb57937f917fe9a8c8c1909c1 100644
--- a/src/backend/parser/parse_clause.c
+++ b/src/backend/parser/parse_clause.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/parser/parse_clause.c,v 1.69 2000/10/05 19:11:33 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/parser/parse_clause.c,v 1.70 2000/10/07 00:58:17 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -375,7 +375,7 @@ transformRangeSubselect(ParseState *pstate, RangeSubselect *r)
 	save_joinlist = pstate->p_joinlist;
 	pstate->p_rtable = NIL;
 	pstate->p_joinlist = NIL;
-	parsetrees = parse_analyze(makeList1(r->subquery), pstate);
+	parsetrees = parse_analyze(r->subquery, pstate);
 	pstate->p_rtable = save_rtable;
 	pstate->p_joinlist = save_joinlist;
 
diff --git a/src/backend/parser/parse_expr.c b/src/backend/parser/parse_expr.c
index 7b647124d1f2abfe98edb79d7250bcaaa36a2627..94ccaa5f69f079fb222b4a09ac7ac5de5c6371ef 100644
--- a/src/backend/parser/parse_expr.c
+++ b/src/backend/parser/parse_expr.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/parser/parse_expr.c,v 1.85 2000/10/05 19:11:33 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/parser/parse_expr.c,v 1.86 2000/10/07 00:58:17 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -294,8 +294,7 @@ transformExpr(ParseState *pstate, Node *expr, int precedence)
 					break;
 				}
 				pstate->p_hasSubLinks = true;
-				qtrees = parse_analyze(makeList1(sublink->subselect),
-									   pstate);
+				qtrees = parse_analyze(sublink->subselect, pstate);
 				if (length(qtrees) != 1)
 					elog(ERROR, "Bad query in subselect");
 				qtree = (Query *) lfirst(qtrees);
@@ -821,19 +820,21 @@ exprIsLengthCoercion(Node *expr, int32 *coercedTypmod)
 static Node *
 parser_typecast_constant(Value *expr, TypeName *typename)
 {
-	Const	   *con;
 	Type		tp;
 	Datum		datum;
+	Const	   *con;
 	char	   *const_string = NULL;
 	bool		string_palloced = false;
 	bool		isNull = false;
 
+	tp = typenameType(TypeNameToInternalName(typename));
+
 	switch (nodeTag(expr))
 	{
 		case T_Integer:
-			string_palloced = true;
 			const_string = DatumGetCString(DirectFunctionCall1(int4out,
 										   Int32GetDatum(expr->val.ival)));
+			string_palloced = true;
 			break;
 		case T_Float:
 		case T_String:
@@ -844,19 +845,9 @@ parser_typecast_constant(Value *expr, TypeName *typename)
 			break;
 		default:
 			elog(ERROR, "Cannot cast this expression to type '%s'",
-				 typename->name);
+				 typeTypeName(tp));
 	}
 
-	if (typename->arrayBounds != NIL)
-	{
-		char		type_string[NAMEDATALEN + 2];
-
-		sprintf(type_string, "_%s", typename->name);
-		tp = (Type) typenameType(type_string);
-	}
-	else
-		tp = (Type) typenameType(typename->name);
-
 	if (isNull)
 		datum = (Datum) NULL;
 	else
@@ -892,15 +883,7 @@ parser_typecast_expression(ParseState *pstate,
 	Type		tp;
 	Oid			targetType;
 
-	if (typename->arrayBounds != NIL)
-	{
-		char		type_string[NAMEDATALEN + 2];
-
-		sprintf(type_string, "_%s", typename->name);
-		tp = (Type) typenameType(type_string);
-	}
-	else
-		tp = (Type) typenameType(typename->name);
+	tp = typenameType(TypeNameToInternalName(typename));
 	targetType = typeTypeId(tp);
 
 	if (inputType == InvalidOid)
@@ -925,3 +908,26 @@ parser_typecast_expression(ParseState *pstate,
 
 	return expr;
 }
+
+/*
+ * Given a TypeName node as returned by the grammar, generate the internal
+ * name of the corresponding type.  Note this does NOT check if the type
+ * exists or not.
+ */
+char *
+TypeNameToInternalName(TypeName *typename)
+{
+	if (typename->arrayBounds != NIL)
+	{
+		/*
+		 * By convention, the name of an array type is the name of its
+		 * element type with "_" prepended.
+		 */
+		char   *arrayname = palloc(strlen(typename->name) + 2);
+
+		sprintf(arrayname, "_%s", typename->name);
+		return arrayname;
+	}
+	else
+		return typename->name;
+}
diff --git a/src/backend/parser/parser.c b/src/backend/parser/parser.c
index 4a6c825498afd76cae0997390787e1c54743a91e..84b27549d3602a27266f5153c3392f8fb61f969a 100644
--- a/src/backend/parser/parser.c
+++ b/src/backend/parser/parser.c
@@ -1,14 +1,20 @@
 /*-------------------------------------------------------------------------
  *
  * parser.c
- *		Main entry point/driver for PostgreSQL parser
+ *		Main entry point/driver for PostgreSQL grammar
+ *
+ * Note that the grammar is not allowed to perform any table access
+ * (since we need to be able to do basic parsing even while inside an
+ * aborted transaction).  Therefore, the data structures returned by
+ * the grammar are "raw" parsetrees that still need to be analyzed by
+ * parse_analyze.
  *
  *
  * Portions Copyright (c) 1996-2000, PostgreSQL, Inc
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/parser/parser.c,v 1.46 2000/09/12 21:07:02 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/parser/parser.c,v 1.47 2000/10/07 00:58:17 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -16,8 +22,6 @@
 #include "postgres.h"
 
 #include "nodes/parsenodes.h"
-#include "nodes/pg_list.h"
-#include "parser/analyze.h"
 #include "parser/gramparse.h"
 #include "parser/parse.h"
 #include "parser/parser.h"
@@ -35,19 +39,17 @@ List	   *parsetree;			/* result of parsing is left here */
 static int	lookahead_token;	/* one-token lookahead */
 static bool have_lookahead;		/* lookahead_token set? */
 
-#ifdef SETS_FIXED
-static void fixupsets();
-static void define_sets();
-
-#endif
 
 /*
- * parser-- returns a list of parse trees
+ * parser
+ *		Given a query in string form, and optionally info about
+ *		parameter types, do lexical and syntactic analysis.
+ *
+ * Returns a list of raw (un-analyzed) parse trees.
  */
 List *
 parser(char *str, Oid *typev, int nargs)
 {
-	List	   *queryList;
 	int			yyresult;
 
 	parseString = str;
@@ -67,28 +69,9 @@ parser(char *str, Oid *typev, int nargs)
 	clearerr(stdin);
 
 	if (yyresult)				/* error */
-		return (List *) NULL;
-
-	queryList = parse_analyze(parsetree, NULL);
+		return NIL;
 
-#ifdef SETS_FIXED
-
-	/*
-	 * Fixing up sets calls the parser, so it reassigns the global
-	 * variable parsetree. So save the real parsetree.
-	 */
-	savetree = parsetree;
-	foreach(parse, savetree)
-	{							/* savetree is really a list of parses */
-
-		/* find set definitions embedded in query */
-		fixupsets((Query *) lfirst(parse));
-
-	}
-	return savetree;
-#endif
-
-	return queryList;
+	return parsetree;
 }
 
 
@@ -135,83 +118,3 @@ yylex(void)
 
 	return cur_token;
 }
-
-
-#ifdef SETS_FIXED
-static void
-fixupsets(Query *parse)
-{
-	if (parse == NULL)
-		return;
-	if (parse->commandType == CMD_UTILITY)		/* utility */
-		return;
-	if (parse->commandType != CMD_INSERT)
-		return;
-	define_sets(parse);
-}
-
-/* Recursively find all of the Consts in the parsetree.  Some of
- * these may represent a set.  The value of the Const will be the
- * query (a string) which defines the set.	Call SetDefine to define
- * the set, and store the OID of the new set in the Const instead.
- */
-static void
-define_sets(Node *clause)
-{
-	Oid			setoid;
-	Type		t = typeidType(OIDOID);
-	Oid			typeoid = typeTypeId(t);
-	Size		oidsize = typeLen(t);
-	bool		oidbyval = typeByVal(t);
-
-	if (clause == NULL)
-		return;
-	else if (IsA(clause, LispList))
-	{
-		define_sets(lfirst(clause));
-		define_sets(lnext(clause));
-	}
-	else if (IsA(clause, Const))
-	{
-		if (get_constisnull((Const) clause) ||
-			!get_constisset((Const) clause))
-			return;
-		setoid = SetDefine(((Const *) clause)->constvalue,
-						   typeidTypeName(((Const *) clause)->consttype));
-		set_constvalue((Const) clause, setoid);
-		set_consttype((Const) clause, typeoid);
-		set_constlen((Const) clause, oidsize);
-		set_constypeByVal((Const) clause, oidbyval);
-	}
-	else if (IsA(clause, Iter))
-		define_sets(((Iter *) clause)->iterexpr);
-	else if (single_node(clause))
-		return;
-	else if (or_clause(clause) || and_clause(clause))
-	{
-		List	   *temp;
-
-		/* mapcan */
-		foreach(temp, ((Expr *) clause)->args)
-			define_sets(lfirst(temp));
-	}
-	else if (is_funcclause(clause))
-	{
-		List	   *temp;
-
-		/* mapcan */
-		foreach(temp, ((Expr *) clause)->args)
-			define_sets(lfirst(temp));
-	}
-	else if (IsA(clause, ArrayRef))
-		define_sets(((ArrayRef *) clause)->refassgnexpr);
-	else if (not_clause(clause))
-		define_sets(get_notclausearg(clause));
-	else if (is_opclause(clause))
-	{
-		define_sets(get_leftop(clause));
-		define_sets(get_rightop(clause));
-	}
-}
-
-#endif
diff --git a/src/backend/tcop/postgres.c b/src/backend/tcop/postgres.c
index bddad9e4f6ed754c18cb39fe33b0aec783cdb19e..3eaa3a1562d59cf8a0119b8d28ecfb4a3e07cb85 100644
--- a/src/backend/tcop/postgres.c
+++ b/src/backend/tcop/postgres.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/tcop/postgres.c,v 1.177 2000/10/03 03:11:19 momjian Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/tcop/postgres.c,v 1.178 2000/10/07 00:58:18 tgl Exp $
  *
  * NOTES
  *	  this is the "main" module of the postgres backend and
@@ -47,6 +47,8 @@
 #include "nodes/print.h"
 #include "optimizer/cost.h"
 #include "optimizer/planner.h"
+#include "parser/analyze.h"
+#include "parser/parse.h"
 #include "parser/parser.h"
 #include "rewrite/rewriteHandler.h"
 #include "tcop/fastpath.h"
@@ -90,8 +92,6 @@ extern char ControlFilePath[];
 
 static bool	dontExecute = false;
 
-static bool IsEmptyQuery = false;
-
 /* note: these declarations had better match tcopprot.h */
 DLLIMPORT sigjmp_buf Warn_restart;
 
@@ -129,6 +129,10 @@ int			XfuncMode = 0;
 static int	InteractiveBackend(StringInfo inBuf);
 static int	SocketBackend(StringInfo inBuf);
 static int	ReadCommand(StringInfo inBuf);
+static List *pg_parse_query(char *query_string, Oid *typev, int nargs);
+static List *pg_analyze_and_rewrite(Node *parsetree);
+static void start_xact_command(void);
+static void finish_xact_command(void);
 static void SigHupHandler(SIGNAL_ARGS);
 static void FloatExceptionHandler(SIGNAL_ARGS);
 static void quickdie(SIGNAL_ARGS);
@@ -341,46 +345,120 @@ ReadCommand(StringInfo inBuf)
  *
  * A list of Query nodes is returned, since the string might contain
  * multiple queries and/or the rewriter might expand one query to several.
+ *
+ * NOTE: this routine is no longer used for processing interactive queries,
+ * but it is still needed for parsing of SQL function bodies.
  */
 List *
 pg_parse_and_rewrite(char *query_string,	/* string to execute */
 					 Oid *typev,			/* parameter types */
 					 int nargs)				/* number of parameters */
 {
+	List	   *raw_parsetree_list;
 	List	   *querytree_list;
-	List	   *querytree_list_item;
-	Query	   *querytree;
-	List	   *new_list;
+	List	   *list_item;
+
+	/* ----------------
+	 *	(1) parse the request string into a list of raw parse trees.
+	 * ----------------
+	 */
+	raw_parsetree_list = pg_parse_query(query_string, typev, nargs);
+
+	/* ----------------
+	 *	(2) Do parse analysis and rule rewrite.
+	 * ----------------
+	 */
+	querytree_list = NIL;
+	foreach(list_item, raw_parsetree_list)
+	{
+		Node   *parsetree = (Node *) lfirst(list_item);
+
+		querytree_list = nconc(querytree_list,
+							   pg_analyze_and_rewrite(parsetree));
+	}
+
+	return querytree_list;
+}
+
+/*
+ * Do raw parsing (only).
+ *
+ * A list of parsetrees is returned, since there might be multiple
+ * commands in the given string.
+ *
+ * NOTE: for interactive queries, it is important to keep this routine
+ * separate from the analysis & rewrite stages.  Analysis and rewriting
+ * cannot be done in an aborted transaction, since they require access to
+ * database tables.  So, we rely on the raw parser to determine whether
+ * we've seen a COMMIT or ABORT command; when we are in abort state, other
+ * commands are not processed any further than the raw parse stage.
+ */
+static List *
+pg_parse_query(char *query_string, Oid *typev, int nargs)
+{
+	List	   *raw_parsetree_list;
 
 	if (Debug_print_query)
 		elog(DEBUG, "query: %s", query_string);
 
+	if (Show_parser_stats)
+		ResetUsage();
+
+	raw_parsetree_list = parser(query_string, typev, nargs);
+
+	if (Show_parser_stats)
+	{
+		fprintf(StatFp, "PARSER STATISTICS\n");
+		ShowUsage();
+	}
+
+	return raw_parsetree_list;
+}
+
+/*
+ * Given a raw parsetree (gram.y output), perform parse analysis and
+ * rule rewriting.
+ *
+ * A list of Query nodes is returned, since either the analyzer or the
+ * rewriter might expand one query to several.
+ *
+ * NOTE: for reasons mentioned above, this must be separate from raw parsing.
+ */
+static List *
+pg_analyze_and_rewrite(Node *parsetree)
+{
+	List	   *querytree_list;
+	List	   *list_item;
+	Query	   *querytree;
+	List	   *new_list;
+
 	/* ----------------
-	 *	(1) parse the request string into a list of parse trees
+	 *	(1) Perform parse analysis.
 	 * ----------------
 	 */
 	if (Show_parser_stats)
 		ResetUsage();
 
-	querytree_list = parser(query_string, typev, nargs);
+	querytree_list = parse_analyze(parsetree, NULL);
 
 	if (Show_parser_stats)
 	{
-		fprintf(StatFp, "PARSER STATISTICS\n");
+		fprintf(StatFp, "PARSE ANALYSIS STATISTICS\n");
 		ShowUsage();
+		ResetUsage();
 	}
 
 	/* ----------------
-	 *	(2) rewrite the queries, as necessary
+	 *	(2) Rewrite the queries, as necessary
 	 *
 	 *	rewritten queries are collected in new_list.  Note there may be
 	 *	more or fewer than in the original list.
 	 * ----------------
 	 */
 	new_list = NIL;
-	foreach(querytree_list_item, querytree_list)
+	foreach(list_item, querytree_list)
 	{
-		querytree = (Query *) lfirst(querytree_list_item);
+		querytree = (Query *) lfirst(list_item);
 
 		if (Debug_print_parse)
 		{
@@ -409,19 +487,18 @@ pg_parse_and_rewrite(char *query_string,	/* string to execute */
 
 	querytree_list = new_list;
 
+	if (Show_parser_stats)
+	{
+		fprintf(StatFp, "REWRITER STATISTICS\n");
+		ShowUsage();
+	}
+
 #ifdef COPY_PARSE_PLAN_TREES
-	/* Optional debugging check: pass parsetree output through copyObject() */
-	/*
-	 * Note: we run this test after rewrite, not before, because copyObject()
-	 * does not handle most kinds of nodes that are used only in raw parse
-	 * trees.  The present (bizarre) implementation of UNION/INTERSECT/EXCEPT
-	 * doesn't run analysis of the second and later subqueries until rewrite,
-	 * so we'd get false failures on these queries if we did it beforehand.
-	 */
+	/* Optional debugging check: pass querytree output through copyObject() */
 	new_list = (List *) copyObject(querytree_list);
 	/* This checks both copyObject() and the equal() routines... */
 	if (! equal(new_list, querytree_list))
-		elog(NOTICE, "pg_parse_and_rewrite: copyObject failed on parse tree");
+		elog(NOTICE, "pg_analyze_and_rewrite: copyObject failed on parse tree");
 	else
 		querytree_list = new_list;
 #endif
@@ -431,9 +508,9 @@ pg_parse_and_rewrite(char *query_string,	/* string to execute */
 		if (Debug_pretty_print)
 		{
 			elog(DEBUG, "rewritten parse tree:");
-			foreach(querytree_list_item, querytree_list)
+			foreach(list_item, querytree_list)
 			{
-				querytree = (Query *) lfirst(querytree_list_item);
+				querytree = (Query *) lfirst(list_item);
 				nodeDisplay(querytree);
 				printf("\n");
 			}
@@ -441,10 +518,9 @@ pg_parse_and_rewrite(char *query_string,	/* string to execute */
 		else
 		{
 			elog(DEBUG, "rewritten parse tree:");
-
-			foreach(querytree_list_item, querytree_list)
+			foreach(list_item, querytree_list)
 			{
-				querytree = (Query *) lfirst(querytree_list_item);
+				querytree = (Query *) lfirst(list_item);
 				elog(DEBUG, "%s", nodeToString(querytree));
 			}
 		}
@@ -514,7 +590,7 @@ pg_plan_query(Query *querytree)
 
 
 /* ----------------------------------------------------------------
- *		pg_exec_query_dest()
+ *		pg_exec_query_string()
  *
  *		Takes a querystring, runs the parser/utilities or
  *		parser/planner/executor over it as necessary.
@@ -545,21 +621,31 @@ pg_plan_query(Query *querytree)
  */
 
 void
-pg_exec_query_dest(char *query_string,	/* string to execute */
-				   CommandDest dest,	/* where results should go */
-				   MemoryContext parse_context)	/* context for parsetrees */
+pg_exec_query_string(char *query_string,	/* string to execute */
+					 CommandDest dest,		/* where results should go */
+					 MemoryContext parse_context) /* context for parsetrees */
 {
+	bool		xact_started;
 	MemoryContext oldcontext;
-	List	   *querytree_list,
-			   *querytree_item;
+	List	   *parsetree_list,
+			   *parsetree_item;
 
 	/*
-	 * If you called this routine with parse_context = CurrentMemoryContext,
-	 * you blew it.  They *must* be different, else the context reset
-	 * at the bottom of the loop will destroy the querytree list.
-	 * (We really ought to check that parse_context isn't a child of
-	 * CurrentMemoryContext either, but that would take more cycles than
-	 * it's likely to be worth.)
+	 * Start up a transaction command.  All queries generated by the
+	 * query_string will be in this same command block, *unless* we find
+	 * a BEGIN/COMMIT/ABORT statement; we have to force a new xact command
+	 * after one of those, else bad things will happen in xact.c.
+	 * (Note that this will possibly change execution memory context.)
+	 */
+	start_xact_command();
+	xact_started = true;
+
+	/*
+	 * parse_context *must* be different from the execution memory context,
+	 * else the context reset at the bottom of the loop will destroy the
+	 * parsetree list.  (We really ought to check that parse_context isn't a
+	 * child of CurrentMemoryContext either, but that would take more cycles
+	 * than it's likely to be worth.)
 	 */
 	Assert(parse_context != CurrentMemoryContext);
 
@@ -569,48 +655,57 @@ pg_exec_query_dest(char *query_string,	/* string to execute */
 	oldcontext = MemoryContextSwitchTo(parse_context);
 
 	/*
-	 * Parse and rewrite the query or queries.
+	 * Do basic parsing of the query or queries (this should be safe
+	 * even if we are in aborted transaction state!)
 	 */
-	querytree_list = pg_parse_and_rewrite(query_string, NULL, 0);
+	parsetree_list = pg_parse_query(query_string, NULL, 0);
 
 	/*
-	 * Switch back to execution context for planning and execution.
+	 * Switch back to execution context to enter the loop.
 	 */
 	MemoryContextSwitchTo(oldcontext);
 
 	/*
-	 * Run through the query or queries and execute each one.
+	 * Run through the parsetree(s) and process each one.
 	 */
-	foreach(querytree_item, querytree_list)
+	foreach(parsetree_item, parsetree_list)
 	{
-		Query	   *querytree = (Query *) lfirst(querytree_item);
+		Node   *parsetree = (Node *) lfirst(parsetree_item);
+		bool	isTransactionStmt;
+		List   *querytree_list,
+			   *querytree_item;
 
-		/* if we got a cancel signal in parsing or prior command, quit */
-		if (QueryCancel)
-			CancelQuery();
+		/* Transaction control statements need some special handling */
+		isTransactionStmt = IsA(parsetree, TransactionStmt);
 
-		if (querytree->commandType == CMD_UTILITY)
+		/*
+		 * If we are in an aborted transaction, ignore all commands except
+		 * COMMIT/ABORT.  It is important that this test occur before we
+		 * try to do parse analysis, rewrite, or planning, since all those
+		 * phases try to do database accesses, which may fail in abort state.
+		 * (It might be safe to allow some additional utility commands in
+		 * this state, but not many...)
+		 */
+		if (IsAbortedTransactionBlockState())
 		{
-			/* ----------------
-			 *	 process utility functions (create, destroy, etc..)
-			 *
-			 *	 Note: we do not check for the transaction aborted state
-			 *	 because that is done in ProcessUtility.
-			 * ----------------
-			 */
-			if (Debug_print_query)
-				elog(DEBUG, "ProcessUtility: %s", query_string);
-			else if (DebugLvl > 1)
-				elog(DEBUG, "ProcessUtility");
+			bool	allowit = false;
 
-			ProcessUtility(querytree->utilityStmt, dest);
-		}
-		else
-		{
-			Plan	   *plan;
+			if (isTransactionStmt)
+			{
+				TransactionStmt *stmt = (TransactionStmt *) parsetree;
+
+				switch (stmt->command)
+				{
+					case COMMIT:
+					case ROLLBACK:
+						allowit = true;
+						break;
+					default:
+						break;
+				}
+			}
 
-			/* If aborted transaction, skip planning and execution */
-			if (IsAbortedTransactionBlockState())
+			if (! allowit)
 			{
 				/* ----------------
 				 *	 the EndCommand() stuff is to tell the frontend
@@ -631,58 +726,180 @@ pg_exec_query_dest(char *query_string,	/* string to execute */
 				 */
 				continue;
 			}
+		}
 
-			plan = pg_plan_query(querytree);
+		/* Make sure we are in a transaction command */
+		if (! xact_started)
+		{
+			start_xact_command();
+			xact_started = true;
+		}
 
-			/* if we got a cancel signal whilst planning, quit */
-			if (QueryCancel)
-				CancelQuery();
+		/* If we got a cancel signal in parsing or prior command, quit */
+		if (QueryCancel)
+			CancelQuery();
+
+		/*
+		 * OK to analyze and rewrite this query.
+		 *
+		 * Switch to appropriate context for constructing querytrees
+		 * (again, these must outlive the execution context).
+		 */
+		oldcontext = MemoryContextSwitchTo(parse_context);
 
-			/* Initialize snapshot state for query */
-			SetQuerySnapshot();
+		querytree_list = pg_analyze_and_rewrite(parsetree);
 
-			/*
-			 * execute the plan
-			 */
-			if (Show_executor_stats)
-				ResetUsage();
+		/*
+		 * Switch back to execution context for planning and execution.
+		 */
+		MemoryContextSwitchTo(oldcontext);
+
+		/*
+		 * Inner loop handles the individual queries generated from a
+		 * single parsetree by analysis and rewrite.
+		 */
+		foreach(querytree_item, querytree_list)
+		{
+			Query	   *querytree = (Query *) lfirst(querytree_item);
 
-			if (dontExecute)
+			/* Make sure we are in a transaction command */
+			if (! xact_started)
 			{
-				/* don't execute it, just show the query plan */
-				print_plan(plan, querytree);
+				start_xact_command();
+				xact_started = true;
+			}
+
+			/* If we got a cancel signal in analysis or prior command, quit */
+			if (QueryCancel)
+				CancelQuery();
+
+			if (querytree->commandType == CMD_UTILITY)
+			{
+				/* ----------------
+				 *	 process utility functions (create, destroy, etc..)
+				 * ----------------
+				 */
+				if (Debug_print_query)
+					elog(DEBUG, "ProcessUtility: %s", query_string);
+				else if (DebugLvl > 1)
+					elog(DEBUG, "ProcessUtility");
+
+				ProcessUtility(querytree->utilityStmt, dest);
 			}
 			else
 			{
-				if (DebugLvl > 1)
-					elog(DEBUG, "ProcessQuery");
-				ProcessQuery(querytree, plan, dest);
+				/* ----------------
+				 *	 process a plannable query.
+				 * ----------------
+				 */
+				Plan	   *plan;
+
+				plan = pg_plan_query(querytree);
+
+				/* if we got a cancel signal whilst planning, quit */
+				if (QueryCancel)
+					CancelQuery();
+
+				/* Initialize snapshot state for query */
+				SetQuerySnapshot();
+
+				/*
+				 * execute the plan
+				 */
+				if (Show_executor_stats)
+					ResetUsage();
+
+				if (dontExecute)
+				{
+					/* don't execute it, just show the query plan */
+					print_plan(plan, querytree);
+				}
+				else
+				{
+					if (DebugLvl > 1)
+						elog(DEBUG, "ProcessQuery");
+					ProcessQuery(querytree, plan, dest);
+				}
+
+				if (Show_executor_stats)
+				{
+					fprintf(stderr, "EXECUTOR STATISTICS\n");
+					ShowUsage();
+				}
 			}
 
-			if (Show_executor_stats)
+			/*
+			 * In a query block, we want to increment the command counter
+			 * between queries so that the effects of early queries are
+			 * visible to subsequent ones.  In particular we'd better
+			 * do so before checking constraints.
+			 */
+			if (!isTransactionStmt)
+				CommandCounterIncrement();
+
+			/*
+			 * Invoke IMMEDIATE constraint triggers
+			 */
+			DeferredTriggerEndQuery();
+
+			/*
+			 * Clear the execution context to recover temporary
+			 * memory used by the query.  NOTE: if query string contains
+			 * BEGIN/COMMIT transaction commands, execution context may
+			 * now be different from what we were originally passed;
+			 * so be careful to clear current context not "oldcontext".
+			 */
+			Assert(parse_context != CurrentMemoryContext);
+
+			MemoryContextResetAndDeleteChildren(CurrentMemoryContext);
+
+			/*
+			 * If this was a transaction control statement, commit it
+			 * and arrange to start a new xact command for the next
+			 * command (if any).
+			 */
+			if (isTransactionStmt)
 			{
-				fprintf(stderr, "EXECUTOR STATISTICS\n");
-				ShowUsage();
+				finish_xact_command();
+				xact_started = false;
 			}
-		}
 
-		/*
-		 * In a query block, we want to increment the command counter
-		 * between queries so that the effects of early queries are
-		 * visible to subsequent ones.
-		 */
-		CommandCounterIncrement();
-		/*
-		 * Also, clear the execution context to recover temporary
-		 * memory used by the query.  NOTE: if query string contains
-		 * BEGIN/COMMIT transaction commands, execution context may
-		 * now be different from what we were originally passed;
-		 * so be careful to clear current context not "oldcontext".
-		 */
-		MemoryContextResetAndDeleteChildren(CurrentMemoryContext);
-	}
+		} /* end loop over queries generated from a parsetree */
+	} /* end loop over parsetrees */
+
+	/*
+	 * Close down transaction statement, if one is open.
+	 */
+	if (xact_started)
+		finish_xact_command();
 }
 
+/*
+ * Convenience routines for starting/committing a single command.
+ */
+static void
+start_xact_command(void)
+{
+	if (DebugLvl >= 1)
+		elog(DEBUG, "StartTransactionCommand");
+	StartTransactionCommand();
+}
+
+static void
+finish_xact_command(void)
+{
+	if (DebugLvl >= 1)
+		elog(DEBUG, "CommitTransactionCommand");
+	set_ps_display("commit");	/* XXX probably the wrong place to do this */
+	CommitTransactionCommand();
+#ifdef SHOW_MEMORY_STATS
+	/* print mem stats at each commit for leak tracking */
+	if (ShowStats)
+		MemoryContextStats(TopMemoryContext);
+#endif
+}
+
+
 /* --------------------------------
  *		signal handler routines used in PostgresMain()
  *
@@ -1397,7 +1614,7 @@ PostgresMain(int argc, char *argv[], int real_argc, char *real_argv[], const cha
 	if (!IsUnderPostmaster)
 	{
 		puts("\nPOSTGRES backend interactive interface ");
-		puts("$Revision: 1.177 $ $Date: 2000/10/03 03:11:19 $\n");
+		puts("$Revision: 1.178 $ $Date: 2000/10/07 00:58:18 $\n");
 	}
 
 	/*
@@ -1524,22 +1741,20 @@ PostgresMain(int argc, char *argv[], int real_argc, char *real_argv[], const cha
 		{
 				/* ----------------
 				 *	'F' indicates a fastpath call.
-				 *		XXX HandleFunctionRequest
 				 * ----------------
 				 */
 			case 'F':
-				IsEmptyQuery = false;
-
 				/* start an xact for this function invocation */
-				if (DebugLvl >= 1)
-					elog(DEBUG, "StartTransactionCommand");
-				StartTransactionCommand();
+				start_xact_command();
 
 				if (HandleFunctionRequest() == EOF)
 				{
 					/* lost frontend connection during F message input */
 					goto normalexit;
 				}
+
+				/* commit the function-invocation transaction */
+				finish_xact_command();
 				break;
 
 				/* ----------------
@@ -1551,35 +1766,28 @@ PostgresMain(int argc, char *argv[], int real_argc, char *real_argv[], const cha
 				{
 					/* ----------------
 					 *	if there is nothing in the input buffer, don't bother
-					 *	trying to parse and execute anything..
+					 *	trying to parse and execute anything; just send
+					 *	back a quick NullCommand response.
 					 * ----------------
 					 */
-					IsEmptyQuery = true;
+					if (IsUnderPostmaster)
+						NullCommand(Remote);
 				}
 				else
 				{
 					/* ----------------
 					 *	otherwise, process the input string.
+					 *
+					 * Note: transaction command start/end is now done
+					 * within pg_exec_query_string(), not here.
 					 * ----------------
 					 */
-					IsEmptyQuery = false;
 					if (Show_query_stats)
 						ResetUsage();
 
-					/* start an xact for this query */
-					if (DebugLvl >= 1)
-						elog(DEBUG, "StartTransactionCommand");
-					StartTransactionCommand();
-
-					pg_exec_query_dest(parser_input->data,
-									   whereToSendOutput,
-									   QueryContext);
-
-					/*
-					 * Invoke IMMEDIATE constraint triggers
-					 *
-					 */
-					DeferredTriggerEndQuery();
+					pg_exec_query_string(parser_input->data,
+										 whereToSendOutput,
+										 QueryContext);
 
 					if (Show_query_stats)
 					{
@@ -1603,39 +1811,14 @@ PostgresMain(int argc, char *argv[], int real_argc, char *real_argv[], const cha
 				elog(ERROR, "unknown frontend message was received");
 		}
 
-		/* ----------------
-		 *	 (6) commit the current transaction
-		 *
-		 *	 Note: if we had an empty input buffer, then we didn't
-		 *	 call pg_exec_query_dest, so we don't bother to commit
-		 *	 this transaction.
-		 * ----------------
-		 */
-		if (!IsEmptyQuery)
-		{
-			if (DebugLvl >= 1)
-				elog(DEBUG, "CommitTransactionCommand");
-			set_ps_display("commit");
-			CommitTransactionCommand();
-#ifdef SHOW_MEMORY_STATS
-			/* print global-context stats at each commit for leak tracking */
-			if (ShowStats)
-				MemoryContextStats(TopMemoryContext);
-#endif
-		}
-		else
-		{
-			if (IsUnderPostmaster)
-				NullCommand(Remote);
-		}
-
 #ifdef MEMORY_CONTEXT_CHECKING
 		/*
-		 * Check all memory after each backend loop
+		 * Check all memory after each backend loop.  This is a rather
+		 * weird place to do it, perhaps.
 		 */
 		MemoryContextCheck(TopMemoryContext);	
 #endif
-	}							/* end of main loop */
+	}							/* end of input-reading loop */
 
 normalexit:
 	ExitAfterAbort = true;		/* ensure we will exit if elog during abort */
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index 9edb092e62da06be7cef3b62b7f60afc8dbac6f3..9b52e9b66cd266616dd8935d825d4dfd4b85ea42 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.94 2000/09/12 05:09:45 momjian Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/tcop/utility.c,v 1.95 2000/10/07 00:58:18 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -37,6 +37,7 @@
 #include "commands/view.h"
 #include "miscadmin.h"
 #include "parser/parse.h"
+#include "parser/parse_expr.h"
 #include "rewrite/rewriteDefine.h"
 #include "rewrite/rewriteRemove.h"
 #include "tcop/utility.h"
@@ -45,24 +46,6 @@
 #include "utils/syscache.h"
 
 
-/* ----------------
- *		CHECK_IF_ABORTED() is used to avoid doing unnecessary
- *		processing within an aborted transaction block.
- * ----------------
- */
- /* we have to use IF because of the 'break' */
-#define CHECK_IF_ABORTED() \
-if (1) \
-{ \
-	if (IsAbortedTransactionBlockState()) \
-	{ \
-		elog(NOTICE, "current transaction is aborted, " \
-			 "queries ignored until end of transaction block"); \
-		commandTag = "*ABORT STATE*"; \
-		break; \
-	} \
-} else
-
 /* ----------------
  *		general utility function invoker
  * ----------------
@@ -90,7 +73,6 @@ ProcessUtility(Node *parsetree,
 				{
 					case BEGIN_TRANS:
 						set_ps_display(commandTag = "BEGIN");
-						CHECK_IF_ABORTED();
 						BeginTransactionBlock();
 						break;
 
@@ -116,7 +98,6 @@ ProcessUtility(Node *parsetree,
 				ClosePortalStmt *stmt = (ClosePortalStmt *) parsetree;
 
 				set_ps_display(commandTag = "CLOSE");
-				CHECK_IF_ABORTED();
 
 				PerformPortalClose(stmt->portalname, dest);
 			}
@@ -130,7 +111,6 @@ ProcessUtility(Node *parsetree,
 				int			count;
 
 				set_ps_display(commandTag = (stmt->ismove) ? "MOVE" : "FETCH");
-				CHECK_IF_ABORTED();
 
 				SetQuerySnapshot();
 
@@ -153,7 +133,6 @@ ProcessUtility(Node *parsetree,
 			 */
 		case T_CreateStmt:
 			set_ps_display(commandTag = "CREATE");
-			CHECK_IF_ABORTED();
 
 			DefineRelation((CreateStmt *) parsetree, RELKIND_RELATION);
 
@@ -174,7 +153,6 @@ ProcessUtility(Node *parsetree,
 				List	   *arg;
 
 				set_ps_display(commandTag = "DROP");
-				CHECK_IF_ABORTED();
 
 				/* check as much as we can before we start dropping ... */
 				foreach(arg, args)
@@ -216,7 +194,6 @@ ProcessUtility(Node *parsetree,
 				Relation	rel;
 
 				set_ps_display(commandTag = "TRUNCATE");
-				CHECK_IF_ABORTED();
 
 				relname = ((TruncateStmt *) parsetree)->relName;
 				if (!allowSystemTableMods && IsSystemRelationName(relname))
@@ -243,27 +220,23 @@ ProcessUtility(Node *parsetree,
 
 		case T_CommentStmt:
 			{
-
 				CommentStmt *statement;
 
 				statement = ((CommentStmt *) parsetree);
 
 				set_ps_display(commandTag = "COMMENT");
-				CHECK_IF_ABORTED();
+
 				CommentObject(statement->objtype, statement->objname,
 							  statement->objproperty, statement->objlist,
 							  statement->comment);
 			}
 			break;
 
-
-
 		case T_CopyStmt:
 			{
 				CopyStmt   *stmt = (CopyStmt *) parsetree;
 
 				set_ps_display(commandTag = "COPY");
-				CHECK_IF_ABORTED();
 
 				if (stmt->direction != FROM)
 					SetQuerySnapshot();
@@ -292,7 +265,6 @@ ProcessUtility(Node *parsetree,
 				RenameStmt *stmt = (RenameStmt *) parsetree;
 
 				set_ps_display(commandTag = "ALTER");
-				CHECK_IF_ABORTED();
 
 				relname = stmt->relname;
 				if (!allowSystemTableMods && IsSystemRelationName(relname))
@@ -345,7 +317,6 @@ ProcessUtility(Node *parsetree,
 				AlterTableStmt *stmt = (AlterTableStmt *) parsetree;
 
 				set_ps_display(commandTag = "ALTER");
-				CHECK_IF_ABORTED();
 
 				/*
 				 * Some or all of these functions are recursive to cover
@@ -385,34 +356,10 @@ ProcessUtility(Node *parsetree,
 		case T_ChangeACLStmt:
 			{
 				ChangeACLStmt *stmt = (ChangeACLStmt *) parsetree;
-				List	   *i;
-				AclItem    *aip;
-				unsigned	modechg;
 
 				set_ps_display(commandTag = "CHANGE");
-				CHECK_IF_ABORTED();
-
-				aip = stmt->aclitem;
 
-				modechg = stmt->modechg;
-				foreach(i, stmt->relNames)
-				{
-					Relation	rel;
-
-					relname = strVal(lfirst(i));
-					rel = heap_openr(relname, AccessExclusiveLock);
-					if (rel && rel->rd_rel->relkind == RELKIND_INDEX)
-						elog(ERROR, "\"%s\" is an index relation",
-							 relname);
-					/* close rel, but keep lock until end of xact */
-					heap_close(rel, NoLock);
-#ifndef NO_SECURITY
-					if (!pg_ownercheck(GetUserId(), relname, RELNAME))
-						elog(ERROR, "you do not own class \"%s\"",
-							 relname);
-#endif
-					ChangeAcl(relname, aip, modechg);
-				}
+				ExecuteChangeACLStmt(stmt);
 			}
 			break;
 
@@ -426,7 +373,6 @@ ProcessUtility(Node *parsetree,
 				DefineStmt *stmt = (DefineStmt *) parsetree;
 
 				set_ps_display(commandTag = "CREATE");
-				CHECK_IF_ABORTED();
 
 				switch (stmt->defType)
 				{
@@ -450,14 +396,14 @@ ProcessUtility(Node *parsetree,
 				ViewStmt   *stmt = (ViewStmt *) parsetree;
 
 				set_ps_display(commandTag = "CREATE");
-				CHECK_IF_ABORTED();
+
 				DefineView(stmt->viewname, stmt->query);		/* retrieve parsetree */
 			}
 			break;
 
 		case T_ProcedureStmt:	/* CREATE FUNCTION */
 			set_ps_display(commandTag = "CREATE");
-			CHECK_IF_ABORTED();
+
 			CreateFunction((ProcedureStmt *) parsetree, dest);	/* everything */
 			break;
 
@@ -466,7 +412,7 @@ ProcessUtility(Node *parsetree,
 				IndexStmt  *stmt = (IndexStmt *) parsetree;
 
 				set_ps_display(commandTag = "CREATE");
-				CHECK_IF_ABORTED();
+
 				DefineIndex(stmt->relname,		/* relation name */
 							stmt->idxname,		/* index name */
 							stmt->accessMethod, /* am name */
@@ -491,14 +437,13 @@ ProcessUtility(Node *parsetree,
 					elog(ERROR, "%s: %s", relname, aclcheck_error_strings[aclcheck_result]);
 #endif
 				set_ps_display(commandTag = "CREATE");
-				CHECK_IF_ABORTED();
+
 				DefineQueryRewrite(stmt);
 			}
 			break;
 
 		case T_CreateSeqStmt:
 			set_ps_display(commandTag = "CREATE");
-			CHECK_IF_ABORTED();
 
 			DefineSequence((CreateSeqStmt *) parsetree);
 			break;
@@ -508,7 +453,6 @@ ProcessUtility(Node *parsetree,
 				ExtendStmt *stmt = (ExtendStmt *) parsetree;
 
 				set_ps_display(commandTag = "EXTEND");
-				CHECK_IF_ABORTED();
 
 				ExtendIndex(stmt->idxname,		/* index name */
 							(Expr *) stmt->whereClause, /* where */
@@ -521,7 +465,6 @@ ProcessUtility(Node *parsetree,
 				RemoveStmt *stmt = (RemoveStmt *) parsetree;
 
 				set_ps_display(commandTag = "DROP");
-				CHECK_IF_ABORTED();
 
 				switch (stmt->removeType)
 				{
@@ -581,10 +524,14 @@ ProcessUtility(Node *parsetree,
 		case T_RemoveAggrStmt:
 			{
 				RemoveAggrStmt *stmt = (RemoveAggrStmt *) parsetree;
+				char	   *typename = (char *) NULL;
 
 				set_ps_display(commandTag = "DROP");
-				CHECK_IF_ABORTED();
-				RemoveAggregate(stmt->aggname, stmt->aggtype);
+
+				if (stmt->aggtype != NULL)
+					typename = TypeNameToInternalName((TypeName *) stmt->aggtype);
+
+				RemoveAggregate(stmt->aggname, typename);
 			}
 			break;
 
@@ -593,27 +540,27 @@ ProcessUtility(Node *parsetree,
 				RemoveFuncStmt *stmt = (RemoveFuncStmt *) parsetree;
 
 				set_ps_display(commandTag = "DROP");
-				CHECK_IF_ABORTED();
-				RemoveFunction(stmt->funcname,
-							   length(stmt->args),
-							   stmt->args);
+
+				RemoveFunction(stmt->funcname, stmt->args);
 			}
 			break;
 
 		case T_RemoveOperStmt:
 			{
 				RemoveOperStmt *stmt = (RemoveOperStmt *) parsetree;
-				char	   *type1 = (char *) NULL;
-				char	   *type2 = (char *) NULL;
+				TypeName   *typenode1 = (TypeName *) lfirst(stmt->args);
+				TypeName   *typenode2 = (TypeName *) lsecond(stmt->args);
+				char	   *typename1 = (char *) NULL;
+				char	   *typename2 = (char *) NULL;
 
 				set_ps_display(commandTag = "DROP");
-				CHECK_IF_ABORTED();
 
-				if (lfirst(stmt->args) != NULL)
-					type1 = strVal(lfirst(stmt->args));
-				if (lsecond(stmt->args) != NULL)
-					type2 = strVal(lsecond(stmt->args));
-				RemoveOperator(stmt->opname, type1, type2);
+				if (typenode1 != NULL)
+					typename1 = TypeNameToInternalName(typenode1);
+				if (typenode2 != NULL)
+					typename2 = TypeNameToInternalName(typenode2);
+
+				RemoveOperator(stmt->opname, typename1, typename2);
 			}
 			break;
 
@@ -626,7 +573,7 @@ ProcessUtility(Node *parsetree,
 				CreatedbStmt *stmt = (CreatedbStmt *) parsetree;
 
 				set_ps_display(commandTag = "CREATE DATABASE");
-				CHECK_IF_ABORTED();
+
 				createdb(stmt->dbname, stmt->dbpath, stmt->encoding);
 			}
 			break;
@@ -636,7 +583,7 @@ ProcessUtility(Node *parsetree,
 				DropdbStmt *stmt = (DropdbStmt *) parsetree;
 
 				set_ps_display(commandTag = "DROP DATABASE");
-				CHECK_IF_ABORTED();
+
 				dropdb(stmt->dbname);
 			}
 			break;
@@ -647,7 +594,6 @@ ProcessUtility(Node *parsetree,
 				NotifyStmt *stmt = (NotifyStmt *) parsetree;
 
 				set_ps_display(commandTag = "NOTIFY");
-				CHECK_IF_ABORTED();
 
 				Async_Notify(stmt->relname);
 			}
@@ -658,7 +604,6 @@ ProcessUtility(Node *parsetree,
 				ListenStmt *stmt = (ListenStmt *) parsetree;
 
 				set_ps_display(commandTag = "LISTEN");
-				CHECK_IF_ABORTED();
 
 				Async_Listen(stmt->relname, MyProcPid);
 			}
@@ -669,7 +614,6 @@ ProcessUtility(Node *parsetree,
 				UnlistenStmt *stmt = (UnlistenStmt *) parsetree;
 
 				set_ps_display(commandTag = "UNLISTEN");
-				CHECK_IF_ABORTED();
 
 				Async_Unlisten(stmt->relname, MyProcPid);
 			}
@@ -684,7 +628,6 @@ ProcessUtility(Node *parsetree,
 				LoadStmt   *stmt = (LoadStmt *) parsetree;
 
 				set_ps_display(commandTag = "LOAD");
-				CHECK_IF_ABORTED();
 
 				closeAllVfds(); /* probably not necessary... */
 				load_file(stmt->filename);
@@ -696,7 +639,6 @@ ProcessUtility(Node *parsetree,
 				ClusterStmt *stmt = (ClusterStmt *) parsetree;
 
 				set_ps_display(commandTag = "CLUSTER");
-				CHECK_IF_ABORTED();
 
 				cluster(stmt->relname, stmt->indexname);
 			}
@@ -704,7 +646,7 @@ ProcessUtility(Node *parsetree,
 
 		case T_VacuumStmt:
 			set_ps_display(commandTag = "VACUUM");
-			CHECK_IF_ABORTED();
+
 			vacuum(((VacuumStmt *) parsetree)->vacrel,
 				   ((VacuumStmt *) parsetree)->verbose,
 				   ((VacuumStmt *) parsetree)->analyze,
@@ -716,7 +658,6 @@ ProcessUtility(Node *parsetree,
 				ExplainStmt *stmt = (ExplainStmt *) parsetree;
 
 				set_ps_display(commandTag = "EXPLAIN");
-				CHECK_IF_ABORTED();
 
 				ExplainQuery(stmt->query, stmt->verbose, dest);
 			}
@@ -732,7 +673,7 @@ ProcessUtility(Node *parsetree,
 				RecipeStmt *stmt = (RecipeStmt *) parsetree;
 
 				set_ps_display(commandTag = "EXECUTE RECIPE");
-				CHECK_IF_ABORTED();
+
 				beginRecipe(stmt);
 			}
 			break;
@@ -773,14 +714,12 @@ ProcessUtility(Node *parsetree,
 			 */
 		case T_CreateTrigStmt:
 			set_ps_display(commandTag = "CREATE");
-			CHECK_IF_ABORTED();
 
 			CreateTrigger((CreateTrigStmt *) parsetree);
 			break;
 
 		case T_DropTrigStmt:
 			set_ps_display(commandTag = "DROP");
-			CHECK_IF_ABORTED();
 
 			DropTrigger((DropTrigStmt *) parsetree);
 			break;
@@ -790,14 +729,12 @@ ProcessUtility(Node *parsetree,
 			 */
 		case T_CreatePLangStmt:
 			set_ps_display(commandTag = "CREATE");
-			CHECK_IF_ABORTED();
 
 			CreateProceduralLanguage((CreatePLangStmt *) parsetree);
 			break;
 
 		case T_DropPLangStmt:
 			set_ps_display(commandTag = "DROP");
-			CHECK_IF_ABORTED();
 
 			DropProceduralLanguage((DropPLangStmt *) parsetree);
 			break;
@@ -808,56 +745,48 @@ ProcessUtility(Node *parsetree,
 			 */
 		case T_CreateUserStmt:
 			set_ps_display(commandTag = "CREATE USER");
-			CHECK_IF_ABORTED();
 
 			CreateUser((CreateUserStmt *) parsetree);
 			break;
 
 		case T_AlterUserStmt:
 			set_ps_display(commandTag = "ALTER USER");
-			CHECK_IF_ABORTED();
 
 			AlterUser((AlterUserStmt *) parsetree);
 			break;
 
 		case T_DropUserStmt:
 			set_ps_display(commandTag = "DROP USER");
-			CHECK_IF_ABORTED();
 
 			DropUser((DropUserStmt *) parsetree);
 			break;
 
 		case T_LockStmt:
 			set_ps_display(commandTag = "LOCK TABLE");
-			CHECK_IF_ABORTED();
 
 			LockTableCommand((LockStmt *) parsetree);
 			break;
 
 		case T_ConstraintsSetStmt:
 			set_ps_display(commandTag = "SET CONSTRAINTS");
-			CHECK_IF_ABORTED();
 
 			DeferredTriggerSetState((ConstraintsSetStmt *) parsetree);
 			break;
 
 		case T_CreateGroupStmt:
 			set_ps_display(commandTag = "CREATE GROUP");
-			CHECK_IF_ABORTED();
 
 			CreateGroup((CreateGroupStmt *) parsetree);
 			break;
 
 		case T_AlterGroupStmt:
 			set_ps_display(commandTag = "ALTER GROUP");
-			CHECK_IF_ABORTED();
 
 			AlterGroup((AlterGroupStmt *) parsetree, "ALTER GROUP");
 			break;
 
 		case T_DropGroupStmt:
 			set_ps_display(commandTag = "DROP GROUP");
-			CHECK_IF_ABORTED();
 
 			DropGroup((DropGroupStmt *) parsetree);
 			break;
@@ -867,7 +796,6 @@ ProcessUtility(Node *parsetree,
 				ReindexStmt *stmt = (ReindexStmt *) parsetree;
 
 				set_ps_display(commandTag = "REINDEX");
-				CHECK_IF_ABORTED();
 
 				switch (stmt->reindexType)
 				{
diff --git a/src/backend/utils/adt/acl.c b/src/backend/utils/adt/acl.c
index 3d43a45cd1e757c9e2071aa2dfedaddd8843b5df..401ed1a9375477363bc05460a637ecc3e3a6ba06 100644
--- a/src/backend/utils/adt/acl.c
+++ b/src/backend/utils/adt/acl.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/utils/adt/acl.c,v 1.49 2000/10/02 04:49:27 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/utils/adt/acl.c,v 1.50 2000/10/07 00:58:19 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -16,10 +16,12 @@
 
 #include "postgres.h"
 
+#include "access/heapam.h"
 #include "catalog/catalog.h"
 #include "catalog/pg_shadow.h"
 #include "catalog/pg_type.h"
 #include "lib/stringinfo.h"
+#include "miscadmin.h"
 #include "utils/acl.h"
 #include "utils/builtins.h"
 #include "utils/memutils.h"
@@ -561,7 +563,52 @@ aclcontains(PG_FUNCTION_ARGS)
 	PG_RETURN_BOOL(false);
 }
 
-/* parser support routines */
+/*
+ * ExecuteChangeACLStmt
+ *		Called to execute the utility command type ChangeACLStmt
+ */
+void
+ExecuteChangeACLStmt(ChangeACLStmt *stmt)
+{
+	AclItem		aclitem;
+	unsigned	modechg;
+	List	   *i;
+
+	/* see comment in pg_type.h */
+	Assert(ACLITEMSIZE == sizeof(AclItem));
+
+	/* Convert string ACL spec into internal form */
+	aclparse(stmt->aclString, &aclitem, &modechg);
+
+	foreach(i, stmt->relNames)
+	{
+		char	   *relname = strVal(lfirst(i));
+		Relation	rel;
+
+		rel = heap_openr(relname, AccessExclusiveLock);
+		if (rel && rel->rd_rel->relkind == RELKIND_INDEX)
+			elog(ERROR, "\"%s\" is an index relation",
+				 relname);
+#ifndef NO_SECURITY
+		if (!pg_ownercheck(GetUserId(), relname, RELNAME))
+			elog(ERROR, "you do not own class \"%s\"",
+				 relname);
+#endif
+		ChangeAcl(relname, &aclitem, modechg);
+		/* close rel, but keep lock until end of xact */
+		heap_close(rel, NoLock);
+	}
+}
+
+
+/*
+ * Parser support routines for ACL-related statements.
+ *
+ * XXX CAUTION: these are called from gram.y, which is not allowed to
+ * do any table accesses.  Therefore, it is not kosher to do things
+ * like trying to translate usernames to user IDs here.  Keep it all
+ * in string form until statement execution time.
+ */
 
 /*
  * aclmakepriv
@@ -569,9 +616,7 @@ aclcontains(PG_FUNCTION_ARGS)
  * and a new privilege
  *
  * does not add duplicate privileges
- *
  */
-
 char *
 aclmakepriv(char *old_privlist, char new_priv)
 {
@@ -619,12 +664,9 @@ aclmakepriv(char *old_privlist, char new_priv)
  *						"G"  - group
  *						"U"  - user
  *
- * concatenates the two strings together with a space in between
- *
- * this routine is used in the parser
- *
+ * Just concatenates the two strings together with a space in between.
+ * Per above comments, we can't try to resolve a user or group name here.
  */
-
 char *
 aclmakeuser(char *user_type, char *user)
 {
@@ -635,20 +677,16 @@ aclmakeuser(char *user_type, char *user)
 	return user_list;
 }
 
-
 /*
  * makeAclStmt:
- *	  this is a helper routine called by the parser
- * create a ChangeAclStmt
- *	  we take in the privilegs, relation_name_list, and grantee
- * as well as a single character '+' or '-' to indicate grant or revoke
+ *	  create a ChangeACLStmt at parse time.
+ *	  we take in the privileges, relation_name_list, and grantee
+ *	  as well as a single character '+' or '-' to indicate grant or revoke
  *
- * returns a new ChangeACLStmt*
- *
- * this routines works by creating a old-style changle acl string and
- * then calling aclparse;
+ * We convert the information to the same external form recognized by
+ * aclitemin (see aclparse), and save that string in the ChangeACLStmt.
+ * Conversion to internal form happens when the statement is executed.
  */
-
 ChangeACLStmt *
 makeAclStmt(char *privileges, List *rel_list, char *grantee,
 			char grant_or_revoke)
@@ -658,11 +696,6 @@ makeAclStmt(char *privileges, List *rel_list, char *grantee,
 
 	initStringInfo(&str);
 
-	/* see comment in pg_type.h */
-	Assert(ACLITEMSIZE == sizeof(AclItem));
-
-	n->aclitem = (AclItem *) palloc(sizeof(AclItem));
-
 	/* the grantee string is "G <group_name>", "U <user_name>", or "ALL" */
 	if (grantee[0] == 'G')		/* group permissions */
 	{
@@ -683,7 +716,8 @@ makeAclStmt(char *privileges, List *rel_list, char *grantee,
 						 grant_or_revoke, privileges);
 	}
 	n->relNames = rel_list;
-	aclparse(str.data, n->aclitem, (unsigned *) &n->modechg);
+	n->aclString = pstrdup(str.data);
+
 	pfree(str.data);
 	return n;
 }
diff --git a/src/include/commands/defrem.h b/src/include/commands/defrem.h
index 2a7a4bdcfa82464f297871162df9275581022b1a..dce401d7d9beb725c6b801e9250f80afcf54c665 100644
--- a/src/include/commands/defrem.h
+++ b/src/include/commands/defrem.h
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2000, PostgreSQL, Inc
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: defrem.h,v 1.20 2000/08/24 03:29:09 tgl Exp $
+ * $Id: defrem.h,v 1.21 2000/10/07 00:58:20 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -48,7 +48,7 @@ extern void DefineType(char *name, List *parameters);
 /*
  * prototypes in remove.c
  */
-extern void RemoveFunction(char *functionName, int nargs, List *argNameList);
+extern void RemoveFunction(char *functionName, List *argTypes);
 extern void RemoveOperator(char *operatorName,
 			   char *typeName1, char *typeName2);
 extern void RemoveType(char *typeName);
diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h
index 41309426e8b04fea273146e52c9b70d1d6a89a6b..b50990fe299c35c2bd6e134c1c845f0e39deb9fd 100644
--- a/src/include/nodes/parsenodes.h
+++ b/src/include/nodes/parsenodes.h
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2000, PostgreSQL, Inc
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: parsenodes.h,v 1.115 2000/10/05 19:11:36 tgl Exp $
+ * $Id: parsenodes.h,v 1.116 2000/10/07 00:58:21 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -114,9 +114,8 @@ typedef struct AlterTableStmt
 typedef struct ChangeACLStmt
 {
 	NodeTag		type;
-	struct AclItem *aclitem;
-	unsigned	modechg;
 	List	   *relNames;
+	char	   *aclString;
 } ChangeACLStmt;
 
 /* ----------------------
@@ -488,10 +487,8 @@ typedef struct ProcedureStmt
 {
 	NodeTag		type;
 	char	   *funcname;		/* name of function to create */
-	List	   *defArgs;		/* list of definitions a list of strings
-								 * (as Value *) */
-	Node	   *returnType;		/* the return type (as a string or a
-								 * TypeName (ie.setof) */
+	List	   *argTypes;		/* list of argument types (TypeName nodes) */
+	Node	   *returnType;		/* the return type (a TypeName node) */
 	List	   *withClause;		/* a list of DefElem */
 	List	   *as;				/* definition of function body */
 	char	   *language;		/* C, SQL, etc */
@@ -505,7 +502,7 @@ typedef struct RemoveAggrStmt
 {
 	NodeTag		type;
 	char	   *aggname;		/* aggregate to drop */
-	char	   *aggtype;		/* for this type */
+	Node	   *aggtype;		/* TypeName for input datatype, or NULL */
 } RemoveAggrStmt;
 
 /* ----------------------
diff --git a/src/include/parser/analyze.h b/src/include/parser/analyze.h
index afd8a34fb3e43ace2534ef9f9f2777d824d67ec3..9d60e0f64c8f65c85a328feebcb20d93c5beea51 100644
--- a/src/include/parser/analyze.h
+++ b/src/include/parser/analyze.h
@@ -6,7 +6,7 @@
  * Portions Copyright (c) 1996-2000, PostgreSQL, Inc
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: analyze.h,v 1.11 2000/10/05 19:11:38 tgl Exp $
+ * $Id: analyze.h,v 1.12 2000/10/07 00:58:21 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -15,6 +15,6 @@
 
 #include "parser/parse_node.h"
 
-extern List *parse_analyze(List *pl, ParseState *parentParseState);
+extern List *parse_analyze(Node *parseTree, ParseState *parentParseState);
 
 #endif	 /* ANALYZE_H */
diff --git a/src/include/parser/parse_expr.h b/src/include/parser/parse_expr.h
index 7f1b5d5122d0fcea83f3e5c2c8e4ed6e9cc72209..410a24bc45503a6effa5c74a5635f3beb0fffeca 100644
--- a/src/include/parser/parse_expr.h
+++ b/src/include/parser/parse_expr.h
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2000, PostgreSQL, Inc
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: parse_expr.h,v 1.19 2000/06/15 03:32:55 momjian Exp $
+ * $Id: parse_expr.h,v 1.20 2000/10/07 00:58:21 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -26,5 +26,6 @@ extern Oid	exprType(Node *expr);
 extern int32 exprTypmod(Node *expr);
 extern bool exprIsLengthCoercion(Node *expr, int32 *coercedTypmod);
 extern void parse_expr_init(void);
+extern char *TypeNameToInternalName(TypeName *typename);
 
 #endif	 /* PARSE_EXPR_H */
diff --git a/src/include/tcop/tcopprot.h b/src/include/tcop/tcopprot.h
index 562efca25ccf5803bb27b089bb828ba74a170080..062a58184074aefdf0334abe1372ea807d00dbcf 100644
--- a/src/include/tcop/tcopprot.h
+++ b/src/include/tcop/tcopprot.h
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2000, PostgreSQL, Inc
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: tcopprot.h,v 1.34 2000/09/06 14:15:28 petere Exp $
+ * $Id: tcopprot.h,v 1.35 2000/10/07 00:58:23 tgl Exp $
  *
  * OLD COMMENTS
  *	  This file was created so that other c files could get the two
@@ -35,9 +35,9 @@ extern bool ShowPortNumber;
 extern List *pg_parse_and_rewrite(char *query_string,
 								  Oid *typev, int nargs);
 extern Plan *pg_plan_query(Query *querytree);
-extern void pg_exec_query_dest(char *query_string,
-							   CommandDest dest,
-							   MemoryContext parse_context);
+extern void pg_exec_query_string(char *query_string,
+								 CommandDest dest,
+								 MemoryContext parse_context);
 
 #endif	 /* BOOTSTRAP_INCLUDE */
 
diff --git a/src/include/utils/acl.h b/src/include/utils/acl.h
index 4add4202fcb0e2588e7747f4f6aa9010e334859e..8836bd1733e01ff90241c00ca933b500c654e234 100644
--- a/src/include/utils/acl.h
+++ b/src/include/utils/acl.h
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2000, PostgreSQL, Inc
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: acl.h,v 1.28 2000/10/02 04:49:27 tgl Exp $
+ * $Id: acl.h,v 1.29 2000/10/07 00:58:23 tgl Exp $
  *
  * NOTES
  *	  For backward-compatibility purposes we have to allow there
@@ -167,12 +167,15 @@ extern char *aclcheck_error_strings[];
 /*#define ACLDEBUG_TRACE*/
 
 /*
- * routines used internally (parser, etc.)
+ * routines used internally
  */
 extern Acl *acldefault(char *relname, AclId ownerid);
 
 extern Acl *aclinsert3(Acl *old_acl, AclItem *mod_aip, unsigned modechg);
 
+/*
+ * routines used by the parser
+ */
 extern char *aclmakepriv(char *old_privlist, char new_priv);
 extern char *aclmakeuser(char *user_type, char *user);
 extern ChangeACLStmt *makeAclStmt(char *privs, List *rel_list, char *grantee,
@@ -187,6 +190,7 @@ extern Datum aclitemout(PG_FUNCTION_ARGS);
 extern Datum aclinsert(PG_FUNCTION_ARGS);
 extern Datum aclremove(PG_FUNCTION_ARGS);
 extern Datum aclcontains(PG_FUNCTION_ARGS);
+extern void ExecuteChangeACLStmt(ChangeACLStmt *stmt);
 
 /*
  * prototypes for functions in aclchk.c