Should the hook only be called when the conn or result was
> successfull or should the hooks be called for failed
connections/commands as well?


ISTM that the hooks should be called on success and error (doesn't include cases where a NULL conn or result is returned). I think this makes things more useful. If the hooker (pun intended) isn't interested in error results or conns, it can easily ignore them.

connCreate: The best solution I found was to hook into PQconnectPoll. This required making the current PQconnectPoll a static named connectPoll and making PQconnectPoll a wrapper to it. The wrapper just calls connectPoll and checks the status for hook points.

resultCreate: PQgetResult seemed like the best place. I only notify the hooks if the resultStatus is NOT copyin or copyout.

I diff'd fe-connect.c and fe-exec.c against cvs which shows these changes.

--
Andrew Chernow
eSilo, LLC
every bit counts
http://www.esilo.com/
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        12 Apr 2008 13:22:30 -0000
***************
*** 240,252 ****
  static int parseServiceInfo(PQconninfoOption *options,
                                 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;
  
  
  /*
--- 240,252 ----
  static int parseServiceInfo(PQconninfoOption *options,
                                 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);
! static void notifyConnCreateHooks(PGconn *conn);
  
  /* global variable because fe-auth.c needs to access it */
  pgthreadlock_t pg_g_threadlock = default_threadlock;
  
  
  /*
***************
*** 891,903 ****
--- 891,907 ----
  connectDBComplete(PGconn *conn)
  {
        PostgresPollingStatusType flag = PGRES_POLLING_WRITING;
        time_t          finish_time = ((time_t) -1);
  
        if (conn == NULL || conn->status == CONNECTION_BAD)
+       {
+               if(conn)
+                       notifyConnCreateHooks(conn);
                return 0;
+       }
  
        /*
         * Set up a time limit, if connect_timeout isn't zero.
         */
        if (conn->connect_timeout != NULL)
        {
***************
*** 979,992 ****
   *     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];
  
        if (conn == NULL)
                return PGRES_POLLING_FAILED;
--- 983,997 ----
   *     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.
   *
   * ----------------
   */
! 
! static PostgresPollingStatusType
! connectPoll(PGconn *conn)
  {
        PGresult   *res;
        char            sebuf[256];
  
        if (conn == NULL)
                return PGRES_POLLING_FAILED;
***************
*** 1875,1886 ****
--- 1880,1908 ----
         * the connection structure must be freed).
         */
        conn->status = CONNECTION_BAD;
        return PGRES_POLLING_FAILED;
  }
  
+ /* See connectPoll.  All this does is wrap the real PQconnectPoll so
+  * we can trap PGRES_POLLING_OK or PGRES_POLLING_FAILED statuses.  This
+  * could be done in the real connectPoll, but that requires littering the
+  * function with notifyConnCreateHooks calls because there are many
+  * places the function returns a status.  This solution is much less
+  * obtrusive and avoids messing with the black magic of connectPoll.
+  */
+ PostgresPollingStatusType
+ PQconnectPoll(PGconn *conn)
+ {
+       PostgresPollingStatusType status = connectPoll(conn);
+ 
+       if(status == PGRES_POLLING_OK || status == PGRES_POLLING_FAILED)
+               notifyConnCreateHooks(conn);
+ 
+       return status;
+ }
  
  /*
   * makeEmptyPGconn
   *     - create a PGconn data structure with (as yet) no interesting data
   */
  static PGconn *
***************
*** 1970,1981 ****
--- 1992,2015 ----
   * 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 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, 
(const PGconn *)conn);
+       }
+       pqReleaseObjectHooks();
        if (conn->pghost)
                free(conn->pghost);
        if (conn->pghostaddr)
                free(conn->pghostaddr);
        if (conn->pgport)
                free(conn->pgport);
***************
*** 3843,3854 ****
--- 3877,3904 ----
                pthread_mutex_lock(&singlethread_lock);
        else
                pthread_mutex_unlock(&singlethread_lock);
  #endif
  }
  
+ /* Calls the connCreate hook. */
+ static void notifyConnCreateHooks(PGconn *conn)
+ {
+       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, (const 
PGconn *)conn);
+       }
+ 
+       pqReleaseObjectHooks();
+ }
  pgthreadlock_t
  PQregisterThreadLock(pgthreadlock_t newhandler)
  {
        pgthreadlock_t prev = pg_g_threadlock;
  
        if (newhandler)
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   12 Apr 2008 13:22:31 -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
***************
*** 192,203 ****
--- 192,331 ----
                result->client_encoding = PG_SQL_ASCII;
        }
  
        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.
   *
   * nBytes is the amount of space needed for the object.
   * If isBinary is true, we assume that we need to align the object on
***************
*** 350,365 ****
--- 478,504 ----
   *      free's the memory associated with a PGresult
   */
  void
  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, 
(const PGresult *)res);
+       }
+       pqReleaseObjectHooks();
        /* Free all the subsidiary blocks */
        while ((block = res->curBlock) != NULL)
        {
                res->curBlock = block->next;
                free(block);
        }
***************
*** 1179,1190 ****
--- 1318,1346 ----
        parseInput(conn);
  
        /* PQgetResult will return immediately in all states except BUSY. */
        return conn->asyncStatus == PGASYNC_BUSY;
  }
  
+ /* Used by PQgetResult */
+ static void notifyResultCreateHooks(PGconn *conn, PGresult *res)
+ {
+       int objHooksCount;
+       const PGobjectHooks *objHooks;
+ 
+       if((objHooksCount = pqGetObjectHooks(&objHooks)) > 0)
+       {
+               int i;
+               for(i=0; i < objHooksCount; i++)
+                       if(objHooks[i].resultCreate)
+                               objHooks[i].resultCreate(objHooks[i].name, 
(const PGconn *)conn,
+                                       (const PGresult *)res);
+       }
+ 
+       pqReleaseObjectHooks();
+ }
  
  /*
   * 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).
***************
*** 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);
        }
  
--- 1383,1403 ----
                        /*
                         * 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)
!                       {
!                               notifyResultCreateHooks(conn, res);
!                       }
! 
!                       return res;
                }
  
                /* Parse it. */
                parseInput(conn);
        }
  
***************
*** 1265,1276 ****
--- 1429,1446 ----
                                                          
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)
+       {
+               notifyResultCreateHooks(conn, res);
+       }
+ 
        return res;
  }
  
  
  /*
   * PQexec
-- 
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