From aa7e7ae9a6adfd5553ed05144cf765fbf7c8f5af Mon Sep 17 00:00:00 2001 From: Bruce Momjian <bruce@momjian.us> Date: Tue, 16 Feb 2010 20:58:14 +0000 Subject: [PATCH] Have SELECT and CREATE TABLE AS queries return a row count. While this is invisible in psql, other interfaces, like libpq, make this value visible. Boszormenyi Zoltan --- doc/src/sgml/libpq.sgml | 21 ++++++++-------- doc/src/sgml/protocol.sgml | 8 +++++- src/backend/tcop/pquery.c | 46 +++++++++++++++++----------------- src/interfaces/libpq/fe-exec.c | 5 ++-- 4 files changed, 43 insertions(+), 37 deletions(-) diff --git a/doc/src/sgml/libpq.sgml b/doc/src/sgml/libpq.sgml index c7131fea4c4..4972a8c2592 100644 --- a/doc/src/sgml/libpq.sgml +++ b/doc/src/sgml/libpq.sgml @@ -1,4 +1,4 @@ -<!-- $PostgreSQL: pgsql/doc/src/sgml/libpq.sgml,v 1.297 2010/02/05 03:09:04 joe Exp $ --> +<!-- $PostgreSQL: pgsql/doc/src/sgml/libpq.sgml,v 1.298 2010/02/16 20:58:13 momjian Exp $ --> <chapter id="libpq"> <title><application>libpq</application> - C Library</title> @@ -2869,12 +2869,11 @@ typedef struct { </sect2> <sect2 id="libpq-exec-nonselect"> - <title>Retrieving Result Information for Other Commands</title> + <title>Retrieving Other Result Information</title> <para> - These functions are used to extract information from - <structname>PGresult</structname> objects that are not - <command>SELECT</> results. + These functions are used to extract other information from + <structname>PGresult</structname> objects. </para> <variablelist> @@ -2925,12 +2924,12 @@ typedef struct { This function returns a string containing the number of rows affected by the <acronym>SQL</> statement that generated the <structname>PGresult</>. This function can only be used following - the execution of an <command>INSERT</>, <command>UPDATE</>, - <command>DELETE</>, <command>MOVE</>, <command>FETCH</>, or - <command>COPY</> statement, or an <command>EXECUTE</> of a - prepared query that contains an <command>INSERT</>, - <command>UPDATE</>, or <command>DELETE</> statement. If the - command that generated the <structname>PGresult</> was anything + the execution of a <command>SELECT</>, <command>CREATE TABLE AS</>, + <command>INSERT</>, <command>UPDATE</>, <command>DELETE</>, + <command>MOVE</>, <command>FETCH</>, or <command>COPY</> statement, + or an <command>EXECUTE</> of a prepared query that contains an + <command>INSERT</>, <command>UPDATE</>, or <command>DELETE</> statement. + If the command that generated the <structname>PGresult</> was anything else, <function>PQcmdTuples</> returns an empty string. The caller should not free the return value directly. It will be freed when the associated <structname>PGresult</> handle is passed to diff --git a/doc/src/sgml/protocol.sgml b/doc/src/sgml/protocol.sgml index 5b69f373243..e4364ec305f 100644 --- a/doc/src/sgml/protocol.sgml +++ b/doc/src/sgml/protocol.sgml @@ -1,4 +1,4 @@ -<!-- $PostgreSQL: pgsql/doc/src/sgml/protocol.sgml,v 1.79 2010/02/16 20:15:14 momjian Exp $ --> +<!-- $PostgreSQL: pgsql/doc/src/sgml/protocol.sgml,v 1.80 2010/02/16 20:58:14 momjian Exp $ --> <chapter id="protocol"> <title>Frontend/Backend Protocol</title> @@ -2221,6 +2221,12 @@ CommandComplete (B) <replaceable>rows</replaceable> is the number of rows updated. </para> + <para> + For a <command>SELECT</command> or <command>CREATE TABLE AS</command> + command, the tag is <literal>SELECT <replaceable>rows</replaceable></literal> + where <replaceable>rows</replaceable> is the number of rows retrieved. + </para> + <para> For a <command>MOVE</command> command, the tag is <literal>MOVE <replaceable>rows</replaceable></literal> where diff --git a/src/backend/tcop/pquery.c b/src/backend/tcop/pquery.c index 42960b82250..8beb82385a6 100644 --- a/src/backend/tcop/pquery.c +++ b/src/backend/tcop/pquery.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/tcop/pquery.c,v 1.135 2010/02/13 22:45:41 momjian Exp $ + * $PostgreSQL: pgsql/src/backend/tcop/pquery.c,v 1.136 2010/02/16 20:58:14 momjian Exp $ * *------------------------------------------------------------------------- */ @@ -205,7 +205,8 @@ ProcessQuery(PlannedStmt *plan, switch (queryDesc->operation) { case CMD_SELECT: - strcpy(completionTag, "SELECT"); + snprintf(completionTag, COMPLETION_TAG_BUFSIZE, + "SELECT %u", queryDesc->estate->es_processed); break; case CMD_INSERT: if (queryDesc->estate->es_processed == 1) @@ -714,6 +715,7 @@ PortalRun(Portal portal, long count, bool isTopLevel, char *completionTag) { bool result; + uint32 nprocessed; ResourceOwner saveTopTransactionResourceOwner; MemoryContext saveTopTransactionContext; Portal saveActivePortal; @@ -776,39 +778,35 @@ PortalRun(Portal portal, long count, bool isTopLevel, 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. + * results in the portal's tuplestore. Do this only for the + * PORTAL_ONE_RETURNING and PORTAL_UTIL_SELECT cases. */ - if (!portal->holdStore) + if (portal->strategy != PORTAL_ONE_SELECT && !portal->holdStore) FillPortalStore(portal, isTopLevel); /* * Now fetch desired portion of results. */ - (void) PortalRunSelect(portal, true, count, dest); + nprocessed = PortalRunSelect(portal, true, count, dest); - /* we know the query is supposed to set the tag */ + /* + * 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) - strcpy(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; @@ -1331,7 +1329,9 @@ PortalRunMulti(Portal portal, bool isTopLevel, { if (portal->commandTag) strcpy(completionTag, portal->commandTag); - if (strcmp(completionTag, "INSERT") == 0) + if (strcmp(completionTag, "SELECT") == 0) + sprintf(completionTag, "SELECT 0 0"); + else if (strcmp(completionTag, "INSERT") == 0) strcpy(completionTag, "INSERT 0 0"); else if (strcmp(completionTag, "UPDATE") == 0) strcpy(completionTag, "UPDATE 0"); diff --git a/src/interfaces/libpq/fe-exec.c b/src/interfaces/libpq/fe-exec.c index 2e5551d31e5..df4e8879c84 100644 --- a/src/interfaces/libpq/fe-exec.c +++ b/src/interfaces/libpq/fe-exec.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/interfaces/libpq/fe-exec.c,v 1.208 2010/01/21 18:43:25 rhaas Exp $ + * $PostgreSQL: pgsql/src/interfaces/libpq/fe-exec.c,v 1.209 2010/02/16 20:58:14 momjian Exp $ * *------------------------------------------------------------------------- */ @@ -2752,7 +2752,8 @@ PQcmdTuples(PGresult *res) goto interpret_error; /* no space? */ p++; } - else if (strncmp(res->cmdStatus, "DELETE ", 7) == 0 || + else if (strncmp(res->cmdStatus, "SELECT ", 7) == 0 || + strncmp(res->cmdStatus, "DELETE ", 7) == 0 || strncmp(res->cmdStatus, "UPDATE ", 7) == 0) p = res->cmdStatus + 7; else if (strncmp(res->cmdStatus, "FETCH ", 6) == 0) -- GitLab