diff options
Diffstat (limited to 'src/interfaces/libpq/fe-exec.c')
-rw-r--r-- | src/interfaces/libpq/fe-exec.c | 359 |
1 files changed, 352 insertions, 7 deletions
diff --git a/src/interfaces/libpq/fe-exec.c b/src/interfaces/libpq/fe-exec.c index 149a0b73f6b..7db303ce008 100644 --- a/src/interfaces/libpq/fe-exec.c +++ b/src/interfaces/libpq/fe-exec.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/interfaces/libpq/fe-exec.c,v 1.197 2008/09/10 17:01:07 tgl Exp $ + * $PostgreSQL: pgsql/src/interfaces/libpq/fe-exec.c,v 1.198 2008/09/17 04:31:08 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -48,6 +48,7 @@ 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, @@ -63,6 +64,7 @@ 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); /* ---------------- @@ -128,13 +130,8 @@ static int PQsendDescribe(PGconn *conn, char desc_type, * PQmakeEmptyPGresult * returns a newly allocated, initialized PGresult with given status. * If conn is not NULL and status indicates an error, the conn's - * errorMessage is copied. - * - * Note this is exported --- you wouldn't think an application would need - * to build its own PGresults, but this has proven useful in both libpgtcl - * and the Perl5 interface, so maybe it's not so unreasonable. + * errorMessage is copied. Also, any PGEvents are copied from the conn. */ - PGresult * PQmakeEmptyPGresult(PGconn *conn, ExecStatusType status) { @@ -154,6 +151,8 @@ PQmakeEmptyPGresult(PGconn *conn, ExecStatusType status) 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'; @@ -181,6 +180,18 @@ PQmakeEmptyPGresult(PGconn *conn, ExecStatusType status) 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 { @@ -196,6 +207,301 @@ PQmakeEmptyPGresult(PGconn *conn, ExecStatusType status) } /* + * PQsetResultAttrs + * + * Set the attributes for a given result. This function fails if there are + * already attributes contained in the provided result. The call is + * ignored if numAttributes is is zero or attDescs is NULL. If the + * function fails, 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 controls which portions of the result will or will + * NOT be copied. The created result is always put into the + * PGRES_TUPLES_OK status. 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 source result'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? NB: this should be last, as we don't want + * to trigger RESULTDESTROY events on a useless PGresult. + */ + 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++) + { + PGEventResultCopy evt; + + evt.src = src; + evt.dest = dest; + if (!dest->events[i].proc(PGEVT_RESULTCOPY, &evt, + dest->events[i].passThrough)) + { + PQclear(dest); + return NULL; + } + } + + return dest; +} + +/* + * Copy an array of PGEvents (with no extra space for more) + * Does not duplicate the event instance data, sets this to NULL + */ +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; + + memcpy(newEvents, events, count * sizeof(PGEvent)); + + /* NULL out the data pointers and deep copy names */ + for (i = 0; i < count; i++) + { + newEvents[i].data = NULL; + newEvents[i].name = strdup(newEvents[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 new tuple 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) + return FALSE; + 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. * @@ -353,10 +659,24 @@ void PQclear(PGresult *res) { PGresult_data *block; + int i; if (!res) return; + for (i = 0; i < res->nEvents; i++) + { + 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) { @@ -373,6 +693,8 @@ PQclear(PGresult *res) 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 */ @@ -1270,6 +1592,29 @@ PQgetResult(PGconn *conn) 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; + } + } + } + return res; } |