aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorTom Lane <tgl@sss.pgh.pa.us>2003-06-21 21:51:35 +0000
committerTom Lane <tgl@sss.pgh.pa.us>2003-06-21 21:51:35 +0000
commitefc3a25bb02ada63158fe7006673518b005261ba (patch)
tree0f64a370bfcca9f8fa223904179c9e4ba1755779 /src
parentb8d601e7359bdafbe9fede9bdfb9037b19a50272 (diff)
downloadpostgresql-efc3a25bb02ada63158fe7006673518b005261ba.tar.gz
postgresql-efc3a25bb02ada63158fe7006673518b005261ba.zip
Update libpq to make new features of FE/BE protocol available to
client applications. Some editorial work on libpq.sgml, too.
Diffstat (limited to 'src')
-rw-r--r--src/interfaces/libpq/blibpqdll.def29
-rw-r--r--src/interfaces/libpq/fe-connect.c106
-rw-r--r--src/interfaces/libpq/fe-exec.c782
-rw-r--r--src/interfaces/libpq/fe-misc.c36
-rw-r--r--src/interfaces/libpq/fe-protocol2.c194
-rw-r--r--src/interfaces/libpq/fe-protocol3.c340
-rw-r--r--src/interfaces/libpq/libpq-fe.h106
-rw-r--r--src/interfaces/libpq/libpq-int.h84
-rw-r--r--src/interfaces/libpq/libpqdll.def15
9 files changed, 1324 insertions, 368 deletions
diff --git a/src/interfaces/libpq/blibpqdll.def b/src/interfaces/libpq/blibpqdll.def
index 8445a29b1b4..ff85e9cdfc8 100644
--- a/src/interfaces/libpq/blibpqdll.def
+++ b/src/interfaces/libpq/blibpqdll.def
@@ -97,6 +97,20 @@ EXPORTS
_pg_utf_mblen @ 93
_PQunescapeBytea @ 94
_PQfreemem @ 95
+ _PQtransactionStatus @ 96
+ _PQparameterStatus @ 97
+ _PQprotocolVersion @ 98
+ _PQsetErrorVerbosity @ 99
+ _PQsetNoticeReceiver @ 100
+ _PQexecParams @ 101
+ _PQsendQueryParams @ 102
+ _PQputCopyData @ 103
+ _PQputCopyEnd @ 104
+ _PQgetCopyData @ 105
+ _PQresultErrorField @ 106
+ _PQftable @ 107
+ _PQftablecol @ 108
+ _PQfformat @ 109
; Aliases for MS compatible names
PQconnectdb = _PQconnectdb
@@ -194,4 +208,17 @@ EXPORTS
pg_utf_mblen = _pg_utf_mblen
PQunescapeBytea = _PQunescapeBytea
PQfreemem = _PQfreemem
-
+ PQtransactionStatus = _PQtransactionStatus
+ PQparameterStatus = _PQparameterStatus
+ PQprotocolVersion = _PQprotocolVersion
+ PQsetErrorVerbosity = _PQsetErrorVerbosity
+ PQsetNoticeReceiver = _PQsetNoticeReceiver
+ PQexecParams = _PQexecParams
+ PQsendQueryParams = _PQsendQueryParams
+ PQputCopyData = _PQputCopyData
+ PQputCopyEnd = _PQputCopyEnd
+ PQgetCopyData = _PQgetCopyData
+ PQresultErrorField = _PQresultErrorField
+ PQftable = _PQftable
+ PQftablecol = _PQftablecol
+ PQfformat = _PQfformat
diff --git a/src/interfaces/libpq/fe-connect.c b/src/interfaces/libpq/fe-connect.c
index a58caa3f3aa..7008d99601d 100644
--- a/src/interfaces/libpq/fe-connect.c
+++ b/src/interfaces/libpq/fe-connect.c
@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/interfaces/libpq/fe-connect.c,v 1.249 2003/06/20 04:09:12 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/interfaces/libpq/fe-connect.c,v 1.250 2003/06/21 21:51:33 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -176,6 +176,7 @@ static PQconninfoOption *conninfo_parse(const char *conninfo,
PQExpBuffer errorMessage);
static char *conninfo_getval(PQconninfoOption *connOptions,
const char *keyword);
+static void defaultNoticeReceiver(void *arg, const PGresult *res);
static void defaultNoticeProcessor(void *arg, const char *message);
static int parseServiceInfo(PQconninfoOption *options,
PQExpBuffer errorMessage);
@@ -1804,11 +1805,14 @@ makeEmptyPGconn(void)
/* Zero all pointers and booleans */
MemSet((char *) conn, 0, sizeof(PGconn));
- conn->noticeHook = defaultNoticeProcessor;
+ conn->noticeHooks.noticeRec = defaultNoticeReceiver;
+ conn->noticeHooks.noticeProc = defaultNoticeProcessor;
conn->status = CONNECTION_BAD;
conn->asyncStatus = PGASYNC_IDLE;
+ conn->xactStatus = PQTRANS_IDLE;
conn->setenv_state = SETENV_STATE_IDLE;
conn->client_encoding = PG_SQL_ASCII;
+ conn->verbosity = PQERRORS_DEFAULT;
conn->notifyList = DLNewList();
conn->sock = -1;
#ifdef USE_SSL
@@ -1850,7 +1854,6 @@ makeEmptyPGconn(void)
/*
* freePGconn
* - free the PGconn data structure
- *
*/
static void
freePGconn(PGconn *conn)
@@ -1899,9 +1902,9 @@ freePGconn(PGconn *conn)
}
/*
- closePGconn
- - properly close a connection to the backend
-*/
+ * closePGconn
+ * - properly close a connection to the backend
+ */
static void
closePGconn(PGconn *conn)
{
@@ -2662,6 +2665,41 @@ PQstatus(const PGconn *conn)
return conn->status;
}
+PGTransactionStatusType
+PQtransactionStatus(const PGconn *conn)
+{
+ if (!conn || conn->status != CONNECTION_OK)
+ return PQTRANS_UNKNOWN;
+ if (conn->asyncStatus != PGASYNC_IDLE)
+ return PQTRANS_ACTIVE;
+ return conn->xactStatus;
+}
+
+const char *
+PQparameterStatus(const PGconn *conn, const char *paramName)
+{
+ const pgParameterStatus *pstatus;
+
+ if (!conn || !paramName)
+ return NULL;
+ for (pstatus = conn->pstatus; pstatus != NULL; pstatus = pstatus->next)
+ {
+ if (strcmp(pstatus->name, paramName) == 0)
+ return pstatus->value;
+ }
+ return NULL;
+}
+
+int
+PQprotocolVersion(const PGconn *conn)
+{
+ if (!conn)
+ return 0;
+ if (conn->status == CONNECTION_BAD)
+ return 0;
+ return PG_PROTOCOL_MAJOR(conn->pversion);
+}
+
char *
PQerrorMessage(const PGconn *conn)
{
@@ -2731,11 +2769,22 @@ PQsetClientEncoding(PGconn *conn, const char *encoding)
return (status);
}
+PGVerbosity
+PQsetErrorVerbosity(PGconn *conn, PGVerbosity verbosity)
+{
+ PGVerbosity old;
+
+ if (!conn)
+ return PQERRORS_DEFAULT;
+ old = conn->verbosity;
+ conn->verbosity = verbosity;
+ return old;
+}
+
void
PQtrace(PGconn *conn, FILE *debug_port)
{
- if (conn == NULL ||
- conn->status == CONNECTION_BAD)
+ if (conn == NULL)
return;
PQuntrace(conn);
conn->Pfdebug = debug_port;
@@ -2744,7 +2793,6 @@ PQtrace(PGconn *conn, FILE *debug_port)
void
PQuntrace(PGconn *conn)
{
- /* note: better allow untrace even when connection bad */
if (conn == NULL)
return;
if (conn->Pfdebug)
@@ -2754,6 +2802,23 @@ PQuntrace(PGconn *conn)
}
}
+PQnoticeReceiver
+PQsetNoticeReceiver(PGconn *conn, PQnoticeReceiver proc, void *arg)
+{
+ PQnoticeReceiver old;
+
+ if (conn == NULL)
+ return NULL;
+
+ old = conn->noticeHooks.noticeRec;
+ if (proc)
+ {
+ conn->noticeHooks.noticeRec = proc;
+ conn->noticeHooks.noticeRecArg = arg;
+ }
+ return old;
+}
+
PQnoticeProcessor
PQsetNoticeProcessor(PGconn *conn, PQnoticeProcessor proc, void *arg)
{
@@ -2762,22 +2827,35 @@ PQsetNoticeProcessor(PGconn *conn, PQnoticeProcessor proc, void *arg)
if (conn == NULL)
return NULL;
- old = conn->noticeHook;
+ old = conn->noticeHooks.noticeProc;
if (proc)
{
- conn->noticeHook = proc;
- conn->noticeArg = arg;
+ conn->noticeHooks.noticeProc = proc;
+ conn->noticeHooks.noticeProcArg = arg;
}
return old;
}
/*
- * The default notice/error message processor just prints the
+ * The default notice message receiver just gets the standard notice text
+ * and sends it to the notice processor. This two-level setup exists
+ * mostly for backwards compatibility; perhaps we should deprecate use of
+ * PQsetNoticeProcessor?
+ */
+static void
+defaultNoticeReceiver(void *arg, const PGresult *res)
+{
+ (void) arg; /* not used */
+ (*res->noticeHooks.noticeProc) (res->noticeHooks.noticeProcArg,
+ PQresultErrorMessage(res));
+}
+
+/*
+ * The default notice message processor just prints the
* message on stderr. Applications can override this if they
* want the messages to go elsewhere (a window, for example).
* Note that simply discarding notices is probably a bad idea.
*/
-
static void
defaultNoticeProcessor(void *arg, const char *message)
{
diff --git a/src/interfaces/libpq/fe-exec.c b/src/interfaces/libpq/fe-exec.c
index 45db359bde1..4c96bbd3868 100644
--- a/src/interfaces/libpq/fe-exec.c
+++ b/src/interfaces/libpq/fe-exec.c
@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/interfaces/libpq/fe-exec.c,v 1.138 2003/06/12 01:17:19 momjian Exp $
+ * $Header: /cvsroot/pgsql/src/interfaces/libpq/fe-exec.c,v 1.139 2003/06/21 21:51:34 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -43,7 +43,10 @@ char *const pgresStatus[] = {
+static bool PQsendQueryStart(PGconn *conn);
static void parseInput(PGconn *conn);
+static bool PQexecStart(PGconn *conn);
+static PGresult *PQexecFinish(PGconn *conn);
/* ----------------
@@ -137,16 +140,7 @@ PQmakeEmptyPGresult(PGconn *conn, ExecStatusType status)
result->cmdStatus[0] = '\0';
result->binary = 0;
result->errMsg = NULL;
- result->errSeverity = NULL;
- result->errCode = NULL;
- result->errPrimary = NULL;
- result->errDetail = NULL;
- result->errHint = NULL;
- result->errPosition = NULL;
- result->errContext = NULL;
- result->errFilename = NULL;
- result->errLineno = NULL;
- result->errFuncname = NULL;
+ result->errFields = NULL;
result->null_field[0] = '\0';
result->curBlock = NULL;
result->curOffset = 0;
@@ -155,8 +149,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 */
@@ -177,9 +170,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;
@@ -445,6 +440,41 @@ pqPrepareAsyncResult(PGconn *conn)
}
/*
+ * pqInternalNotice - helper routine for internally-generated notices
+ *
+ * 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 *msgtext)
+{
+ PGresult *res;
+
+ if (hooks->noticeRec == NULL)
+ return; /* nobody home? */
+
+ /* Make a PGresult to pass to the notice receiver */
+ res = PQmakeEmptyPGresult(NULL, PGRES_NONFATAL_ERROR);
+ res->noticeHooks = *hooks;
+ /*
+ * Set up fields of notice.
+ */
+ pqSaveMessageField(res, 'M', msgtext);
+ pqSaveMessageField(res, 'S', libpq_gettext("NOTICE"));
+ /* XXX should provide a SQLSTATE too? */
+ /*
+ * Result text is always just the primary message + newline.
+ */
+ res->errMsg = (char *) pqResultAlloc(res, strlen(msgtext) + 2, FALSE);
+ sprintf(res->errMsg, "%s\n", msgtext);
+ /*
+ * 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
@@ -484,6 +514,25 @@ pqAddTuple(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
@@ -543,26 +592,6 @@ pqSaveParameterStatus(PGconn *conn, const char *name, const char *value)
StrNCpy(conn->sversion, value, sizeof(conn->sversion));
}
-/*
- * pqGetParameterStatus - fetch parameter value, if available
- *
- * Returns NULL if info not available
- *
- * XXX this probably should be exported for client use
- */
-const char *
-pqGetParameterStatus(PGconn *conn, const char *name)
-{
- pgParameterStatus *pstatus;
-
- for (pstatus = conn->pstatus; pstatus != NULL; pstatus = pstatus->next)
- {
- if (strcmp(pstatus->name, name) == 0)
- return pstatus->value;
- }
- return NULL;
-}
-
/*
* PQsendQuery
@@ -574,12 +603,9 @@ pqGetParameterStatus(PGconn *conn, const char *name)
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,
@@ -587,25 +613,6 @@ PQsendQuery(PGconn *conn, const char *query)
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 0;
- }
- /* 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 0;
- }
-
- /* initialize async result-accumulation state */
- conn->result = NULL;
- conn->curTuple = NULL;
-
/* construct the outgoing Query message */
if (pqPutMsgStart('Q', false, conn) < 0 ||
pqPuts(query, conn) < 0 ||
@@ -617,7 +624,7 @@ PQsendQuery(PGconn *conn, const char *query)
/*
* Give the data a push. In nonblock mode, don't complain if we're
- * unable to send it all; PQconsumeInput() will do any additional flushing
+ * unable to send it all; PQgetResult() will do any additional flushing
* needed.
*/
if (pqFlush(conn) < 0)
@@ -632,6 +639,194 @@ PQsendQuery(PGconn *conn, const char *query)
}
/*
+ * PQsendQueryParams
+ * Like PQsendQuery, but use 3.0 protocol 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)
+{
+ int i;
+
+ 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 3.0 protocol\n"));
+ return 0;
+ }
+
+ if (!command)
+ {
+ printfPQExpBuffer(&conn->errorMessage,
+ libpq_gettext("command string is a null pointer\n"));
+ return 0;
+ }
+
+ /*
+ * We will send Parse, Bind, Describe Portal, Execute, Sync, using
+ * unnamed statement and portal.
+ */
+
+ /* construct the Parse message */
+ if (pqPutMsgStart('P', false, conn) < 0 ||
+ pqPuts("", 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;
+
+ /* construct the Bind message */
+ if (pqPutMsgStart('B', false, conn) < 0 ||
+ pqPuts("", conn) < 0 ||
+ pqPuts("", conn) < 0)
+ goto sendFailed;
+ if (nParams > 0 && paramFormats)
+ {
+ if (pqPutInt(nParams, 2, conn) < 0)
+ goto sendFailed;
+ for (i = 0; i < nParams; i++)
+ {
+ if (pqPutInt(paramFormats[i], 2, conn) < 0)
+ goto sendFailed;
+ }
+ }
+ 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])
+ {
+ int nbytes;
+
+ if (paramFormats && paramFormats[i] != 0)
+ {
+ /* binary parameter */
+ nbytes = paramLengths[i];
+ }
+ else
+ {
+ /* 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
+ {
+ /* take the param as NULL */
+ if (pqPutInt(-1, 4, conn) < 0)
+ goto sendFailed;
+ }
+ }
+ 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;
+
+ /*
+ * 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;
+}
+
+/*
+ * Common startup code for PQsendQuery and PQsendQueryParams
+ */
+static bool
+PQsendQueryStart(PGconn *conn)
+{
+ if (!conn)
+ return false;
+
+ /* clear the error string */
+ resetPQExpBuffer(&conn->errorMessage);
+
+ /* 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;
+ }
+
+ /* initialize async result-accumulation state */
+ conn->result = NULL;
+ conn->curTuple = NULL;
+
+ /* ready to send command message */
+ return true;
+}
+
+/*
* pqHandleSendFailure: try to clean up after failure to send command.
*
* Primarily, what we want to accomplish here is to process an async
@@ -746,8 +941,24 @@ 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)
{
/*
@@ -758,6 +969,7 @@ PQgetResult(PGconn *conn)
conn->asyncStatus = PGASYNC_IDLE;
return pqPrepareAsyncResult(conn);
}
+
/* Parse it. */
parseInput(conn);
}
@@ -774,10 +986,16 @@ PQgetResult(PGconn *conn)
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,
@@ -806,18 +1024,46 @@ PQgetResult(PGconn *conn)
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 3.0 protocol 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);
+}
+
+/*
+ * Common code for PQexec and PQexecParams: 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
@@ -832,15 +1078,23 @@ PQexec(PGconn *conn, const char *query)
PQclear(result);
printfPQExpBuffer(&conn->errorMessage,
libpq_gettext("COPY state must be terminated first\n"));
- /* restore blocking status */
- goto errout;
+ return false;
}
PQclear(result);
}
- /* 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 PQexecParams: wait for command result
+ */
+static PGresult *
+PQexecFinish(PGconn *conn)
+{
+ PGresult *result;
+ PGresult *lastResult;
/*
* For backwards compatibility, return the last result if there are
@@ -848,7 +1102,7 @@ PQexec(PGconn *conn, const char *query)
* 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.
*/
lastResult = NULL;
while ((result = PQgetResult(conn)) != NULL)
@@ -874,14 +1128,7 @@ PQexec(PGconn *conn, const char *query)
break;
}
- if (PQsetnonblocking(conn, savedblocking) == -1)
- return NULL;
return lastResult;
-
-errout:
- if (PQsetnonblocking(conn, savedblocking) == -1)
- return NULL;
- return NULL;
}
/*
@@ -894,7 +1141,6 @@ errout:
*
* the CALLER is responsible for FREE'ing the structure returned
*/
-
PGnotify *
PQnotifies(PGconn *conn)
{
@@ -917,6 +1163,156 @@ PQnotifies(PGconn *conn)
}
/*
+ * 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;
+ }
+ 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;
+ }
+ }
+ return 1;
+}
+
+/*
+ * 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.
+ */
+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;
+ }
+ /*
+ * Send the COPY END indicator. This is simple enough that we don't
+ * bother delegating it to the fe-protocol files.
+ */
+ 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;
+ }
+ }
+ else
+ {
+ if (errormsg)
+ {
+ /* Ooops, no way to do this in 2.0 */
+ printfPQExpBuffer(&conn->errorMessage,
+ libpq_gettext("function requires at least 3.0 protocol\n"));
+ return -1;
+ }
+ else
+ {
+ /* Send old-style end-of-data marker */
+ if (pqPutMsgStart(0, false, conn) < 0 ||
+ pqPuts("\\.\n", conn) < 0 ||
+ pqPutMsgEnd(conn) < 0)
+ return -1;
+ }
+ }
+
+ /* Return to active duty */
+ conn->asyncStatus = PGASYNC_BUSY;
+ resetPQExpBuffer(&conn->errorMessage);
+
+ /* Try to flush data */
+ if (pqFlush(conn) < 0)
+ return -1;
+
+ return 1;
+}
+
+/*
+ * PQgetCopyData - read a row of data from the backend during COPY OUT
+ *
+ * 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).
+ */
+int
+PQgetCopyData(PGconn *conn, char **buffer, int async)
+{
+ *buffer = NULL; /* for all failure cases */
+ if (!conn)
+ 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);
+}
+
+/*
* PQgetline - gets a newline-terminated string from the backend.
*
* Chiefly here so that applications can use "COPY <rel> to stdout"
@@ -1002,11 +1398,12 @@ PQgetlineAsync(PGconn *conn, char *buffer, int 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.
*
- * This exists to support "COPY <rel> from stdin". The backend will ignore
- * the string if not doing COPY.
+ * 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)
@@ -1021,27 +1418,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;
- if (nbytes > 0)
- {
- /* This is too simple to bother with separate subroutines */
- if (PG_PROTOCOL_MAJOR(conn->pversion) >= 3)
- {
- if (pqPutMsgStart('d', false, conn) < 0 ||
- pqPutnchar(buffer, nbytes, conn) < 0 ||
- pqPutMsgEnd(conn) < 0)
- return EOF;
- }
- else
- {
- if (pqPutMsgStart(0, false, conn) < 0 ||
- pqPutnchar(buffer, nbytes, conn) < 0 ||
- pqPutMsgEnd(conn) < 0)
- return EOF;
- }
- }
- return 0;
}
/*
@@ -1049,6 +1429,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 3.0 protocol 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
@@ -1133,7 +1518,7 @@ ExecStatusType
PQresultStatus(const PGresult *res)
{
if (!res)
- return PGRES_NONFATAL_ERROR;
+ return PGRES_FATAL_ERROR;
return res->resultStatus;
}
@@ -1153,6 +1538,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)
{
@@ -1191,13 +1591,10 @@ check_field_number(const PGresult *res, int field_num)
return FALSE; /* no way to display error message... */
if (field_num < 0 || field_num >= res->numAttributes)
{
- if (res->noticeHook)
- {
- snprintf(noticeBuf, sizeof(noticeBuf),
- libpq_gettext("column number %d is out of range 0..%d\n"),
- field_num, res->numAttributes - 1);
- PGDONOTICE(res, noticeBuf);
- }
+ snprintf(noticeBuf, sizeof(noticeBuf),
+ libpq_gettext("column number %d is out of range 0..%d"),
+ field_num, res->numAttributes - 1);
+ PGDONOTICE(res, noticeBuf);
return FALSE;
}
return TRUE;
@@ -1213,32 +1610,26 @@ check_tuple_field_number(const PGresult *res,
return FALSE; /* no way to display error message... */
if (tup_num < 0 || tup_num >= res->ntups)
{
- if (res->noticeHook)
- {
- snprintf(noticeBuf, sizeof(noticeBuf),
- libpq_gettext("row number %d is out of range 0..%d\n"),
- tup_num, res->ntups - 1);
- PGDONOTICE(res, noticeBuf);
- }
+ snprintf(noticeBuf, sizeof(noticeBuf),
+ libpq_gettext("row number %d is out of range 0..%d"),
+ tup_num, res->ntups - 1);
+ PGDONOTICE(res, noticeBuf);
return FALSE;
}
if (field_num < 0 || field_num >= res->numAttributes)
{
- if (res->noticeHook)
- {
- snprintf(noticeBuf, sizeof(noticeBuf),
- libpq_gettext("column number %d is out of range 0..%d\n"),
- field_num, res->numAttributes - 1);
- PGDONOTICE(res, noticeBuf);
- }
+ snprintf(noticeBuf, sizeof(noticeBuf),
+ libpq_gettext("column number %d is out of range 0..%d"),
+ field_num, res->numAttributes - 1);
+ PGDONOTICE(res, noticeBuf);
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)
{
@@ -1251,8 +1642,8 @@ PQfname(const PGresult *res, int field_num)
}
/*
- returns -1 on a bad field name
-*/
+ * returns -1 on a bad field name
+ */
int
PQfnumber(const PGresult *res, const char *field_name)
{
@@ -1291,6 +1682,39 @@ PQfnumber(const PGresult *res, const char *field_name)
}
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(res, field_num))
@@ -1332,10 +1756,10 @@ 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)
{
@@ -1360,10 +1784,10 @@ PQoidStatus(const PGresult *res)
}
/*
- 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)
{
@@ -1388,13 +1812,13 @@ PQoidValue(const PGresult *res)
/*
- PQcmdTuples -
- If the last command was an INSERT/UPDATE/DELETE/MOVE/FETCH, return a
- string containing the number of inserted/affected tuples. If not,
- return "".
-
- XXX: this should probably return an int
-*/
+ * PQcmdTuples -
+ * If the last command was an INSERT/UPDATE/DELETE/MOVE/FETCH, return a
+ * string containing the number of inserted/affected tuples. If not,
+ * return "".
+ *
+ * XXX: this should probably return an int
+ */
char *
PQcmdTuples(PGresult *res)
{
@@ -1426,13 +1850,10 @@ PQcmdTuples(PGresult *res)
if (*p == 0)
{
- if (res->noticeHook)
- {
- snprintf(noticeBuf, sizeof(noticeBuf),
- libpq_gettext("could not interpret result from server: %s\n"),
- res->cmdStatus);
- PGDONOTICE(res, noticeBuf);
- }
+ snprintf(noticeBuf, sizeof(noticeBuf),
+ libpq_gettext("could not interpret result from server: %s"),
+ res->cmdStatus);
+ PGDONOTICE(res, noticeBuf);
return "";
}
@@ -1440,15 +1861,9 @@ PQcmdTuples(PGresult *res)
}
/*
- 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)
{
@@ -1458,11 +1873,8 @@ PQgetvalue(const PGresult *res, int tup_num, int field_num)
}
/* 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)
{
@@ -1475,8 +1887,8 @@ 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)
{
@@ -1489,16 +1901,17 @@ PQgetisnull(const PGresult *res, int tup_num, int field_num)
}
/* 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.
-*/
-
+ * 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)
{
+ if (!conn || conn->status == CONNECTION_BAD)
+ return -1;
+
arg = (arg == TRUE) ? 1 : 0;
/* early out if the socket is already in the state requested */
if (arg == conn->nonblocking)
@@ -1520,9 +1933,10 @@ PQsetnonblocking(PGconn *conn, int arg)
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)
{
diff --git a/src/interfaces/libpq/fe-misc.c b/src/interfaces/libpq/fe-misc.c
index 3856145a4ef..f10c3112d7e 100644
--- a/src/interfaces/libpq/fe-misc.c
+++ b/src/interfaces/libpq/fe-misc.c
@@ -23,7 +23,7 @@
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/interfaces/libpq/fe-misc.c,v 1.96 2003/06/14 17:49:54 momjian Exp $
+ * $Header: /cvsroot/pgsql/src/interfaces/libpq/fe-misc.c,v 1.97 2003/06/21 21:51:34 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -197,7 +197,7 @@ pqPutnchar(const char *s, size_t len, PGconn *conn)
}
/*
- * pgGetInt
+ * pqGetInt
* read a 2 or 4 byte integer and convert from network byte order
* to local byte order
*/
@@ -226,7 +226,7 @@ pqGetInt(int *result, size_t bytes, PGconn *conn)
break;
default:
snprintf(noticeBuf, sizeof(noticeBuf),
- libpq_gettext("integer of size %lu not supported by pqGetInt\n"),
+ libpq_gettext("integer of size %lu not supported by pqGetInt"),
(unsigned long) bytes);
PGDONOTICE(conn, noticeBuf);
return EOF;
@@ -239,7 +239,7 @@ pqGetInt(int *result, size_t bytes, PGconn *conn)
}
/*
- * pgPutInt
+ * pqPutInt
* write an integer of 2 or 4 bytes, converting from host byte order
* to network byte order.
*/
@@ -264,7 +264,7 @@ pqPutInt(int value, size_t bytes, PGconn *conn)
break;
default:
snprintf(noticeBuf, sizeof(noticeBuf),
- libpq_gettext("integer of size %lu not supported by pqPutInt\n"),
+ libpq_gettext("integer of size %lu not supported by pqPutInt"),
(unsigned long) bytes);
PGDONOTICE(conn, noticeBuf);
return EOF;
@@ -282,7 +282,7 @@ pqPutInt(int value, size_t bytes, PGconn *conn)
*
* Returns 0 on success, EOF if failed to enlarge buffer
*/
-static int
+int
pqCheckOutBufferSpace(int bytes_needed, PGconn *conn)
{
int newsize = conn->outBufSize;
@@ -748,7 +748,7 @@ pqSendSome(PGconn *conn, int len)
if (sent < 0)
{
/*
- * Anything except EAGAIN or EWOULDBLOCK is trouble. If it's
+ * Anything except EAGAIN/EWOULDBLOCK/EINTR is trouble. If it's
* EPIPE or ECONNRESET, assume we've lost the backend
* connection permanently.
*/
@@ -804,25 +804,17 @@ pqSendSome(PGconn *conn, int len)
if (len > 0)
{
- /* We didn't send it all, wait till we can send more */
-
/*
- * if the socket is in non-blocking mode we may need to abort
- * here and return 1 to indicate that data is still pending.
+ * We didn't send it all, wait till we can send more.
+ *
+ * If the connection is in non-blocking mode we don't wait,
+ * but return 1 to indicate that data is still pending.
*/
-#ifdef USE_SSL
- /* can't do anything for our SSL users yet */
- if (conn->ssl == NULL)
+ if (pqIsnonblocking(conn))
{
-#endif
- if (pqIsnonblocking(conn))
- {
- result = 1;
- break;
- }
-#ifdef USE_SSL
+ result = 1;
+ break;
}
-#endif
if (pqWait(FALSE, TRUE, conn))
{
diff --git a/src/interfaces/libpq/fe-protocol2.c b/src/interfaces/libpq/fe-protocol2.c
index 2a7b6b43bf5..6b909334079 100644
--- a/src/interfaces/libpq/fe-protocol2.c
+++ b/src/interfaces/libpq/fe-protocol2.c
@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/interfaces/libpq/fe-protocol2.c,v 1.1 2003/06/08 17:43:00 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/interfaces/libpq/fe-protocol2.c,v 1.2 2003/06/21 21:51:34 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -38,6 +38,7 @@
static int getRowDescriptions(PGconn *conn);
static int getAnotherTuple(PGconn *conn, bool binary);
static int pqGetErrorNotice2(PGconn *conn, bool isError);
+static void checkXactStatus(PGconn *conn, const char *cmdTag);
static int getNotify(PGconn *conn);
@@ -312,7 +313,7 @@ pqSetenvPoll(PGconn *conn)
val);
else
{
- val = pqGetParameterStatus(conn, "server_encoding");
+ val = PQparameterStatus(conn, "server_encoding");
if (val && *val)
pqSaveParameterStatus(conn, "client_encoding",
val);
@@ -424,7 +425,7 @@ pqParseInput2(PGconn *conn)
else
{
snprintf(noticeWorkspace, sizeof(noticeWorkspace),
- libpq_gettext("message type 0x%02x arrived from server while idle\n"),
+ libpq_gettext("message type 0x%02x arrived from server while idle"),
id);
PGDONOTICE(conn, noticeWorkspace);
/* Discard the unexpected message; good idea?? */
@@ -447,6 +448,7 @@ pqParseInput2(PGconn *conn)
PGRES_COMMAND_OK);
strncpy(conn->result->cmdStatus, conn->workBuffer.data,
CMDSTATUS_LEN);
+ checkXactStatus(conn, conn->workBuffer.data);
conn->asyncStatus = PGASYNC_READY;
break;
case 'E': /* error return */
@@ -464,7 +466,7 @@ pqParseInput2(PGconn *conn)
if (id != '\0')
{
snprintf(noticeWorkspace, sizeof(noticeWorkspace),
- libpq_gettext("unexpected character %c following empty query response (\"I\" message)\n"),
+ libpq_gettext("unexpected character %c following empty query response (\"I\" message)"),
id);
PGDONOTICE(conn, noticeWorkspace);
}
@@ -521,7 +523,7 @@ pqParseInput2(PGconn *conn)
else
{
snprintf(noticeWorkspace, sizeof(noticeWorkspace),
- libpq_gettext("server sent data (\"D\" message) without prior row description (\"T\" message)\n"));
+ libpq_gettext("server sent data (\"D\" message) without prior row description (\"T\" message)"));
PGDONOTICE(conn, noticeWorkspace);
/* Discard the unexpected message; good idea?? */
conn->inStart = conn->inEnd;
@@ -538,7 +540,7 @@ pqParseInput2(PGconn *conn)
else
{
snprintf(noticeWorkspace, sizeof(noticeWorkspace),
- libpq_gettext("server sent binary data (\"B\" message) without prior row description (\"T\" message)\n"));
+ libpq_gettext("server sent binary data (\"B\" message) without prior row description (\"T\" message)"));
PGDONOTICE(conn, noticeWorkspace);
/* Discard the unexpected message; good idea?? */
conn->inStart = conn->inEnd;
@@ -628,6 +630,9 @@ getRowDescriptions(PGconn *conn)
result->attDescs[i].name = pqResultStrdup(result,
conn->workBuffer.data);
+ result->attDescs[i].tableid = 0;
+ result->attDescs[i].columnid = 0;
+ result->attDescs[i].format = 0;
result->attDescs[i].typid = typid;
result->attDescs[i].typlen = typlen;
result->attDescs[i].atttypmod = atttypmod;
@@ -674,6 +679,15 @@ getAnotherTuple(PGconn *conn, bool binary)
if (conn->curTuple == NULL)
goto outOfMemory;
MemSet((char *) conn->curTuple, 0, nfields * sizeof(PGresAttValue));
+ /*
+ * If it's binary, fix the column format indicators. We assume
+ * the backend will consistently send either B or D, not a mix.
+ */
+ if (binary)
+ {
+ for (i = 0; i < nfields; i++)
+ result->attDescs[i].format = 1;
+ }
}
tup = conn->curTuple;
@@ -778,6 +792,8 @@ pqGetErrorNotice2(PGconn *conn, bool isError)
{
PGresult *res;
PQExpBufferData workBuf;
+ char *startp;
+ char *splitp;
/*
* Since the message might be pretty long, we create a temporary
@@ -801,18 +817,62 @@ pqGetErrorNotice2(PGconn *conn, bool isError)
res->errMsg = pqResultStrdup(res, workBuf.data);
/*
+ * Break the message into fields. We can't do very much here, but we
+ * can split the severity code off, and remove trailing newlines. Also,
+ * we use the heuristic that the primary message extends only to the
+ * first newline --- anything after that is detail message. (In some
+ * cases it'd be better classed as hint, but we can hardly be expected
+ * to guess that here.)
+ */
+ while (workBuf.len > 0 && workBuf.data[workBuf.len-1] == '\n')
+ workBuf.data[--workBuf.len] = '\0';
+ splitp = strstr(workBuf.data, ": ");
+ if (splitp)
+ {
+ /* what comes before the colon is severity */
+ *splitp = '\0';
+ pqSaveMessageField(res, 'S', workBuf.data);
+ startp = splitp + 3;
+ }
+ else
+ {
+ /* can't find a colon? oh well... */
+ startp = workBuf.data;
+ }
+ splitp = strchr(startp, '\n');
+ if (splitp)
+ {
+ /* what comes before the newline is primary message */
+ *splitp++ = '\0';
+ pqSaveMessageField(res, 'M', startp);
+ /* the rest is detail; strip any leading whitespace */
+ while (*splitp && isspace((unsigned char) *splitp))
+ splitp++;
+ pqSaveMessageField(res, 'D', splitp);
+ }
+ else
+ {
+ /* single-line message, so all primary */
+ pqSaveMessageField(res, 'M', startp);
+ }
+
+ /*
* Either save error as current async result, or just emit the notice.
+ * Also, if it's an error and we were in a transaction block, assume
+ * the server has now gone to error-in-transaction state.
*/
if (isError)
{
pqClearAsyncResult(conn);
conn->result = res;
resetPQExpBuffer(&conn->errorMessage);
- appendPQExpBufferStr(&conn->errorMessage, workBuf.data);
+ appendPQExpBufferStr(&conn->errorMessage, res->errMsg);
+ if (conn->xactStatus == PQTRANS_INTRANS)
+ conn->xactStatus = PQTRANS_INERROR;
}
else
{
- PGDONOTICE(conn, workBuf.data);
+ (*res->noticeHooks.noticeRec) (res->noticeHooks.noticeRecArg, res);
PQclear(res);
}
@@ -820,6 +880,37 @@ pqGetErrorNotice2(PGconn *conn, bool isError)
return 0;
}
+/*
+ * checkXactStatus - attempt to track transaction-block status of server
+ *
+ * This is called each time we receive a command-complete message. By
+ * watching for messages from BEGIN/COMMIT/ROLLBACK commands, we can do
+ * a passable job of tracking the server's xact status. BUT: this does
+ * not work at all on 7.3 servers with AUTOCOMMIT OFF. (Man, was that
+ * feature ever a mistake.) Caveat user.
+ *
+ * The tags known here are all those used as far back as 7.0; is it worth
+ * adding those from even-older servers?
+ */
+static void
+checkXactStatus(PGconn *conn, const char *cmdTag)
+{
+ if (strcmp(cmdTag, "BEGIN") == 0)
+ conn->xactStatus = PQTRANS_INTRANS;
+ else if (strcmp(cmdTag, "COMMIT") == 0)
+ conn->xactStatus = PQTRANS_IDLE;
+ else if (strcmp(cmdTag, "ROLLBACK") == 0)
+ conn->xactStatus = PQTRANS_IDLE;
+ else if (strcmp(cmdTag, "START TRANSACTION") == 0) /* 7.3 only */
+ conn->xactStatus = PQTRANS_INTRANS;
+ /*
+ * Normally we get into INERROR state by detecting an Error message.
+ * However, if we see one of these tags then we know for sure the
+ * server is in abort state ...
+ */
+ else if (strcmp(cmdTag, "*ABORT STATE*") == 0) /* pre-7.3 only */
+ conn->xactStatus = PQTRANS_INERROR;
+}
/*
* Attempt to read a Notify response message.
@@ -832,6 +923,7 @@ static int
getNotify(PGconn *conn)
{
int be_pid;
+ int nmlen;
PGnotify *newNotify;
if (pqGetInt(&be_pid, 4, conn))
@@ -844,12 +936,14 @@ getNotify(PGconn *conn)
* can all be freed at once. We don't use NAMEDATALEN because we
* don't want to tie this interface to a specific server name length.
*/
- newNotify = (PGnotify *) malloc(sizeof(PGnotify) +
- strlen(conn->workBuffer.data) +1);
+ nmlen = strlen(conn->workBuffer.data);
+ newNotify = (PGnotify *) malloc(sizeof(PGnotify) + nmlen + 1);
if (newNotify)
{
newNotify->relname = (char *) newNotify + sizeof(PGnotify);
strcpy(newNotify->relname, conn->workBuffer.data);
+ /* fake up an empty-string extra field */
+ newNotify->extra = newNotify->relname + nmlen;
newNotify->be_pid = be_pid;
DLAddTail(conn->notifyList, DLNewElem(newNotify));
}
@@ -859,6 +953,84 @@ getNotify(PGconn *conn)
/*
+ * PQgetCopyData - read a row of data from the backend during COPY OUT
+ *
+ * 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).
+ */
+int
+pqGetCopyData2(PGconn *conn, char **buffer, int async)
+{
+ bool found;
+ int msgLength;
+
+ for (;;)
+ {
+ /*
+ * Do we have a complete line of data?
+ */
+ conn->inCursor = conn->inStart;
+ found = false;
+ while (conn->inCursor < conn->inEnd)
+ {
+ char c = conn->inBuffer[conn->inCursor++];
+
+ if (c == '\n')
+ {
+ found = true;
+ break;
+ }
+ }
+ if (!found)
+ goto nodata;
+ msgLength = conn->inCursor - conn->inStart;
+
+ /*
+ * If it's the end-of-data marker, consume it, exit COPY_OUT mode,
+ * and let caller read status with PQgetResult().
+ */
+ if (msgLength == 3 &&
+ strncmp(&conn->inBuffer[conn->inStart], "\\.\n", 3) == 0)
+ {
+ conn->inStart = conn->inCursor;
+ conn->asyncStatus = PGASYNC_BUSY;
+ return -1;
+ }
+
+ /*
+ * Pass the line back to the caller.
+ */
+ *buffer = (char *) malloc(msgLength + 1);
+ if (*buffer == NULL)
+ {
+ printfPQExpBuffer(&conn->errorMessage,
+ libpq_gettext("out of memory\n"));
+ return -2;
+ }
+ memcpy(*buffer, &conn->inBuffer[conn->inStart], msgLength);
+ (*buffer)[msgLength] = '\0'; /* Add terminating null */
+
+ /* Mark message consumed */
+ conn->inStart = conn->inCursor;
+
+ return msgLength;
+
+ nodata:
+ /* Don't block if async read requested */
+ if (async)
+ return 0;
+ /* Need to load more data */
+ if (pqWait(TRUE, FALSE, conn) ||
+ pqReadData(conn) < 0)
+ return -2;
+ }
+}
+
+
+/*
* PQgetline - gets a newline-terminated string from the backend.
*
* See fe-exec.c for documentation.
@@ -1020,7 +1192,7 @@ pqEndcopy2(PGconn *conn)
if (conn->errorMessage.len > 0)
PGDONOTICE(conn, conn->errorMessage.data);
- PGDONOTICE(conn, libpq_gettext("lost synchronization with server, resetting connection\n"));
+ PGDONOTICE(conn, libpq_gettext("lost synchronization with server, resetting connection"));
/*
* Users doing non-blocking connections need to handle the reset
diff --git a/src/interfaces/libpq/fe-protocol3.c b/src/interfaces/libpq/fe-protocol3.c
index 2fbfa01566e..05543f8e76d 100644
--- a/src/interfaces/libpq/fe-protocol3.c
+++ b/src/interfaces/libpq/fe-protocol3.c
@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/interfaces/libpq/fe-protocol3.c,v 1.1 2003/06/08 17:43:00 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/interfaces/libpq/fe-protocol3.c,v 1.2 2003/06/21 21:51:34 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -40,6 +40,8 @@ static int getRowDescriptions(PGconn *conn);
static int getAnotherTuple(PGconn *conn, int msgLength);
static int getParameterStatus(PGconn *conn);
static int getNotify(PGconn *conn);
+static int getCopyStart(PGconn *conn, ExecStatusType copytype);
+static int getReadyForQuery(PGconn *conn);
static int build_startup_packet(const PGconn *conn, char *packet,
const PQEnvironmentOption *options);
@@ -171,7 +173,7 @@ pqParseInput3(PGconn *conn)
else
{
snprintf(noticeWorkspace, sizeof(noticeWorkspace),
- libpq_gettext("message type 0x%02x arrived from server while idle\n"),
+ libpq_gettext("message type 0x%02x arrived from server while idle"),
id);
PGDONOTICE(conn, noticeWorkspace);
/* Discard the unexpected message */
@@ -201,7 +203,7 @@ pqParseInput3(PGconn *conn)
conn->asyncStatus = PGASYNC_READY;
break;
case 'Z': /* backend is ready for new query */
- if (pqGetc(&conn->xact_status, conn))
+ if (getReadyForQuery(conn))
return;
conn->asyncStatus = PGASYNC_IDLE;
break;
@@ -211,6 +213,11 @@ pqParseInput3(PGconn *conn)
PGRES_EMPTY_QUERY);
conn->asyncStatus = PGASYNC_READY;
break;
+ case '1': /* Parse Complete */
+ case '2': /* Bind Complete */
+ case '3': /* Close Complete */
+ /* Nothing to do for these message types */
+ break;
case 'S': /* parameter status */
if (getParameterStatus(conn))
return;
@@ -276,17 +283,13 @@ pqParseInput3(PGconn *conn)
}
break;
case 'G': /* Start Copy In */
- if (pqGetc(&conn->copy_is_binary, conn))
+ if (getCopyStart(conn, PGRES_COPY_IN))
return;
- /* XXX we currently ignore the rest of the message */
- conn->inCursor = conn->inStart + 5 + msgLength;
conn->asyncStatus = PGASYNC_COPY_IN;
break;
case 'H': /* Start Copy Out */
- if (pqGetc(&conn->copy_is_binary, conn))
+ if (getCopyStart(conn, PGRES_COPY_OUT))
return;
- /* XXX we currently ignore the rest of the message */
- conn->inCursor = conn->inStart + 5 + msgLength;
conn->asyncStatus = PGASYNC_COPY_OUT;
conn->copy_already_done = 0;
break;
@@ -398,6 +401,9 @@ getRowDescriptions(PGconn *conn)
MemSet((char *) result->attDescs, 0, nfields * sizeof(PGresAttDesc));
}
+ /* result->binary is true only if ALL columns are binary */
+ result->binary = (nfields > 0) ? 1 : 0;
+
/* get type info */
for (i = 0; i < nfields; i++)
{
@@ -430,10 +436,15 @@ getRowDescriptions(PGconn *conn)
result->attDescs[i].name = pqResultStrdup(result,
conn->workBuffer.data);
+ result->attDescs[i].tableid = tableid;
+ result->attDescs[i].columnid = columnid;
+ result->attDescs[i].format = format;
result->attDescs[i].typid = typid;
result->attDescs[i].typlen = typlen;
result->attDescs[i].atttypmod = atttypmod;
- /* XXX todo: save tableid/columnid, format too */
+
+ if (format != 1)
+ result->binary = 0;
}
/* Success! */
@@ -503,7 +514,9 @@ getAnotherTuple(PGconn *conn, int msgLength)
vlen = 0;
if (tup[i].value == NULL)
{
- tup[i].value = (char *) pqResultAlloc(result, vlen + 1, false);
+ bool isbinary = (result->attDescs[i].format != 0);
+
+ tup[i].value = (char *) pqResultAlloc(result, vlen + 1, isbinary);
if (tup[i].value == NULL)
goto outOfMemory;
}
@@ -553,6 +566,7 @@ pqGetErrorNotice3(PGconn *conn, bool isError)
PGresult *res;
PQExpBufferData workBuf;
char id;
+ const char *val;
/*
* Make a PGresult to hold the accumulated fields. We temporarily
@@ -580,68 +594,63 @@ pqGetErrorNotice3(PGconn *conn, bool isError)
break; /* terminator found */
if (pqGets(&workBuf, conn))
goto fail;
- switch (id)
- {
- case 'S':
- res->errSeverity = pqResultStrdup(res, workBuf.data);
- break;
- case 'C':
- res->errCode = pqResultStrdup(res, workBuf.data);
- break;
- case 'M':
- res->errPrimary = pqResultStrdup(res, workBuf.data);
- break;
- case 'D':
- res->errDetail = pqResultStrdup(res, workBuf.data);
- break;
- case 'H':
- res->errHint = pqResultStrdup(res, workBuf.data);
- break;
- case 'P':
- res->errPosition = pqResultStrdup(res, workBuf.data);
- break;
- case 'W':
- res->errContext = pqResultStrdup(res, workBuf.data);
- break;
- case 'F':
- res->errFilename = pqResultStrdup(res, workBuf.data);
- break;
- case 'L':
- res->errLineno = pqResultStrdup(res, workBuf.data);
- break;
- case 'R':
- res->errFuncname = pqResultStrdup(res, workBuf.data);
- break;
- default:
- /* silently ignore any other field type */
- break;
- }
+ pqSaveMessageField(res, id, workBuf.data);
}
/*
* Now build the "overall" error message for PQresultErrorMessage.
- *
- * XXX this should be configurable somehow.
*/
resetPQExpBuffer(&workBuf);
- if (res->errSeverity)
- appendPQExpBuffer(&workBuf, "%s: ", res->errSeverity);
- if (res->errPrimary)
- appendPQExpBufferStr(&workBuf, res->errPrimary);
- /* translator: %s represents a digit string */
- if (res->errPosition)
- appendPQExpBuffer(&workBuf, libpq_gettext(" at character %s"),
- res->errPosition);
+ val = PQresultErrorField(res, 'S'); /* Severity */
+ if (val)
+ appendPQExpBuffer(&workBuf, "%s: ", val);
+ if (conn->verbosity == PQERRORS_VERBOSE)
+ {
+ val = PQresultErrorField(res, 'C'); /* SQLSTATE Code */
+ if (val)
+ appendPQExpBuffer(&workBuf, "%s: ", val);
+ }
+ val = PQresultErrorField(res, 'M'); /* Primary message */
+ if (val)
+ appendPQExpBufferStr(&workBuf, val);
+ val = PQresultErrorField(res, 'P'); /* Position */
+ if (val)
+ {
+ /* translator: %s represents a digit string */
+ appendPQExpBuffer(&workBuf, libpq_gettext(" at character %s"), val);
+ }
appendPQExpBufferChar(&workBuf, '\n');
- if (res->errDetail)
- appendPQExpBuffer(&workBuf, libpq_gettext("DETAIL: %s\n"),
- res->errDetail);
- if (res->errHint)
- appendPQExpBuffer(&workBuf, libpq_gettext("HINT: %s\n"),
- res->errHint);
- if (res->errContext)
- appendPQExpBuffer(&workBuf, libpq_gettext("CONTEXT: %s\n"),
- res->errContext);
+ if (conn->verbosity != PQERRORS_TERSE)
+ {
+ val = PQresultErrorField(res, 'D'); /* Detail */
+ if (val)
+ appendPQExpBuffer(&workBuf, libpq_gettext("DETAIL: %s\n"), val);
+ val = PQresultErrorField(res, 'H'); /* Hint */
+ if (val)
+ appendPQExpBuffer(&workBuf, libpq_gettext("HINT: %s\n"), val);
+ val = PQresultErrorField(res, 'W'); /* Where */
+ if (val)
+ appendPQExpBuffer(&workBuf, libpq_gettext("CONTEXT: %s\n"), val);
+ }
+ if (conn->verbosity == PQERRORS_VERBOSE)
+ {
+ const char *valf;
+ const char *vall;
+
+ valf = PQresultErrorField(res, 'F'); /* File */
+ vall = PQresultErrorField(res, 'L'); /* Line */
+ val = PQresultErrorField(res, 'R'); /* Routine */
+ if (val || valf || vall)
+ {
+ appendPQExpBufferStr(&workBuf, libpq_gettext("LOCATION: "));
+ if (val)
+ appendPQExpBuffer(&workBuf, libpq_gettext("%s, "), val);
+ if (valf && vall) /* unlikely we'd have just one */
+ appendPQExpBuffer(&workBuf, libpq_gettext("%s:%s"),
+ valf, vall);
+ appendPQExpBufferChar(&workBuf, '\n');
+ }
+ }
/*
* Either save error as current async result, or just emit the notice.
@@ -656,7 +665,9 @@ pqGetErrorNotice3(PGconn *conn, bool isError)
}
else
{
- PGDONOTICE(conn, workBuf.data);
+ /* We can cheat a little here and not copy the message. */
+ res->errMsg = workBuf.data;
+ (*res->noticeHooks.noticeRec) (res->noticeHooks.noticeRecArg, res);
PQclear(res);
}
@@ -710,35 +721,216 @@ static int
getNotify(PGconn *conn)
{
int be_pid;
+ char *svname;
+ int nmlen;
+ int extralen;
PGnotify *newNotify;
if (pqGetInt(&be_pid, 4, conn))
return EOF;
if (pqGets(&conn->workBuffer, conn))
return EOF;
+ /* must save name while getting extra string */
+ svname = strdup(conn->workBuffer.data);
+ if (!svname)
+ return EOF;
+ if (pqGets(&conn->workBuffer, conn))
+ {
+ free(svname);
+ return EOF;
+ }
/*
- * Store the relation name right after the PQnotify structure so it
+ * Store the strings right after the PQnotify structure so it
* can all be freed at once. We don't use NAMEDATALEN because we
* don't want to tie this interface to a specific server name length.
*/
- newNotify = (PGnotify *) malloc(sizeof(PGnotify) +
- strlen(conn->workBuffer.data) +1);
+ nmlen = strlen(svname);
+ extralen = strlen(conn->workBuffer.data);
+ newNotify = (PGnotify *) malloc(sizeof(PGnotify) + nmlen + extralen + 2);
if (newNotify)
{
newNotify->relname = (char *) newNotify + sizeof(PGnotify);
- strcpy(newNotify->relname, conn->workBuffer.data);
+ strcpy(newNotify->relname, svname);
+ newNotify->extra = newNotify->relname + nmlen + 1;
+ strcpy(newNotify->extra, conn->workBuffer.data);
newNotify->be_pid = be_pid;
DLAddTail(conn->notifyList, DLNewElem(newNotify));
}
- /* Swallow extra string (not presently used) */
- if (pqGets(&conn->workBuffer, conn))
+ free(svname);
+ return 0;
+}
+
+/*
+ * getCopyStart - process CopyInResponse or CopyOutResponse message
+ *
+ * parseInput already read the message type and length.
+ */
+static int
+getCopyStart(PGconn *conn, ExecStatusType copytype)
+{
+ PGresult *result;
+ int nfields;
+ int i;
+
+ result = PQmakeEmptyPGresult(conn, copytype);
+
+ if (pqGetc(&conn->copy_is_binary, conn))
+ {
+ PQclear(result);
return EOF;
+ }
+ result->binary = conn->copy_is_binary;
+ /* the next two bytes are the number of fields */
+ if (pqGetInt(&(result->numAttributes), 2, conn))
+ {
+ PQclear(result);
+ return EOF;
+ }
+ 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));
+ }
+
+ for (i = 0; i < nfields; i++)
+ {
+ int format;
+ if (pqGetInt(&format, 2, conn))
+ {
+ PQclear(result);
+ return EOF;
+ }
+
+ /*
+ * Since pqGetInt treats 2-byte integers as unsigned, we need to
+ * coerce these results to signed form.
+ */
+ format = (int) ((int16) format);
+
+ result->attDescs[i].format = format;
+ }
+
+ /* Success! */
+ conn->result = result;
return 0;
}
+/*
+ * getReadyForQuery - process ReadyForQuery message
+ */
+static int
+getReadyForQuery(PGconn *conn)
+{
+ char xact_status;
+
+ if (pqGetc(&xact_status, conn))
+ return EOF;
+ switch (xact_status)
+ {
+ case 'I':
+ conn->xactStatus = PQTRANS_IDLE;
+ break;
+ case 'T':
+ conn->xactStatus = PQTRANS_INTRANS;
+ break;
+ case 'E':
+ conn->xactStatus = PQTRANS_INERROR;
+ break;
+ default:
+ conn->xactStatus = PQTRANS_UNKNOWN;
+ break;
+ }
+
+ return 0;
+}
+
+/*
+ * PQgetCopyData - read a row of data from the backend during COPY OUT
+ *
+ * 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).
+ */
+int
+pqGetCopyData3(PGconn *conn, char **buffer, int async)
+{
+ char id;
+ int msgLength;
+ int avail;
+
+ for (;;)
+ {
+ /*
+ * Do we have the next input message? To make life simpler for async
+ * callers, we keep returning 0 until the next message is fully
+ * available, even if it is not Copy Data.
+ */
+ conn->inCursor = conn->inStart;
+ if (pqGetc(&id, conn))
+ goto nodata;
+ if (pqGetInt(&msgLength, 4, conn))
+ goto nodata;
+ avail = conn->inEnd - conn->inCursor;
+ if (avail < msgLength - 4)
+ goto nodata;
+
+ /*
+ * If it's anything except Copy Data, exit COPY_OUT mode and let
+ * caller read status with PQgetResult(). The normal case is that
+ * it's Copy Done, but we let parseInput read that.
+ */
+ if (id != 'd')
+ {
+ conn->asyncStatus = PGASYNC_BUSY;
+ return -1;
+ }
+
+ /*
+ * Drop zero-length messages (shouldn't happen anyway). Otherwise
+ * pass the data back to the caller.
+ */
+ msgLength -= 4;
+ if (msgLength > 0)
+ {
+ *buffer = (char *) malloc(msgLength + 1);
+ if (*buffer == NULL)
+ {
+ printfPQExpBuffer(&conn->errorMessage,
+ libpq_gettext("out of memory\n"));
+ return -2;
+ }
+ memcpy(*buffer, &conn->inBuffer[conn->inCursor], msgLength);
+ (*buffer)[msgLength] = '\0'; /* Add terminating null */
+
+ /* Mark message consumed */
+ conn->inStart = conn->inCursor + msgLength;
+
+ return msgLength;
+ }
+
+ /* Empty, so drop it and loop around for another */
+ conn->inStart = conn->inCursor;
+ continue;
+
+ nodata:
+ /* Don't block if async read requested */
+ if (async)
+ return 0;
+ /* Need to load more data */
+ if (pqWait(TRUE, FALSE, conn) ||
+ pqReadData(conn) < 0)
+ return -2;
+ }
+}
/*
* PQgetline - gets a newline-terminated string from the backend.
@@ -1108,7 +1300,7 @@ pqFunctionCall3(PGconn *conn, Oid fnid,
continue;
break;
case 'Z': /* backend is ready for new query */
- if (pqGetc(&conn->xact_status, conn))
+ if (getReadyForQuery(conn))
continue;
/* consume the message and exit */
conn->inStart += 5 + msgLength;
diff --git a/src/interfaces/libpq/libpq-fe.h b/src/interfaces/libpq/libpq-fe.h
index da61b770906..d8ff52d6b0e 100644
--- a/src/interfaces/libpq/libpq-fe.h
+++ b/src/interfaces/libpq/libpq-fe.h
@@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $Id: libpq-fe.h,v 1.93 2003/06/08 17:43:00 tgl Exp $
+ * $Id: libpq-fe.h,v 1.94 2003/06/21 21:51:34 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -88,6 +88,22 @@ typedef enum
PGRES_FATAL_ERROR /* query failed */
} ExecStatusType;
+typedef enum
+{
+ PQTRANS_IDLE, /* connection idle */
+ PQTRANS_ACTIVE, /* command in progress */
+ PQTRANS_INTRANS, /* idle, within transaction block */
+ PQTRANS_INERROR, /* idle, within failed transaction */
+ PQTRANS_UNKNOWN /* cannot determine status */
+} PGTransactionStatusType;
+
+typedef enum
+{
+ PQERRORS_TERSE, /* single-line error messages */
+ PQERRORS_DEFAULT, /* recommended style */
+ PQERRORS_VERBOSE /* all the facts, ma'am */
+} PGVerbosity;
+
/* PGconn encapsulates a connection to the backend.
* The contents of this struct are not supposed to be known to applications.
*/
@@ -108,12 +124,13 @@ typedef struct pg_result PGresult;
*/
typedef struct pgNotify
{
- char *relname; /* name of relation containing data */
- int be_pid; /* process id of backend */
+ char *relname; /* notification condition name */
+ int be_pid; /* process ID of server process */
+ char *extra; /* notification parameter */
} PGnotify;
-/* PQnoticeProcessor is the function type for the notice-message callback.
- */
+/* Function types for notice-handling callbacks */
+typedef void (*PQnoticeReceiver) (void *arg, const PGresult *res);
typedef void (*PQnoticeProcessor) (void *arg, const char *message);
/* Print options for PQprint() */
@@ -227,6 +244,10 @@ extern char *PQport(const PGconn *conn);
extern char *PQtty(const PGconn *conn);
extern char *PQoptions(const PGconn *conn);
extern ConnStatusType PQstatus(const PGconn *conn);
+extern PGTransactionStatusType PQtransactionStatus(const PGconn *conn);
+extern const char *PQparameterStatus(const PGconn *conn,
+ const char *paramName);
+extern int PQprotocolVersion(const PGconn *conn);
extern char *PQerrorMessage(const PGconn *conn);
extern int PQsocket(const PGconn *conn);
extern int PQbackendPID(const PGconn *conn);
@@ -238,42 +259,58 @@ extern int PQsetClientEncoding(PGconn *conn, const char *encoding);
extern SSL *PQgetssl(PGconn *conn);
#endif
+/* Set verbosity for PQerrorMessage and PQresultErrorMessage */
+extern PGVerbosity PQsetErrorVerbosity(PGconn *conn, PGVerbosity verbosity);
/* Enable/disable tracing */
extern void PQtrace(PGconn *conn, FILE *debug_port);
extern void PQuntrace(PGconn *conn);
-/* Override default notice processor */
+/* Override default notice handling routines */
+extern PQnoticeReceiver PQsetNoticeReceiver(PGconn *conn,
+ PQnoticeReceiver proc,
+ void *arg);
extern PQnoticeProcessor PQsetNoticeProcessor(PGconn *conn,
PQnoticeProcessor proc,
void *arg);
/* === in fe-exec.c === */
-/* Quoting strings before inclusion in queries. */
-extern size_t PQescapeString(char *to, const char *from, size_t length);
-extern unsigned char *PQescapeBytea(const unsigned char *bintext, size_t binlen,
- size_t *bytealen);
-extern unsigned char *PQunescapeBytea(const unsigned char *strtext,
- size_t *retbuflen);
-extern void PQfreemem(void *ptr);
-
-
/* Simple synchronous query */
extern PGresult *PQexec(PGconn *conn, const char *query);
-extern PGnotify *PQnotifies(PGconn *conn);
-/* Exists for backward compatibility. bjm 2003-03-24 */
-#define PQfreeNotify(ptr) PQfreemem(ptr)
+extern PGresult *PQexecParams(PGconn *conn,
+ const char *command,
+ int nParams,
+ const Oid *paramTypes,
+ const char * const *paramValues,
+ const int *paramLengths,
+ const int *paramFormats,
+ int resultFormat);
/* Interface for multiple-result or asynchronous queries */
extern int PQsendQuery(PGconn *conn, const char *query);
+extern int PQsendQueryParams(PGconn *conn,
+ const char *command,
+ int nParams,
+ const Oid *paramTypes,
+ const char * const *paramValues,
+ const int *paramLengths,
+ const int *paramFormats,
+ int resultFormat);
extern PGresult *PQgetResult(PGconn *conn);
/* Routines for managing an asynchronous query */
extern int PQisBusy(PGconn *conn);
extern int PQconsumeInput(PGconn *conn);
+/* LISTEN/NOTIFY support */
+extern PGnotify *PQnotifies(PGconn *conn);
+
/* Routines for copy in/out */
+extern int PQputCopyData(PGconn *conn, const char *buffer, int nbytes);
+extern int PQputCopyEnd(PGconn *conn, const char *errormsg);
+extern int PQgetCopyData(PGconn *conn, char **buffer, int async);
+/* Deprecated routines for copy in/out */
extern int PQgetline(PGconn *conn, char *string, int length);
extern int PQputline(PGconn *conn, const char *string);
extern int PQgetlineAsync(PGconn *conn, char *buffer, int bufsize);
@@ -303,11 +340,15 @@ extern PGresult *PQfn(PGconn *conn,
extern ExecStatusType PQresultStatus(const PGresult *res);
extern char *PQresStatus(ExecStatusType status);
extern char *PQresultErrorMessage(const PGresult *res);
+extern char *PQresultErrorField(const PGresult *res, int fieldcode);
extern int PQntuples(const PGresult *res);
extern int PQnfields(const PGresult *res);
extern int PQbinaryTuples(const PGresult *res);
extern char *PQfname(const PGresult *res, int field_num);
extern int PQfnumber(const PGresult *res, const char *field_name);
+extern Oid PQftable(const PGresult *res, int field_num);
+extern int PQftablecol(const PGresult *res, int field_num);
+extern int PQfformat(const PGresult *res, int field_num);
extern Oid PQftype(const PGresult *res, int field_num);
extern int PQfsize(const PGresult *res, int field_num);
extern int PQfmod(const PGresult *res, int field_num);
@@ -322,6 +363,12 @@ extern int PQgetisnull(const PGresult *res, int tup_num, int field_num);
/* Delete a PGresult */
extern void PQclear(PGresult *res);
+/* For freeing other alloc'd results, such as PGnotify structs */
+extern void PQfreemem(void *ptr);
+
+/* Exists for backward compatibility. bjm 2003-03-24 */
+#define PQfreeNotify(ptr) PQfreemem(ptr)
+
/*
* Make an empty PGresult with given status (some apps find this
* useful). If conn is not NULL and status indicates an error, the
@@ -329,26 +376,33 @@ extern void PQclear(PGresult *res);
*/
extern PGresult *PQmakeEmptyPGresult(PGconn *conn, ExecStatusType status);
+
+/* Quoting strings before inclusion in queries. */
+extern size_t PQescapeString(char *to, const char *from, size_t length);
+extern unsigned char *PQescapeBytea(const unsigned char *bintext, size_t binlen,
+ size_t *bytealen);
+extern unsigned char *PQunescapeBytea(const unsigned char *strtext,
+ size_t *retbuflen);
+
+
+
/* === in fe-print.c === */
-extern void
-PQprint(FILE *fout, /* output stream */
- const PGresult *res,
- const PQprintOpt *ps); /* option structure */
+extern void PQprint(FILE *fout, /* output stream */
+ const PGresult *res,
+ const PQprintOpt *ps); /* option structure */
/*
* really old printing routines
*/
-extern void
-PQdisplayTuples(const PGresult *res,
+extern void PQdisplayTuples(const PGresult *res,
FILE *fp, /* where to send the output */
int fillAlign, /* pad the fields with spaces */
const char *fieldSep, /* field separator */
int printHeader, /* display headers? */
int quiet);
-extern void
-PQprintTuples(const PGresult *res,
+extern void PQprintTuples(const PGresult *res,
FILE *fout, /* output stream */
int printAttName, /* print attribute names */
int terseOutput, /* delimiter bars */
diff --git a/src/interfaces/libpq/libpq-int.h b/src/interfaces/libpq/libpq-int.h
index 019683ac265..f651320e1cb 100644
--- a/src/interfaces/libpq/libpq-int.h
+++ b/src/interfaces/libpq/libpq-int.h
@@ -12,7 +12,7 @@
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $Id: libpq-int.h,v 1.74 2003/06/14 17:49:54 momjian Exp $
+ * $Id: libpq-int.h,v 1.75 2003/06/21 21:51:34 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -20,6 +20,9 @@
#ifndef LIBPQ_INT_H
#define LIBPQ_INT_H
+/* We assume libpq-fe.h has already been included. */
+#include "postgres_fe.h"
+
#include <time.h>
#include <sys/types.h>
#ifndef WIN32
@@ -28,13 +31,10 @@
#if defined(WIN32) && (!defined(ssize_t))
-typedef int ssize_t; /* ssize_t doesn't exist in VC (atleast
+typedef int ssize_t; /* ssize_t doesn't exist in VC (at least
* not VC6) */
#endif
-/* We assume libpq-fe.h has already been included. */
-#include "postgres_fe.h"
-
/* include stuff common to fe and be */
#include "getaddrinfo.h"
#include "libpq/pqcomm.h"
@@ -78,7 +78,10 @@ union pgresult_data
typedef struct pgresAttDesc
{
- char *name; /* type name */
+ char *name; /* column name */
+ Oid tableid; /* source table, if known */
+ int columnid; /* source column, if known */
+ int format; /* format code for value (text/binary) */
Oid typid; /* type id */
int typlen; /* type size */
int atttypmod; /* type-specific modifier info */
@@ -91,7 +94,7 @@ typedef struct pgresAttDesc
*
* The value pointer always points to a null-terminated area; we add a
* null (zero) byte after whatever the backend sends us. This is only
- * particularly useful for text tuples ... with a binary value, the
+ * particularly useful for text values ... with a binary value, the
* value might have embedded nulls, so the application can't use C string
* operators on it. But we add a null anyway for consistency.
* Note that the value itself does not contain a length word.
@@ -111,6 +114,23 @@ typedef struct pgresAttValue
* byte */
} PGresAttValue;
+/* Typedef for message-field list entries */
+typedef struct pgMessageField
+{
+ struct pgMessageField *next; /* list link */
+ char code; /* field code */
+ char contents[1]; /* field value (VARIABLE LENGTH) */
+} PGMessageField;
+
+/* Fields needed for notice handling */
+typedef struct
+{
+ PQnoticeReceiver noticeRec; /* notice message receiver */
+ void *noticeRecArg;
+ PQnoticeProcessor noticeProc; /* notice message processor */
+ void *noticeProcArg;
+} PGNoticeHooks;
+
struct pg_result
{
int ntups;
@@ -118,10 +138,10 @@ struct pg_result
PGresAttDesc *attDescs;
PGresAttValue **tuples; /* each PGresTuple is an array of
* PGresAttValue's */
- int tupArrSize; /* size of tuples array allocated */
+ int tupArrSize; /* allocated size of tuples array */
ExecStatusType resultStatus;
char cmdStatus[CMDSTATUS_LEN]; /* cmd status from the
- * last query */
+ * query */
int binary; /* binary tuple values if binary == 1,
* otherwise text */
@@ -129,35 +149,23 @@ struct pg_result
* These fields are copied from the originating PGconn, so that
* operations on the PGresult don't have to reference the PGconn.
*/
- PQnoticeProcessor noticeHook; /* notice/error message processor */
- void *noticeArg;
+ PGNoticeHooks noticeHooks;
int client_encoding; /* encoding id */
/*
* Error information (all NULL if not an error result). errMsg is the
* "overall" error message returned by PQresultErrorMessage. If we
- * got a field-ized error from the server then the additional fields
- * may be set.
+ * have per-field info then it is stored in a linked list.
*/
char *errMsg; /* error message, or NULL if no error */
-
- char *errSeverity; /* severity code */
- char *errCode; /* SQLSTATE code */
- char *errPrimary; /* primary message text */
- char *errDetail; /* detail text */
- char *errHint; /* hint text */
- char *errPosition; /* cursor position */
- char *errContext; /* location information */
- char *errFilename; /* source-code file name */
- char *errLineno; /* source-code line number */
- char *errFuncname; /* source-code function name */
+ PGMessageField *errFields; /* message broken into fields */
/* All NULL attributes in the query result point to this null string */
char null_field[1];
/*
- * Space management information. Note that attDescs and errMsg, if
- * not null, point into allocated blocks. But tuples points to a
+ * Space management information. Note that attDescs and error stuff,
+ * if not null, point into allocated blocks. But tuples points to a
* separately malloc'd block, so that we can realloc it.
*/
PGresult_data *curBlock; /* most recently allocated block */
@@ -245,18 +253,18 @@ struct pg_conn
/* Optional file to write trace info to */
FILE *Pfdebug;
- /* Callback procedure for notice/error message processing */
- PQnoticeProcessor noticeHook;
- void *noticeArg;
+ /* Callback procedures for notice message processing */
+ PGNoticeHooks noticeHooks;
/* Status indicators */
ConnStatusType status;
PGAsyncStatusType asyncStatus;
- char xact_status; /* status flag from latest ReadyForQuery */
- char copy_is_binary; /* 1 = copy binary, 0 = copy text */
- int copy_already_done; /* # bytes already returned in COPY OUT */
+ PGTransactionStatusType xactStatus;
+ /* note: xactStatus never changes to ACTIVE */
int nonblocking; /* whether this connection is using a
* blocking socket to the backend or not */
+ char copy_is_binary; /* 1 = copy binary, 0 = copy text */
+ int copy_already_done; /* # bytes already returned in COPY OUT */
Dllist *notifyList; /* Notify msgs not yet handed to
* application */
@@ -281,6 +289,7 @@ struct pg_conn
char cryptSalt[2]; /* password salt received from backend */
pgParameterStatus *pstatus; /* ParameterStatus data */
int client_encoding; /* encoding id */
+ PGVerbosity verbosity; /* error/notice message verbosity */
PGlobjfuncs *lobjfuncs; /* private state for large-object access
* fns */
@@ -351,10 +360,12 @@ extern char *pqResultStrdup(PGresult *res, const char *str);
extern void pqClearAsyncResult(PGconn *conn);
extern void pqSaveErrorResult(PGconn *conn);
extern PGresult *pqPrepareAsyncResult(PGconn *conn);
+extern void pqInternalNotice(const PGNoticeHooks *hooks, const char *msgtext);
extern int pqAddTuple(PGresult *res, PGresAttValue *tup);
+extern void pqSaveMessageField(PGresult *res, char code,
+ const char *value);
extern void pqSaveParameterStatus(PGconn *conn, const char *name,
const char *value);
-extern const char *pqGetParameterStatus(PGconn *conn, const char *name);
extern void pqHandleSendFailure(PGconn *conn);
/* === in fe-protocol2.c === */
@@ -364,6 +375,7 @@ extern PostgresPollingStatusType pqSetenvPoll(PGconn *conn);
extern char *pqBuildStartupPacket2(PGconn *conn, int *packetlen,
const PQEnvironmentOption *options);
extern void pqParseInput2(PGconn *conn);
+extern int pqGetCopyData2(PGconn *conn, char **buffer, int async);
extern int pqGetline2(PGconn *conn, char *s, int maxlen);
extern int pqGetlineAsync2(PGconn *conn, char *buffer, int bufsize);
extern int pqEndcopy2(PGconn *conn);
@@ -378,6 +390,7 @@ extern char *pqBuildStartupPacket3(PGconn *conn, int *packetlen,
const PQEnvironmentOption *options);
extern void pqParseInput3(PGconn *conn);
extern int pqGetErrorNotice3(PGconn *conn, bool isError);
+extern int pqGetCopyData3(PGconn *conn, char **buffer, int async);
extern int pqGetline3(PGconn *conn, char *s, int maxlen);
extern int pqGetlineAsync3(PGconn *conn, char *buffer, int bufsize);
extern int pqEndcopy3(PGconn *conn);
@@ -393,6 +406,7 @@ extern PGresult *pqFunctionCall3(PGconn *conn, Oid fnid,
* for Get, EOF merely means the buffer is exhausted, not that there is
* necessarily any error.
*/
+extern int pqCheckOutBufferSpace(int bytes_needed, PGconn *conn);
extern int pqCheckInBufferSpace(int bytes_needed, PGconn *conn);
extern int pqGetc(char *result, PGconn *conn);
extern int pqPutc(char c, PGconn *conn);
@@ -423,10 +437,10 @@ extern ssize_t pqsecure_write(PGconn *, const void *ptr, size_t len);
/* Note: PGDONOTICE macro will work if applied to either PGconn or PGresult */
#define PGDONOTICE(conn,message) \
- ((*(conn)->noticeHook) ((conn)->noticeArg, (message)))
+ pqInternalNotice(&(conn)->noticeHooks, (message))
/*
- * this is so that we can check is a connection is non-blocking internally
+ * this is so that we can check if a connection is non-blocking internally
* without the overhead of a function call
*/
#define pqIsnonblocking(conn) ((conn)->nonblocking)
diff --git a/src/interfaces/libpq/libpqdll.def b/src/interfaces/libpq/libpqdll.def
index f8432fc713a..8ff902f5218 100644
--- a/src/interfaces/libpq/libpqdll.def
+++ b/src/interfaces/libpq/libpqdll.def
@@ -97,4 +97,17 @@ EXPORTS
pg_utf_mblen @ 93
PQunescapeBytea @ 94
PQfreemem @ 95
-
+ PQtransactionStatus @ 96
+ PQparameterStatus @ 97
+ PQprotocolVersion @ 98
+ PQsetErrorVerbosity @ 99
+ PQsetNoticeReceiver @ 100
+ PQexecParams @ 101
+ PQsendQueryParams @ 102
+ PQputCopyData @ 103
+ PQputCopyEnd @ 104
+ PQgetCopyData @ 105
+ PQresultErrorField @ 106
+ PQftable @ 107
+ PQftablecol @ 108
+ PQfformat @ 109