*** 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 $$ +