Thread: Re: [PATCHES] libpq events patch (with sgml docs)

Re: [PATCHES] libpq events patch (with sgml docs)

From
Andrew Chernow
Date:
Alvaro Herrera wrote:
> Andrew Chernow escribió:
>
>> !                         printfPQExpBuffer(&conn->errorMessage,
>> !                             libpq_gettext("PGEventProc \"%s\" failed during PGEVT_CONNRESET event\n"),
>> !                             conn->events[i].name);
>> !                     else
>> !                         printfPQExpBuffer(&conn->errorMessage,
>> !                             libpq_gettext("PGEventProc \"addr:%p\" failed during PGEVT_CONNRESET event\n"),
>> !                             conn->events[i].proc);
>> !                     break;
>
> Please don't do this.  It creates extra unnecessary work for
> translators.  Better create a local var, assign either "name" or
> "addr:<value>" to it, and then use that in the message.
>
> (For the record, I'd prefer that the name is made mandatory.)
>

The name is now mandatory, no need to toggle the format string anymore.

--
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 19:36:39 -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 19:36:39 -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 19:36:39 -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 19:36:39 -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 19:36:39 -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,170 ----
  #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);
+         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 ****
--- 186,219 ----
      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 ****
--- 236,494 ----
          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 ****
--- 640,672 ----
   * 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);
--- 1499,1511 ----
   *      memory).
   */

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

      if (!conn)
          return NULL;

      /* Parse any available data, if our state permits. */
      parseInput(conn);
***************
*** 1267,1278 ****
--- 1574,1613 ----
                                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 19:36:39 -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 19:36:39 -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

Re: [PATCHES] libpq events patch (with sgml docs)

From
Alvaro Herrera
Date:
Andrew Chernow escribió:

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

Don't do this either.  We don't really need to know when the thing was
changed; the comment should just state what the function does.  I had
folded the last paragraph into the introductory one, but I think you
lost that part of my change.

> +     /* resultalloc the attribute names.  The above memcpy has the attr
> +      * names pointing at the callers provided attDescs memory.
> +      */

"resultalloc"?  Why not just "allocate"?

>    * PQclear -
>    *      free's the memory associated with a PGresult
>    */

I'd add a comment here stating why the event name is not deallocated;
otherwise it just looks like it's being leaked.


-- 
Alvaro Herrera                                http://www.CommandPrompt.com/
The PostgreSQL Company - Command Prompt, Inc.


Re: [PATCHES] libpq events patch (with sgml docs)

From
Andrew Chernow
Date:
Alvaro Herrera wrote:
> Andrew Chernow escribió:
>
>>    * 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.
>>    */
>
> Don't do this either.  We don't really need to know when the thing was
>

Changed that and made it one paragraph.

>> +     /* resultalloc the attribute names.  The above memcpy has the attr
>> +      * names pointing at the callers provided attDescs memory.
>> +      */
>
> "resultalloc"?  Why not just "allocate"?
>

Because resultalloc is what a PGresult uses to allocate its attribute
names (See line 513 of fe-protocol3.c).  Also, PQclear frees its memory
blocks, not individual items like attname.

>>    * PQclear -
>>    *      free's the memory associated with a PGresult
>>    */
>
> I'd add a comment here stating why the event name is not deallocated;
> otherwise it just looks like it's being leaked.
>
>

The event name is being deallocated.

--
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:03:03 -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:03:03 -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:03:03 -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:03:03 -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:03:04 -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,169 ----
  #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);
+         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 ****
--- 185,218 ----
      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 ****
--- 235,493 ----
          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 ****
--- 639,671 ----
   * 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);
--- 1498,1510 ----
   *      memory).
   */

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

      if (!conn)
          return NULL;

      /* Parse any available data, if our state permits. */
      parseInput(conn);
***************
*** 1267,1278 ****
--- 1573,1612 ----
                                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:03:04 -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:03:04 -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

Re: [PATCHES] libpq events patch (with sgml docs)

From
Alvaro Herrera
Date:
Andrew Chernow escribió:
> Alvaro Herrera wrote:
>> Andrew Chernow escribió:

>>>    * PQclear -
>>>    *      free's the memory associated with a PGresult
>>>    */
>>
>> I'd add a comment here stating why the event name is not deallocated;
>> otherwise it just looks like it's being leaked.
>
> The event name is being deallocated.

Doh!  Sorry, you're right.  In that case, you're missing a NULL result
check from strdup() in dupEvents() ;-)

-- 
Alvaro Herrera                                http://www.CommandPrompt.com/
The PostgreSQL Company - Command Prompt, Inc.


Re: [PATCHES] libpq events patch (with sgml docs)

From
Andrew Chernow
Date:
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

Re: [PATCHES] libpq events patch (with sgml docs)

From
Tom Lane
Date:
Andrew Chernow <ac@esilo.com> writes:
> Missed that one.  Good catch :)  Update attached.

Looking at this now.  Question: why does PQgetResult invoke the
RESULTCREATE event only for non-error results?  This seems a rather
odd asymmetry, particularly in view of the fact that a RESULTDESTROY
event will occur for error results.  And surely we do not need to
micro-optimize error cases for speed.
        regards, tom lane


Re: [PATCHES] libpq events patch (with sgml docs)

From
Andrew Chernow
Date:
Tom Lane wrote:
> Andrew Chernow <ac@esilo.com> writes:
>> Missed that one.  Good catch :)  Update attached.
> 
> Looking at this now.  Question: why does PQgetResult invoke the
> RESULTCREATE event only for non-error results?  

It didn't seem useful to have the eventproc implementation allocate its private 
storage (or do whatever prep work it does), just to have it PQclear'd once the 
user gets the dead result back.  I guess we figured an error result typically 
has a short life-span, not very useful outside of indicating an error.

Is there a use case you think requires the opposite behavior?

> odd asymmetry, particularly in view of the fact that a RESULTDESTROY
> event will occur for error results.  And surely we do not need to
> micro-optimize error cases for speed.
> 

It is odd.  Actually, it looks like a bug to me.  PQgetResult is the behavior we 
were after, don't trigger the event if the creation failed.  Same thing occurs 
during a conn reset.  Looks like PQclear needs to check resultStatus before it 
triggers RESULTDESTROY events.

But before I fix this and put a patch up, please let me know if you prefer the 
opposite behavior ... trigger events on success or failure (including connreset).

-- 
Andrew Chernow
eSilo, LLC
every bit counts
http://www.esilo.com/


Re: [PATCHES] libpq events patch (with sgml docs)

From
Tom Lane
Date:
Andrew Chernow <ac@esilo.com> writes:
> Tom Lane wrote:
>> Looking at this now.  Question: why does PQgetResult invoke the
>> RESULTCREATE event only for non-error results?  

> It didn't seem useful to have the eventproc implementation allocate its private 
> storage (or do whatever prep work it does), just to have it PQclear'd once the 
> user gets the dead result back.  I guess we figured an error result typically 
> has a short life-span, not very useful outside of indicating an error.

Well, you could do a PQresultStatus and skip any prep work if you
actually saw a need to micro-optimize such cases.

> It is odd.  Actually, it looks like a bug to me.  PQgetResult is the
> behavior we were after, don't trigger the event if the creation
> failed.  Same thing occurs during a conn reset.  Looks like PQclear
> needs to check resultStatus before it triggers RESULTDESTROY events.

If you did that, then you WOULD have a bug.  Consider case where you
successfully init two events, and the third one fails.  You'll now
change the result's status to ERROR.  If you don't RESULTDESTROY
then the first two events will leak whatever storage they allocated.

The reason I suggested not being conditional about the init call was
to reduce the probability of bugs of similar ilks associated with
an event proc assuming that it could only get a DESTROY call for
something it had seen CREATEd --- for example, if it were frobbing
a reference count for some long-lived data, it could very easily
screw up the reference count that way.  I suppose that the risk of
an earlier event proc failing means it has to cope with that case
anyway, but I don't think it's a particularly good idea to have
arbitrary asymmetries increasing the odds of a problem.
        regards, tom lane


Re: [PATCHES] libpq events patch (with sgml docs)

From
Andrew Chernow
Date:
Tom Lane wrote:
>> It is odd.  Actually, it looks like a bug to me.  PQgetResult is the
>> behavior we were after, don't trigger the event if the creation
>> failed.  Same thing occurs during a conn reset.  Looks like PQclear
>> needs to check resultStatus before it triggers RESULTDESTROY events.
>
> If you did that, then you WOULD have a bug.  Consider case where you
> successfully init two events, and the third one fails.  You'll now
> change the result's status to ERROR.  If you don't RESULTDESTROY
> then the first two events will leak whatever storage they allocated.
>
> The reason I suggested not being conditional about the init call was
> to reduce the probability of bugs of similar ilks associated with
> an event proc assuming that it could only get a DESTROY call for
> something it had seen CREATEd --- for example, if it were frobbing
> a reference count for some long-lived data, it could very easily
> screw up the reference count that way.  I suppose that the risk of
> an earlier event proc failing means it has to cope with that case
> anyway, but I don't think it's a particularly good idea to have
> arbitrary asymmetries increasing the odds of a problem.
>
>             regards, tom lane
>
>

Yeah.  Good point.  I fixed it to always fire events.

--
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    17 Sep 2008 03:54:20 -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    17 Sep 2008 03:54:20 -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    17 Sep 2008 03:54:20 -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    17 Sep 2008 03:54:20 -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.197
diff -C6 -r1.197 fe-exec.c
*** src/interfaces/libpq/fe-exec.c    10 Sep 2008 17:01:07 -0000    1.197
--- src/interfaces/libpq/fe-exec.c    17 Sep 2008 03:54:20 -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,1617 ----
                                libpq_gettext("unexpected asyncStatus: %d\n"),
                                (int) conn->asyncStatus);
              res = PQmakeEmptyPGresult(conn, PGRES_FATAL_ERROR);
              break;
      }

+     if (res)
+     {
+         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    17 Sep 2008 03:54:20 -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    17 Sep 2008 03:54:20 -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

Re: [PATCHES] libpq events patch (with sgml docs)

From
Tom Lane
Date:
Andrew Chernow <ac@esilo.com> writes:
> Yeah.  Good point.  I fixed it to always fire events.

Applied with editorial revisions --- I don't think I changed any
functionality, but I fixed a number of corner-case bugs and
editorialized on style a bit.

The general question of symmetry between RESULTCREATE and RESULTDESTROY
callbacks is still bothering me.  As committed, PQmakeEmptyPGresult
will copy events into its result, but not fire RESULTCREATE for them
... but they'll get RESULTDESTROY when it's deleted.  Is that what we
want?  I don't have a lot of faith that PQgetResult is the only place
inside libpq that needs to fire RESULTCREATE, either.

Now, it's questionable how tense we need to be about that as long as
event proc failure aborts calling of later event procs.  That means
that procs have to be robust against getting DESTROY with no CREATE
calls in any case.  Should we try to make that less uncertain?
        regards, tom lane


Re: [PATCHES] libpq events patch (with sgml docs)

From
Andrew Chernow
Date:
 > Now, it's questionable how tense we need to be about that as long as
 > event proc failure aborts calling of later event procs.  That means
 > that procs have to be robust against getting DESTROY with no CREATE
 > calls in any case.  Should we try to make that less uncertain?
 >
 >

I attached a patch that adds a 'needDestroy' member to PGEvent  It is set when
resultcreate or resultcopy succeeds and checked during a PQclear.  That *should*
resolve the issue of "no resultcreate but gets a resultdestroy".

> The general question of symmetry between RESULTCREATE and RESULTDESTROY
> callbacks is still bothering me.  As committed, PQmakeEmptyPGresult
> will copy events into its result, but not fire RESULTCREATE for them
> ... but they'll get RESULTDESTROY when it's deleted.  Is that what we
> want?

PQmakeEmptyPGResult was given thought.  The problem is every internal function
that generates a result calls PQmakeEmptyPGResult, but those cases should not
fire a resultcreate.  resultcreate should be called when the result is fully
constructed (tuples and all) so the eventproc gets a useful PGresult.

One solution is to do something like the below:

PGresult *
PQmakeEmptyPGresult(PGconn *conn, ExecStatusType status)
{
   return pqMakeEmptyPGresult(conn, status, TRUE);
}

PGresult *
pqMakeEmptyPGresult(PGconn *conn, ExecStatusType status, int fireEvents)
{
   // existing function, only change is handling fireEvents
}

I am willing to create a patch for this.  Is this an acceptable idea?

 > I don't have a lot of faith that PQgetResult is the only place
 > inside libpq that needs to fire RESULTCREATE, either.
 >

I looked again and I didn't see anything.  Is there something your thinking of?
  ISTM that PQgetResult is called every where a result is created (outside of
PQmakeEmptyPGresult).

--
Andrew Chernow
eSilo, LLC
every bit counts
http://www.esilo.com/
Index: src/interfaces/libpq/fe-exec.c
===================================================================
RCS file: /projects/cvsroot/pgsql/src/interfaces/libpq/fe-exec.c,v
retrieving revision 1.198
diff -C6 -r1.198 fe-exec.c
*** src/interfaces/libpq/fe-exec.c    17 Sep 2008 04:31:08 -0000    1.198
--- src/interfaces/libpq/fe-exec.c    17 Sep 2008 10:40:41 -0000
***************
*** 356,367 ****
--- 356,369 ----
          if (!dest->events[i].proc(PGEVT_RESULTCOPY, &evt,
                                    dest->events[i].passThrough))
          {
              PQclear(dest);
              return NULL;
          }
+
+         dest->events[i].needDestroy = TRUE;
      }

      return dest;
  }

  /*
***************
*** 378,394 ****
          return NULL;

      newEvents = (PGEvent *) malloc(count * sizeof(PGEvent));
      if (!newEvents)
          return NULL;

-     memcpy(newEvents, events, count * sizeof(PGEvent));
-
-     /* NULL out the data pointers and deep copy names */
      for (i = 0; i < count; i++)
      {
          newEvents[i].data = NULL;
          newEvents[i].name = strdup(newEvents[i].name);
          if (!newEvents[i].name)
          {
              while (--i >= 0)
                  free(newEvents[i].name);
--- 380,396 ----
          return NULL;

      newEvents = (PGEvent *) malloc(count * sizeof(PGEvent));
      if (!newEvents)
          return NULL;

      for (i = 0; i < count; i++)
      {
+         newEvents[i].proc = events[i].proc;
+         newEvents[i].passThrough = events[i].passThrough;
+         newEvents[i].needDestroy = FALSE;
          newEvents[i].data = NULL;
          newEvents[i].name = strdup(newEvents[i].name);
          if (!newEvents[i].name)
          {
              while (--i >= 0)
                  free(newEvents[i].name);
***************
*** 663,679 ****

      if (!res)
          return;

      for (i = 0; i < res->nEvents; i++)
      {
!         PGEventResultDestroy evt;

-         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);

--- 665,685 ----

      if (!res)
          return;

      for (i = 0; i < res->nEvents; i++)
      {
!         if(res->events[i].needDestroy)
!         {
!             PGEventResultDestroy evt;
!
!             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);

***************
*** 1609,1620 ****
--- 1615,1628 ----
                                    libpq_gettext("PGEventProc \"%s\" failed during PGEVT_RESULTCREATE event\n"),
                                    res->events[i].name);
                  pqSetResultError(res, conn->errorMessage.data);
                  res->resultStatus = PGRES_FATAL_ERROR;
                  break;
              }
+
+             res->events[i].needDestroy = TRUE;
          }
      }

      return res;
  }

Index: src/interfaces/libpq/libpq-int.h
===================================================================
RCS file: /projects/cvsroot/pgsql/src/interfaces/libpq/libpq-int.h,v
retrieving revision 1.132
diff -C6 -r1.132 libpq-int.h
*** src/interfaces/libpq/libpq-int.h    17 Sep 2008 04:31:08 -0000    1.132
--- src/interfaces/libpq/libpq-int.h    17 Sep 2008 10:40:41 -0000
***************
*** 153,164 ****
--- 153,165 ----
  typedef struct PGEvent
  {
      PGEventProc    proc;            /* the function to call on events */
      char       *name;            /* used only for error messages */
      void       *passThrough;    /* pointer supplied at registration time */
      void       *data;            /* optional state (instance) data */
+     int           needDestroy;     /* indicates a PGEVT_RESULTDESTROY is needed. */
  } PGEvent;

  struct pg_result
  {
      int            ntups;
      int            numAttributes;

Re: [PATCHES] libpq events patch (with sgml docs)

From
Andrew Chernow
Date:
Andrew Chernow wrote:
>  > Now, it's questionable how tense we need to be about that as long as
>  > event proc failure aborts calling of later event procs.  That means
>  > that procs have to be robust against getting DESTROY with no CREATE
>  > calls in any case.  Should we try to make that less uncertain?
>  >
>  >
>
> I attached a patch that adds a 'needDestroy' member to PGEvent  It is
> set when resultcreate or resultcopy succeeds and checked during a
> PQclear.  That *should* resolve the issue of "no resultcreate but gets a
> resultdestroy".
>
>

Shoot.  I have a booboo in that last patch.  Attached is the corrected version.

--
Andrew Chernow
eSilo, LLC
every bit counts
http://www.esilo.com/
Index: src/interfaces/libpq/fe-exec.c
===================================================================
RCS file: /projects/cvsroot/pgsql/src/interfaces/libpq/fe-exec.c,v
retrieving revision 1.198
diff -C6 -r1.198 fe-exec.c
*** src/interfaces/libpq/fe-exec.c    17 Sep 2008 04:31:08 -0000    1.198
--- src/interfaces/libpq/fe-exec.c    17 Sep 2008 10:49:10 -0000
***************
*** 356,367 ****
--- 356,369 ----
          if (!dest->events[i].proc(PGEVT_RESULTCOPY, &evt,
                                    dest->events[i].passThrough))
          {
              PQclear(dest);
              return NULL;
          }
+
+         dest->events[i].needDestroy = TRUE;
      }

      return dest;
  }

  /*
***************
*** 378,396 ****
          return NULL;

      newEvents = (PGEvent *) malloc(count * sizeof(PGEvent));
      if (!newEvents)
          return NULL;

-     memcpy(newEvents, events, count * sizeof(PGEvent));
-
-     /* NULL out the data pointers and deep copy names */
      for (i = 0; i < count; i++)
      {
          newEvents[i].data = NULL;
!         newEvents[i].name = strdup(newEvents[i].name);
          if (!newEvents[i].name)
          {
              while (--i >= 0)
                  free(newEvents[i].name);
              free(newEvents);
              return NULL;
--- 380,398 ----
          return NULL;

      newEvents = (PGEvent *) malloc(count * sizeof(PGEvent));
      if (!newEvents)
          return NULL;

      for (i = 0; i < count; i++)
      {
+         newEvents[i].proc = events[i].proc;
+         newEvents[i].passThrough = events[i].passThrough;
+         newEvents[i].needDestroy = FALSE;
          newEvents[i].data = NULL;
!         newEvents[i].name = strdup(events[i].name);
          if (!newEvents[i].name)
          {
              while (--i >= 0)
                  free(newEvents[i].name);
              free(newEvents);
              return NULL;
***************
*** 663,679 ****

      if (!res)
          return;

      for (i = 0; i < res->nEvents; i++)
      {
!         PGEventResultDestroy evt;

-         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);

--- 665,685 ----

      if (!res)
          return;

      for (i = 0; i < res->nEvents; i++)
      {
!         if(res->events[i].needDestroy)
!         {
!             PGEventResultDestroy evt;
!
!             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);

***************
*** 1609,1620 ****
--- 1615,1628 ----
                                    libpq_gettext("PGEventProc \"%s\" failed during PGEVT_RESULTCREATE event\n"),
                                    res->events[i].name);
                  pqSetResultError(res, conn->errorMessage.data);
                  res->resultStatus = PGRES_FATAL_ERROR;
                  break;
              }
+
+             res->events[i].needDestroy = TRUE;
          }
      }

      return res;
  }

Index: src/interfaces/libpq/libpq-int.h
===================================================================
RCS file: /projects/cvsroot/pgsql/src/interfaces/libpq/libpq-int.h,v
retrieving revision 1.132
diff -C6 -r1.132 libpq-int.h
*** src/interfaces/libpq/libpq-int.h    17 Sep 2008 04:31:08 -0000    1.132
--- src/interfaces/libpq/libpq-int.h    17 Sep 2008 10:49:10 -0000
***************
*** 153,164 ****
--- 153,165 ----
  typedef struct PGEvent
  {
      PGEventProc    proc;            /* the function to call on events */
      char       *name;            /* used only for error messages */
      void       *passThrough;    /* pointer supplied at registration time */
      void       *data;            /* optional state (instance) data */
+     int           needDestroy;     /* indicates a PGEVT_RESULTDESTROY is needed. */
  } PGEvent;

  struct pg_result
  {
      int            ntups;
      int            numAttributes;

Re: [PATCHES] libpq events patch (with sgml docs)

From
Tom Lane
Date:
Andrew Chernow <ac@esilo.com> writes:
>>> Now, it's questionable how tense we need to be about that as long as
>>> event proc failure aborts calling of later event procs.  That means
>>> that procs have to be robust against getting DESTROY with no CREATE
>>> calls in any case.  Should we try to make that less uncertain?

> I attached a patch that adds a 'needDestroy' member to PGEvent It is
> set when resultcreate or resultcopy succeeds and checked during a
> PQclear.  That *should* resolve the issue of "no resultcreate but gets
> a resultdestroy".

Some thought would need to be given to how that interacts with
RESULTCOPY.  Presumably on the destination side, RESULTCOPY is the
equivalent of RESULTCREATE, ie, don't DESTROY if you didn't get COPY.
But what of the source side?  Arguably you shouldn't invoke COPY on
events that were never initialized in this object.

I also think that a little bit of thought should go into whether or
not to call DESTROY on an event that *did* get a CREATE and failed it.
You could argue that one either way: should a failing CREATE operation
be expected to fully clean up after itself, or should it be expected
to leave things in a state where DESTROY can clean up properly?
I'm not entirely sure, but option A might force duplication of code
between CREATE's failure path and DESTROY.  Whichever semantics we
choose needs to be documented.

Also, if we choose option B, then the same would hold for REGISTER
versus CONNDESTROY: failing REGISTER should still mean that you get
a CONNDESTROY.  So maybe that's an argument for option A, because
that's how REGISTER works now.
Lastly, the idea that was in the back of my mind was to resolve this
issue by unconditionally calling all the event procs at CREATE time,
not by cutting off the later ones if an earlier one failed.  Again,
I do not see a performance argument for skipping the extra steps,
and it seems to me that it might improve symmetry/robustness.
I'm not necessarily wedded to that approach but it bears thinking
about.
        regards, tom lane


Re: [PATCHES] libpq events patch (with sgml docs)

From
Andrew Chernow
Date:
Tom Lane wrote:
> 
> Some thought would need to be given to how that interacts with
> RESULTCOPY.  Presumably on the destination side, RESULTCOPY is the
> equivalent of RESULTCREATE, ie, don't DESTROY if you didn't get COPY.
> But what of the source side?  Arguably you shouldn't invoke COPY on
> events that were never initialized in this object.
> 

You are correct.  The resultcopy function needs to check if the src 
result eventproc was initialized.  BUT, that should not be possible 
unless someone is not checking return values.  Meaning, the only way to 
have an uninitialized eventproc in this instance is if something failed 
during a resultcreate.  Actually, if you call PQmakeEmptyPGResult then 
you can also run into this problem.  That can be solved by adding an 
argument to makeResult as I previously suggested.

> I also think that a little bit of thought should go into whether or
> not to call DESTROY on an event that *did* get a CREATE and failed it.
> You could argue that one either way: should a failing CREATE operation
> be expected to fully clean up after itself, or should it be expected
> to leave things in a state where DESTROY can clean up properly?
> I'm not entirely sure, but option A might force duplication of code
> between CREATE's failure path and DESTROY.  Whichever semantics we
> choose needs to be documented.
> 

If a resultcreate fails, than I think that resultdestroy should not be 
delivered to that eventproc (same for resultcopy).  That is how register 
works and how I saw this patch working (eventhough it appears I have a 
few logical errors).  I don't think it makes sense to do it otherwise, 
it would be like calling free after a malloc failure.

The needDestroy member should be called resultInitialized.  Although the 
conn doesn't reference the 'resultInitialized' member, I should 
initialize it to FALSE.  I did not do this in the last patch ... 
register function.

-- 
Andrew Chernow
eSilo, LLC
every bit counts
http://www.esilo.com/


Re: [PATCHES] libpq events patch (with sgml docs)

From
Tom Lane
Date:
Andrew Chernow <ac@esilo.com> writes:
> Tom Lane wrote:
>> Some thought would need to be given to how that interacts with
>> RESULTCOPY.  Presumably on the destination side, RESULTCOPY is the
>> equivalent of RESULTCREATE, ie, don't DESTROY if you didn't get COPY.
>> But what of the source side?  Arguably you shouldn't invoke COPY on
>> events that were never initialized in this object.

> You are correct.  The resultcopy function needs to check if the src 
> result eventproc was initialized.  BUT, that should not be possible 
> unless someone is not checking return values.  Meaning, the only way to 
> have an uninitialized eventproc in this instance is if something failed 
> during a resultcreate.  Actually, if you call PQmakeEmptyPGResult then 
> you can also run into this problem.  That can be solved by adding an 
> argument to makeResult as I previously suggested.

I still think it would be a good idea to expend a couple more lines in
PQcopyResult to cover the case --- the likelihood of there being code
paths that make an empty result and never invoke RESULTCREATE just seems
too high.  Defensive programming 'n all that.  In any case I'm not
convinced that we should force a RESULTCREATE cycle on external callers
of PQmakeEmptyPGResult.  If you guys didn't see a need for it in your
development then it probably doesn't exist.

> If a resultcreate fails, than I think that resultdestroy should not be 
> delivered to that eventproc (same for resultcopy).  That is how register 
> works and how I saw this patch working (eventhough it appears I have a 
> few logical errors).  I don't think it makes sense to do it otherwise, 
> it would be like calling free after a malloc failure.

I can live with that definition, but please document it.

> The needDestroy member should be called resultInitialized.

Yeah, probably, so that you can set it FALSE in conn events and continue
to use memcpy to move things over.
        regards, tom lane


Re: [PATCHES] libpq events patch (with sgml docs)

From
Andrew Chernow
Date:
New patch following our discussion with updated docs.

>> few logical errors).  I don't think it makes sense to do it otherwise,
>> it would be like calling free after a malloc failure.
>
> I can live with that definition, but please document it.
>
>

To build on this analogy, PGEVT_CONNRESET is like a realloc.  Meaning,
the initial malloc "PGEVT_REGISTER" worked by the realloc
"PGEVT_CONNRESET" didn't ... you still have free "PGEVT_CONNDESTROY" the
initial.  Its documented that way.  Basically if a register succeeds, a
destroy will always be sent regardless of what happens with a reset.

--
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    17 Sep 2008 03:54:20 -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    17 Sep 2008 03:54:20 -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    17 Sep 2008 03:54:20 -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    17 Sep 2008 03:54:20 -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.197
diff -C6 -r1.197 fe-exec.c
*** src/interfaces/libpq/fe-exec.c    10 Sep 2008 17:01:07 -0000    1.197
--- src/interfaces/libpq/fe-exec.c    17 Sep 2008 03:54:20 -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,1617 ----
                                libpq_gettext("unexpected asyncStatus: %d\n"),
                                (int) conn->asyncStatus);
              res = PQmakeEmptyPGresult(conn, PGRES_FATAL_ERROR);
              break;
      }

+     if (res)
+     {
+         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    17 Sep 2008 03:54:20 -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    17 Sep 2008 03:54:20 -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 */

Re: [PATCHES] libpq events patch (with sgml docs)

From
Andrew Chernow
Date:
Andrew Chernow wrote:
> New patch following our discussion with updated docs.
>
>>> few logical errors).  I don't think it makes sense to do it
>>> otherwise, it would be like calling free after a malloc failure.
>>
>> I can live with that definition, but please document it.
>>
>>
>
> To build on this analogy, PGEVT_CONNRESET is like a realloc.  Meaning,
> the initial malloc "PGEVT_REGISTER" worked by the realloc
> "PGEVT_CONNRESET" didn't ... you still have free "PGEVT_CONNDESTROY" the
> initial.  Its documented that way.  Basically if a register succeeds, a
> destroy will always be sent regardless of what happens with a reset.
>
>

I attached the wrong patch.  I'm sorry.

--
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.261
diff -C6 -r1.261 libpq.sgml
*** doc/src/sgml/libpq.sgml    17 Sep 2008 04:31:08 -0000    1.261
--- doc/src/sgml/libpq.sgml    17 Sep 2008 14:19:29 -0000
***************
*** 4911,4923 ****
         When a <literal>PGEVT_REGISTER</literal> event is received, the
         <parameter>evtInfo</parameter> pointer should be cast 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>.
        </para>
       </listitem>
      </varlistentry>

      <varlistentry>
       <term><literal>PGEVT_CONNRESET</literal></term>
--- 4911,4925 ----
         When a <literal>PGEVT_REGISTER</literal> event is received, the
         <parameter>evtInfo</parameter> pointer should be cast 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>.  When returning a failure code, all
!        cleanup must be performed as no <literal>PGEVT_CONNDESTROY</literal>
!        event will be sent.
        </para>
       </listitem>
      </varlistentry>

      <varlistentry>
       <term><literal>PGEVT_CONNRESET</literal></term>
***************
*** 4941,4953 ****

         When a <literal>PGEVT_CONNRESET</literal> event is received, the
         <parameter>evtInfo</parameter> pointer should be cast to a
         <structname>PGEventConnReset *</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
!        associated <literal>instanceData</literal>.
        </para>
       </listitem>
      </varlistentry>

      <varlistentry>
       <term><literal>PGEVT_CONNDESTROY</literal></term>
--- 4943,4956 ----

         When a <literal>PGEVT_CONNRESET</literal> event is received, the
         <parameter>evtInfo</parameter> pointer should be cast to a
         <structname>PGEventConnReset *</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
!        associated <literal>instanceData</literal>.  A PGEVT_CONNDESTROY event
!        is always sent, regardless of the event procedure's return value.
        </para>
       </listitem>
      </varlistentry>

      <varlistentry>
       <term><literal>PGEVT_CONNDESTROY</literal></term>
***************
*** 5000,5023 ****
         <structname>PGEventResultCreate *</structname>.  The
         <parameter>conn</parameter> is 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.  The event procedure must not try to
!        <function>PQclear</> the result object for itself.
        </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;
--- 5003,5030 ----
         <structname>PGEventResultCreate *</structname>.  The
         <parameter>conn</parameter> is 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.  The event procedure must not try to
!        <function>PQclear</> the result object for itself.  When returning a
!        failure code, all cleanup must be performed as no
!        <literal>PGEVT_RESULTDESTROY</literal> event will be sent.
        </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.  Only source result event procedures that have
!        successfully handled the <literal>PGEVT_RESULTCREATE</literal> event,
!        will be copied to the destination result.

        <synopsis>
  typedef struct
  {
      const PGresult *src;
      PGresult *dest;
***************
*** 5029,5041 ****
         <structname>PGEventResultCopy *</structname>.  The
         <parameter>src</parameter> result is what was 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>
--- 5036,5050 ----
         <structname>PGEventResultCopy *</structname>.  The
         <parameter>src</parameter> result is what was 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.   When returning a
!        failure code, all cleanup must be performed as no
!        <literal>PGEVT_RESULTDESTROY</literal> event will be sent.
        </para>
       </listitem>
      </varlistentry>

      <varlistentry>
       <term><literal>PGEVT_RESULTDESTROY</literal></term>
Index: src/interfaces/libpq/fe-exec.c
===================================================================
RCS file: /projects/cvsroot/pgsql/src/interfaces/libpq/fe-exec.c,v
retrieving revision 1.198
diff -C6 -r1.198 fe-exec.c
*** src/interfaces/libpq/fe-exec.c    17 Sep 2008 04:31:08 -0000    1.198
--- src/interfaces/libpq/fe-exec.c    17 Sep 2008 14:19:29 -0000
***************
*** 346,366 ****
          dest->nEvents = src->nEvents;
      }

      /* Okay, trigger PGEVT_RESULTCOPY event */
      for (i = 0; i < dest->nEvents; i++)
      {
!         PGEventResultCopy evt;
!
!         evt.src = src;
!         evt.dest = dest;
!         if (!dest->events[i].proc(PGEVT_RESULTCOPY, &evt,
!                                   dest->events[i].passThrough))
          {
!             PQclear(dest);
!             return NULL;
          }
      }

      return dest;
  }

--- 346,371 ----
          dest->nEvents = src->nEvents;
      }

      /* Okay, trigger PGEVT_RESULTCOPY event */
      for (i = 0; i < dest->nEvents; i++)
      {
!         if(src->events[i].resultInitialized)
          {
!             PGEventResultCopy evt;
!
!             evt.src = src;
!             evt.dest = dest;
!             if (!dest->events[i].proc(PGEVT_RESULTCOPY, &evt,
!                                       dest->events[i].passThrough))
!             {
!                 PQclear(dest);
!                 return NULL;
!             }
!
!             dest->events[i].resultInitialized = TRUE;
          }
      }

      return dest;
  }

***************
*** 378,396 ****
          return NULL;

      newEvents = (PGEvent *) malloc(count * sizeof(PGEvent));
      if (!newEvents)
          return NULL;

-     memcpy(newEvents, events, count * sizeof(PGEvent));
-
-     /* NULL out the data pointers and deep copy names */
      for (i = 0; i < count; i++)
      {
          newEvents[i].data = NULL;
!         newEvents[i].name = strdup(newEvents[i].name);
          if (!newEvents[i].name)
          {
              while (--i >= 0)
                  free(newEvents[i].name);
              free(newEvents);
              return NULL;
--- 383,401 ----
          return NULL;

      newEvents = (PGEvent *) malloc(count * sizeof(PGEvent));
      if (!newEvents)
          return NULL;

      for (i = 0; i < count; i++)
      {
+         newEvents[i].proc = events[i].proc;
+         newEvents[i].passThrough = events[i].passThrough;
+         newEvents[i].resultInitialized = FALSE;
          newEvents[i].data = NULL;
!         newEvents[i].name = strdup(events[i].name);
          if (!newEvents[i].name)
          {
              while (--i >= 0)
                  free(newEvents[i].name);
              free(newEvents);
              return NULL;
***************
*** 663,679 ****

      if (!res)
          return;

      for (i = 0; i < res->nEvents; i++)
      {
!         PGEventResultDestroy evt;

-         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);

--- 668,688 ----

      if (!res)
          return;

      for (i = 0; i < res->nEvents; i++)
      {
!         if(res->events[i].resultInitialized)
!         {
!             PGEventResultDestroy evt;
!
!             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);

***************
*** 1609,1620 ****
--- 1618,1631 ----
                                    libpq_gettext("PGEventProc \"%s\" failed during PGEVT_RESULTCREATE event\n"),
                                    res->events[i].name);
                  pqSetResultError(res, conn->errorMessage.data);
                  res->resultStatus = PGRES_FATAL_ERROR;
                  break;
              }
+
+             res->events[i].resultInitialized = TRUE;
          }
      }

      return res;
  }

Index: src/interfaces/libpq/libpq-events.c
===================================================================
RCS file: /projects/cvsroot/pgsql/src/interfaces/libpq/libpq-events.c,v
retrieving revision 1.1
diff -C6 -r1.1 libpq-events.c
*** src/interfaces/libpq/libpq-events.c    17 Sep 2008 04:31:08 -0000    1.1
--- src/interfaces/libpq/libpq-events.c    17 Sep 2008 14:19:29 -0000
***************
*** 73,84 ****
--- 73,85 ----
      conn->events[conn->nEvents].proc = proc;
      conn->events[conn->nEvents].name = strdup(name);
      if (!conn->events[conn->nEvents].name)
          return FALSE;
      conn->events[conn->nEvents].passThrough = passThrough;
      conn->events[conn->nEvents].data = NULL;
+     conn->events[conn->nEvents].resultInitialized = FALSE;
      conn->nEvents++;

      regevt.conn = conn;
      if (!proc(PGEVT_REGISTER, ®evt, passThrough))
      {
          conn->nEvents--;
Index: src/interfaces/libpq/libpq-int.h
===================================================================
RCS file: /projects/cvsroot/pgsql/src/interfaces/libpq/libpq-int.h,v
retrieving revision 1.132
diff -C6 -r1.132 libpq-int.h
*** src/interfaces/libpq/libpq-int.h    17 Sep 2008 04:31:08 -0000    1.132
--- src/interfaces/libpq/libpq-int.h    17 Sep 2008 14:19:29 -0000
***************
*** 153,164 ****
--- 153,165 ----
  typedef struct PGEvent
  {
      PGEventProc    proc;            /* the function to call on events */
      char       *name;            /* used only for error messages */
      void       *passThrough;    /* pointer supplied at registration time */
      void       *data;            /* optional state (instance) data */
+     int           resultInitialized; /* indicates a PGEVT_RESULTCREATE succeeded. */
  } PGEvent;

  struct pg_result
  {
      int            ntups;
      int            numAttributes;

Re: [PATCHES] libpq events patch (with sgml docs)

From
Andrew Chernow
Date:
Are there any plans to commit these libpq-events changes this fest?

http://archives.postgresql.org/pgsql-hackers/2008-09/msg01109.php

I wanted to release an updated libpqtypes but am waiting on the above 
patch.  If not, I'll release it now.

-- 
Andrew Chernow
eSilo, LLC
every bit counts
http://www.esilo.com/


Re: [PATCHES] libpq events patch (with sgml docs)

From
Tom Lane
Date:
Andrew Chernow <ac@esilo.com> writes:
> Are there any plans to commit these libpq-events changes this fest?

Sorry about that, I got distracted.  Will look at it shortly.
        regards, tom lane


Re: [PATCHES] libpq events patch (with sgml docs)

From
Tom Lane
Date:
Andrew Chernow <ac@esilo.com> writes:
>> To build on this analogy, PGEVT_CONNRESET is like a realloc.  Meaning, 
>> the initial malloc "PGEVT_REGISTER" worked by the realloc 
>> "PGEVT_CONNRESET" didn't ... you still have free "PGEVT_CONNDESTROY" the 
>> initial.  Its documented that way.  Basically if a register succeeds, a 
>> destroy will always be sent regardless of what happens with a reset.

> I attached the wrong patch.  I'm sorry.

I had a further thought about this: after applying this patch, it is
essentially useless for the exposed PQmakeEmptyPGresult function to
copy events into the result.  If it doesn't give them a RESULTCREATE
call, then they cannot receive RESULTCOPY or RESULTDESTROY either,
so they might as well not be there.

The argument for not having PQmakeEmptyPGresult fire RESULTCREATE still
makes sense, but I am thinking that maybe what we ought to do is expose
a new function named something like PQfireResultCreateEvents() that just
does that.  This would allow an application to exactly emulate what
PQgetResult does: make an empty PGresult, fill it, then fire the create
events.

I'll go ahead and apply this patch in a little bit, but if you concur
with the above reasoning, please put together a followon patch to add
such a function.
        regards, tom lane


Re: [PATCHES] libpq events patch (with sgml docs)

From
Andrew Chernow
Date:
Tom Lane wrote:
>
> I'll go ahead and apply this patch in a little bit, but if you concur
> with the above reasoning, please put together a followon patch to add
> such a function.
>
>             regards, tom lane
>
>

I attached a patch.  You have to copy the events in PQmakeEmptyPGResult
because there is no where else to do this, other than copyresult but
that is different because it copies from a result not a conn.

PQmakeEmptyPGResult - must copy events here
PQsetResultAttrs - set attributes
PQsetvalue - set tuple values
PQfireResultCreateEvents(conn,res) - now fire resultcreate event

PQgetResult now calls PQfireResultCreateEvents.

BTW, the event system might be an alternative solution for PGNoticeHooks
(PGEVT_NOTICE).

--
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.262
diff -C6 -r1.262 libpq.sgml
*** doc/src/sgml/libpq.sgml    19 Sep 2008 16:40:40 -0000    1.262
--- doc/src/sgml/libpq.sgml    19 Sep 2008 18:05:55 -0000
***************
*** 4592,4604 ****
        <parameter>conn</parameter> is not null and <parameter>status</>
        indicates an error, the current error message of the specified
        connection is copied into the <structname>PGresult</structname>.
        Also, if <parameter>conn</parameter> is not null, any event handlers
        registered in the connection are copied into the
        <structname>PGresult</structname> (but they don't get
!       <literal>PGEVT_RESULTCREATE</> calls).
        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>
--- 4592,4606 ----
        <parameter>conn</parameter> is not null and <parameter>status</>
        indicates an error, the current error message of the specified
        connection is copied into the <structname>PGresult</structname>.
        Also, if <parameter>conn</parameter> is not null, any event handlers
        registered in the connection are copied into the
        <structname>PGresult</structname> (but they don't get
!       <literal>PGEVT_RESULTCREATE</> calls).  Although,
!       <function>PQfireResultCreateEvents</function> can be used to fire
!       <literal>PGEVT_RESULTCREATE</> events.
        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>
***************
*** 5242,5253 ****
--- 5244,5279 ----
          void *PQresultInstanceData(const PGresult *res, PGEventProc proc);
         </synopsis>
        </para>
       </listitem>
      </varlistentry>
     </variablelist>
+
+     <varlistentry>
+      <term>
+       <function>PQfireResultCreateEvents</function>
+       <indexterm>
+        <primary>PQfireResultCreateEvents</primary>
+       </indexterm>
+      </term>
+      <listitem>
+       <para>
+        Manually fires a <literal>PGEVT_RESULTCREATE</literal> event.  This is
+        useful for applications that create a result using
+        <function>PQmakeEmptyPGResult</function>, which does not fire a
+        <literal>PGEVT_RESULTCREATE</literal> event.  It allows an application
+        to create the result, fill it and then fire the creation event.  This
+        returns non-zero for success and zero for failure.
+
+        <synopsis>
+         int PQfireResultCreateEvents(const PGconn conn, PGresult *res);
+        </synopsis>
+       </para>
+      </listitem>
+     </varlistentry>
+    </variablelist>
    </sect2>

    <sect2 id="libpq-events-example">
     <title>Event Example</title>

     <para>
Index: src/interfaces/libpq/exports.txt
===================================================================
RCS file: /projects/cvsroot/pgsql/src/interfaces/libpq/exports.txt,v
retrieving revision 1.20
diff -C6 -r1.20 exports.txt
*** src/interfaces/libpq/exports.txt    17 Sep 2008 04:31:08 -0000    1.20
--- src/interfaces/libpq/exports.txt    19 Sep 2008 18:05:55 -0000
***************
*** 147,152 ****
--- 147,153 ----
  PQresultAlloc             145
  PQregisterEventProc       146
  PQinstanceData            147
  PQsetInstanceData         148
  PQresultInstanceData      149
  PQresultSetInstanceData   150
+ PQfireResultCreateEvents  151
Index: src/interfaces/libpq/fe-exec.c
===================================================================
RCS file: /projects/cvsroot/pgsql/src/interfaces/libpq/fe-exec.c,v
retrieving revision 1.199
diff -C6 -r1.199 fe-exec.c
*** src/interfaces/libpq/fe-exec.c    19 Sep 2008 16:40:40 -0000    1.199
--- src/interfaces/libpq/fe-exec.c    19 Sep 2008 18:05:55 -0000
***************
*** 1595,1630 ****
                                libpq_gettext("unexpected asyncStatus: %d\n"),
                                (int) conn->asyncStatus);
              res = PQmakeEmptyPGresult(conn, PGRES_FATAL_ERROR);
              break;
      }

!     if (res)
!     {
!         int i;
!
!         for (i = 0; i < res->nEvents; i++)
!         {
!             PGEventResultCreate evt;
!
!             evt.conn = conn;
!             evt.result = res;
!             if (!res->events[i].proc(PGEVT_RESULTCREATE, &evt,
!                                      res->events[i].passThrough))
!             {
!                 printfPQExpBuffer(&conn->errorMessage,
!                                   libpq_gettext("PGEventProc \"%s\" failed during PGEVT_RESULTCREATE event\n"),
!                                   res->events[i].name);
!                 pqSetResultError(res, conn->errorMessage.data);
!                 res->resultStatus = PGRES_FATAL_ERROR;
!                 break;
!             }
!             res->events[i].resultInitialized = TRUE;
!         }
!     }
!
      return res;
  }


  /*
   * PQexec
--- 1595,1608 ----
                                libpq_gettext("unexpected asyncStatus: %d\n"),
                                (int) conn->asyncStatus);
              res = PQmakeEmptyPGresult(conn, PGRES_FATAL_ERROR);
              break;
      }

!     /* Function performs error handling: message and resultStatus. */
!     (void) PQfireResultCreateEvents(conn, res);
      return res;
  }


  /*
   * PQexec
Index: src/interfaces/libpq/libpq-events.c
===================================================================
RCS file: /projects/cvsroot/pgsql/src/interfaces/libpq/libpq-events.c,v
retrieving revision 1.2
diff -C6 -r1.2 libpq-events.c
*** src/interfaces/libpq/libpq-events.c    19 Sep 2008 16:40:40 -0000    1.2
--- src/interfaces/libpq/libpq-events.c    19 Sep 2008 18:05:55 -0000
***************
*** 172,177 ****
--- 172,209 ----
      for (i = 0; i < result->nEvents; i++)
          if (result->events[i].proc == proc)
              return result->events[i].data;

      return NULL;
  }
+
+ int
+ PQfireResultCreateEvents(const PGconn *conn, PGresult *res)
+ {
+     int i;
+
+     if (!conn || !res)
+         return FALSE;
+
+     for (i = 0; i < res->nEvents; i++)
+     {
+         PGEventResultCreate evt;
+
+         evt.conn = conn;
+         evt.result = res;
+         if (!res->events[i].proc(PGEVT_RESULTCREATE, &evt,
+                                  res->events[i].passThrough))
+         {
+             printfPQExpBuffer(&conn->errorMessage,
+                               libpq_gettext("PGEventProc \"%s\" failed during PGEVT_RESULTCREATE event\n"),
+                               res->events[i].name);
+             pqSetResultError(res, conn->errorMessage.data);
+             res->resultStatus = PGRES_FATAL_ERROR;
+             return FALSE;
+         }
+
+         res->events[i].resultInitialized = TRUE;
+     }
+
+     return TRUE;
+ }
+
Index: src/interfaces/libpq/libpq-events.h
===================================================================
RCS file: /projects/cvsroot/pgsql/src/interfaces/libpq/libpq-events.h,v
retrieving revision 1.1
diff -C6 -r1.1 libpq-events.h
*** src/interfaces/libpq/libpq-events.h    17 Sep 2008 04:31:08 -0000    1.1
--- src/interfaces/libpq/libpq-events.h    19 Sep 2008 18:05:55 -0000
***************
*** 81,91 ****
--- 81,93 ----
  /* Sets the PGresult instance data for the provided proc to data. */
  extern int    PQresultSetInstanceData(PGresult *result, PGEventProc proc, void *data);

  /* Gets the PGresult instance data for the provided proc. */
  extern void *PQresultInstanceData(const PGresult *result, PGEventProc proc);

+ extern int PQfireResultCreateEvents(const PGconn *conn, PGresult *res);
+
  #ifdef __cplusplus
  }
  #endif

  #endif /* LIBPQ_EVENTS_H */

Re: [PATCHES] libpq events patch (with sgml docs)

From
"Merlin Moncure"
Date:
On Fri, Sep 19, 2008 at 2:14 PM, Andrew Chernow <ac@esilo.com> wrote:
>
> BTW, the event system might be an alternative solution for PGNoticeHooks
> (PGEVT_NOTICE).
>

Another possible use of the event hooks -- just spitballing here -- is
to generate an event when a notification comes through (you would
still receive events the old way., by command or PQconsumeInput).
Maybe this would eventually replace the current notification interface
(wasn't this going to be changed anyways?)

merlin


Re: [PATCHES] libpq events patch (with sgml docs)

From
Tom Lane
Date:
BTW, why are all the conn parameters to events declared "const"?  Isn't
the reason for passing them in mainly to give the event proc a chance
to issue queries?
        regards, tom lane


Re: [PATCHES] libpq events patch (with sgml docs)

From
Andrew Chernow
Date:
Tom Lane wrote:
> BTW, why are all the conn parameters to events declared "const"?  Isn't

Because it looked prettier?  Kidding.  No idea, do you want me to change 
that or do you want to?

> the reason for passing them in mainly to give the event proc a chance
> to issue queries?
>
> 

Partly.  You also want to give the eventproc a chance to issue a 
PQinstanceData call, so it can copy stuff to the created result.

-- 
Andrew Chernow
eSilo, LLC
every bit counts
http://www.esilo.com/


Re: [PATCHES] libpq events patch (with sgml docs)

From
Tom Lane
Date:
Andrew Chernow <ac@esilo.com> writes:
> Tom Lane wrote:
>> BTW, why are all the conn parameters to events declared "const"?  Isn't

> Because it looked prettier?  Kidding.  No idea, do you want me to change 
> that or do you want to?

I'll fix it; it's hardly worth passing a patch around for.
        regards, tom lane


Re: [PATCHES] libpq events patch (with sgml docs)

From
Tom Lane
Date:
Andrew Chernow <ac@esilo.com> writes:
> I attached a patch.  You have to copy the events in PQmakeEmptyPGResult 
> because there is no where else to do this, other than copyresult but 
> that is different because it copies from a result not a conn.

Applied ...

> PQgetResult now calls PQfireResultCreateEvents.

... except I didn't do that because the error handling didn't seem
appropriate.  Since PQmakeEmptyPGResult allows a null conn,
PQfireResultCreateEvents ought to as well.  So I just made it return
false on failure.
        regards, tom lane


Re: [PATCHES] libpq events patch (with sgml docs)

From
Tom Lane
Date:
"Merlin Moncure" <mmoncure@gmail.com> writes:
> On Fri, Sep 19, 2008 at 2:14 PM, Andrew Chernow <ac@esilo.com> wrote:
>> BTW, the event system might be an alternative solution for PGNoticeHooks
>> (PGEVT_NOTICE).

> Another possible use of the event hooks -- just spitballing here -- is
> to generate an event when a notification comes through (you would
> still receive events the old way., by command or PQconsumeInput).

Seems rather pointless; you'd just be adding multiple ways to do things
we can do perfectly well now.

> Maybe this would eventually replace the current notification interface
> (wasn't this going to be changed anyways?)

No, I don't think anyone was unhappy with the libpq API for it.
        regards, tom lane