Re: exec_execute_message crash

Поиск
Список
Период
Сортировка
От Tatsuo Ishii
Тема Re: exec_execute_message crash
Дата
Msg-id 20091231.155611.128866384.t-ishii@sraoss.co.jp
обсуждение исходный текст
Ответ на Re: exec_execute_message crash  (Tatsuo Ishii <ishii@postgresql.org>)
Ответы Re: exec_execute_message crash
Список pgsql-hackers
> > It'd be nice to have a test case for this, hint hint ...
> 
> Still working on...

Done. Inclded are C test program along with modified fe-exec.c.

The modification made to fe-exec.c is sending Sync after Parse, Bind
and Describe. Pgpool-II does this in order to get current transaction
status.
--
Tatsuo Ishii
SRA OSS, Inc. Japan
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>

#include "libpq-fe.h"
#include "libpq/libpq-fs.h"

int main(int argc, char **argv) {PGconn *conn;PGresult *res;int doTrace = 0;int doTransaction = 1;int i;
static char *commands[] = {    "SELECT 1 FROM pg_catalog.pg_type WHERE typname = 'smgr' AND
typinput='array_in'::regproc",   "SELECT typname FROM pg_catalog.pg_type WHERE oid = 210",    "SELECT NULL , n.nspname,
ct.relname,  a.attname,  a.attnum,  ci.relname FROM pg_catalog.pg_namespace n, pg_catalog.pg_class ct,
pg_catalog.pg_classci, pg_catalog.pg_attribute a, pg_catalog.pg_index i  WHERE ct.oid=i.indrelid AND
ci.oid=i.indexrelid AND a.attrelid=ci.oid AND i.indisprimary  AND ct.relname = 'mst_Ucompany_feature_setting'  AND
ct.relnamespace= n.oid  AND n.nspname = 'foo' ORDER BY 1, 2, 3",    "SELECT * FROM foo",};conn =
PQconnectdb("user=t-ishiidbname=test port=5433");if (PQstatus(conn) == CONNECTION_BAD) {    printf("Unable to connect
todb\n");    PQfinish(conn);    return 1;}
 
if(doTrace == 1)  PQtrace(conn, stdout);
if(doTransaction)  PQexec(conn,"BEGIN;");
for (i=0;i<sizeof(commands)/sizeof(char *);i++){    char *command = commands[i];    res = PQexecParams(conn, command,
0,NULL, NULL, NULL, NULL, 0);    switch(PQresultStatus(res)) {        case PGRES_COMMAND_OK:        case
PGRES_TUPLES_OK:           fprintf(stderr, "\"%s\" : succeeded\n", command);            break;        default:
 fprintf(stderr, "\"%s\" failed: %s\n", command, PQresultErrorMessage(res));            break;    }    PQclear(res);
 
}
if(doTransaction == 1) {    PQexec(conn,"COMMIT;");}PQfinish(conn);return 0;
}
/*-------------------------------------------------------------------------** fe-exec.c*      functions related to
sendinga query down to the backend** Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group* Portions
Copyright(c) 1994, Regents of the University of California*** IDENTIFICATION*      $PostgreSQL:
pgsql/src/interfaces/libpq/fe-exec.c,v1.203 2009/06/11 14:49:13 momjian Exp
$**-------------------------------------------------------------------------*/
#include "postgres_fe.h"

#include <ctype.h>
#include <fcntl.h>

#include "libpq-fe.h"
#include "libpq-int.h"

#include "mb/pg_wchar.h"

#ifdef WIN32
#include "win32.h"
#else
#include <unistd.h>
#endif

/* keep this in same order as ExecStatusType in libpq-fe.h */
char       *const pgresStatus[] =
{"PGRES_EMPTY_QUERY","PGRES_COMMAND_OK","PGRES_TUPLES_OK","PGRES_COPY_OUT","PGRES_COPY_IN","PGRES_BAD_RESPONSE","PGRES_NONFATAL_ERROR","PGRES_FATAL_ERROR"
};

/** static state needed by PQescapeString and PQescapeBytea; initialize to* values that result in backward-compatible
behavior*/
static int    static_client_encoding = PG_SQL_ASCII;
static bool static_std_strings = false;


static PGEvent *dupEvents(PGEvent *events, int count);
static bool PQsendQueryStart(PGconn *conn);
static int PQsendQueryGuts(PGconn *conn,            const char *command,            const char *stmtName,
intnParams,            const Oid *paramTypes,            const char *const * paramValues,            const int
*paramLengths,           const int *paramFormats,            int resultFormat);
 
static void parseInput(PGconn *conn);
static bool PQexecStart(PGconn *conn);
static PGresult *PQexecFinish(PGconn *conn);
static int PQsendDescribe(PGconn *conn, char desc_type,           const char *desc_target);
static int    check_field_number(const PGresult *res, int field_num);


/* ----------------* Space management for PGresult.** Formerly, libpq did a separate malloc() for each field of each
tuple*returned by a query.  This was remarkably expensive --- malloc/free* consumed a sizable part of the application's
runtime. And there is* no real need to keep track of the fields separately, since they will* all be freed together when
thePGresult is released.  So now, we grab* large blocks of storage from malloc and allocate space for query data*
withinthese blocks, using a trivially simple allocator.  This reduces* the number of malloc/free calls dramatically,
andit also avoids* fragmentation of the malloc storage arena.* The PGresult structure itself is still malloc'd
separately. We could* combine it with the first allocation block, but that would waste space* for the common case that
noextra storage is actually needed (that is,* the SQL command did not return tuples).** We also malloc the top-level
arrayof tuple pointers separately, because* we need to be able to enlarge it via realloc, and our trivial space*
allocatordoesn't handle that effectively.  (Too bad the FE/BE protocol* doesn't tell us up front how many tuples will
bereturned.)* All other subsidiary storage for a PGresult is kept in PGresult_data blocks* of size
PGRESULT_DATA_BLOCKSIZE. The overhead at the start of each block* is just a link to the next one, if any.    Free-space
managementinfo is* kept in the owning PGresult.* A query returning a small amount of data will thus require three
malloc*calls: one for the PGresult, one for the tuples pointer array, and one* PGresult_data block.** Only the most
recentlyallocated PGresult_data block is a candidate to* have more stuff added to it --- any extra space left over in
olderblocks* is wasted.  We could be smarter and search the whole chain, but the point* here is to be simple and fast.
Typicalapplications do not keep a PGresult* around very long anyway, so some wasted space within one is not a
problem.**Tuning constants for the space allocator are:* PGRESULT_DATA_BLOCKSIZE: size of a standard allocation block,
inbytes* PGRESULT_ALIGN_BOUNDARY: assumed alignment requirement for binary data* PGRESULT_SEP_ALLOC_THRESHOLD: objects
biggerthan this are given separate*     blocks, instead of being crammed into a regular allocation block.* Requirements
forcorrect function are:* PGRESULT_ALIGN_BOUNDARY must be a multiple of the alignment requirements*        of all
machinedata types.    (Currently this is set from configure*        tests, so it should be OK automatically.)*
PGRESULT_SEP_ALLOC_THRESHOLD+ PGRESULT_BLOCK_OVERHEAD <=*            PGRESULT_DATA_BLOCKSIZE*        pqResultAlloc
assumesan object smaller than the threshold will fit*        in a new block.* The amount of space wasted at the end of
ablock could be as much as* PGRESULT_SEP_ALLOC_THRESHOLD, so it doesn't pay to make that too large.*
----------------*/

#define PGRESULT_DATA_BLOCKSIZE        2048
#define PGRESULT_ALIGN_BOUNDARY        MAXIMUM_ALIGNOF        /* from configure */
#define PGRESULT_BLOCK_OVERHEAD        Max(sizeof(PGresult_data), PGRESULT_ALIGN_BOUNDARY)
#define PGRESULT_SEP_ALLOC_THRESHOLD    (PGRESULT_DATA_BLOCKSIZE / 2)


/** PQmakeEmptyPGresult*     returns a newly allocated, initialized PGresult with given status.*     If conn is not
NULLand status indicates an error, the conn's*     errorMessage is copied.  Also, any PGEvents are copied from the
conn.*/
PGresult *
PQmakeEmptyPGresult(PGconn *conn, ExecStatusType status)
{PGresult   *result;
result = (PGresult *) malloc(sizeof(PGresult));if (!result)    return NULL;
result->ntups = 0;result->numAttributes = 0;result->attDescs = NULL;result->tuples = NULL;result->tupArrSize =
0;result->numParameters= 0;result->paramDescs = NULL;result->resultStatus = status;result->cmdStatus[0] =
'\0';result->binary= 0;result->events = NULL;result->nEvents = 0;result->errMsg = NULL;result->errFields =
NULL;result->null_field[0]= '\0';result->curBlock = NULL;result->curOffset = 0;result->spaceLeft = 0;
 
if (conn){    /* copy connection data we might need for operations on PGresult */    result->noticeHooks =
conn->noticeHooks;   result->client_encoding = conn->client_encoding;
 
    /* consider copying conn's errorMessage */    switch (status)    {        case PGRES_EMPTY_QUERY:        case
PGRES_COMMAND_OK:       case PGRES_TUPLES_OK:        case PGRES_COPY_OUT:        case PGRES_COPY_IN:            /*
non-errorcases */            break;        default:            pqSetResultError(result, conn->errorMessage.data);
    break;    }
 
    /* copy events last; result must be valid if we need to PQclear */    if (conn->nEvents > 0)    {
result->events= dupEvents(conn->events, conn->nEvents);        if (!result->events)        {
PQclear(result);           return NULL;        }        result->nEvents = conn->nEvents;    }}else{    /* defaults...
*/   result->noticeHooks.noticeRec = NULL;    result->noticeHooks.noticeRecArg = NULL;
result->noticeHooks.noticeProc= NULL;    result->noticeHooks.noticeProcArg = NULL;    result->client_encoding =
PG_SQL_ASCII;}
return result;
}

/** PQsetResultAttrs** Set the attributes for a given result.  This function fails if there are* already attributes
containedin the provided result.  The call is* ignored if numAttributes is is zero or attDescs is NULL.  If the*
functionfails, it returns zero.  If the function succeeds, it* returns a non-zero value.*/
 
int
PQsetResultAttrs(PGresult *res, int numAttributes, PGresAttDesc *attDescs)
{int            i;
/* If attrs already exist, they cannot be overwritten. */if (!res || res->numAttributes > 0)    return FALSE;
/* ignore no-op request */if (numAttributes <= 0 || !attDescs)    return TRUE;
res->attDescs = (PGresAttDesc *)    PQresultAlloc(res, numAttributes * sizeof(PGresAttDesc));
if (!res->attDescs)    return FALSE;
res->numAttributes = numAttributes;memcpy(res->attDescs, attDescs, numAttributes * sizeof(PGresAttDesc));
/* deep-copy the attribute names, and determine format */res->binary = 1;for (i = 0; i < res->numAttributes; i++){
if(res->attDescs[i].name)        res->attDescs[i].name = pqResultStrdup(res, res->attDescs[i].name);    else
res->attDescs[i].name= res->null_field;
 
    if (!res->attDescs[i].name)        return FALSE;
    if (res->attDescs[i].format == 0)        res->binary = 0;}
return TRUE;
}

/** PQcopyResult** Returns a deep copy of the provided 'src' PGresult, which cannot be NULL.* The 'flags' argument
controlswhich portions of the result will or will* NOT be copied.  The created result is always put into the*
PGRES_TUPLES_OKstatus.    The source result error message is not copied,* although cmdStatus is.** To set custom
attributes,use PQsetResultAttrs.    That function requires* that there are no attrs contained in the result, so to use
that*function you cannot use the PG_COPYRES_ATTRS or PG_COPYRES_TUPLES* options with this function.** Options:*
PG_COPYRES_ATTRS- Copy the source result's attributes**     PG_COPYRES_TUPLES - Copy the source result's tuples.  This
implies*    copying the attrs, seeeing how the attrs are needed by the tuples.**     PG_COPYRES_EVENTS - Copy the
sourceresult's events.**     PG_COPYRES_NOTICEHOOKS - Copy the source result's notice hooks.*/
 
PGresult *
PQcopyResult(const PGresult *src, int flags)
{PGresult   *dest;int            i;
if (!src)    return NULL;
dest = PQmakeEmptyPGresult(NULL, PGRES_TUPLES_OK);if (!dest)    return NULL;
/* Always copy these over.    Is cmdStatus really useful here? */dest->client_encoding =
src->client_encoding;strcpy(dest->cmdStatus,src->cmdStatus);
 
/* Wants attrs? */if (flags & (PG_COPYRES_ATTRS | PG_COPYRES_TUPLES)){    if (!PQsetResultAttrs(dest,
src->numAttributes,src->attDescs))    {        PQclear(dest);        return NULL;    }}
 
/* Wants to copy tuples? */if (flags & PG_COPYRES_TUPLES){    int            tup,                field;
    for (tup = 0; tup < src->ntups; tup++)    {        for (field = 0; field < src->numAttributes; field++)        {
       if (!PQsetvalue(dest, tup, field,                            src->tuples[tup][field].value,
     src->tuples[tup][field].len))            {                PQclear(dest);                return NULL;            }
     }    }}
 
/* Wants to copy notice hooks? */if (flags & PG_COPYRES_NOTICEHOOKS)    dest->noticeHooks = src->noticeHooks;
/* Wants to copy PGEvents? */if ((flags & PG_COPYRES_EVENTS) && src->nEvents > 0){    dest->events =
dupEvents(src->events,src->nEvents);    if (!dest->events)    {        PQclear(dest);        return NULL;    }
dest->nEvents= src->nEvents;}
 
/* Okay, trigger PGEVT_RESULTCOPY event */for (i = 0; i < dest->nEvents; i++){    if (src->events[i].resultInitialized)
  {        PGEventResultCopy evt;
 
        evt.src = src;        evt.dest = dest;        if (!dest->events[i].proc(PGEVT_RESULTCOPY, &evt,
                dest->events[i].passThrough))        {            PQclear(dest);            return NULL;        }
dest->events[i].resultInitialized = TRUE;    }}
 
return dest;
}

/** Copy an array of PGEvents (with no extra space for more).* Does not duplicate the event instance data, sets this to
NULL.*Also, the resultInitialized flags are all cleared.*/
 
static PGEvent *
dupEvents(PGEvent *events, int count)
{PGEvent    *newEvents;int            i;
if (!events || count <= 0)    return NULL;
newEvents = (PGEvent *) malloc(count * sizeof(PGEvent));if (!newEvents)    return NULL;
for (i = 0; i < count; i++){    newEvents[i].proc = events[i].proc;    newEvents[i].passThrough =
events[i].passThrough;   newEvents[i].data = NULL;    newEvents[i].resultInitialized = FALSE;    newEvents[i].name =
strdup(events[i].name);   if (!newEvents[i].name)    {        while (--i >= 0)            free(newEvents[i].name);
 free(newEvents);        return NULL;    }}
 
return newEvents;
}


/** Sets the value for a tuple field.  The tup_num must be less than or* equal to PQntuples(res).  If it is equal, a
newtuple is created and* added to the result.* Returns a non-zero value for success and zero for failure.*/
 
int
PQsetvalue(PGresult *res, int tup_num, int field_num, char *value, int len)
{PGresAttValue *attval;
if (!check_field_number(res, field_num))    return FALSE;
/* Invalid tup_num, must be <= ntups */if (tup_num < 0 || tup_num > res->ntups)    return FALSE;
/* need to grow the tuple table? */if (res->ntups >= res->tupArrSize){    int            n = res->tupArrSize ?
res->tupArrSize* 2 : 128;    PGresAttValue **tups;
 
    if (res->tuples)        tups = (PGresAttValue **) realloc(res->tuples, n * sizeof(PGresAttValue *));    else
tups= (PGresAttValue **) malloc(n * sizeof(PGresAttValue *));
 
    if (!tups)        return FALSE;
    memset(tups + res->tupArrSize, 0,           (n - res->tupArrSize) * sizeof(PGresAttValue *));    res->tuples =
tups;   res->tupArrSize = n;}
 
/* need to allocate a new tuple? */if (tup_num == res->ntups && !res->tuples[tup_num]){    PGresAttValue *tup;    int
        i;
 
    tup = (PGresAttValue *)        pqResultAlloc(res, res->numAttributes * sizeof(PGresAttValue),
TRUE);
    if (!tup)        return FALSE;
    /* initialize each column to NULL */    for (i = 0; i < res->numAttributes; i++)    {        tup[i].len = NULL_LEN;
      tup[i].value = res->null_field;    }
 
    res->tuples[tup_num] = tup;    res->ntups++;}
attval = &res->tuples[tup_num][field_num];
/* treat either NULL_LEN or NULL value pointer as a NULL field */if (len == NULL_LEN || value == NULL){    attval->len
=NULL_LEN;    attval->value = res->null_field;}else if (len <= 0){    attval->len = 0;    attval->value =
res->null_field;}else{   attval->value = (char *) pqResultAlloc(res, len + 1, TRUE);    if (!attval->value)
returnFALSE;    attval->len = len;    memcpy(attval->value, value, len);    attval->value[len] = '\0';}
 
return TRUE;
}

/** pqResultAlloc - exported routine to allocate local storage in a PGresult.** We force all such allocations to be
maxaligned,since we don't know* whether the value might be binary.*/
 
void *
PQresultAlloc(PGresult *res, size_t nBytes)
{return pqResultAlloc(res, nBytes, TRUE);
}

/** pqResultAlloc -*        Allocate subsidiary storage for a PGresult.** nBytes is the amount of space needed for the
object.*If isBinary is true, we assume that we need to align the object on* a machine allocation boundary.* If isBinary
isfalse, we assume the object is a char string and can* be allocated on any byte boundary.*/
 
void *
pqResultAlloc(PGresult *res, size_t nBytes, bool isBinary)
{char       *space;PGresult_data *block;
if (!res)    return NULL;
if (nBytes <= 0)    return res->null_field;
/* * If alignment is needed, round up the current position to an alignment * boundary. */if (isBinary){    int
 offset = res->curOffset % PGRESULT_ALIGN_BOUNDARY;
 
    if (offset)    {        res->curOffset += PGRESULT_ALIGN_BOUNDARY - offset;        res->spaceLeft -=
PGRESULT_ALIGN_BOUNDARY- offset;    }}
 
/* If there's enough space in the current block, no problem. */if (nBytes <= (size_t) res->spaceLeft){    space =
res->curBlock->space+ res->curOffset;    res->curOffset += nBytes;    res->spaceLeft -= nBytes;    return space;}
 
/* * If the requested object is very large, give it its own block; this * avoids wasting what might be most of the
currentblock to start a new * block.  (We'd have to special-case requests bigger than the block size * anyway.)  The
objectis always given binary alignment in this case. */if (nBytes >= PGRESULT_SEP_ALLOC_THRESHOLD){    block =
(PGresult_data*) malloc(nBytes + PGRESULT_BLOCK_OVERHEAD);    if (!block)        return NULL;    space = block->space +
PGRESULT_BLOCK_OVERHEAD;   if (res->curBlock)    {        /*         * Tuck special block below the active block, so
thatwe don't         * have to waste the free space in the active block.         */        block->next =
res->curBlock->next;       res->curBlock->next = block;    }    else    {        /* Must set up the new block as the
firstactive block. */        block->next = NULL;        res->curBlock = block;        res->spaceLeft = 0; /* be sure
it'smarked full */    }    return space;}
 
/* Otherwise, start a new block. */block = (PGresult_data *) malloc(PGRESULT_DATA_BLOCKSIZE);if (!block)    return
NULL;block->next= res->curBlock;res->curBlock = block;if (isBinary){    /* object needs full alignment */
res->curOffset= PGRESULT_BLOCK_OVERHEAD;    res->spaceLeft = PGRESULT_DATA_BLOCKSIZE - PGRESULT_BLOCK_OVERHEAD;}else{
/* we can cram it right after the overhead pointer */    res->curOffset = sizeof(PGresult_data);    res->spaceLeft =
PGRESULT_DATA_BLOCKSIZE- sizeof(PGresult_data);}
 
space = block->space + res->curOffset;res->curOffset += nBytes;res->spaceLeft -= nBytes;return space;
}

/** pqResultStrdup -*        Like strdup, but the space is subsidiary PGresult space.*/
char *
pqResultStrdup(PGresult *res, const char *str)
{char       *space = (char *) pqResultAlloc(res, strlen(str) + 1, FALSE);
if (space)    strcpy(space, str);return space;
}

/** pqSetResultError -*        assign a new error message to a PGresult*/
void
pqSetResultError(PGresult *res, const char *msg)
{if (!res)    return;if (msg && *msg)    res->errMsg = pqResultStrdup(res, msg);else    res->errMsg = NULL;
}

/** pqCatenateResultError -*        concatenate a new error message to the one already in a PGresult*/
void
pqCatenateResultError(PGresult *res, const char *msg)
{PQExpBufferData errorBuf;
if (!res || !msg)    return;initPQExpBuffer(&errorBuf);if (res->errMsg)    appendPQExpBufferStr(&errorBuf,
res->errMsg);appendPQExpBufferStr(&errorBuf,msg);pqSetResultError(res, errorBuf.data);termPQExpBuffer(&errorBuf);
 
}

/** PQclear -*      free's the memory associated with a PGresult*/
void
PQclear(PGresult *res)
{PGresult_data *block;int            i;
if (!res)    return;
for (i = 0; i < res->nEvents; i++){    /* only send DESTROY to successfully-initialized event procs */    if
(res->events[i].resultInitialized)   {        PGEventResultDestroy evt;
 
        evt.result = res;        (void) res->events[i].proc(PGEVT_RESULTDESTROY, &evt,
res->events[i].passThrough);    }    free(res->events[i].name);}
 
if (res->events)    free(res->events);
/* Free all the subsidiary blocks */while ((block = res->curBlock) != NULL){    res->curBlock = block->next;
free(block);}
/* Free the top-level tuple pointer array */if (res->tuples)    free(res->tuples);
/* zero out the pointer fields to catch programming errors */res->attDescs = NULL;res->tuples = NULL;res->paramDescs =
NULL;res->errFields= NULL;res->events = NULL;res->nEvents = 0;/* res->curBlock was zeroed out earlier */
 
/* Free the PGresult structure itself */free(res);
}

/** Handy subroutine to deallocate any partially constructed async result.*/

void
pqClearAsyncResult(PGconn *conn)
{if (conn->result)    PQclear(conn->result);conn->result = NULL;conn->curTuple = NULL;
}

/** This subroutine deletes any existing async result, sets conn->result* to a PGresult with status PGRES_FATAL_ERROR,
andstores the current* contents of conn->errorMessage into that result.  It differs from a* plain call on
PQmakeEmptyPGresult()in that if there is already an* async result with status PGRES_FATAL_ERROR, the current error
message*is APPENDED to the old error message instead of replacing it.  This* behavior lets us report multiple error
conditionsproperly, if necessary.* (An example where this is needed is when the backend sends an 'E' message* and
immediatelycloses the connection --- we want to report both the* backend error and the connection closure error.)*/
 
void
pqSaveErrorResult(PGconn *conn)
{/* * If no old async result, just let PQmakeEmptyPGresult make one. Likewise * if old result is not an error message.
*/if(conn->result == NULL ||    conn->result->resultStatus != PGRES_FATAL_ERROR ||    conn->result->errMsg == NULL){
pqClearAsyncResult(conn);   conn->result = PQmakeEmptyPGresult(conn, PGRES_FATAL_ERROR);}else{    /* Else, concatenate
errormessage to existing async result. */    pqCatenateResultError(conn->result, conn->errorMessage.data);}
 
}

/** This subroutine prepares an async result object for return to the caller.* If there is not already an async result
object,build an error object* using whatever is in conn->errorMessage.  In any case, clear the async* result storage
andmake sure PQerrorMessage will agree with the result's* error string.*/
 
PGresult *
pqPrepareAsyncResult(PGconn *conn)
{PGresult   *res;
/* * conn->result is the PGresult to return.    If it is NULL (which probably * shouldn't happen) we assume there is an
appropriateerror message in * conn->errorMessage. */res = conn->result;conn->result = NULL;        /* handing over
ownershipto caller */conn->curTuple = NULL;        /* just in case */if (!res)    res = PQmakeEmptyPGresult(conn,
PGRES_FATAL_ERROR);else{   /*     * Make sure PQerrorMessage agrees with result; it could be different     * if we have
concatenatedmessages.     */    resetPQExpBuffer(&conn->errorMessage);    appendPQExpBufferStr(&conn->errorMessage,
                   PQresultErrorMessage(res));}return res;
 
}

/** pqInternalNotice - produce an internally-generated notice message** A format string and optional arguments can be
passed. Note that we do* libpq_gettext() here, so callers need not.** The supplied text is taken as primary message
(ie.,it should not include* a trailing newline, and should not be more than one line).*/
 
void
pqInternalNotice(const PGNoticeHooks *hooks, const char *fmt,...)
{char        msgBuf[1024];va_list        args;PGresult   *res;
if (hooks->noticeRec == NULL)    return;                    /* nobody home to receive notice? */
/* Format the message */va_start(args, fmt);vsnprintf(msgBuf, sizeof(msgBuf), libpq_gettext(fmt),
args);va_end(args);msgBuf[sizeof(msgBuf)- 1] = '\0';    /* make real sure it's terminated */
 
/* Make a PGresult to pass to the notice receiver */res = PQmakeEmptyPGresult(NULL, PGRES_NONFATAL_ERROR);if (!res)
return;res->noticeHooks= *hooks;
 
/* * Set up fields of notice. */pqSaveMessageField(res, PG_DIAG_MESSAGE_PRIMARY, msgBuf);pqSaveMessageField(res,
PG_DIAG_SEVERITY,libpq_gettext("NOTICE"));/* XXX should provide a SQLSTATE too? */
 
/* * Result text is always just the primary message + newline. If we can't * allocate it, don't bother invoking the
receiver.*/res->errMsg = (char *) pqResultAlloc(res, strlen(msgBuf) + 2, FALSE);if (res->errMsg){
sprintf(res->errMsg,"%s\n", msgBuf);
 
    /*     * Pass to receiver, then free it.     */    (*res->noticeHooks.noticeRec) (res->noticeHooks.noticeRecArg,
res);}PQclear(res);
}

/** pqAddTuple*      add a row pointer to the PGresult structure, growing it if necessary*      Returns TRUE if OK,
FALSEif not enough memory to add the row*/
 
int
pqAddTuple(PGresult *res, PGresAttValue *tup)
{if (res->ntups >= res->tupArrSize){    /*     * Try to grow the array.     *     * We can use realloc because shallow
copyingof the structure is     * okay. Note that the first time through, res->tuples is NULL. While     * ANSI says
thatrealloc() should act like malloc() in that case,     * some old C libraries (like SunOS 4.1.x) coredump instead. On
   * failure realloc is supposed to return NULL without damaging the     * existing allocation. Note that the positions
beyondres->ntups are     * garbage, not necessarily NULL.     */    int            newSize = (res->tupArrSize > 0) ?
res->tupArrSize* 2 : 128;    PGresAttValue **newTuples;
 
    if (res->tuples == NULL)        newTuples = (PGresAttValue **)            malloc(newSize * sizeof(PGresAttValue
*));   else        newTuples = (PGresAttValue **)            realloc(res->tuples, newSize * sizeof(PGresAttValue *));
if (!newTuples)        return FALSE;        /* malloc or realloc failed */    res->tupArrSize = newSize;    res->tuples
=newTuples;}res->tuples[res->ntups] = tup;res->ntups++;return TRUE;
 
}

/** pqSaveMessageField - save one field of an error or notice message*/
void
pqSaveMessageField(PGresult *res, char code, const char *value)
{PGMessageField *pfield;
pfield = (PGMessageField *)    pqResultAlloc(res,                  sizeof(PGMessageField) + strlen(value),
   TRUE);if (!pfield)    return;                    /* out of memory? */pfield->code = code;strcpy(pfield->contents,
value);pfield->next= res->errFields;res->errFields = pfield;
 
}

/** pqSaveParameterStatus - remember parameter status sent by backend*/
void
pqSaveParameterStatus(PGconn *conn, const char *name, const char *value)
{pgParameterStatus *pstatus;pgParameterStatus *prev;
if (conn->Pfdebug)    fprintf(conn->Pfdebug, "pqSaveParameterStatus: '%s' = '%s'\n",            name, value);
/* * Forget any old information about the parameter */for (pstatus = conn->pstatus, prev = NULL;     pstatus != NULL;
 prev = pstatus, pstatus = pstatus->next){    if (strcmp(pstatus->name, name) == 0)    {        if (prev)
prev->next= pstatus->next;        else            conn->pstatus = pstatus->next;        free(pstatus);        /* frees
nameand value strings too */        break;    }}
 
/* * Store new info as a single malloc block */pstatus = (pgParameterStatus *) malloc(sizeof(pgParameterStatus) +
                               strlen(name) +strlen(value) + 2);if (pstatus){    char       *ptr;
 
    ptr = ((char *) pstatus) + sizeof(pgParameterStatus);    pstatus->name = ptr;    strcpy(ptr, name);    ptr +=
strlen(name)+ 1;    pstatus->value = ptr;    strcpy(ptr, value);    pstatus->next = conn->pstatus;    conn->pstatus =
pstatus;}
/* * Special hacks: remember client_encoding and * standard_conforming_strings, and convert server version to a numeric
*form.  We keep the first two of these in static variables as well, so * that PQescapeString and PQescapeBytea can
behavesomewhat sanely (at * least in single-connection-using programs). */if (strcmp(name, "client_encoding") == 0){
conn->client_encoding= pg_char_to_encoding(value);    /* if we don't recognize the encoding name, fall back to
SQL_ASCII*/    if (conn->client_encoding < 0)        conn->client_encoding = PG_SQL_ASCII;    static_client_encoding =
conn->client_encoding;}elseif (strcmp(name, "standard_conforming_strings") == 0){    conn->std_strings = (strcmp(value,
"on")== 0);    static_std_strings = conn->std_strings;}else if (strcmp(name, "server_version") == 0){    int
cnt;   int            vmaj,                vmin,                vrev;
 
    cnt = sscanf(value, "%d.%d.%d", &vmaj, &vmin, &vrev);
    if (cnt < 2)        conn->sversion = 0; /* unknown */    else    {        if (cnt == 2)            vrev = 0;
conn->sversion= (100 * vmaj + vmin) * 100 + vrev;    }}
 
}


/** PQsendQuery*     Submit a query, but don't wait for it to finish** Returns: 1 if successfully submitted*
0if error (conn->errorMessage is set)*/
 
int
PQsendQuery(PGconn *conn, const char *query)
{if (!PQsendQueryStart(conn))    return 0;
if (!query){    printfPQExpBuffer(&conn->errorMessage,                    libpq_gettext("command string is a null
pointer\n"));   return 0;}
 
/* construct the outgoing Query message */if (pqPutMsgStart('Q', false, conn) < 0 ||    pqPuts(query, conn) < 0 ||
pqPutMsgEnd(conn)< 0){    pqHandleSendFailure(conn);    return 0;}
 
/* remember we are using simple query protocol */conn->queryclass = PGQUERY_SIMPLE;
/* and remember the query text too, if possible *//* if insufficient memory, last_query just winds up NULL */if
(conn->last_query)   free(conn->last_query);conn->last_query = strdup(query);
 
/* * Give the data a push.  In nonblock mode, don't complain if we're unable * to send it all; PQgetResult() will do
anyadditional flushing needed. */if (pqFlush(conn) < 0){    pqHandleSendFailure(conn);    return 0;}
 
/* OK, it's launched! */conn->asyncStatus = PGASYNC_BUSY;return 1;
}

/** PQsendQueryParams*        Like PQsendQuery, but use protocol 3.0 so we can pass parameters*/
int
PQsendQueryParams(PGconn *conn,              const char *command,              int nParams,              const Oid
*paramTypes,             const char *const * paramValues,              const int *paramLengths,              const int
*paramFormats,             int resultFormat)
 
{if (!PQsendQueryStart(conn))    return 0;
if (!command){    printfPQExpBuffer(&conn->errorMessage,                    libpq_gettext("command string is a null
pointer\n"));   return 0;}
 
return PQsendQueryGuts(conn,                       command,                       "",    /* use unnamed statement */
                  nParams,                       paramTypes,                       paramValues,
paramLengths,                      paramFormats,                       resultFormat);
 
}

/** PQsendPrepare*     Submit a Parse message, but don't wait for it to finish** Returns: 1 if successfully submitted*
         0 if error (conn->errorMessage is set)*/
 
int
PQsendPrepare(PGconn *conn,          const char *stmtName, const char *query,          int nParams, const Oid
*paramTypes)
{if (!PQsendQueryStart(conn))    return 0;
if (!stmtName){    printfPQExpBuffer(&conn->errorMessage,                    libpq_gettext("statement name is a null
pointer\n"));   return 0;}
 
if (!query){    printfPQExpBuffer(&conn->errorMessage,                    libpq_gettext("command string is a null
pointer\n"));   return 0;}
 
/* This isn't gonna work on a 2.0 server */if (PG_PROTOCOL_MAJOR(conn->pversion) < 3){
printfPQExpBuffer(&conn->errorMessage,    libpq_gettext("function requires at least protocol version 3.0\n"));
return0;}
 
/* construct the Parse message */if (pqPutMsgStart('P', false, conn) < 0 ||    pqPuts(stmtName, conn) < 0 ||
pqPuts(query,conn) < 0)    goto sendFailed;
 
if (nParams > 0 && paramTypes){    int            i;
    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)
gotosendFailed;}if (pqPutMsgEnd(conn) < 0)    goto sendFailed;
 
/* construct the Sync message */if (pqPutMsgStart('S', false, conn) < 0 ||    pqPutMsgEnd(conn) < 0)    goto
sendFailed;
/* remember we are doing just a Parse */conn->queryclass = PGQUERY_PREPARE;
/* and remember the query text too, if possible *//* if insufficient memory, last_query just winds up NULL */if
(conn->last_query)   free(conn->last_query);conn->last_query = strdup(query);
 
/* * Give the data a push.  In nonblock mode, don't complain if we're unable * to send it all; PQgetResult() will do
anyadditional flushing needed. */if (pqFlush(conn) < 0)    goto sendFailed;
 
/* OK, it's launched! */conn->asyncStatus = PGASYNC_BUSY;return 1;

sendFailed:pqHandleSendFailure(conn);return 0;
}

/** PQsendQueryPrepared*        Like PQsendQuery, but execute a previously prepared statement,*        using protocol
3.0so we can pass parameters*/
 
int
PQsendQueryPrepared(PGconn *conn,                const char *stmtName,                int nParams,                const
char*const * paramValues,                const int *paramLengths,                const int *paramFormats,
int resultFormat)
 
{if (!PQsendQueryStart(conn))    return 0;
if (!stmtName){    printfPQExpBuffer(&conn->errorMessage,                    libpq_gettext("statement name is a null
pointer\n"));   return 0;}
 
return PQsendQueryGuts(conn,                       NULL,    /* no command to parse */                       stmtName,
                   nParams,                       NULL,    /* no param types */                       paramValues,
                paramLengths,                       paramFormats,                       resultFormat);
 
}

/** Common startup code for PQsendQuery and sibling routines*/
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"));
returnfalse;}
 
/* initialize async result-accumulation state */conn->result = NULL;conn->curTuple = NULL;
/* ready to send command message */return true;
}

/** PQsendQueryGuts*        Common code for protocol-3.0 query sending*        PQsendQueryStart should be done
already**command may be NULL to indicate we use an already-prepared statement*/
 
static int
PQsendQueryGuts(PGconn *conn,            const char *command,            const char *stmtName,            int nParams,
         const Oid *paramTypes,            const char *const * paramValues,            const int *paramLengths,
  const int *paramFormats,            int resultFormat)
 
{int            i;
/* This isn't gonna work on a 2.0 server */if (PG_PROTOCOL_MAJOR(conn->pversion) < 3){
printfPQExpBuffer(&conn->errorMessage,    libpq_gettext("function requires at least protocol version 3.0\n"));
return0;}
 
/* * We will send Parse (if needed), Bind, Describe Portal, Execute, Sync, * using specified statement name and the
unnamedportal. */
 

#define ADD_SYNC
if (command){    /* construct the Parse message */    if (pqPutMsgStart('P', false, conn) < 0 ||
pqPuts(stmtName,conn) < 0 ||        pqPuts(command, conn) < 0)        goto sendFailed;    if (nParams > 0 &&
paramTypes)   {        if (pqPutInt(nParams, 2, conn) < 0)            goto sendFailed;        for (i = 0; i < nParams;
i++)       {            if (pqPutInt(paramTypes[i], 4, conn) < 0)                goto sendFailed;        }    }    else
  {        if (pqPutInt(0, 2, conn) < 0)            goto sendFailed;    }    if (pqPutMsgEnd(conn) < 0)        goto
sendFailed;}

#ifdef ADD_SYNC/* construct the Sync message */if (pqPutMsgStart('S', false, conn) < 0 ||    pqPutMsgEnd(conn) < 0)
gotosendFailed;
 
#endif
/* Construct the Bind message */if (pqPutMsgStart('B', false, conn) < 0 ||    pqPuts("", conn) < 0 ||
pqPuts(stmtName,conn) < 0)    goto sendFailed;
 
/* Send parameter formats */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;
/* Send parameters */for (i = 0; i < nParams; i++){    if (paramValues && paramValues[i])    {        int
nbytes;
        if (paramFormats && paramFormats[i] != 0)        {            /* binary parameter */            if
(paramLengths)               nbytes = paramLengths[i];            else            {
printfPQExpBuffer(&conn->errorMessage,                                 libpq_gettext("length must be given for binary
parameter\n"));               goto sendFailed;            }        }        else        {            /* text parameter,
donot use paramLengths */            nbytes = strlen(paramValues[i]);        }        if (pqPutInt(nbytes, 4, conn) < 0
||           pqPutnchar(paramValues[i], nbytes, conn) < 0)            goto sendFailed;    }    else    {        /* take
theparam 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;
 

#ifdef ADD_SYNC/* construct the Sync message */if (pqPutMsgStart('S', false, conn) < 0 ||    pqPutMsgEnd(conn) < 0)
gotosendFailed;
 
#endif
/* construct the Describe Portal message */if (pqPutMsgStart('D', false, conn) < 0 ||    pqPutc('P', conn) < 0 ||
pqPuts("",conn) < 0 ||    pqPutMsgEnd(conn) < 0)    goto sendFailed;
 

#ifdef ADD_SYNC/* construct the Sync message */if (pqPutMsgStart('S', false, conn) < 0 ||    pqPutMsgEnd(conn) < 0)
gotosendFailed;
 
#endif
/* construct the Execute message */if (pqPutMsgStart('E', false, conn) < 0 ||    pqPuts("", conn) < 0 ||    pqPutInt(0,
4,conn) < 0 ||    pqPutMsgEnd(conn) < 0)    goto sendFailed;
 
/* construct the Sync message */if (pqPutMsgStart('S', false, conn) < 0 ||    pqPutMsgEnd(conn) < 0)    goto
sendFailed;
/* remember we are using extended query protocol */conn->queryclass = PGQUERY_EXTENDED;
/* and remember the query text too, if possible *//* if insufficient memory, last_query just winds up NULL */if
(conn->last_query)   free(conn->last_query);if (command)    conn->last_query = strdup(command);else    conn->last_query
=NULL;
 
/* * Give the data a push.  In nonblock mode, don't complain if we're unable * to send it all; PQgetResult() will do
anyadditional flushing needed. */if (pqFlush(conn) < 0)    goto sendFailed;
 
/* OK, it's launched! */conn->asyncStatus = PGASYNC_BUSY;return 1;

sendFailed:pqHandleSendFailure(conn);return 0;
}

/** pqHandleSendFailure: try to clean up after failure to send command.** Primarily, what we want to accomplish here is
toprocess an async* NOTICE message that the backend might have sent just before it died.** NOTE: this routine should
onlybe called in PGASYNC_IDLE state.*/
 
void
pqHandleSendFailure(PGconn *conn)
{/* * Accept any available input data, ignoring errors.  Note that if * pqReadData decides the backend has closed the
channel,it will close * our side of the socket --- that's just what we want here. */while (pqReadData(conn) > 0)     /*
loopuntil no more data readable */ ;
 
/* * Parse any available input messages.    Since we are in PGASYNC_IDLE * state, only NOTICE and NOTIFY messages will
beeaten. */parseInput(conn);
 
}

/** Consume any available input from the backend* 0 return: some kind of trouble* 1 return: no problem*/
int
PQconsumeInput(PGconn *conn)
{if (!conn)    return 0;
/* * for non-blocking connections try to flush the send-queue, otherwise we * may never get a response for something
thatmay not have already been * sent because it's in our write buffer! */if (pqIsnonblocking(conn)){    if
(pqFlush(conn)< 0)        return 0;}
 
/* * Load more data, if available. We do this no matter what state we are * in, since we are probably getting called
becausethe application wants * to get rid of a read-select condition. Note that we will NOT block * waiting for more
input.*/if (pqReadData(conn) < 0)    return 0;
 
/* Parsing of the data waits till later. */return 1;
}


/** parseInput: if appropriate, parse input data from backend* until input is exhausted or a stopping state is
reached.*Note that this function will NOT attempt to read more data from the backend.*/
 
static void
parseInput(PGconn *conn)
{if (PG_PROTOCOL_MAJOR(conn->pversion) >= 3)    pqParseInput3(conn);else    pqParseInput2(conn);
}

/** PQisBusy*     Return TRUE if PQgetResult would block waiting for input.*/

int
PQisBusy(PGconn *conn)
{if (!conn)    return FALSE;
/* Parse any available data, if our state permits. */parseInput(conn);
/* PQgetResult will return immediately in all states except BUSY. */return conn->asyncStatus == PGASYNC_BUSY;
}


/** PQgetResult*      Get the next PGresult produced by a query.  Returns NULL if no*      query work remains or an
errorhas occurred (e.g. out of*      memory).*/
 

PGresult *
PQgetResult(PGconn *conn)
{PGresult   *res;
if (!conn)    return NULL;
/* Parse any available data, if our state permits. */parseInput(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'teven got yet.     */    while ((flushResult = pqFlush(conn)) > 0)    {        if (pqWait(FALSE, TRUE, conn))
 {            flushResult = -1;            break;        }    }
 
    /* Wait for some more data, and load it. */    if (flushResult ||        pqWait(TRUE, FALSE, conn) ||
pqReadData(conn)< 0)    {        /*         * conn->errorMessage has been set by pqWait or pqReadData. We         *
wantto append it to any already-received error message.         */        pqSaveErrorResult(conn);
conn->asyncStatus= PGASYNC_IDLE;        return pqPrepareAsyncResult(conn);    }
 
    /* Parse it. */    parseInput(conn);}
/* Return the appropriate thing. */switch (conn->asyncStatus){    case PGASYNC_IDLE:        res = NULL;            /*
queryis complete */        break;    case PGASYNC_READY:        res = pqPrepareAsyncResult(conn);        /* Set the
stateback to BUSY, allowing parsing to proceed. */        conn->asyncStatus = PGASYNC_BUSY;        break;    case
PGASYNC_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:       if (conn->result && conn->result->resultStatus == PGRES_COPY_OUT)            res =
pqPrepareAsyncResult(conn);       else            res = PQmakeEmptyPGresult(conn, PGRES_COPY_OUT);        break;
default:       printfPQExpBuffer(&conn->errorMessage,                          libpq_gettext("unexpected asyncStatus:
%d\n"),                         (int) conn->asyncStatus);        res = PQmakeEmptyPGresult(conn, PGRES_FATAL_ERROR);
   break;}
 
if (res){    int            i;
    for (i = 0; i < res->nEvents; i++)    {        PGEventResultCreate evt;
        evt.conn = conn;        evt.result = res;        if (!res->events[i].proc(PGEVT_RESULTCREATE, &evt,
                   res->events[i].passThrough))        {            printfPQExpBuffer(&conn->errorMessage,
               libpq_gettext("PGEventProc \"%s\" failed during PGEVT_RESULTCREATE event\n"),
 res->events[i].name);            pqSetResultError(res, conn->errorMessage.data);            res->resultStatus =
PGRES_FATAL_ERROR;           break;        }        res->events[i].resultInitialized = TRUE;    }}
 
return res;
}


/** PQexec*      send a query to the backend and package up the result in a PGresult** If the query was not even sent,
returnNULL; conn->errorMessage is set to* a relevant message.* If the query was sent, a new PGresult is returned (which
couldindicate* either success or failure).* The user is responsible for freeing the PGresult via PQclear()* when done
withit.*/
 
PGresult *
PQexec(PGconn *conn, const char *query)
{if (!PQexecStart(conn))    return NULL;if (!PQsendQuery(conn, query))    return NULL;return PQexecFinish(conn);
}

/** PQexecParams*        Like PQexec, but use protocol 3.0 so we can pass parameters*/
PGresult *
PQexecParams(PGconn *conn,         const char *command,         int nParams,         const Oid *paramTypes,
constchar *const * paramValues,         const int *paramLengths,         const int *paramFormats,         int
resultFormat)
{if (!PQexecStart(conn))    return NULL;if (!PQsendQueryParams(conn, command,                       nParams,
paramTypes,paramValues, paramLengths,                       paramFormats, resultFormat))    return NULL;return
PQexecFinish(conn);
}

/** PQprepare*      Creates a prepared statement by issuing a v3.0 parse message.** If the query was not even sent,
returnNULL; conn->errorMessage is set to* a relevant message.* If the query was sent, a new PGresult is returned (which
couldindicate* either success or failure).* The user is responsible for freeing the PGresult via PQclear()* when done
withit.*/
 
PGresult *
PQprepare(PGconn *conn,      const char *stmtName, const char *query,      int nParams, const Oid *paramTypes)
{if (!PQexecStart(conn))    return NULL;if (!PQsendPrepare(conn, stmtName, query, nParams, paramTypes))    return
NULL;returnPQexecFinish(conn);
 
}

/** PQexecPrepared*        Like PQexec, but execute a previously prepared statement,*        using protocol 3.0 so we
canpass parameters*/
 
PGresult *
PQexecPrepared(PGconn *conn,           const char *stmtName,           int nParams,           const char *const *
paramValues,          const int *paramLengths,           const int *paramFormats,           int resultFormat)
 
{if (!PQexecStart(conn))    return NULL;if (!PQsendQueryPrepared(conn, stmtName,                         nParams,
paramValues,paramLengths,                         paramFormats, resultFormat))    return NULL;return
PQexecFinish(conn);
}

/** Common code for PQexec and sibling routines: prepare to send command*/
static bool
PQexecStart(PGconn *conn)
{PGresult   *result;
if (!conn)    return false;
/* * Silently discard any prior query result that application didn't eat. * This is probably poor design, but it's here
forbackward compatibility. */while ((result = PQgetResult(conn)) != NULL){    ExecStatusType resultStatus =
result->resultStatus;
    PQclear(result);        /* only need its status */    if (resultStatus == PGRES_COPY_IN)    {        if
(PG_PROTOCOL_MAJOR(conn->pversion)>= 3)        {            /* In protocol 3, we can get out of a COPY IN state */
     if (PQputCopyEnd(conn,                     libpq_gettext("COPY terminated by new PQexec")) < 0)
returnfalse;            /* keep waiting to swallow the copy's failure message */        }        else        {
 /* In older protocols we have to punt */            printfPQExpBuffer(&conn->errorMessage,
libpq_gettext("COPYIN state must be terminated first\n"));            return false;        }    }    else if
(resultStatus== PGRES_COPY_OUT)    {        if (PG_PROTOCOL_MAJOR(conn->pversion) >= 3)        {            /*
  * In protocol 3, we can get out of a COPY OUT state: we just             * switch back to BUSY and allow the
remainingCOPY data to be             * dropped on the floor.             */            conn->asyncStatus =
PGASYNC_BUSY;           /* keep waiting to swallow the copy's completion message */        }        else        {
    /* In older protocols we have to punt */            printfPQExpBuffer(&conn->errorMessage,
libpq_gettext("COPYOUT state must be terminated first\n"));            return false;        }    }    /* check for loss
ofconnection, too */    if (conn->status == CONNECTION_BAD)        return false;}
 
/* OK to send a command */return true;
}

/** Common code for PQexec and sibling routines: wait for command result*/
static PGresult *
PQexecFinish(PGconn *conn)
{PGresult   *result;PGresult   *lastResult;
/* * For backwards compatibility, return the last result if there are more * than one --- but merge error messages if
weget more than one error * result. * * We have to stop if we see copy in/out, however. We will resume parsing * after
applicationperforms the data transfer. * * Also stop if the connection is lost (else we'll loop infinitely).
*/lastResult= NULL;while ((result = PQgetResult(conn)) != NULL){    if (lastResult)    {        if
(lastResult->resultStatus== PGRES_FATAL_ERROR &&            result->resultStatus == PGRES_FATAL_ERROR)        {
  pqCatenateResultError(lastResult, result->errMsg);            PQclear(result);            result = lastResult;
 
            /*             * Make sure PQerrorMessage agrees with concatenated result             */
resetPQExpBuffer(&conn->errorMessage);           appendPQExpBufferStr(&conn->errorMessage, result->errMsg);        }
   else            PQclear(lastResult);    }    lastResult = result;    if (result->resultStatus == PGRES_COPY_IN ||
   result->resultStatus == PGRES_COPY_OUT ||        conn->status == CONNECTION_BAD)        break;}
 
return lastResult;
}

/** PQdescribePrepared*      Obtain information about a previously prepared statement** If the query was not even sent,
returnNULL; conn->errorMessage is set to* a relevant message.* If the query was sent, a new PGresult is returned (which
couldindicate* either success or failure).    On success, the PGresult contains status* PGRES_COMMAND_OK, and its
parameterand column-heading fields describe* the statement's inputs and outputs respectively.* The user is responsible
forfreeing the PGresult via PQclear()* when done with it.*/
 
PGresult *
PQdescribePrepared(PGconn *conn, const char *stmt)
{if (!PQexecStart(conn))    return NULL;if (!PQsendDescribe(conn, 'S', stmt))    return NULL;return
PQexecFinish(conn);
}

/** PQdescribePortal*      Obtain information about a previously created portal** This is much like PQdescribePrepared,
exceptthat no parameter info is* returned.  Note that at the moment, libpq doesn't really expose portals* to the
client;but this can be used with a portal created by a SQL* DECLARE CURSOR command.*/
 
PGresult *
PQdescribePortal(PGconn *conn, const char *portal)
{if (!PQexecStart(conn))    return NULL;if (!PQsendDescribe(conn, 'P', portal))    return NULL;return
PQexecFinish(conn);
}

/** PQsendDescribePrepared*     Submit a Describe Statement command, but don't wait for it to finish** Returns: 1 if
successfullysubmitted*            0 if error (conn->errorMessage is set)*/
 
int
PQsendDescribePrepared(PGconn *conn, const char *stmt)
{return PQsendDescribe(conn, 'S', stmt);
}

/** PQsendDescribePortal*     Submit a Describe Portal command, but don't wait for it to finish** Returns: 1 if
successfullysubmitted*            0 if error (conn->errorMessage is set)*/
 
int
PQsendDescribePortal(PGconn *conn, const char *portal)
{return PQsendDescribe(conn, 'P', portal);
}

/** PQsendDescribe*     Common code to send a Describe command** Available options for desc_type are*     'S' to
describea prepared statement; or*     'P' to describe a portal.* Returns 1 on success and 0 on failure.*/
 
static int
PQsendDescribe(PGconn *conn, char desc_type, const char *desc_target)
{/* Treat null desc_target as empty string */if (!desc_target)    desc_target = "";
if (!PQsendQueryStart(conn))    return 0;
/* This isn't gonna work on a 2.0 server */if (PG_PROTOCOL_MAJOR(conn->pversion) < 3){
printfPQExpBuffer(&conn->errorMessage,    libpq_gettext("function requires at least protocol version 3.0\n"));
return0;}
 
/* construct the Describe message */if (pqPutMsgStart('D', false, conn) < 0 ||    pqPutc(desc_type, conn) < 0 ||
pqPuts(desc_target,conn) < 0 ||    pqPutMsgEnd(conn) < 0)    goto sendFailed;
 
/* construct the Sync message */if (pqPutMsgStart('S', false, conn) < 0 ||    pqPutMsgEnd(conn) < 0)    goto
sendFailed;
/* remember we are doing a Describe */conn->queryclass = PGQUERY_DESCRIBE;
/* reset last-query string (not relevant now) */if (conn->last_query){    free(conn->last_query);    conn->last_query =
NULL;}
/* * Give the data a push.  In nonblock mode, don't complain if we're unable * to send it all; PQgetResult() will do
anyadditional flushing needed. */if (pqFlush(conn) < 0)    goto sendFailed;
 
/* OK, it's launched! */conn->asyncStatus = PGASYNC_BUSY;return 1;

sendFailed:pqHandleSendFailure(conn);return 0;
}

/** PQnotifies*      returns a PGnotify* structure of the latest async notification* that has not yet been handled**
returnsNULL, if there is currently* no unhandled async notification from the backend** the CALLER is responsible for
FREE'ingthe structure returned*/
 
PGnotify *
PQnotifies(PGconn *conn)
{PGnotify   *event;
if (!conn)    return NULL;
/* Parse any available data to see if we can extract NOTIFY messages. */parseInput(conn);
event = conn->notifyHead;if (event){    conn->notifyHead = event->next;    if (!conn->notifyHead)
conn->notifyTail= NULL;    event->next = NULL;        /* don't let app see the internal state */}return event;
 
}

/** PQputCopyData - send some data to the backend during COPY IN** Returns 1 if successful, 0 if data could not be sent
(onlypossible* in nonblock mode), or -1 if an error occurs.*/
 
int
PQputCopyData(PGconn *conn, const char *buffer, int nbytes)
{if (!conn)    return -1;if (conn->asyncStatus != PGASYNC_COPY_IN){    printfPQExpBuffer(&conn->errorMessage,
          libpq_gettext("no COPY in progress\n"));    return -1;}
 
/* * Process any NOTICE or NOTIFY messages that might be pending in the * input buffer.  Since the server might
generatemany notices during the * COPY, we want to clean those out reasonably promptly to prevent * indefinite
expansionof the input buffer.  (Note: the actual read of * input data into the input buffer happens down inside
pqSendSome,but * it's not authorized to get rid of the data again.) */parseInput(conn);
 
if (nbytes > 0){    /*     * Try to flush any previously sent data in preference to growing the     * output buffer.
Ifwe 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 + (size_t) 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
commandcompletion status.** Returns 1 if successful, 0 if data could not be sent (only possible* in nonblock mode), or
-1if 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;    }
 
    /*     * If we sent the COPY command in extended-query mode, we must issue a     * Sync as well.     */    if
(conn->queryclass!= PGQUERY_SIMPLE)    {        if (pqPutMsgStart('S', false, conn) < 0 ||            pqPutMsgEnd(conn)
<0)            return -1;    }}else{    if (errormsg)    {        /* Ooops, no way to do this in 2.0 */
printfPQExpBuffer(&conn->errorMessage,                         libpq_gettext("function requires at least protocol
version3.0\n"));        return -1;    }    else    {        /* Send old-style end-of-data marker */        if
(pqPutMsgStart(0,false, conn) < 0 ||            pqPutnchar("\\.\n", 3, conn) < 0 ||            pqPutMsgEnd(conn) < 0)
        return -1;    }}
 
/* 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'drow of data, and* returns row length (always > 0) as result.* Returns 0 if no row available yet (only possible
ifasync 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"* and read the output string.    Returns a null-terminated string in s.** XXX this routine is now
deprecated,because it can't handle binary data.* If called during a COPY BINARY we return EOF.** PQgetline reads up to
maxlen-1characters (like fgets(3)) but strips* the terminating \n (like gets(3)).** CAUTION: the caller is responsible
fordetecting the end-of-copy signal* (a line containing just "\.") when using this routine.** RETURNS:*        EOF if
error(eg, invalid arguments are given)*        0 if EOL is reached (i.e., \n has been read)*                (this is
requiredfor backward-compatibility -- this*                 routine used to always return EOF or 0, assuming that*
          the line ended within maxlen bytes.)*        1 in other cases (i.e., the buffer was filled before \n is
reached)*/
int
PQgetline(PGconn *conn, char *s, int maxlen)
{if (!s || maxlen <= 0)    return EOF;*s = '\0';/* maxlen must be at least 3 to hold the \. terminator! */if (maxlen <
3)   return EOF;
 
if (!conn)    return EOF;
if (PG_PROTOCOL_MAJOR(conn->pversion) >= 3)    return pqGetline3(conn, s, maxlen);else    return pqGetline2(conn, s,
maxlen);
}

/** PQgetlineAsync - gets a COPY data row without blocking.** This routine is for applications that want to do "COPY
<rel>to stdout"* asynchronously, that is without blocking.  Having issued the COPY command* and gotten a PGRES_COPY_OUT
response,the app should call PQconsumeInput* and this routine until the end-of-data signal is detected.  Unlike*
PQgetline,this routine takes responsibility for detecting end-of-data.** On each call, PQgetlineAsync will return data
ifa complete data row* is available in libpq's input buffer.  Otherwise, no data is returned* until the rest of the row
arrives.**If -1 is returned, the end-of-data signal has been recognized (and removed* from libpq's input buffer).  The
caller*must* next call PQendcopy and* then return to normal processing.** RETURNS:*     -1    if the end-of-copy-data
markerhas been recognized*     0       if no data is available*     >0    the number of bytes returned.** The data
returnedwill not extend beyond a data-row boundary.  If possible* a whole row will be returned at one time.  But if the
bufferoffered by* the caller is too small to hold a row sent by the backend, then a partial* data row will be returned.
In text mode this can be detected by testing* whether the last returned byte is '\n' or not.** The returned data is
*not*null-terminated.*/
 

int
PQgetlineAsync(PGconn *conn, char *buffer, int bufsize)
{if (!conn)    return -1;
if (PG_PROTOCOL_MAJOR(conn->pversion) >= 3)    return pqGetlineAsync3(conn, buffer, bufsize);else    return
pqGetlineAsync2(conn,buffer, bufsize);
 
}

/** PQputline -- sends a string to the backend during COPY IN.* Returns 0 if OK, EOF if not.** This is deprecated
primarilybecause 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)
{return PQputnbytes(conn, s, strlen(s));
}

/** PQputnbytes -- like PQputline, but buffer need not be null-terminated.* Returns 0 if OK, EOF if not.*/
int
PQputnbytes(PGconn *conn, const char *buffer, int nbytes)
{if (PQputCopyData(conn, buffer, nbytes) > 0)    return 0;else    return EOF;
}

/** PQendcopy*        After completing the data transfer portion of a copy in/out,*        the application must call
thisroutine to finish the command protocol.** When using protocol 3.0 this is deprecated; it's cleaner to use
PQgetResult*to get the transfer status.    Note however that when using 2.0 protocol,* recovering from a copy failure
oftenrequires a PQreset.  PQendcopy will* take care of that, PQgetResult won't.** RETURNS:*        0 on success*
1on failure*/
 
int
PQendcopy(PGconn *conn)
{if (!conn)    return 0;
if (PG_PROTOCOL_MAJOR(conn->pversion) >= 3)    return pqEndcopy3(conn);else    return pqEndcopy2(conn);
}


/* ----------------*        PQfn -    Send a function call to the POSTGRES backend.**        conn            : backend
connection*       fnid            : function id*        result_buf        : pointer to result buffer (&int if integer)*
      result_len        : length of return value.*        actual_result_len: actual length returned. (differs from
result_len*                         for varlena structures.)*        result_type        : If the result is an integer,
thismust be 1,*                          otherwise this should be 0*        args            : pointer to an array of
functionarguments.*                          (each has length, if integer, and value/pointer)*        nargs
:# of arguments in args array.** RETURNS*        PGresult with status = PGRES_COMMAND_OK if successful.*
*actual_result_lenis > 0 if there is a return value, 0 if not.*        PGresult with status = PGRES_FATAL_ERROR if
backendreturns an error.*        NULL on communications failure.  conn->errorMessage will be set.* ----------------*/
 

PGresult *
PQfn(PGconn *conn, int fnid, int *result_buf, int *actual_result_len, int result_is_int, const PQArgBlock *args, int
nargs)
{*actual_result_len = 0;
if (!conn)    return NULL;
/* clear the error string */resetPQExpBuffer(&conn->errorMessage);
if (conn->sock < 0 || conn->asyncStatus != PGASYNC_IDLE ||    conn->result != NULL){
printfPQExpBuffer(&conn->errorMessage,                     libpq_gettext("connection in wrong state\n"));    return
NULL;}
if (PG_PROTOCOL_MAJOR(conn->pversion) >= 3)    return pqFunctionCall3(conn, fnid,                           result_buf,
actual_result_len,                          result_is_int,                           args, nargs);else    return
pqFunctionCall2(conn,fnid,                           result_buf, actual_result_len,
result_is_int,                          args, nargs);
 
}


/* ====== accessor funcs for PGresult ======== */

ExecStatusType
PQresultStatus(const PGresult *res)
{if (!res)    return PGRES_FATAL_ERROR;return res->resultStatus;
}

char *
PQresStatus(ExecStatusType status)
{if (status < 0 || status >= sizeof pgresStatus / sizeof pgresStatus[0])    return libpq_gettext("invalid
ExecStatusTypecode");return pgresStatus[status];
 
}

char *
PQresultErrorMessage(const PGresult *res)
{if (!res || !res->errMsg)    return "";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)
{if (!res)    return 0;return res->ntups;
}

int
PQnfields(const PGresult *res)
{if (!res)    return 0;return res->numAttributes;
}

int
PQbinaryTuples(const PGresult *res)
{if (!res)    return 0;return res->binary;
}

/** Helper routines to range-check field numbers and tuple numbers.* Return TRUE if OK, FALSE if not*/

static int
check_field_number(const PGresult *res, int field_num)
{if (!res)    return FALSE;            /* no way to display error message... */if (field_num < 0 || field_num >=
res->numAttributes){   pqInternalNotice(&res->noticeHooks,                     "column number %d is out of range
0..%d",                    field_num, res->numAttributes - 1);    return FALSE;}return TRUE;
 
}

static int
check_tuple_field_number(const PGresult *res,                     int tup_num, int field_num)
{if (!res)    return FALSE;            /* no way to display error message... */if (tup_num < 0 || tup_num >=
res->ntups){   pqInternalNotice(&res->noticeHooks,                     "row number %d is out of range 0..%d",
         tup_num, res->ntups - 1);    return FALSE;}if (field_num < 0 || field_num >= res->numAttributes){
pqInternalNotice(&res->noticeHooks,                    "column number %d is out of range 0..%d",
field_num,res->numAttributes - 1);    return FALSE;}return TRUE;
 
}

static int
check_param_number(const PGresult *res, int param_num)
{if (!res)    return FALSE;            /* no way to display error message... */if (param_num < 0 || param_num >=
res->numParameters){   pqInternalNotice(&res->noticeHooks,                     "parameter number %d is out of range
0..%d",                    param_num, res->numParameters - 1);    return FALSE;}
 
return TRUE;
}

/** returns NULL if the field_num is invalid*/
char *
PQfname(const PGresult *res, int field_num)
{if (!check_field_number(res, field_num))    return NULL;if (res->attDescs)    return
res->attDescs[field_num].name;else   return NULL;
 
}

/** PQfnumber: find column number given column name** The column name is parsed as if it were in a SQL statement,
including*case-folding and double-quote processing.  But note a possible gotcha:* downcasing in the frontend might
followdifferent locale rules than* downcasing in the backend...** Returns -1 if no match.    In the present backend it
isalso possible* to have multiple matches, in which case the first one is found.*/
 
int
PQfnumber(const PGresult *res, const char *field_name)
{char       *field_case;bool        in_quotes;char       *iptr;char       *optr;int            i;
if (!res)    return -1;
/* * Note: it is correct to reject a zero-length input string; the proper * input to match a zero-length field name
wouldbe "". */if (field_name == NULL ||    field_name[0] == '\0' ||    res->attDescs == NULL)    return -1;
 
/* * Note: this code will not reject partially quoted strings, eg * foo"BAR"foo will become fooBARfoo when it probably
oughtto be an error * condition. */field_case = strdup(field_name);if (field_case == NULL)    return -1;
/*grotty */
 
in_quotes = false;optr = field_case;for (iptr = field_case; *iptr; iptr++){    char        c = *iptr;
    if (in_quotes)    {        if (c == '"')        {            if (iptr[1] == '"')            {                /*
doubledquotes become a single quote */                *optr++ = '"';                iptr++;            }
else               in_quotes = false;        }        else            *optr++ = c;    }    else if (c == '"')
in_quotes= true;    else    {        c = pg_tolower((unsigned char) c);        *optr++ = c;    }}*optr = '\0';
 
for (i = 0; i < res->numAttributes; i++){    if (strcmp(field_case, res->attDescs[i].name) == 0)    {
free(field_case);       return i;    }}free(field_case);return -1;
 
}

Oid
PQftable(const PGresult *res, int field_num)
{if (!check_field_number(res, field_num))    return InvalidOid;if (res->attDescs)    return
res->attDescs[field_num].tableid;else   return InvalidOid;
 
}

int
PQftablecol(const PGresult *res, int field_num)
{if (!check_field_number(res, field_num))    return 0;if (res->attDescs)    return
res->attDescs[field_num].columnid;else   return 0;
 
}

int
PQfformat(const PGresult *res, int field_num)
{if (!check_field_number(res, field_num))    return 0;if (res->attDescs)    return res->attDescs[field_num].format;else
  return 0;
 
}

Oid
PQftype(const PGresult *res, int field_num)
{if (!check_field_number(res, field_num))    return InvalidOid;if (res->attDescs)    return
res->attDescs[field_num].typid;else   return InvalidOid;
 
}

int
PQfsize(const PGresult *res, int field_num)
{if (!check_field_number(res, field_num))    return 0;if (res->attDescs)    return res->attDescs[field_num].typlen;else
  return 0;
 
}

int
PQfmod(const PGresult *res, int field_num)
{if (!check_field_number(res, field_num))    return 0;if (res->attDescs)    return
res->attDescs[field_num].atttypmod;else   return 0;
 
}

char *
PQcmdStatus(PGresult *res)
{if (!res)    return NULL;return res->cmdStatus;
}

/** PQoidStatus -*    if the last command was an INSERT, return the oid string*    if not, return ""*/
char *
PQoidStatus(const PGresult *res)
{/* * This must be enough to hold the result. Don't laugh, this is better * than what this function used to do.
*/staticchar buf[24];
 
size_t        len;
if (!res || !res->cmdStatus || strncmp(res->cmdStatus, "INSERT ", 7) != 0)    return "";
len = strspn(res->cmdStatus + 7, "0123456789");if (len > 23)    len = 23;strncpy(buf, res->cmdStatus + 7, len);buf[len]
='\0';
 
return buf;
}

/** PQoidValue -*    a perhaps preferable form of the above which just returns*    an Oid type*/
Oid
PQoidValue(const PGresult *res)
{char       *endptr = NULL;unsigned long result;
if (!res ||    !res->cmdStatus ||    strncmp(res->cmdStatus, "INSERT ", 7) != 0 ||    res->cmdStatus[7] < '0' ||
res->cmdStatus[7]> '9')    return InvalidOid;
 
result = strtoul(res->cmdStatus + 7, &endptr, 10);
if (!endptr || (*endptr != ' ' && *endptr != '\0'))    return InvalidOid;else    return (Oid) result;
}


/** PQcmdTuples -*    If the last command was INSERT/UPDATE/DELETE/MOVE/FETCH/COPY, return*    a string containing the
numberof inserted/affected tuples. If not,*    return "".**    XXX: this should probably return an int*/
 
char *
PQcmdTuples(PGresult *res)
{char       *p,           *c;
if (!res)    return "";
if (strncmp(res->cmdStatus, "INSERT ", 7) == 0){    p = res->cmdStatus + 7;    /* INSERT: skip oid and space */
while(*p && *p != ' ')        p++;    if (*p == 0)        goto interpret_error;        /* no space? */    p++;}else if
(strncmp(res->cmdStatus,"DELETE ", 7) == 0 ||         strncmp(res->cmdStatus, "UPDATE ", 7) == 0)    p = res->cmdStatus
+7;else if (strncmp(res->cmdStatus, "FETCH ", 6) == 0)    p = res->cmdStatus + 6;else if (strncmp(res->cmdStatus, "MOVE
",5) == 0 ||         strncmp(res->cmdStatus, "COPY ", 5) == 0)    p = res->cmdStatus + 5;else    return "";
 
/* check that we have an integer (at least one digit, nothing else) */for (c = p; *c; c++){    if (!isdigit((unsigned
char)*c))        goto interpret_error;}if (c == p)    goto interpret_error;
 
return p;

interpret_error:pqInternalNotice(&res->noticeHooks,                 "could not interpret result from server: %s",
         res->cmdStatus);return "";
 
}

/** PQgetvalue:*    return the value of field 'field_num' of row 'tup_num'*/
char *
PQgetvalue(const PGresult *res, int tup_num, int field_num)
{if (!check_tuple_field_number(res, tup_num, field_num))    return NULL;return res->tuples[tup_num][field_num].value;
}

/* PQgetlength:*    returns the actual length of a field value in bytes.*/
int
PQgetlength(const PGresult *res, int tup_num, int field_num)
{if (!check_tuple_field_number(res, tup_num, field_num))    return 0;if (res->tuples[tup_num][field_num].len !=
NULL_LEN)   return res->tuples[tup_num][field_num].len;else    return 0;
 
}

/* PQgetisnull:*    returns the null status of a field value.*/
int
PQgetisnull(const PGresult *res, int tup_num, int field_num)
{if (!check_tuple_field_number(res, tup_num, field_num))    return 1;                /* pretend it is null */if
(res->tuples[tup_num][field_num].len== NULL_LEN)    return 1;else    return 0;
 
}

/* PQnparams:*    returns the number of input parameters of a prepared statement.*/
int
PQnparams(const PGresult *res)
{if (!res)    return 0;return res->numParameters;
}

/* PQparamtype:*    returns type Oid of the specified statement parameter.*/
Oid
PQparamtype(const PGresult *res, int param_num)
{if (!check_param_number(res, param_num))    return InvalidOid;if (res->paramDescs)    return
res->paramDescs[param_num].typid;else   return InvalidOid;
 
}


/* PQsetnonblocking:*    sets the PGconn's database connection non-blocking if the arg is TRUE*    or makes it
non-blockingif the arg is FALSE, this will not protect*    you from PQexec(), you'll only be safe when using the
non-blockingAPI.*    Needs to be called only on a connected database connection.*/
 
int
PQsetnonblocking(PGconn *conn, int arg)
{bool        barg;
if (!conn || conn->status == CONNECTION_BAD)    return -1;
barg = (arg ? TRUE : FALSE);
/* early out if the socket is already in the state requested */if (barg == conn->nonblocking)    return 0;
/* * to guarantee constancy for flushing/query/result-polling behavior we * need to flush the send queue at this point
inorder to guarantee proper * behavior. this is ok because either they are making a transition _from_ * or _to_
blockingmode, either way we can block them. *//* if we are going from blocking to non-blocking flush here */if
(pqFlush(conn))   return -1;
 
conn->nonblocking = barg;
return 0;
}

/** return the blocking status of the database connection*        TRUE == nonblocking, FALSE == blocking*/
int
PQisnonblocking(const PGconn *conn)
{return pqIsnonblocking(conn);
}

/* libpq is thread-safe? */
int
PQisthreadsafe(void)
{
#ifdef ENABLE_THREAD_SAFETYreturn true;
#elsereturn false;
#endif
}


/* try to force data out, really only useful for non-blocking users */
int
PQflush(PGconn *conn)
{return pqFlush(conn);
}


/**        PQfreemem - safely frees memory allocated** Needed mostly by Win32, unless multithreaded DLL (/MD in VC6)*
Usedfor freeing memory from PQescapeByte()a/PQunescapeBytea()*/
 
void
PQfreemem(void *ptr)
{free(ptr);
}

/** PQfreeNotify - free's the memory associated with a PGnotify** This function is here only for binary backward
compatibility.*New code should use PQfreemem().  A macro will automatically map* calls to PQfreemem.    It should be
removedin the future.  bjm 2003-03-24*/
 

#undef PQfreeNotify
void        PQfreeNotify(PGnotify *notify);

void
PQfreeNotify(PGnotify *notify)
{PQfreemem(notify);
}


/** Escaping arbitrary strings to get valid SQL literal strings.** Replaces "'" with "''", and if not std_strings,
replaces"\" with "\\".** length is the length of the source string.  (Note: if a terminating NUL* is encountered
sooner,PQescapeString stops short of "length"; the behavior* is thus rather like strncpy.)** For safety the buffer at
"to"must be at least 2*length + 1 bytes long.* A terminating NUL character is added to the output string, whether the*
inputis NUL-terminated or not.** Returns the actual length of the output (not counting the terminating NUL).*/
 
static size_t
PQescapeStringInternal(PGconn *conn,                   char *to, const char *from, size_t length,                   int
*error,                  int encoding, bool std_strings)
 
{const char *source = from;char       *target = to;size_t        remaining = length;
if (error)    *error = 0;
while (remaining > 0 && *source != '\0'){    char        c = *source;    int            len;    int            i;
    /* Fast path for plain ASCII */    if (!IS_HIGHBIT_SET(c))    {        /* Apply quoting if needed */        if
(SQL_STR_DOUBLE(c,!std_strings))            *target++ = c;        /* Copy the character */        *target++ = c;
source++;       remaining--;        continue;    }
 
    /* Slow path for possible multibyte characters */    len = pg_encoding_mblen(encoding, source);
    /* Copy the character */    for (i = 0; i < len; i++)    {        if (remaining == 0 || *source == '\0')
break;       *target++ = *source++;        remaining--;    }
 
    /*     * If we hit premature end of string (ie, incomplete multibyte     * character), try to pad out to the
correctlength with spaces. We     * may not be able to pad completely, but we will always be able to     * insert at
leastone pad space (since we'd not have quoted a     * multibyte character).  This should be enough to make a string
that    * the server will error out on.     */    if (i < len)    {        if (error)            *error = 1;        if
(conn)           printfPQExpBuffer(&conn->errorMessage,                      libpq_gettext("incomplete multibyte
character\n"));       for (; i < len; i++)        {            if (((size_t) (target - to)) / 2 >= length)
 break;            *target++ = ' ';        }        break;    }}
 
/* Write the terminating NUL character. */*target = '\0';
return target - to;
}

size_t
PQescapeStringConn(PGconn *conn,               char *to, const char *from, size_t length,               int *error)
{if (!conn){    /* force empty-string result */    *to = '\0';    if (error)        *error = 1;    return 0;}return
PQescapeStringInternal(conn,to, from, length, error,                              conn->client_encoding,
             conn->std_strings);
 
}

size_t
PQescapeString(char *to, const char *from, size_t length)
{return PQescapeStringInternal(NULL, to, from, length, NULL,                              static_client_encoding,
                      static_std_strings);
 
}

/**        PQescapeBytea    - converts from binary string to the*        minimal encoding necessary to include the
stringin an SQL*        INSERT statement with a bytea type column as the target.**        The following transformations
areapplied*        '\0' == ASCII  0 == \000*        '\'' == ASCII 39 == ''*        '\\' == ASCII 92 == \\*
anything< 0x20, or > 0x7e ---> \ooo*                                        (where ooo is an octal expression)*
Ifnot std_strings, all backslashes sent to the output are doubled.*/
 
static unsigned char *
PQescapeByteaInternal(PGconn *conn,                  const unsigned char *from, size_t from_length,
size_t*to_length, bool std_strings)
 
{const unsigned char *vp;unsigned char *rp;unsigned char *result;size_t        i;size_t        len;size_t
bslash_len= (std_strings ? 1 : 2);
 
/* * empty string has 1 char ('\0') */len = 1;
vp = from;for (i = from_length; i > 0; i--, vp++){    if (*vp < 0x20 || *vp > 0x7e)        len += bslash_len + 3;
elseif (*vp == '\'')        len += 2;    else if (*vp == '\\')        len += bslash_len + bslash_len;    else
len++;}
*to_length = len;rp = result = (unsigned char *) malloc(len);if (rp == NULL){    if (conn)
printfPQExpBuffer(&conn->errorMessage,                         libpq_gettext("out of memory\n"));    return NULL;}
 
vp = from;for (i = from_length; i > 0; i--, vp++){    if (*vp < 0x20 || *vp > 0x7e)    {        int            val =
*vp;
        if (!std_strings)            *rp++ = '\\';        *rp++ = '\\';        *rp++ = (val >> 6) + '0';        *rp++ =
((val>> 3) & 07) + '0';        *rp++ = (val & 07) + '0';    }    else if (*vp == '\'')    {        *rp++ = '\'';
*rp++= '\'';    }    else if (*vp == '\\')    {        if (!std_strings)        {            *rp++ = '\\';
*rp++= '\\';        }        *rp++ = '\\';        *rp++ = '\\';    }    else        *rp++ = *vp;}*rp = '\0';
 
return result;
}

unsigned char *
PQescapeByteaConn(PGconn *conn,              const unsigned char *from, size_t from_length,              size_t
*to_length)
{if (!conn)    return NULL;return PQescapeByteaInternal(conn, from, from_length, to_length,
conn->std_strings);
}

unsigned char *
PQescapeBytea(const unsigned char *from, size_t from_length, size_t *to_length)
{return PQescapeByteaInternal(NULL, from, from_length, to_length,                             static_std_strings);
}


#define ISFIRSTOCTDIGIT(CH) ((CH) >= '0' && (CH) <= '3')
#define ISOCTDIGIT(CH) ((CH) >= '0' && (CH) <= '7')
#define OCTVAL(CH) ((CH) - '0')

/**        PQunescapeBytea - converts the null terminated string representation*        of a bytea, strtext, into
binary,filling a buffer. It returns a*        pointer to the buffer (or NULL on error), and the size of the*
bufferin retbuflen. The pointer may subsequently be used as an*        argument to the function PQfreemem.**        The
followingtransformations are made:*        \\     == ASCII 92 == \*        \ooo == a byte whose value = ooo (ooo is an
octalnumber)*        \x     == x (x is any character not matched by the above transformations)*/
 
unsigned char *
PQunescapeBytea(const unsigned char *strtext, size_t *retbuflen)
{size_t        strtextlen,            buflen;unsigned char *buffer,           *tmpbuf;size_t        i,            j;
if (strtext == NULL)    return NULL;
strtextlen = strlen((const char *) strtext);
/* * Length of input is max length of output, but add one to avoid * unportable malloc(0) if input is zero-length.
*/buffer= (unsigned char *) malloc(strtextlen + 1);if (buffer == NULL)    return NULL;
 
for (i = j = 0; i < strtextlen;){    switch (strtext[i])    {        case '\\':            i++;            if
(strtext[i]== '\\')                buffer[j++] = strtext[i++];            else            {                if
((ISFIRSTOCTDIGIT(strtext[i]))&&                    (ISOCTDIGIT(strtext[i + 1])) &&
(ISOCTDIGIT(strtext[i+ 2])))                {                    int byte;
 
                    byte = OCTVAL(strtext[i++]);                    byte = (byte <<3) +OCTVAL(strtext[i++]);
       byte = (byte <<3) +OCTVAL(strtext[i++]);                    buffer[j++] = byte;                }            }
 
            /*             * Note: if we see '\' followed by something that isn't a             * recognized escape
sequence,we loop around having done             * nothing except advance i.  Therefore the something will be
* emitted as ordinary data on the next cycle. Corner case:             * '\' at end of string will just be discarded.
         */            break;
 
        default:            buffer[j++] = strtext[i++];            break;    }}buflen = j;                    /* buflen
isthe length of the dequoted data */
 
/* Shrink the buffer to be no larger than necessary *//* +1 avoids unportable behavior when buflen==0 */tmpbuf =
realloc(buffer,buflen + 1);
 
/* It would only be a very brain-dead realloc that could fail, but... */if (!tmpbuf){    free(buffer);    return
NULL;}
*retbuflen = buflen;return tmpbuf;
}

В списке pgsql-hackers по дате отправления:

Предыдущее
От: Craig Ringer
Дата:
Сообщение: Re: Thoughts on statistics for continuously advancing columns
Следующее
От: "Albe Laurenz"
Дата:
Сообщение: Re: A third lock method