Here is a patch to add a few functions to libpq: PQmakeResult: creates a new result with attributes but no rows PQsetValue: set a field inside a result, creating a row if necessary (but only one at a time) PQresultAlloc (basically public wrapper to existing internal function).
also, the attribute structure (but not the result) is moved to the public header. We thought these functions, particularly PQsetValue, are something positive that came out of the libpqtypes dicussions. merlin
Index: interfaces/libpq/exports.txt =================================================================== RCS file: /projects/cvsroot/pgsql/src/interfaces/libpq/exports.txt,v retrieving revision 1.19 diff -C6 -r1.19 exports.txt *** interfaces/libpq/exports.txt 19 Mar 2008 00:39:33 -0000 1.19 --- interfaces/libpq/exports.txt 15 Apr 2008 14:22:00 -0000 *************** *** 138,143 **** --- 138,146 ---- PQsendDescribePortal 136 lo_truncate 137 PQconnectionUsedPassword 138 pg_valid_server_encoding_id 139 PQconnectionNeedsPassword 140 lo_import_with_oid 141 + PQmakeResult 142 + PQsetvalue 143 + PQresultAlloc 144 Index: interfaces/libpq/fe-exec.c =================================================================== RCS file: /projects/cvsroot/pgsql/src/interfaces/libpq/fe-exec.c,v retrieving revision 1.194 diff -C6 -r1.194 fe-exec.c *** interfaces/libpq/fe-exec.c 1 Jan 2008 19:46:00 -0000 1.194 --- interfaces/libpq/fe-exec.c 15 Apr 2008 14:22:00 -0000 *************** *** 60,72 **** 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); ! /* ---------------- * 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 --- 60,73 ---- 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 *************** *** 192,203 **** --- 193,338 ---- result->client_encoding = PG_SQL_ASCII; } return result; } + PGresult * + PQmakeResult(const PGconn *conn, int numAttributes, + PGresAttDesc *attDescs) + { + int i; + PGresult *res; + + if(numAttributes <= 0 || !attDescs) + return NULL; + + res = PQmakeEmptyPGresult((PGconn *)conn, PGRES_TUPLES_OK); + if(!res) + return NULL; + + res->attDescs = (PGresAttDesc *) + pqResultAlloc(res, numAttributes * sizeof(PGresAttDesc), TRUE); + + if(!res->attDescs) + { + PQclear(res); + return NULL; + } + + res->numAttributes = numAttributes; + memcpy(res->attDescs, attDescs, numAttributes * sizeof(PGresAttDesc)); + + /* resultalloc the attribute names. */ + res->binary = 1; + for(i=0; i < numAttributes; i++) + { + if(attDescs[i].name) + res->attDescs[i].name = pqResultStrdup(res, attDescs[i].name); + else + res->attDescs[i].name = res->null_field; + + if(!res->attDescs[i].name) + { + PQclear(res); + return NULL; + } + + /* Although deprecated, because results can have text+binary columns, + * its easy enough to deduce so set it for completeness. + */ + if(res->attDescs[i].format == 0) + res->binary = 0; + } + + return res; + } + + 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 > res->ntups) + return FALSE; + + /* need to grow the tuple table */ + if(res->ntups >= res->tupArrSize) + { + int n = res->tupArrSize ? (res->tupArrSize*3)/2 : 64; + PGresAttValue **tups = (PGresAttValue **) + (res->tuples ? realloc(res->tuples, n*sizeof(PGresAttValue *)) : + malloc(n*sizeof(PGresAttValue *))); + + if(!tups) + return FALSE; + + res->tuples = tups; + res->tupArrSize = n; + } + + /* new to allocate a new tuple */ + if(tup_num == res->ntups && !res->tuples[tup_num]) + { + int i; + PGresAttValue *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]; + + /* On top of NULL_LEN, treat a NULL value as a NULL field */ + if(len == NULL_LEN || value == NULL) + { + attval->len = NULL_LEN; + attval->value = res->null_field; + } + else + { + if(len < 0) + len = 0; + + 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; + } + + /* wrapper for pqResultAlloc, avoids refactoring pqResultAlloc + * to PQresultAlloc everywhere. Always sets isBinary to TRUE. + */ + 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 Index: interfaces/libpq/libpq-fe.h =================================================================== RCS file: /projects/cvsroot/pgsql/src/interfaces/libpq/libpq-fe.h,v retrieving revision 1.142 diff -C6 -r1.142 libpq-fe.h *** interfaces/libpq/libpq-fe.h 19 Mar 2008 00:39:33 -0000 1.142 --- interfaces/libpq/libpq-fe.h 15 Apr 2008 14:22:01 -0000 *************** *** 189,200 **** --- 189,212 ---- { int *ptr; /* can't use void (dec compiler barfs) */ int integer; } u; } PQArgBlock; + /* Data about a single attribute (column) of a query result */ + typedef struct pgresAttDesc + { + char *name; /* column name */ + Oid tableid; /* source table, if known */ + int columnid; /* source column, if known */ + int format; /* format code for value (text/binary) */ + Oid typid; /* type id */ + int typlen; /* type size */ + int atttypmod; /* type-specific modifier info */ + } PGresAttDesc; + /* ---------------- * Exported functions of libpq * ---------------- */ /* === in fe-connect.c === */ *************** *** 414,425 **** --- 426,463 ---- /* Describe prepared statements and portals */ extern PGresult *PQdescribePrepared(PGconn *conn, const char *stmt); extern PGresult *PQdescribePortal(PGconn *conn, const char *portal); extern int PQsendDescribePrepared(PGconn *conn, const char *stmt); extern int PQsendDescribePortal(PGconn *conn, const char *portal); + /* + * Makes a new result using the provided field descriptors. + * If conn is not NULL, some information will be copied to the + * new result. The returned result has zero tuples and a + * resultStatus of PGRES_TUPLES_OK. To add tuples and set field + * values, see PQsetvalue. + */ + extern PGresult * + PQmakeResult(const PGconn *conn, int numAttributes, + PGresAttDesc *attDescs); + + /* Allocate subsidiary storage for a PGresult. */ + extern void * + PQresultAlloc(PGresult *res, size_t nBytes); + + /* + * Sets the value for a tuple field. The tup_num must be less than or + * equal to PQntuples(res). This function will generate tuples as needed. + * A new tuple is generated when tup_num equals PQntuples(res) and there + * are no fields defined for that tuple. + * Returns a non-zero value for success and zero for failure. + */ + extern int + PQsetvalue(PGresult *res, int tup_num, int field_num, + char *value, int len); + /* Delete a PGresult */ extern void PQclear(PGresult *res); /* For freeing other alloc'd results, such as PGnotify structs */ extern void PQfreemem(void *ptr); Index: interfaces/libpq/libpq-int.h =================================================================== RCS file: /projects/cvsroot/pgsql/src/interfaces/libpq/libpq-int.h,v retrieving revision 1.129 diff -C6 -r1.129 libpq-int.h *** interfaces/libpq/libpq-int.h 1 Jan 2008 19:46:00 -0000 1.129 --- interfaces/libpq/libpq-int.h 15 Apr 2008 14:22:01 -0000 *************** *** 97,121 **** union pgresult_data { PGresult_data *next; /* link to next block, or NULL */ char space[1]; /* dummy for accessing block as bytes */ }; - /* Data about a single attribute (column) of a query result */ - - typedef struct pgresAttDesc - { - char *name; /* column name */ - Oid tableid; /* source table, if known */ - int columnid; /* source column, if known */ - int format; /* format code for value (text/binary) */ - Oid typid; /* type id */ - int typlen; /* type size */ - int atttypmod; /* type-specific modifier info */ - } PGresAttDesc; - /* Data about a single parameter of a prepared statement */ typedef struct pgresParamDesc { Oid typid; /* type id */ } PGresParamDesc; --- 97,108 ----
-- Sent via pgsql-patches mailing list (pgsql-patches@postgresql.org) To make changes to your subscription: http://www.postgresql.org/mailpref/pgsql-patches