From 523adeb1119bbc68e056c4e3a2aa2a62dcf26e9e Mon Sep 17 00:00:00 2001
From: Tom Lane <tgl@sss.pgh.pa.us>
Date: Fri, 3 Mar 2006 20:57:32 +0000
Subject: [PATCH] Teach PQcmdTuples() that a COPY command tag might contain a
 row count, and tighten up its sanity checking of the tag as a safety measure.
 Volkan Yazici.

---
 doc/src/sgml/libpq.sgml        | 11 +++++----
 src/interfaces/libpq/fe-exec.c | 45 ++++++++++++++++++++--------------
 2 files changed, 32 insertions(+), 24 deletions(-)

diff --git a/doc/src/sgml/libpq.sgml b/doc/src/sgml/libpq.sgml
index 9c48948c868..ccec9c72b87 100644
--- a/doc/src/sgml/libpq.sgml
+++ b/doc/src/sgml/libpq.sgml
@@ -1,5 +1,5 @@
 <!--
-$PostgreSQL: pgsql/doc/src/sgml/libpq.sgml,v 1.204 2006/03/01 00:23:21 momjian Exp $
+$PostgreSQL: pgsql/doc/src/sgml/libpq.sgml,v 1.205 2006/03/03 20:57:32 tgl Exp $
 -->
 
  <chapter id="libpq">
@@ -2127,12 +2127,13 @@ char *PQcmdTuples(PGresult *res);
           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</>, or
-          <command>FETCH</> statement, or an <command>EXECUTE</> of a
-          prepared query that contains a <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 the empty
+          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/src/interfaces/libpq/fe-exec.c b/src/interfaces/libpq/fe-exec.c
index 4edb065c9cc..2643166df6c 100644
--- a/src/interfaces/libpq/fe-exec.c
+++ b/src/interfaces/libpq/fe-exec.c
@@ -8,13 +8,12 @@
  *
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/interfaces/libpq/fe-exec.c,v 1.179 2006/01/25 20:44:32 tgl Exp $
+ *	  $PostgreSQL: pgsql/src/interfaces/libpq/fe-exec.c,v 1.180 2006/03/03 20:57:32 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
 #include "postgres_fe.h"
 
-#include <errno.h>
 #include <ctype.h>
 #include <fcntl.h>
 
@@ -2168,8 +2167,8 @@ PQoidValue(const PGresult *res)
 
 /*
  * PQcmdTuples -
- *	If the last command was an INSERT/UPDATE/DELETE/MOVE/FETCH, return a
- *	string containing the number of inserted/affected tuples. If not,
+ *	If the last command was INSERT/UPDATE/DELETE/MOVE/FETCH/COPY, return
+ *	a string containing the number of inserted/affected tuples. If not,
  *	return "".
  *
  *	XXX: this should probably return an int
@@ -2177,40 +2176,48 @@ PQoidValue(const PGresult *res)
 char *
 PQcmdTuples(PGresult *res)
 {
-	char	   *p;
+	char	   *p, *c;
 
 	if (!res)
 		return "";
 
 	if (strncmp(res->cmdStatus, "INSERT ", 7) == 0)
 	{
-		p = res->cmdStatus + 6;
-		p++;
-		/* INSERT: skip oid */
-		while (*p != ' ' && *p)
+		p = res->cmdStatus + 7;
+		/* INSERT: skip oid and space */
+		while (*p && *p != ' ')
 			p++;
+		if (*p == 0)
+			goto interpret_error;				/* no space? */
+		p++;
 	}
 	else if (strncmp(res->cmdStatus, "DELETE ", 7) == 0 ||
 			 strncmp(res->cmdStatus, "UPDATE ", 7) == 0)
-		p = res->cmdStatus + 6;
+		p = res->cmdStatus + 7;
 	else if (strncmp(res->cmdStatus, "FETCH ", 6) == 0)
+		p = res->cmdStatus + 6;
+	else if (strncmp(res->cmdStatus, "MOVE ", 5) == 0 ||
+			 strncmp(res->cmdStatus, "COPY ", 5) == 0)
 		p = res->cmdStatus + 5;
-	else if (strncmp(res->cmdStatus, "MOVE ", 5) == 0)
-		p = res->cmdStatus + 4;
 	else
 		return "";
 
-	p++;
-
-	if (*p == 0)
+	/* check that we have an integer (at least one digit, nothing else) */
+	for (c = p; *c; c++)
 	{
-		pqInternalNotice(&res->noticeHooks,
-						 "could not interpret result from server: %s",
-						 res->cmdStatus);
-		return "";
+		if (!isdigit((unsigned char) *c))
+			goto interpret_error;
 	}
+	if (c == p)
+		goto interpret_error;
 
 	return p;
+	
+interpret_error:
+	pqInternalNotice(&res->noticeHooks,
+					 "could not interpret result from server: %s",
+					 res->cmdStatus);
+	return "";
 }
 
 /*
-- 
GitLab