*** a/src/backend/commands/extension.c
--- b/src/backend/commands/extension.c
***************
*** 751,757 **** execute_sql_string(const char *sql, const char *filename)
  							   NULL,
  							   dest,
  							   NULL,
! 							   PROCESS_UTILITY_QUERY);
  			}
  
  			PopActiveSnapshot();
--- 751,758 ----
  							   NULL,
  							   dest,
  							   NULL,
! 							   PROCESS_UTILITY_QUERY,
! 							   NULL);
  			}
  
  			PopActiveSnapshot();
*** a/src/backend/commands/schemacmds.c
--- b/src/backend/commands/schemacmds.c
***************
*** 134,140 **** CreateSchemaCommand(CreateSchemaStmt *stmt, const char *queryString)
  					   NULL,
  					   None_Receiver,
  					   NULL,
! 					   PROCESS_UTILITY_SUBCOMMAND);
  		/* make sure later steps can see the object created here */
  		CommandCounterIncrement();
  	}
--- 134,141 ----
  					   NULL,
  					   None_Receiver,
  					   NULL,
! 					   PROCESS_UTILITY_SUBCOMMAND,
! 					   NULL);
  		/* make sure later steps can see the object created here */
  		CommandCounterIncrement();
  	}
*** a/src/backend/commands/trigger.c
--- b/src/backend/commands/trigger.c
***************
*** 1011,1017 **** ConvertTriggerToFK(CreateTrigStmt *stmt, Oid funcoid)
  		/* ... and execute it */
  		ProcessUtility((Node *) atstmt,
  					   "(generated ALTER TABLE ADD FOREIGN KEY command)",
! 					   NULL, None_Receiver, NULL, PROCESS_UTILITY_GENERATED);
  
  		/* Remove the matched item from the list */
  		info_list = list_delete_ptr(info_list, info);
--- 1011,1018 ----
  		/* ... and execute it */
  		ProcessUtility((Node *) atstmt,
  					   "(generated ALTER TABLE ADD FOREIGN KEY command)",
! 					   NULL, None_Receiver, NULL, PROCESS_UTILITY_GENERATED,
! 					   NULL);
  
  		/* Remove the matched item from the list */
  		info_list = list_delete_ptr(info_list, info);
*** a/src/backend/executor/functions.c
--- b/src/backend/executor/functions.c
***************
*** 785,791 **** postquel_getnext(execution_state *es, SQLFunctionCachePtr fcache)
  					   es->qd->params,
  					   es->qd->dest,
  					   NULL,
! 					   PROCESS_UTILITY_QUERY);
  		result = true;			/* never stops early */
  	}
  	else
--- 785,792 ----
  					   es->qd->params,
  					   es->qd->dest,
  					   NULL,
! 					   PROCESS_UTILITY_QUERY,
! 					   NULL);
  		result = true;			/* never stops early */
  	}
  	else
*** a/src/backend/executor/spi.c
--- b/src/backend/executor/spi.c
***************
*** 1908,1920 **** _SPI_execute_plan(SPIPlanPtr plan, ParamListInfo paramLI,
  			else
  			{
  				char		completionTag[COMPLETION_TAG_BUFSIZE];
  
  				ProcessUtility(stmt,
  							   plansource->query_string,
  							   paramLI,
  							   dest,
  							   completionTag,
! 							   PROCESS_UTILITY_QUERY);
  
  				/* Update "processed" if stmt returned tuples */
  				if (_SPI_current->tuptable)
--- 1908,1924 ----
  			else
  			{
  				char		completionTag[COMPLETION_TAG_BUFSIZE];
+ 				uint64		processed;
  
  				ProcessUtility(stmt,
  							   plansource->query_string,
  							   paramLI,
  							   dest,
  							   completionTag,
! 							   PROCESS_UTILITY_QUERY,
! 							   &processed);
! 
! 				_SPI_current->processed = (uint32) processed;
  
  				/* Update "processed" if stmt returned tuples */
  				if (_SPI_current->tuptable)
*** a/src/backend/tcop/pquery.c
--- b/src/backend/tcop/pquery.c
***************
*** 44,53 **** static uint32 RunFromStore(Portal portal, ScanDirection direction, long count,
  static long PortalRunSelect(Portal portal, bool forward, long count,
  				DestReceiver *dest);
  static void PortalRunUtility(Portal portal, Node *utilityStmt, bool isTopLevel,
! 				 DestReceiver *dest, char *completionTag);
  static void PortalRunMulti(Portal portal, bool isTopLevel,
  			   DestReceiver *dest, DestReceiver *altdest,
! 			   char *completionTag);
  static long DoPortalRunFetch(Portal portal,
  				 FetchDirection fdirection,
  				 long count,
--- 44,54 ----
  static long PortalRunSelect(Portal portal, bool forward, long count,
  				DestReceiver *dest);
  static void PortalRunUtility(Portal portal, Node *utilityStmt, bool isTopLevel,
! 				 DestReceiver *dest, char *completionTag,
! 				 uint64 *processed);
  static void PortalRunMulti(Portal portal, bool isTopLevel,
  			   DestReceiver *dest, DestReceiver *altdest,
! 			   char *completionTag, uint64 *processed);
  static long DoPortalRunFetch(Portal portal,
  				 FetchDirection fdirection,
  				 long count,
***************
*** 813,819 **** PortalRun(Portal portal, long count, bool isTopLevel,
  
  			case PORTAL_MULTI_QUERY:
  				PortalRunMulti(portal, isTopLevel,
! 							   dest, altdest, completionTag);
  
  				/* Prevent portal's commands from being re-executed */
  				MarkPortalDone(portal);
--- 814,821 ----
  
  			case PORTAL_MULTI_QUERY:
  				PortalRunMulti(portal, isTopLevel,
! 							   dest, altdest, completionTag,
! 							   &portal->processed);
  
  				/* Prevent portal's commands from being re-executed */
  				MarkPortalDone(portal);
***************
*** 1053,1064 **** FillPortalStore(Portal portal, bool isTopLevel)
  			 * tuplestore. Auxiliary query outputs are discarded.
  			 */
  			PortalRunMulti(portal, isTopLevel,
! 						   treceiver, None_Receiver, completionTag);
  			break;
  
  		case PORTAL_UTIL_SELECT:
  			PortalRunUtility(portal, (Node *) linitial(portal->stmts),
! 							 isTopLevel, treceiver, completionTag);
  			break;
  
  		default:
--- 1055,1068 ----
  			 * tuplestore. Auxiliary query outputs are discarded.
  			 */
  			PortalRunMulti(portal, isTopLevel,
! 						   treceiver, None_Receiver, completionTag,
! 						   &portal->processed);
  			break;
  
  		case PORTAL_UTIL_SELECT:
  			PortalRunUtility(portal, (Node *) linitial(portal->stmts),
! 							 isTopLevel, treceiver, completionTag,
! 							 &portal->processed);
  			break;
  
  		default:
***************
*** 1148,1154 **** RunFromStore(Portal portal, ScanDirection direction, long count,
   */
  static void
  PortalRunUtility(Portal portal, Node *utilityStmt, bool isTopLevel,
! 				 DestReceiver *dest, char *completionTag)
  {
  	bool		active_snapshot_set;
  
--- 1152,1159 ----
   */
  static void
  PortalRunUtility(Portal portal, Node *utilityStmt, bool isTopLevel,
! 				 DestReceiver *dest, char *completionTag,
! 				 uint64 *processed)
  {
  	bool		active_snapshot_set;
  
***************
*** 1189,1195 **** PortalRunUtility(Portal portal, Node *utilityStmt, bool isTopLevel,
  				   dest,
  				   completionTag,
  				   isTopLevel ?
! 					PROCESS_UTILITY_TOPLEVEL : PROCESS_UTILITY_QUERY);
  
  	/* Some utility statements may change context on us */
  	MemoryContextSwitchTo(PortalGetHeapMemory(portal));
--- 1194,1201 ----
  				   dest,
  				   completionTag,
  				   isTopLevel ?
! 					PROCESS_UTILITY_TOPLEVEL : PROCESS_UTILITY_QUERY,
! 				   processed);
  
  	/* Some utility statements may change context on us */
  	MemoryContextSwitchTo(PortalGetHeapMemory(portal));
***************
*** 1213,1219 **** PortalRunUtility(Portal portal, Node *utilityStmt, bool isTopLevel,
  static void
  PortalRunMulti(Portal portal, bool isTopLevel,
  			   DestReceiver *dest, DestReceiver *altdest,
! 			   char *completionTag)
  {
  	bool		active_snapshot_set = false;
  	ListCell   *stmtlist_item;
--- 1219,1226 ----
  static void
  PortalRunMulti(Portal portal, bool isTopLevel,
  			   DestReceiver *dest, DestReceiver *altdest,
! 			   char *completionTag,
! 			   uint64 *processed)
  {
  	bool		active_snapshot_set = false;
  	ListCell   *stmtlist_item;
***************
*** 1316,1329 **** PortalRunMulti(Portal portal, bool isTopLevel,
  				Assert(!active_snapshot_set);
  				/* statement can set tag string */
  				PortalRunUtility(portal, stmt, isTopLevel,
! 								 dest, completionTag);
  			}
  			else
  			{
  				Assert(IsA(stmt, NotifyStmt));
  				/* stmt added by rewrite cannot set tag */
  				PortalRunUtility(portal, stmt, isTopLevel,
! 								 altdest, NULL);
  			}
  		}
  
--- 1323,1338 ----
  				Assert(!active_snapshot_set);
  				/* statement can set tag string */
  				PortalRunUtility(portal, stmt, isTopLevel,
! 								 dest, completionTag,
! 								 processed);
  			}
  			else
  			{
  				Assert(IsA(stmt, NotifyStmt));
  				/* stmt added by rewrite cannot set tag */
  				PortalRunUtility(portal, stmt, isTopLevel,
! 								 altdest, NULL,
! 								 processed);
  			}
  		}
  
*** a/src/backend/tcop/utility.c
--- b/src/backend/tcop/utility.c
***************
*** 322,328 **** ProcessUtility(Node *parsetree,
  			   ParamListInfo params,
  			   DestReceiver *dest,
  			   char *completionTag,
! 			   ProcessUtilityContext context)
  {
  	Assert(queryString != NULL);	/* required as of 8.4 */
  
--- 322,329 ----
  			   ParamListInfo params,
  			   DestReceiver *dest,
  			   char *completionTag,
! 			   ProcessUtilityContext context,
! 			   uint64 *processed)
  {
  	Assert(queryString != NULL);	/* required as of 8.4 */
  
***************
*** 333,342 **** ProcessUtility(Node *parsetree,
  	 */
  	if (ProcessUtility_hook)
  		(*ProcessUtility_hook) (parsetree, queryString, params,
! 								dest, completionTag, context);
  	else
  		standard_ProcessUtility(parsetree, queryString, params,
! 								dest, completionTag, context);
  }
  
  void
--- 334,345 ----
  	 */
  	if (ProcessUtility_hook)
  		(*ProcessUtility_hook) (parsetree, queryString, params,
! 								dest, completionTag, context,
! 								processed);
  	else
  		standard_ProcessUtility(parsetree, queryString, params,
! 								dest, completionTag, context,
! 								processed);
  }
  
  void
***************
*** 345,351 **** standard_ProcessUtility(Node *parsetree,
  						ParamListInfo params,
  						DestReceiver *dest,
  						char *completionTag,
! 						ProcessUtilityContext context)
  {
  	bool		isTopLevel = (context == PROCESS_UTILITY_TOPLEVEL);
  	bool		isCompleteQuery = (context <= PROCESS_UTILITY_QUERY);
--- 348,355 ----
  						ParamListInfo params,
  						DestReceiver *dest,
  						char *completionTag,
! 						ProcessUtilityContext context,
! 						uint64 *processed)
  {
  	bool		isTopLevel = (context == PROCESS_UTILITY_TOPLEVEL);
  	bool		isCompleteQuery = (context <= PROCESS_UTILITY_QUERY);
***************
*** 355,360 **** standard_ProcessUtility(Node *parsetree,
--- 359,367 ----
  	if (completionTag)
  		completionTag[0] = '\0';
  
+ 	if (processed != NULL)
+ 		*processed = 0;
+ 
  	switch (nodeTag(parsetree))
  	{
  			/*
***************
*** 575,581 **** standard_ProcessUtility(Node *parsetree,
  									   params,
  									   None_Receiver,
  									   NULL,
! 									   PROCESS_UTILITY_GENERATED);
  					}
  
  					/* Need CCI between commands */
--- 582,589 ----
  									   params,
  									   None_Receiver,
  									   NULL,
! 									   PROCESS_UTILITY_GENERATED,
! 									   processed);
  					}
  
  					/* Need CCI between commands */
***************
*** 705,716 **** standard_ProcessUtility(Node *parsetree,
  
  		case T_CopyStmt:
  			{
! 				uint64		processed;
  
! 				processed = DoCopy((CopyStmt *) parsetree, queryString);
  				if (completionTag)
  					snprintf(completionTag, COMPLETION_TAG_BUFSIZE,
! 							 "COPY " UINT64_FORMAT, processed);
  			}
  			break;
  
--- 713,727 ----
  
  		case T_CopyStmt:
  			{
! 				uint64 processed_rows;
  
! 				processed_rows = DoCopy((CopyStmt *) parsetree, queryString);
  				if (completionTag)
  					snprintf(completionTag, COMPLETION_TAG_BUFSIZE,
! 							 "COPY " UINT64_FORMAT, processed_rows);
! 
! 				if (processed != NULL)
! 					*processed = processed_rows;
  			}
  			break;
  
***************
*** 812,818 **** standard_ProcessUtility(Node *parsetree,
  										   params,
  										   None_Receiver,
  										   NULL,
! 										   PROCESS_UTILITY_GENERATED);
  						}
  
  						/* Need CCI between commands */
--- 823,830 ----
  										   params,
  										   None_Receiver,
  										   NULL,
! 										   PROCESS_UTILITY_GENERATED,
! 										   processed);
  						}
  
  						/* Need CCI between commands */
*** a/src/include/tcop/utility.h
--- b/src/include/tcop/utility.h
***************
*** 28,42 **** typedef enum
  typedef void (*ProcessUtility_hook_type) (Node *parsetree,
  			  const char *queryString, ParamListInfo params,
  			  DestReceiver *dest, char *completionTag,
! 			  ProcessUtilityContext context);
  extern PGDLLIMPORT ProcessUtility_hook_type ProcessUtility_hook;
  
  extern void ProcessUtility(Node *parsetree, const char *queryString,
  			   ParamListInfo params, DestReceiver *dest, char *completionTag,
! 			   ProcessUtilityContext context);
  extern void standard_ProcessUtility(Node *parsetree, const char *queryString,
  						ParamListInfo params, DestReceiver *dest,
! 						char *completionTag, ProcessUtilityContext context);
  
  extern bool UtilityReturnsTuples(Node *parsetree);
  
--- 28,45 ----
  typedef void (*ProcessUtility_hook_type) (Node *parsetree,
  			  const char *queryString, ParamListInfo params,
  			  DestReceiver *dest, char *completionTag,
! 			  ProcessUtilityContext context,
! 			  uint64 *processed);
  extern PGDLLIMPORT ProcessUtility_hook_type ProcessUtility_hook;
  
  extern void ProcessUtility(Node *parsetree, const char *queryString,
  			   ParamListInfo params, DestReceiver *dest, char *completionTag,
! 			   ProcessUtilityContext context,
! 			   uint64 *processed);
  extern void standard_ProcessUtility(Node *parsetree, const char *queryString,
  						ParamListInfo params, DestReceiver *dest,
! 						char *completionTag, ProcessUtilityContext context,
! 						uint64 *processed);
  
  extern bool UtilityReturnsTuples(Node *parsetree);
  
*** a/src/include/utils/portal.h
--- b/src/include/utils/portal.h
***************
*** 130,135 **** typedef struct PortalData
--- 130,136 ----
  	const char *commandTag;		/* command tag for original query */
  	List	   *stmts;			/* PlannedStmts and/or utility statements */
  	CachedPlan *cplan;			/* CachedPlan, if stmts are from one */
+ 	uint64	   processed;			/* returned rows */
  
  	ParamListInfo portalParams; /* params to pass to query */
  
*** a/src/test/regress/input/copy.source
--- b/src/test/regress/input/copy.source
***************
*** 106,108 **** this is just a line full of junk that would error out if parsed
--- 106,112 ----
  \.
  
  copy copytest3 to stdout csv header;
+ 
+ -- copy should to return processed rows
+ do $$
+   
