libpq was not consistently checking for memory allocation failures. This

patch adds missing checks to the call sites of malloc(), strdup(),
PQmakeEmptyPGresult(), pqResultAlloc(), and pqResultStrdup(), and updates
the documentation. Per original report from Volkan Yazici about
PQmakeEmptyPGresult() not checking for malloc() failure.
This commit is contained in:
Neil Conway 2005-06-12 00:00:21 +00:00
parent 2f1210629c
commit 72a5db15d1
8 changed files with 206 additions and 117 deletions

View File

@ -1,5 +1,5 @@
<!--
$PostgreSQL: pgsql/doc/src/sgml/libpq.sgml,v 1.184 2005/06/10 03:02:01 momjian Exp $
$PostgreSQL: pgsql/doc/src/sgml/libpq.sgml,v 1.185 2005/06/12 00:00:20 neilc Exp $
-->
<chapter id="libpq">
@ -581,14 +581,15 @@ typedef struct
</para>
<para>
Returns a connection options array. This may
be used to determine all possible <function>PQconnectdb</function> options and their
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.
<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,18 +1652,22 @@ void PQclear(PGresult *res);
<para>
Constructs an empty <structname>PGresult</structname> object with the given status.
<synopsis>
PGresult* PQmakeEmptyPGresult(PGconn *conn, ExecStatusType status);
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.
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,15 +2271,15 @@ unsigned char *PQescapeBytea(const unsigned char *from,
<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.
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>

View File

@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/interfaces/libpq/fe-connect.c,v 1.309 2005/06/10 04:01:36 momjian Exp $
* $PostgreSQL: pgsql/src/interfaces/libpq/fe-connect.c,v 1.310 2005/06/12 00:00:21 neilc Exp $
*
*-------------------------------------------------------------------------
*/
@ -368,6 +368,8 @@ connectOptions1(PGconn *conn, const char *conninfo)
*
* 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,7 +461,6 @@ connectOptions2(PGconn *conn)
}
#ifdef NOT_USED
/*
* parse dbName to get all additional info in it, if any
*/
@ -2167,11 +2168,9 @@ closePGconn(PGconn *conn)
}
/*
PQfinish:
properly close a connection to the backend
also frees the PGconn data structure so it shouldn't be re-used
after this
*/
* 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,10 +2181,10 @@ PQfinish(PGconn *conn)
}
}
/* PQreset :
resets the connection to the backend
closes the existing connection and makes a new one
*/
/*
* PQreset: resets the connection to the backend by closing the
* existing connection and creating a new one.
*/
void
PQreset(PGconn *conn)
{
@ -2199,11 +2198,12 @@ PQreset(PGconn *conn)
}
/* PQresetStart :
resets the connection to the backend
closes the existing connection and makes a new one
Returns 1 on success, 0 on failure.
*/
/*
* 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,11 +2218,11 @@ PQresetStart(PGconn *conn)
}
/* PQresetPoll :
resets the connection to the backend
closes the existing connection and makes a new one
*/
/*
* PQresetPoll:
* resets the connection to the backend
* closes the existing connection and makes a new one
*/
PostgresPollingStatusType
PQresetPoll(PGconn *conn)
{
@ -2514,7 +2514,7 @@ parseServiceInfo(PQconninfoOption *options, PQExpBuffer errorMessage)
* location to find our config files.
*/
snprintf(serviceFile, MAXPGPATH, "%s/pg_service.conf",
getenv("PGSYSCONFDIR") ? getenv("PGSYSCONFDIR") : SYSCONFDIR);
getenv("PGSYSCONFDIR") ? getenv("PGSYSCONFDIR") : SYSCONFDIR);
if (service != NULL)
{
@ -2802,7 +2802,14 @@ conninfo_parse(const char *conninfo, PQExpBuffer errorMessage)
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,6 +2842,13 @@ conninfo_parse(const char *conninfo, PQExpBuffer errorMessage)
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,6 +2860,13 @@ conninfo_parse(const char *conninfo, PQExpBuffer errorMessage)
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;
}

View File

@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/interfaces/libpq/fe-exec.c,v 1.168 2005/06/09 20:01:16 tgl Exp $
* $PostgreSQL: pgsql/src/interfaces/libpq/fe-exec.c,v 1.169 2005/06/12 00:00:21 neilc Exp $
*
*-------------------------------------------------------------------------
*/
@ -134,6 +134,8 @@ PQmakeEmptyPGresult(PGconn *conn, ExecStatusType status)
PGresult *result;
result = (PGresult *) malloc(sizeof(PGresult));
if (!result)
return NULL;
result->ntups = 0;
result->numAttributes = 0;
@ -453,7 +455,7 @@ pqPrepareAsyncResult(PGconn *conn)
* a trailing newline, and should not be more than one line).
*/
void
pqInternalNotice(const PGNoticeHooks *hooks, const char *fmt,...)
pqInternalNotice(const PGNoticeHooks *hooks, const char *fmt, ...)
{
char msgBuf[1024];
va_list args;
@ -470,6 +472,8 @@ pqInternalNotice(const PGNoticeHooks *hooks, const char *fmt,...)
/* Make a PGresult to pass to the notice receiver */
res = PQmakeEmptyPGresult(NULL, PGRES_NONFATAL_ERROR);
if (!res)
return;
res->noticeHooks = *hooks;
/*
@ -480,15 +484,19 @@ pqInternalNotice(const PGNoticeHooks *hooks, const char *fmt,...)
/* XXX should provide a SQLSTATE too? */
/*
* Result text is always just the primary message + newline.
* 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);
sprintf(res->errMsg, "%s\n", msgBuf);
if (res->errMsg)
{
sprintf(res->errMsg, "%s\n", msgBuf);
/*
* Pass to receiver, then free it.
*/
(*res->noticeHooks.noticeRec) (res->noticeHooks.noticeRecArg, res);
/*
* Pass to receiver, then free it.
*/
(*res->noticeHooks.noticeRec) (res->noticeHooks.noticeRecArg, res);
}
PQclear(res);
}
@ -1127,8 +1135,9 @@ PQisBusy(PGconn *conn)
/*
* PQgetResult
* Get the next PGresult produced by a query.
* Returns NULL if and only if no query work remains.
* 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 *

View File

@ -23,7 +23,7 @@
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/interfaces/libpq/fe-misc.c,v 1.113 2005/02/22 04:42:20 momjian Exp $
* $PostgreSQL: pgsql/src/interfaces/libpq/fe-misc.c,v 1.114 2005/06/12 00:00:21 neilc Exp $
*
*-------------------------------------------------------------------------
*/
@ -175,7 +175,8 @@ pqGetnchar(char *s, size_t len, PGconn *conn)
conn->inCursor += len;
if (conn->Pfdebug)
fprintf(conn->Pfdebug, libpq_gettext("From backend (%lu)> %.*s\n"), (unsigned long) len, (int) len, s);
fprintf(conn->Pfdebug, libpq_gettext("From backend (%lu)> %.*s\n"),
(unsigned long) len, (int) len, s);
return 0;
}

View File

@ -10,7 +10,7 @@
* didn't really belong there.
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/interfaces/libpq/fe-print.c,v 1.59 2005/02/22 04:42:20 momjian Exp $
* $PostgreSQL: pgsql/src/interfaces/libpq/fe-print.c,v 1.60 2005/06/12 00:00:21 neilc Exp $
*
*-------------------------------------------------------------------------
*/
@ -62,14 +62,11 @@ static void fill(int length, int max, char filler, FILE *fp);
* details
*
* This function should probably be removed sometime since psql
* doesn't use it anymore. It is unclear to what extend this is used
* 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)
PQprint(FILE *fout, const PGresult *res, const PQprintOpt *po)
{
int nFields;
@ -611,6 +608,12 @@ PQdisplayTuples(const PGresult *res,
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,6 +708,11 @@ PQprintTuples(const PGresult *res,
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';

View File

@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/interfaces/libpq/fe-protocol2.c,v 1.17 2005/05/11 01:26:02 neilc Exp $
* $PostgreSQL: pgsql/src/interfaces/libpq/fe-protocol2.c,v 1.18 2005/06/12 00:00:21 neilc Exp $
*
*-------------------------------------------------------------------------
*/
@ -438,8 +438,12 @@ pqParseInput2(PGconn *conn)
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,19 +576,18 @@ pqParseInput2(PGconn *conn)
static int
getRowDescriptions(PGconn *conn)
{
PGresult *result;
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))
{
PQclear(result);
return EOF;
}
goto failure;
nfields = result->numAttributes;
/* allocate space for the attribute descriptors */
@ -592,6 +595,8 @@ getRowDescriptions(PGconn *conn)
{
result->attDescs = (PGresAttDesc *)
pqResultAlloc(result, nfields * sizeof(PGresAttDesc), TRUE);
if (!result->attDescs)
goto failure;
MemSet(result->attDescs, 0, nfields * sizeof(PGresAttDesc));
}
@ -606,10 +611,7 @@ getRowDescriptions(PGconn *conn)
pqGetInt(&typid, 4, conn) ||
pqGetInt(&typlen, 2, conn) ||
pqGetInt(&atttypmod, 4, conn))
{
PQclear(result);
return EOF;
}
goto failure;
/*
* Since pqGetInt treats 2-byte integers as unsigned, we need to
@ -619,6 +621,8 @@ getRowDescriptions(PGconn *conn)
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,6 +634,11 @@ getRowDescriptions(PGconn *conn)
/* Success! */
conn->result = result;
return 0;
failure:
if (result)
PQclear(result);
return EOF;
}
/*
@ -685,7 +694,11 @@ getAnotherTuple(PGconn *conn, bool binary)
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,13 +771,18 @@ outOfMemory:
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 != std_bitmap)
if (bitmap != NULL && bitmap != std_bitmap)
free(bitmap);
return EOF;
}
@ -780,7 +798,7 @@ EOFexit:
static int
pqGetErrorNotice2(PGconn *conn, bool isError)
{
PGresult *res;
PGresult *res = NULL;
PQExpBufferData workBuf;
char *startp;
char *splitp;
@ -792,10 +810,7 @@ pqGetErrorNotice2(PGconn *conn, bool isError)
*/
initPQExpBuffer(&workBuf);
if (pqGets(&workBuf, conn))
{
termPQExpBuffer(&workBuf);
return EOF;
}
goto failure;
/*
* Make a PGresult to hold the message. We temporarily lie about the
@ -803,8 +818,12 @@ pqGetErrorNotice2(PGconn *conn, bool isError)
* 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,6 +888,12 @@ pqGetErrorNotice2(PGconn *conn, bool isError)
termPQExpBuffer(&workBuf);
return 0;
failure:
if (res)
PQclear(res);
termPQExpBuffer(&workBuf);
return EOF;
}
/*

View File

@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/interfaces/libpq/fe-protocol3.c,v 1.20 2004/12/31 22:03:50 pgsql Exp $
* $PostgreSQL: pgsql/src/interfaces/libpq/fe-protocol3.c,v 1.21 2005/06/12 00:00:21 neilc Exp $
*
*-------------------------------------------------------------------------
*/
@ -51,7 +51,7 @@ static int getParameterStatus(PGconn *conn);
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,
static int build_startup_packet(const PGconn *conn, char *packet,
const PQEnvironmentOption *options);
@ -197,8 +197,12 @@ pqParseInput3(PGconn *conn)
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,8 +219,12 @@ pqParseInput3(PGconn *conn)
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,8 +232,12 @@ pqParseInput3(PGconn *conn)
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,14 +424,13 @@ getRowDescriptions(PGconn *conn)
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))
{
PQclear(result);
return EOF;
}
goto failure;
nfields = result->numAttributes;
/* allocate space for the attribute descriptors */
@ -427,7 +438,9 @@ getRowDescriptions(PGconn *conn)
{
result->attDescs = (PGresAttDesc *)
pqResultAlloc(result, nfields * sizeof(PGresAttDesc), TRUE);
MemSet((char *) result->attDescs, 0, nfields * sizeof(PGresAttDesc));
if (!result->attDescs)
goto failure;
MemSet(result->attDescs, 0, nfields * sizeof(PGresAttDesc));
}
/* result->binary is true only if ALL columns are binary */
@ -451,8 +464,7 @@ getRowDescriptions(PGconn *conn)
pqGetInt(&atttypmod, 4, conn) ||
pqGetInt(&format, 2, conn))
{
PQclear(result);
return EOF;
goto failure;
}
/*
@ -465,6 +477,8 @@ getRowDescriptions(PGconn *conn)
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,6 +493,10 @@ getRowDescriptions(PGconn *conn)
/* Success! */
conn->result = result;
return 0;
failure:
PQclear(result);
return EOF;
}
/*
@ -507,7 +525,7 @@ getAnotherTuple(PGconn *conn, int msgLength)
pqResultAlloc(result, nfields * sizeof(PGresAttValue), TRUE);
if (conn->curTuple == NULL)
goto outOfMemory;
MemSet((char *) conn->curTuple, 0, nfields * sizeof(PGresAttValue));
MemSet(conn->curTuple, 0, nfields * sizeof(PGresAttValue));
}
tup = conn->curTuple;
@ -593,19 +611,11 @@ outOfMemory:
int
pqGetErrorNotice3(PGconn *conn, bool isError)
{
PGresult *res;
PGresult *res = NULL;
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
@ -614,6 +624,16 @@ pqGetErrorNotice3(PGconn *conn, bool isError)
*/
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.
*/
@ -702,6 +722,8 @@ pqGetErrorNotice3(PGconn *conn, bool isError)
if (isError)
{
res->errMsg = pqResultStrdup(res, workBuf.data);
if (!res->errMsg)
goto fail;
pqClearAsyncResult(conn);
conn->result = res;
resetPQExpBuffer(&conn->errorMessage);
@ -825,19 +847,15 @@ getCopyStart(PGconn *conn, ExecStatusType copytype)
int i;
result = PQmakeEmptyPGresult(conn, copytype);
if (!result)
goto failure;
if (pqGetc(&conn->copy_is_binary, conn))
{
PQclear(result);
return EOF;
}
goto failure;
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;
}
goto failure;
nfields = result->numAttributes;
/* allocate space for the attribute descriptors */
@ -845,7 +863,9 @@ getCopyStart(PGconn *conn, ExecStatusType copytype)
{
result->attDescs = (PGresAttDesc *)
pqResultAlloc(result, nfields * sizeof(PGresAttDesc), TRUE);
MemSet((char *) result->attDescs, 0, nfields * sizeof(PGresAttDesc));
if (!result->attDescs)
goto failure;
MemSet(result->attDescs, 0, nfields * sizeof(PGresAttDesc));
}
for (i = 0; i < nfields; i++)
@ -853,23 +873,23 @@ getCopyStart(PGconn *conn, ExecStatusType copytype)
int format;
if (pqGetInt(&format, 2, conn))
{
PQclear(result);
return EOF;
}
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;
}
/*

View File

@ -12,7 +12,7 @@
* Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* $PostgreSQL: pgsql/src/interfaces/libpq/libpq-int.h,v 1.101 2005/06/04 20:42:43 momjian Exp $
* $PostgreSQL: pgsql/src/interfaces/libpq/libpq-int.h,v 1.102 2005/06/12 00:00:21 neilc Exp $
*
*-------------------------------------------------------------------------
*/
@ -403,7 +403,7 @@ extern void pqClearAsyncResult(PGconn *conn);
extern void pqSaveErrorResult(PGconn *conn);
extern PGresult *pqPrepareAsyncResult(PGconn *conn);
extern void
pqInternalNotice(const PGNoticeHooks *hooks, const char *fmt,...)
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);