Update libpq to make new features of FE/BE protocol available to
client applications. Some editorial work on libpq.sgml, too.
This commit is contained in:
parent
b8d601e735
commit
efc3a25bb0
File diff suppressed because it is too large
Load Diff
@ -1,5 +1,5 @@
|
||||
<!--
|
||||
$Header: /cvsroot/pgsql/doc/src/sgml/lobj.sgml,v 1.28 2003/03/13 01:30:28 petere Exp $
|
||||
$Header: /cvsroot/pgsql/doc/src/sgml/lobj.sgml,v 1.29 2003/06/21 21:51:33 tgl Exp $
|
||||
-->
|
||||
|
||||
<chapter id="largeObjects">
|
||||
@ -181,7 +181,8 @@ int lo_open(PGconn *conn, Oid lobjId, int mode);
|
||||
<function>lo_open</function> returns a large object descriptor
|
||||
for later use in <function>lo_read</function>, <function>lo_write</function>,
|
||||
<function>lo_lseek</function>, <function>lo_tell</function>, and
|
||||
<function>lo_close</function>.
|
||||
<function>lo_close</function>. The descriptor is only valid for
|
||||
the duration of the current transaction.
|
||||
</para>
|
||||
</sect2>
|
||||
|
||||
@ -256,6 +257,11 @@ int lo_close(PGconn *conn, int fd);
|
||||
<function>lo_open</function>. On success, <function>lo_close</function>
|
||||
returns zero. On error, the return value is negative.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
Any large object descriptors that remain open at the end of a
|
||||
transaction will be closed automatically.
|
||||
</para>
|
||||
</sect2>
|
||||
|
||||
<sect2>
|
||||
@ -296,6 +302,14 @@ SELECT lo_export(image.raster, '/tmp/motd') FROM image
|
||||
WHERE name = 'beautiful image';
|
||||
</programlisting>
|
||||
</para>
|
||||
|
||||
<para>
|
||||
These functions read and write files in the server's filesystem, using the
|
||||
permissions of the database's owning user. Therefore, their use is restricted
|
||||
to superusers. (In contrast, the client-side import and export functions
|
||||
read and write files in the client's filesystem, using the permissions of
|
||||
the client program. Their use is not restricted.)
|
||||
</para>
|
||||
</sect1>
|
||||
|
||||
<sect1 id="lo-examplesect">
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
{
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -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))
|
||||
{
|
||||
|
@ -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
|
||||
@ -800,19 +816,63 @@ pqGetErrorNotice2(PGconn *conn, bool isError)
|
||||
res->resultStatus = isError ? PGRES_FATAL_ERROR : PGRES_NONFATAL_ERROR;
|
||||
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));
|
||||
}
|
||||
@ -858,6 +952,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.
|
||||
*
|
||||
@ -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
|
||||
|
@ -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;
|
||||
|
@ -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 */
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
|
Loading…
x
Reference in New Issue
Block a user