aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/interfaces/libpq/fe-connect.c4
-rw-r--r--src/interfaces/libpq/fe-exec.c175
-rw-r--r--src/interfaces/libpq/fe-lobj.c26
-rw-r--r--src/interfaces/libpq/libpq-fe.h59
-rw-r--r--src/interfaces/libpq/libpq-int.h19
-rw-r--r--src/interfaces/libpq/libpqdll.def1
6 files changed, 205 insertions, 79 deletions
diff --git a/src/interfaces/libpq/fe-connect.c b/src/interfaces/libpq/fe-connect.c
index e21d17f23f7..3a2b16c22d9 100644
--- a/src/interfaces/libpq/fe-connect.c
+++ b/src/interfaces/libpq/fe-connect.c
@@ -7,7 +7,7 @@
*
*
* 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)
sprintf(envbuf, "%s=%s", envname, encoding);
putenv(envbuf);
}
- PQclear(rtn);
}
+ PQclear(rtn);
if (!encoding)
{ /* this should not happen */
sprintf(envbuf, "%s=%s", envname, pg_encoding_to_char(MULTIBYTE));
diff --git a/src/interfaces/libpq/fe-exec.c b/src/interfaces/libpq/fe-exec.c
index 5f889414e19..82c697ef05b 100644
--- a/src/interfaces/libpq/fe-exec.c
+++ b/src/interfaces/libpq/fe-exec.c
@@ -7,7 +7,7 @@
*
*
* 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[] = {
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 int getRowDescriptions(PGconn *conn);
static int getAnotherTuple(PGconn *conn, int binary);
@@ -60,7 +60,9 @@ static int getNotice(PGconn *conn);
/*
* 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
* to build its own PGresults, but this has proven useful in both libpgtcl
@@ -74,7 +76,7 @@ PQmakeEmptyPGresult(PGconn *conn, ExecStatusType status)
result = (PGresult *) malloc(sizeof(PGresult));
- result->conn = conn;
+ result->conn = conn; /* should go away eventually */
result->ntups = 0;
result->numAttributes = 0;
result->attDescs = NULL;
@@ -83,13 +85,45 @@ PQmakeEmptyPGresult(PGconn *conn, ExecStatusType status)
result->resultStatus = status;
result->cmdStatus[0] = '\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;
}
/*
+ * 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 -
* free's the memory associated with a PGresult
- *
*/
void
PQclear(PGresult *res)
@@ -118,6 +152,10 @@ PQclear(PGresult *res)
free(res->attDescs);
}
+ /* free the error text */
+ if (res->errMsg)
+ free(res->errMsg);
+
/* free the structure itself */
free(res);
}
@@ -164,27 +202,35 @@ pqClearAsyncResult(PGconn *conn)
/*
* addTuple
* 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)
{
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.
- * realloc is supposed to do the right thing in that case. Also
- * note that the positions beyond res->ntups are garbage, not
+ * realloc is supposed to do the right thing in that case. Also,
+ * 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.
*/
- res->tuples = (PGresAttValue **)
- realloc(res->tuples, res->tupArrSize * sizeof(PGresAttValue *));
+ int newSize = res->tupArrSize + TUPARR_GROW_BY;
+ 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->ntups++;
+ return TRUE;
}
@@ -235,7 +281,6 @@ PQsendQuery(PGconn *conn, const char *query)
/* initialize async result-accumulation state */
conn->result = NULL;
conn->curTuple = NULL;
- conn->asyncErrorMessage[0] = '\0';
/* send the query to the backend; */
/* the frontend-backend protocol uses 'Q' to designate queries */
@@ -270,10 +315,8 @@ PQconsumeInput(PGconn *conn)
* application wants to get rid of a read-select condition. Note that
* we will NOT block waiting for more input.
*/
- if (pqReadData(conn) < 0) {
- strcpy(conn->asyncErrorMessage, conn->errorMessage);
+ if (pqReadData(conn) < 0)
return 0;
- }
/* Parsing of the data waits till later. */
return 1;
}
@@ -360,16 +403,13 @@ parseInput(PGconn *conn)
conn->asyncStatus = PGASYNC_READY;
break;
case 'E': /* error return */
- if (pqGets(conn->asyncErrorMessage, ERROR_MSG_LENGTH, conn))
+ if (pqGets(conn->errorMessage, ERROR_MSG_LENGTH, conn))
return;
/* delete any partially constructed result */
pqClearAsyncResult(conn);
-
- /*
- * we leave result NULL while setting
- * asyncStatus=READY; this signals an error condition
- * to PQgetResult.
- */
+ /* and build an error result holding the error message */
+ conn->result = PQmakeEmptyPGresult(conn,
+ PGRES_FATAL_ERROR);
conn->asyncStatus = PGASYNC_READY;
break;
case 'Z': /* backend is ready for new query */
@@ -470,15 +510,18 @@ parseInput(PGconn *conn)
conn->asyncStatus = PGASYNC_COPY_OUT;
break;
default:
- sprintf(conn->asyncErrorMessage,
+ sprintf(conn->errorMessage,
"unknown protocol character '%c' read from backend. "
"(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);
/* Discard the unexpected message; good idea?? */
conn->inStart = conn->inEnd;
/* delete any partially constructed result */
pqClearAsyncResult(conn);
+ /* and build an error result holding the error message */
+ conn->result = PQmakeEmptyPGresult(conn,
+ PGRES_FATAL_ERROR);
conn->asyncStatus = PGASYNC_READY;
return;
} /* switch on protocol character */
@@ -565,7 +608,7 @@ getRowDescriptions(PGconn *conn)
/*
* parseInput subroutine to read a 'B' or 'D' (row data) message.
* 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
* the message after more data is received. We keep a partially constructed
@@ -593,6 +636,8 @@ getAnotherTuple(PGconn *conn, int binary)
{
conn->curTuple = (PGresAttValue *)
malloc(nfields * sizeof(PGresAttValue));
+ if (conn->curTuple == NULL)
+ goto outOfMemory;
MemSet((char *) conn->curTuple, 0, nfields * sizeof(PGresAttValue));
}
tup = conn->curTuple;
@@ -601,9 +646,11 @@ getAnotherTuple(PGconn *conn, int binary)
nbytes = (nfields + BYTELEN - 1) / BYTELEN;
if (nbytes >= MAX_FIELDS)
{
- sprintf(conn->asyncErrorMessage,
- "getAnotherTuple() -- null-values bitmap is too large\n");
+ /* Replace partially constructed result with an error result */
pqClearAsyncResult(conn);
+ sprintf(conn->errorMessage,
+ "getAnotherTuple() -- null-values bitmap is too large\n");
+ conn->result = PQmakeEmptyPGresult(conn, PGRES_FATAL_ERROR);
conn->asyncStatus = PGASYNC_READY;
/* Discard the broken message */
conn->inStart = conn->inEnd;
@@ -624,7 +671,11 @@ getAnotherTuple(PGconn *conn, int binary)
{
/* if the field value is absent, make it a null string */
if (tup[i].value == NULL)
+ {
tup[i].value = strdup("");
+ if (tup[i].value == NULL)
+ goto outOfMemory;
+ }
tup[i].len = NULL_LEN;
}
else
@@ -637,7 +688,11 @@ getAnotherTuple(PGconn *conn, int binary)
if (vlen < 0)
vlen = 0;
if (tup[i].value == NULL)
+ {
tup[i].value = (char *) malloc(vlen + 1);
+ if (tup[i].value == NULL)
+ goto outOfMemory;
+ }
tup[i].len = vlen;
/* read in the value */
if (vlen > 0)
@@ -659,10 +714,28 @@ getAnotherTuple(PGconn *conn, int binary)
}
/* 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 */
conn->curTuple = NULL;
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)
res = NULL; /* query is complete */
break;
case PGASYNC_READY:
-
/*
- * conn->result is the PGresult to return, or possibly NULL
- * indicating an error. conn->asyncErrorMessage holds the
- * errorMessage to return. (We keep it stashed there so that
- * other user calls can't overwrite it prematurely.)
+ * conn->result is the PGresult to return. If it is NULL
+ * (which probably shouldn't happen) we assume there is
+ * an appropriate error message in conn->errorMessage.
*/
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 */
if (!res)
+ {
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. */
conn->asyncStatus = PGASYNC_BUSY;
break;
@@ -763,11 +843,12 @@ PQgetResult(PGconn *conn)
* PQexec
* 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
- * a relevant message
- * if query is successful, a new PGresult is returned
- * the user is responsible for freeing that structure when done with it
- *
+ * If the query was not even sent, return NULL; conn->errorMessage is set to
+ * a relevant message.
+ * If the query was sent, a new PGresult is returned (which could indicate
+ * either success or failure).
+ * The user is responsible for freeing the PGresult via PQclear()
+ * when done with it.
*/
PGresult *
@@ -1312,6 +1393,14 @@ PQresultStatus(PGresult *res)
return res->resultStatus;
}
+const char *
+PQresultErrorMessage(PGresult *res)
+{
+ if (!res || !res->errMsg)
+ return "";
+ return res->errMsg;
+}
+
int
PQntuples(PGresult *res)
{
diff --git a/src/interfaces/libpq/fe-lobj.c b/src/interfaces/libpq/fe-lobj.c
index 190cab18106..19c7770f3f1 100644
--- a/src/interfaces/libpq/fe-lobj.c
+++ b/src/interfaces/libpq/fe-lobj.c
@@ -7,7 +7,7 @@
*
*
* 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)
return fd;
}
else
+ {
+ PQclear(res);
return -1;
+ }
}
/*
@@ -111,7 +114,10 @@ lo_close(PGconn *conn, int fd)
return retval;
}
else
+ {
+ PQclear(res);
return -1;
+ }
}
/*
@@ -151,7 +157,10 @@ lo_read(PGconn *conn, int fd, char *buf, int len)
return result_len;
}
else
+ {
+ PQclear(res);
return -1;
+ }
}
/*
@@ -192,7 +201,10 @@ lo_write(PGconn *conn, int fd, char *buf, int len)
return retval;
}
else
+ {
+ PQclear(res);
return -1;
+ }
}
/*
@@ -236,7 +248,10 @@ lo_lseek(PGconn *conn, int fd, int offset, int whence)
return retval;
}
else
+ {
+ PQclear(res);
return -1;
+ }
}
/*
@@ -273,7 +288,10 @@ lo_creat(PGconn *conn, int mode)
return (Oid) retval;
}
else
+ {
+ PQclear(res);
return InvalidOid;
+ }
}
@@ -309,7 +327,10 @@ lo_tell(PGconn *conn, int fd)
return retval;
}
else
+ {
+ PQclear(res);
return -1;
+ }
}
/*
@@ -344,7 +365,10 @@ lo_unlink(PGconn *conn, Oid lobjId)
return retval;
}
else
+ {
+ PQclear(res);
return -1;
+ }
}
/*
diff --git a/src/interfaces/libpq/libpq-fe.h b/src/interfaces/libpq/libpq-fe.h
index 8f090352635..13ce50b873d 100644
--- a/src/interfaces/libpq/libpq-fe.h
+++ b/src/interfaces/libpq/libpq-fe.h
@@ -6,7 +6,7 @@
*
* 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"
/* PGnotify represents the occurrence of a NOTIFY message.
* Ideally this would be an opaque typedef, but it's so simple that it's
* 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
{
@@ -78,7 +80,7 @@ extern "C"
/* 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() */
@@ -219,15 +221,16 @@ typedef void (*PQnoticeProcessor) (void * arg, const char * message);
* use
*/
extern PGresult *PQfn(PGconn *conn,
- int fnid,
- int *result_buf,
- int *result_len,
- int result_is_int,
- PQArgBlock *args,
- int nargs);
+ int fnid,
+ int *result_buf,
+ int *result_len,
+ int result_is_int,
+ PQArgBlock *args,
+ int nargs);
/* Accessor functions for PGresult objects */
extern ExecStatusType PQresultStatus(PGresult *res);
+ extern const char *PQresultErrorMessage(PGresult *res);
extern int PQntuples(PGresult *res);
extern int PQnfields(PGresult *res);
extern int PQbinaryTuples(PGresult *res);
@@ -246,35 +249,39 @@ typedef void (*PQnoticeProcessor) (void * arg, const char * message);
/* Delete a PGresult */
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);
/* === in fe-print.c === */
- extern void PQprint(FILE *fout, /* output stream */
- PGresult *res,
- PQprintOpt *ps); /* option structure */
+ extern void PQprint(FILE *fout, /* output stream */
+ PGresult *res,
+ PQprintOpt *ps); /* option structure */
/*
* PQdisplayTuples() is a better version of PQprintTuples(), but both
* are obsoleted by PQprint().
*/
extern void PQdisplayTuples(PGresult *res,
- FILE *fp, /* where to send the
- * output */
- int fillAlign, /* pad the fields with
- * spaces */
- const char *fieldSep, /* field separator */
- int printHeader, /* display headers? */
- int quiet);
+ FILE *fp, /* where to send the
+ * output */
+ int fillAlign, /* pad the fields with
+ * spaces */
+ const char *fieldSep, /* field separator */
+ int printHeader, /* display headers? */
+ int quiet);
+
extern void PQprintTuples(PGresult *res,
- FILE *fout, /* output stream */
- int printAttName, /* print attribute names
- * or not */
- int terseOutput, /* delimiter bars or
- * not? */
- int width); /* width of column, if
- * 0, use variable width */
+ FILE *fout, /* output stream */
+ int printAttName, /* print attribute names
+ * or not */
+ int terseOutput, /* delimiter bars or
+ * not? */
+ int width); /* width of column, if
+ * 0, use variable width */
#ifdef MULTIBYTE
extern int PQmblen(unsigned char *s);
diff --git a/src/interfaces/libpq/libpq-int.h b/src/interfaces/libpq/libpq-int.h
index 4c877de2d2e..e05ea4bebff 100644
--- a/src/interfaces/libpq/libpq-int.h
+++ b/src/interfaces/libpq/libpq-int.h
@@ -11,7 +11,7 @@
*
* 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 @@
char *value; /* actual value */
} PGresAttValue;
- struct pg_result
+ struct pg_result
{
int ntups;
int numAttributes;
@@ -91,7 +91,15 @@
* last insert query */
int binary; /* binary tuple values if binary == 1,
* 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 */
+ char *errMsg; /* error message, or NULL if no error */
};
/* PGAsyncStatusType defines the state of the query-execution state machine */
@@ -174,12 +182,8 @@
PGresult *result; /* result being constructed */
PGresAttValue *curTuple; /* tuple currently being read */
- /* 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. */
+ /* Message space. Placed last for code-size reasons. */
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);
/* === in fe-exec.c === */
+extern void pqSetResultError(PGresult *res, const char *msg);
extern void pqClearAsyncResult(PGconn *conn);
/* === in fe-misc.c === */
diff --git a/src/interfaces/libpq/libpqdll.def b/src/interfaces/libpq/libpqdll.def
index fee6f217d60..a7c9b28ef80 100644
--- a/src/interfaces/libpq/libpqdll.def
+++ b/src/interfaces/libpq/libpqdll.def
@@ -63,3 +63,4 @@ EXPORTS
lo_unlink @ 60
lo_import @ 61
lo_export @ 62
+ PQresultErrorMessage @ 63