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

Reply via email to