Here are the changes to libpq. It adds the ability to register an
Object Hook and create a home-grown result. Patch adds 4 functions.
We changed the name of PQresultSetFieldValue to PQsetvalue, which better
compliments PQgetvalue. If this patch is acceptable, we will move on to
making the required changes to pqtypes; some work has already been done.
--
Andrew Chernow
eSilo, LLC
every bit counts
http://www.esilo.com/
Index: src/interfaces/libpq/exports.txt
===================================================================
RCS file: /projects/cvsroot/pgsql/src/interfaces/libpq/exports.txt,v
retrieving revision 1.19
diff -c -r1.19 exports.txt
*** src/interfaces/libpq/exports.txt 19 Mar 2008 00:39:33 -0000 1.19
--- src/interfaces/libpq/exports.txt 11 Apr 2008 17:36:26 -0000
***************
*** 141,143 ****
--- 141,147 ----
pg_valid_server_encoding_id 139
PQconnectionNeedsPassword 140
lo_import_with_oid 141
+ PQaddObjectHook 142
+ PQremoveObjectHook 143
+ PQmakeResult 144
+ PQsetvalue 145
\ No newline at end of file
Index: src/interfaces/libpq/fe-connect.c
===================================================================
RCS file: /projects/cvsroot/pgsql/src/interfaces/libpq/fe-connect.c,v
retrieving revision 1.357
diff -c -r1.357 fe-connect.c
*** src/interfaces/libpq/fe-connect.c 31 Mar 2008 02:43:14 -0000 1.357
--- src/interfaces/libpq/fe-connect.c 11 Apr 2008 17:36:26 -0000
***************
*** 866,872 ****
--- 866,887 ----
* we are in PGRES_POLLING_WRITING connection state.
*/
if (PQconnectPoll(conn) == PGRES_POLLING_WRITING)
+ {
+ int objHooksCount;
+ const PGobjectHooks *objHooks;
+
+ if((objHooksCount = pqGetObjectHooks(&objHooks)) > 0)
+ {
+ int i;
+
+ for(i=0; i < objHooksCount; i++)
+ if(objHooks[i].connCreate)
+
objHooks[i].connCreate(objHooks[i].name, conn);
+ }
+ pqReleaseObjectHooks();
+
return 1;
+ }
connect_errReturn:
if (conn->sock >= 0)
***************
*** 1973,1978 ****
--- 1988,2006 ----
static void
freePGconn(PGconn *conn)
{
+ int objHooksCount;
+ const PGobjectHooks *objHooks;
+
+ if((objHooksCount = pqGetObjectHooks(&objHooks)) > 0)
+ {
+ int i;
+
+ for(i=0; i < objHooksCount; i++)
+ if(objHooks[i].connDestroy)
+ objHooks[i].connDestroy(objHooks[i].name, conn);
+ }
+ pqReleaseObjectHooks();
+
if (conn->pghost)
free(conn->pghost);
if (conn->pghostaddr)
Index: src/interfaces/libpq/fe-exec.c
===================================================================
RCS file: /projects/cvsroot/pgsql/src/interfaces/libpq/fe-exec.c,v
retrieving revision 1.194
diff -c -r1.194 fe-exec.c
*** src/interfaces/libpq/fe-exec.c 1 Jan 2008 19:46:00 -0000 1.194
--- src/interfaces/libpq/fe-exec.c 11 Apr 2008 17:36:27 -0000
***************
*** 63,69 ****
static PGresult *PQexecFinish(PGconn *conn);
static int PQsendDescribe(PGconn *conn, char desc_type,
const char *desc_target);
!
/* ----------------
* Space management for PGresult.
--- 63,69 ----
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.
***************
*** 139,144 ****
--- 139,146 ----
PQmakeEmptyPGresult(PGconn *conn, ExecStatusType status)
{
PGresult *result;
+ int objHooksCount;
+ const PGobjectHooks *objHooks;
result = (PGresult *) malloc(sizeof(PGresult));
if (!result)
***************
*** 192,200 ****
--- 194,341 ----
result->client_encoding = PG_SQL_ASCII;
}
+ if((objHooksCount = pqGetObjectHooks(&objHooks)) > 0)
+ {
+ int i;
+
+ for(i=0; i < objHooksCount; i++)
+ if(objHooks[i].resultCreate)
+ objHooks[i].resultCreate(objHooks[i].name,
conn, result);
+ }
+ pqReleaseObjectHooks();
+
return result;
}
+ PGresult *PQmakeResult(
+ PGconn *conn,
+ int numAttributes,
+ PGresAttDesc *attDescs)
+ {
+ int i;
+ PGresult *res;
+
+ if(numAttributes <= 0 || !attDescs)
+ return NULL;
+
+ res = PQmakeEmptyPGresult(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;
+ }
+
/*
* pqResultAlloc -
* Allocate subsidiary storage for a PGresult.
***************
*** 353,362 ****
--- 494,515 ----
PQclear(PGresult *res)
{
PGresult_data *block;
+ int objHooksCount;
+ const PGobjectHooks *objHooks;
if (!res)
return;
+ if((objHooksCount = pqGetObjectHooks(&objHooks)) > 0)
+ {
+ int i;
+
+ for(i=0; i < objHooksCount; i++)
+ if(objHooks[i].resultDestroy)
+ objHooks[i].resultDestroy(objHooks[i].name,
res);
+ }
+ pqReleaseObjectHooks();
+
/* Free all the subsidiary blocks */
while ((block = res->curBlock) != NULL)
{
Index: src/interfaces/libpq/fe-misc.c
===================================================================
RCS file: /projects/cvsroot/pgsql/src/interfaces/libpq/fe-misc.c,v
retrieving revision 1.133
diff -c -r1.133 fe-misc.c
*** src/interfaces/libpq/fe-misc.c 1 Jan 2008 19:46:00 -0000 1.133
--- src/interfaces/libpq/fe-misc.c 11 Apr 2008 17:36:27 -0000
***************
*** 1155,1157 ****
--- 1155,1332 ----
}
#endif /* ENABLE_NLS */
+
+
+ /* ---------------------
+ * ObjectHooks
+ */
+
+ #ifdef ENABLE_THREAD_SAFETY
+ # ifdef WIN32
+ # include "pthread-win32.h"
+ static pthread_mutex_t objHookMutex = NULL;
+ # else
+ # include <pthread.h>
+ static pthread_mutex_t objHookMutex = PTHREAD_MUTEX_INITIALIZER;
+ # endif
+ #endif
+
+ /* Object Hook array management variables. The hooks array is
+ * dynamically grown as needed. When removing an element, the
+ * element is memmove'd out.
+ */
+ static int objHooksCount = 0;
+ static int objHooksArrSize = 0;
+ static PGobjectHooks *objHooks = NULL;
+
+ int PQaddObjectHook(PGobjectHooks *hook, char **errMsg)
+ {
+ int i;
+
+ if(!hook)
+ {
+ if(errMsg)
+ *errMsg = "ObjectHook pointer cannot be NULL";
+ return FALSE;
+ }
+
+ /* hook names must be provided */
+ if(!hook->name || !*hook->name)
+ {
+ if(errMsg)
+ *errMsg = "ObjectHook name cannot be NULL or an empty
string";
+ return FALSE;
+ }
+
+ /* At least one hook func must be provided (pointless otherwise) */
+ if(!hook->connCreate && !hook->connDestroy &&
+ !hook->resultCreate && !hook->resultDestroy)
+ {
+ if(errMsg)
+ *errMsg = "At least one ObjectHook function must be
provided";
+ return FALSE;
+ }
+
+ (void)pqGetObjectHooks(NULL);
+
+ /* hook names must be unique, case ignored. */
+ for(i=0; i < objHooksCount; i++)
+ {
+ if(pg_strcasecmp(objHooks[i].name, hook->name)==0)
+ {
+ pqReleaseObjectHooks();
+ if(errMsg)
+ *errMsg = "ObjectHook name already exists";
+ return FALSE;
+ }
+ }
+
+ /* grow objHooks array */
+ if(objHooksCount == objHooksArrSize)
+ {
+ int n = objHooksArrSize ? (objHooksArrSize*3)/2 : 4;
+
+ objHooks = (PGobjectHooks *)malloc(sizeof(PGobjectHooks) * n);
+ if(!objHooks)
+ {
+ if(errMsg)
+ *errMsg = "Out of memory error";
+ return FALSE;
+ }
+
+ objHooksArrSize = n;
+ }
+
+ memcpy(objHooks + objHooksCount, hook, sizeof(PGobjectHooks));
+ objHooks[objHooksCount++].name = strdup(hook->name);
+
+ pqReleaseObjectHooks();
+
+ if(errMsg)
+ *errMsg = "";
+ return TRUE;
+ }
+
+ /* Removes a hook from the global hooks array. */
+ void PQremoveObjectHook(const char *name)
+ {
+ int i;
+
+ if(!name || !*name)
+ return;
+
+ (void)pqGetObjectHooks(NULL);
+
+ for(i=0; i < objHooksCount; i++)
+ {
+ if(pg_strcasecmp(objHooks[i].name, name)==0)
+ {
+ free((void *)objHooks[i].name);
+ memmove(objHooks + i, objHooks + i + 1,
+ (objHooksCount - i - 1) *
sizeof(PGobjectHooks));
+ objHooksCount--;
+ break;
+ }
+ }
+
+ pqReleaseObjectHooks();
+ }
+
+ /* Gets the object hooks array, storing the array at *hooks and returning
+ * the number of elements in the array. The hooks array should be
+ * treated as read-only memory.
+ *
+ * When ENABLE_THREAD_SAFETY is set, this will lock a private mutex
+ * before returning. To unlock the hooks, call pqReleaseObjectHooks.
+ * Warning, you must call pqReleaseObjectHooks for each call to
+ * pqGetObjectHooks.
+ *
+ * const PGobjectHooks *hooks;
+ * int count = pqGetObjectHooks(&hooks);
+ * // ... do some work with hooks
+ * pqReleaseObjectHooks();
+ *
+ * If the provided hooks argument is NULL, the internal mutex is still
+ * locked and the function returns the number of hooks registered.
+ * You still have to call pqReleaseObjectHooks.
+ *
+ * If there are no hooks registered, *hooks will be NULL and the
+ * function returns 0.
+ */
+ int pqGetObjectHooks(const PGobjectHooks **hooks)
+ {
+ #ifdef ENABLE_THREAD_SAFETY
+ # ifdef WIN32
+ static long initlock = 0;
+
+ /* copied from init_ssl_system */
+ if(objHookMutex == NULL)
+ {
+ while (InterlockedExchange(&initlock, 1) == 1)
+ /* loop, another thread own the lock */ ;
+ if (objHookMutex == NULL)
+ pthread_mutex_init(&objHookMutex, NULL);
+ InterlockedExchange(&initlock, 0);
+ }
+ # endif
+
+ pthread_mutex_lock(&objHookMutex);
+ #endif
+
+ if(hooks)
+ *hooks = (const PGobjectHooks *)objHooks;
+ return objHooksCount;
+ }
+
+ /* Must be called after pqGetObjectHooks, so the mutex can be unlocked
+ * when ENABLE_THREAD_SAFETY is set.
+ */
+ void pqReleaseObjectHooks(void)
+ {
+ #ifdef ENABLE_THREAD_SAFETY
+ pthread_mutex_unlock(&objHookMutex);
+ #endif
+ }
+
+
+
Index: src/interfaces/libpq/libpq-fe.h
===================================================================
RCS file: /projects/cvsroot/pgsql/src/interfaces/libpq/libpq-fe.h,v
retrieving revision 1.142
diff -c -r1.142 libpq-fe.h
*** src/interfaces/libpq/libpq-fe.h 19 Mar 2008 00:39:33 -0000 1.142
--- src/interfaces/libpq/libpq-fe.h 11 Apr 2008 17:36:27 -0000
***************
*** 193,198 ****
--- 193,236 ----
} 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
+ {
+ const char *name;
+
+ /* Invoked on PQconnectdb, PQsetdbLogin, PQconnectStart or PQreset */
+ int (*connCreate)(const char *hookName, PGconn *conn);
+
+ /* Invoked on PQfinish. */
+ int (*connDestroy)(const char *hookName, PGconn *conn);
+
+ /* Invoked when PQmakeEmptyResult is called */
+ int (*resultCreate)(const char *hookName, PGconn *conn, PGresult
*result);
+
+ /* Invoked when PQclear is called */
+ int (*resultDestroy)(const char *hookName, PGresult *result);
+ } PGobjectHooks;
+
+ /* ----------------
* Exported functions of libpq
* ----------------
*/
***************
*** 437,442 ****
--- 475,500 ----
*/
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);
+
+ /*
+ * 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,
***************
*** 509,514 ****
--- 567,581 ----
/* Get encoding id from environment variable PGCLIENTENCODING */
extern int PQenv2encoding(void);
+ /* Add a libpq global Object Hook. If errMsg is not NULL, it will
+ * be set to an error message if the function fails. This returns
+ * non-zero on success and zero on failure.
+ */
+ extern int PQaddObjectHook(PGobjectHooks *hook, char **errMsg);
+
+ /* Removes an Object Hook previously added with PQaddObjectHook */
+ extern void PQremoveObjectHook(const char *name);
+
/* === in fe-auth.c === */
extern char *PQencryptPassword(const char *passwd, const char *user);
Index: src/interfaces/libpq/libpq-int.h
===================================================================
RCS file: /projects/cvsroot/pgsql/src/interfaces/libpq/libpq-int.h,v
retrieving revision 1.129
diff -c -r1.129 libpq-int.h
*** src/interfaces/libpq/libpq-int.h 1 Jan 2008 19:46:00 -0000 1.129
--- src/interfaces/libpq/libpq-int.h 11 Apr 2008 17:36:27 -0000
***************
*** 100,118 ****
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
{
--- 100,105 ----
***************
*** 205,210 ****
--- 192,198 ----
int spaceLeft; /* number of free bytes
remaining in block */
};
+
/* PGAsyncStatusType defines the state of the query-execution state machine */
typedef enum
{
***************
*** 524,529 ****
--- 512,522 ----
extern int pqReadReady(PGconn *conn);
extern int pqWriteReady(PGconn *conn);
+ /* === in objhooks.c === */
+
+ extern int pqGetObjectHooks(const PGobjectHooks **hooks);
+ extern void pqReleaseObjectHooks(void);
+
/* === in fe-secure.c === */
extern int pqsecure_initialize(PGconn *);
--
Sent via pgsql-patches mailing list (pgsql-patches@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-patches