Commit 6428074e authored by Tom Lane's avatar Tom Lane

Update libpq to store an error message in PGresult, per pgsq-interfaces discussion of 21-Sep.

parent 502769d0
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $Header: /cvsroot/pgsql/src/interfaces/libpq/fe-connect.c,v 1.83 1998/09/20 04:51:10 momjian Exp $ * $Header: /cvsroot/pgsql/src/interfaces/libpq/fe-connect.c,v 1.84 1998/10/01 01:40:19 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -822,8 +822,8 @@ PQsetenv(PGconn *conn) ...@@ -822,8 +822,8 @@ PQsetenv(PGconn *conn)
sprintf(envbuf, "%s=%s", envname, encoding); sprintf(envbuf, "%s=%s", envname, encoding);
putenv(envbuf); putenv(envbuf);
} }
PQclear(rtn);
} }
PQclear(rtn);
if (!encoding) if (!encoding)
{ /* this should not happen */ { /* this should not happen */
sprintf(envbuf, "%s=%s", envname, pg_encoding_to_char(MULTIBYTE)); sprintf(envbuf, "%s=%s", envname, pg_encoding_to_char(MULTIBYTE));
......
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $Header: /cvsroot/pgsql/src/interfaces/libpq/fe-exec.c,v 1.68 1998/09/10 15:18:02 momjian Exp $ * $Header: /cvsroot/pgsql/src/interfaces/libpq/fe-exec.c,v 1.69 1998/10/01 01:40:21 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -50,7 +50,7 @@ const char *const pgresStatus[] = { ...@@ -50,7 +50,7 @@ const char *const pgresStatus[] = {
static void freeTuple(PGresAttValue *tuple, int numAttributes); static void freeTuple(PGresAttValue *tuple, int numAttributes);
static void addTuple(PGresult *res, PGresAttValue *tup); static int addTuple(PGresult *res, PGresAttValue *tup);
static void parseInput(PGconn *conn); static void parseInput(PGconn *conn);
static int getRowDescriptions(PGconn *conn); static int getRowDescriptions(PGconn *conn);
static int getAnotherTuple(PGconn *conn, int binary); static int getAnotherTuple(PGconn *conn, int binary);
...@@ -60,7 +60,9 @@ static int getNotice(PGconn *conn); ...@@ -60,7 +60,9 @@ static int getNotice(PGconn *conn);
/* /*
* PQmakeEmptyPGresult * PQmakeEmptyPGresult
* returns a newly allocated, initialized PGresult with given status * returns a newly allocated, initialized PGresult with given status.
* If conn is not NULL and status indicates an error, the conn's
* errorMessage is copied.
* *
* Note this is exported --- you wouldn't think an application would need * Note this is exported --- you wouldn't think an application would need
* to build its own PGresults, but this has proven useful in both libpgtcl * to build its own PGresults, but this has proven useful in both libpgtcl
...@@ -74,7 +76,7 @@ PQmakeEmptyPGresult(PGconn *conn, ExecStatusType status) ...@@ -74,7 +76,7 @@ PQmakeEmptyPGresult(PGconn *conn, ExecStatusType status)
result = (PGresult *) malloc(sizeof(PGresult)); result = (PGresult *) malloc(sizeof(PGresult));
result->conn = conn; result->conn = conn; /* should go away eventually */
result->ntups = 0; result->ntups = 0;
result->numAttributes = 0; result->numAttributes = 0;
result->attDescs = NULL; result->attDescs = NULL;
...@@ -83,13 +85,45 @@ PQmakeEmptyPGresult(PGconn *conn, ExecStatusType status) ...@@ -83,13 +85,45 @@ PQmakeEmptyPGresult(PGconn *conn, ExecStatusType status)
result->resultStatus = status; result->resultStatus = status;
result->cmdStatus[0] = '\0'; result->cmdStatus[0] = '\0';
result->binary = 0; result->binary = 0;
result->errMsg = NULL;
if (conn) /* consider copying conn's errorMessage */
{
switch (status)
{
case PGRES_EMPTY_QUERY:
case PGRES_COMMAND_OK:
case PGRES_TUPLES_OK:
case PGRES_COPY_OUT:
case PGRES_COPY_IN:
/* non-error cases */
break;
default:
pqSetResultError(result, conn->errorMessage);
break;
}
}
return result; return result;
} }
/*
* pqSetResultError -
* assign a new error message to a PGresult
*/
void
pqSetResultError(PGresult *res, const char *msg)
{
if (!res)
return;
if (res->errMsg)
free(res->errMsg);
res->errMsg = NULL;
if (msg && *msg)
res->errMsg = strdup(msg);
}
/* /*
* PQclear - * PQclear -
* free's the memory associated with a PGresult * free's the memory associated with a PGresult
*
*/ */
void void
PQclear(PGresult *res) PQclear(PGresult *res)
...@@ -118,6 +152,10 @@ PQclear(PGresult *res) ...@@ -118,6 +152,10 @@ PQclear(PGresult *res)
free(res->attDescs); free(res->attDescs);
} }
/* free the error text */
if (res->errMsg)
free(res->errMsg);
/* free the structure itself */ /* free the structure itself */
free(res); free(res);
} }
...@@ -164,27 +202,35 @@ pqClearAsyncResult(PGconn *conn) ...@@ -164,27 +202,35 @@ pqClearAsyncResult(PGconn *conn)
/* /*
* addTuple * addTuple
* add a row to the PGresult structure, growing it if necessary * add a row to the PGresult structure, growing it if necessary
* Returns TRUE if OK, FALSE if not enough memory to add the row
*/ */
static void static int
addTuple(PGresult *res, PGresAttValue *tup) addTuple(PGresult *res, PGresAttValue *tup)
{ {
if (res->ntups >= res->tupArrSize) if (res->ntups >= res->tupArrSize)
{ {
/* grow the array */
res->tupArrSize += TUPARR_GROW_BY;
/* /*
* we can use realloc because shallow copying of the structure is * Try to grow the array.
*
* We can use realloc because shallow copying of the structure is
* okay. Note that the first time through, res->tuples is NULL. * okay. Note that the first time through, res->tuples is NULL.
* realloc is supposed to do the right thing in that case. Also * realloc is supposed to do the right thing in that case. Also,
* note that the positions beyond res->ntups are garbage, not * on failure realloc is supposed to return NULL without damaging
* the existing allocation.
* Note that the positions beyond res->ntups are garbage, not
* necessarily NULL. * necessarily NULL.
*/ */
res->tuples = (PGresAttValue **) int newSize = res->tupArrSize + TUPARR_GROW_BY;
realloc(res->tuples, res->tupArrSize * sizeof(PGresAttValue *)); PGresAttValue ** newTuples = (PGresAttValue **)
realloc(res->tuples, newSize * sizeof(PGresAttValue *));
if (! newTuples)
return FALSE; /* realloc failed */
res->tupArrSize = newSize;
res->tuples = newTuples;
} }
res->tuples[res->ntups] = tup; res->tuples[res->ntups] = tup;
res->ntups++; res->ntups++;
return TRUE;
} }
...@@ -235,7 +281,6 @@ PQsendQuery(PGconn *conn, const char *query) ...@@ -235,7 +281,6 @@ PQsendQuery(PGconn *conn, const char *query)
/* initialize async result-accumulation state */ /* initialize async result-accumulation state */
conn->result = NULL; conn->result = NULL;
conn->curTuple = NULL; conn->curTuple = NULL;
conn->asyncErrorMessage[0] = '\0';
/* send the query to the backend; */ /* send the query to the backend; */
/* the frontend-backend protocol uses 'Q' to designate queries */ /* the frontend-backend protocol uses 'Q' to designate queries */
...@@ -270,10 +315,8 @@ PQconsumeInput(PGconn *conn) ...@@ -270,10 +315,8 @@ PQconsumeInput(PGconn *conn)
* application wants to get rid of a read-select condition. Note that * application wants to get rid of a read-select condition. Note that
* we will NOT block waiting for more input. * we will NOT block waiting for more input.
*/ */
if (pqReadData(conn) < 0) { if (pqReadData(conn) < 0)
strcpy(conn->asyncErrorMessage, conn->errorMessage);
return 0; return 0;
}
/* Parsing of the data waits till later. */ /* Parsing of the data waits till later. */
return 1; return 1;
} }
...@@ -360,16 +403,13 @@ parseInput(PGconn *conn) ...@@ -360,16 +403,13 @@ parseInput(PGconn *conn)
conn->asyncStatus = PGASYNC_READY; conn->asyncStatus = PGASYNC_READY;
break; break;
case 'E': /* error return */ case 'E': /* error return */
if (pqGets(conn->asyncErrorMessage, ERROR_MSG_LENGTH, conn)) if (pqGets(conn->errorMessage, ERROR_MSG_LENGTH, conn))
return; return;
/* delete any partially constructed result */ /* delete any partially constructed result */
pqClearAsyncResult(conn); pqClearAsyncResult(conn);
/* and build an error result holding the error message */
/* conn->result = PQmakeEmptyPGresult(conn,
* we leave result NULL while setting PGRES_FATAL_ERROR);
* asyncStatus=READY; this signals an error condition
* to PQgetResult.
*/
conn->asyncStatus = PGASYNC_READY; conn->asyncStatus = PGASYNC_READY;
break; break;
case 'Z': /* backend is ready for new query */ case 'Z': /* backend is ready for new query */
...@@ -470,15 +510,18 @@ parseInput(PGconn *conn) ...@@ -470,15 +510,18 @@ parseInput(PGconn *conn)
conn->asyncStatus = PGASYNC_COPY_OUT; conn->asyncStatus = PGASYNC_COPY_OUT;
break; break;
default: default:
sprintf(conn->asyncErrorMessage, sprintf(conn->errorMessage,
"unknown protocol character '%c' read from backend. " "unknown protocol character '%c' read from backend. "
"(The protocol character is the first character the " "(The protocol character is the first character the "
"backend sends in response to a query it receives).\n", "backend sends in response to a query it receives).\n",
id); id);
/* Discard the unexpected message; good idea?? */ /* Discard the unexpected message; good idea?? */
conn->inStart = conn->inEnd; conn->inStart = conn->inEnd;
/* delete any partially constructed result */ /* delete any partially constructed result */
pqClearAsyncResult(conn); pqClearAsyncResult(conn);
/* and build an error result holding the error message */
conn->result = PQmakeEmptyPGresult(conn,
PGRES_FATAL_ERROR);
conn->asyncStatus = PGASYNC_READY; conn->asyncStatus = PGASYNC_READY;
return; return;
} /* switch on protocol character */ } /* switch on protocol character */
...@@ -565,7 +608,7 @@ getRowDescriptions(PGconn *conn) ...@@ -565,7 +608,7 @@ getRowDescriptions(PGconn *conn)
/* /*
* parseInput subroutine to read a 'B' or 'D' (row data) message. * parseInput subroutine to read a 'B' or 'D' (row data) message.
* We add another tuple to the existing PGresult structure. * We add another tuple to the existing PGresult structure.
* Returns: 0 if completed message, EOF if not enough data yet. * Returns: 0 if completed message, EOF if error or not enough data yet.
* *
* Note that if we run out of data, we have to suspend and reprocess * Note that if we run out of data, we have to suspend and reprocess
* the message after more data is received. We keep a partially constructed * the message after more data is received. We keep a partially constructed
...@@ -593,6 +636,8 @@ getAnotherTuple(PGconn *conn, int binary) ...@@ -593,6 +636,8 @@ getAnotherTuple(PGconn *conn, int binary)
{ {
conn->curTuple = (PGresAttValue *) conn->curTuple = (PGresAttValue *)
malloc(nfields * sizeof(PGresAttValue)); malloc(nfields * sizeof(PGresAttValue));
if (conn->curTuple == NULL)
goto outOfMemory;
MemSet((char *) conn->curTuple, 0, nfields * sizeof(PGresAttValue)); MemSet((char *) conn->curTuple, 0, nfields * sizeof(PGresAttValue));
} }
tup = conn->curTuple; tup = conn->curTuple;
...@@ -601,9 +646,11 @@ getAnotherTuple(PGconn *conn, int binary) ...@@ -601,9 +646,11 @@ getAnotherTuple(PGconn *conn, int binary)
nbytes = (nfields + BYTELEN - 1) / BYTELEN; nbytes = (nfields + BYTELEN - 1) / BYTELEN;
if (nbytes >= MAX_FIELDS) if (nbytes >= MAX_FIELDS)
{ {
sprintf(conn->asyncErrorMessage, /* Replace partially constructed result with an error result */
"getAnotherTuple() -- null-values bitmap is too large\n");
pqClearAsyncResult(conn); pqClearAsyncResult(conn);
sprintf(conn->errorMessage,
"getAnotherTuple() -- null-values bitmap is too large\n");
conn->result = PQmakeEmptyPGresult(conn, PGRES_FATAL_ERROR);
conn->asyncStatus = PGASYNC_READY; conn->asyncStatus = PGASYNC_READY;
/* Discard the broken message */ /* Discard the broken message */
conn->inStart = conn->inEnd; conn->inStart = conn->inEnd;
...@@ -624,7 +671,11 @@ getAnotherTuple(PGconn *conn, int binary) ...@@ -624,7 +671,11 @@ getAnotherTuple(PGconn *conn, int binary)
{ {
/* if the field value is absent, make it a null string */ /* if the field value is absent, make it a null string */
if (tup[i].value == NULL) if (tup[i].value == NULL)
{
tup[i].value = strdup(""); tup[i].value = strdup("");
if (tup[i].value == NULL)
goto outOfMemory;
}
tup[i].len = NULL_LEN; tup[i].len = NULL_LEN;
} }
else else
...@@ -637,7 +688,11 @@ getAnotherTuple(PGconn *conn, int binary) ...@@ -637,7 +688,11 @@ getAnotherTuple(PGconn *conn, int binary)
if (vlen < 0) if (vlen < 0)
vlen = 0; vlen = 0;
if (tup[i].value == NULL) if (tup[i].value == NULL)
{
tup[i].value = (char *) malloc(vlen + 1); tup[i].value = (char *) malloc(vlen + 1);
if (tup[i].value == NULL)
goto outOfMemory;
}
tup[i].len = vlen; tup[i].len = vlen;
/* read in the value */ /* read in the value */
if (vlen > 0) if (vlen > 0)
...@@ -659,10 +714,28 @@ getAnotherTuple(PGconn *conn, int binary) ...@@ -659,10 +714,28 @@ getAnotherTuple(PGconn *conn, int binary)
} }
/* Success! Store the completed tuple in the result */ /* Success! Store the completed tuple in the result */
addTuple(conn->result, tup); if (! addTuple(conn->result, tup))
{
/* Oops, not enough memory to add the tuple to conn->result,
* so must free it ourselves...
*/
freeTuple(tup, nfields);
goto outOfMemory;
}
/* and reset for a new message */ /* and reset for a new message */
conn->curTuple = NULL; conn->curTuple = NULL;
return 0; return 0;
outOfMemory:
/* Replace partially constructed result with an error result */
pqClearAsyncResult(conn);
sprintf(conn->errorMessage,
"getAnotherTuple() -- out of memory for result\n");
conn->result = PQmakeEmptyPGresult(conn, PGRES_FATAL_ERROR);
conn->asyncStatus = PGASYNC_READY;
/* Discard the failed message --- good idea? */
conn->inStart = conn->inEnd;
return EOF;
} }
...@@ -725,19 +798,26 @@ PQgetResult(PGconn *conn) ...@@ -725,19 +798,26 @@ PQgetResult(PGconn *conn)
res = NULL; /* query is complete */ res = NULL; /* query is complete */
break; break;
case PGASYNC_READY: case PGASYNC_READY:
/* /*
* conn->result is the PGresult to return, or possibly NULL * conn->result is the PGresult to return. If it is NULL
* indicating an error. conn->asyncErrorMessage holds the * (which probably shouldn't happen) we assume there is
* errorMessage to return. (We keep it stashed there so that * an appropriate error message in conn->errorMessage.
* other user calls can't overwrite it prematurely.)
*/ */
res = conn->result; res = conn->result;
conn->result = NULL;/* handing over ownership to caller */ conn->result = NULL; /* handing over ownership to caller */
conn->curTuple = NULL; /* just in case */ conn->curTuple = NULL; /* just in case */
if (!res) if (!res)
{
res = PQmakeEmptyPGresult(conn, PGRES_FATAL_ERROR); res = PQmakeEmptyPGresult(conn, PGRES_FATAL_ERROR);
strcpy(conn->errorMessage, conn->asyncErrorMessage); }
else
{
/* Make sure PQerrorMessage agrees with result; it could be
* that we have done other operations that changed
* errorMessage since the result's error message was saved.
*/
strcpy(conn->errorMessage, PQresultErrorMessage(res));
}
/* Set the state back to BUSY, allowing parsing to proceed. */ /* Set the state back to BUSY, allowing parsing to proceed. */
conn->asyncStatus = PGASYNC_BUSY; conn->asyncStatus = PGASYNC_BUSY;
break; break;
...@@ -763,11 +843,12 @@ PQgetResult(PGconn *conn) ...@@ -763,11 +843,12 @@ PQgetResult(PGconn *conn)
* PQexec * PQexec
* send a query to the backend and package up the result in a PGresult * send a query to the backend and package up the result in a PGresult
* *
* if the query failed, return NULL, conn->errorMessage is set to * If the query was not even sent, return NULL; conn->errorMessage is set to
* a relevant message * a relevant message.
* if query is successful, a new PGresult is returned * If the query was sent, a new PGresult is returned (which could indicate
* the user is responsible for freeing that structure when done with it * either success or failure).
* * The user is responsible for freeing the PGresult via PQclear()
* when done with it.
*/ */
PGresult * PGresult *
...@@ -1312,6 +1393,14 @@ PQresultStatus(PGresult *res) ...@@ -1312,6 +1393,14 @@ PQresultStatus(PGresult *res)
return res->resultStatus; return res->resultStatus;
} }
const char *
PQresultErrorMessage(PGresult *res)
{
if (!res || !res->errMsg)
return "";
return res->errMsg;
}
int int
PQntuples(PGresult *res) PQntuples(PGresult *res)
{ {
......
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $Header: /cvsroot/pgsql/src/interfaces/libpq/fe-lobj.c,v 1.16 1998/09/01 04:40:07 momjian Exp $ * $Header: /cvsroot/pgsql/src/interfaces/libpq/fe-lobj.c,v 1.17 1998/10/01 01:40:22 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -76,7 +76,10 @@ lo_open(PGconn *conn, Oid lobjId, int mode) ...@@ -76,7 +76,10 @@ lo_open(PGconn *conn, Oid lobjId, int mode)
return fd; return fd;
} }
else else
{
PQclear(res);
return -1; return -1;
}
} }
/* /*
...@@ -111,7 +114,10 @@ lo_close(PGconn *conn, int fd) ...@@ -111,7 +114,10 @@ lo_close(PGconn *conn, int fd)
return retval; return retval;
} }
else else
{
PQclear(res);
return -1; return -1;
}
} }
/* /*
...@@ -151,7 +157,10 @@ lo_read(PGconn *conn, int fd, char *buf, int len) ...@@ -151,7 +157,10 @@ lo_read(PGconn *conn, int fd, char *buf, int len)
return result_len; return result_len;
} }
else else
{
PQclear(res);
return -1; return -1;
}
} }
/* /*
...@@ -192,7 +201,10 @@ lo_write(PGconn *conn, int fd, char *buf, int len) ...@@ -192,7 +201,10 @@ lo_write(PGconn *conn, int fd, char *buf, int len)
return retval; return retval;
} }
else else
{
PQclear(res);
return -1; return -1;
}
} }
/* /*
...@@ -236,7 +248,10 @@ lo_lseek(PGconn *conn, int fd, int offset, int whence) ...@@ -236,7 +248,10 @@ lo_lseek(PGconn *conn, int fd, int offset, int whence)
return retval; return retval;
} }
else else
{
PQclear(res);
return -1; return -1;
}
} }
/* /*
...@@ -273,7 +288,10 @@ lo_creat(PGconn *conn, int mode) ...@@ -273,7 +288,10 @@ lo_creat(PGconn *conn, int mode)
return (Oid) retval; return (Oid) retval;
} }
else else
{
PQclear(res);
return InvalidOid; return InvalidOid;
}
} }
...@@ -309,7 +327,10 @@ lo_tell(PGconn *conn, int fd) ...@@ -309,7 +327,10 @@ lo_tell(PGconn *conn, int fd)
return retval; return retval;
} }
else else
{
PQclear(res);
return -1; return -1;
}
} }
/* /*
...@@ -344,7 +365,10 @@ lo_unlink(PGconn *conn, Oid lobjId) ...@@ -344,7 +365,10 @@ lo_unlink(PGconn *conn, Oid lobjId)
return retval; return retval;
} }
else else
{
PQclear(res);
return -1; return -1;
}
} }
/* /*
......
...@@ -6,7 +6,7 @@ ...@@ -6,7 +6,7 @@
* *
* Copyright (c) 1994, Regents of the University of California * Copyright (c) 1994, Regents of the University of California
* *
* $Id: libpq-fe.h,v 1.43 1998/09/18 16:46:06 momjian Exp $ * $Id: libpq-fe.h,v 1.44 1998/10/01 01:40:23 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -67,6 +67,8 @@ extern "C" ...@@ -67,6 +67,8 @@ extern "C"
/* PGnotify represents the occurrence of a NOTIFY message. /* PGnotify represents the occurrence of a NOTIFY message.
* Ideally this would be an opaque typedef, but it's so simple that it's * Ideally this would be an opaque typedef, but it's so simple that it's
* unlikely to change. * unlikely to change.
* NOTE: in Postgres 6.4 and later, the be_pid is the notifying backend's,
* whereas in earlier versions it was always your own backend's PID.
*/ */
typedef struct pgNotify typedef struct pgNotify
{ {
...@@ -78,7 +80,7 @@ extern "C" ...@@ -78,7 +80,7 @@ extern "C"
/* PQnoticeProcessor is the function type for the notice-message callback. /* PQnoticeProcessor is the function type for the notice-message callback.
*/ */
typedef void (*PQnoticeProcessor) (void * arg, const char * message); typedef void (*PQnoticeProcessor) (void * arg, const char * message);
/* Print options for PQprint() */ /* Print options for PQprint() */
...@@ -219,15 +221,16 @@ typedef void (*PQnoticeProcessor) (void * arg, const char * message); ...@@ -219,15 +221,16 @@ typedef void (*PQnoticeProcessor) (void * arg, const char * message);
* use * use
*/ */
extern PGresult *PQfn(PGconn *conn, extern PGresult *PQfn(PGconn *conn,
int fnid, int fnid,
int *result_buf, int *result_buf,
int *result_len, int *result_len,
int result_is_int, int result_is_int,
PQArgBlock *args, PQArgBlock *args,
int nargs); int nargs);
/* Accessor functions for PGresult objects */ /* Accessor functions for PGresult objects */
extern ExecStatusType PQresultStatus(PGresult *res); extern ExecStatusType PQresultStatus(PGresult *res);
extern const char *PQresultErrorMessage(PGresult *res);
extern int PQntuples(PGresult *res); extern int PQntuples(PGresult *res);
extern int PQnfields(PGresult *res); extern int PQnfields(PGresult *res);
extern int PQbinaryTuples(PGresult *res); extern int PQbinaryTuples(PGresult *res);
...@@ -246,35 +249,39 @@ typedef void (*PQnoticeProcessor) (void * arg, const char * message); ...@@ -246,35 +249,39 @@ typedef void (*PQnoticeProcessor) (void * arg, const char * message);
/* Delete a PGresult */ /* Delete a PGresult */
extern void PQclear(PGresult *res); extern void PQclear(PGresult *res);
/* Make an empty PGresult with given status (some apps find this useful) */ /* Make an empty PGresult with given status (some apps find this useful).
* If conn is not NULL and status indicates an error, the conn's
* errorMessage is copied.
*/
extern PGresult * PQmakeEmptyPGresult(PGconn *conn, ExecStatusType status); extern PGresult * PQmakeEmptyPGresult(PGconn *conn, ExecStatusType status);
/* === in fe-print.c === */ /* === in fe-print.c === */
extern void PQprint(FILE *fout, /* output stream */ extern void PQprint(FILE *fout, /* output stream */
PGresult *res, PGresult *res,
PQprintOpt *ps); /* option structure */ PQprintOpt *ps); /* option structure */
/* /*
* PQdisplayTuples() is a better version of PQprintTuples(), but both * PQdisplayTuples() is a better version of PQprintTuples(), but both
* are obsoleted by PQprint(). * are obsoleted by PQprint().
*/ */
extern void PQdisplayTuples(PGresult *res, extern void PQdisplayTuples(PGresult *res,
FILE *fp, /* where to send the FILE *fp, /* where to send the
* output */ * output */
int fillAlign, /* pad the fields with int fillAlign, /* pad the fields with
* spaces */ * spaces */
const char *fieldSep, /* field separator */ const char *fieldSep, /* field separator */
int printHeader, /* display headers? */ int printHeader, /* display headers? */
int quiet); int quiet);
extern void PQprintTuples(PGresult *res, extern void PQprintTuples(PGresult *res,
FILE *fout, /* output stream */ FILE *fout, /* output stream */
int printAttName, /* print attribute names int printAttName, /* print attribute names
* or not */ * or not */
int terseOutput, /* delimiter bars or int terseOutput, /* delimiter bars or
* not? */ * not? */
int width); /* width of column, if int width); /* width of column, if
* 0, use variable width */ * 0, use variable width */
#ifdef MULTIBYTE #ifdef MULTIBYTE
extern int PQmblen(unsigned char *s); extern int PQmblen(unsigned char *s);
......
...@@ -11,7 +11,7 @@ ...@@ -11,7 +11,7 @@
* *
* Copyright (c) 1994, Regents of the University of California * Copyright (c) 1994, Regents of the University of California
* *
* $Id: libpq-int.h,v 1.3 1998/09/03 02:10:53 momjian Exp $ * $Id: libpq-int.h,v 1.4 1998/10/01 01:40:25 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -78,7 +78,7 @@ ...@@ -78,7 +78,7 @@
char *value; /* actual value */ char *value; /* actual value */
} PGresAttValue; } PGresAttValue;
struct pg_result struct pg_result
{ {
int ntups; int ntups;
int numAttributes; int numAttributes;
...@@ -91,7 +91,15 @@ ...@@ -91,7 +91,15 @@
* last insert query */ * last insert query */
int binary; /* binary tuple values if binary == 1, int binary; /* binary tuple values if binary == 1,
* otherwise ASCII */ * otherwise ASCII */
/* NOTE: conn is kept here only for the temporary convenience of
* applications that rely on it being here. It will go away in a
* future release, because relying on it is a bad idea --- what if
* the PGresult has outlived the PGconn? About the only thing it was
* really good for was fetching the errorMessage, and we stash that
* here now anyway.
*/
PGconn *conn; /* connection we did the query on */ PGconn *conn; /* connection we did the query on */
char *errMsg; /* error message, or NULL if no error */
}; };
/* PGAsyncStatusType defines the state of the query-execution state machine */ /* PGAsyncStatusType defines the state of the query-execution state machine */
...@@ -174,12 +182,8 @@ ...@@ -174,12 +182,8 @@
PGresult *result; /* result being constructed */ PGresult *result; /* result being constructed */
PGresAttValue *curTuple; /* tuple currently being read */ PGresAttValue *curTuple; /* tuple currently being read */
/* Message space. Placed last for code-size reasons. /* Message space. Placed last for code-size reasons. */
* errorMessage is the message last returned to the application.
* When asyncStatus=READY, asyncErrorMessage is the pending message
* that will be put in errorMessage by PQgetResult. */
char errorMessage[ERROR_MSG_LENGTH]; char errorMessage[ERROR_MSG_LENGTH];
char asyncErrorMessage[ERROR_MSG_LENGTH];
}; };
/* ---------------- /* ----------------
...@@ -197,6 +201,7 @@ extern int pqPacketSend(PGconn *conn, const char *buf, size_t len); ...@@ -197,6 +201,7 @@ extern int pqPacketSend(PGconn *conn, const char *buf, size_t len);
/* === in fe-exec.c === */ /* === in fe-exec.c === */
extern void pqSetResultError(PGresult *res, const char *msg);
extern void pqClearAsyncResult(PGconn *conn); extern void pqClearAsyncResult(PGconn *conn);
/* === in fe-misc.c === */ /* === in fe-misc.c === */
......
...@@ -63,3 +63,4 @@ EXPORTS ...@@ -63,3 +63,4 @@ EXPORTS
lo_unlink @ 60 lo_unlink @ 60
lo_import @ 61 lo_import @ 61
lo_export @ 62 lo_export @ 62
PQresultErrorMessage @ 63
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment