Re: libpq object hooks (libpq events)

Поиск
Список
Период
Сортировка
От Andrew Chernow
Тема Re: libpq object hooks (libpq events)
Дата
Msg-id 48325386.2010506@esilo.com
обсуждение исходный текст
Ответ на Re: libpq object hooks (libpq events)  (Andrew Chernow <ac@esilo.com>)
Список pgsql-patches
Attached is the latest patch.  It has addressed the requested changes found
here: http://archives.postgresql.org/pgsql-patches/2008-05/msg00389.php

Its a tarball because there are two new files, libpq-events.c and
libpq-events.h.  The patch is in the tarball as well as attached to the email.

--
Andrew Chernow
eSilo, LLC
every bit counts
http://www.esilo.com/
Index: src/interfaces/libpq/Makefile
===================================================================
RCS file: /projects/cvsroot/pgsql/src/interfaces/libpq/Makefile,v
retrieving revision 1.166
diff -C6 -r1.166 Makefile
*** src/interfaces/libpq/Makefile    16 Apr 2008 14:19:56 -0000    1.166
--- src/interfaces/libpq/Makefile    20 May 2008 04:18:07 -0000
***************
*** 29,41 ****
  # the object files from libpgport, this would not be true on all
  # platforms.
  LIBS := $(LIBS:-lpgport=)

  OBJS=    fe-auth.o fe-connect.o fe-exec.o fe-misc.o fe-print.o fe-lobj.o \
      fe-protocol2.o fe-protocol3.o pqexpbuffer.o pqsignal.o fe-secure.o \
!     md5.o ip.o wchar.o encnames.o noblock.o pgstrcasecmp.o thread.o \
      $(filter crypt.o getaddrinfo.o inet_aton.o open.o snprintf.o strerror.o strlcpy.o win32error.o, $(LIBOBJS))

  ifeq ($(PORTNAME), cygwin)
  override shlib = cyg$(NAME)$(DLSUFFIX)
  endif

--- 29,41 ----
  # the object files from libpgport, this would not be true on all
  # platforms.
  LIBS := $(LIBS:-lpgport=)

  OBJS=    fe-auth.o fe-connect.o fe-exec.o fe-misc.o fe-print.o fe-lobj.o \
      fe-protocol2.o fe-protocol3.o pqexpbuffer.o pqsignal.o fe-secure.o \
!     md5.o ip.o wchar.o encnames.o noblock.o pgstrcasecmp.o thread.o libpq-events.o \
      $(filter crypt.o getaddrinfo.o inet_aton.o open.o snprintf.o strerror.o strlcpy.o win32error.o, $(LIBOBJS))

  ifeq ($(PORTNAME), cygwin)
  override shlib = cyg$(NAME)$(DLSUFFIX)
  endif

***************
*** 103,114 ****
--- 103,115 ----

  $(top_builddir)/src/port/pg_config_paths.h:
      $(MAKE) -C $(top_builddir)/src/port pg_config_paths.h

  install: all installdirs install-lib
      $(INSTALL_DATA) $(srcdir)/libpq-fe.h '$(DESTDIR)$(includedir)'
+     $(INSTALL_DATA) $(srcdir)/libpq-events.h '$(DESTDIR)$(includedir)'
      $(INSTALL_DATA) $(srcdir)/libpq-int.h '$(DESTDIR)$(includedir_internal)'
      $(INSTALL_DATA) $(srcdir)/pqexpbuffer.h '$(DESTDIR)$(includedir_internal)'
      $(INSTALL_DATA) $(srcdir)/pg_service.conf.sample '$(DESTDIR)$(datadir)/pg_service.conf.sample'

  installdirs: installdirs-lib
      $(mkinstalldirs) '$(DESTDIR)$(includedir)' '$(DESTDIR)$(includedir_internal)'
Index: src/interfaces/libpq/exports.txt
===================================================================
RCS file: /projects/cvsroot/pgsql/src/interfaces/libpq/exports.txt,v
retrieving revision 1.19
diff -C6 -r1.19 exports.txt
*** src/interfaces/libpq/exports.txt    19 Mar 2008 00:39:33 -0000    1.19
--- src/interfaces/libpq/exports.txt    20 May 2008 04:18:07 -0000
***************
*** 138,143 ****
--- 138,153 ----
  PQsendDescribePortal      136
  lo_truncate               137
  PQconnectionUsedPassword  138
  pg_valid_server_encoding_id 139
  PQconnectionNeedsPassword 140
  lo_import_with_oid          141
+ PQcopyResult              142
+ PQsetvalue                143
+ PQresultAlloc             144
+ PQregisterEventProc       145
+ PQinstanceData            146
+ PQsetInstanceData         147
+ PQresultInstanceData      148
+ PQresultSetInstanceData   149
+ PQpassThroughData         150
+ PQresultPassThroughData   151
\ 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.358
diff -C6 -r1.358 fe-connect.c
*** src/interfaces/libpq/fe-connect.c    16 May 2008 18:30:53 -0000    1.358
--- src/interfaces/libpq/fe-connect.c    20 May 2008 04:18:08 -0000
***************
*** 1970,1981 ****
--- 1970,1999 ----
   * 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;
+     PGEventConnDestroy evt;
+
+     /* Let the event procs cleanup their state data */
+     for(i=0; i < conn->nEvents; i++)
+     {
+         evt.conn = conn;
+         (void)conn->events[i].proc(PGEVT_CONNDESTROY, &evt);
+     }
+
+     /* free the PGEvent array */
+     if(conn->events)
+     {
+         free(conn->events);
+         conn->events = NULL;
+         conn->nEvents = conn->eventArrSize = 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:
--- 2169,2199 ----
  PQreset(PGconn *conn)
  {
      if (conn)
      {
          closePGconn(conn);

!         if (connectDBStart(conn) && connectDBComplete(conn))
!         {
!             int i;
!             PGEventConnReset evt;
!
!             for(i=0; i < conn->nEvents; i++)
!             {
!                 evt.conn = conn;
!
!                 if(!conn->events[i].proc(PGEVT_CONNRESET, &evt))
!                 {
!                     conn->status = CONNECTION_BAD;
!                     printfPQExpBuffer(&conn->errorMessage,
!                         libpq_gettext("PGEventProc %p failed during PGEVT_CONNRESET event\n"),
!                         conn->events[i].proc);
!                     break;
!                 }
!             }
!         }
      }
  }


  /*
   * PQresetStart:
***************
*** 2186,2198 ****
   * 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.
--- 2221,2257 ----
   * 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;
!             PGEventConnReset evt;
!
!             for(i=0; i < conn->nEvents; i++)
!             {
!                 evt.conn = conn;
!
!                 if(!conn->events[i].proc(PGEVT_CONNRESET, &evt))
!                 {
!                     conn->status = CONNECTION_BAD;
!                     printfPQExpBuffer(&conn->errorMessage,
!                         libpq_gettext("PGEventProc %p failed during PGEVT_CONNRESET event\n"),
!                         conn->events[i].proc);
!                     return PGRES_POLLING_FAILED;
!                 }
!             }
!         }
!
!         return status;
!     }

      return PGRES_POLLING_FAILED;
  }

  /*
   * PQcancelGet: get a PGcancel structure corresponding to a connection.
Index: src/interfaces/libpq/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
*** src/interfaces/libpq/fe-exec.c    1 Jan 2008 19:46:00 -0000    1.194
--- src/interfaces/libpq/fe-exec.c    20 May 2008 04:18:08 -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
***************
*** 121,141 ****
--- 121,167 ----
  #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 event instance data, sets this to NULL */
+ static PGEvent *
+ dupEvents(PGEvent *events, int count)
+ {
+     int i;
+     PGEvent *newEvents;
+
+     if(!events || count <= 0)
+         return NULL;
+
+     newEvents = (PGEvent *)malloc(count * sizeof(PGEvent));
+     if(!newEvents)
+         return NULL;
+
+     memcpy(newEvents, events, count * sizeof(PGEvent));
+
+     /* NULL out the data pointer */
+     for(i=0; i < count; i++)
+         newEvents[i].data = NULL;
+
+     return newEvents;
+ }
+
  /*
   * 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.
   *
   * Note this is exported --- you wouldn't think an application would need
   * to build its own PGresults, but this has proven useful in both libpgtcl
   * and the Perl5 interface, so maybe it's not so unreasonable.
+  *
+  * Updated April 2008 - If conn is not NULL, event states will be copied
+  * from the PGconn to the created PGresult.
   */

  PGresult *
  PQmakeEmptyPGresult(PGconn *conn, ExecStatusType status)
  {
      PGresult   *result;
***************
*** 157,175 ****
--- 183,216 ----
      result->errMsg = NULL;
      result->errFields = NULL;
      result->null_field[0] = '\0';
      result->curBlock = NULL;
      result->curOffset = 0;
      result->spaceLeft = 0;
+     result->nEvents = 0;
+     result->events = NULL;

      if (conn)
      {
          /* copy connection data we might need for operations on PGresult */
          result->noticeHooks = conn->noticeHooks;
          result->client_encoding = conn->client_encoding;

+         /* copy events from connection */
+         if(conn->nEvents > 0)
+         {
+             result->events = dupEvents(conn->events, conn->nEvents);
+             if(!result->events)
+             {
+                 PQclear(result);
+                 return NULL;
+             }
+
+             result->nEvents = conn->nEvents;
+         }
+
          /* consider copying conn's errorMessage */
          switch (status)
          {
              case PGRES_EMPTY_QUERY:
              case PGRES_COMMAND_OK:
              case PGRES_TUPLES_OK:
***************
*** 193,204 ****
--- 234,481 ----
      }

      return result;
  }

  /*
+  * PQcopyResult
+  * Returns a deep copy of the provided 'src' PGresult, which cannot be NULL.
+  * The 'options' argument controls which portions of the result will or will
+  * NOT be copied.  If this value is 0, the entire result is deep copied.
+  * The created result is always put into the PGRES_TUPLES_OK status.  The
+  * source result error message is not copied, although cmdStatus is.
+  *
+  * Options:
+  *   PG_COPYRES_NO_TUPLES - Do not copy the tuples.  This option is
+  *   automatically enabled when PG_COPYRES_USE_ATTRS is set.
+  *
+  *   PG_COPYRES_USE_ATTRS - Indicates that the 'numAttributes' and 'attDescs'
+  *   arguments should be used as the fields for the created result, rather
+  *   than copying them from the source result.  When this option is set,
+  *   the tuples will NOT be copied; behaving identically to setting the
+  *   PG_COPYRES_NO_TUPLES option.  One must use PQsetvalue to manually
+  *   add tuples to the returned result.  NOTE: numAttributes and attDescs
+  *   arguments are ignored unless this option is set!
+  *
+  *   PG_COPYRES_NO_EVENTS - Indicates that the source result's
+  *   events should NOT be copied to the created result.
+  *
+  *   PG_COPYRES_NO_NOTICEHOOKS - Indicates that the source result's
+  *   NoticeHooks should NOT be copied to the created result.
+  */
+
+ PGresult *
+ PQcopyResult(const PGresult *src, int numAttributes,
+     PGresAttDesc *attDescs, int options)
+ {
+     int i;
+     PGresult *dest;
+     PGEventResultCopy evt;
+
+     if(!src)
+         return NULL;
+
+     /* Automatically turn on no_tuples since use_attrs is set.  It makes
+      * no sense to copy tuples into an unknown set of columns.
+      */
+     if(options & PG_COPYRES_USE_ATTRS)
+         options |= PG_COPYRES_NO_TUPLES;
+
+     /* If use_attrs is set, verify attr arguments. */
+     if((options & PG_COPYRES_USE_ATTRS) && (numAttributes <= 0 || !attDescs))
+         return NULL;
+
+     dest = PQmakeEmptyPGresult((PGconn *)NULL, PGRES_TUPLES_OK);
+     if(!dest)
+         return NULL;
+
+     /* always copy these over.  Is cmdStatus useful here? */
+     dest->client_encoding = src->client_encoding;
+     strcpy(dest->cmdStatus, src->cmdStatus);
+
+     /* Wants to copy notice hooks */
+     if(!(options & PG_COPYRES_NO_NOTICEHOOKS))
+         dest->noticeHooks = src->noticeHooks;
+
+     /* Copy from src result when not supplying attrs */
+     if(!(options & PG_COPYRES_USE_ATTRS) && src->numAttributes > 0)
+     {
+         numAttributes = src->numAttributes;
+         attDescs = src->attDescs;
+     }
+
+     /* copy attrs */
+     if(numAttributes > 0)
+     {
+         dest->attDescs = (PGresAttDesc *)PQresultAlloc(dest,
+             numAttributes * sizeof(PGresAttDesc));
+
+         if(!dest->attDescs)
+         {
+             PQclear(dest);
+             return NULL;
+         }
+
+         dest->numAttributes = numAttributes;
+         memcpy(dest->attDescs, attDescs, numAttributes * sizeof(PGresAttDesc));
+
+         /* resultalloc the attribute names.  The above memcpy has the attr
+          * names pointing at the source result's private memory (or at the
+          * callers provided attDescs memory).
+          */
+         dest->binary = 1;
+         for(i=0; i < numAttributes; i++)
+         {
+             if(attDescs[i].name)
+                 dest->attDescs[i].name = pqResultStrdup(dest, attDescs[i].name);
+             else
+                 dest->attDescs[i].name = dest->null_field;
+
+             if(!dest->attDescs[i].name)
+             {
+                 PQclear(dest);
+                 return NULL;
+             }
+
+             /* Although deprecated, because results can have text+binary columns,
+              * its easy enough to deduce so set it for completeness.
+              */
+             if(dest->attDescs[i].format == 0)
+                 dest->binary = 0;
+         }
+     }
+
+     /* Wants to copy result tuples: use PQsetvalue(). */
+     if(!(options & PG_COPYRES_NO_TUPLES) && src->ntups > 0)
+     {
+         int tup, field;
+         for(tup=0; tup < src->ntups; tup++)
+             for(field=0; field < src->numAttributes; field++)
+                 PQsetvalue(dest, tup, field, src->tuples[tup][field].value,
+                     src->tuples[tup][field].len);
+     }
+
+     /* Wants to copy PGEvents. */
+     if(!(options & PG_COPYRES_NO_EVENTS) && src->nEvents > 0)
+     {
+         dest->events = dupEvents(src->events, src->nEvents);
+         if(!dest->events)
+         {
+             PQclear(dest);
+             return NULL;
+         }
+
+         dest->nEvents = src->nEvents;
+     }
+
+     /* Trigger PGEVT_RESULTCOPY event */
+     for(i=0; i < dest->nEvents; i++)
+     {
+         evt.src = src;
+         evt.dest = dest;
+         if(!dest->events[i].proc(PGEVT_RESULTCOPY, &evt))
+         {
+             PQclear(dest);
+             return NULL;
+         }
+     }
+
+     return dest;
+ }
+
+ 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;
+
+         memset(tups + res->tupArrSize, 0,
+             (n - res->tupArrSize) * sizeof(PGresAttValue *));
+         res->tuples = tups;
+         res->tupArrSize = n;
+     }
+
+     /* need 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));
+
+         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;
+
+         if(len == 0)
+         {
+             attval->len = 0;
+             attval->value = res->null_field;
+         }
+         else
+         {
+             attval->value = (char *)PQresultAlloc(res, len + 1);
+             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
   * a machine allocation boundary.
***************
*** 349,365 ****
--- 626,657 ----
   * PQclear -
   *      free's the memory associated with a PGresult
   */
  void
  PQclear(PGresult *res)
  {
+     int i;
      PGresult_data *block;
+     PGEventResultDestroy evt;

      if (!res)
          return;

+     for(i=0; i < res->nEvents; i++)
+     {
+         evt.result = res;
+         (void)res->events[i].proc(PGEVT_RESULTDESTROY, &evt);
+     }
+
+     if(res->events)
+     {
+         free(res->events);
+         res->events = NULL;
+         res->nEvents = 0;
+     }
+
      /* Free all the subsidiary blocks */
      while ((block = res->curBlock) != NULL)
      {
          res->curBlock = block->next;
          free(block);
      }
***************
*** 1190,1202 ****
   *      memory).
   */

  PGresult *
  PQgetResult(PGconn *conn)
  {
!     PGresult   *res;

      if (!conn)
          return NULL;

      /* Parse any available data, if our state permits. */
      parseInput(conn);
--- 1482,1494 ----
   *      memory).
   */

  PGresult *
  PQgetResult(PGconn *conn)
  {
!     PGresult   *res=NULL;

      if (!conn)
          return NULL;

      /* Parse any available data, if our state permits. */
      parseInput(conn);
***************
*** 1265,1276 ****
--- 1557,1593 ----
                                libpq_gettext("unexpected asyncStatus: %d\n"),
                                (int) conn->asyncStatus);
              res = PQmakeEmptyPGresult(conn, PGRES_FATAL_ERROR);
              break;
      }

+     if(res && (res->resultStatus == PGRES_COMMAND_OK ||
+          res->resultStatus == PGRES_TUPLES_OK ||
+          res->resultStatus == PGRES_EMPTY_QUERY))
+     {
+         int i;
+         PGEventResultCreate evt;
+
+         for(i=0; i < res->nEvents; i++)
+         {
+             evt.conn = conn;
+             evt.result = res;
+
+             if(!res->events[i].proc(PGEVT_RESULTCREATE, &evt))
+             {
+                 char msg[256];
+                 sprintf(msg,
+                     "PGEventProc %p failed during PGEVT_RESULTCREATE event",
+                     res->events[i].proc);
+                 pqSetResultError(res, msg);
+                 res->resultStatus = PGRES_FATAL_ERROR;
+                 break;
+             }
+         }
+     }
+
      return res;
  }


  /*
   * PQexec
Index: src/interfaces/libpq/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
*** src/interfaces/libpq/fe-misc.c    1 Jan 2008 19:46:00 -0000    1.133
--- src/interfaces/libpq/fe-misc.c    20 May 2008 04:18:09 -0000
***************
*** 1152,1157 ****
--- 1152,1158 ----
      }

      return dgettext("libpq", msgid);
  }

  #endif   /* ENABLE_NLS */
+
Index: src/interfaces/libpq/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
*** src/interfaces/libpq/libpq-fe.h    19 Mar 2008 00:39:33 -0000    1.142
--- src/interfaces/libpq/libpq-fe.h    20 May 2008 04:18:09 -0000
***************
*** 25,36 ****
--- 25,45 ----
  /*
   * postgres_ext.h defines the backend's externally visible types,
   * such as Oid.
   */
  #include "postgres_ext.h"

+ /* -----------------------
+  * Options for PQcopyResult
+  */
+
+ #define PG_COPYRES_NO_TUPLES      0x01
+ #define PG_COPYRES_USE_ATTRS      0x02
+ #define PG_COPYRES_NO_EVENTS      0x04
+ #define PG_COPYRES_NO_NOTICEHOOKS 0x08
+
  /* Application-visible enum types */

  typedef enum
  {
      /*
       * Although it is okay to add to this list, values which become unused
***************
*** 190,201 ****
--- 199,225 ----
          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;
+
+ /* ----------------
   * Exported functions of libpq
   * ----------------
   */

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

***************
*** 434,445 ****
--- 458,488 ----
   * 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);

+ extern PGresult *
+ PQcopyResult(const PGresult *src, int numAttributes,
+     PGresAttDesc *attDescs, int options);
+
+ 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,
Index: src/interfaces/libpq/libpq-int.h
===================================================================
RCS file: /projects/cvsroot/pgsql/src/interfaces/libpq/libpq-int.h,v
retrieving revision 1.130
diff -C6 -r1.130 libpq-int.h
*** src/interfaces/libpq/libpq-int.h    16 May 2008 18:30:53 -0000    1.130
--- src/interfaces/libpq/libpq-int.h    20 May 2008 04:18:09 -0000
***************
*** 19,30 ****
--- 19,31 ----

  #ifndef LIBPQ_INT_H
  #define LIBPQ_INT_H

  /* We assume libpq-fe.h has already been included. */
  #include "postgres_fe.h"
+ #include "libpq-events.h"

  #include <time.h>
  #include <sys/types.h>
  #ifndef WIN32
  #include <sys/time.h>
  #endif
***************
*** 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;

--- 98,109 ----
***************
*** 159,170 ****
--- 147,167 ----
      PQnoticeReceiver noticeRec; /* notice message receiver */
      void       *noticeRecArg;
      PQnoticeProcessor noticeProc;        /* notice message processor */
      void       *noticeProcArg;
  } PGNoticeHooks;

+ typedef struct
+ {
+     /* pointer supplied by user */
+     void *passThrough;
+     /* state data, optionally generated by event proc */
+     void *data;
+     PGEventProc proc;
+ } PGEvent;
+
  struct pg_result
  {
      int            ntups;
      int            numAttributes;
      PGresAttDesc *attDescs;
      PGresAttValue **tuples;        /* each PGresTuple is an array of
***************
*** 181,192 ****
--- 178,193 ----
       * 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 */

+     /* registered events, copied from conn */
+     int nEvents;
+     PGEvent *events;
+
      /*
       * 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.
       */
      char       *errMsg;            /* error message, or NULL if no error */
***************
*** 300,311 ****
--- 301,317 ----
      /* Optional file to write trace info to */
      FILE       *Pfdebug;

      /* Callback procedures for notice message processing */
      PGNoticeHooks noticeHooks;

+     /* registered events via PQregisterEventProc */
+     int nEvents;
+     int eventArrSize;
+     PGEvent *events;
+
      /* Status indicators */
      ConnStatusType status;
      PGAsyncStatusType asyncStatus;
      PGTransactionStatusType xactStatus; /* never changes to ACTIVE */
      PGQueryClass queryclass;
      char       *last_query;        /* last SQL command, or NULL if unknown */

Вложения

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

Предыдущее
От: Andrew Chernow
Дата:
Сообщение: Re: libpq object hooks (libpq events)
Следующее
От: Bryce Nesbitt
Дата:
Сообщение: Patch for psql 8.0, 8.1 and 8.2 backwards compatibility