aboutsummaryrefslogtreecommitdiff
path: root/src/interfaces/libpq/fe-exec.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/interfaces/libpq/fe-exec.c')
-rw-r--r--src/interfaces/libpq/fe-exec.c782
1 files changed, 598 insertions, 184 deletions
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)
{