]> granicus.if.org Git - postgresql/commitdiff
Update libpq to store an error message in PGresult, per pgsq-interfaces discussion...
authorTom Lane <tgl@sss.pgh.pa.us>
Thu, 1 Oct 1998 01:40:26 +0000 (01:40 +0000)
committerTom Lane <tgl@sss.pgh.pa.us>
Thu, 1 Oct 1998 01:40:26 +0000 (01:40 +0000)
src/interfaces/libpq/fe-connect.c
src/interfaces/libpq/fe-exec.c
src/interfaces/libpq/fe-lobj.c
src/interfaces/libpq/libpq-fe.h
src/interfaces/libpq/libpq-int.h
src/interfaces/libpq/libpqdll.def

index e21d17f23f7f2190c78780f5084d60d39e7f6480..3a2b16c22d958d5be0dca9721e275b932f31099a 100644 (file)
@@ -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));
index 5f889414e19752f15178ca7ae414b9451a800825..82c697ef05b614f5e9193e433e8110b4aea14318 100644 (file)
@@ -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)
 {
index 190cab1810674b651e2d446ed8853d7594713209..19c7770f3f194c3f5668094233dee7b4240a9118 100644 (file)
@@ -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;
+       }
 }
 
 /*
index 8f0903526353ed6f604370ccdcd54fd8a7d0fe94..13ce50b873d18655b38ce4e7a606ec81be5e4054 100644 (file)
@@ -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);
index 4c877de2d2e8895af23da79fb14cef3d5ee635e9..e05ea4bebffc2780b1cf492b6bd74f5e2dd413a8 100644 (file)
@@ -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;
                                                                                                 * 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 */
                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 === */
index fee6f217d6020612a85bc5efddd7fe29e6f127db..a7c9b28ef80abbab0fa505655cd6b20b6a1a5a32 100644 (file)
@@ -63,3 +63,4 @@ EXPORTS
        lo_unlink               @ 60
        lo_import               @ 61
        lo_export               @ 62
+       PQresultErrorMessage    @ 63