diff --git a/src/backend/commands/command.c b/src/backend/commands/command.c
index f01cf4498fdd88bcdb8f95d3e257543394436429..676caba22d583e345c0d2d17fb2d7aba812623c0 100644
--- a/src/backend/commands/command.c
+++ b/src/backend/commands/command.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/commands/Attic/command.c,v 1.154 2002/02/19 20:11:12 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/commands/Attic/command.c,v 1.155 2002/02/26 22:47:04 tgl Exp $
  *
  * NOTES
  *	  The PerformAddAttribute() code, like most of the relation
@@ -89,16 +89,25 @@ PortalCleanup(Portal portal)
 	MemoryContextSwitchTo(oldcontext);
 }
 
-/* --------------------------------
- *		PerformPortalFetch
- * --------------------------------
+
+/*
+ * PerformPortalFetch
+ *
+ *	name: name of portal
+ *	forward: forward or backward fetch?
+ *	count: # of tuples to fetch (0 implies all)
+ *	dest: where to send results
+ *	completionTag: points to a buffer of size COMPLETION_TAG_BUFSIZE
+ *		in which to store a command completion status string.
+ *
+ * completionTag may be NULL if caller doesn't want a status string.
  */
 void
 PerformPortalFetch(char *name,
 				   bool forward,
 				   int count,
-				   char *tag,
-				   CommandDest dest)
+				   CommandDest dest,
+				   char *completionTag)
 {
 	Portal		portal;
 	QueryDesc  *queryDesc;
@@ -107,6 +116,10 @@ PerformPortalFetch(char *name,
 	CommandId	savedId;
 	bool		temp_desc = false;
 
+	/* initialize completion status in case of early exit */
+	if (completionTag)
+		strcpy(completionTag, (dest == None) ? "MOVE 0" : "FETCH 0");
+
 	/*
 	 * sanity checks
 	 */
@@ -167,7 +180,7 @@ PerformPortalFetch(char *name,
 								 * relations */
 				 false,			/* this is a portal fetch, not a "retrieve
 								 * portal" */
-				 tag,
+				 NULL,			/* not used */
 				 queryDesc->dest);
 
 	/*
@@ -193,16 +206,15 @@ PerformPortalFetch(char *name,
 		{
 			ExecutorRun(queryDesc, estate, EXEC_FOR, (long) count);
 
-			/*
-			 * I use CMD_UPDATE, because no CMD_MOVE or the like exists,
-			 * and I would like to provide the same kind of info as
-			 * CMD_UPDATE
-			 */
-			UpdateCommandInfo(CMD_UPDATE, 0, estate->es_processed);
 			if (estate->es_processed > 0)
 				portal->atStart = false;		/* OK to back up now */
 			if (count <= 0 || (int) estate->es_processed < count)
 				portal->atEnd = true;	/* we retrieved 'em all */
+
+			if (completionTag)
+				snprintf(completionTag, COMPLETION_TAG_BUFSIZE, "%s %u",
+						 (dest == None) ? "MOVE" : "FETCH",
+						 estate->es_processed);
 		}
 	}
 	else
@@ -211,16 +223,15 @@ PerformPortalFetch(char *name,
 		{
 			ExecutorRun(queryDesc, estate, EXEC_BACK, (long) count);
 
-			/*
-			 * I use CMD_UPDATE, because no CMD_MOVE or the like exists,
-			 * and I would like to provide the same kind of info as
-			 * CMD_UPDATE
-			 */
-			UpdateCommandInfo(CMD_UPDATE, 0, estate->es_processed);
 			if (estate->es_processed > 0)
 				portal->atEnd = false;	/* OK to go forward now */
 			if (count <= 0 || (int) estate->es_processed < count)
 				portal->atStart = true; /* we retrieved 'em all */
+
+			if (completionTag)
+				snprintf(completionTag, COMPLETION_TAG_BUFSIZE, "%s %u",
+						 (dest == None) ? "MOVE" : "FETCH",
+						 estate->es_processed);
 		}
 	}
 
@@ -236,11 +247,6 @@ PerformPortalFetch(char *name,
 		pfree(queryDesc);
 
 	MemoryContextSwitchTo(oldcontext);
-
-	/*
-	 * Note: the "end-of-command" tag is returned by higher-level utility
-	 * code
-	 */
 }
 
 /* --------------------------------
diff --git a/src/backend/commands/explain.c b/src/backend/commands/explain.c
index c14d4ce3e5acad9e1727c2596d1760af1a76b1f2..9c96687314737e2ac5a1e99313063d067dc164af 100644
--- a/src/backend/commands/explain.c
+++ b/src/backend/commands/explain.c
@@ -5,7 +5,7 @@
  * Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994-5, Regents of the University of California
  *
- * $Header: /cvsroot/pgsql/src/backend/commands/explain.c,v 1.67 2001/10/25 05:49:25 momjian Exp $
+ * $Header: /cvsroot/pgsql/src/backend/commands/explain.c,v 1.68 2002/02/26 22:47:04 tgl Exp $
  *
  */
 
@@ -120,7 +120,7 @@ ExplainOneQuery(Query *query, bool verbose, bool analyze, CommandDest dest)
 		plan->instrument = InstrAlloc();
 
 		gettimeofday(&starttime, NULL);
-		ProcessQuery(query, plan, None);
+		ProcessQuery(query, plan, None, NULL);
 		CommandCounterIncrement();
 		gettimeofday(&endtime, NULL);
 
diff --git a/src/backend/executor/functions.c b/src/backend/executor/functions.c
index 7f73cdd26a933e6c5f53a5dce90d180b3ea3ec8c..c38b8077f520ce2d83ba0b7f8d2c6c8507c2595b 100644
--- a/src/backend/executor/functions.c
+++ b/src/backend/executor/functions.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/executor/functions.c,v 1.47 2001/10/28 06:25:43 momjian Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/executor/functions.c,v 1.48 2002/02/26 22:47:05 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -275,7 +275,7 @@ postquel_getnext(execution_state *es)
 		/*
 		 * Process a utility command. (create, destroy...)	DZ - 30-8-1996
 		 */
-		ProcessUtility(es->qd->parsetree->utilityStmt, es->qd->dest);
+		ProcessUtility(es->qd->parsetree->utilityStmt, es->qd->dest, NULL);
 		if (!LAST_POSTQUEL_COMMAND(es))
 			CommandCounterIncrement();
 		return (TupleTableSlot *) NULL;
diff --git a/src/backend/executor/spi.c b/src/backend/executor/spi.c
index b9ab64226585e7f3a2ca5c48602cf6d4eeac1ec7..05044d6cd39a123c39ef7b777e3717c872acd44c 100644
--- a/src/backend/executor/spi.c
+++ b/src/backend/executor/spi.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/executor/spi.c,v 1.65 2002/02/14 15:24:08 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/executor/spi.c,v 1.66 2002/02/26 22:47:05 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -1011,7 +1011,7 @@ _SPI_execute(char *src, int tcount, _SPI_plan *plan)
 			res = SPI_OK_UTILITY;
 			if (plan == NULL)
 			{
-				ProcessUtility(queryTree->utilityStmt, None);
+				ProcessUtility(queryTree->utilityStmt, None, NULL);
 				if (!islastquery)
 					CommandCounterIncrement();
 				else
@@ -1085,7 +1085,7 @@ _SPI_execute_plan(_SPI_plan *plan, Datum *Values, char *Nulls, int tcount)
 
 		if (queryTree->commandType == CMD_UTILITY)
 		{
-			ProcessUtility(queryTree->utilityStmt, None);
+			ProcessUtility(queryTree->utilityStmt, None, NULL);
 			if (!islastquery)
 				CommandCounterIncrement();
 			else
diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c
index 03ceb08eba2c6aded68543c4f4f3fd10b7c9cbe1..9838d98553176d1e6972603b5009c1d74b6b8d47 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.162 2002/02/24 20:20:20 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/nodes/copyfuncs.c,v 1.163 2002/02/26 22:47:05 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -1755,6 +1755,7 @@ _copyQuery(Query *from)
 	newnode->isTemp = from->isTemp;
 	newnode->hasAggs = from->hasAggs;
 	newnode->hasSubLinks = from->hasSubLinks;
+	newnode->originalQuery = from->originalQuery;
 
 	Node_Copy(from, newnode, rtable);
 	Node_Copy(from, newnode, jointree);
diff --git a/src/backend/nodes/equalfuncs.c b/src/backend/nodes/equalfuncs.c
index 2ea41d6e522600cf7cf7690fa05a1d3a72f3ef90..408c14bb7e4be2bbb4744671bc6d921eb28deb44 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.110 2002/02/24 20:20:20 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/nodes/equalfuncs.c,v 1.111 2002/02/26 22:47:05 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -593,6 +593,7 @@ _equalQuery(Query *a, Query *b)
 		return false;
 	if (a->hasSubLinks != b->hasSubLinks)
 		return false;
+	/* we deliberately ignore originalQuery */
 	if (!equal(a->rtable, b->rtable))
 		return false;
 	if (!equal(a->jointree, b->jointree))
diff --git a/src/backend/nodes/readfuncs.c b/src/backend/nodes/readfuncs.c
index 26fbebea77ae88163ed3d22611a36849efacf8cc..4414c032150594d283e2cf91607547fc6159496a 100644
--- a/src/backend/nodes/readfuncs.c
+++ b/src/backend/nodes/readfuncs.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/nodes/readfuncs.c,v 1.113 2001/10/25 05:49:31 momjian Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/nodes/readfuncs.c,v 1.114 2002/02/26 22:47:07 tgl Exp $
  *
  * NOTES
  *	  Most of the read functions for plan nodes are tested. (In fact, they
@@ -168,6 +168,9 @@ _readQuery(void)
 	token = pg_strtok(&length); /* get hasSubLinks */
 	local_node->hasSubLinks = strtobool(token);
 
+	/* we always want originalQuery to be false in a read-in query */
+	local_node->originalQuery = false;
+
 	token = pg_strtok(&length); /* skip :rtable */
 	local_node->rtable = nodeRead(true);
 
diff --git a/src/backend/parser/analyze.c b/src/backend/parser/analyze.c
index aed1cf296495574e53393cd2ffadebc8bc9c071f..35944ff65333c67e8cfd5319190039da3ef53d3b 100644
--- a/src/backend/parser/analyze.c
+++ b/src/backend/parser/analyze.c
@@ -6,7 +6,7 @@
  * Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- *	$Header: /cvsroot/pgsql/src/backend/parser/analyze.c,v 1.214 2002/02/25 04:21:55 momjian Exp $
+ *	$Header: /cvsroot/pgsql/src/backend/parser/analyze.c,v 1.215 2002/02/26 22:47:08 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -122,10 +122,11 @@ parse_analyze(Node *parseTree, ParseState *parentParseState)
 {
 	List	   *result = NIL;
 	ParseState *pstate = make_parsestate(parentParseState);
-	Query	   *query;
 	/* Lists to return extra commands from transformation */
-	List *extras_before = NIL;
-	List *extras_after = NIL;
+	List	   *extras_before = NIL;
+	List	   *extras_after = NIL;
+	Query	   *query;
+	List	   *listscan;
 
 	query = transformStmt(pstate, parseTree, &extras_before, &extras_after);
 	release_pstate_resources(pstate);
@@ -144,6 +145,18 @@ parse_analyze(Node *parseTree, ParseState *parentParseState)
 		extras_after = lnext(extras_after);
 	}
 
+	/*
+	 * Make sure that only the original query is marked original.
+	 * We have to do this explicitly since recursive calls of parse_analyze
+	 * will have set originalQuery in some of the added-on queries.
+	 */
+	foreach(listscan, result)
+	{
+		Query  *q = lfirst(listscan);
+
+		q->originalQuery = (q == query);
+	}
+
 	pfree(pstate);
 
 	return result;
diff --git a/src/backend/tcop/dest.c b/src/backend/tcop/dest.c
index 5ed8896938a8f6868dc344c24b2e92055649877b..f055bc3137b251a1ef46cf8ba30524e66238b382 100644
--- a/src/backend/tcop/dest.c
+++ b/src/backend/tcop/dest.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/tcop/dest.c,v 1.46 2001/10/28 06:25:51 momjian Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/tcop/dest.c,v 1.47 2002/02/26 22:47:08 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -41,8 +41,6 @@
 #include "libpq/pqformat.h"
 
 
-static char CommandInfo[32] = {0};
-
 /* ----------------
  *		dummy DestReceiver functions
  * ----------------
@@ -102,7 +100,6 @@ BeginCommand(char *pname,
 			 * if this is a "retrieve into portal" query, done because
 			 * nothing needs to be sent to the fe.
 			 */
-			CommandInfo[0] = '\0';
 			if (isIntoPortal)
 				break;
 
@@ -198,30 +195,22 @@ DestToFunction(CommandDest dest)
 }
 
 /* ----------------
- *		EndCommand - tell destination that no more tuples will arrive
+ *		EndCommand - tell destination that query is complete
  * ----------------
  */
 void
-EndCommand(char *commandTag, CommandDest dest)
+EndCommand(const char *commandTag, CommandDest dest)
 {
-	char		buf[64];
-
 	switch (dest)
 	{
 		case Remote:
 		case RemoteInternal:
-
-			/*
-			 * tell the fe that the query is over
-			 */
-			sprintf(buf, "%s%s", commandTag, CommandInfo);
-			pq_puttextmessage('C', buf);
-			CommandInfo[0] = '\0';
+			pq_puttextmessage('C', commandTag);
 			break;
 
-		case Debug:
 		case None:
-		default:
+		case Debug:
+		case SPI:
 			break;
 	}
 }
@@ -317,23 +306,3 @@ ReadyForQuery(CommandDest dest)
 			break;
 	}
 }
-
-void
-UpdateCommandInfo(int operation, Oid lastoid, uint32 tuples)
-{
-	switch (operation)
-	{
-		case CMD_INSERT:
-			if (tuples > 1)
-				lastoid = InvalidOid;
-			sprintf(CommandInfo, " %u %u", lastoid, tuples);
-			break;
-		case CMD_DELETE:
-		case CMD_UPDATE:
-			sprintf(CommandInfo, " %u", tuples);
-			break;
-		default:
-			CommandInfo[0] = '\0';
-			break;
-	}
-}
diff --git a/src/backend/tcop/postgres.c b/src/backend/tcop/postgres.c
index 976b434655f307f2313930d55f8b56473f8d9387..61fdf9c5acfd83ef17c6e8b15e9f0e9e248dedce 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.247 2002/02/23 01:31:36 petere Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/tcop/postgres.c,v 1.248 2002/02/26 22:47:08 tgl Exp $
  *
  * NOTES
  *	  this is the "main" module of the postgres backend and
@@ -64,6 +64,7 @@
 
 #include "pgstat.h"
 
+
 /* ----------------
  *		global variables
  * ----------------
@@ -87,6 +88,13 @@ bool		InError = false;
 
 static bool EchoQuery = false;	/* default don't echo */
 
+/*
+ * Flag to mark SIGHUP. Whenever the main loop comes around it
+ * will reread the configuration file. (Better than doing the
+ * reading in the signal handler, ey?)
+ */
+static volatile bool got_SIGHUP = false;
+
 /* ----------------
  *		people who want to use EOF should #define DONTUSENEWLINE in
  *		tcop/tcopdebug.h
@@ -118,13 +126,7 @@ static void start_xact_command(void);
 static void finish_xact_command(void);
 static void SigHupHandler(SIGNAL_ARGS);
 static void FloatExceptionHandler(SIGNAL_ARGS);
-
-/*
- * Flag to mark SIGHUP. Whenever the main loop comes around it
- * will reread the configuration file. (Better than doing the
- * reading in the signal handler, ey?)
- */
-static volatile bool got_SIGHUP = false;
+static const char *CreateCommandTag(Node *parsetree);
 
 
 /* ----------------------------------------------------------------
@@ -635,6 +637,8 @@ pg_exec_query_string(char *query_string,		/* string to execute */
 	{
 		Node	   *parsetree = (Node *) lfirst(parsetree_item);
 		bool		isTransactionStmt;
+		const char *commandTag;
+		char		completionTag[COMPLETION_TAG_BUFSIZE];
 		List	   *querytree_list,
 				   *querytree_item;
 
@@ -670,16 +674,17 @@ pg_exec_query_string(char *query_string,		/* string to execute */
 
 			if (!allowit)
 			{
-				/*
-				 * the EndCommand() stuff is to tell the frontend that the
-				 * command ended. -cim 6/1/90
-				 */
-				char	   *tag = "*ABORT STATE*";
-
 				elog(NOTICE, "current transaction is aborted, "
 					 "queries ignored until end of transaction block");
 
-				EndCommand(tag, dest);
+				/*
+				 * We need to emit a command-complete report to the client,
+				 * even though we didn't process the query.
+				 * - cim 6/1/90
+				 */
+				commandTag = "*ABORT STATE*";
+
+				EndCommand(commandTag, dest);
 
 				/*
 				 * We continue in the loop, on the off chance that there
@@ -702,7 +707,18 @@ pg_exec_query_string(char *query_string,		/* string to execute */
 
 		/*
 		 * OK to analyze and rewrite this query.
-		 *
+		 */
+		 
+		/*
+		 * First we set the command-completion tag to the main query
+		 * (as opposed to each of the others that may be generated by
+		 * analyze and rewrite).  Also set ps_status to the main query tag.
+		 */
+		commandTag = CreateCommandTag(parsetree);
+
+		set_ps_display(commandTag);
+
+		/*
 		 * Switch to appropriate context for constructing querytrees (again,
 		 * these must outlive the execution context).
 		 */
@@ -746,7 +762,19 @@ pg_exec_query_string(char *query_string,		/* string to execute */
 				else if (DebugLvl > 1)
 					elog(DEBUG, "ProcessUtility");
 
-				ProcessUtility(querytree->utilityStmt, dest);
+				if (querytree->originalQuery)
+				{
+					/* utility statement can override default tag string */
+					ProcessUtility(querytree->utilityStmt, dest,
+								   completionTag);
+					if (completionTag[0])
+						commandTag = completionTag;
+				}
+				else
+				{
+					/* utility added by rewrite cannot override tag */
+					ProcessUtility(querytree->utilityStmt, dest, NULL);
+				}
 			}
 			else
 			{
@@ -778,7 +806,18 @@ pg_exec_query_string(char *query_string,		/* string to execute */
 				{
 					if (DebugLvl > 1)
 						elog(DEBUG, "ProcessQuery");
-					ProcessQuery(querytree, plan, dest);
+
+					if (querytree->originalQuery)
+					{
+						/* original stmt can override default tag string */
+						ProcessQuery(querytree, plan, dest, completionTag);
+						commandTag = completionTag;
+					}
+					else
+					{
+						/* stmt added by rewrite cannot override tag */
+						ProcessQuery(querytree, plan, dest, NULL);
+					}
 				}
 
 				if (Show_executor_stats)
@@ -818,6 +857,29 @@ pg_exec_query_string(char *query_string,		/* string to execute */
 
 		}						/* end loop over queries generated from a
 								 * parsetree */
+
+		/*
+		 * It is possible that the original query was removed due to
+		 * a DO INSTEAD rewrite rule.  In that case we will still have
+		 * the default completion tag, which is fine for most purposes,
+		 * but it may confuse clients if it's INSERT/UPDATE/DELETE.
+		 * Clients expect those tags to have counts after them (cf.
+		 * ProcessQuery).
+		 */
+		if (strcmp(commandTag, "INSERT") == 0)
+			commandTag = "INSERT 0 0";
+		else if (strcmp(commandTag, "UPDATE") == 0)
+			commandTag = "UPDATE 0";
+		else if (strcmp(commandTag, "DELETE") == 0)
+			commandTag = "DELETE 0";
+
+		/*
+		 * Tell client that we're done with this query.  Note we emit
+		 * exactly one EndCommand report for each raw parsetree, thus
+		 * one for each SQL command the client sent, regardless of
+		 * rewriting.
+		 */
+		EndCommand(commandTag, dest);
 	}							/* end loop over parsetrees */
 
 	/*
@@ -1626,7 +1688,7 @@ PostgresMain(int argc, char *argv[], const char *username)
 	if (!IsUnderPostmaster)
 	{
 		puts("\nPOSTGRES backend interactive interface ");
-		puts("$Revision: 1.247 $ $Date: 2002/02/23 01:31:36 $\n");
+		puts("$Revision: 1.248 $ $Date: 2002/02/26 22:47:08 $\n");
 	}
 
 	/*
@@ -2037,3 +2099,268 @@ assertTest(int val)
 #endif
 
 #endif
+
+
+/* ----------------------------------------------------------------
+ *		CreateCommandTag
+ *
+ *		utility to get a string representation of the
+ *		command operation.
+ * ----------------------------------------------------------------
+ */
+static const char *
+CreateCommandTag(Node *parsetree)
+{
+	const char   *tag;
+
+	switch (nodeTag(parsetree))
+	{
+		case T_InsertStmt:
+			tag = "INSERT";
+			break;
+
+		case T_DeleteStmt:
+			tag = "DELETE";
+			break;
+
+		case T_UpdateStmt:
+			tag = "UPDATE";
+			break;
+
+		case T_SelectStmt:
+			tag = "SELECT";
+			break;
+
+		case T_TransactionStmt:
+			{
+				TransactionStmt *stmt = (TransactionStmt *) parsetree;
+
+				switch (stmt->command)
+				{
+					case BEGIN_TRANS:
+						tag = "BEGIN";
+						break;
+
+					case COMMIT:
+						tag = "COMMIT";
+						break;
+
+					case ROLLBACK:
+						tag = "ROLLBACK";
+						break;
+
+					default:
+						tag = "???";
+						break;
+				}
+			}
+			break;
+
+		case T_ClosePortalStmt:
+			tag = "CLOSE";
+			break;
+
+		case T_FetchStmt:
+			{
+				FetchStmt  *stmt = (FetchStmt *) parsetree;
+				tag = (stmt->ismove) ? "MOVE" : "FETCH";
+			}
+			break;
+
+		case T_CreateStmt:
+			tag = "CREATE";
+			break;
+
+		case T_DropStmt:
+			tag = "DROP";
+			break;
+
+		case T_TruncateStmt:
+			tag = "TRUNCATE";
+			break;
+
+		case T_CommentStmt:
+			tag = "COMMENT";
+			break;
+
+		case T_CopyStmt:
+			tag = "COPY";
+			break;
+
+		case T_RenameStmt:
+			tag = "ALTER";
+			break;
+
+		case T_AlterTableStmt:
+			tag = "ALTER";
+			break;
+
+		case T_GrantStmt:
+			{
+				GrantStmt  *stmt = (GrantStmt *) parsetree;
+				tag = (stmt->is_grant) ? "GRANT" : "REVOKE";
+			}
+			break;
+
+		case T_DefineStmt:
+			tag = "CREATE";
+			break;
+
+		case T_ViewStmt:		/* CREATE VIEW */
+			tag = "CREATE";
+			break;
+
+		case T_ProcedureStmt:	/* CREATE FUNCTION */
+			tag = "CREATE";
+			break;
+
+		case T_IndexStmt:		/* CREATE INDEX */
+			tag = "CREATE";
+			break;
+
+		case T_RuleStmt:		/* CREATE RULE */
+			tag = "CREATE";
+			break;
+
+		case T_CreateSeqStmt:
+			tag = "CREATE";
+			break;
+
+		case T_RemoveAggrStmt:
+			tag = "DROP";
+			break;
+
+		case T_RemoveFuncStmt:
+			tag = "DROP";
+			break;
+
+		case T_RemoveOperStmt:
+			tag = "DROP";
+			break;
+
+		case T_VersionStmt:
+			tag = "CREATE VERSION";
+			break;
+
+		case T_CreatedbStmt:
+			tag = "CREATE DATABASE";
+			break;
+
+		case T_DropdbStmt:
+			tag = "DROP DATABASE";
+			break;
+
+		case T_NotifyStmt:
+			tag = "NOTIFY";
+			break;
+
+		case T_ListenStmt:
+			tag = "LISTEN";
+			break;
+
+		case T_UnlistenStmt:
+			tag = "UNLISTEN";
+			break;
+
+		case T_LoadStmt:
+			tag = "LOAD";
+			break;
+
+		case T_ClusterStmt:
+			tag = "CLUSTER";
+			break;
+
+		case T_VacuumStmt:
+			if (((VacuumStmt *) parsetree)->vacuum)
+				tag = "VACUUM";
+			else
+				tag = "ANALYZE";
+			break;
+
+		case T_ExplainStmt:
+			tag = "EXPLAIN";
+			break;
+
+#ifdef NOT_USED
+		case T_RecipeStmt:
+			tag = "EXECUTE RECIPE";
+			break;
+#endif
+
+		case T_VariableSetStmt:
+			tag = "SET VARIABLE";
+			break;
+
+		case T_VariableShowStmt:
+			tag = "SHOW VARIABLE";
+			break;
+
+		case T_VariableResetStmt:
+			tag = "RESET VARIABLE";
+			break;
+
+		case T_CreateTrigStmt:
+			tag = "CREATE";
+			break;
+
+		case T_DropTrigStmt:
+			tag = "DROP";
+			break;
+
+		case T_CreatePLangStmt:
+			tag = "CREATE";
+			break;
+
+		case T_DropPLangStmt:
+			tag = "DROP";
+			break;
+
+		case T_CreateUserStmt:
+			tag = "CREATE USER";
+			break;
+
+		case T_AlterUserStmt:
+			tag = "ALTER USER";
+			break;
+
+		case T_DropUserStmt:
+			tag = "DROP USER";
+			break;
+
+		case T_LockStmt:
+			tag = "LOCK TABLE";
+			break;
+
+		case T_ConstraintsSetStmt:
+			tag = "SET CONSTRAINTS";
+			break;
+
+		case T_CreateGroupStmt:
+			tag = "CREATE GROUP";
+			break;
+
+		case T_AlterGroupStmt:
+			tag = "ALTER GROUP";
+			break;
+
+		case T_DropGroupStmt:
+			tag = "DROP GROUP";
+			break;
+
+		case T_CheckPointStmt:
+			tag = "CHECKPOINT";
+			break;
+
+		case T_ReindexStmt:
+			tag = "REINDEX";
+			break;
+
+		default:
+			elog(DEBUG, "CreateCommandTag: unknown parse node type %d",
+				 nodeTag(parsetree));
+			tag = "???";
+			break;
+	}
+
+	return tag;
+}
diff --git a/src/backend/tcop/pquery.c b/src/backend/tcop/pquery.c
index 91d98a2d50de919ff2cdcb49e614fc97d8259bce..75b99c21fab6f4210dcfebd70684ed2db7c0c23c 100644
--- a/src/backend/tcop/pquery.c
+++ b/src/backend/tcop/pquery.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/tcop/pquery.c,v 1.46 2001/10/25 05:49:43 momjian Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/tcop/pquery.c,v 1.47 2002/02/26 22:47:09 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -23,9 +23,6 @@
 #include "utils/ps_status.h"
 
 
-static char *CreateOperationTag(int operationType);
-
-
 /* ----------------------------------------------------------------
  *		CreateQueryDesc
  * ----------------------------------------------------------------
@@ -89,42 +86,6 @@ CreateExecutorState(void)
 	return state;
 }
 
-/* ----------------------------------------------------------------
- *		CreateOperationTag
- *
- *		utility to get a string representation of the
- *		query operation.
- * ----------------------------------------------------------------
- */
-static char *
-CreateOperationTag(int operationType)
-{
-	char	   *tag;
-
-	switch (operationType)
-	{
-		case CMD_SELECT:
-			tag = "SELECT";
-			break;
-		case CMD_INSERT:
-			tag = "INSERT";
-			break;
-		case CMD_DELETE:
-			tag = "DELETE";
-			break;
-		case CMD_UPDATE:
-			tag = "UPDATE";
-			break;
-		default:
-			elog(DEBUG, "CreateOperationTag: unknown operation type %d",
-				 operationType);
-			tag = "???";
-			break;
-	}
-
-	return tag;
-}
-
 /* ----------------
  *		PreparePortal
  * ----------------
@@ -158,19 +119,25 @@ PreparePortal(char *portalName)
 }
 
 
-/* ----------------------------------------------------------------
- *		ProcessQuery
+/*
+ * ProcessQuery
+ *		Execute a query
  *
- *		Execute a plan, the non-parallel version
- * ----------------------------------------------------------------
+ *	parsetree: the query tree
+ *	plan: the plan tree for the query
+ *	dest: where to send results
+ *	completionTag: points to a buffer of size COMPLETION_TAG_BUFSIZE
+ *		in which to store a command completion status string.
+ *
+ * completionTag may be NULL if caller doesn't want a status string.
  */
 void
 ProcessQuery(Query *parsetree,
 			 Plan *plan,
-			 CommandDest dest)
+			 CommandDest dest,
+			 char *completionTag)
 {
 	int			operation = parsetree->commandType;
-	char	   *tag;
 	bool		isRetrieveIntoPortal;
 	bool		isRetrieveIntoRelation;
 	char	   *intoName = NULL;
@@ -180,8 +147,6 @@ ProcessQuery(Query *parsetree,
 	EState	   *state;
 	TupleDesc	attinfo;
 
-	set_ps_display(tag = CreateOperationTag(operation));
-
 	/*
 	 * initialize portal/into relation status
 	 */
@@ -238,8 +203,7 @@ ProcessQuery(Query *parsetree,
 	 * When performing a retrieve into, we override the normal
 	 * communication destination during the processing of the the query.
 	 * This only affects the tuple-output function - the correct
-	 * destination will still see BeginCommand() and EndCommand()
-	 * messages.
+	 * destination will still see the BeginCommand() call.
 	 */
 	if (isRetrieveIntoRelation)
 		queryDesc->dest = None;
@@ -263,7 +227,7 @@ ProcessQuery(Query *parsetree,
 				 attinfo,
 				 isRetrieveIntoRelation,
 				 isRetrieveIntoPortal,
-				 tag,
+				 NULL,			/* not used */
 				 dest);
 
 	/*
@@ -281,7 +245,9 @@ ProcessQuery(Query *parsetree,
 		/* Now we can return to caller's memory context. */
 		MemoryContextSwitchTo(oldContext);
 
-		EndCommand(tag, dest);
+		/* Set completion tag.  SQL calls this operation DECLARE CURSOR */
+		if (completionTag)
+			strcpy(completionTag, "DECLARE");
 
 		return;
 	}
@@ -292,16 +258,42 @@ ProcessQuery(Query *parsetree,
 	 */
 	ExecutorRun(queryDesc, state, EXEC_RUN, 0L);
 
-	/* save infos for EndCommand */
-	UpdateCommandInfo(operation, state->es_lastoid, state->es_processed);
-
 	/*
-	 * Now, we close down all the scans and free allocated resources.
+	 * Build command completion status string, if caller wants one.
 	 */
-	ExecutorEnd(queryDesc, state);
+	if (completionTag)
+	{
+		Oid		lastOid;
+
+		switch (operation)
+		{
+			case CMD_SELECT:
+				strcpy(completionTag, "SELECT");
+				break;
+			case CMD_INSERT:
+				if (state->es_processed == 1)
+					lastOid = state->es_lastoid;
+				else
+					lastOid = InvalidOid;
+				snprintf(completionTag, COMPLETION_TAG_BUFSIZE,
+						 "INSERT %u %u", lastOid, state->es_processed);
+				break;
+			case CMD_UPDATE:
+				snprintf(completionTag, COMPLETION_TAG_BUFSIZE,
+						 "UPDATE %u", state->es_processed);
+				break;
+			case CMD_DELETE:
+				snprintf(completionTag, COMPLETION_TAG_BUFSIZE,
+						 "DELETE %u", state->es_processed);
+				break;
+			default:
+				strcpy(completionTag, "???");
+				break;
+		}
+	}
 
 	/*
-	 * Notify the destination of end of processing.
+	 * Now, we close down all the scans and free allocated resources.
 	 */
-	EndCommand(tag, dest);
+	ExecutorEnd(queryDesc, state);
 }
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index f80a6bdca44f75357e94f5be72a8ffb13c7efaa0..a6a8b561e02fd2ec0be36d790102c1473f13dab6 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.126 2002/02/24 20:20:20 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/tcop/utility.c,v 1.127 2002/02/26 22:47:09 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -44,7 +44,6 @@
 #include "rewrite/rewriteRemove.h"
 #include "tcop/utility.h"
 #include "utils/acl.h"
-#include "utils/ps_status.h"
 #include "utils/syscache.h"
 #include "utils/temprel.h"
 #include "access/xlog.h"
@@ -130,18 +129,31 @@ CheckDropPermissions(char *name, char rightkind)
 }
 
 
-/* ----------------
+/*
+ * ProcessUtility
  *		general utility function invoker
- * ----------------
+ *
+ *	parsetree: the parse tree for the utility statement
+ *	dest: where to send results
+ *	completionTag: points to a buffer of size COMPLETION_TAG_BUFSIZE
+ *		in which to store a command completion status string.
+ *
+ * completionTag is only set nonempty if we want to return a nondefault
+ * status (currently, only used for MOVE/FETCH).
+ *
+ * completionTag may be NULL if caller doesn't want a status string.
  */
 void
 ProcessUtility(Node *parsetree,
-			   CommandDest dest)
+			   CommandDest dest,
+			   char *completionTag)
 {
-	char	   *commandTag = NULL;
 	char	   *relname;
 	char	   *relationName;
 
+	if (completionTag)
+		completionTag[0] = '\0';
+
 	switch (nodeTag(parsetree))
 	{
 			/*
@@ -155,17 +167,14 @@ ProcessUtility(Node *parsetree,
 				switch (stmt->command)
 				{
 					case BEGIN_TRANS:
-						set_ps_display(commandTag = "BEGIN");
 						BeginTransactionBlock();
 						break;
 
 					case COMMIT:
-						set_ps_display(commandTag = "COMMIT");
 						EndTransactionBlock();
 						break;
 
 					case ROLLBACK:
-						set_ps_display(commandTag = "ROLLBACK");
 						UserAbortTransactionBlock();
 						break;
 				}
@@ -180,8 +189,6 @@ ProcessUtility(Node *parsetree,
 			{
 				ClosePortalStmt *stmt = (ClosePortalStmt *) parsetree;
 
-				set_ps_display(commandTag = "CLOSE");
-
 				PerformPortalClose(stmt->portalname, dest);
 			}
 			break;
@@ -193,8 +200,6 @@ ProcessUtility(Node *parsetree,
 				bool		forward;
 				int			count;
 
-				set_ps_display(commandTag = (stmt->ismove) ? "MOVE" : "FETCH");
-
 				SetQuerySnapshot();
 
 				forward = (bool) (stmt->direction == FORWARD);
@@ -204,8 +209,9 @@ ProcessUtility(Node *parsetree,
 				 */
 
 				count = stmt->howMany;
-				PerformPortalFetch(portalName, forward, count, commandTag,
-								   (stmt->ismove) ? None : dest);		/* /dev/null for MOVE */
+				PerformPortalFetch(portalName, forward, count,
+								   (stmt->ismove) ? None : dest,
+								   completionTag);
 			}
 			break;
 
@@ -215,8 +221,6 @@ ProcessUtility(Node *parsetree,
 			 *
 			 */
 		case T_CreateStmt:
-			set_ps_display(commandTag = "CREATE");
-
 			DefineRelation((CreateStmt *) parsetree, RELKIND_RELATION);
 
 			/*
@@ -234,8 +238,6 @@ ProcessUtility(Node *parsetree,
 				List	   *args = stmt->names;
 				List	   *arg;
 
-				set_ps_display(commandTag = "DROP");
-
 				foreach(arg, args)
 				{
 					relname = strVal(lfirst(arg));
@@ -296,8 +298,6 @@ ProcessUtility(Node *parsetree,
 			{
 				Relation	rel;
 
-				set_ps_display(commandTag = "TRUNCATE");
-
 				relname = ((TruncateStmt *) parsetree)->relName;
 				if (!allowSystemTableMods && IsSystemRelationName(relname))
 					elog(ERROR, "TRUNCATE cannot be used on system tables. '%s' is a system table",
@@ -325,8 +325,6 @@ ProcessUtility(Node *parsetree,
 
 				statement = ((CommentStmt *) parsetree);
 
-				set_ps_display(commandTag = "COMMENT");
-
 				CommentObject(statement->objtype, statement->objname,
 							  statement->objproperty, statement->objlist,
 							  statement->comment);
@@ -337,8 +335,6 @@ ProcessUtility(Node *parsetree,
 			{
 				CopyStmt   *stmt = (CopyStmt *) parsetree;
 
-				set_ps_display(commandTag = "COPY");
-
 				if (stmt->direction != FROM)
 					SetQuerySnapshot();
 
@@ -365,8 +361,6 @@ ProcessUtility(Node *parsetree,
 			{
 				RenameStmt *stmt = (RenameStmt *) parsetree;
 
-				set_ps_display(commandTag = "ALTER");
-
 				relname = stmt->relname;
 				if (!allowSystemTableMods && IsSystemRelationName(relname))
 					elog(ERROR, "ALTER TABLE: relation \"%s\" is a system catalog",
@@ -413,8 +407,6 @@ ProcessUtility(Node *parsetree,
 			{
 				AlterTableStmt *stmt = (AlterTableStmt *) parsetree;
 
-				set_ps_display(commandTag = "ALTER");
-
 				/*
 				 * Some or all of these functions are recursive to cover
 				 * inherited things, so permission checks are done there.
@@ -475,9 +467,6 @@ ProcessUtility(Node *parsetree,
 			{
 				GrantStmt  *stmt = (GrantStmt *) parsetree;
 
-				commandTag = stmt->is_grant ? "GRANT" : "REVOKE";
-				set_ps_display(commandTag);
-
 				ExecuteGrantStmt(stmt);
 			}
 			break;
@@ -491,8 +480,6 @@ ProcessUtility(Node *parsetree,
 			{
 				DefineStmt *stmt = (DefineStmt *) parsetree;
 
-				set_ps_display(commandTag = "CREATE");
-
 				switch (stmt->defType)
 				{
 					case OPERATOR:
@@ -514,15 +501,11 @@ ProcessUtility(Node *parsetree,
 			{
 				ViewStmt   *stmt = (ViewStmt *) parsetree;
 
-				set_ps_display(commandTag = "CREATE");
-
 				DefineView(stmt->viewname, stmt->query);		/* retrieve parsetree */
 			}
 			break;
 
 		case T_ProcedureStmt:	/* CREATE FUNCTION */
-			set_ps_display(commandTag = "CREATE");
-
 			CreateFunction((ProcedureStmt *) parsetree);
 			break;
 
@@ -530,8 +513,6 @@ ProcessUtility(Node *parsetree,
 			{
 				IndexStmt  *stmt = (IndexStmt *) parsetree;
 
-				set_ps_display(commandTag = "CREATE");
-
 				relname = stmt->relname;
 				if (!allowSystemTableMods && IsSystemRelationName(relname))
 					elog(ERROR, "CREATE INDEX: relation \"%s\" is a system catalog",
@@ -559,15 +540,12 @@ ProcessUtility(Node *parsetree,
 				aclcheck_result = pg_aclcheck(relname, GetUserId(), ACL_RULE);
 				if (aclcheck_result != ACLCHECK_OK)
 					elog(ERROR, "%s: %s", relname, aclcheck_error_strings[aclcheck_result]);
-				set_ps_display(commandTag = "CREATE");
 
 				DefineQueryRewrite(stmt);
 			}
 			break;
 
 		case T_CreateSeqStmt:
-			set_ps_display(commandTag = "CREATE");
-
 			DefineSequence((CreateSeqStmt *) parsetree);
 			break;
 
@@ -576,8 +554,6 @@ ProcessUtility(Node *parsetree,
 				RemoveAggrStmt *stmt = (RemoveAggrStmt *) parsetree;
 				char	   *typename = (char *) NULL;
 
-				set_ps_display(commandTag = "DROP");
-
 				if (stmt->aggtype != NULL)
 					typename = TypeNameToInternalName((TypeName *) stmt->aggtype);
 
@@ -589,8 +565,6 @@ ProcessUtility(Node *parsetree,
 			{
 				RemoveFuncStmt *stmt = (RemoveFuncStmt *) parsetree;
 
-				set_ps_display(commandTag = "DROP");
-
 				RemoveFunction(stmt->funcname, stmt->args);
 			}
 			break;
@@ -603,8 +577,6 @@ ProcessUtility(Node *parsetree,
 				char	   *typename1 = (char *) NULL;
 				char	   *typename2 = (char *) NULL;
 
-				set_ps_display(commandTag = "DROP");
-
 				if (typenode1 != NULL)
 					typename1 = TypeNameToInternalName(typenode1);
 				if (typenode2 != NULL)
@@ -622,8 +594,6 @@ ProcessUtility(Node *parsetree,
 			{
 				CreatedbStmt *stmt = (CreatedbStmt *) parsetree;
 
-				set_ps_display(commandTag = "CREATE DATABASE");
-
 				createdb(stmt->dbname, stmt->dbowner,
 						 stmt->dbpath, stmt->dbtemplate,
 						 stmt->encoding);
@@ -634,8 +604,6 @@ ProcessUtility(Node *parsetree,
 			{
 				DropdbStmt *stmt = (DropdbStmt *) parsetree;
 
-				set_ps_display(commandTag = "DROP DATABASE");
-
 				dropdb(stmt->dbname);
 			}
 			break;
@@ -645,8 +613,6 @@ ProcessUtility(Node *parsetree,
 			{
 				NotifyStmt *stmt = (NotifyStmt *) parsetree;
 
-				set_ps_display(commandTag = "NOTIFY");
-
 				Async_Notify(stmt->relname);
 			}
 			break;
@@ -655,8 +621,6 @@ ProcessUtility(Node *parsetree,
 			{
 				ListenStmt *stmt = (ListenStmt *) parsetree;
 
-				set_ps_display(commandTag = "LISTEN");
-
 				Async_Listen(stmt->relname, MyProcPid);
 			}
 			break;
@@ -665,8 +629,6 @@ ProcessUtility(Node *parsetree,
 			{
 				UnlistenStmt *stmt = (UnlistenStmt *) parsetree;
 
-				set_ps_display(commandTag = "UNLISTEN");
-
 				Async_Unlisten(stmt->relname, MyProcPid);
 			}
 			break;
@@ -679,8 +641,6 @@ ProcessUtility(Node *parsetree,
 			{
 				LoadStmt   *stmt = (LoadStmt *) parsetree;
 
-				set_ps_display(commandTag = "LOAD");
-
 				closeAllVfds(); /* probably not necessary... */
 				load_file(stmt->filename);
 			}
@@ -690,8 +650,6 @@ ProcessUtility(Node *parsetree,
 			{
 				ClusterStmt *stmt = (ClusterStmt *) parsetree;
 
-				set_ps_display(commandTag = "CLUSTER");
-
 				relname = stmt->relname;
 				if (IsSystemRelationName(relname))
 					elog(ERROR, "CLUSTER: relation \"%s\" is a system catalog",
@@ -704,12 +662,6 @@ ProcessUtility(Node *parsetree,
 			break;
 
 		case T_VacuumStmt:
-			if (((VacuumStmt *) parsetree)->vacuum)
-				commandTag = "VACUUM";
-			else
-				commandTag = "ANALYZE";
-			set_ps_display(commandTag);
-
 			vacuum((VacuumStmt *) parsetree);
 			break;
 
@@ -717,8 +669,6 @@ ProcessUtility(Node *parsetree,
 			{
 				ExplainStmt *stmt = (ExplainStmt *) parsetree;
 
-				set_ps_display(commandTag = "EXPLAIN");
-
 				ExplainQuery(stmt->query, stmt->verbose, stmt->analyze, dest);
 			}
 			break;
@@ -732,8 +682,6 @@ ProcessUtility(Node *parsetree,
 			{
 				RecipeStmt *stmt = (RecipeStmt *) parsetree;
 
-				set_ps_display(commandTag = "EXECUTE RECIPE");
-
 				beginRecipe(stmt);
 			}
 			break;
@@ -747,7 +695,6 @@ ProcessUtility(Node *parsetree,
 				VariableSetStmt *n = (VariableSetStmt *) parsetree;
 
 				SetPGVariable(n->name, n->args);
-				set_ps_display(commandTag = "SET VARIABLE");
 			}
 			break;
 
@@ -756,7 +703,6 @@ ProcessUtility(Node *parsetree,
 				VariableShowStmt *n = (VariableShowStmt *) parsetree;
 
 				GetPGVariable(n->name);
-				set_ps_display(commandTag = "SHOW VARIABLE");
 			}
 			break;
 
@@ -765,7 +711,6 @@ ProcessUtility(Node *parsetree,
 				VariableResetStmt *n = (VariableResetStmt *) parsetree;
 
 				ResetPGVariable(n->name);
-				set_ps_display(commandTag = "RESET VARIABLE");
 			}
 			break;
 
@@ -773,14 +718,10 @@ ProcessUtility(Node *parsetree,
 			 * ******************************** TRIGGER statements *******************************
 			 */
 		case T_CreateTrigStmt:
-			set_ps_display(commandTag = "CREATE");
-
 			CreateTrigger((CreateTrigStmt *) parsetree);
 			break;
 
 		case T_DropTrigStmt:
-			set_ps_display(commandTag = "DROP");
-
 			DropTrigger((DropTrigStmt *) parsetree);
 			break;
 
@@ -788,14 +729,10 @@ ProcessUtility(Node *parsetree,
 			 * ************* PROCEDURAL LANGUAGE statements *****************
 			 */
 		case T_CreatePLangStmt:
-			set_ps_display(commandTag = "CREATE");
-
 			CreateProceduralLanguage((CreatePLangStmt *) parsetree);
 			break;
 
 		case T_DropPLangStmt:
-			set_ps_display(commandTag = "DROP");
-
 			DropProceduralLanguage((DropPLangStmt *) parsetree);
 			break;
 
@@ -804,57 +741,39 @@ ProcessUtility(Node *parsetree,
 			 *
 			 */
 		case T_CreateUserStmt:
-			set_ps_display(commandTag = "CREATE USER");
-
 			CreateUser((CreateUserStmt *) parsetree);
 			break;
 
 		case T_AlterUserStmt:
-			set_ps_display(commandTag = "ALTER USER");
-
 			AlterUser((AlterUserStmt *) parsetree);
 			break;
 
 		case T_DropUserStmt:
-			set_ps_display(commandTag = "DROP USER");
-
 			DropUser((DropUserStmt *) parsetree);
 			break;
 
 		case T_LockStmt:
-			set_ps_display(commandTag = "LOCK TABLE");
-
 			LockTableCommand((LockStmt *) parsetree);
 			break;
 
 		case T_ConstraintsSetStmt:
-			set_ps_display(commandTag = "SET CONSTRAINTS");
-
 			DeferredTriggerSetState((ConstraintsSetStmt *) parsetree);
 			break;
 
 		case T_CreateGroupStmt:
-			set_ps_display(commandTag = "CREATE GROUP");
-
 			CreateGroup((CreateGroupStmt *) parsetree);
 			break;
 
 		case T_AlterGroupStmt:
-			set_ps_display(commandTag = "ALTER GROUP");
-
 			AlterGroup((AlterGroupStmt *) parsetree, "ALTER GROUP");
 			break;
 
 		case T_DropGroupStmt:
-			set_ps_display(commandTag = "DROP GROUP");
-
 			DropGroup((DropGroupStmt *) parsetree);
 			break;
 
 		case T_CheckPointStmt:
 			{
-				set_ps_display(commandTag = "CHECKPOINT");
-
 				if (!superuser())
 					elog(ERROR, "permission denied");
 				CreateCheckPoint(false);
@@ -865,8 +784,6 @@ ProcessUtility(Node *parsetree,
 			{
 				ReindexStmt *stmt = (ReindexStmt *) parsetree;
 
-				set_ps_display(commandTag = "REINDEX");
-
 				switch (stmt->reindexType)
 				{
 					case INDEX:
@@ -912,9 +829,4 @@ ProcessUtility(Node *parsetree,
 				 nodeTag(parsetree));
 			break;
 	}
-
-	/*
-	 * tell fe/be or whatever that we're done.
-	 */
-	EndCommand(commandTag, dest);
 }
diff --git a/src/include/commands/command.h b/src/include/commands/command.h
index 4531b90d7cb71118bf4d8183164158dc37598335..ee4e2c0aa3cbe1ef59dcbb4502a3bc6ef36e2036 100644
--- a/src/include/commands/command.h
+++ b/src/include/commands/command.h
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: command.h,v 1.31 2001/11/05 17:46:33 momjian Exp $
+ * $Id: command.h,v 1.32 2002/02/26 22:47:10 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -27,7 +27,7 @@
  *		"ERROR" if portal not found.
  */
 extern void PerformPortalFetch(char *name, bool forward, int count,
-				   char *tag, CommandDest dest);
+							   CommandDest dest, char *completionTag);
 
 /*
  * PerformPortalClose
diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h
index 0b40fe99480f66f1a2ca1b8a8a53783855d01988..bfcbc91cd43a0b8daf044cfb99d08ef929f32e2f 100644
--- a/src/include/nodes/parsenodes.h
+++ b/src/include/nodes/parsenodes.h
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: parsenodes.h,v 1.153 2002/02/24 20:20:21 tgl Exp $
+ * $Id: parsenodes.h,v 1.154 2002/02/26 22:47:10 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -48,6 +48,8 @@ typedef struct Query
 	bool		hasAggs;		/* has aggregates in tlist or havingQual */
 	bool		hasSubLinks;	/* has subquery SubLink */
 
+	bool		originalQuery;	/* marks original query through rewriting */
+
 	List	   *rtable;			/* list of range table entries */
 	FromExpr   *jointree;		/* table join tree (FROM and WHERE
 								 * clauses) */
diff --git a/src/include/tcop/dest.h b/src/include/tcop/dest.h
index e23eb6bc58d284273671af3ca52eb933bc099df1..d5ac420ce6ed4968a669cb3564aee723fbe487cb 100644
--- a/src/include/tcop/dest.h
+++ b/src/include/tcop/dest.h
@@ -39,7 +39,7 @@
  * Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: dest.h,v 1.28 2001/11/05 17:46:36 momjian Exp $
+ * $Id: dest.h,v 1.29 2002/02/26 22:47:11 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -48,6 +48,11 @@
 
 #include "access/htup.h"
 
+
+/* buffer size to use for command completion tags */
+#define COMPLETION_TAG_BUFSIZE	64
+
+
 /* ----------------
  *		CommandDest is a simplistic means of identifying the desired
  *		destination.  Someday this will probably need to be improved.
@@ -88,7 +93,7 @@ extern void BeginCommand(char *pname, int operation, TupleDesc attinfo,
 			 bool isIntoRel, bool isIntoPortal, char *tag,
 			 CommandDest dest);
 extern DestReceiver *DestToFunction(CommandDest dest);
-extern void EndCommand(char *commandTag, CommandDest dest);
+extern void EndCommand(const char *commandTag, CommandDest dest);
 
 /* Additional functions that go with destination management, more or less. */
 
@@ -96,6 +101,5 @@ extern void SendCopyBegin(void);
 extern void ReceiveCopyBegin(void);
 extern void NullCommand(CommandDest dest);
 extern void ReadyForQuery(CommandDest dest);
-extern void UpdateCommandInfo(int operation, Oid lastoid, uint32 tuples);
 
 #endif   /* DEST_H */
diff --git a/src/include/tcop/pquery.h b/src/include/tcop/pquery.h
index dbca9de8c69f15b89fa4406daa2f243101a35fd3..6333e01653c7cf2f2fc3eda79126bc005f786ca4 100644
--- a/src/include/tcop/pquery.h
+++ b/src/include/tcop/pquery.h
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: pquery.h,v 1.19 2001/11/05 17:46:36 momjian Exp $
+ * $Id: pquery.h,v 1.20 2002/02/26 22:47:12 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -18,7 +18,8 @@
 #include "utils/portal.h"
 
 
-extern void ProcessQuery(Query *parsetree, Plan *plan, CommandDest dest);
+extern void ProcessQuery(Query *parsetree, Plan *plan, CommandDest dest,
+						 char *completionTag);
 
 extern EState *CreateExecutorState(void);
 
diff --git a/src/include/tcop/utility.h b/src/include/tcop/utility.h
index 8e05c424a4abdca9c7453fb03f56f4b8d9458133..5b37eb01916d0e99bee2a6990e325d448c01db17 100644
--- a/src/include/tcop/utility.h
+++ b/src/include/tcop/utility.h
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: utility.h,v 1.13 2001/11/05 17:46:36 momjian Exp $
+ * $Id: utility.h,v 1.14 2002/02/26 22:47:12 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -16,6 +16,7 @@
 
 #include "executor/execdesc.h"
 
-extern void ProcessUtility(Node *parsetree, CommandDest dest);
+extern void ProcessUtility(Node *parsetree, CommandDest dest,
+						   char *completionTag);
 
 #endif   /* UTILITY_H */