diff -dcrpN pgsql.orig/doc/src/sgml/libpq.sgml pgsql/doc/src/sgml/libpq.sgml *** pgsql.orig/doc/src/sgml/libpq.sgml 2010-02-05 12:20:10.000000000 +0100 --- pgsql/doc/src/sgml/libpq.sgml 2010-02-12 19:27:08.000000000 +0100 *************** typedef struct { *** 2869,2880 **** ! Retrieving Result Information for Other Commands ! These functions are used to extract information from ! PGresult objects that are not ! SELECT results. --- 2869,2879 ---- ! Retrieving Other Result Information ! These functions are used to extract other information from ! PGresult objects. *************** typedef struct { *** 2925,2936 **** This function returns a string containing the number of rows affected by the SQL statement that generated the PGresult. This function can only be used following ! the execution of an INSERT, UPDATE, ! DELETE, MOVE, FETCH, or ! COPY statement, or an EXECUTE of a ! prepared query that contains an INSERT, ! UPDATE, or DELETE statement. If the ! command that generated the PGresult was anything else, PQcmdTuples returns an empty string. The caller should not free the return value directly. It will be freed when the associated PGresult handle is passed to --- 2924,2935 ---- This function returns a string containing the number of rows affected by the SQL statement that generated the PGresult. This function can only be used following ! the execution of a SELECT, WITH, ! INSERT, UPDATE, DELETE, ! MOVE, FETCH, or COPY statement, ! or an EXECUTE of a prepared query that contains an ! INSERT, UPDATE, or DELETE statement. ! If the command that generated the PGresult was anything else, PQcmdTuples returns an empty string. The caller should not free the return value directly. It will be freed when the associated PGresult handle is passed to diff -dcrpN pgsql.orig/doc/src/sgml/protocol.sgml pgsql/doc/src/sgml/protocol.sgml *** pgsql.orig/doc/src/sgml/protocol.sgml 2010-02-03 11:12:23.000000000 +0100 --- pgsql/doc/src/sgml/protocol.sgml 2010-02-12 19:17:24.000000000 +0100 *************** CommandComplete (B) *** 2222,2227 **** --- 2222,2233 ---- + For a SELECT or CREATE TABLE AS + command, the tag is SELECT rows + where rows is the number of rows retrieved. + + + For a MOVE command, the tag is MOVE rows where rows is the number of rows the diff -dcrpN pgsql.orig/src/backend/tcop/pquery.c pgsql/src/backend/tcop/pquery.c *** pgsql.orig/src/backend/tcop/pquery.c 2010-01-03 12:54:25.000000000 +0100 --- pgsql/src/backend/tcop/pquery.c 2010-02-08 11:46:33.000000000 +0100 *************** ProcessQuery(PlannedStmt *plan, *** 205,211 **** switch (queryDesc->operation) { case CMD_SELECT: ! strcpy(completionTag, "SELECT"); break; case CMD_INSERT: if (queryDesc->estate->es_processed == 1) --- 205,212 ---- switch (queryDesc->operation) { case CMD_SELECT: ! snprintf(completionTag, COMPLETION_TAG_BUFSIZE, ! "SELECT %u", queryDesc->estate->es_processed); break; case CMD_INSERT: if (queryDesc->estate->es_processed == 1) *************** PortalRun(Portal portal, long count, boo *** 714,719 **** --- 715,721 ---- char *completionTag) { bool result; + uint32 nprocessed; ResourceOwner saveTopTransactionResourceOwner; MemoryContext saveTopTransactionContext; Portal saveActivePortal; *************** PortalRun(Portal portal, long count, boo *** 776,814 **** switch (portal->strategy) { case PORTAL_ONE_SELECT: - (void) PortalRunSelect(portal, true, count, dest); - - /* we know the query is supposed to set the tag */ - if (completionTag && portal->commandTag) - strcpy(completionTag, portal->commandTag); - - /* Mark portal not active */ - portal->status = PORTAL_READY; - - /* - * Since it's a forward fetch, say DONE iff atEnd is now true. - */ - result = portal->atEnd; - break; - case PORTAL_ONE_RETURNING: case PORTAL_UTIL_SELECT: /* * If we have not yet run the command, do so, storing its ! * results in the portal's tuplestore. */ ! if (!portal->holdStore) FillPortalStore(portal, isTopLevel); /* * Now fetch desired portion of results. */ ! (void) PortalRunSelect(portal, true, count, dest); ! /* we know the query is supposed to set the tag */ if (completionTag && portal->commandTag) ! strcpy(completionTag, portal->commandTag); /* Mark portal not active */ portal->status = PORTAL_READY; --- 778,812 ---- switch (portal->strategy) { case PORTAL_ONE_SELECT: case PORTAL_ONE_RETURNING: case PORTAL_UTIL_SELECT: /* * If we have not yet run the command, do so, storing its ! * results in the portal's tuplestore. Do this only for the ! * PORTAL_ONE_RETURNING and PORTAL_UTIL_SELECT cases. */ ! if ((portal->strategy != PORTAL_ONE_SELECT) && (!portal->holdStore)) FillPortalStore(portal, isTopLevel); /* * Now fetch desired portion of results. */ ! nprocessed = PortalRunSelect(portal, true, count, dest); ! /* ! * If the portal result contains a command tag and the caller ! * gave us a pointer to store it, copy it. Patch the "SELECT" ! * tag to also provide the rowcount. ! */ if (completionTag && portal->commandTag) ! { ! if (strcmp(portal->commandTag, "SELECT") == 0) ! snprintf(completionTag, COMPLETION_TAG_BUFSIZE, ! "SELECT %u", nprocessed); ! else ! strcpy(completionTag, portal->commandTag); ! } /* Mark portal not active */ portal->status = PORTAL_READY; *************** PortalRunMulti(Portal portal, bool isTop *** 1318,1337 **** * If a command completion tag was supplied, use it. Otherwise use the * portal's commandTag as the default completion tag. * ! * Exception: clients will expect INSERT/UPDATE/DELETE tags to have ! * counts, so fake something up if necessary. (This could happen if the * original query was replaced by a DO INSTEAD rule.) */ if (completionTag && completionTag[0] == '\0') { if (portal->commandTag) strcpy(completionTag, portal->commandTag); if (strcmp(completionTag, "INSERT") == 0) ! strcpy(completionTag, "INSERT 0 0"); else if (strcmp(completionTag, "UPDATE") == 0) ! strcpy(completionTag, "UPDATE 0"); else if (strcmp(completionTag, "DELETE") == 0) ! strcpy(completionTag, "DELETE 0"); } } --- 1316,1346 ---- * If a command completion tag was supplied, use it. Otherwise use the * portal's commandTag as the default completion tag. * ! * Exception: clients will expect SELECT/INSERT/UPDATE/DELETE tags to have ! * counts, so try to provide the proper info. (This could happen if the * original query was replaced by a DO INSTEAD rule.) */ if (completionTag && completionTag[0] == '\0') { + Oid lastOid = InvalidOid; + uint32 processed = 0; + + if (portal->queryDesc && portal->queryDesc->estate) + { + lastOid = portal->queryDesc->estate->es_lastoid; + processed = portal->queryDesc->estate->es_processed; + } + if (portal->commandTag) strcpy(completionTag, portal->commandTag); if (strcmp(completionTag, "INSERT") == 0) ! sprintf(completionTag, "INSERT %u %u", lastOid, processed); else if (strcmp(completionTag, "UPDATE") == 0) ! sprintf(completionTag, "UPDATE %u", processed); else if (strcmp(completionTag, "DELETE") == 0) ! sprintf(completionTag, "DELETE %u", processed); ! else if (strcmp(completionTag, "SELECT") == 0) ! sprintf(completionTag, "SELECT %u", processed); } } diff -dcrpN pgsql.orig/src/interfaces/libpq/fe-exec.c pgsql/src/interfaces/libpq/fe-exec.c *** pgsql.orig/src/interfaces/libpq/fe-exec.c 2010-01-21 20:45:39.000000000 +0100 --- pgsql/src/interfaces/libpq/fe-exec.c 2010-02-08 10:32:25.000000000 +0100 *************** PQcmdTuples(PGresult *res) *** 2753,2758 **** --- 2753,2759 ---- p++; } else if (strncmp(res->cmdStatus, "DELETE ", 7) == 0 || + strncmp(res->cmdStatus, "SELECT ", 7) == 0 || strncmp(res->cmdStatus, "UPDATE ", 7) == 0) p = res->cmdStatus + 7; else if (strncmp(res->cmdStatus, "FETCH ", 6) == 0)