Re: libpq object hooks patch

Поиск
Список
Период
Сортировка
От Andrew Chernow
Тема Re: libpq object hooks patch
Дата
Msg-id 4805598C.2060904@esilo.com
обсуждение исходный текст
Ответ на libpq object hooks patch  (Andrew Chernow <ac@esilo.com>)
Список pgsql-patches
Andrew Chernow wrote:
> Here is an updated version of the object hooks patch.  It now supports a
> list of hooks for a PGconn, and PGresult.  This had to re-introduce the
> concept of hook name.  Being that there is now a list, you need a way to
> reference an item of that list.
>
> Also added PQobjectHooks and PQresultObjectHooks, to get a pointer to
> the conn or result hook list.  PQmakeResult must allow the ability to
> pass a list of object hooks in.  So, PQresultObjectHooks was born.
> pqtypes doesn't need (at least at this time) PQobjectHooks but leaving
> it out felt unbalanced.
>
> Note: PQhookData and PQresultHookData can be removed.  There
> functionality can be reproduced by an API user issuing PQobjectHooks or
> PQresultObjectHooks and manually looking for there hook (normaly to get
> at the hook->data).  Basically, an API user would do themselves what
> PQhookData is doing.
>
>

Made some changes:

1. Removed the hookName argument to PQaddObjectHooks, since its in the
supplied PQobjectHooks struct.  I think the argument was lingering from
a previous patch.

2. Added the ability to install global object hooks:
PQaddGlobalObjectHooks(PGobjectHooks*).  The header docs for this
function warns that it should only be used before calling libpq
functions or creating application threads.  There are no management
functions for global hooks, like get or remove.  If you add global
object hooks, your stuck with them until process death.

3. There is a new internal pqInitObjectHooks(PGconn *) that installs the
global object hooks on new conns in the CONNECTION_OK status.  I call
this function within PQconnectPoll (3 different locations).  This will
call PQaddObjectHooks(conn) for each global hook (so global hooks are
always at the front of a conn's hook list).  pqInitObjectHooks checks to
see if conn->objHooksCount > 0 and if it is, the request is ignored and
the function returns success.  This only happens during a PQreset and
PQresetPoll.

// global
PQaddGlobalObjectHooks(&libpq_debugger_hook);

// per-conn
PQaddObjectHooks(conn, &session_manager_hook);

Since the existing list of object hooks is scanned for duplicate names
when adding them, you will never run into duplicate object hooks in a
connection (or in the global list).  If the two examples above were both
called by an application, the per-conn call would fail.

--
Andrew Chernow
eSilo, LLC
every bit counts
http://www.esilo.com/
? exports.list
? libpq.so.5.2
? object_hooks.patch
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    16 Apr 2008 01:14:40 -0000
***************
*** 138,143 ****
--- 138,152 ----
  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
+ PQaddGlobalObjectHooks    150
\ 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    16 Apr 2008 01:14:40 -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];

***************
*** 998,1009 ****
--- 998,1010 ----
               * We really shouldn't have been polled in these two cases, but we
               * can handle it.
               */
          case CONNECTION_BAD:
              return PGRES_POLLING_FAILED;
          case CONNECTION_OK:
+             pqInitObjectHooks(conn);
              return PGRES_POLLING_OK;

              /* These are reading states */
          case CONNECTION_AWAITING_RESPONSE:
          case CONNECTION_AUTH_OK:
              {
***************
*** 1816,1827 ****
--- 1817,1829 ----
                      conn->next_eo = EnvironmentOptions;
                      return PGRES_POLLING_WRITING;
                  }

                  /* Otherwise, we are open for business! */
                  conn->status = CONNECTION_OK;
+                 pqInitObjectHooks(conn);
                  return PGRES_POLLING_OK;
              }

          case CONNECTION_SETENV:

              /*
***************
*** 1848,1859 ****
--- 1850,1862 ----
                  default:
                      goto error_return;
              }

              /* We are open for business! */
              conn->status = CONNECTION_OK;
+             pqInitObjectHooks(conn);
              return PGRES_POLLING_OK;

          default:
              printfPQExpBuffer(&conn->errorMessage,
                                libpq_gettext(
                                              "invalid connection state %c, "
***************
*** 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)
--- 1878,1889 ----
***************
*** 1970,1981 ****
--- 1972,2001 ----
   * 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);
***************
*** 2151,2164 ****
  PQreset(PGconn *conn)
  {
      if (conn)
      {
          closePGconn(conn);

!         if (connectDBStart(conn))
!             (void) connectDBComplete(conn);
      }
  }


  /*
   * PQresetStart:
--- 2171,2189 ----
  PQreset(PGconn *conn)
  {
      if (conn)
      {
          closePGconn(conn);

!         if (connectDBStart(conn) && connectDBComplete(conn))
!         {
!             int i;
!             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)
!         {
!             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    16 Apr 2008 01:14:41 -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    16 Apr 2008 01:14:41 -0000
***************
*** 1152,1157 ****
--- 1152,1324 ----
      }

      return dgettext("libpq", msgid);
  }

  #endif   /* ENABLE_NLS */
+
+
+ /* ---------------------
+  * ObjectHooks
+  */
+
+ /* global object hooks, see PQaddGlobalObjectHooks */
+ static int g_objHooksCount = 0;
+ static int g_objHooksSize = 0;
+ static PGobjectHooks *g_objHooks = NULL;
+
+ static int
+ hookExists(PGobjectHooks *objHooks, int objHooksCount, const char *name)
+ {
+     int i;
+     for(i=0; i < objHooksCount; i++)
+         if(pg_strcasecmp(objHooks[i].name, name)==0)
+             return TRUE;
+     return FALSE;
+ }
+
+ static int
+ growHooks(PGobjectHooks **objHooks, int *objHooksSize,
+     int objHooksCount)
+ {
+     /* grow list */
+     if(objHooksCount == *objHooksSize)
+     {
+         PGobjectHooks *oh;
+         int n = *objHooksSize ? (*objHooksSize*3)/2 : 4;
+
+         if(*objHooks)
+             oh = (PGobjectHooks *)realloc(
+                 *objHooks, n*sizeof(PGobjectHooks));
+         else
+             oh = (PGobjectHooks *)malloc(n*sizeof(PGobjectHooks));
+
+         if(!oh)
+             return FALSE;
+
+         *objHooks = oh;
+         *objHooksSize = n;
+     }
+
+     return TRUE;
+ }
+
+ static int
+ hookCopy(PGobjectHooks *dst, PGobjectHooks *src)
+ {
+     memcpy(dst, src, sizeof(PGobjectHooks));
+     dst->data = NULL;
+     dst->name = strdup(src->name);
+     return dst->name ? TRUE : FALSE;
+ }
+
+ int
+ PQaddGlobalObjectHooks(PGobjectHooks *hooks)
+ {
+     if(!hooks || !hooks->name || !*hooks->name)
+         return FALSE;
+
+     if(hookExists(g_objHooks, g_objHooksCount, hooks->name) ||
+          !growHooks(&g_objHooks, &g_objHooksSize, g_objHooksCount) ||
+          !hookCopy(g_objHooks + g_objHooksCount, hooks))
+         return FALSE;
+
+     g_objHooksCount++;
+     return TRUE;
+ }
+
+ /* inits the global hooks for a conn object.  If the hooks were already
+  * installed, the request is ignored and a success value is returned.
+  * This will happen during a PQreset and PQresetPoll.
+  * Returns non-zero for success and zero on error.
+  */
+ int
+ pqInitObjectHooks(PGconn *conn)
+ {
+     int i;
+
+     /* Already installed, occurs on PQreset and PQresetPoll */
+     if(conn->objHooksCount > 0)
+         return TRUE;
+
+     for(i=0; i < g_objHooksCount; i++)
+         if(!PQaddObjectHooks(conn, &g_objHooks[i]))
+             return FALSE;
+
+     return TRUE;
+ }
+
+ int
+ PQaddObjectHooks(PGconn *conn, PGobjectHooks *hooks)
+ {
+     PGobjectHooks *oh;
+
+     if(!conn || !hooks || !hooks->name || !*hooks->name)
+         return FALSE;
+
+     /* names must be unique */
+     if(hookExists(conn->objHooks, conn->objHooksCount, hooks->name) ||
+          !growHooks(&conn->objHooks, &conn->objHooksSize, conn->objHooksCount) ||
+          !hookCopy(conn->objHooks + conn->objHooksCount, hooks))
+         return FALSE;
+
+     oh = &conn->objHooks[conn->objHooksCount++];
+     if(oh->initHookData)
+         oh->data = oh->initHookData((const PGconn *)conn);
+
+     return TRUE;
+ }
+
+ 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    16 Apr 2008 01:14:41 -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,625 ----
  /* 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 global object hooks, which will be inherited by every
+  * PGconn.  Object hooks added in this fashion do not require being
+  * added on a per-connection basis.  Any number of object hooks can be
+  * be added.
+  *
+  * WARNING: This should only be called in the application's main thread
+  * before using any other libpq functions.  This is not thread-safe and
+  * should be used prior to creating any application threads.
+  */
+ int
+ PQaddGlobalObjectHooks(PGobjectHooks *hooks);
+
+ /* Adds a set of object hooks to the given connection. */
+ extern int
+ PQaddObjectHooks(PGconn *conn, 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    16 Apr 2008 01:14:41 -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,531 ****
  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 === */

  extern int    pqsecure_initialize(PGconn *);
  extern void pqsecure_destroy(void);
  extern PostgresPollingStatusType pqsecure_open_client(PGconn *);
--- 512,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);
+ extern int pqInitObjectHooks(PGconn *conn);

  /* === in fe-secure.c === */

  extern int    pqsecure_initialize(PGconn *);
  extern void pqsecure_destroy(void);
  extern PostgresPollingStatusType pqsecure_open_client(PGconn *);

В списке pgsql-patches по дате отправления:

Предыдущее
От: Euler Taveira de Oliveira
Дата:
Сообщение: Re: lc_time and localized dates
Следующее
От: ITAGAKI Takahiro
Дата:
Сообщение: Re: Sorting writes during checkpoint