Re: PQmakeEmptyPGresult() will return NULL if allocation - Mailing list pgsql-patches

From Neil Conway
Subject Re: PQmakeEmptyPGresult() will return NULL if allocation
Date
Msg-id 42AADAC5.8010106@samurai.com
Whole thread Raw
In response to Re: PQmakeEmptyPGresult() will return NULL if allocation  (Neil Conway <neilc@samurai.com>)
Responses Re: PQmakeEmptyPGresult() will return NULL if allocation  (Neil Conway <neilc@samurai.com>)
List pgsql-patches
Neil Conway wrote:
> Some checking reveals that there are a few other bits of code in libpq
> that don't handle malloc() failures correctly. I'll take a look at
> fixing this today or tomorrow.

Attached is a patch that implements this. This adds checking for most of
the call sites of strdup(), malloc(), PQmakeEmptyPGresult(),
pqResultAlloc(), and pqResultStrdup() in libpq. Barring any objections,
I'll apply this to HEAD tomorrow.

-Neil
Index: doc/src/sgml/libpq.sgml
===================================================================
RCS file: /Users/neilc/local/cvs/pgsql/doc/src/sgml/libpq.sgml,v
retrieving revision 1.184
diff -c -r1.184 libpq.sgml
*** doc/src/sgml/libpq.sgml    10 Jun 2005 03:02:01 -0000    1.184
--- doc/src/sgml/libpq.sgml    11 Jun 2005 09:28:08 -0000
***************
*** 581,594 ****
  </para>

  <para>
!    Returns a connection options array.  This may
!    be used to determine all possible <function>PQconnectdb</function> options and their
     current default values.  The return value points to an array of
!    <structname>PQconninfoOption</structname> structures, which ends with an entry having a null
!    <structfield>keyword</> pointer.  Note that the current default values
!    (<structfield>val</structfield> fields)
!    will depend on environment variables and other context.
!    Callers must treat the connection options data as read-only.
     </para>

     <para>
--- 581,595 ----
  </para>

  <para>
!    Returns a connection options array.  This may be used to determine
!    all possible <function>PQconnectdb</function> options and their
     current default values.  The return value points to an array of
!    <structname>PQconninfoOption</structname> structures, which ends
!    with an entry having a null <structfield>keyword</> pointer.  The
!    null pointer is returned if memory could not be allocated. Note that
!    the current default values (<structfield>val</structfield> fields)
!    will depend on environment variables and other context.  Callers
!    must treat the connection options data as read-only.
     </para>

     <para>
***************
*** 1651,1668 ****
  <para>
            Constructs an empty <structname>PGresult</structname> object with the given status.
  <synopsis>
! PGresult* PQmakeEmptyPGresult(PGconn *conn, ExecStatusType status);
  </synopsis>
  </para>

  <para>
! This is <application>libpq</>'s internal function to allocate and initialize an empty
! <structname>PGresult</structname> object.  It is exported because some applications find it
! useful to generate result objects (particularly objects with error
! status) themselves.  If <parameter>conn</parameter> is not null and <parameter>status</> indicates an error,
! the current error message of the specified connection is copied into the <structname>PGresult</structname>.
! Note that <function>PQclear</function> should eventually be called on the object, just
! as with a <structname>PGresult</structname> returned by <application>libpq</application> itself.
  </para>
  </listitem>
  </varlistentry>
--- 1652,1673 ----
  <para>
            Constructs an empty <structname>PGresult</structname> object with the given status.
  <synopsis>
! PGresult *PQmakeEmptyPGresult(PGconn *conn, ExecStatusType status);
  </synopsis>
  </para>

  <para>
! This is <application>libpq</>'s internal function to allocate and
! initialize an empty <structname>PGresult</structname> object.  This
! function returns NULL if memory could not be allocated. It is exported
! because some applications find it useful to generate result objects
! (particularly objects with error status) themselves.  If
! <parameter>conn</parameter> is not null and <parameter>status</>
! indicates an error, the current error message of the specified
! connection is copied into the <structname>PGresult</structname>.  Note
! that <function>PQclear</function> should eventually be called on the
! object, just as with a <structname>PGresult</structname> returned by
! <application>libpq</application> itself.
  </para>
  </listitem>
  </varlistentry>
***************
*** 2266,2280 ****
    <para>
     <function>PQescapeBytea</> returns an escaped version of the
     <parameter>from</parameter> parameter binary string in memory
!    allocated with <function>malloc()</>.  This memory must be freed
!    using <function>PQfreemem</> when the result is no longer needed.
!    The return string has all special characters replaced so that they
!    can be properly processed by the
!    <productname>PostgreSQL</productname> string literal parser, and
!    the <type>bytea</type> input function. A terminating zero byte is
!    also added.  The single quotes that must surround
!    <productname>PostgreSQL</productname> string literals are not part
!    of the result string.
    </para>
    </listitem>
    </varlistentry>
--- 2271,2285 ----
    <para>
     <function>PQescapeBytea</> returns an escaped version of the
     <parameter>from</parameter> parameter binary string in memory
!    allocated with <function>malloc()</> (a null pointer is returned if
!    memory could not be allocated).  This memory must be freed using
!    <function>PQfreemem</> when the result is no longer needed.  The
!    return string has all special characters replaced so that they can
!    be properly processed by the <productname>PostgreSQL</productname>
!    string literal parser, and the <type>bytea</type> input function. A
!    terminating zero byte is also added.  The single quotes that must
!    surround <productname>PostgreSQL</productname> string literals are
!    not part of the result string.
    </para>
    </listitem>
    </varlistentry>
Index: src/interfaces/libpq/fe-connect.c
===================================================================
RCS file: /Users/neilc/local/cvs/pgsql/src/interfaces/libpq/fe-connect.c,v
retrieving revision 1.309
diff -c -r1.309 fe-connect.c
*** src/interfaces/libpq/fe-connect.c    10 Jun 2005 04:01:36 -0000    1.309
--- src/interfaces/libpq/fe-connect.c    11 Jun 2005 09:25:08 -0000
***************
*** 368,373 ****
--- 368,375 ----
       *
       * Don't put anything cute here --- intelligence should be in
       * connectOptions2 ...
+      *
+      * XXX: probably worth checking strdup() return value here...
       */
      tmp = conninfo_getval(connOptions, "hostaddr");
      conn->pghostaddr = tmp ? strdup(tmp) : NULL;
***************
*** 459,465 ****
      }

  #ifdef NOT_USED
-
      /*
       * parse dbName to get all additional info in it, if any
       */
--- 461,466 ----
***************
*** 2167,2177 ****
  }

  /*
!    PQfinish:
!       properly close a connection to the backend
!       also frees the PGconn data structure so it shouldn't be re-used
!       after this
! */
  void
  PQfinish(PGconn *conn)
  {
--- 2168,2176 ----
  }

  /*
!  * PQfinish: properly close a connection to the backend. Also frees
!  * the PGconn data structure so it shouldn't be re-used after this.
!  */
  void
  PQfinish(PGconn *conn)
  {
***************
*** 2182,2191 ****
      }
  }

! /* PQreset :
!    resets the connection to the backend
!    closes the existing connection and makes a new one
! */
  void
  PQreset(PGconn *conn)
  {
--- 2181,2190 ----
      }
  }

! /*
!  * PQreset: resets the connection to the backend by closing the
!  * existing connection and creating a new one.
!  */
  void
  PQreset(PGconn *conn)
  {
***************
*** 2199,2209 ****
  }


! /* PQresetStart :
!    resets the connection to the backend
!    closes the existing connection and makes a new one
!    Returns 1 on success, 0 on failure.
! */
  int
  PQresetStart(PGconn *conn)
  {
--- 2198,2209 ----
  }


! /*
!  * PQresetStart:
!  * resets the connection to the backend
!  * closes the existing connection and makes a new one
!  * Returns 1 on success, 0 on failure.
!  */
  int
  PQresetStart(PGconn *conn)
  {
***************
*** 2218,2228 ****
  }


! /* PQresetPoll :
!    resets the connection to the backend
!    closes the existing connection and makes a new one
! */
!
  PostgresPollingStatusType
  PQresetPoll(PGconn *conn)
  {
--- 2218,2228 ----
  }


! /*
!  * PQresetPoll:
!  * resets the connection to the backend
!  * closes the existing connection and makes a new one
!  */
  PostgresPollingStatusType
  PQresetPoll(PGconn *conn)
  {
***************
*** 2514,2520 ****
       * location to find our config files.
       */
      snprintf(serviceFile, MAXPGPATH, "%s/pg_service.conf",
!            getenv("PGSYSCONFDIR") ? getenv("PGSYSCONFDIR") : SYSCONFDIR);

      if (service != NULL)
      {
--- 2514,2520 ----
       * location to find our config files.
       */
      snprintf(serviceFile, MAXPGPATH, "%s/pg_service.conf",
!              getenv("PGSYSCONFDIR") ? getenv("PGSYSCONFDIR") : SYSCONFDIR);

      if (service != NULL)
      {
***************
*** 2802,2808 ****
          if (option->val)
              free(option->val);
          option->val = strdup(pval);
!
      }

      /* Done with the modifiable input string */
--- 2802,2815 ----
          if (option->val)
              free(option->val);
          option->val = strdup(pval);
!         if (!option->val)
!         {
!             printfPQExpBuffer(errorMessage,
!                               libpq_gettext("out of memory\n"));
!             PQconninfoFree(options);
!             free(buf);
!             return NULL;
!         }
      }

      /* Done with the modifiable input string */
***************
*** 2835,2840 ****
--- 2842,2854 ----
              if ((tmp = getenv(option->envvar)) != NULL)
              {
                  option->val = strdup(tmp);
+                 if (!option->val)
+                 {
+                     printfPQExpBuffer(errorMessage,
+                                       libpq_gettext("out of memory\n"));
+                     PQconninfoFree(options);
+                     return NULL;
+                 }
                  continue;
              }
          }
***************
*** 2846,2851 ****
--- 2860,2872 ----
          if (option->compiled != NULL)
          {
              option->val = strdup(option->compiled);
+             if (!option->val)
+             {
+                 printfPQExpBuffer(errorMessage,
+                                   libpq_gettext("out of memory\n"));
+                 PQconninfoFree(options);
+                 return NULL;
+             }
              continue;
          }

Index: src/interfaces/libpq/fe-exec.c
===================================================================
RCS file: /Users/neilc/local/cvs/pgsql/src/interfaces/libpq/fe-exec.c,v
retrieving revision 1.168
diff -c -r1.168 fe-exec.c
*** src/interfaces/libpq/fe-exec.c    9 Jun 2005 20:01:16 -0000    1.168
--- src/interfaces/libpq/fe-exec.c    11 Jun 2005 12:28:45 -0000
***************
*** 134,139 ****
--- 134,141 ----
      PGresult   *result;

      result = (PGresult *) malloc(sizeof(PGresult));
+     if (!result)
+         return NULL;

      result->ntups = 0;
      result->numAttributes = 0;
***************
*** 453,459 ****
   * a trailing newline, and should not be more than one line).
   */
  void
! pqInternalNotice(const PGNoticeHooks *hooks, const char *fmt,...)
  {
      char        msgBuf[1024];
      va_list        args;
--- 455,461 ----
   * a trailing newline, and should not be more than one line).
   */
  void
! pqInternalNotice(const PGNoticeHooks *hooks, const char *fmt, ...)
  {
      char        msgBuf[1024];
      va_list        args;
***************
*** 470,475 ****
--- 472,479 ----

      /* Make a PGresult to pass to the notice receiver */
      res = PQmakeEmptyPGresult(NULL, PGRES_NONFATAL_ERROR);
+     if (!res)
+         return;
      res->noticeHooks = *hooks;

      /*
***************
*** 480,494 ****
      /* XXX should provide a SQLSTATE too? */

      /*
!      * Result text is always just the primary message + newline.
       */
      res->errMsg = (char *) pqResultAlloc(res, strlen(msgBuf) + 2, FALSE);
!     sprintf(res->errMsg, "%s\n", msgBuf);

!     /*
!      * Pass to receiver, then free it.
!      */
!     (*res->noticeHooks.noticeRec) (res->noticeHooks.noticeRecArg, res);
      PQclear(res);
  }

--- 484,502 ----
      /* XXX should provide a SQLSTATE too? */

      /*
!      * Result text is always just the primary message + newline. If we
!      * can't allocate it, don't bother invoking the receiver.
       */
      res->errMsg = (char *) pqResultAlloc(res, strlen(msgBuf) + 2, FALSE);
!     if (res->errMsg)
!     {
!         sprintf(res->errMsg, "%s\n", msgBuf);

!         /*
!          * Pass to receiver, then free it.
!          */
!         (*res->noticeHooks.noticeRec) (res->noticeHooks.noticeRecArg, res);
!     }
      PQclear(res);
  }

***************
*** 1127,1134 ****

  /*
   * PQgetResult
!  *      Get the next PGresult produced by a query.
!  *      Returns NULL if and only if no query work remains.
   */

  PGresult *
--- 1135,1143 ----

  /*
   * PQgetResult
!  *      Get the next PGresult produced by a query.  Returns NULL if no
!  *      query work remains or an error has occurred (e.g. out of
!  *      memory).
   */

  PGresult *
Index: src/interfaces/libpq/fe-misc.c
===================================================================
RCS file: /Users/neilc/local/cvs/pgsql/src/interfaces/libpq/fe-misc.c,v
retrieving revision 1.113
diff -c -r1.113 fe-misc.c
*** src/interfaces/libpq/fe-misc.c    22 Feb 2005 04:42:20 -0000    1.113
--- src/interfaces/libpq/fe-misc.c    11 Jun 2005 07:19:32 -0000
***************
*** 175,181 ****
      conn->inCursor += len;

      if (conn->Pfdebug)
!         fprintf(conn->Pfdebug, libpq_gettext("From backend (%lu)> %.*s\n"), (unsigned long) len, (int) len, s);

      return 0;
  }
--- 175,182 ----
      conn->inCursor += len;

      if (conn->Pfdebug)
!         fprintf(conn->Pfdebug, libpq_gettext("From backend (%lu)> %.*s\n"),
!                 (unsigned long) len, (int) len, s);

      return 0;
  }
Index: src/interfaces/libpq/fe-print.c
===================================================================
RCS file: /Users/neilc/local/cvs/pgsql/src/interfaces/libpq/fe-print.c,v
retrieving revision 1.59
diff -c -r1.59 fe-print.c
*** src/interfaces/libpq/fe-print.c    22 Feb 2005 04:42:20 -0000    1.59
--- src/interfaces/libpq/fe-print.c    11 Jun 2005 09:40:47 -0000
***************
*** 62,75 ****
   * details
   *
   * This function should probably be removed sometime since psql
!  * doesn't use it anymore. It is unclear to what extend this is used
   * by external clients, however.
   */
-
  void
! PQprint(FILE *fout,
!         const PGresult *res,
!         const PQprintOpt *po)
  {
      int            nFields;

--- 62,72 ----
   * details
   *
   * This function should probably be removed sometime since psql
!  * doesn't use it anymore. It is unclear to what extent this is used
   * by external clients, however.
   */
  void
! PQprint(FILE *fout, const PGresult *res, const PQprintOpt *po)
  {
      int            nFields;

***************
*** 611,616 ****
--- 608,619 ----
      if (fillAlign)
      {
          fLength = (int *) malloc(nFields * sizeof(int));
+         if (!fLength)
+         {
+             fprintf(stderr, libpq_gettext("out of memory\n"));
+             exit(1);
+         }
+
          for (j = 0; j < nFields; j++)
          {
              fLength[j] = strlen(PQfname(res, j));
***************
*** 705,710 ****
--- 708,718 ----

              width = nFields * 14;
              tborder = malloc(width + 1);
+             if (!tborder)
+             {
+                 fprintf(stderr, libpq_gettext("out of memory\n"));
+                 exit(1);
+             }
              for (i = 0; i <= width; i++)
                  tborder[i] = '-';
              tborder[i] = '\0';
Index: src/interfaces/libpq/fe-protocol2.c
===================================================================
RCS file: /Users/neilc/local/cvs/pgsql/src/interfaces/libpq/fe-protocol2.c,v
retrieving revision 1.17
diff -c -r1.17 fe-protocol2.c
*** src/interfaces/libpq/fe-protocol2.c    11 May 2005 01:26:02 -0000    1.17
--- src/interfaces/libpq/fe-protocol2.c    11 Jun 2005 09:42:59 -0000
***************
*** 438,445 ****
--- 438,449 ----
                      if (pqGets(&conn->workBuffer, conn))
                          return;
                      if (conn->result == NULL)
+                     {
                          conn->result = PQmakeEmptyPGresult(conn,
                                                         PGRES_COMMAND_OK);
+                         if (!conn->result)
+                             return;
+                     }
                      strncpy(conn->result->cmdStatus, conn->workBuffer.data,
                              CMDSTATUS_LEN);
                      checkXactStatus(conn, conn->workBuffer.data);
***************
*** 572,590 ****
  static int
  getRowDescriptions(PGconn *conn)
  {
!     PGresult   *result;
      int            nfields;
      int            i;

      result = PQmakeEmptyPGresult(conn, PGRES_TUPLES_OK);

      /* parseInput already read the 'T' label. */
      /* the next two bytes are the number of fields    */
      if (pqGetInt(&(result->numAttributes), 2, conn))
!     {
!         PQclear(result);
!         return EOF;
!     }
      nfields = result->numAttributes;

      /* allocate space for the attribute descriptors */
--- 576,593 ----
  static int
  getRowDescriptions(PGconn *conn)
  {
!     PGresult   *result = NULL;
      int            nfields;
      int            i;

      result = PQmakeEmptyPGresult(conn, PGRES_TUPLES_OK);
+     if (!result)
+         goto failure;

      /* parseInput already read the 'T' label. */
      /* the next two bytes are the number of fields    */
      if (pqGetInt(&(result->numAttributes), 2, conn))
!         goto failure;
      nfields = result->numAttributes;

      /* allocate space for the attribute descriptors */
***************
*** 592,597 ****
--- 595,602 ----
      {
          result->attDescs = (PGresAttDesc *)
              pqResultAlloc(result, nfields * sizeof(PGresAttDesc), TRUE);
+         if (!result->attDescs)
+             goto failure;
          MemSet(result->attDescs, 0, nfields * sizeof(PGresAttDesc));
      }

***************
*** 606,615 ****
              pqGetInt(&typid, 4, conn) ||
              pqGetInt(&typlen, 2, conn) ||
              pqGetInt(&atttypmod, 4, conn))
!         {
!             PQclear(result);
!             return EOF;
!         }

          /*
           * Since pqGetInt treats 2-byte integers as unsigned, we need to
--- 611,617 ----
              pqGetInt(&typid, 4, conn) ||
              pqGetInt(&typlen, 2, conn) ||
              pqGetInt(&atttypmod, 4, conn))
!             goto failure;

          /*
           * Since pqGetInt treats 2-byte integers as unsigned, we need to
***************
*** 619,624 ****
--- 621,628 ----

          result->attDescs[i].name = pqResultStrdup(result,
                                                    conn->workBuffer.data);
+         if (!result->attDescs[i].name)
+             goto failure;
          result->attDescs[i].tableid = 0;
          result->attDescs[i].columnid = 0;
          result->attDescs[i].format = 0;
***************
*** 630,635 ****
--- 634,644 ----
      /* Success! */
      conn->result = result;
      return 0;
+
+ failure:
+     if (result)
+         PQclear(result);
+     return EOF;
  }

  /*
***************
*** 685,691 ****
--- 694,704 ----
      nbytes = (nfields + BITS_PER_BYTE - 1) / BITS_PER_BYTE;
      /* malloc() only for unusually large field counts... */
      if (nbytes > sizeof(std_bitmap))
+     {
          bitmap = (char *) malloc(nbytes);
+         if (!bitmap)
+             goto outOfMemory;
+     }

      if (pqGetnchar(bitmap, nbytes, conn))
          goto EOFexit;
***************
*** 758,770 ****
      pqClearAsyncResult(conn);
      printfPQExpBuffer(&conn->errorMessage,
                        libpq_gettext("out of memory for query result\n"));
      conn->result = PQmakeEmptyPGresult(conn, PGRES_FATAL_ERROR);
      conn->asyncStatus = PGASYNC_READY;
      /* Discard the failed message --- good idea? */
      conn->inStart = conn->inEnd;

  EOFexit:
!     if (bitmap != std_bitmap)
          free(bitmap);
      return EOF;
  }
--- 771,788 ----
      pqClearAsyncResult(conn);
      printfPQExpBuffer(&conn->errorMessage,
                        libpq_gettext("out of memory for query result\n"));
+
+     /*
+      * XXX: if PQmakeEmptyPGresult() fails, there's probably not much
+      * we can do to recover...
+      */
      conn->result = PQmakeEmptyPGresult(conn, PGRES_FATAL_ERROR);
      conn->asyncStatus = PGASYNC_READY;
      /* Discard the failed message --- good idea? */
      conn->inStart = conn->inEnd;

  EOFexit:
!     if (bitmap != NULL && bitmap != std_bitmap)
          free(bitmap);
      return EOF;
  }
***************
*** 780,786 ****
  static int
  pqGetErrorNotice2(PGconn *conn, bool isError)
  {
!     PGresult   *res;
      PQExpBufferData workBuf;
      char       *startp;
      char       *splitp;
--- 798,804 ----
  static int
  pqGetErrorNotice2(PGconn *conn, bool isError)
  {
!     PGresult   *res = NULL;
      PQExpBufferData workBuf;
      char       *startp;
      char       *splitp;
***************
*** 792,801 ****
       */
      initPQExpBuffer(&workBuf);
      if (pqGets(&workBuf, conn))
!     {
!         termPQExpBuffer(&workBuf);
!         return EOF;
!     }

      /*
       * Make a PGresult to hold the message.  We temporarily lie about the
--- 810,816 ----
       */
      initPQExpBuffer(&workBuf);
      if (pqGets(&workBuf, conn))
!         goto failure;

      /*
       * Make a PGresult to hold the message.  We temporarily lie about the
***************
*** 803,810 ****
--- 818,829 ----
       * conn->errorMessage.
       */
      res = PQmakeEmptyPGresult(conn, PGRES_EMPTY_QUERY);
+     if (!res)
+         goto failure;
      res->resultStatus = isError ? PGRES_FATAL_ERROR : PGRES_NONFATAL_ERROR;
      res->errMsg = pqResultStrdup(res, workBuf.data);
+     if (!res->errMsg)
+         goto failure;

      /*
       * Break the message into fields.  We can't do very much here, but we
***************
*** 869,874 ****
--- 888,899 ----

      termPQExpBuffer(&workBuf);
      return 0;
+
+ failure:
+     if (res)
+         PQclear(res);
+     termPQExpBuffer(&workBuf);
+     return EOF;
  }

  /*
Index: src/interfaces/libpq/fe-protocol3.c
===================================================================
RCS file: /Users/neilc/local/cvs/pgsql/src/interfaces/libpq/fe-protocol3.c,v
retrieving revision 1.20
diff -c -r1.20 fe-protocol3.c
*** src/interfaces/libpq/fe-protocol3.c    31 Dec 2004 22:03:50 -0000    1.20
--- src/interfaces/libpq/fe-protocol3.c    11 Jun 2005 12:30:51 -0000
***************
*** 51,57 ****
  static int    getNotify(PGconn *conn);
  static int    getCopyStart(PGconn *conn, ExecStatusType copytype);
  static int    getReadyForQuery(PGconn *conn);
! static int build_startup_packet(const PGconn *conn, char *packet,
                       const PQEnvironmentOption *options);


--- 51,57 ----
  static int    getNotify(PGconn *conn);
  static int    getCopyStart(PGconn *conn, ExecStatusType copytype);
  static int    getReadyForQuery(PGconn *conn);
! static int    build_startup_packet(const PGconn *conn, char *packet,
                       const PQEnvironmentOption *options);


***************
*** 197,204 ****
--- 197,208 ----
                      if (pqGets(&conn->workBuffer, conn))
                          return;
                      if (conn->result == NULL)
+                     {
                          conn->result = PQmakeEmptyPGresult(conn,
                                                         PGRES_COMMAND_OK);
+                         if (!conn->result)
+                             return;
+                     }
                      strncpy(conn->result->cmdStatus, conn->workBuffer.data,
                              CMDSTATUS_LEN);
                      conn->asyncStatus = PGASYNC_READY;
***************
*** 215,222 ****
--- 219,230 ----
                      break;
                  case 'I':        /* empty query */
                      if (conn->result == NULL)
+                     {
                          conn->result = PQmakeEmptyPGresult(conn,
                                                        PGRES_EMPTY_QUERY);
+                         if (!conn->result)
+                             return;
+                     }
                      conn->asyncStatus = PGASYNC_READY;
                      break;
                  case '1':        /* Parse Complete */
***************
*** 224,231 ****
--- 232,243 ----
                      if (conn->queryclass == PGQUERY_PREPARE)
                      {
                          if (conn->result == NULL)
+                         {
                              conn->result = PQmakeEmptyPGresult(conn,
                                                                 PGRES_COMMAND_OK);
+                             if (!conn->result)
+                                 return;
+                         }
                          conn->asyncStatus = PGASYNC_READY;
                      }
                      break;
***************
*** 412,425 ****
      int            i;

      result = PQmakeEmptyPGresult(conn, PGRES_TUPLES_OK);

      /* parseInput already read the 'T' label and message length. */
      /* the next two bytes are the number of fields    */
      if (pqGetInt(&(result->numAttributes), 2, conn))
!     {
!         PQclear(result);
!         return EOF;
!     }
      nfields = result->numAttributes;

      /* allocate space for the attribute descriptors */
--- 424,436 ----
      int            i;

      result = PQmakeEmptyPGresult(conn, PGRES_TUPLES_OK);
+     if (!result)
+         goto failure;

      /* parseInput already read the 'T' label and message length. */
      /* the next two bytes are the number of fields    */
      if (pqGetInt(&(result->numAttributes), 2, conn))
!         goto failure;
      nfields = result->numAttributes;

      /* allocate space for the attribute descriptors */
***************
*** 427,433 ****
      {
          result->attDescs = (PGresAttDesc *)
              pqResultAlloc(result, nfields * sizeof(PGresAttDesc), TRUE);
!         MemSet((char *) result->attDescs, 0, nfields * sizeof(PGresAttDesc));
      }

      /* result->binary is true only if ALL columns are binary */
--- 438,446 ----
      {
          result->attDescs = (PGresAttDesc *)
              pqResultAlloc(result, nfields * sizeof(PGresAttDesc), TRUE);
!         if (!result->attDescs)
!             goto failure;
!         MemSet(result->attDescs, 0, nfields * sizeof(PGresAttDesc));
      }

      /* result->binary is true only if ALL columns are binary */
***************
*** 451,458 ****
              pqGetInt(&atttypmod, 4, conn) ||
              pqGetInt(&format, 2, conn))
          {
!             PQclear(result);
!             return EOF;
          }

          /*
--- 464,470 ----
              pqGetInt(&atttypmod, 4, conn) ||
              pqGetInt(&format, 2, conn))
          {
!             goto failure;
          }

          /*
***************
*** 465,470 ****
--- 477,484 ----

          result->attDescs[i].name = pqResultStrdup(result,
                                                    conn->workBuffer.data);
+         if (!result->attDescs[i].name)
+             goto failure;
          result->attDescs[i].tableid = tableid;
          result->attDescs[i].columnid = columnid;
          result->attDescs[i].format = format;
***************
*** 479,484 ****
--- 493,502 ----
      /* Success! */
      conn->result = result;
      return 0;
+
+ failure:
+     PQclear(result);
+     return EOF;
  }

  /*
***************
*** 507,513 ****
              pqResultAlloc(result, nfields * sizeof(PGresAttValue), TRUE);
          if (conn->curTuple == NULL)
              goto outOfMemory;
!         MemSet((char *) conn->curTuple, 0, nfields * sizeof(PGresAttValue));
      }
      tup = conn->curTuple;

--- 525,531 ----
              pqResultAlloc(result, nfields * sizeof(PGresAttValue), TRUE);
          if (conn->curTuple == NULL)
              goto outOfMemory;
!         MemSet(conn->curTuple, 0, nfields * sizeof(PGresAttValue));
      }
      tup = conn->curTuple;

***************
*** 593,612 ****
  int
  pqGetErrorNotice3(PGconn *conn, bool isError)
  {
!     PGresult   *res;
      PQExpBufferData workBuf;
      char        id;
      const char *val;

      /*
-      * Make a PGresult to hold the accumulated fields.    We temporarily lie
-      * about the result status, so that PQmakeEmptyPGresult doesn't
-      * uselessly copy conn->errorMessage.
-      */
-     res = PQmakeEmptyPGresult(conn, PGRES_EMPTY_QUERY);
-     res->resultStatus = isError ? PGRES_FATAL_ERROR : PGRES_NONFATAL_ERROR;
-
-     /*
       * Since the fields might be pretty long, we create a temporary
       * PQExpBuffer rather than using conn->workBuffer.    workBuffer is
       * intended for stuff that is expected to be short.  We shouldn't use
--- 611,622 ----
  int
  pqGetErrorNotice3(PGconn *conn, bool isError)
  {
!     PGresult   *res = NULL;
      PQExpBufferData workBuf;
      char        id;
      const char *val;

      /*
       * Since the fields might be pretty long, we create a temporary
       * PQExpBuffer rather than using conn->workBuffer.    workBuffer is
       * intended for stuff that is expected to be short.  We shouldn't use
***************
*** 615,620 ****
--- 625,640 ----
      initPQExpBuffer(&workBuf);

      /*
+      * Make a PGresult to hold the accumulated fields.    We temporarily lie
+      * about the result status, so that PQmakeEmptyPGresult doesn't
+      * uselessly copy conn->errorMessage.
+      */
+     res = PQmakeEmptyPGresult(conn, PGRES_EMPTY_QUERY);
+     if (!res)
+         goto fail;
+     res->resultStatus = isError ? PGRES_FATAL_ERROR : PGRES_NONFATAL_ERROR;
+
+     /*
       * Read the fields and save into res.
       */
      for (;;)
***************
*** 702,707 ****
--- 722,729 ----
      if (isError)
      {
          res->errMsg = pqResultStrdup(res, workBuf.data);
+         if (!res->errMsg)
+             goto fail;
          pqClearAsyncResult(conn);
          conn->result = res;
          resetPQExpBuffer(&conn->errorMessage);
***************
*** 825,843 ****
      int            i;

      result = PQmakeEmptyPGresult(conn, copytype);

      if (pqGetc(&conn->copy_is_binary, conn))
!     {
!         PQclear(result);
!         return EOF;
!     }
      result->binary = conn->copy_is_binary;
      /* the next two bytes are the number of fields    */
      if (pqGetInt(&(result->numAttributes), 2, conn))
!     {
!         PQclear(result);
!         return EOF;
!     }
      nfields = result->numAttributes;

      /* allocate space for the attribute descriptors */
--- 847,861 ----
      int            i;

      result = PQmakeEmptyPGresult(conn, copytype);
+     if (!result)
+         goto failure;

      if (pqGetc(&conn->copy_is_binary, conn))
!         goto failure;
      result->binary = conn->copy_is_binary;
      /* the next two bytes are the number of fields    */
      if (pqGetInt(&(result->numAttributes), 2, conn))
!         goto failure;
      nfields = result->numAttributes;

      /* allocate space for the attribute descriptors */
***************
*** 845,851 ****
      {
          result->attDescs = (PGresAttDesc *)
              pqResultAlloc(result, nfields * sizeof(PGresAttDesc), TRUE);
!         MemSet((char *) result->attDescs, 0, nfields * sizeof(PGresAttDesc));
      }

      for (i = 0; i < nfields; i++)
--- 863,871 ----
      {
          result->attDescs = (PGresAttDesc *)
              pqResultAlloc(result, nfields * sizeof(PGresAttDesc), TRUE);
!         if (!result->attDescs)
!             goto failure;
!         MemSet(result->attDescs, 0, nfields * sizeof(PGresAttDesc));
      }

      for (i = 0; i < nfields; i++)
***************
*** 853,875 ****
          int            format;

          if (pqGetInt(&format, 2, conn))
!         {
!             PQclear(result);
!             return EOF;
!         }

          /*
           * Since pqGetInt treats 2-byte integers as unsigned, we need to
           * coerce these results to signed form.
           */
          format = (int) ((int16) format);
-
          result->attDescs[i].format = format;
      }

      /* Success! */
      conn->result = result;
      return 0;
  }

  /*
--- 873,895 ----
          int            format;

          if (pqGetInt(&format, 2, conn))
!             goto failure;

          /*
           * Since pqGetInt treats 2-byte integers as unsigned, we need to
           * coerce these results to signed form.
           */
          format = (int) ((int16) format);
          result->attDescs[i].format = format;
      }

      /* Success! */
      conn->result = result;
      return 0;
+
+ failure:
+     PQclear(result);
+     return EOF;
  }

  /*
Index: src/interfaces/libpq/libpq-int.h
===================================================================
RCS file: /Users/neilc/local/cvs/pgsql/src/interfaces/libpq/libpq-int.h,v
retrieving revision 1.101
diff -c -r1.101 libpq-int.h
*** src/interfaces/libpq/libpq-int.h    4 Jun 2005 20:42:43 -0000    1.101
--- src/interfaces/libpq/libpq-int.h    11 Jun 2005 09:29:50 -0000
***************
*** 403,409 ****
  extern void pqSaveErrorResult(PGconn *conn);
  extern PGresult *pqPrepareAsyncResult(PGconn *conn);
  extern void
! pqInternalNotice(const PGNoticeHooks *hooks, const char *fmt,...)
  /* This lets gcc check the format string for consistency. */
  __attribute__((format(printf, 2, 3)));
  extern int    pqAddTuple(PGresult *res, PGresAttValue *tup);
--- 403,409 ----
  extern void pqSaveErrorResult(PGconn *conn);
  extern PGresult *pqPrepareAsyncResult(PGconn *conn);
  extern void
! pqInternalNotice(const PGNoticeHooks *hooks, const char *fmt, ...)
  /* This lets gcc check the format string for consistency. */
  __attribute__((format(printf, 2, 3)));
  extern int    pqAddTuple(PGresult *res, PGresAttValue *tup);

pgsql-patches by date:

Previous
From: Matthias Schmidt
Date:
Subject: Re: pg_starttime()
Next
From: Guillaume LELARGE
Date:
Subject: Updated french .po files