Re: [PATCHES] libpq events patch (with sgml docs) - Mailing list pgsql-hackers

From Andrew Chernow
Subject Re: [PATCHES] libpq events patch (with sgml docs)
Date
Msg-id 48C19759.90402@esilo.com
Whole thread Raw
In response to Re: [PATCHES] libpq events patch (with sgml docs)  (Alvaro Herrera <alvherre@commandprompt.com>)
Responses Re: [PATCHES] libpq events patch (with sgml docs)
List pgsql-hackers
Alvaro Herrera wrote:
>
> Doh!  Sorry, you're right.  In that case, you're missing a NULL result
> check from strdup() in dupEvents() ;-)
>

Missed that one.  Good catch :)  Update attached.

--
Andrew Chernow
eSilo, LLC
every bit counts
http://www.esilo.com/
Index: doc/src/sgml/libpq.sgml
===================================================================
RCS file: /projects/cvsroot/pgsql/doc/src/sgml/libpq.sgml,v
retrieving revision 1.260
diff -C6 -r1.260 libpq.sgml
*** doc/src/sgml/libpq.sgml    27 Jun 2008 02:44:31 -0000    1.260
--- doc/src/sgml/libpq.sgml    5 Sep 2008 20:25:29 -0000
***************
*** 2092,2103 ****
--- 2092,2222 ----
          Note that <function>PQclear</function> should eventually be called
          on the object, just as with a <structname>PGresult</structname>
          returned by <application>libpq</application> itself.
         </para>
        </listitem>
       </varlistentry>
+
+      <varlistentry>
+       <term>
+        <function>PQcopyResult</function>
+        <indexterm>
+         <primary>PQcopyResult</primary>
+        </indexterm>
+       </term>
+
+       <listitem>
+        <para>
+         Makes a copy of a <structname>PGresult</structname> object.
+         <synopsis>
+          PGresult *PQcopyResult(const PGresult *src, int flags);
+         </synopsis>
+        </para>
+
+        <para>
+         The returned result is always put into <literal>PGRES_TUPLES_OK</literal> status.
+         It is not linked to the source result in any way and
+         <function>PQclear</function> must be called when the result is no longer needed.
+         If the function fails, NULL is returned.
+        </para>
+
+        <para>
+         Optionally, the <parameter>flags</parameter> argument can be used to copy
+         more parts of the result.  <literal>PG_COPYRES_ATTRS</literal> will copy the
+         source result's attributes.  <literal>PG_COPYRES_TUPLES</literal> will copy
+         the source result's tuples.  This implies copying the attrs, being how the
+         attrs are needed by the tuples.  <literal>PG_COPYRES_EVENTS</literal> will
+         copy the source result's events.  <literal>PG_COPYRES_NOTICEHOOKS</literal>
+         will copy the source result's notify hooks.
+        </para>
+       </listitem>
+      </varlistentry>
+
+      <varlistentry>
+       <term>
+        <function>PQsetResultAttrs</function>
+        <indexterm>
+         <primary>PQsetResultAttrs</primary>
+        </indexterm>
+       </term>
+
+       <listitem>
+        <para>
+         Sets the attributes of a <structname>PGresult</structname> object.
+         <synopsis>
+          int PQsetResultAttrs(PGresult *res, int numAttributes, PGresAttDesc *attDescs);
+         </synopsis>
+        </para>
+
+        <para>
+         The provided <parameter>attDescs</parameter> are copied into the result, thus the
+         <parameter>attDescs</parameter> are no longer needed by <parameter>res</parameter> after
+         the function returns.  If the <parameter>attDescs</parameter> are NULL or
+         <parameter>numAttributes</parameter> is less than one, the request is ignored and
+         the function succeeds.  If <parameter>res</parameter> already contains attributes,
+         the function will fail.  If the function fails, the return value is zero.  If the
+         function succeeds, the return value is non-zero.
+        </para>
+       </listitem>
+      </varlistentry>
+
+      <varlistentry>
+       <term>
+        <function>PQsetvalue</function>
+        <indexterm>
+         <primary>PQsetvalue</primary>
+        </indexterm>
+       </term>
+
+       <listitem>
+        <para>
+         Sets a tuple field value of a <structname>PGresult</structname> object.
+         <synopsis>
+          int PQsetvalue(PGresult *res, int tup_num, int field_num, char *value, int len);
+         </synopsis>
+        </para>
+
+        <para>
+         The function will automatically grow the result's internal tuples array as needed.
+         The <parameter>tup_num</parameter> argument must be less than or equal to
+         <function>PQntuples</function>, meaning this function can only grow the
+         tuples array in order one tuple at a time.  Although, any field from any
+         existing tuple can be modified in any order.  If a value at <parameter>field_num</parameter>
+         already eixsts, it will be overwritten.  If <parameter>len</parameter> is <literal>-1</literal> or
+         <parameter>value</parameter> is <literal>NULL</literal>, the field value will
+         be set to an SQL <literal>NULL</literal>.  The <parameter>value</parameter> is copied
+         into result's private storage, thus no longer needed by <parameter>res</parameter> after
+         the function returns.  If the function fails, the return value
+         is zero.  If the function succeeds, the return value is non-zero.
+        </para>
+       </listitem>
+      </varlistentry>
+
+      <varlistentry>
+       <term>
+        <function>PQresultAlloc</function>
+        <indexterm>
+         <primary>PQresultAlloc</primary>
+        </indexterm>
+       </term>
+
+       <listitem>
+        <para>
+         Allocate subsidiary storage for a <structname>PGresult</structname> object.
+         <synopsis>
+          void *PQresultAlloc(PGresult *res, size_t nBytes);
+         </synopsis>
+        </para>
+
+        <para>
+         Any memory allocated with this function, will be freed when
+         <parameter>res</parameter> is cleared.  If the function fails,
+         the return value is <literal>NULL</literal>.
+        </para>
+       </listitem>
+      </varlistentry>
      </variablelist>
     </para>
    </sect2>

    <sect2 id="libpq-exec-select-info">
     <title>Retrieving Query Result Information</title>
***************
*** 4558,4569 ****
--- 4677,5157 ----
      </listitem>
     </varlistentry>
    </variablelist>

   </sect1>

+  <sect1 id="libpq-events">
+   <title>Event System</title>
+
+   <para>
+    The event system is designed to notify registered event handlers
+    about particular libpq events; such as the creation or destruction
+    of <structname>PGconn</structname> and <structname>PGresult</structname>
+    objects.  One use case is that this allows applications to associate
+    their own data with a <structname>PGconn</structname> and
+    <structname>PGresult</structname>, that can retrieved via
+    <function>PQinstanceData</function> and <function>PQresultInstanceData</function>.
+    Basically, its like adding members to the opaque conn or result
+    structures at runtime.
+   </para>
+
+   <para>
+    Below is a list of event system concepts:
+    <itemizedlist mark='bullet'>
+     <listitem>
+      <para>
+       <literal>eventId</literal> - Identifies which event was fired by libpq.  All
+       events begin with <literal>PGEVT_</literal>.
+      </para>
+     </listitem>
+     <listitem>
+      <para>
+       <literal>eventInfo</literal> - Every <literal>eventId</literal> has a corresponding
+       event info structure, that contains data to aid in processing the event.
+      </para>
+     </listitem>
+     <listitem>
+      <para>
+       <literal>instanceData</literal> - The instance data is memory associated with a
+       <structname>PGconn</structname> or <structname>PGresult</structname>.  The data is
+       created and destroyed along with with the connection or result.
+       Typically, instance data is created in response to
+       a <literal>PGEVT_REGISTER</literal> event, but can be created at any time or
+       not at all.  Management of instance data is done using the <function>PQinstanceData</function>,
+       <function>PQsetInstanceData</function>, <function>PQresultInstanceData</function> and
+       <function>PQsetResultInstanceData</function> functions.
+      </para>
+     </listitem>
+    </itemizedlist>
+   </para>
+
+
+   <sect2 id="libpq-events-types">
+    <title>Event Types</title>
+     <para>
+      <variablelist>
+       <varlistentry>
+        <term><literal>PGEVT_REGISTER</literal></term>
+        <listitem>
+         <para>
+          The register event occurs when <function>PQregisterEventProc</function> is
+          called.  It is the ideal time to initialize any <literal>instanceData</literal>
+          an event procedure may need.  Only one register event will be fired per connection.
+          If the event procedure fails, the registration is aborted.
+
+         <synopsis>
+ typedef struct
+ {
+     const PGconn *conn;
+ } PGEventRegister;</synopsis>
+
+          When a <literal>PGEVT_REGISTER</literal> event is received, the <parameter>evtInfo</parameter>
+          pointer should be casted to a <structname>PGEventRegister*</structname>.  This structure
+          contains a <structname>PGconn</structname> that should be in the <literal>CONNECTION_OK</literal>
+          status; guaranteed if one calls <function>PQregisterEventProc</function> right after obtaining
+          a good <structname>PGconn</structname>.  The connection can be used to initialize any
+          application specific needs: like allocating structures as the event <literal>instanceData</literal>
+          or executing SQL statements.
+         </para>
+        </listitem>
+       </varlistentry>
+
+       <varlistentry>
+        <term><literal>PGEVT_CONNRESET</literal></term>
+        <listitem>
+         <para>
+          The connection reset event is fired in response to a <function>PQreset</function> or
<function>PQresetPoll</function>.
+          In both cases, the event is only fired if the reset was successful.  If the event procedure fails,
+          the entire connection reset will fail; the <structname>PGconn</structname> is put into
+          <literal>CONNECTION_BAD</literal> status and <function>PQresetPoll</function> will return
+          <literal>PGRES_POLLING_FAILED</literal>.
+         <synopsis>
+ typedef struct
+ {
+     const PGconn *conn;
+ } PGEventReset;</synopsis>
+
+          When a <literal>PGEVT_CONNRESET</literal> event is received, the <parameter>evtInfo</parameter>
+          pointer should be casted to a <structname>PGEventReset*</structname>.  Although the contained
+          <structname>PGconn</structname> was just reset, all event data remains unchanged.  This event
+          should be used to reset/reload/requery any assocaited <literal>instanceData</literal>.
+         </para>
+        </listitem>
+       </varlistentry>
+
+       <varlistentry>
+        <term><literal>PGEVT_CONNDESTROY</literal></term>
+        <listitem>
+         <para>
+          The connection destroy event is fired in response to a <function>PQfinish</function>.
+          It is the event procedure's responsibility to properly cleanup its event data as libpq
+          has no ability to manage this memory.  Failure to properly cleanup will lead to memory
+          leaks.
+         <synopsis>
+ typedef struct
+ {
+     const PGconn *conn;
+ } PGEventConnDestroy;</synopsis>
+
+          When a <literal>PGEVT_CONNDESTROY</literal> event is received, the <parameter>evtInfo</parameter>
+          pointer should be casted to a <structname>PGEventConnDestroy*</structname>.  This event is fired
+          prior to <function>PQfinish</function> performing any cleanup.  The return value of the event
+          procedure is ignored since there is no way of indicating a failure from <function>PQfinish</function>.
+          Also, an event procedure failure should not abort the process of cleaning up unwanted memory.
+         </para>
+        </listitem>
+       </varlistentry>
+
+       <varlistentry>
+        <term><literal>PGEVT_RESULTCREATE</literal></term>
+        <listitem>
+         <para>
+          The result create event is fired in response to any exec function that generates a result,
+          including <function>PQgetResult</function>.  This event will only be fired after the result
+          has been created successfully, with a result status of either <literal>PGRES_COMMAND_OK</literal>,
+          <literal>PGRES_TUPLES_OK</literal> or <literal>PGRES_EMPTY_QUERY</literal>.
+         <synopsis>
+ typedef struct
+ {
+     const PGconn *conn;
+     const PGresult *result;
+ } PGEventResultCreate;</synopsis>
+
+          When a <literal>PGEVT_RESULTCREATE</literal> event is received, the <parameter>evtInfo</parameter>
+          pointer should be casted to a <structname>PGEventResultCreate*</structname>.  The
<parameter>conn</parameter>
+          was the connection used to generate the result.  This is the ideal place to initialize
+          any <literal>instanceData</literal> that needs to be associated with the result.  If the event procedure
fails,
+          the result will be cleared and the failure will be propagated.  Do not attempt to
+          clear the result object contained in the <structname>PGEventResultCreate</structname> structure.
+         </para>
+        </listitem>
+       </varlistentry>
+
+       <varlistentry>
+        <term><literal>PGEVT_RESULTCOPY</literal></term>
+        <listitem>
+         <para>
+          The result copy event is fired in response to <function>PQcopyResult</function>.
+          This event will only be fired after the copy is complete.
+         <synopsis>
+ typedef struct
+ {
+     const PGresult *src;
+     PGresult *dest;
+ } PGEventResultCopy;</synopsis>
+
+          When a <literal>PGEVT_RESULTCOPY</literal> event is received, the <parameter>evtInfo</parameter>
+          pointer should be casted to a <structname>PGEventResultCopy*</structname>.  The <parameter>src</parameter>
+          result is what is being copied while the <parameter>dest</parameter> result is the copy destination.
+          This event can be used to provide a deep copy of <literal>instanceData</literal>, since
+          <literal>PQcopyResult</literal> cannot do that.  If the event procedure fails, the entire copy operation
+          will fail and the <parameter>dest</parameter> result will be cleared.
+         </para>
+        </listitem>
+       </varlistentry>
+
+       <varlistentry>
+        <term><literal>PGEVT_RESULTDESTROY</literal></term>
+        <listitem>
+         <para>
+          The result destroy event is fired in response to a <function>PQclear</function>.
+          It is the event procedure's responsibility to properly cleanup its event data as libpq
+          has no ability to manage this memory.  Failure to properly cleanup will lead to memory
+          leaks.
+         <synopsis>
+ typedef struct
+ {
+     const PGresult *result;
+ } PGEventResultDestroy;</synopsis>
+
+          When a <literal>PGEVT_RESULTDESTROY</literal> event is received, the <parameter>evtInfo</parameter>
+          pointer should be casted to a <structname>PGEventResultDestroy*</structname>.  This event is fired
+          prior to <function>PQclear</function> performing any cleanup.  The return value of the event
+          procedure is ignored since there is no way of indicating a failure from <function>PQclear</function>.
+          Also, an event procedure failure should not abort the process of cleaning up unwanted memory.
+         </para>
+        </listitem>
+       </varlistentry>
+      </variablelist>
+    </para>
+   </sect2>
+
+   <sect2 id="libpq-events-proc">
+    <title>Event Callback Procedure</title>
+    <variablelist>
+     <varlistentry>
+      <term>
+       <function>PGEventProc</function>
+       <indexterm>
+        <primary>PGEventProc</primary>
+       </indexterm>
+      </term>
+
+      <listitem>
+       <para>
+        User callback function that receives events from libpq. The address of the event
+        proc is also used to lookup the <literal>instanceData</literal>
+        associated with a <structname>PGconn</structname> or <structname>PGresult</structname>.
+        <synopsis>typedef int (*PGEventProc)(PGEventId evtId, void *evtInfo, void *passThrough);</synopsis>
+       </para>
+
+       <para>
+        The <parameter>evtId</parameter> inidcates which <literal>PGEVT_</literal> event occurred.
+        The <parameter>evtInfo</parameter> must be casted to the appropriate structure: ex. PGEVT_REGISTER
+        means cast evtInfo to a PGEventRegister pointer.  The <parameter>passThrough</parameter> is the
+        pointer provided to the <function>PQregisterEventProc</function>, which can be NULL.  The function
+        should return a non-zero value if it succeeds and zero if it fails.
+       </para>
+      </listitem>
+     </varlistentry>
+    </variablelist>
+   </sect2>
+
+   <sect2 id="libpq-events-funcs">
+    <title>Event Functions</title>
+     <variablelist>
+     <varlistentry>
+      <term>
+       <function>PQregisterEventProc</function>
+       <indexterm>
+        <primary>PQregisterEventProc</primary>
+       </indexterm>
+      </term>
+
+      <listitem>
+       <para>
+        Registers an event callback procedure with libpq.
+        <synopsis>int PQregisterEventProc(PGconn *conn, PGEventProc proc, const char *name, void
*passThrough);</synopsis>
+       </para>
+
+       <para>
+        Registration must be called once on every PGconn you want to receive events about.
+        There is no limit on the number of event procedures that can be registered with a connection.
+        The function returns a non-zero value if it succeeds and zero if it fails.
+       </para>
+
+       <para>
+        The <parameter>proc</parameter> argument will be called when a libpq event is fired.  Its
+        memory address is also used to lookup <literal>instanceData</literal>.
+        The <parameter>name</parameter> argument is used to contruct error messages to aid
+        in debugging.  This value cannot be NULL or a zero-length string.
+        The <parameter>passThrough</parameter> pointer is passed to the <parameter>proc</parameter> whenever
+        an event occurs. This argument can be NULL.
+       </para>
+      </listitem>
+     </varlistentry>
+
+     <varlistentry>
+      <term>
+       <function>PQinstanceData</function>
+       <indexterm>
+        <primary>PQinstanceData</primary>
+       </indexterm>
+      </term>
+      <listitem>
+       <para>
+        Returns the conn's instanceData associated with proc.
+        <synopsis>void *PQinstanceData(const PGconn *conn, PGEventProc proc);</synopsis>
+       </para>
+      </listitem>
+     </varlistentry>
+
+     <varlistentry>
+      <term>
+       <function>PQsetInstanceData</function>
+       <indexterm>
+        <primary>PQsetInstanceData</primary>
+       </indexterm>
+      </term>
+      <listitem>
+       <para>
+        Sets the conn's instanceData for proc to data.  This reutrns non-zero for
+        succees and zero for failure.
+        <synopsis>int PQsetInstanceData(PGconn *conn, PGEventProc proc, void *data);</synopsis>
+       </para>
+      </listitem>
+     </varlistentry>
+
+     <varlistentry>
+      <term>
+       <function>PQresultInstanceData</function>
+       <indexterm>
+        <primary>PQresultInstanceData</primary>
+       </indexterm>
+      </term>
+      <listitem>
+       <para>
+        Returns the result's instanceData associated by proc.
+        <synopsis>void *PQresultInstanceData(const PGresult *res, PGEventProc proc);</synopsis>
+       </para>
+      </listitem>
+     </varlistentry>
+
+     <varlistentry>
+      <term>
+       <function>PQresultSetInstanceData</function>
+       <indexterm>
+        <primary>PQresultSetInstanceData</primary>
+       </indexterm>
+      </term>
+      <listitem>
+       <para>
+        Sets the result's instanceData for proc to data.  This reutrns non-zero for
+        succees and zero for failure.
+        <synopsis>int PQresultSetInstanceData(PGresult *res, PGEventProc proc, void *data);</synopsis>
+       </para>
+      </listitem>
+     </varlistentry>
+    </variablelist>
+   </sect2>
+
+   <sect2 id="libpq-events-example">
+    <title>Event Example</title>
+    <programlisting>
+ /* required header for libpq events (note: includes libpq-fe.h)*/
+ #include <libpq-events.h>
+
+ /* The instanceData */
+ typedef struct
+ {
+     int n;
+     char *str;
+ } mydata;
+
+ /* PGEventProc */
+ int myEventProc(PGEventId evtId, void *evtInfo, void *passThrough);
+
+ int main(void)
+ {
+     mydata *data;
+     PGresult *res;
+     PGconn *conn = PQconnectdb("dbname = postgres");
+
+     if (PQstatus(conn) != CONNECTION_OK)
+     {
+         fprintf(stderr, "Connection to database failed: %s",
+             PQerrorMessage(conn));
+         PQfinish(conn);
+         return 1;
+     }
+
+     /* called once on any connection that should receive events.
+      * Sends a PGEVT_REGISTER to myEventProc.
+      */
+     if (!PQregisterEventProc(conn, myEventProc, "mydata_proc", NULL))
+     {
+         fprintf(stderr, "Cannot register PGEventProc\n");
+         PQfinish(conn);
+         return 1;
+     }
+
+     /* conn instanceData is available */
+     data = PQinstanceData(conn, myEventProc);
+
+     /* Sends a PGEVT_RESULTCREATE to myEventProc */
+     res = PQexec(conn, "SELECT 1 + 1");
+
+     /* result instanceData is available */
+     data = PQresultInstanceData(res, myEventProc);
+
+     /* If PG_COPYRES_EVENTS is used, sends a PGEVT_RESULTCOPY to myEventProc */
+     res_copy = PQcopyResult(res, PG_COPYRES_TUPLES | PG_COPYRES_EVENTS);
+
+     /* result instanceData is available if PG_COPYRES_EVENTS was
+      * used during the PQcopyResult call.
+      */
+     data = PQresultInstanceData(res_copy, myEventProc);
+
+     /* Both clears send a PGEVT_RESULTDESTORY to myEventProc */
+     PQclear(res);
+     PQclear(res_copy);
+
+     /* Sends a PGEVT_CONNDESTROY to myEventProc */
+     PQfinish(conn);
+
+     return 0;
+ }
+
+ int myEventProc(PGEventId evtId, void *evtInfo, void *passThrough)
+ {
+     switch (evtId)
+     {
+         case PGEVT_REGISTER:
+         {
+             PGEventRegister *e = (PGEventRegister *)evtInfo;
+             mydata *data = get_mydata(e->conn);
+
+             /* associate app specific data with connection */
+             PQsetInstanceData(e->conn, myEventProc, data);
+             break;
+         }
+
+         case PGEVT_CONNRESET:
+         {
+             PGEventConnReset *e = (PGEventConnReset *)evtInfo;
+             mydata *data = PQinstanceData(e->conn, myEventProc);
+             if (data)
+               memset(data, 0, sizeof(mydata));
+             break;
+         }
+
+         case PGEVT_CONNDESTROY:
+         {
+             PGEventConnDestroy *e = (PGEventConnDestroy *)evtInfo;
+             mydata *data = PQinstanceData(e->conn, myEventProc);
+
+             /* free instance data because the conn is being destroyed */
+             if (data)
+               free_mydata(data);
+             break;
+         }
+
+         case PGEVT_RESULTCREATE:
+         {
+             PGEventResultCreate *e = (PGEventResultCreate *)evtInfo;
+             mydata *conn_data = PQinstanceData(e->conn, myEventProc);
+             mydata *res_data = dup_mydata(conn_data);
+
+             /* associate app specific data with result (copy it from conn) */
+             PQsetResultInstanceData(e->result, myEventProc, res_data);
+             break;
+         }
+
+         case PGEVT_RESULTCOPY:
+         {
+             PGEventResultCopy *e = (PGEventResultCopy *)evtInfo;
+             mydata *src_data = PQresultInstanceData(e->src, myEventProc);
+             mydata *dest_data = dup_mydata(src_data);
+
+             /* associate app specific data with result (copy it from a result) */
+             PQsetResultInstanceData(e->dest, myEventProc, dest_data);
+             break;
+         }
+
+         case PGEVT_RESULTDESTROY:
+         {
+             PGEventResultDestroy *e = (PGEventResultDestroy *)evtInfo;
+             mydata *data = PQresultInstanceData(e->result, myEventProc);
+
+             /* free instance data because the result is being destroyed */
+             if( data)
+               free_mydata(data);
+             break;
+         }
+
+         /* unknown event id, just return TRUE. */
+         default:
+             break;
+     }
+
+     return TRUE; /* event processing succeeded */
+ }  </programlisting>
+   </sect2>
+  </sect1>
+
   <sect1 id="libpq-misc">
    <title>Miscellaneous Functions</title>

    <para>
     As always, there are some functions that just don't fit anywhere.
    </para>
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    5 Sep 2008 20:25:30 -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,123 ****

  $(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-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)'

  uninstall: uninstall-lib
!     rm -f '$(DESTDIR)$(includedir)/libpq-fe.h' '$(DESTDIR)$(includedir_internal)/libpq-int.h'
'$(DESTDIR)$(includedir_internal)/pqexpbuffer.h''$(DESTDIR)$(datadir)/pg_service.conf.sample' 

  clean distclean: clean-lib
      rm -f $(OBJS) pg_config_paths.h crypt.c getaddrinfo.c inet_aton.c noblock.c open.c pgstrcasecmp.c snprintf.c
strerror.cstrlcpy.c thread.c md5.c ip.c encnames.c wchar.c win32error.c pgsleep.c pthread.h libpq.rc 
  # Might be left over from a Win32 client-only build
      rm -f pg_config_paths.h

--- 103,124 ----

  $(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)'

  uninstall: uninstall-lib
!     rm -f '$(DESTDIR)$(includedir)/libpq-fe.h' '$(DESTDIR)$(includedir)/libpq-events.h'
'$(DESTDIR)$(includedir_internal)/libpq-int.h''$(DESTDIR)$(includedir_internal)/pqexpbuffer.h'
'$(DESTDIR)$(datadir)/pg_service.conf.sample'

  clean distclean: clean-lib
      rm -f $(OBJS) pg_config_paths.h crypt.c getaddrinfo.c inet_aton.c noblock.c open.c pgstrcasecmp.c snprintf.c
strerror.cstrlcpy.c thread.c md5.c ip.c encnames.c wchar.c win32error.c pgsleep.c pthread.h libpq.rc 
  # Might be left over from a Win32 client-only build
      rm -f pg_config_paths.h

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    5 Sep 2008 20:25:30 -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
+ PQcopyResult              142
+ PQsetResultAttrs          143
+ PQsetvalue                144
+ PQresultAlloc             145
+ PQregisterEventProc       146
+ PQinstanceData            147
+ PQsetInstanceData         148
+ PQresultInstanceData      149
+ PQresultSetInstanceData   150
\ 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.359
diff -C6 -r1.359 fe-connect.c
*** src/interfaces/libpq/fe-connect.c    29 May 2008 22:02:44 -0000    1.359
--- src/interfaces/libpq/fe-connect.c    5 Sep 2008 20:25:30 -0000
***************
*** 1971,1982 ****
--- 1971,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;
+     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, conn->events[i].passThrough);
+         free(conn->events[i].name);
+     }
+
+     /* 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);
***************
*** 2152,2165 ****
  PQreset(PGconn *conn)
  {
      if (conn)
      {
          closePGconn(conn);

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


  /*
   * PQresetStart:
--- 2171,2201 ----
  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->events[i].passThrough))
!                 {
!                     conn->status = CONNECTION_BAD;
!                     printfPQExpBuffer(&conn->errorMessage,
!                         libpq_gettext("PGEventProc \"%s\" failed during PGEVT_CONNRESET event\n"),
!                         conn->events[i].name);
!                     break;
!                 }
!             }
!         }
      }
  }


  /*
   * PQresetStart:
***************
*** 2187,2199 ****
   * 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.
--- 2223,2259 ----
   * 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->events[i].passThrough))
!                 {
!                     conn->status = CONNECTION_BAD;
!                     printfPQExpBuffer(&conn->errorMessage,
!                         libpq_gettext("PGEventProc \"%s\" failed during PGEVT_CONNRESET event\n"),
!                         conn->events[i].name);
!                     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.196
diff -C6 -r1.196 fe-exec.c
*** src/interfaces/libpq/fe-exec.c    23 Jun 2008 21:10:49 -0000    1.196
--- src/interfaces/libpq/fe-exec.c    5 Sep 2008 20:25:30 -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 ****
  #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)


  /*
   * 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.
   */

  PGresult *
  PQmakeEmptyPGresult(PGconn *conn, ExecStatusType status)
  {
      PGresult   *result;
--- 121,177 ----
  #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 and deep copy name */
+     for (i = 0; i < count; i++)
+     {
+         newEvents[i].name = strdup(newEvents[i].name);
+         if (!newEvents[i].name)
+         {
+             while (--i >= 0)
+                 free(newEvents[i].name);
+             free(newEvents);
+             return NULL;
+         }
+
+         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.  If conn is
!  * not NULL, the PGevents array will be copied from the PGconn to the
!  * created PGresult.
   */

  PGresult *
  PQmakeEmptyPGresult(PGconn *conn, ExecStatusType status)
  {
      PGresult   *result;
***************
*** 157,175 ****
--- 193,226 ----
      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:
***************
*** 192,203 ****
--- 243,501 ----
          result->client_encoding = PG_SQL_ASCII;
      }

      return result;
  }

+ /* PQsetResultAttrs
+  * Set the attributes for a given result.  This function fails if there are
+  * already attributes contained in the provided result.  The call is
+  * ignored if numAttributes is is zero or attDescs is NULL.  If the
+  * function fails, it returns zero.  If the function succeeds, it
+  * returns a non-zero value.
+  */
+ int
+ PQsetResultAttrs(PGresult *res, int numAttributes, PGresAttDesc *attDescs)
+ {
+     int i;
+
+     /* If attrs already exist, they cannot be overwritten. */
+     if (!res || res->numAttributes > 0)
+         return FALSE;
+
+     /* ignore request */
+     if (numAttributes <= 0 || !attDescs)
+         return TRUE;
+
+     res->attDescs = (PGresAttDesc *) PQresultAlloc(res,
+         numAttributes * sizeof(PGresAttDesc));
+
+     if (!res->attDescs)
+         return FALSE;
+
+     res->numAttributes = numAttributes;
+     memcpy(res->attDescs, attDescs,
+         numAttributes * sizeof(PGresAttDesc));
+
+     /* resultalloc the attribute names.  The above memcpy has the attr
+      * names pointing at the callers provided attDescs memory.
+      */
+     res->binary = 1;
+     for (i = 0; i < res->numAttributes; i++)
+     {
+         if (res->attDescs[i].name)
+             res->attDescs[i].name = pqResultStrdup(res, res->attDescs[i].name);
+         else
+             res->attDescs[i].name = res->null_field;
+
+         if (!res->attDescs[i].name)
+             return FALSE;
+
+         /* 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 TRUE;
+ }
+
+ /*
+  * PQcopyResult
+  * Returns a deep copy of the provided 'src' PGresult, which cannot be NULL.
+  * The 'flags' argument controls which portions of the result will or will
+  * NOT be copied.  The created result is always put into the
+  * PGRES_TUPLES_OK status.  The source result error message is not copied,
+  * although cmdStatus is.
+  *
+  * To set custom attributes, see PQsetResultAttrs.  That function requires
+  * that there are no attrs contained in the result, so to use that
+  * function you cannot use the PG_COPYRES_ATTRS or PG_COPYRES_TUPLES
+  * options with this function.
+  *
+  * Options:
+  *   PG_COPYRES_ATTRS - Copy the source result's attributes
+  *
+  *   PG_COPYRES_TUPLES - Copy the source result's tuples.  This implies
+  *   copying the attrs, being how the attrs are needed by the tuples.
+  *
+  *   PG_COPYRES_EVENTS - Copy the source result's events.
+  *
+  *   PG_COPYRES_NOTICEHOOKS - Copy the source result's notice hooks.
+  */
+
+ PGresult *
+ PQcopyResult(const PGresult *src, int flags)
+ {
+     int i;
+     PGresult *dest;
+     PGEventResultCopy evt;
+
+     if (!src)
+         return NULL;
+
+     /* Automatically turn on attrs flags because you can't copy tuples
+      * without copying the attrs.  _TUPLES implies _ATTRS.
+      */
+     if (flags & PG_COPYRES_TUPLES)
+         flags |= PG_COPYRES_ATTRS;
+
+     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 (flags & PG_COPYRES_NOTICEHOOKS)
+         dest->noticeHooks = src->noticeHooks;
+
+     /* Wants attrs */
+     if ((flags & PG_COPYRES_ATTRS) &&
+          !PQsetResultAttrs(dest, src->numAttributes, src->attDescs))
+     {
+         PQclear(dest);
+         return NULL;
+     }
+
+     /* Wants to copy result tuples: use PQsetvalue(). */
+     if ((flags & PG_COPYRES_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 ((flags & PG_COPYRES_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, dest->events[i].passThrough))
+         {
+             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 * 2 : 128;
+         PGresAttValue **tups;
+
+         if (res->tuples)
+             tups = (PGresAttValue **) realloc(res->tuples, n * sizeof(PGresAttValue *));
+         else
+             tups = (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;
+
+         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;
+
+         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
***************
*** 349,365 ****
--- 647,679 ----
   * 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, res->events[i].passThrough);
+         free(res->events[i].name);
+     }
+
+     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);
      }
***************
*** 1192,1204 ****
   *      memory).
   */

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

      if (!conn)
          return NULL;

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

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

      if (!conn)
          return NULL;

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

+     if (res && res->nEvents > 0 &&
+         (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, res->events[i].passThrough))
+             {
+                 char msg[256];
+
+                 sprintf(msg,
+                     "PGEventProc \"%s\" failed during PGEVT_RESULTCREATE event",
+                     res->events[i].name);
+
+                 pqSetResultError(res, msg);
+                 res->resultStatus = PGRES_FATAL_ERROR;
+                 break;
+             }
+         }
+     }
+
      return res;
  }


  /*
   * PQexec
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    5 Sep 2008 20:25:30 -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_ATTRS          0x01
+ #define PG_COPYRES_TUPLES         0x02 /* Implies PG_COPYRES_ATTRS */
+ #define PG_COPYRES_EVENTS         0x04
+ #define PG_COPYRES_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,487 ----
   * 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 copy of a result */
+ extern PGresult *PQcopyResult(const PGresult *src, int flags);
+
+ /* Sets the attributes of a result, no attributes can already exist. */
+ extern int PQsetResultAttrs(PGresult *res, int numAttributes, PGresAttDesc *attDescs);
+
+ /* Allocate subsidiary storage for a PGresult. */
+ 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.131
diff -C6 -r1.131 libpq-int.h
*** src/interfaces/libpq/libpq-int.h    29 May 2008 22:02:44 -0000    1.131
--- src/interfaces/libpq/libpq-int.h    5 Sep 2008 20:25:30 -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,166 ----
      PQnoticeReceiver noticeRec; /* notice message receiver */
      void       *noticeRecArg;
      PQnoticeProcessor noticeProc;        /* notice message processor */
      void       *noticeProcArg;
  } PGNoticeHooks;

+ typedef struct
+ {
+     char       *name;    /* for error messages */
+     void       *passThrough; /* pointer supplied by user */
+     void       *data; /* state (instance) data, optionally generated by event proc */
+     PGEventProc proc; /* the function to call on events */
+ } PGEvent;
+
  struct pg_result
  {
      int            ntups;
      int            numAttributes;
      PGresAttDesc *attDescs;
      PGresAttValue **tuples;        /* each PGresTuple is an array of
***************
*** 181,192 ****
--- 177,192 ----
       * 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 ****
--- 300,316 ----
      /* 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 */

Attachment

pgsql-hackers by date:

Previous
From: Alvaro Herrera
Date:
Subject: Re: [PATCHES] libpq events patch (with sgml docs)
Next
From: Martijn van Oosterhout
Date:
Subject: Re: Need more reviewers!