Index: exports.txt =================================================================== RCS file: /projects/cvsroot/pgsql/src/interfaces/libpq/exports.txt,v retrieving revision 1.19 diff -C6 -r1.19 exports.txt *** exports.txt 19 Mar 2008 00:39:33 -0000 1.19 --- exports.txt 15 Apr 2008 17:18:38 -0000 *************** *** 138,143 **** --- 138,151 ---- 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 + PQaddObjectHooks 145 + PQhookData 146 + PQresultHookData 147 + PQobjectHooks 148 + PQresultObjectHooks 149 \ No newline at end of file Index: fe-connect.c =================================================================== RCS file: /projects/cvsroot/pgsql/src/interfaces/libpq/fe-connect.c,v retrieving revision 1.357 diff -C6 -r1.357 fe-connect.c *** fe-connect.c 31 Mar 2008 02:43:14 -0000 1.357 --- fe-connect.c 15 Apr 2008 17:18:39 -0000 *************** *** 241,253 **** PQExpBuffer errorMessage); static char *pwdfMatchesString(char *buf, char *token); static char *PasswordFromFile(char *hostname, char *port, char *dbname, char *username); static void default_threadlock(int acquire); - /* global variable because fe-auth.c needs to access it */ pgthreadlock_t pg_g_threadlock = default_threadlock; /* * Connecting to a Database --- 241,252 ---- *************** *** 979,990 **** --- 978,990 ---- * o If your backend wants to use Kerberos authentication then you must * supply both a host name and a host address, otherwise this function * may block on gethostname. * * ---------------- */ + PostgresPollingStatusType PQconnectPoll(PGconn *conn) { PGresult *res; char sebuf[256]; *************** *** 1875,1887 **** * the connection structure must be freed). */ conn->status = CONNECTION_BAD; return PGRES_POLLING_FAILED; } - /* * makeEmptyPGconn * - create a PGconn data structure with (as yet) no interesting data */ static PGconn * makeEmptyPGconn(void) --- 1875,1886 ---- *************** *** 1970,1981 **** --- 1969,1998 ---- * release data that is to be held for the life of the PGconn structure. * If a value ought to be cleared/freed during PQreset(), do it there not here. */ static void freePGconn(PGconn *conn) { + int i; + + for(i=0; i < conn->objHooksCount; i++) + { + if(conn->objHooks[i].connDestroy) + { + conn->objHooks[i].connDestroy((const PGconn *)conn); + free(conn->objHooks[i].name); + } + } + + if(conn->objHooks) + { + free(conn->objHooks); + conn->objHooks = NULL; + conn->objHooksCount = conn->objHooksSize = 0; + } + if (conn->pghost) free(conn->pghost); if (conn->pghostaddr) free(conn->pghostaddr); if (conn->pgport) free(conn->pgport); *************** *** 2152,2164 **** --- 2169,2189 ---- { if (conn) { closePGconn(conn); if (connectDBStart(conn)) + { + int i; + (void) connectDBComplete(conn); + + for(i=0; i < conn->objHooksCount; i++) + if(conn->objHooks[i].connReset) + conn->objHooks[i].connReset((const PGconn *)conn); + } } } /* * PQresetStart: *************** *** 2176,2198 **** return connectDBStart(conn); } return 0; } - /* * PQresetPoll: * resets the connection to the backend * closes the existing connection and makes a new one */ PostgresPollingStatusType PQresetPoll(PGconn *conn) { if (conn) ! return PQconnectPoll(conn); return PGRES_POLLING_FAILED; } /* * PQcancelGet: get a PGcancel structure corresponding to a connection. --- 2201,2234 ---- return connectDBStart(conn); } return 0; } /* * PQresetPoll: * resets the connection to the backend * closes the existing connection and makes a new one */ PostgresPollingStatusType PQresetPoll(PGconn *conn) { if (conn) ! { ! PostgresPollingStatusType status = PQconnectPoll(conn); ! ! if(status == PGRES_POLLING_OK || status == PGRES_POLLING_FAILED) ! { ! int i; ! for(i=0; i < conn->objHooksCount; i++) ! if(conn->objHooks[i].connReset) ! conn->objHooks[i].connReset((const PGconn *)conn); ! } ! ! return status; ! } return PGRES_POLLING_FAILED; } /* * PQcancelGet: get a PGcancel structure corresponding to a connection. *************** *** 3855,3860 **** --- 3891,3897 ---- pg_g_threadlock = newhandler; else pg_g_threadlock = default_threadlock; return prev; } + Index: 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 *** fe-exec.c 1 Jan 2008 19:46:00 -0000 1.194 --- fe-exec.c 15 Apr 2008 17:18:39 -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,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); ! 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 *************** *** 120,131 **** --- 120,156 ---- #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) + /* Does not duplicate the hook data, sets this to NULL */ + static PGobjectHooks * + dupObjectHooks(PGobjectHooks *hooks, int count) + { + int i; + PGobjectHooks *newHooks; + + if(!hooks || count <= 0) + return NULL; + + newHooks = (PGobjectHooks *)malloc(count * sizeof(PGobjectHooks)); + if(!newHooks) + return NULL; + + memcpy(newHooks, hooks, count * sizeof(PGobjectHooks)); + + /* NULL out the hook data pointer */ + for(i=0; i < count; i++) + { + newHooks[i].data = NULL; + newHooks[i].name = strdup(hooks[i].name); + } + + return newHooks; + } /* * 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. *************** *** 157,175 **** --- 182,216 ---- result->errMsg = NULL; result->errFields = NULL; result->null_field[0] = '\0'; result->curBlock = NULL; result->curOffset = 0; result->spaceLeft = 0; + result->objHooksCount = 0; + result->objHooksSize = 0; + result->objHooks = NULL; if (conn) { /* copy connection data we might need for operations on PGresult */ result->noticeHooks = conn->noticeHooks; result->client_encoding = conn->client_encoding; + /* copy object hooks from connection */ + if(conn->objHooksCount > 0) + { + result->objHooks = dupObjectHooks(conn->objHooks, conn->objHooksCount); + if(!result->objHooks) + { + PQclear(result); + return NULL; + } + + result->objHooksCount = result->objHooksSize = conn->objHooksCount; + } + /* consider copying conn's errorMessage */ switch (status) { case PGRES_EMPTY_QUERY: case PGRES_COMMAND_OK: case PGRES_TUPLES_OK: *************** *** 192,203 **** --- 233,409 ---- result->client_encoding = PG_SQL_ASCII; } return result; } + PGresult * + PQmakeResult(PGconn *conn, int numAttributes, PGresAttDesc *attDescs, + int hookCount, PGobjectHooks *hooks) + { + int i; + PGresult *res; + int saveHookCount = 0; + + if(numAttributes <= 0 || !attDescs) + return NULL; + + /* If hooks have been provided along with a conn, set the conn's + * objHookCount to 0 so they are no duplicated by makeemptyres. + * The provided hooks override what's in the conn. This avoids + * an unwanted allocation within makeemptyres. + */ + if(conn && hookCount > 0 && hooks) + { + saveHookCount = conn->objHooksCount; + conn->objHooksCount = 0; + } + + res = PQmakeEmptyPGresult((PGconn *)conn, PGRES_TUPLES_OK); + + /* reset the conn's objHookCount */ + if(conn && hookCount > 0 && hooks) + conn->objHooksCount = saveHookCount; + + 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; + } + + if(hookCount > 0 && hooks) + { + res->objHooks = dupObjectHooks(hooks, hookCount); + if(!res->objHooks) + { + PQclear(res); + return NULL; + } + + res->objHooksCount = res->objHooksSize = conn->objHooksCount; + } + + for(i=0; i < res->objHooksCount; i++) + if(res->objHooks[i].resultCreate) + res->objHooks[i].data = res->objHooks[i].resultCreate( + conn, (const PGresult *)res); + + 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; + } + + 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 *************** *** 349,365 **** --- 555,588 ---- * PQclear - * free's the memory associated with a PGresult */ void PQclear(PGresult *res) { + int i; PGresult_data *block; if (!res) return; + for(i=0; i < res->objHooksCount; i++) + { + if(res->objHooks[i].resultDestroy) + { + res->objHooks[i].resultDestroy((const PGresult *)res); + free(res->objHooks[i].name); + } + } + + if(res->objHooks) + { + free(res->objHooks); + res->objHooks = NULL; + res->objHooksCount = res->objHooksSize = 0; + } + /* Free all the subsidiary blocks */ while ((block = res->curBlock) != NULL) { res->curBlock = block->next; free(block); } *************** *** 1179,1191 **** 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 error has occurred (e.g. out of * memory). */ --- 1402,1413 ---- *************** *** 1227,1239 **** /* * conn->errorMessage has been set by pqWait or pqReadData. We * want to append it to any already-received error message. */ pqSaveErrorResult(conn); conn->asyncStatus = PGASYNC_IDLE; ! return pqPrepareAsyncResult(conn); } /* Parse it. */ parseInput(conn); } --- 1449,1473 ---- /* * conn->errorMessage has been set by pqWait or pqReadData. We * want to append it to any already-received error message. */ pqSaveErrorResult(conn); conn->asyncStatus = PGASYNC_IDLE; ! res = pqPrepareAsyncResult(conn); ! ! if(res && res->resultStatus != PGRES_COPY_IN && ! res->resultStatus != PGRES_COPY_OUT) ! { ! int i; ! for(i=0; i < res->objHooksCount; i++) ! if(res->objHooks[i].resultCreate) ! res->objHooks[i].data = res->objHooks[i].resultCreate( ! (const PGconn *)conn, (const PGresult *)res); ! } ! ! return res; } /* Parse it. */ parseInput(conn); } *************** *** 1265,1276 **** --- 1499,1520 ---- libpq_gettext("unexpected asyncStatus: %d\n"), (int) conn->asyncStatus); res = PQmakeEmptyPGresult(conn, PGRES_FATAL_ERROR); break; } + if(res && res->resultStatus != PGRES_COPY_IN && + res->resultStatus != PGRES_COPY_OUT) + { + int i; + for(i=0; i < res->objHooksCount; i++) + if(res->objHooks[i].resultCreate) + res->objHooks[i].data = res->objHooks[i].resultCreate( + (const PGconn *)conn, (const PGresult *)res); + } + return res; } /* * PQexec Index: fe-misc.c =================================================================== RCS file: /projects/cvsroot/pgsql/src/interfaces/libpq/fe-misc.c,v retrieving revision 1.133 diff -C6 -r1.133 fe-misc.c *** fe-misc.c 1 Jan 2008 19:46:00 -0000 1.133 --- fe-misc.c 15 Apr 2008 17:18:40 -0000 *************** *** 1152,1157 **** --- 1152,1262 ---- } return dgettext("libpq", msgid); } #endif /* ENABLE_NLS */ + + + /* --------------------- + * ObjectHooks + */ + + int + PQaddObjectHooks(PGconn *conn, const char *name, PGobjectHooks *hooks) + { + if(conn && hooks && name && *name) + { + int i; + PGobjectHooks *oh; + + /* names must be unique, ignore case */ + for(i=0; i < conn->objHooksCount; i++) + if(pg_strcasecmp(conn->objHooks[i].name, name)==0) + return FALSE; + + /* grow list */ + if(conn->objHooksCount == conn->objHooksSize) + { + int n = conn->objHooksSize ? (conn->objHooksSize*3)/2 : 4; + + if(conn->objHooks) + oh = (PGobjectHooks *)realloc( + conn->objHooks, n*sizeof(PGobjectHooks)); + else + oh = (PGobjectHooks *)malloc(n*sizeof(PGobjectHooks)); + + if(!oh) + return FALSE; + + conn->objHooks = oh; + conn->objHooksSize = n; + } + + oh = (PGobjectHooks *)&conn->objHooks[conn->objHooksCount++]; + memcpy(oh, hooks, sizeof(PGobjectHooks)); + oh->data = NULL; + oh->name = strdup(name); + + if(oh->initHookData) + oh->data = oh->initHookData((const PGconn *)conn); + + return TRUE; + } + + return FALSE; + } + + void * + PQhookData(const PGconn *conn, const char *hookName) + { + if(conn && hookName && *hookName) + { + int i; + PGobjectHooks *hooks; + int count = PQobjectHooks(conn, &hooks); + + for(i=0; i < count; i++) + if(pg_strcasecmp(hooks[i].name, hookName)==0) + return hooks[i].data; + } + + return NULL; + } + + int + PQobjectHooks(const PGconn *conn, PGobjectHooks **hooks) + { + if(!conn || !hooks) + return 0; + + *hooks = conn->objHooks; + return conn->objHooksCount; + } + + void * + PQresultHookData(const PGresult *res, const char *hookName) + { + if(res && hookName && *hookName) + { + int i; + PGobjectHooks *hooks; + int count = PQresultObjectHooks(res, &hooks); + + for(i=0; i < count; i++) + if(pg_strcasecmp(hooks[i].name, hookName)==0) + return hooks[i].data; + } + + return NULL; + } + + int + PQresultObjectHooks(const PGresult *res, PGobjectHooks **hooks) + { + if(!res || !hooks) + return 0; + + *hooks = res->objHooks; + return res->objHooksCount; + } + Index: 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 *** libpq-fe.h 19 Mar 2008 00:39:33 -0000 1.142 --- libpq-fe.h 15 Apr 2008 17:18:40 -0000 *************** *** 190,201 **** --- 190,248 ---- int *ptr; /* can't use void (dec compiler barfs) */ int integer; } u; } PQArgBlock; /* ---------------- + * PGresAttDesc -- 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; + + /* ---------------- + * PGobjectHooks -- structure for adding libpq object hooks + * Monitors the creation and deletion of objects. + * ---------------- + */ + + typedef struct + { + char *name; + + void *data; + + /* Invoked when PQsetObjectHook is called. The pointer returned + * by the hook implementation is stored in the private storage of + * the PGconn, accessible via PQhookData(PGconn*). If no + * storage is needed, return NULL or set this hook to NULL. + */ + void *(*initHookData)(const PGconn *conn); + + /* Invoked on PQreset and PQresetPoll */ + void (*connReset)(const PGconn *conn); + + /* Invoked on PQfinish. */ + void (*connDestroy)(const PGconn *conn); + + /* Invoked on PQgetResult, internally called by all exec funcs */ + void *(*resultCreate)(const PGconn *conn, const PGresult *result); + + /* Invoked when PQclear is called */ + void (*resultDestroy)(const PGresult *result); + } PGobjectHooks; + + /* ---------------- * Exported functions of libpq * ---------------- */ /* === in fe-connect.c === */ *************** *** 434,445 **** --- 481,517 ---- * Make an empty PGresult with given status (some apps find this * useful). If conn is not NULL and status indicates an error, the * conn's errorMessage is copied. */ extern PGresult *PQmakeEmptyPGresult(PGconn *conn, ExecStatusType status); + /* + * 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(PGconn *conn, int numAttributes, + PGresAttDesc *attDescs, int hookCount, PGobjectHooks *hooks); + + 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); + /* Quoting strings before inclusion in queries. */ extern size_t PQescapeStringConn(PGconn *conn, char *to, const char *from, size_t length, int *error); extern unsigned char *PQescapeByteaConn(PGconn *conn, *************** *** 506,517 **** --- 578,613 ---- /* Determine display length of multibyte encoded char at *s */ extern int PQdsplen(const char *s, int encoding); /* Get encoding id from environment variable PGCLIENTENCODING */ extern int PQenv2encoding(void); + /* Adds a set of object hooks to the given connection. */ + extern int + PQaddObjectHooks(PGconn *conn, const char *name, PGobjectHooks *hooks); + + extern void * + PQhookData(const PGconn *conn, const char *hookName); + + extern void * + PQresultHookData(const PGresult *res, const char *hookName); + + /* Gets the array of object hooks installed on the given conn. The + * number of hooks is returned, which can be zero. The array is pointed + * at *hooks. + */ + extern int + PQobjectHooks(const PGconn *conn, PGobjectHooks **hooks); + + /* Gets the array of object hooks installed on the given result. The + * number of hooks is returned, which can be zero. The array is pointed + * at *hooks. + */ + extern int + PQresultObjectHooks(const PGresult *res, PGobjectHooks **hooks); + /* === in fe-auth.c === */ extern char *PQencryptPassword(const char *passwd, const char *user); /* === in encnames.c === */ Index: 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 *** libpq-int.h 1 Jan 2008 19:46:00 -0000 1.129 --- libpq-int.h 15 Apr 2008 17:18:40 -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 ---- *************** *** 180,191 **** --- 167,181 ---- /* * These fields are copied from the originating PGconn, so that operations * on the PGresult don't have to reference the PGconn. */ PGNoticeHooks noticeHooks; int client_encoding; /* encoding id */ + int objHooksCount; + int objHooksSize; + PGobjectHooks *objHooks; /* Object Hooks */ /* * Error information (all NULL if not an error result). errMsg is the * "overall" error message returned by PQresultErrorMessage. If we have * per-field info then it is stored in a linked list. */ *************** *** 202,213 **** --- 192,204 ---- */ PGresult_data *curBlock; /* most recently allocated block */ int curOffset; /* start offset of free space in block */ int spaceLeft; /* number of free bytes remaining in block */ }; + /* PGAsyncStatusType defines the state of the query-execution state machine */ typedef enum { PGASYNC_IDLE, /* nothing's happening, dude */ PGASYNC_BUSY, /* query in progress */ PGASYNC_READY, /* result ready for PQgetResult */ *************** *** 300,311 **** --- 291,307 ---- /* Optional file to write trace info to */ FILE *Pfdebug; /* Callback procedures for notice message processing */ PGNoticeHooks noticeHooks; + /* Object Hooks */ + int objHooksCount; + int objHooksSize; + PGobjectHooks *objHooks; + /* Status indicators */ ConnStatusType status; PGAsyncStatusType asyncStatus; PGTransactionStatusType xactStatus; /* never changes to ACTIVE */ PGQueryClass queryclass; char *last_query; /* last SQL command, or NULL if unknown */ *************** *** 516,528 **** extern int pqPutInt(int value, size_t bytes, PGconn *conn); extern int pqPutMsgStart(char msg_type, bool force_len, PGconn *conn); extern int pqPutMsgEnd(PGconn *conn); extern int pqReadData(PGconn *conn); extern int pqFlush(PGconn *conn); extern int pqWait(int forRead, int forWrite, PGconn *conn); ! extern int pqWaitTimed(int forRead, int forWrite, PGconn *conn, time_t finish_time); extern int pqReadReady(PGconn *conn); extern int pqWriteReady(PGconn *conn); /* === in fe-secure.c === */ --- 512,524 ---- extern int pqPutInt(int value, size_t bytes, PGconn *conn); extern int pqPutMsgStart(char msg_type, bool force_len, PGconn *conn); extern int pqPutMsgEnd(PGconn *conn); extern int pqReadData(PGconn *conn); extern int pqFlush(PGconn *conn); extern int pqWait(int forRead, int forWrite, PGconn *conn); ! extern int pqWaitTimed(int forRead, int forWrite, PGconn *conn, time_t finish_time); extern int pqReadReady(PGconn *conn); extern int pqWriteReady(PGconn *conn); /* === in fe-secure.c === */