Re: exec_execute_message crash - Mailing list pgsql-hackers
From | Tatsuo Ishii |
---|---|
Subject | Re: exec_execute_message crash |
Date | |
Msg-id | 20091231.155611.128866384.t-ishii@sraoss.co.jp Whole thread Raw |
In response to | Re: exec_execute_message crash (Tatsuo Ishii <ishii@postgresql.org>) |
Responses |
Re: exec_execute_message crash
|
List | 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 by date: