]> granicus.if.org Git - postgresql/blobdiff - src/interfaces/libpq/fe-exec.c
pgindent run for 8.2.
[postgresql] / src / interfaces / libpq / fe-exec.c
index b90159af4dd1125010a8cc9bf5013d713a975d2b..5a7adb2ff8b4f6d645d0d19de7c19371087bc54d 100644 (file)
@@ -3,23 +3,25 @@
  * fe-exec.c
  *       functions related to sending a query down to the backend
  *
- * Portions Copyright (c) 1996-2000, PostgreSQL, Inc
+ * Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/interfaces/libpq/fe-exec.c,v 1.95 2000/05/25 19:09:55 momjian Exp $
+ *       $PostgreSQL: pgsql/src/interfaces/libpq/fe-exec.c,v 1.191 2006/10/04 00:30:13 momjian Exp $
  *
  *-------------------------------------------------------------------------
  */
-#include <errno.h>
+#include "postgres_fe.h"
+
 #include <ctype.h>
 #include <fcntl.h>
 
-#include "postgres.h"
 #include "libpq-fe.h"
 #include "libpq-int.h"
 
+#include "mb/pg_wchar.h"
+
 #ifdef WIN32
 #include "win32.h"
 #else
@@ -38,22 +40,29 @@ char           *const pgresStatus[] = {
        "PGRES_FATAL_ERROR"
 };
 
-
-/* Note: DONOTICE macro will work if applied to either PGconn or PGresult */
-#define DONOTICE(conn,message) \
-       ((*(conn)->noticeHook) ((conn)->noticeArg, (message)))
-
-
-static void pqCatenateResultError(PGresult *res, const char *msg);
-static void saveErrorResult(PGconn *conn);
-static PGresult *prepareAsyncResult(PGconn *conn);
-static int     addTuple(PGresult *res, PGresAttValue * tup);
+/*
+ * static state needed by PQescapeString and PQescapeBytea; initialize to
+ * values that result in backward-compatible behavior
+ */
+static int     static_client_encoding = PG_SQL_ASCII;
+static bool static_std_strings = false;
+
+
+static bool PQsendQueryStart(PGconn *conn);
+static int PQsendQueryGuts(PGconn *conn,
+                               const char *command,
+                               const char *stmtName,
+                               int nParams,
+                               const Oid *paramTypes,
+                               const char *const * paramValues,
+                               const int *paramLengths,
+                               const int *paramFormats,
+                               int resultFormat);
 static void parseInput(PGconn *conn);
-static void handleSendFailure(PGconn *conn);
-static int     getRowDescriptions(PGconn *conn);
-static int     getAnotherTuple(PGconn *conn, int binary);
-static int     getNotify(PGconn *conn);
-static int     getNotice(PGconn *conn);
+static bool PQexecStart(PGconn *conn);
+static PGresult *PQexecFinish(PGconn *conn);
+static int PQsendDescribe(PGconn *conn, char desc_type,
+                          const char *desc_target);
 
 
 /* ----------------
@@ -72,6 +81,7 @@ static int    getNotice(PGconn *conn);
  * combine it with the first allocation block, but that would waste space
  * for the common case that no extra storage is actually needed (that is,
  * the SQL command did not return tuples).
+ *
  * We also malloc the top-level array of tuple pointers separately, because
  * we need to be able to enlarge it via realloc, and our trivial space
  * allocator doesn't handle that effectively.  (Too bad the FE/BE protocol
@@ -83,6 +93,7 @@ static int    getNotice(PGconn *conn);
  * A query returning a small amount of data will thus require three malloc
  * calls: one for the PGresult, one for the tuples pointer array, and one
  * PGresult_data block.
+ *
  * Only the most recently allocated PGresult_data block is a candidate to
  * have more stuff added to it --- any extra space left over in older blocks
  * is wasted.  We could be smarter and search the whole chain, but the point
@@ -107,14 +118,9 @@ static int getNotice(PGconn *conn);
  * ----------------
  */
 
-#ifdef MAX
-#undef MAX
-#endif
-#define MAX(a,b)  ((a) > (b) ? (a) : (b))
-
 #define PGRESULT_DATA_BLOCKSIZE                2048
 #define PGRESULT_ALIGN_BOUNDARY                MAXIMUM_ALIGNOF         /* from configure */
-#define PGRESULT_BLOCK_OVERHEAD                MAX(sizeof(PGresult_data), PGRESULT_ALIGN_BOUNDARY)
+#define PGRESULT_BLOCK_OVERHEAD                Max(sizeof(PGresult_data), PGRESULT_ALIGN_BOUNDARY)
 #define PGRESULT_SEP_ALLOC_THRESHOLD   (PGRESULT_DATA_BLOCKSIZE / 2)
 
 
@@ -129,23 +135,27 @@ static int        getNotice(PGconn *conn);
  * and the Perl5 interface, so maybe it's not so unreasonable.
  */
 
-PGresult   *
+PGresult *
 PQmakeEmptyPGresult(PGconn *conn, ExecStatusType status)
 {
        PGresult   *result;
 
        result = (PGresult *) malloc(sizeof(PGresult));
+       if (!result)
+               return NULL;
 
-       result->xconn = conn;           /* might be NULL */
        result->ntups = 0;
        result->numAttributes = 0;
        result->attDescs = NULL;
        result->tuples = NULL;
        result->tupArrSize = 0;
+       result->numParameters = 0;
+       result->paramDescs = NULL;
        result->resultStatus = status;
        result->cmdStatus[0] = '\0';
        result->binary = 0;
        result->errMsg = NULL;
+       result->errFields = NULL;
        result->null_field[0] = '\0';
        result->curBlock = NULL;
        result->curOffset = 0;
@@ -154,8 +164,7 @@ PQmakeEmptyPGresult(PGconn *conn, ExecStatusType status)
        if (conn)
        {
                /* copy connection data we might need for operations on PGresult */
-               result->noticeHook = conn->noticeHook;
-               result->noticeArg = conn->noticeArg;
+               result->noticeHooks = conn->noticeHooks;
                result->client_encoding = conn->client_encoding;
 
                /* consider copying conn's errorMessage */
@@ -176,9 +185,11 @@ PQmakeEmptyPGresult(PGconn *conn, ExecStatusType status)
        else
        {
                /* defaults... */
-               result->noticeHook = NULL;
-               result->noticeArg = NULL;
-               result->client_encoding = 0;    /* should be SQL_ASCII */
+               result->noticeHooks.noticeRec = NULL;
+               result->noticeHooks.noticeRecArg = NULL;
+               result->noticeHooks.noticeProc = NULL;
+               result->noticeHooks.noticeProcArg = NULL;
+               result->client_encoding = PG_SQL_ASCII;
        }
 
        return result;
@@ -207,8 +218,8 @@ pqResultAlloc(PGresult *res, size_t nBytes, bool isBinary)
                return res->null_field;
 
        /*
-        * If alignment is needed, round up the current position to an
-        * alignment boundary.
+        * If alignment is needed, round up the current position to an alignment
+        * boundary.
         */
        if (isBinary)
        {
@@ -222,7 +233,7 @@ pqResultAlloc(PGresult *res, size_t nBytes, bool isBinary)
        }
 
        /* If there's enough space in the current block, no problem. */
-       if (nBytes <= res->spaceLeft)
+       if (nBytes <= (size_t) res->spaceLeft)
        {
                space = res->curBlock->space + res->curOffset;
                res->curOffset += nBytes;
@@ -232,10 +243,9 @@ pqResultAlloc(PGresult *res, size_t nBytes, bool isBinary)
 
        /*
         * If the requested object is very large, give it its own block; this
-        * avoids wasting what might be most of the current block to start a
-        * new block.  (We'd have to special-case requests bigger than the
-        * block size anyway.)  The object is always given binary alignment in
-        * this case.
+        * avoids wasting what might be most of the current block to start a new
+        * block.  (We'd have to special-case requests bigger than the block size
+        * anyway.)  The object is always given binary alignment in this case.
         */
        if (nBytes >= PGRESULT_SEP_ALLOC_THRESHOLD)
        {
@@ -245,7 +255,6 @@ pqResultAlloc(PGresult *res, size_t nBytes, bool isBinary)
                space = block->space + PGRESULT_BLOCK_OVERHEAD;
                if (res->curBlock)
                {
-
                        /*
                         * Tuck special block below the active block, so that we don't
                         * have to waste the free space in the active block.
@@ -321,7 +330,7 @@ pqSetResultError(PGresult *res, const char *msg)
  * pqCatenateResultError -
  *             concatenate a new error message to the one already in a PGresult
  */
-static void
+void
 pqCatenateResultError(PGresult *res, const char *msg)
 {
        PQExpBufferData errorBuf;
@@ -359,6 +368,13 @@ PQclear(PGresult *res)
        if (res->tuples)
                free(res->tuples);
 
+       /* zero out the pointer fields to catch programming errors */
+       res->attDescs = NULL;
+       res->tuples = NULL;
+       res->paramDescs = NULL;
+       res->errFields = NULL;
+       /* res->curBlock was zeroed out earlier */
+
        /* Free the PGresult structure itself */
        free(res);
 }
@@ -388,13 +404,12 @@ pqClearAsyncResult(PGconn *conn)
  * and immediately closes the connection --- we want to report both the
  * backend error and the connection closure error.)
  */
-static void
-saveErrorResult(PGconn *conn)
+void
+pqSaveErrorResult(PGconn *conn)
 {
-
        /*
-        * If no old async result, just let PQmakeEmptyPGresult make one.
-        * Likewise if old result is not an error message.
+        * If no old async result, just let PQmakeEmptyPGresult make one. Likewise
+        * if old result is not an error message.
         */
        if (conn->result == NULL ||
                conn->result->resultStatus != PGRES_FATAL_ERROR ||
@@ -417,15 +432,15 @@ saveErrorResult(PGconn *conn)
  * result storage and make sure PQerrorMessage will agree with the result's
  * error string.
  */
-static PGresult *
-prepareAsyncResult(PGconn *conn)
+PGresult *
+pqPrepareAsyncResult(PGconn *conn)
 {
        PGresult   *res;
 
        /*
-        * 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.
+        * 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 */
@@ -434,10 +449,9 @@ prepareAsyncResult(PGconn *conn)
                res = PQmakeEmptyPGresult(conn, PGRES_FATAL_ERROR);
        else
        {
-
                /*
-                * Make sure PQerrorMessage agrees with result; it could be
-                * different if we have concatenated messages.
+                * Make sure PQerrorMessage agrees with result; it could be different
+                * if we have concatenated messages.
                 */
                resetPQExpBuffer(&conn->errorMessage);
                appendPQExpBufferStr(&conn->errorMessage,
@@ -447,26 +461,80 @@ prepareAsyncResult(PGconn *conn)
 }
 
 /*
- * addTuple
+ * pqInternalNotice - produce an internally-generated notice message
+ *
+ * A format string and optional arguments can be passed.  Note that we do
+ * libpq_gettext() here, so callers need not.
+ *
+ * The supplied text is taken as primary message (ie., it should not include
+ * a trailing newline, and should not be more than one line).
+ */
+void
+pqInternalNotice(const PGNoticeHooks *hooks, const char *fmt,...)
+{
+       char            msgBuf[1024];
+       va_list         args;
+       PGresult   *res;
+
+       if (hooks->noticeRec == NULL)
+               return;                                 /* nobody home to receive notice? */
+
+       /* Format the message */
+       va_start(args, fmt);
+       vsnprintf(msgBuf, sizeof(msgBuf), libpq_gettext(fmt), args);
+       va_end(args);
+       msgBuf[sizeof(msgBuf) - 1] = '\0';      /* make real sure it's terminated */
+
+       /* Make a PGresult to pass to the notice receiver */
+       res = PQmakeEmptyPGresult(NULL, PGRES_NONFATAL_ERROR);
+       if (!res)
+               return;
+       res->noticeHooks = *hooks;
+
+       /*
+        * Set up fields of notice.
+        */
+       pqSaveMessageField(res, PG_DIAG_MESSAGE_PRIMARY, msgBuf);
+       pqSaveMessageField(res, PG_DIAG_SEVERITY, libpq_gettext("NOTICE"));
+       /* XXX should provide a SQLSTATE too? */
+
+       /*
+        * Result text is always just the primary message + newline. If we can't
+        * allocate it, don't bother invoking the receiver.
+        */
+       res->errMsg = (char *) pqResultAlloc(res, strlen(msgBuf) + 2, FALSE);
+       if (res->errMsg)
+       {
+               sprintf(res->errMsg, "%s\n", msgBuf);
+
+               /*
+                * Pass to receiver, then free it.
+                */
+               (*res->noticeHooks.noticeRec) (res->noticeHooks.noticeRecArg, res);
+       }
+       PQclear(res);
+}
+
+/*
+ * pqAddTuple
  *       add a row pointer to the PGresult structure, growing it if necessary
  *       Returns TRUE if OK, FALSE if not enough memory to add the row
  */
-static int
-addTuple(PGresult *res, PGresAttValue * tup)
+int
+pqAddTuple(PGresult *res, PGresAttValue *tup)
 {
        if (res->ntups >= res->tupArrSize)
        {
-
                /*
                 * 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.
-                * While ANSI says that realloc() should act like malloc() in that
-                * case, some old C libraries (like SunOS 4.1.x) coredump instead.
-                * 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.
+                * okay. Note that the first time through, res->tuples is NULL. While
+                * ANSI says that realloc() should act like malloc() in that case,
+                * some old C libraries (like SunOS 4.1.x) coredump instead. 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.
                 */
                int                     newSize = (res->tupArrSize > 0) ? res->tupArrSize * 2 : 128;
                PGresAttValue **newTuples;
@@ -487,6 +555,113 @@ addTuple(PGresult *res, PGresAttValue * tup)
        return TRUE;
 }
 
+/*
+ * pqSaveMessageField - save one field of an error or notice message
+ */
+void
+pqSaveMessageField(PGresult *res, char code, const char *value)
+{
+       PGMessageField *pfield;
+
+       pfield = (PGMessageField *)
+               pqResultAlloc(res,
+                                         sizeof(PGMessageField) + strlen(value),
+                                         TRUE);
+       if (!pfield)
+               return;                                 /* out of memory? */
+       pfield->code = code;
+       strcpy(pfield->contents, value);
+       pfield->next = res->errFields;
+       res->errFields = pfield;
+}
+
+/*
+ * pqSaveParameterStatus - remember parameter status sent by backend
+ */
+void
+pqSaveParameterStatus(PGconn *conn, const char *name, const char *value)
+{
+       pgParameterStatus *pstatus;
+       pgParameterStatus *prev;
+
+       if (conn->Pfdebug)
+               fprintf(conn->Pfdebug, "pqSaveParameterStatus: '%s' = '%s'\n",
+                               name, value);
+
+       /*
+        * Forget any old information about the parameter
+        */
+       for (pstatus = conn->pstatus, prev = NULL;
+                pstatus != NULL;
+                prev = pstatus, pstatus = pstatus->next)
+       {
+               if (strcmp(pstatus->name, name) == 0)
+               {
+                       if (prev)
+                               prev->next = pstatus->next;
+                       else
+                               conn->pstatus = pstatus->next;
+                       free(pstatus);          /* frees name and value strings too */
+                       break;
+               }
+       }
+
+       /*
+        * Store new info as a single malloc block
+        */
+       pstatus = (pgParameterStatus *) malloc(sizeof(pgParameterStatus) +
+                                                                                  strlen(name) +strlen(value) + 2);
+       if (pstatus)
+       {
+               char       *ptr;
+
+               ptr = ((char *) pstatus) + sizeof(pgParameterStatus);
+               pstatus->name = ptr;
+               strcpy(ptr, name);
+               ptr += strlen(name) + 1;
+               pstatus->value = ptr;
+               strcpy(ptr, value);
+               pstatus->next = conn->pstatus;
+               conn->pstatus = pstatus;
+       }
+
+       /*
+        * Special hacks: remember client_encoding and
+        * standard_conforming_strings, and convert server version to a numeric
+        * form.  We keep the first two of these in static variables as well, so
+        * that PQescapeString and PQescapeBytea can behave somewhat sanely (at
+        * least in single- connection-using programs).
+        */
+       if (strcmp(name, "client_encoding") == 0)
+       {
+               conn->client_encoding = pg_char_to_encoding(value);
+               static_client_encoding = conn->client_encoding;
+       }
+       else if (strcmp(name, "standard_conforming_strings") == 0)
+       {
+               conn->std_strings = (strcmp(value, "on") == 0);
+               static_std_strings = conn->std_strings;
+       }
+       else if (strcmp(name, "server_version") == 0)
+       {
+               int                     cnt;
+               int                     vmaj,
+                                       vmin,
+                                       vrev;
+
+               cnt = sscanf(value, "%d.%d.%d", &vmaj, &vmin, &vrev);
+
+               if (cnt < 2)
+                       conn->sversion = 0; /* unknown */
+               else
+               {
+                       if (cnt == 2)
+                               vrev = 0;
+                       conn->sversion = (100 * vmaj + vmin) * 100 + vrev;
+               }
+       }
+}
+
 
 /*
  * PQsendQuery
@@ -495,594 +670,497 @@ addTuple(PGresult *res, PGresAttValue * tup)
  * Returns: 1 if successfully submitted
  *                     0 if error (conn->errorMessage is set)
  */
-
 int
 PQsendQuery(PGconn *conn, const char *query)
 {
-       if (!conn)
+       if (!PQsendQueryStart(conn))
                return 0;
 
-       /* clear the error string */
-       resetPQExpBuffer(&conn->errorMessage);
-
        if (!query)
        {
                printfPQExpBuffer(&conn->errorMessage,
-                                                 "PQsendQuery() -- query pointer is null.\n");
+                                               libpq_gettext("command string is a null pointer\n"));
                return 0;
        }
 
-       /* Don't try to send if we know there's no live connection. */
-       if (conn->status != CONNECTION_OK)
+       /* construct the outgoing Query message */
+       if (pqPutMsgStart('Q', false, conn) < 0 ||
+               pqPuts(query, conn) < 0 ||
+               pqPutMsgEnd(conn) < 0)
        {
-               printfPQExpBuffer(&conn->errorMessage,
-                                                 "PQsendQuery() -- There is no connection "
-                                                 "to the backend.\n");
+               pqHandleSendFailure(conn);
                return 0;
        }
-       /* Can't send while already busy, either. */
-       if (conn->asyncStatus != PGASYNC_IDLE)
+
+       /* remember we are using simple query protocol */
+       conn->queryclass = PGQUERY_SIMPLE;
+
+       /* and remember the query text too, if possible */
+       /* if insufficient memory, last_query just winds up NULL */
+       if (conn->last_query)
+               free(conn->last_query);
+       conn->last_query = strdup(query);
+
+       /*
+        * Give the data a push.  In nonblock mode, don't complain if we're unable
+        * to send it all; PQgetResult() will do any additional flushing needed.
+        */
+       if (pqFlush(conn) < 0)
+       {
+               pqHandleSendFailure(conn);
+               return 0;
+       }
+
+       /* OK, it's launched! */
+       conn->asyncStatus = PGASYNC_BUSY;
+       return 1;
+}
+
+/*
+ * PQsendQueryParams
+ *             Like PQsendQuery, but use protocol 3.0 so we can pass parameters
+ */
+int
+PQsendQueryParams(PGconn *conn,
+                                 const char *command,
+                                 int nParams,
+                                 const Oid *paramTypes,
+                                 const char *const * paramValues,
+                                 const int *paramLengths,
+                                 const int *paramFormats,
+                                 int resultFormat)
+{
+       if (!PQsendQueryStart(conn))
+               return 0;
+
+       if (!command)
        {
                printfPQExpBuffer(&conn->errorMessage,
-                               "PQsendQuery() -- another query already in progress.\n");
+                                               libpq_gettext("command string is a null pointer\n"));
                return 0;
        }
 
-       /* initialize async result-accumulation state */
-       conn->result = NULL;
-       conn->curTuple = NULL;
+       return PQsendQueryGuts(conn,
+                                                  command,
+                                                  "",  /* use unnamed statement */
+                                                  nParams,
+                                                  paramTypes,
+                                                  paramValues,
+                                                  paramLengths,
+                                                  paramFormats,
+                                                  resultFormat);
+}
 
-       /* send the query to the backend; */
+/*
+ * PQsendPrepare
+ *      Submit a Parse message, but don't wait for it to finish
+ *
+ * Returns: 1 if successfully submitted
+ *                     0 if error (conn->errorMessage is set)
+ */
+int
+PQsendPrepare(PGconn *conn,
+                         const char *stmtName, const char *query,
+                         int nParams, const Oid *paramTypes)
+{
+       if (!PQsendQueryStart(conn))
+               return 0;
 
-       /*
-        * in order to guarantee that we don't send a partial query where we
-        * would become out of sync with the backend and/or block during a
-        * non-blocking connection we must first flush the send buffer before
-        * sending more data
-        *
-        * an alternative is to implement 'queue reservations' where we are able
-        * to roll up a transaction (the 'Q' along with our query) and make
-        * sure we have enough space for it all in the send buffer.
-        */
-       if (pqIsnonblocking(conn))
+       if (!stmtName)
        {
+               printfPQExpBuffer(&conn->errorMessage,
+                                               libpq_gettext("statement name is a null pointer\n"));
+               return 0;
+       }
 
-               /*
-                * the buffer must have emptied completely before we allow a new
-                * query to be buffered
-                */
-               if (pqFlush(conn))
-                       return 0;
-               /* 'Q' == queries */
-               /* XXX: if we fail here we really ought to not block */
-               if (pqPutnchar("Q", 1, conn) ||
-                       pqPuts(query, conn))
-               {
-                       handleSendFailure(conn);
-                       return 0;
-               }
+       if (!query)
+       {
+               printfPQExpBuffer(&conn->errorMessage,
+                                               libpq_gettext("command string is a null pointer\n"));
+               return 0;
+       }
 
-               /*
-                * give the data a push, ignore the return value as ConsumeInput()
-                * will do any aditional flushing if needed
-                */
-               (void) pqFlush(conn);
+       /* This isn't gonna work on a 2.0 server */
+       if (PG_PROTOCOL_MAJOR(conn->pversion) < 3)
+       {
+               printfPQExpBuffer(&conn->errorMessage,
+                libpq_gettext("function requires at least protocol version 3.0\n"));
+               return 0;
        }
-       else
+
+       /* construct the Parse message */
+       if (pqPutMsgStart('P', false, conn) < 0 ||
+               pqPuts(stmtName, conn) < 0 ||
+               pqPuts(query, conn) < 0)
+               goto sendFailed;
+
+       if (nParams > 0 && paramTypes)
        {
+               int                     i;
 
-               /*
-                * the frontend-backend protocol uses 'Q' to designate queries
-                */
-               if (pqPutnchar("Q", 1, conn) ||
-                       pqPuts(query, conn) ||
-                       pqFlush(conn))
+               if (pqPutInt(nParams, 2, conn) < 0)
+                       goto sendFailed;
+               for (i = 0; i < nParams; i++)
                {
-                       handleSendFailure(conn);
-                       return 0;
+                       if (pqPutInt(paramTypes[i], 4, conn) < 0)
+                               goto sendFailed;
                }
        }
+       else
+       {
+               if (pqPutInt(0, 2, conn) < 0)
+                       goto sendFailed;
+       }
+       if (pqPutMsgEnd(conn) < 0)
+               goto sendFailed;
+
+       /* construct the Sync message */
+       if (pqPutMsgStart('S', false, conn) < 0 ||
+               pqPutMsgEnd(conn) < 0)
+               goto sendFailed;
+
+       /* remember we are doing just a Parse */
+       conn->queryclass = PGQUERY_PREPARE;
+
+       /* and remember the query text too, if possible */
+       /* if insufficient memory, last_query just winds up NULL */
+       if (conn->last_query)
+               free(conn->last_query);
+       conn->last_query = strdup(query);
+
+       /*
+        * Give the data a push.  In nonblock mode, don't complain if we're unable
+        * to send it all; PQgetResult() will do any additional flushing needed.
+        */
+       if (pqFlush(conn) < 0)
+               goto sendFailed;
 
        /* OK, it's launched! */
        conn->asyncStatus = PGASYNC_BUSY;
        return 1;
+
+sendFailed:
+       pqHandleSendFailure(conn);
+       return 0;
 }
 
 /*
- * handleSendFailure: try to clean up after failure to send command.
- *
- * Primarily, what we want to accomplish here is to process an async
- * NOTICE message that the backend might have sent just before it died.
- *
- * NOTE: this routine should only be called in PGASYNC_IDLE state.
+ * PQsendQueryPrepared
+ *             Like PQsendQuery, but execute a previously prepared statement,
+ *             using protocol 3.0 so we can pass parameters
  */
-
-static void
-handleSendFailure(PGconn *conn)
+int
+PQsendQueryPrepared(PGconn *conn,
+                                       const char *stmtName,
+                                       int nParams,
+                                       const char *const * paramValues,
+                                       const int *paramLengths,
+                                       const int *paramFormats,
+                                       int resultFormat)
 {
+       if (!PQsendQueryStart(conn))
+               return 0;
 
-       /*
-        * Accept any available input data, ignoring errors.  Note that if
-        * pqReadData decides the backend has closed the channel, it will
-        * close our side of the socket --- that's just what we want here.
-        */
-       while (pqReadData(conn) > 0)
-                /* loop until no more data readable */ ;
+       if (!stmtName)
+       {
+               printfPQExpBuffer(&conn->errorMessage,
+                                               libpq_gettext("statement name is a null pointer\n"));
+               return 0;
+       }
 
-       /*
-        * Parse any available input messages.  Since we are in PGASYNC_IDLE
-        * state, only NOTICE and NOTIFY messages will be eaten.
-        */
-       parseInput(conn);
+       return PQsendQueryGuts(conn,
+                                                  NULL,        /* no command to parse */
+                                                  stmtName,
+                                                  nParams,
+                                                  NULL,        /* no param types */
+                                                  paramValues,
+                                                  paramLengths,
+                                                  paramFormats,
+                                                  resultFormat);
 }
 
 /*
- * Consume any available input from the backend
- * 0 return: some kind of trouble
- * 1 return: no problem
+ * Common startup code for PQsendQuery and sibling routines
  */
-
-int
-PQconsumeInput(PGconn *conn)
+static bool
+PQsendQueryStart(PGconn *conn)
 {
        if (!conn)
-               return 0;
+               return false;
 
-       /*
-        * Load more data, if available. We do this no matter what state we
-        * are in, since we are probably getting called because the
-        * application wants to get rid of a read-select condition. Note that
-        * we will NOT block waiting for more input.
-        */
-       if (pqReadData(conn) < 0)
-       {
+       /* clear the error string */
+       resetPQExpBuffer(&conn->errorMessage);
 
-               /*
-                * for non-blocking connections try to flush the send-queue
-                * otherwise we may never get a responce for something that may
-                * not have already been sent because it's in our write buffer!
-                */
-               if (pqIsnonblocking(conn))
-                       (void) pqFlush(conn);
-               return 0;
+       /* Don't try to send if we know there's no live connection. */
+       if (conn->status != CONNECTION_OK)
+       {
+               printfPQExpBuffer(&conn->errorMessage,
+                                                 libpq_gettext("no connection to the server\n"));
+               return false;
+       }
+       /* Can't send while already busy, either. */
+       if (conn->asyncStatus != PGASYNC_IDLE)
+       {
+               printfPQExpBuffer(&conn->errorMessage,
+                                 libpq_gettext("another command is already in progress\n"));
+               return false;
        }
-       /* Parsing of the data waits till later. */
-       return 1;
-}
 
+       /* initialize async result-accumulation state */
+       conn->result = NULL;
+       conn->curTuple = NULL;
+
+       /* ready to send command message */
+       return true;
+}
 
 /*
- * parseInput: if appropriate, parse input data from backend
- * until input is exhausted or a stopping state is reached.
- * Note that this function will NOT attempt to read more data from the backend.
+ * PQsendQueryGuts
+ *             Common code for protocol-3.0 query sending
+ *             PQsendQueryStart should be done already
+ *
+ * command may be NULL to indicate we use an already-prepared statement
  */
-
-static void
-parseInput(PGconn *conn)
+static int
+PQsendQueryGuts(PGconn *conn,
+                               const char *command,
+                               const char *stmtName,
+                               int nParams,
+                               const Oid *paramTypes,
+                               const char *const * paramValues,
+                               const int *paramLengths,
+                               const int *paramFormats,
+                               int resultFormat)
 {
-       char            id;
-       char            noticeWorkspace[128];
+       int                     i;
+
+       /* This isn't gonna work on a 2.0 server */
+       if (PG_PROTOCOL_MAJOR(conn->pversion) < 3)
+       {
+               printfPQExpBuffer(&conn->errorMessage,
+                libpq_gettext("function requires at least protocol version 3.0\n"));
+               return 0;
+       }
 
        /*
-        * Loop to parse successive complete messages available in the buffer.
+        * We will send Parse (if needed), Bind, Describe Portal, Execute, Sync,
+        * using specified statement name and the unnamed portal.
         */
-       for (;;)
+
+       if (command)
        {
+               /* construct the Parse message */
+               if (pqPutMsgStart('P', false, conn) < 0 ||
+                       pqPuts(stmtName, conn) < 0 ||
+                       pqPuts(command, conn) < 0)
+                       goto sendFailed;
+               if (nParams > 0 && paramTypes)
+               {
+                       if (pqPutInt(nParams, 2, conn) < 0)
+                               goto sendFailed;
+                       for (i = 0; i < nParams; i++)
+                       {
+                               if (pqPutInt(paramTypes[i], 4, conn) < 0)
+                                       goto sendFailed;
+                       }
+               }
+               else
+               {
+                       if (pqPutInt(0, 2, conn) < 0)
+                               goto sendFailed;
+               }
+               if (pqPutMsgEnd(conn) < 0)
+                       goto sendFailed;
+       }
 
-               /*
-                * Quit if in COPY_OUT state: we expect raw data from the server
-                * until PQendcopy is called.  Don't try to parse it according to
-                * the normal protocol.  (This is bogus.  The data lines ought to
-                * be part of the protocol and have identifying leading
-                * characters.)
-                */
-               if (conn->asyncStatus == PGASYNC_COPY_OUT)
-                       return;
+       /* construct the Bind message */
+       if (pqPutMsgStart('B', false, conn) < 0 ||
+               pqPuts("", conn) < 0 ||
+               pqPuts(stmtName, conn) < 0)
+               goto sendFailed;
 
-               /*
-                * OK to try to read a message type code.
-                */
-               conn->inCursor = conn->inStart;
-               if (pqGetc(&id, conn))
-                       return;
-
-               /*
-                * NOTIFY and NOTICE messages can happen in any state besides COPY
-                * OUT; always process them right away.
-                *
-                * Most other messages should only be processed while in BUSY state.
-                * (In particular, in READY state we hold off further parsing
-                * until the application collects the current PGresult.)
-                *
-                * However, if the state is IDLE then we got trouble; we need to deal
-                * with the unexpected message somehow.
-                */
-               if (id == 'A')
-               {
-                       if (getNotify(conn))
-                               return;
-               }
-               else if (id == 'N')
+       if (nParams > 0 && paramFormats)
+       {
+               if (pqPutInt(nParams, 2, conn) < 0)
+                       goto sendFailed;
+               for (i = 0; i < nParams; i++)
                {
-                       if (getNotice(conn))
-                               return;
+                       if (pqPutInt(paramFormats[i], 2, conn) < 0)
+                               goto sendFailed;
                }
-               else if (conn->asyncStatus != PGASYNC_BUSY)
+       }
+       else
+       {
+               if (pqPutInt(0, 2, conn) < 0)
+                       goto sendFailed;
+       }
+
+       if (pqPutInt(nParams, 2, conn) < 0)
+               goto sendFailed;
+
+       for (i = 0; i < nParams; i++)
+       {
+               if (paramValues && paramValues[i])
                {
-                       /* If not IDLE state, just wait ... */
-                       if (conn->asyncStatus != PGASYNC_IDLE)
-                               return;
+                       int                     nbytes;
 
-                       /*
-                        * Unexpected message in IDLE state; need to recover somehow.
-                        * ERROR messages are displayed using the notice processor;
-                        * anything else is just dropped on the floor after displaying
-                        * a suitable warning notice.  (An ERROR is very possibly the
-                        * backend telling us why it is about to close the connection,
-                        * so we don't want to just discard it...)
-                        */
-                       if (id == 'E')
+                       if (paramFormats && paramFormats[i] != 0)
                        {
-                               if (getNotice(conn))
-                                       return;
+                               /* binary parameter */
+                               if (paramLengths)
+                                       nbytes = paramLengths[i];
+                               else
+                               {
+                                       printfPQExpBuffer(&conn->errorMessage,
+                                                                         libpq_gettext("length must be given for binary parameter\n"));
+                                       goto sendFailed;
+                               }
                        }
                        else
                        {
-                               sprintf(noticeWorkspace,
-                                         "Backend message type 0x%02x arrived while idle\n",
-                                               id);
-                               DONOTICE(conn, noticeWorkspace);
-                               /* Discard the unexpected message; good idea?? */
-                               conn->inStart = conn->inEnd;
-                               break;
+                               /* text parameter, do not use paramLengths */
+                               nbytes = strlen(paramValues[i]);
                        }
+                       if (pqPutInt(nbytes, 4, conn) < 0 ||
+                               pqPutnchar(paramValues[i], nbytes, conn) < 0)
+                               goto sendFailed;
                }
                else
                {
-
-                       /*
-                        * In BUSY state, we can process everything.
-                        */
-                       switch (id)
-                       {
-                               case 'C':               /* command complete */
-                                       if (pqGets(&conn->workBuffer, conn))
-                                               return;
-                                       if (conn->result == NULL)
-                                               conn->result = PQmakeEmptyPGresult(conn,
-                                                                                                          PGRES_COMMAND_OK);
-                                       strncpy(conn->result->cmdStatus, conn->workBuffer.data,
-                                                       CMDSTATUS_LEN);
-                                       conn->asyncStatus = PGASYNC_READY;
-                                       break;
-                               case 'E':               /* error return */
-                                       if (pqGets(&conn->errorMessage, conn))
-                                               return;
-                                       /* build an error result holding the error message */
-                                       saveErrorResult(conn);
-                                       conn->asyncStatus = PGASYNC_READY;
-                                       break;
-                               case 'Z':               /* backend is ready for new query */
-                                       conn->asyncStatus = PGASYNC_IDLE;
-                                       break;
-                               case 'I':               /* empty query */
-                                       /* read and throw away the closing '\0' */
-                                       if (pqGetc(&id, conn))
-                                               return;
-                                       if (id != '\0')
-                                       {
-                                               sprintf(noticeWorkspace,
-                                                               "unexpected character %c following 'I'\n",
-                                                               id);
-                                               DONOTICE(conn, noticeWorkspace);
-                                       }
-                                       if (conn->result == NULL)
-                                               conn->result = PQmakeEmptyPGresult(conn,
-                                                                                                         PGRES_EMPTY_QUERY);
-                                       conn->asyncStatus = PGASYNC_READY;
-                                       break;
-                               case 'K':               /* secret key data from the backend */
-
-                                       /*
-                                        * This is expected only during backend startup, but
-                                        * it's just as easy to handle it as part of the main
-                                        * loop.  Save the data and continue processing.
-                                        */
-                                       if (pqGetInt(&(conn->be_pid), 4, conn))
-                                               return;
-                                       if (pqGetInt(&(conn->be_key), 4, conn))
-                                               return;
-                                       break;
-                               case 'P':               /* synchronous (normal) portal */
-                                       if (pqGets(&conn->workBuffer, conn))
-                                               return;
-                                       /* We pretty much ignore this message type... */
-                                       break;
-                               case 'T':               /* row descriptions (start of query
-                                                                * results) */
-                                       if (conn->result == NULL)
-                                       {
-                                               /* First 'T' in a query sequence */
-                                               if (getRowDescriptions(conn))
-                                                       return;
-                                       }
-                                       else
-                                       {
-
-                                               /*
-                                                * A new 'T' message is treated as the start of
-                                                * another PGresult.  (It is not clear that this
-                                                * is really possible with the current backend.)
-                                                * We stop parsing until the application accepts
-                                                * the current result.
-                                                */
-                                               conn->asyncStatus = PGASYNC_READY;
-                                               return;
-                                       }
-                                       break;
-                               case 'D':               /* ASCII data tuple */
-                                       if (conn->result != NULL)
-                                       {
-                                               /* Read another tuple of a normal query response */
-                                               if (getAnotherTuple(conn, FALSE))
-                                                       return;
-                                       }
-                                       else
-                                       {
-                                               sprintf(noticeWorkspace,
-                                                        "Backend sent D message without prior T\n");
-                                               DONOTICE(conn, noticeWorkspace);
-                                               /* Discard the unexpected message; good idea?? */
-                                               conn->inStart = conn->inEnd;
-                                               return;
-                                       }
-                                       break;
-                               case 'B':               /* Binary data tuple */
-                                       if (conn->result != NULL)
-                                       {
-                                               /* Read another tuple of a normal query response */
-                                               if (getAnotherTuple(conn, TRUE))
-                                                       return;
-                                       }
-                                       else
-                                       {
-                                               sprintf(noticeWorkspace,
-                                                        "Backend sent B message without prior T\n");
-                                               DONOTICE(conn, noticeWorkspace);
-                                               /* Discard the unexpected message; good idea?? */
-                                               conn->inStart = conn->inEnd;
-                                               return;
-                                       }
-                                       break;
-                               case 'G':               /* Start Copy In */
-                                       conn->asyncStatus = PGASYNC_COPY_IN;
-                                       break;
-                               case 'H':               /* Start Copy Out */
-                                       conn->asyncStatus = PGASYNC_COPY_OUT;
-                                       break;
-                               default:
-                                       printfPQExpBuffer(&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",
-                                                                         id);
-                                       /* build an error result holding the error message */
-                                       saveErrorResult(conn);
-                                       /* Discard the unexpected message; good idea?? */
-                                       conn->inStart = conn->inEnd;
-                                       conn->asyncStatus = PGASYNC_READY;
-                                       return;
-                       }                                       /* switch on protocol character */
+                       /* take the param as NULL */
+                       if (pqPutInt(-1, 4, conn) < 0)
+                               goto sendFailed;
                }
-               /* Successfully consumed this message */
-               conn->inStart = conn->inCursor;
        }
-}
-
-
-/*
- * parseInput subroutine to read a 'T' (row descriptions) message.
- * We build a PGresult structure containing the attribute data.
- * Returns: 0 if completed message, EOF if not enough data yet.
- *
- * Note that if we run out of data, we have to release the partially
- * constructed PGresult, and rebuild it again next time.  Fortunately,
- * that shouldn't happen often, since 'T' messages usually fit in a packet.
- */
-
-static int
-getRowDescriptions(PGconn *conn)
-{
-       PGresult   *result;
-       int                     nfields;
-       int                     i;
-
-       result = PQmakeEmptyPGresult(conn, PGRES_TUPLES_OK);
-
-       /* parseInput already read the 'T' label. */
-       /* the next two bytes are the number of fields  */
-       if (pqGetInt(&(result->numAttributes), 2, conn))
-       {
-               PQclear(result);
-               return EOF;
-       }
-       nfields = result->numAttributes;
-
-       /* allocate space for the attribute descriptors */
-       if (nfields > 0)
-       {
-               result->attDescs = (PGresAttDesc *)
-                       pqResultAlloc(result, nfields * sizeof(PGresAttDesc), TRUE);
-               MemSet((char *) result->attDescs, 0, nfields * sizeof(PGresAttDesc));
-       }
-
-       /* get type info */
-       for (i = 0; i < nfields; i++)
-       {
-               int                     typid;
-               int                     typlen;
-               int                     atttypmod;
+       if (pqPutInt(1, 2, conn) < 0 ||
+               pqPutInt(resultFormat, 2, conn))
+               goto sendFailed;
+       if (pqPutMsgEnd(conn) < 0)
+               goto sendFailed;
+
+       /* construct the Describe Portal message */
+       if (pqPutMsgStart('D', false, conn) < 0 ||
+               pqPutc('P', conn) < 0 ||
+               pqPuts("", conn) < 0 ||
+               pqPutMsgEnd(conn) < 0)
+               goto sendFailed;
+
+       /* construct the Execute message */
+       if (pqPutMsgStart('E', false, conn) < 0 ||
+               pqPuts("", conn) < 0 ||
+               pqPutInt(0, 4, conn) < 0 ||
+               pqPutMsgEnd(conn) < 0)
+               goto sendFailed;
+
+       /* construct the Sync message */
+       if (pqPutMsgStart('S', false, conn) < 0 ||
+               pqPutMsgEnd(conn) < 0)
+               goto sendFailed;
+
+       /* remember we are using extended query protocol */
+       conn->queryclass = PGQUERY_EXTENDED;
+
+       /* and remember the query text too, if possible */
+       /* if insufficient memory, last_query just winds up NULL */
+       if (conn->last_query)
+               free(conn->last_query);
+       if (command)
+               conn->last_query = strdup(command);
+       else
+               conn->last_query = NULL;
 
-               if (pqGets(&conn->workBuffer, conn) ||
-                       pqGetInt(&typid, 4, conn) ||
-                       pqGetInt(&typlen, 2, conn) ||
-                       pqGetInt(&atttypmod, 4, conn))
-               {
-                       PQclear(result);
-                       return EOF;
-               }
+       /*
+        * Give the data a push.  In nonblock mode, don't complain if we're unable
+        * to send it all; PQgetResult() will do any additional flushing needed.
+        */
+       if (pqFlush(conn) < 0)
+               goto sendFailed;
 
-               /*
-                * Since pqGetInt treats 2-byte integers as unsigned, we need to
-                * coerce the special value "-1" to signed form.  (-1 is sent for
-                * variable-length fields.)  Formerly, libpq effectively did a
-                * sign-extension on the 2-byte value by storing it in a signed
-                * short. Now we only coerce the single value 65535 == -1; values
-                * 32768..65534 are taken as valid field lengths.
-                */
-               if (typlen == 0xFFFF)
-                       typlen = -1;
-               result->attDescs[i].name = pqResultStrdup(result,
-                                                                                                 conn->workBuffer.data);
-               result->attDescs[i].typid = typid;
-               result->attDescs[i].typlen = typlen;
-               result->attDescs[i].atttypmod = atttypmod;
-       }
+       /* OK, it's launched! */
+       conn->asyncStatus = PGASYNC_BUSY;
+       return 1;
 
-       /* Success! */
-       conn->result = result;
+sendFailed:
+       pqHandleSendFailure(conn);
        return 0;
 }
 
 /*
- * 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 error or not enough data yet.
+ * pqHandleSendFailure: try to clean up after failure to send command.
  *
- * 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
- * tuple in conn->curTuple, and avoid reallocating already-allocated storage.
+ * Primarily, what we want to accomplish here is to process an async
+ * NOTICE message that the backend might have sent just before it died.
+ *
+ * NOTE: this routine should only be called in PGASYNC_IDLE state.
  */
-
-static int
-getAnotherTuple(PGconn *conn, int binary)
+void
+pqHandleSendFailure(PGconn *conn)
 {
-       PGresult   *result = conn->result;
-       int                     nfields = result->numAttributes;
-       PGresAttValue *tup;
-
-       /* the backend sends us a bitmap of which attributes are null */
-       char            std_bitmap[64]; /* used unless it doesn't fit */
-       char       *bitmap = std_bitmap;
-       int                     i;
-       size_t          nbytes;                 /* the number of bytes in bitmap  */
-       char            bmap;                   /* One byte of the bitmap */
-       int                     bitmap_index;   /* Its index */
-       int                     bitcnt;                 /* number of bits examined in current byte */
-       int                     vlen;                   /* length of the current field value */
-
-       result->binary = binary;
-
-       /* Allocate tuple space if first time for this data message */
-       if (conn->curTuple == NULL)
-       {
-               conn->curTuple = (PGresAttValue *)
-                       pqResultAlloc(result, nfields * sizeof(PGresAttValue), TRUE);
-               if (conn->curTuple == NULL)
-                       goto outOfMemory;
-               MemSet((char *) conn->curTuple, 0, nfields * sizeof(PGresAttValue));
-       }
-       tup = conn->curTuple;
-
-       /* Get the null-value bitmap */
-       nbytes = (nfields + BYTELEN - 1) / BYTELEN;
-       /* malloc() only for unusually large field counts... */
-       if (nbytes > sizeof(std_bitmap))
-               bitmap = (char *) malloc(nbytes);
+       /*
+        * Accept any available input data, ignoring errors.  Note that if
+        * pqReadData decides the backend has closed the channel, it will close
+        * our side of the socket --- that's just what we want here.
+        */
+       while (pqReadData(conn) > 0)
+                /* loop until no more data readable */ ;
 
-       if (pqGetnchar(bitmap, nbytes, conn))
-               goto EOFexit;
+       /*
+        * Parse any available input messages.  Since we are in PGASYNC_IDLE
+        * state, only NOTICE and NOTIFY messages will be eaten.
+        */
+       parseInput(conn);
+}
 
-       /* Scan the fields */
-       bitmap_index = 0;
-       bmap = bitmap[bitmap_index];
-       bitcnt = 0;
+/*
+ * Consume any available input from the backend
+ * 0 return: some kind of trouble
+ * 1 return: no problem
+ */
+int
+PQconsumeInput(PGconn *conn)
+{
+       if (!conn)
+               return 0;
 
-       for (i = 0; i < nfields; i++)
+       /*
+        * for non-blocking connections try to flush the send-queue, otherwise we
+        * may never get a response for something that may not have already been
+        * sent because it's in our write buffer!
+        */
+       if (pqIsnonblocking(conn))
        {
-               if (!(bmap & 0200))
-               {
-                       /* if the field value is absent, make it a null string */
-                       tup[i].value = result->null_field;
-                       tup[i].len = NULL_LEN;
-               }
-               else
-               {
-                       /* get the value length (the first four bytes are for length) */
-                       if (pqGetInt(&vlen, 4, conn))
-                               goto EOFexit;
-                       if (binary == 0)
-                               vlen = vlen - 4;
-                       if (vlen < 0)
-                               vlen = 0;
-                       if (tup[i].value == NULL)
-                       {
-                               tup[i].value = (char *) pqResultAlloc(result, vlen + 1, binary);
-                               if (tup[i].value == NULL)
-                                       goto outOfMemory;
-                       }
-                       tup[i].len = vlen;
-                       /* read in the value */
-                       if (vlen > 0)
-                               if (pqGetnchar((char *) (tup[i].value), vlen, conn))
-                                       goto EOFexit;
-                       /* we have to terminate this ourselves */
-                       tup[i].value[vlen] = '\0';
-               }
-               /* advance the bitmap stuff */
-               bitcnt++;
-               if (bitcnt == BYTELEN)
-               {
-                       bitmap_index++;
-                       bmap = bitmap[bitmap_index];
-                       bitcnt = 0;
-               }
-               else
-                       bmap <<= 1;
+               if (pqFlush(conn) < 0)
+                       return 0;
        }
 
-       /* Success!  Store the completed tuple in the result */
-       if (!addTuple(result, tup))
-               goto outOfMemory;
-       /* and reset for a new message */
-       conn->curTuple = NULL;
-
-       if (bitmap != std_bitmap)
-               free(bitmap);
-       return 0;
-
-outOfMemory:
-       /* Replace partially constructed result with an error result */
-
        /*
-        * we do NOT use saveErrorResult() here, because of the likelihood
-        * that there's not enough memory to concatenate messages...
+        * Load more data, if available. We do this no matter what state we are
+        * in, since we are probably getting called because the application wants
+        * to get rid of a read-select condition. Note that we will NOT block
+        * waiting for more input.
         */
-       pqClearAsyncResult(conn);
-       printfPQExpBuffer(&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;
+       if (pqReadData(conn) < 0)
+               return 0;
 
-EOFexit:
-       if (bitmap != std_bitmap)
-               free(bitmap);
-       return EOF;
+       /* Parsing of the data waits till later. */
+       return 1;
 }
 
 
+/*
+ * parseInput: if appropriate, parse input data from backend
+ * until input is exhausted or a stopping state is reached.
+ * Note that this function will NOT attempt to read more data from the backend.
+ */
+static void
+parseInput(PGconn *conn)
+{
+       if (PG_PROTOCOL_MAJOR(conn->pversion) >= 3)
+               pqParseInput3(conn);
+       else
+               pqParseInput2(conn);
+}
+
 /*
  * PQisBusy
  *      Return TRUE if PQgetResult would block waiting for input.
@@ -1104,11 +1182,12 @@ 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   *
+PGresult *
 PQgetResult(PGconn *conn)
 {
        PGresult   *res;
@@ -1122,19 +1201,35 @@ PQgetResult(PGconn *conn)
        /* If not ready to return something, block until we are. */
        while (conn->asyncStatus == PGASYNC_BUSY)
        {
+               int                     flushResult;
+
+               /*
+                * If data remains unsent, send it.  Else we might be waiting for the
+                * result of a command the backend hasn't even got yet.
+                */
+               while ((flushResult = pqFlush(conn)) > 0)
+               {
+                       if (pqWait(FALSE, TRUE, conn))
+                       {
+                               flushResult = -1;
+                               break;
+                       }
+               }
+
                /* Wait for some more data, and load it. */
-               if (pqWait(TRUE, FALSE, conn) ||
+               if (flushResult ||
+                       pqWait(TRUE, FALSE, conn) ||
                        pqReadData(conn) < 0)
                {
-
                        /*
                         * conn->errorMessage has been set by pqWait or pqReadData. We
                         * want to append it to any already-received error message.
                         */
-                       saveErrorResult(conn);
+                       pqSaveErrorResult(conn);
                        conn->asyncStatus = PGASYNC_IDLE;
-                       return prepareAsyncResult(conn);
+                       return pqPrepareAsyncResult(conn);
                }
+
                /* Parse it. */
                parseInput(conn);
        }
@@ -1146,19 +1241,25 @@ PQgetResult(PGconn *conn)
                        res = NULL;                     /* query is complete */
                        break;
                case PGASYNC_READY:
-                       res = prepareAsyncResult(conn);
+                       res = pqPrepareAsyncResult(conn);
                        /* Set the state back to BUSY, allowing parsing to proceed. */
                        conn->asyncStatus = PGASYNC_BUSY;
                        break;
                case PGASYNC_COPY_IN:
-                       res = PQmakeEmptyPGresult(conn, PGRES_COPY_IN);
+                       if (conn->result && conn->result->resultStatus == PGRES_COPY_IN)
+                               res = pqPrepareAsyncResult(conn);
+                       else
+                               res = PQmakeEmptyPGresult(conn, PGRES_COPY_IN);
                        break;
                case PGASYNC_COPY_OUT:
-                       res = PQmakeEmptyPGresult(conn, PGRES_COPY_OUT);
+                       if (conn->result && conn->result->resultStatus == PGRES_COPY_OUT)
+                               res = pqPrepareAsyncResult(conn);
+                       else
+                               res = PQmakeEmptyPGresult(conn, PGRES_COPY_OUT);
                        break;
                default:
                        printfPQExpBuffer(&conn->errorMessage,
-                                                         "PQgetResult: Unexpected asyncStatus %d\n",
+                                                         libpq_gettext("unexpected asyncStatus: %d\n"),
                                                          (int) conn->asyncStatus);
                        res = PQmakeEmptyPGresult(conn, PGRES_FATAL_ERROR);
                        break;
@@ -1179,53 +1280,170 @@ PQgetResult(PGconn *conn)
  * The user is responsible for freeing the PGresult via PQclear()
  * when done with it.
  */
-
-PGresult   *
+PGresult *
 PQexec(PGconn *conn, const char *query)
 {
-       PGresult   *result;
-       PGresult   *lastResult;
-       bool            savedblocking;
+       if (!PQexecStart(conn))
+               return NULL;
+       if (!PQsendQuery(conn, query))
+               return NULL;
+       return PQexecFinish(conn);
+}
 
-       /*
-        * we assume anyone calling PQexec wants blocking behaviour, we force
-        * the blocking status of the connection to blocking for the duration
-        * of this function and restore it on return
-        */
-       savedblocking = pqIsnonblocking(conn);
-       if (PQsetnonblocking(conn, FALSE) == -1)
+/*
+ * PQexecParams
+ *             Like PQexec, but use protocol 3.0 so we can pass parameters
+ */
+PGresult *
+PQexecParams(PGconn *conn,
+                        const char *command,
+                        int nParams,
+                        const Oid *paramTypes,
+                        const char *const * paramValues,
+                        const int *paramLengths,
+                        const int *paramFormats,
+                        int resultFormat)
+{
+       if (!PQexecStart(conn))
+               return NULL;
+       if (!PQsendQueryParams(conn, command,
+                                                  nParams, paramTypes, paramValues, paramLengths,
+                                                  paramFormats, resultFormat))
                return NULL;
+       return PQexecFinish(conn);
+}
+
+/*
+ * PQprepare
+ *       Creates a prepared statement by issuing a v3.0 parse message.
+ *
+ * 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 *
+PQprepare(PGconn *conn,
+                 const char *stmtName, const char *query,
+                 int nParams, const Oid *paramTypes)
+{
+       if (!PQexecStart(conn))
+               return NULL;
+       if (!PQsendPrepare(conn, stmtName, query, nParams, paramTypes))
+               return NULL;
+       return PQexecFinish(conn);
+}
+
+/*
+ * PQexecPrepared
+ *             Like PQexec, but execute a previously prepared statement,
+ *             using protocol 3.0 so we can pass parameters
+ */
+PGresult *
+PQexecPrepared(PGconn *conn,
+                          const char *stmtName,
+                          int nParams,
+                          const char *const * paramValues,
+                          const int *paramLengths,
+                          const int *paramFormats,
+                          int resultFormat)
+{
+       if (!PQexecStart(conn))
+               return NULL;
+       if (!PQsendQueryPrepared(conn, stmtName,
+                                                        nParams, paramValues, paramLengths,
+                                                        paramFormats, resultFormat))
+               return NULL;
+       return PQexecFinish(conn);
+}
+
+/*
+ * Common code for PQexec and sibling routines: prepare to send command
+ */
+static bool
+PQexecStart(PGconn *conn)
+{
+       PGresult   *result;
+
+       if (!conn)
+               return false;
 
        /*
-        * Silently discard any prior query result that application didn't
-        * eat. This is probably poor design, but it's here for backward
-        * compatibility.
+        * Silently discard any prior query result that application didn't eat.
+        * This is probably poor design, but it's here for backward compatibility.
         */
        while ((result = PQgetResult(conn)) != NULL)
        {
-               if (result->resultStatus == PGRES_COPY_IN ||
-                       result->resultStatus == PGRES_COPY_OUT)
+               ExecStatusType resultStatus = result->resultStatus;
+
+               PQclear(result);                /* only need its status */
+               if (resultStatus == PGRES_COPY_IN)
                {
-                       PQclear(result);
-                       printfPQExpBuffer(&conn->errorMessage,
-                               "PQexec: you gotta get out of a COPY state yourself.\n");
-                       /* restore blocking status */
-                       goto errout;
+                       if (PG_PROTOCOL_MAJOR(conn->pversion) >= 3)
+                       {
+                               /* In protocol 3, we can get out of a COPY IN state */
+                               if (PQputCopyEnd(conn,
+                                                libpq_gettext("COPY terminated by new PQexec")) < 0)
+                                       return false;
+                               /* keep waiting to swallow the copy's failure message */
+                       }
+                       else
+                       {
+                               /* In older protocols we have to punt */
+                               printfPQExpBuffer(&conn->errorMessage,
+                                 libpq_gettext("COPY IN state must be terminated first\n"));
+                               return false;
+                       }
+               }
+               else if (resultStatus == PGRES_COPY_OUT)
+               {
+                       if (PG_PROTOCOL_MAJOR(conn->pversion) >= 3)
+                       {
+                               /*
+                                * In protocol 3, we can get out of a COPY OUT state: we just
+                                * switch back to BUSY and allow the remaining COPY data to be
+                                * dropped on the floor.
+                                */
+                               conn->asyncStatus = PGASYNC_BUSY;
+                               /* keep waiting to swallow the copy's completion message */
+                       }
+                       else
+                       {
+                               /* In older protocols we have to punt */
+                               printfPQExpBuffer(&conn->errorMessage,
+                                libpq_gettext("COPY OUT state must be terminated first\n"));
+                               return false;
+                       }
                }
-               PQclear(result);
+               /* check for loss of connection, too */
+               if (conn->status == CONNECTION_BAD)
+                       return false;
        }
 
-       /* OK to send the message */
-       if (!PQsendQuery(conn, query))
-               goto errout;                    /* restore blocking status */
+       /* OK to send a command */
+       return true;
+}
+
+/*
+ * Common code for PQexec and sibling routines: wait for command result
+ */
+static PGresult *
+PQexecFinish(PGconn *conn)
+{
+       PGresult   *result;
+       PGresult   *lastResult;
 
        /*
-        * For backwards compatibility, return the last result if there are
-        * more than one --- but merge error messages if we get more than one
-        * error result.
+        * For backwards compatibility, return the last result if there are more
+        * than one --- but merge error messages if we get more than one error
+        * result.
         *
         * We have to stop if we see copy in/out, however. We will resume parsing
-        * when application calls PQendcopy.
+        * after application performs the data transfer.
+        *
+        * Also stop if the connection is lost (else we'll loop infinitely).
         */
        lastResult = NULL;
        while ((result = PQgetResult(conn)) != NULL)
@@ -1238,113 +1456,362 @@ PQexec(PGconn *conn, const char *query)
                                pqCatenateResultError(lastResult, result->errMsg);
                                PQclear(result);
                                result = lastResult;
-                               /* Make sure PQerrorMessage agrees with catenated result */
+
+                               /*
+                                * Make sure PQerrorMessage agrees with concatenated result
+                                */
                                resetPQExpBuffer(&conn->errorMessage);
                                appendPQExpBufferStr(&conn->errorMessage, result->errMsg);
                        }
                        else
                                PQclear(lastResult);
                }
-               lastResult = result;
-               if (result->resultStatus == PGRES_COPY_IN ||
-                       result->resultStatus == PGRES_COPY_OUT)
-                       break;
+               lastResult = result;
+               if (result->resultStatus == PGRES_COPY_IN ||
+                       result->resultStatus == PGRES_COPY_OUT ||
+                       conn->status == CONNECTION_BAD)
+                       break;
+       }
+
+       return lastResult;
+}
+
+/*
+ * PQdescribePrepared
+ *       Obtain information about a previously prepared statement
+ *
+ * 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). On success, the PGresult contains status
+ * PGRES_COMMAND_OK, and its parameter and column-heading fields describe
+ * the statement's inputs and outputs respectively.
+ * The user is responsible for freeing the PGresult via PQclear()
+ * when done with it.
+ */
+PGresult *
+PQdescribePrepared(PGconn *conn, const char *stmt)
+{
+       if (!PQexecStart(conn))
+               return NULL;
+       if (!PQsendDescribe(conn, 'S', stmt))
+               return NULL;
+       return PQexecFinish(conn);
+}
+
+/*
+ * PQdescribePortal
+ *       Obtain information about a previously created portal
+ *
+ * This is much like PQdescribePrepared, except that no parameter info is
+ * returned.  Note that at the moment, libpq doesn't really expose portals
+ * to the client; but this can be used with a portal created by a SQL
+ * DECLARE CURSOR command.
+ */
+PGresult *
+PQdescribePortal(PGconn *conn, const char *portal)
+{
+       if (!PQexecStart(conn))
+               return NULL;
+       if (!PQsendDescribe(conn, 'P', portal))
+               return NULL;
+       return PQexecFinish(conn);
+}
+
+/*
+ * PQsendDescribePrepared
+ *      Submit a Describe Statement command, but don't wait for it to finish
+ *
+ * Returns: 1 if successfully submitted
+ *                     0 if error (conn->errorMessage is set)
+ */
+int
+PQsendDescribePrepared(PGconn *conn, const char *stmt)
+{
+       return PQsendDescribe(conn, 'S', stmt);
+}
+
+/*
+ * PQsendDescribePortal
+ *      Submit a Describe Portal command, but don't wait for it to finish
+ *
+ * Returns: 1 if successfully submitted
+ *                     0 if error (conn->errorMessage is set)
+ */
+int
+PQsendDescribePortal(PGconn *conn, const char *portal)
+{
+       return PQsendDescribe(conn, 'P', portal);
+}
+
+/*
+ * PQsendDescribe
+ *      Common code to send a Describe command
+ *
+ * Available options for desc_type are
+ *      'S' to describe a prepared statement; or
+ *      'P' to describe a portal.
+ * Returns 1 on success and 0 on failure.
+ */
+static int
+PQsendDescribe(PGconn *conn, char desc_type, const char *desc_target)
+{
+       /* Treat null desc_target as empty string */
+       if (!desc_target)
+               desc_target = "";
+
+       if (!PQsendQueryStart(conn))
+               return 0;
+
+       /* This isn't gonna work on a 2.0 server */
+       if (PG_PROTOCOL_MAJOR(conn->pversion) < 3)
+       {
+               printfPQExpBuffer(&conn->errorMessage,
+                libpq_gettext("function requires at least protocol version 3.0\n"));
+               return 0;
+       }
+
+       /* construct the Describe message */
+       if (pqPutMsgStart('D', false, conn) < 0 ||
+               pqPutc(desc_type, conn) < 0 ||
+               pqPuts(desc_target, conn) < 0 ||
+               pqPutMsgEnd(conn) < 0)
+               goto sendFailed;
+
+       /* construct the Sync message */
+       if (pqPutMsgStart('S', false, conn) < 0 ||
+               pqPutMsgEnd(conn) < 0)
+               goto sendFailed;
+
+       /* remember we are doing a Describe */
+       conn->queryclass = PGQUERY_DESCRIBE;
+
+       /* reset last-query string (not relevant now) */
+       if (conn->last_query)
+       {
+               free(conn->last_query);
+               conn->last_query = NULL;
+       }
+
+       /*
+        * Give the data a push.  In nonblock mode, don't complain if we're unable
+        * to send it all; PQgetResult() will do any additional flushing needed.
+        */
+       if (pqFlush(conn) < 0)
+               goto sendFailed;
+
+       /* OK, it's launched! */
+       conn->asyncStatus = PGASYNC_BUSY;
+       return 1;
+
+sendFailed:
+       pqHandleSendFailure(conn);
+       return 0;
+}
+
+/*
+ * PQnotifies
+ *       returns a PGnotify* structure of the latest async notification
+ * that has not yet been handled
+ *
+ * returns NULL, if there is currently
+ * no unhandled async notification from the backend
+ *
+ * the CALLER is responsible for FREE'ing the structure returned
+ */
+PGnotify *
+PQnotifies(PGconn *conn)
+{
+       PGnotify   *event;
+
+       if (!conn)
+               return NULL;
+
+       /* Parse any available data to see if we can extract NOTIFY messages. */
+       parseInput(conn);
+
+       event = conn->notifyHead;
+       if (event)
+       {
+               conn->notifyHead = event->next;
+               if (!conn->notifyHead)
+                       conn->notifyTail = NULL;
+               event->next = NULL;             /* don't let app see the internal state */
+       }
+       return event;
+}
+
+/*
+ * PQputCopyData - send some data to the backend during COPY IN
+ *
+ * Returns 1 if successful, 0 if data could not be sent (only possible
+ * in nonblock mode), or -1 if an error occurs.
+ */
+int
+PQputCopyData(PGconn *conn, const char *buffer, int nbytes)
+{
+       if (!conn)
+               return -1;
+       if (conn->asyncStatus != PGASYNC_COPY_IN)
+       {
+               printfPQExpBuffer(&conn->errorMessage,
+                                                 libpq_gettext("no COPY in progress\n"));
+               return -1;
+       }
+
+       /*
+        * Process any NOTICE or NOTIFY messages that might be pending in the
+        * input buffer.  Since the server might generate many notices during the
+        * COPY, we want to clean those out reasonably promptly to prevent
+        * indefinite expansion of the input buffer.  (Note: the actual read of
+        * input data into the input buffer happens down inside pqSendSome, but
+        * it's not authorized to get rid of the data again.)
+        */
+       parseInput(conn);
+
+       if (nbytes > 0)
+       {
+               /*
+                * Try to flush any previously sent data in preference to growing the
+                * output buffer.  If we can't enlarge the buffer enough to hold the
+                * data, return 0 in the nonblock case, else hard error. (For
+                * simplicity, always assume 5 bytes of overhead even in protocol 2.0
+                * case.)
+                */
+               if ((conn->outBufSize - conn->outCount - 5) < nbytes)
+               {
+                       if (pqFlush(conn) < 0)
+                               return -1;
+                       if (pqCheckOutBufferSpace(conn->outCount + 5 + nbytes, conn))
+                               return pqIsnonblocking(conn) ? 0 : -1;
+               }
+               /* Send the data (too simple to delegate to fe-protocol files) */
+               if (PG_PROTOCOL_MAJOR(conn->pversion) >= 3)
+               {
+                       if (pqPutMsgStart('d', false, conn) < 0 ||
+                               pqPutnchar(buffer, nbytes, conn) < 0 ||
+                               pqPutMsgEnd(conn) < 0)
+                               return -1;
+               }
+               else
+               {
+                       if (pqPutMsgStart(0, false, conn) < 0 ||
+                               pqPutnchar(buffer, nbytes, conn) < 0 ||
+                               pqPutMsgEnd(conn) < 0)
+                               return -1;
+               }
        }
-
-       if (PQsetnonblocking(conn, savedblocking) == -1)
-               return NULL;
-       return lastResult;
-
-errout:
-       if (PQsetnonblocking(conn, savedblocking) == -1)
-               return NULL;
-       return NULL;
+       return 1;
 }
 
-
 /*
- * Attempt to read a Notice response message.
- * This is possible in several places, so we break it out as a subroutine.
- * Entry: 'N' flag character has already been consumed.
- * Exit: returns 0 if successfully consumed Notice message.
- *              returns EOF if not enough data.
+ * PQputCopyEnd - send EOF indication to the backend during COPY IN
+ *
+ * After calling this, use PQgetResult() to check command completion status.
+ *
+ * Returns 1 if successful, 0 if data could not be sent (only possible
+ * in nonblock mode), or -1 if an error occurs.
  */
-static int
-getNotice(PGconn *conn)
+int
+PQputCopyEnd(PGconn *conn, const char *errormsg)
 {
+       if (!conn)
+               return -1;
+       if (conn->asyncStatus != PGASYNC_COPY_IN)
+       {
+               printfPQExpBuffer(&conn->errorMessage,
+                                                 libpq_gettext("no COPY in progress\n"));
+               return -1;
+       }
 
        /*
-        * Since the Notice might be pretty long, we create a temporary
-        * PQExpBuffer rather than using conn->workBuffer.      workBuffer is
-        * intended for stuff that is expected to be short.
+        * Send the COPY END indicator.  This is simple enough that we don't
+        * bother delegating it to the fe-protocol files.
         */
-       PQExpBufferData noticeBuf;
+       if (PG_PROTOCOL_MAJOR(conn->pversion) >= 3)
+       {
+               if (errormsg)
+               {
+                       /* Send COPY FAIL */
+                       if (pqPutMsgStart('f', false, conn) < 0 ||
+                               pqPuts(errormsg, conn) < 0 ||
+                               pqPutMsgEnd(conn) < 0)
+                               return -1;
+               }
+               else
+               {
+                       /* Send COPY DONE */
+                       if (pqPutMsgStart('c', false, conn) < 0 ||
+                               pqPutMsgEnd(conn) < 0)
+                               return -1;
+               }
 
-       initPQExpBuffer(&noticeBuf);
-       if (pqGets(&noticeBuf, conn))
+               /*
+                * If we sent the COPY command in extended-query mode, we must issue a
+                * Sync as well.
+                */
+               if (conn->queryclass != PGQUERY_SIMPLE)
+               {
+                       if (pqPutMsgStart('S', false, conn) < 0 ||
+                               pqPutMsgEnd(conn) < 0)
+                               return -1;
+               }
+       }
+       else
        {
-               termPQExpBuffer(&noticeBuf);
-               return EOF;
+               if (errormsg)
+               {
+                       /* Ooops, no way to do this in 2.0 */
+                       printfPQExpBuffer(&conn->errorMessage,
+                                                         libpq_gettext("function requires at least protocol version 3.0\n"));
+                       return -1;
+               }
+               else
+               {
+                       /* Send old-style end-of-data marker */
+                       if (pqPutMsgStart(0, false, conn) < 0 ||
+                               pqPutnchar("\\.\n", 3, conn) < 0 ||
+                               pqPutMsgEnd(conn) < 0)
+                               return -1;
+               }
        }
-       DONOTICE(conn, noticeBuf.data);
-       termPQExpBuffer(&noticeBuf);
-       return 0;
-}
 
-/*
- * Attempt to read a Notify response message.
- * This is possible in several places, so we break it out as a subroutine.
- * Entry: 'A' flag character has already been consumed.
- * Exit: returns 0 if successfully consumed Notify message.
- *              returns EOF if not enough data.
- */
-static int
-getNotify(PGconn *conn)
-{
-       int                     be_pid;
-       PGnotify   *newNotify;
+       /* Return to active duty */
+       conn->asyncStatus = PGASYNC_BUSY;
+       resetPQExpBuffer(&conn->errorMessage);
 
-       if (pqGetInt(&be_pid, 4, conn))
-               return EOF;
-       if (pqGets(&conn->workBuffer, conn))
-               return EOF;
-       newNotify = (PGnotify *) malloc(sizeof(PGnotify));
-       strncpy(newNotify->relname, conn->workBuffer.data, NAMEDATALEN);
-       newNotify->be_pid = be_pid;
-       DLAddTail(conn->notifyList, DLNewElem(newNotify));
-       return 0;
+       /* Try to flush data */
+       if (pqFlush(conn) < 0)
+               return -1;
+
+       return 1;
 }
 
 /*
- * PQnotifies
- *       returns a PGnotify* structure of the latest async notification
- * that has not yet been handled
- *
- * returns NULL, if there is currently
- * no unhandled async notification from the backend
+ * PQgetCopyData - read a row of data from the backend during COPY OUT
  *
- * the CALLER is responsible for FREE'ing the structure returned
+ * If successful, sets *buffer to point to a malloc'd row of data, and
+ * returns row length (always > 0) as result.
+ * Returns 0 if no row available yet (only possible if async is true),
+ * -1 if end of copy (consult PQgetResult), or -2 if error (consult
+ * PQerrorMessage).
  */
-
-PGnotify   *
-PQnotifies(PGconn *conn)
+int
+PQgetCopyData(PGconn *conn, char **buffer, int async)
 {
-       Dlelem     *e;
-       PGnotify   *event;
-
+       *buffer = NULL;                         /* for all failure cases */
        if (!conn)
-               return NULL;
-
-       /* Parse any available data to see if we can extract NOTIFY messages. */
-       parseInput(conn);
-
-       /* RemHead returns NULL if list is empty */
-       e = DLRemHead(conn->notifyList);
-       if (!e)
-               return NULL;
-       event = (PGnotify *) DLE_VAL(e);
-       DLFreeElem(e);
-       return event;
+               return -2;
+       if (conn->asyncStatus != PGASYNC_COPY_OUT)
+       {
+               printfPQExpBuffer(&conn->errorMessage,
+                                                 libpq_gettext("no COPY in progress\n"));
+               return -2;
+       }
+       if (PG_PROTOCOL_MAJOR(conn->pversion) >= 3)
+               return pqGetCopyData3(conn, buffer, async);
+       else
+               return pqGetCopyData2(conn, buffer, async);
 }
 
 /*
@@ -1353,6 +1820,9 @@ PQnotifies(PGconn *conn)
  * Chiefly here so that applications can use "COPY <rel> to stdout"
  * and read the output string. Returns a null-terminated string in s.
  *
+ * XXX this routine is now deprecated, because it can't handle binary data.
+ * If called during a COPY BINARY we return EOF.
+ *
  * PQgetline reads up to maxlen-1 characters (like fgets(3)) but strips
  * the terminating \n (like gets(3)).
  *
@@ -1360,7 +1830,7 @@ PQnotifies(PGconn *conn)
  * (a line containing just "\.") when using this routine.
  *
  * RETURNS:
- *             EOF if it is detected or invalid arguments are given
+ *             EOF if error (eg, invalid arguments are given)
  *             0 if EOL is reached (i.e., \n has been read)
  *                             (this is required for backward-compatibility -- this
  *                              routine used to always return EOF or 0, assuming that
@@ -1370,53 +1840,24 @@ PQnotifies(PGconn *conn)
 int
 PQgetline(PGconn *conn, char *s, int maxlen)
 {
-       int                     result = 1;             /* return value if buffer overflows */
-
        if (!s || maxlen <= 0)
                return EOF;
-
-       if (!conn || conn->sock < 0)
-       {
-               *s = '\0';
+       *s = '\0';
+       /* maxlen must be at least 3 to hold the \. terminator! */
+       if (maxlen < 3)
                return EOF;
-       }
 
-       /*
-        * Since this is a purely synchronous routine, we don't bother to
-        * maintain conn->inCursor; there is no need to back up.
-        */
-       while (maxlen > 1)
-       {
-               if (conn->inStart < conn->inEnd)
-               {
-                       char            c = conn->inBuffer[conn->inStart++];
-
-                       if (c == '\n')
-                       {
-                               result = 0;             /* success exit */
-                               break;
-                       }
-                       *s++ = c;
-                       maxlen--;
-               }
-               else
-               {
-                       /* need to load more data */
-                       if (pqWait(TRUE, FALSE, conn) ||
-                               pqReadData(conn) < 0)
-                       {
-                               result = EOF;
-                               break;
-                       }
-               }
-       }
-       *s = '\0';
+       if (!conn)
+               return EOF;
 
-       return result;
+       if (PG_PROTOCOL_MAJOR(conn->pversion) >= 3)
+               return pqGetline3(conn, s, maxlen);
+       else
+               return pqGetline2(conn, s, maxlen);
 }
 
 /*
- * PQgetlineAsync - gets a newline-terminated string without blocking.
+ * PQgetlineAsync - gets a COPY data row without blocking.
  *
  * This routine is for applications that want to do "COPY <rel> to stdout"
  * asynchronously, that is without blocking.  Having issued the COPY command
@@ -1424,10 +1865,9 @@ PQgetline(PGconn *conn, char *s, int maxlen)
  * and this routine until the end-of-data signal is detected.  Unlike
  * PQgetline, this routine takes responsibility for detecting end-of-data.
  *
- * On each call, PQgetlineAsync will return data if a complete newline-
- * terminated data line is available in libpq's input buffer, or if the
- * incoming data line is too long to fit in the buffer offered by the caller.
- * Otherwise, no data is returned until the rest of the line arrives.
+ * On each call, PQgetlineAsync will return data if a complete data row
+ * is available in libpq's input buffer.  Otherwise, no data is returned
+ * until the rest of the row arrives.
  *
  * If -1 is returned, the end-of-data signal has been recognized (and removed
  * from libpq's input buffer).  The caller *must* next call PQendcopy and
@@ -1437,80 +1877,40 @@ PQgetline(PGconn *conn, char *s, int maxlen)
  *      -1    if the end-of-copy-data marker has been recognized
  *      0         if no data is available
  *      >0    the number of bytes returned.
- * The data returned will not extend beyond a newline character.  If possible
- * a whole line will be returned at one time.  But if the buffer offered by
- * the caller is too small to hold a line sent by the backend, then a partial
- * data line will be returned. This can be detected by testing whether the
- * last returned byte is '\n' or not.
- * The returned string is *not* null-terminated.
+ *
+ * The data returned will not extend beyond a data-row boundary.  If possible
+ * a whole row will be returned at one time.  But if the buffer offered by
+ * the caller is too small to hold a row sent by the backend, then a partial
+ * data row will be returned.  In text mode this can be detected by testing
+ * whether the last returned byte is '\n' or not.
+ *
+ * The returned data is *not* null-terminated.
  */
 
 int
 PQgetlineAsync(PGconn *conn, char *buffer, int bufsize)
 {
-       int                     avail;
-
-       if (!conn || conn->asyncStatus != PGASYNC_COPY_OUT)
-               return -1;                              /* we are not doing a copy... */
-
-       /*
-        * Move data from libpq's buffer to the caller's. We want to accept
-        * data only in units of whole lines, not partial lines.  This ensures
-        * that we can recognize the terminator line "\\.\n".  (Otherwise, if
-        * it happened to cross a packet/buffer boundary, we might hand the
-        * first one or two characters off to the caller, which we shouldn't.)
-        */
-
-       conn->inCursor = conn->inStart;
-
-       avail = bufsize;
-       while (avail > 0 && conn->inCursor < conn->inEnd)
-       {
-               char            c = conn->inBuffer[conn->inCursor++];
-
-               *buffer++ = c;
-               --avail;
-               if (c == '\n')
-               {
-                       /* Got a complete line; mark the data removed from libpq */
-                       conn->inStart = conn->inCursor;
-                       /* Is it the endmarker line? */
-                       if (bufsize - avail == 3 && buffer[-3] == '\\' && buffer[-2] == '.')
-                               return -1;
-                       /* No, return the data line to the caller */
-                       return bufsize - avail;
-               }
-       }
+       if (!conn)
+               return -1;
 
-       /*
-        * We don't have a complete line. We'd prefer to leave it in libpq's
-        * buffer until the rest arrives, but there is a special case: what if
-        * the line is longer than the buffer the caller is offering us?  In
-        * that case we'd better hand over a partial line, else we'd get into
-        * an infinite loop. Do this in a way that ensures we can't
-        * misrecognize a terminator line later: leave last 3 characters in
-        * libpq buffer.
-        */
-       if (avail == 0 && bufsize > 3)
-       {
-               conn->inStart = conn->inCursor - 3;
-               return bufsize - 3;
-       }
-       return 0;
+       if (PG_PROTOCOL_MAJOR(conn->pversion) >= 3)
+               return pqGetlineAsync3(conn, buffer, bufsize);
+       else
+               return pqGetlineAsync2(conn, buffer, bufsize);
 }
 
 /*
- * PQputline -- sends a string to the backend.
+ * PQputline -- sends a string to the backend during COPY IN.
  * Returns 0 if OK, EOF if not.
  *
- * Chiefly here so that applications can use "COPY <rel> from stdin".
+ * This is deprecated primarily because the return convention doesn't allow
+ * caller to tell the difference between a hard error and a nonblock-mode
+ * send failure.
  */
 int
 PQputline(PGconn *conn, const char *s)
 {
-       if (!conn || conn->sock < 0)
-               return EOF;
-       return pqPutnchar(s, strlen(s), conn);
+       return PQputnbytes(conn, s, strlen(s));
 }
 
 /*
@@ -1520,9 +1920,10 @@ PQputline(PGconn *conn, const char *s)
 int
 PQputnbytes(PGconn *conn, const char *buffer, int nbytes)
 {
-       if (!conn || conn->sock < 0)
+       if (PQputCopyData(conn, buffer, nbytes) > 0)
+               return 0;
+       else
                return EOF;
-       return pqPutnchar(buffer, nbytes, conn);
 }
 
 /*
@@ -1530,6 +1931,11 @@ PQputnbytes(PGconn *conn, const char *buffer, int nbytes)
  *             After completing the data transfer portion of a copy in/out,
  *             the application must call this routine to finish the command protocol.
  *
+ * When using protocol 3.0 this is deprecated; it's cleaner to use PQgetResult
+ * to get the transfer status. Note however that when using 2.0 protocol,
+ * recovering from a copy failure often requires a PQreset.  PQendcopy will
+ * take care of that, PQgetResult won't.
+ *
  * RETURNS:
  *             0 on success
  *             1 on failure
@@ -1537,67 +1943,13 @@ PQputnbytes(PGconn *conn, const char *buffer, int nbytes)
 int
 PQendcopy(PGconn *conn)
 {
-       PGresult   *result;
-
        if (!conn)
                return 0;
 
-       if (conn->asyncStatus != PGASYNC_COPY_IN &&
-               conn->asyncStatus != PGASYNC_COPY_OUT)
-       {
-               printfPQExpBuffer(&conn->errorMessage,
-                  "PQendcopy() -- I don't think there's a copy in progress.\n");
-               return 1;
-       }
-
-       /*
-        * make sure no data is waiting to be sent, abort if we are
-        * non-blocking and the flush fails
-        */
-       if (pqFlush(conn) && pqIsnonblocking(conn))
-               return (1);
-
-       /* non blocking connections may have to abort at this point. */
-       if (pqIsnonblocking(conn) && PQisBusy(conn))
-               return (1);
-
-       /* Return to active duty */
-       conn->asyncStatus = PGASYNC_BUSY;
-       resetPQExpBuffer(&conn->errorMessage);
-
-       /* Wait for the completion response */
-       result = PQgetResult(conn);
-
-       /* Expecting a successful result */
-       if (result && result->resultStatus == PGRES_COMMAND_OK)
-       {
-               PQclear(result);
-               return 0;
-       }
-
-       /*
-        * Trouble. The worst case is that we've lost sync with the backend
-        * entirely due to application screwup of the copy in/out protocol. To
-        * recover, reset the connection (talk about using a sledgehammer...)
-        */
-       PQclear(result);
-
-       if (conn->errorMessage.len > 0)
-               DONOTICE(conn, conn->errorMessage.data);
-
-       DONOTICE(conn, "PQendcopy: resetting connection\n");
-
-       /*
-        * Users doing non-blocking connections need to handle the reset
-        * themselves, they'll need to check the connection status if we
-        * return an error.
-        */
-       if (pqIsnonblocking(conn))
-               PQresetStart(conn);
+       if (PG_PROTOCOL_MAJOR(conn->pversion) >= 3)
+               return pqEndcopy3(conn);
        else
-               PQreset(conn);
-
-       return 1;
+               return pqEndcopy2(conn);
 }
 
 
@@ -1624,7 +1976,7 @@ PQendcopy(PGconn *conn)
  * ----------------
  */
 
-PGresult   *
+PGresult *
 PQfn(PGconn *conn,
         int fnid,
         int *result_buf,
@@ -1633,11 +1985,6 @@ PQfn(PGconn *conn,
         const PQArgBlock *args,
         int nargs)
 {
-       bool            needInput = false;
-       ExecStatusType status = PGRES_FATAL_ERROR;
-       char            id;
-       int                     i;
-
        *actual_result_len = 0;
 
        if (!conn)
@@ -1650,160 +1997,20 @@ PQfn(PGconn *conn,
                conn->result != NULL)
        {
                printfPQExpBuffer(&conn->errorMessage,
-                                                 "PQfn() -- connection in wrong state\n");
-               return NULL;
-       }
-
-       if (pqPuts("F ", conn) ||       /* function */
-               pqPutInt(fnid, 4, conn) ||              /* function id */
-               pqPutInt(nargs, 4, conn))               /* # of args */
-       {
-               handleSendFailure(conn);
-               return NULL;
-       }
-
-       for (i = 0; i < nargs; ++i)
-       {                                                       /* len.int4 + contents     */
-               if (pqPutInt(args[i].len, 4, conn))
-               {
-                       handleSendFailure(conn);
-                       return NULL;
-               }
-
-               if (args[i].isint)
-               {
-                       if (pqPutInt(args[i].u.integer, 4, conn))
-                       {
-                               handleSendFailure(conn);
-                               return NULL;
-                       }
-               }
-               else
-               {
-                       if (pqPutnchar((char *) args[i].u.ptr, args[i].len, conn))
-                       {
-                               handleSendFailure(conn);
-                               return NULL;
-                       }
-               }
-       }
-       if (pqFlush(conn))
-       {
-               handleSendFailure(conn);
+                                                 libpq_gettext("connection in wrong state\n"));
                return NULL;
        }
 
-       for (;;)
-       {
-               if (needInput)
-               {
-                       /* Wait for some data to arrive (or for the channel to close) */
-                       if (pqWait(TRUE, FALSE, conn) ||
-                               pqReadData(conn) < 0)
-                               break;
-               }
-
-               /*
-                * Scan the message. If we run out of data, loop around to try
-                * again.
-                */
-               conn->inCursor = conn->inStart;
-               needInput = true;
-
-               if (pqGetc(&id, conn))
-                       continue;
-
-               /*
-                * We should see V or E response to the command, but might get N
-                * and/or A notices first. We also need to swallow the final Z
-                * before returning.
-                */
-               switch (id)
-               {
-                       case 'V':                       /* function result */
-                               if (pqGetc(&id, conn))
-                                       continue;
-                               if (id == 'G')
-                               {
-                                       /* function returned nonempty value */
-                                       if (pqGetInt(actual_result_len, 4, conn))
-                                               continue;
-                                       if (result_is_int)
-                                       {
-                                               if (pqGetInt(result_buf, 4, conn))
-                                                       continue;
-                                       }
-                                       else
-                                       {
-                                               if (pqGetnchar((char *) result_buf,
-                                                                          *actual_result_len,
-                                                                          conn))
-                                                       continue;
-                                       }
-                                       if (pqGetc(&id, conn))          /* get the last '0' */
-                                               continue;
-                               }
-                               if (id == '0')
-                               {
-                                       /* correctly finished function result message */
-                                       status = PGRES_COMMAND_OK;
-                               }
-                               else
-                               {
-                                       /* The backend violates the protocol. */
-                                       printfPQExpBuffer(&conn->errorMessage,
-                                                               "FATAL: PQfn: protocol error: id=0x%x\n",
-                                                                         id);
-                                       saveErrorResult(conn);
-                                       conn->inStart = conn->inCursor;
-                                       return prepareAsyncResult(conn);
-                               }
-                               break;
-                       case 'E':                       /* error return */
-                               if (pqGets(&conn->errorMessage, conn))
-                                       continue;
-                               /* build an error result holding the error message */
-                               saveErrorResult(conn);
-                               status = PGRES_FATAL_ERROR;
-                               break;
-                       case 'A':                       /* notify message */
-                               /* handle notify and go back to processing return values */
-                               if (getNotify(conn))
-                                       continue;
-                               break;
-                       case 'N':                       /* notice */
-                               /* handle notice and go back to processing return values */
-                               if (getNotice(conn))
-                                       continue;
-                               break;
-                       case 'Z':                       /* backend is ready for new query */
-                               /* consume the message and exit */
-                               conn->inStart = conn->inCursor;
-                               /* if we saved a result object (probably an error), use it */
-                               if (conn->result)
-                                       return prepareAsyncResult(conn);
-                               return PQmakeEmptyPGresult(conn, status);
-                       default:
-                               /* The backend violates the protocol. */
-                               printfPQExpBuffer(&conn->errorMessage,
-                                                               "FATAL: PQfn: protocol error: id=0x%x\n",
-                                                                 id);
-                               saveErrorResult(conn);
-                               conn->inStart = conn->inCursor;
-                               return prepareAsyncResult(conn);
-               }
-               /* Completed this message, keep going */
-               conn->inStart = conn->inCursor;
-               needInput = false;
-       }
-
-       /*
-        * We fall out of the loop only upon failing to read data.
-        * conn->errorMessage has been set by pqWait or pqReadData. We want to
-        * append it to any already-received error message.
-        */
-       saveErrorResult(conn);
-       return prepareAsyncResult(conn);
+       if (PG_PROTOCOL_MAJOR(conn->pversion) >= 3)
+               return pqFunctionCall3(conn, fnid,
+                                                          result_buf, actual_result_len,
+                                                          result_is_int,
+                                                          args, nargs);
+       else
+               return pqFunctionCall2(conn, fnid,
+                                                          result_buf, actual_result_len,
+                                                          result_is_int,
+                                                          args, nargs);
 }
 
 
@@ -1813,7 +2020,7 @@ ExecStatusType
 PQresultStatus(const PGresult *res)
 {
        if (!res)
-               return PGRES_NONFATAL_ERROR;
+               return PGRES_FATAL_ERROR;
        return res->resultStatus;
 }
 
@@ -1821,7 +2028,7 @@ char *
 PQresStatus(ExecStatusType status)
 {
        if (status < 0 || status >= sizeof pgresStatus / sizeof pgresStatus[0])
-               return "Invalid ExecStatusType code";
+               return libpq_gettext("invalid ExecStatusType code");
        return pgresStatus[status];
 }
 
@@ -1833,6 +2040,21 @@ PQresultErrorMessage(const PGresult *res)
        return res->errMsg;
 }
 
+char *
+PQresultErrorField(const PGresult *res, int fieldcode)
+{
+       PGMessageField *pfield;
+
+       if (!res)
+               return NULL;
+       for (pfield = res->errFields; pfield != NULL; pfield = pfield->next)
+       {
+               if (pfield->code == fieldcode)
+                       return pfield->contents;
+       }
+       return NULL;
+}
+
 int
 PQntuples(const PGresult *res)
 {
@@ -1863,66 +2085,66 @@ PQbinaryTuples(const PGresult *res)
  */
 
 static int
-check_field_number(const char *routineName, const PGresult *res, int field_num)
+check_field_number(const PGresult *res, int field_num)
 {
-       char            noticeBuf[128];
-
        if (!res)
                return FALSE;                   /* no way to display error message... */
        if (field_num < 0 || field_num >= res->numAttributes)
        {
-               if (res->noticeHook)
-               {
-                       sprintf(noticeBuf,
-                                       "%s: ERROR! field number %d is out of range 0..%d\n",
-                                       routineName, field_num, res->numAttributes - 1);
-                       DONOTICE(res, noticeBuf);
-               }
+               pqInternalNotice(&res->noticeHooks,
+                                                "column number %d is out of range 0..%d",
+                                                field_num, res->numAttributes - 1);
                return FALSE;
        }
        return TRUE;
 }
 
 static int
-check_tuple_field_number(const char *routineName, const PGresult *res,
+check_tuple_field_number(const PGresult *res,
                                                 int tup_num, int field_num)
 {
-       char            noticeBuf[128];
-
        if (!res)
                return FALSE;                   /* no way to display error message... */
        if (tup_num < 0 || tup_num >= res->ntups)
        {
-               if (res->noticeHook)
-               {
-                       sprintf(noticeBuf,
-                                       "%s: ERROR! tuple number %d is out of range 0..%d\n",
-                                       routineName, tup_num, res->ntups - 1);
-                       DONOTICE(res, noticeBuf);
-               }
+               pqInternalNotice(&res->noticeHooks,
+                                                "row number %d is out of range 0..%d",
+                                                tup_num, res->ntups - 1);
                return FALSE;
        }
        if (field_num < 0 || field_num >= res->numAttributes)
        {
-               if (res->noticeHook)
-               {
-                       sprintf(noticeBuf,
-                                       "%s: ERROR! field number %d is out of range 0..%d\n",
-                                       routineName, field_num, res->numAttributes - 1);
-                       DONOTICE(res, noticeBuf);
-               }
+               pqInternalNotice(&res->noticeHooks,
+                                                "column number %d is out of range 0..%d",
+                                                field_num, res->numAttributes - 1);
+               return FALSE;
+       }
+       return TRUE;
+}
+
+static int
+check_param_number(const PGresult *res, int param_num)
+{
+       if (!res)
+               return FALSE;                   /* no way to display error message... */
+       if (param_num < 0 || param_num >= res->numParameters)
+       {
+               pqInternalNotice(&res->noticeHooks,
+                                                "parameter number %d is out of range 0..%d",
+                                                param_num, res->numParameters - 1);
                return FALSE;
        }
+
        return TRUE;
 }
 
 /*
  returns NULL if the field_num is invalid
-*/
* returns NULL if the field_num is invalid
+ */
 char *
 PQfname(const PGresult *res, int field_num)
 {
-       if (!check_field_number("PQfname", res, field_num))
+       if (!check_field_number(res, field_num))
                return NULL;
        if (res->attDescs)
                return res->attDescs[field_num].name;
@@ -1931,33 +2153,77 @@ PQfname(const PGresult *res, int field_num)
 }
 
 /*
-   returns -1 on a bad field name
-*/
+ * PQfnumber: find column number given column name
+ *
+ * The column name is parsed as if it were in a SQL statement, including
+ * case-folding and double-quote processing.  But note a possible gotcha:
+ * downcasing in the frontend might follow different locale rules than
+ * downcasing in the backend...
+ *
+ * Returns -1 if no match.     In the present backend it is also possible
+ * to have multiple matches, in which case the first one is found.
+ */
 int
 PQfnumber(const PGresult *res, const char *field_name)
 {
-       int                     i;
        char       *field_case;
+       bool            in_quotes;
+       char       *iptr;
+       char       *optr;
+       int                     i;
 
        if (!res)
                return -1;
 
+       /*
+        * Note: it is correct to reject a zero-length input string; the proper
+        * input to match a zero-length field name would be "".
+        */
        if (field_name == NULL ||
                field_name[0] == '\0' ||
                res->attDescs == NULL)
                return -1;
 
+       /*
+        * Note: this code will not reject partially quoted strings, eg
+        * foo"BAR"foo will become fooBARfoo when it probably ought to be an error
+        * condition.
+        */
        field_case = strdup(field_name);
-       if (*field_case == '"')
+       if (field_case == NULL)
+               return -1;                              /* grotty */
+
+       in_quotes = false;
+       optr = field_case;
+       for (iptr = field_case; *iptr; iptr++)
        {
-               strcpy(field_case, field_case + 1);
-               *(field_case + strlen(field_case) - 1) = '\0';
+               char            c = *iptr;
+
+               if (in_quotes)
+               {
+                       if (c == '"')
+                       {
+                               if (iptr[1] == '"')
+                               {
+                                       /* doubled quotes become a single quote */
+                                       *optr++ = '"';
+                                       iptr++;
+                               }
+                               else
+                                       in_quotes = false;
+                       }
+                       else
+                               *optr++ = c;
+               }
+               else if (c == '"')
+                       in_quotes = true;
+               else
+               {
+                       c = pg_tolower((unsigned char) c);
+                       *optr++ = c;
+               }
        }
-       else
-               for (i = 0; field_case[i]; i++)
-                       if (isascii((unsigned char) field_case[i]) &&
-                               isupper(field_case[i]))
-                               field_case[i] = tolower(field_case[i]);
+       *optr = '\0';
 
        for (i = 0; i < res->numAttributes; i++)
        {
@@ -1971,10 +2237,43 @@ PQfnumber(const PGresult *res, const char *field_name)
        return -1;
 }
 
+Oid
+PQftable(const PGresult *res, int field_num)
+{
+       if (!check_field_number(res, field_num))
+               return InvalidOid;
+       if (res->attDescs)
+               return res->attDescs[field_num].tableid;
+       else
+               return InvalidOid;
+}
+
+int
+PQftablecol(const PGresult *res, int field_num)
+{
+       if (!check_field_number(res, field_num))
+               return 0;
+       if (res->attDescs)
+               return res->attDescs[field_num].columnid;
+       else
+               return 0;
+}
+
+int
+PQfformat(const PGresult *res, int field_num)
+{
+       if (!check_field_number(res, field_num))
+               return 0;
+       if (res->attDescs)
+               return res->attDescs[field_num].format;
+       else
+               return 0;
+}
+
 Oid
 PQftype(const PGresult *res, int field_num)
 {
-       if (!check_field_number("PQftype", res, field_num))
+       if (!check_field_number(res, field_num))
                return InvalidOid;
        if (res->attDescs)
                return res->attDescs[field_num].typid;
@@ -1985,7 +2284,7 @@ PQftype(const PGresult *res, int field_num)
 int
 PQfsize(const PGresult *res, int field_num)
 {
-       if (!check_field_number("PQfsize", res, field_num))
+       if (!check_field_number(res, field_num))
                return 0;
        if (res->attDescs)
                return res->attDescs[field_num].typlen;
@@ -1996,7 +2295,7 @@ PQfsize(const PGresult *res, int field_num)
 int
 PQfmod(const PGresult *res, int field_num)
 {
-       if (!check_field_number("PQfmod", res, field_num))
+       if (!check_field_number(res, field_num))
                return 0;
        if (res->attDescs)
                return res->attDescs[field_num].atttypmod;
@@ -2013,14 +2312,13 @@ PQcmdStatus(PGresult *res)
 }
 
 /*
  PQoidStatus -
      if the last command was an INSERT, return the oid string
      if not, return ""
-*/
* PQoidStatus -
*     if the last command was an INSERT, return the oid string
*     if not, return ""
+ */
 char *
 PQoidStatus(const PGresult *res)
 {
-
        /*
         * This must be enough to hold the result. Don't laugh, this is better
         * than what this function used to do.
@@ -2036,113 +2334,113 @@ PQoidStatus(const PGresult *res)
        if (len > 23)
                len = 23;
        strncpy(buf, res->cmdStatus + 7, len);
-       buf[23] = '\0';
+       buf[len] = '\0';
 
        return buf;
 }
 
 /*
-  PQoidValue -
              a perhaps preferable form of the above which just returns
      an Oid type
-*/
* PQoidValue -
*     a perhaps preferable form of the above which just returns
*     an Oid type
+ */
 Oid
 PQoidValue(const PGresult *res)
 {
        char       *endptr = NULL;
-       long int        result;
+       unsigned long result;
 
-       if (!res || !res->cmdStatus || strncmp(res->cmdStatus, "INSERT ", 7) != 0)
+       if (!res ||
+               !res->cmdStatus ||
+               strncmp(res->cmdStatus, "INSERT ", 7) != 0 ||
+               res->cmdStatus[7] < '0' ||
+               res->cmdStatus[7] > '9')
                return InvalidOid;
 
-       errno = 0;
        result = strtoul(res->cmdStatus + 7, &endptr, 10);
 
-       if (!endptr || (*endptr != ' ' && *endptr != '\0') || errno == ERANGE)
+       if (!endptr || (*endptr != ' ' && *endptr != '\0'))
                return InvalidOid;
        else
                return (Oid) result;
 }
 
+
 /*
-   PQcmdTuples -
-       if the last command was an INSERT/UPDATE/DELETE, return number
-       of inserted/affected tuples, if not, return ""
-*/
+ * PQcmdTuples -
+ *     If the last command was INSERT/UPDATE/DELETE/MOVE/FETCH/COPY, return
+ *     a string containing the number of inserted/affected tuples. If not,
+ *     return "".
+ *
+ *     XXX: this should probably return an int
+ */
 char *
 PQcmdTuples(PGresult *res)
 {
-       char            noticeBuf[128];
+       char       *p,
+                          *c;
 
        if (!res)
                return "";
 
-       if (strncmp(res->cmdStatus, "INSERT", 6) == 0 ||
-               strncmp(res->cmdStatus, "DELETE", 6) == 0 ||
-               strncmp(res->cmdStatus, "UPDATE", 6) == 0)
+       if (strncmp(res->cmdStatus, "INSERT ", 7) == 0)
        {
-               char       *p = res->cmdStatus + 6;
-
+               p = res->cmdStatus + 7;
+               /* INSERT: skip oid and space */
+               while (*p && *p != ' ')
+                       p++;
                if (*p == 0)
-               {
-                       if (res->noticeHook)
-                       {
-                               sprintf(noticeBuf,
-                                               "PQcmdTuples (%s) -- bad input from server\n",
-                                               res->cmdStatus);
-                               DONOTICE(res, noticeBuf);
-                       }
-                       return "";
-               }
-               p++;
-               if (*(res->cmdStatus) != 'I')   /* UPDATE/DELETE */
-                       return p;
-               while (*p != ' ' && *p)
-                       p++;                            /* INSERT: skip oid */
-               if (*p == 0)
-               {
-                       if (res->noticeHook)
-                       {
-                               sprintf(noticeBuf,
-                                        "PQcmdTuples (INSERT) -- there's no # of tuples\n");
-                               DONOTICE(res, noticeBuf);
-                       }
-                       return "";
-               }
+                       goto interpret_error;           /* no space? */
                p++;
-               return p;
        }
+       else if (strncmp(res->cmdStatus, "DELETE ", 7) == 0 ||
+                        strncmp(res->cmdStatus, "UPDATE ", 7) == 0)
+               p = res->cmdStatus + 7;
+       else if (strncmp(res->cmdStatus, "FETCH ", 6) == 0)
+               p = res->cmdStatus + 6;
+       else if (strncmp(res->cmdStatus, "MOVE ", 5) == 0 ||
+                        strncmp(res->cmdStatus, "COPY ", 5) == 0)
+               p = res->cmdStatus + 5;
+       else
+               return "";
+
+       /* check that we have an integer (at least one digit, nothing else) */
+       for (c = p; *c; c++)
+       {
+               if (!isdigit((unsigned char) *c))
+                       goto interpret_error;
+       }
+       if (c == p)
+               goto interpret_error;
+
+       return p;
+
+interpret_error:
+       pqInternalNotice(&res->noticeHooks,
+                                        "could not interpret result from server: %s",
+                                        res->cmdStatus);
        return "";
 }
 
 /*
-   PQgetvalue:
-       return the value of field 'field_num' of row 'tup_num'
-
-       If res is binary, then the value returned is NOT a null-terminated
-       ASCII string, but the binary representation in the server's native
-       format.
-
-       if res is not binary, a null-terminated ASCII string is returned.
-*/
+ * PQgetvalue:
+ *     return the value of field 'field_num' of row 'tup_num'
+ */
 char *
 PQgetvalue(const PGresult *res, int tup_num, int field_num)
 {
-       if (!check_tuple_field_number("PQgetvalue", res, tup_num, field_num))
+       if (!check_tuple_field_number(res, tup_num, field_num))
                return NULL;
        return res->tuples[tup_num][field_num].value;
 }
 
 /* PQgetlength:
-        returns the length of a field value in bytes.  If res is binary,
-        i.e. a result of a binary portal, then the length returned does
-        NOT include the size field of the varlena.  (The data returned
-        by PQgetvalue doesn't either.)
-*/
+ *     returns the actual length of a field value in bytes.
+ */
 int
 PQgetlength(const PGresult *res, int tup_num, int field_num)
 {
-       if (!check_tuple_field_number("PQgetlength", res, tup_num, field_num))
+       if (!check_tuple_field_number(res, tup_num, field_num))
                return 0;
        if (res->tuples[tup_num][field_num].len != NULL_LEN)
                return res->tuples[tup_num][field_num].len;
@@ -2151,12 +2449,12 @@ PQgetlength(const PGresult *res, int tup_num, int field_num)
 }
 
 /* PQgetisnull:
       returns the null status of a field value.
-*/
*     returns the null status of a field value.
+ */
 int
 PQgetisnull(const PGresult *res, int tup_num, int field_num)
 {
-       if (!check_tuple_field_number("PQgetisnull", res, tup_num, field_num))
+       if (!check_tuple_field_number(res, tup_num, field_num))
                return 1;                               /* pretend it is null */
        if (res->tuples[tup_num][field_num].len == NULL_LEN)
                return 1;
@@ -2164,53 +2462,437 @@ PQgetisnull(const PGresult *res, int tup_num, int field_num)
                return 0;
 }
 
-/* PQsetnonblocking:
-        sets the PGconn's database connection non-blocking if the arg is TRUE
-        or makes it non-blocking if the arg is FALSE, this will not protect
-        you from PQexec(), you'll only be safe when using the non-blocking
-        API
-        Needs to be called only on a connected database connection.
-*/
+/* PQnparams:
+ *     returns the number of input parameters of a prepared statement.
+ */
+int
+PQnparams(const PGresult *res)
+{
+       if (!res)
+               return 0;
+       return res->numParameters;
+}
+
+/* PQparamtype:
+ *     returns type Oid of the specified statement parameter.
+ */
+Oid
+PQparamtype(const PGresult *res, int param_num)
+{
+       if (!check_param_number(res, param_num))
+               return InvalidOid;
+       if (res->paramDescs)
+               return res->paramDescs[param_num].typid;
+       else
+               return InvalidOid;
+}
+
 
+/* PQsetnonblocking:
+ *     sets the PGconn's database connection non-blocking if the arg is TRUE
+ *     or makes it non-blocking if the arg is FALSE, this will not protect
+ *     you from PQexec(), you'll only be safe when using the non-blocking API.
+ *     Needs to be called only on a connected database connection.
+ */
 int
 PQsetnonblocking(PGconn *conn, int arg)
 {
+       bool            barg;
+
+       if (!conn || conn->status == CONNECTION_BAD)
+               return -1;
+
+       barg = (arg ? TRUE : FALSE);
 
-       arg = (arg == TRUE) ? 1 : 0;
        /* early out if the socket is already in the state requested */
-       if (arg == conn->nonblocking)
-               return (0);
+       if (barg == conn->nonblocking)
+               return 0;
 
        /*
-        * to guarantee constancy for flushing/query/result-polling behavior
-        * we need to flush the send queue at this point in order to guarantee
-        * proper behavior. this is ok because either they are making a
-        * transition _from_ or _to_ blocking mode, either way we can block
-        * them.
+        * to guarantee constancy for flushing/query/result-polling behavior we
+        * need to flush the send queue at this point in order to guarantee proper
+        * behavior. this is ok because either they are making a transition _from_
+        * or _to_ blocking mode, either way we can block them.
         */
        /* if we are going from blocking to non-blocking flush here */
        if (pqFlush(conn))
-               return (-1);
+               return -1;
 
-       conn->nonblocking = arg;
+       conn->nonblocking = barg;
 
-       return (0);
+       return 0;
 }
 
-/* return the blocking status of the database connection, TRUE == nonblocking,
-        FALSE == blocking
-*/
+/*
+ * return the blocking status of the database connection
+ *             TRUE == nonblocking, FALSE == blocking
+ */
 int
 PQisnonblocking(const PGconn *conn)
 {
+       return pqIsnonblocking(conn);
+}
 
-       return (pqIsnonblocking(conn));
+/* libpq is thread-safe? */
+int
+PQisthreadsafe(void)
+{
+#ifdef ENABLE_THREAD_SAFETY
+       return true;
+#else
+       return false;
+#endif
 }
 
+
 /* try to force data out, really only useful for non-blocking users */
 int
 PQflush(PGconn *conn)
 {
+       return pqFlush(conn);
+}
+
+
+/*
+ *             PQfreemem - safely frees memory allocated
+ *
+ * Needed mostly by Win32, unless multithreaded DLL (/MD in VC6)
+ * Used for freeing memory from PQescapeByte()a/PQunescapeBytea()
+ */
+void
+PQfreemem(void *ptr)
+{
+       free(ptr);
+}
+
+/*
+ * PQfreeNotify - free's the memory associated with a PGnotify
+ *
+ * This function is here only for binary backward compatibility.
+ * New code should use PQfreemem().  A macro will automatically map
+ * calls to PQfreemem. It should be removed in the future.  bjm 2003-03-24
+ */
+
+#undef PQfreeNotify
+void           PQfreeNotify(PGnotify *notify);
+
+void
+PQfreeNotify(PGnotify *notify)
+{
+       PQfreemem(notify);
+}
+
+
+/*
+ * Escaping arbitrary strings to get valid SQL literal strings.
+ *
+ * Replaces "'" with "''", and if not std_strings, replaces "\" with "\\".
+ *
+ * length is the length of the source string.  (Note: if a terminating NUL
+ * is encountered sooner, PQescapeString stops short of "length"; the behavior
+ * is thus rather like strncpy.)
+ *
+ * For safety the buffer at "to" must be at least 2*length + 1 bytes long.
+ * A terminating NUL character is added to the output string, whether the
+ * input is NUL-terminated or not.
+ *
+ * Returns the actual length of the output (not counting the terminating NUL).
+ */
+static size_t
+PQescapeStringInternal(PGconn *conn,
+                                          char *to, const char *from, size_t length,
+                                          int *error,
+                                          int encoding, bool std_strings)
+{
+       const char *source = from;
+       char       *target = to;
+       size_t          remaining = length;
+
+       if (error)
+               *error = 0;
+
+       while (remaining > 0 && *source != '\0')
+       {
+               char            c = *source;
+               int                     len;
+               int                     i;
+
+               /* Fast path for plain ASCII */
+               if (!IS_HIGHBIT_SET(c))
+               {
+                       /* Apply quoting if needed */
+                       if (SQL_STR_DOUBLE(c, !std_strings))
+                               *target++ = c;
+                       /* Copy the character */
+                       *target++ = c;
+                       source++;
+                       remaining--;
+                       continue;
+               }
+
+               /* Slow path for possible multibyte characters */
+               len = pg_encoding_mblen(encoding, source);
+
+               /* Copy the character */
+               for (i = 0; i < len; i++)
+               {
+                       if (remaining == 0 || *source == '\0')
+                               break;
+                       *target++ = *source++;
+                       remaining--;
+               }
+
+               /*
+                * If we hit premature end of string (ie, incomplete multibyte
+                * character), try to pad out to the correct length with spaces. We
+                * may not be able to pad completely, but we will always be able to
+                * insert at least one pad space (since we'd not have quoted a
+                * multibyte character).  This should be enough to make a string that
+                * the server will error out on.
+                */
+               if (i < len)
+               {
+                       if (error)
+                               *error = 1;
+                       if (conn)
+                               printfPQExpBuffer(&conn->errorMessage,
+                                                 libpq_gettext("incomplete multibyte character\n"));
+                       for (; i < len; i++)
+                       {
+                               if (((size_t) (target - to)) / 2 >= length)
+                                       break;
+                               *target++ = ' ';
+                       }
+                       break;
+               }
+       }
+
+       /* Write the terminating NUL character. */
+       *target = '\0';
+
+       return target - to;
+}
+
+size_t
+PQescapeStringConn(PGconn *conn,
+                                  char *to, const char *from, size_t length,
+                                  int *error)
+{
+       if (!conn)
+       {
+               /* force empty-string result */
+               *to = '\0';
+               if (error)
+                       *error = 1;
+               return 0;
+       }
+       return PQescapeStringInternal(conn, to, from, length, error,
+                                                                 conn->client_encoding,
+                                                                 conn->std_strings);
+}
+
+size_t
+PQescapeString(char *to, const char *from, size_t length)
+{
+       return PQescapeStringInternal(NULL, to, from, length, NULL,
+                                                                 static_client_encoding,
+                                                                 static_std_strings);
+}
+
+/*
+ *             PQescapeBytea   - converts from binary string to the
+ *             minimal encoding necessary to include the string in an SQL
+ *             INSERT statement with a bytea type column as the target.
+ *
+ *             The following transformations are applied
+ *             '\0' == ASCII  0 == \000
+ *             '\'' == ASCII 39 == ''
+ *             '\\' == ASCII 92 == \\
+ *             anything < 0x20, or > 0x7e ---> \ooo
+ *                                                                             (where ooo is an octal expression)
+ *             If not std_strings, all backslashes sent to the output are doubled.
+ */
+static unsigned char *
+PQescapeByteaInternal(PGconn *conn,
+                                         const unsigned char *from, size_t from_length,
+                                         size_t *to_length, bool std_strings)
+{
+       const unsigned char *vp;
+       unsigned char *rp;
+       unsigned char *result;
+       size_t          i;
+       size_t          len;
+       size_t          bslash_len = (std_strings ? 1 : 2);
+
+       /*
+        * empty string has 1 char ('\0')
+        */
+       len = 1;
+
+       vp = from;
+       for (i = from_length; i > 0; i--, vp++)
+       {
+               if (*vp < 0x20 || *vp > 0x7e)
+                       len += bslash_len + 3;
+               else if (*vp == '\'')
+                       len += 2;
+               else if (*vp == '\\')
+                       len += bslash_len + bslash_len;
+               else
+                       len++;
+       }
+
+       *to_length = len;
+       rp = result = (unsigned char *) malloc(len);
+       if (rp == NULL)
+       {
+               if (conn)
+                       printfPQExpBuffer(&conn->errorMessage,
+                                                         libpq_gettext("out of memory\n"));
+               return NULL;
+       }
+
+       vp = from;
+       for (i = from_length; i > 0; i--, vp++)
+       {
+               if (*vp < 0x20 || *vp > 0x7e)
+               {
+                       if (!std_strings)
+                               *rp++ = '\\';
+                       (void) sprintf((char *) rp, "\\%03o", *vp);
+                       rp += 4;
+               }
+               else if (*vp == '\'')
+               {
+                       *rp++ = '\'';
+                       *rp++ = '\'';
+               }
+               else if (*vp == '\\')
+               {
+                       if (!std_strings)
+                       {
+                               *rp++ = '\\';
+                               *rp++ = '\\';
+                       }
+                       *rp++ = '\\';
+                       *rp++ = '\\';
+               }
+               else
+                       *rp++ = *vp;
+       }
+       *rp = '\0';
+
+       return result;
+}
+
+unsigned char *
+PQescapeByteaConn(PGconn *conn,
+                                 const unsigned char *from, size_t from_length,
+                                 size_t *to_length)
+{
+       if (!conn)
+               return NULL;
+       return PQescapeByteaInternal(conn, from, from_length, to_length,
+                                                                conn->std_strings);
+}
+
+unsigned char *
+PQescapeBytea(const unsigned char *from, size_t from_length, size_t *to_length)
+{
+       return PQescapeByteaInternal(NULL, from, from_length, to_length,
+                                                                static_std_strings);
+}
+
+
+#define ISFIRSTOCTDIGIT(CH) ((CH) >= '0' && (CH) <= '3')
+#define ISOCTDIGIT(CH) ((CH) >= '0' && (CH) <= '7')
+#define OCTVAL(CH) ((CH) - '0')
+
+/*
+ *             PQunescapeBytea - converts the null terminated string representation
+ *             of a bytea, strtext, into binary, filling a buffer. It returns a
+ *             pointer to the buffer (or NULL on error), and the size of the
+ *             buffer in retbuflen. The pointer may subsequently be used as an
+ *             argument to the function PQfreemem.
+ *
+ *             The following transformations are made:
+ *             \\       == ASCII 92 == \
+ *             \ooo == a byte whose value = ooo (ooo is an octal number)
+ *             \x       == x (x is any character not matched by the above transformations)
+ */
+unsigned char *
+PQunescapeBytea(const unsigned char *strtext, size_t *retbuflen)
+{
+       size_t          strtextlen,
+                               buflen;
+       unsigned char *buffer,
+                          *tmpbuf;
+       size_t          i,
+                               j;
+
+       if (strtext == NULL)
+               return NULL;
+
+       strtextlen = strlen((const char *) strtext);
+
+       /*
+        * Length of input is max length of output, but add one to avoid
+        * unportable malloc(0) if input is zero-length.
+        */
+       buffer = (unsigned char *) malloc(strtextlen + 1);
+       if (buffer == NULL)
+               return NULL;
+
+       for (i = j = 0; i < strtextlen;)
+       {
+               switch (strtext[i])
+               {
+                       case '\\':
+                               i++;
+                               if (strtext[i] == '\\')
+                                       buffer[j++] = strtext[i++];
+                               else
+                               {
+                                       if ((ISFIRSTOCTDIGIT(strtext[i])) &&
+                                               (ISOCTDIGIT(strtext[i + 1])) &&
+                                               (ISOCTDIGIT(strtext[i + 2])))
+                                       {
+                                               int                     byte;
+
+                                               byte = OCTVAL(strtext[i++]);
+                                               byte = (byte << 3) + OCTVAL(strtext[i++]);
+                                               byte = (byte << 3) + OCTVAL(strtext[i++]);
+                                               buffer[j++] = byte;
+                                       }
+                               }
+
+                               /*
+                                * Note: if we see '\' followed by something that isn't a
+                                * recognized escape sequence, we loop around having done
+                                * nothing except advance i.  Therefore the something will be
+                                * emitted as ordinary data on the next cycle. Corner case:
+                                * '\' at end of string will just be discarded.
+                                */
+                               break;
+
+                       default:
+                               buffer[j++] = strtext[i++];
+                               break;
+               }
+       }
+       buflen = j;                                     /* buflen is the length of the dequoted data */
+
+       /* Shrink the buffer to be no larger than necessary */
+       /* +1 avoids unportable behavior when buflen==0 */
+       tmpbuf = realloc(buffer, buflen + 1);
+
+       /* It would only be a very brain-dead realloc that could fail, but... */
+       if (!tmpbuf)
+       {
+               free(buffer);
+               return NULL;
+       }
 
-       return (pqFlush(conn));
+       *retbuflen = buflen;
+       return tmpbuf;
 }