Add an "events" system to libpq, whereby applications can get callbacks that
authorTom Lane
Wed, 17 Sep 2008 04:31:08 +0000 (04:31 +0000)
committerTom Lane
Wed, 17 Sep 2008 04:31:08 +0000 (04:31 +0000)
enable them to manage private data associated with PGconns and PGresults.

Andrew Chernow and Merlin Moncure

doc/src/sgml/libpq.sgml
src/interfaces/libpq/Makefile
src/interfaces/libpq/exports.txt
src/interfaces/libpq/fe-connect.c
src/interfaces/libpq/fe-exec.c
src/interfaces/libpq/libpq-events.c [new file with mode: 0644]
src/interfaces/libpq/libpq-events.h [new file with mode: 0644]
src/interfaces/libpq/libpq-fe.h
src/interfaces/libpq/libpq-int.h
src/tools/msvc/Install.pm

index deb052e5c49421d262aa81be20fae0728beee3e9..2db369e906dfbb005e17201ad42c8ea7e728dcce 100644 (file)
@@ -1,4 +1,4 @@
-
+
 
 
  <application>libpq</application> - C Library
@@ -2063,38 +2063,6 @@ PGresult *PQdescribePortal(PGconn *conn, const char *portalName);
        
       
      
-
-     
-      
-       PQmakeEmptyPGresult
-       
-        PQmakeEmptyPGresult
-       
-      
-
-      
-       
-        Constructs an empty PGresult object with the given status.
-        
-         PGresult *PQmakeEmptyPGresult(PGconn *conn, ExecStatusType status);
-        
-       
-
-       
-        This is libpq's internal function to allocate and
-        initialize an empty PGresult object.  This
-        function returns NULL if memory could not be allocated. It is
-        exported because some applications find it useful to generate result
-        objects (particularly objects with error status) themselves.  If
-        conn is not null and status
-        indicates an error, the current error message of the specified
-        connection is copied into the PGresult.
-        Note that PQclear should eventually be called
-        on the object, just as with a PGresult
-        returned by libpq itself.
-       
-      
-     
     
    
   
@@ -4598,6 +4566,170 @@ char *pg_encoding_to_char(int encoding_id);
      
     
    
+
+   
+    
+     PQmakeEmptyPGresult
+     
+      PQmakeEmptyPGresult
+     
+    
+
+    
+     
+      Constructs an empty PGresult object with the given status.
+      
+       PGresult *PQmakeEmptyPGresult(PGconn *conn, ExecStatusType status);
+      
+     
+
+     
+      This is libpq's internal function to allocate and
+      initialize an empty PGresult object.  This
+      function returns NULL if memory could not be allocated. It is
+      exported because some applications find it useful to generate result
+      objects (particularly objects with error status) themselves.  If
+      conn is not null and status
+      indicates an error, the current error message of the specified
+      connection is copied into the PGresult.
+      Also, if conn is not null, any event handlers
+      registered in the connection are copied into the
+      PGresult (but they don't get
+      PGEVT_RESULTCREATE calls).
+      Note that PQclear should eventually be called
+      on the object, just as with a PGresult
+      returned by libpq itself.
+     
+    
+   
+
+   
+    
+     PQcopyResult
+     
+      PQcopyResult
+     
+    
+
+    
+     
+      Makes a copy of a PGresult object.  The copy is
+      not linked to the source result in any way and
+      PQclear must be called when the copy is no longer
+      needed.  If the function fails, NULL is returned.
+
+      
+       PGresult *PQcopyResult(const PGresult *src, int flags);
+      
+     
+
+     
+      This is not intended to make an exact copy.  The returned result is
+      always put into PGRES_TUPLES_OK status, and does not
+      copy any error message in the source.  (It does copy the command status
+      string, however.)  The flags argument determines
+      what else is copied.  It is a bitwise OR of several flags.
+      PG_COPYRES_ATTRS specifies copying the source
+      result's attributes (column definitions).
+      PG_COPYRES_TUPLES specifies copying the source
+      result's tuples.  (This implies copying the attributes, too.)
+      PG_COPYRES_NOTICEHOOKS specifies
+      copying the source result's notify hooks.
+      PG_COPYRES_EVENTS specifies copying the source
+      result's events.  (But any instance data associated with the source
+      is not copied.)
+     
+    
+   
+
+   
+    
+     PQsetResultAttrs
+     
+      PQsetResultAttrs
+     
+    
+
+    
+     
+      Sets the attributes of a PGresult object.
+      
+       int PQsetResultAttrs(PGresult *res, int numAttributes, PGresAttDesc *attDescs);
+      
+     
+
+     
+      The provided attDescs are copied into the result.
+      If the attDescs pointer is NULL or
+      numAttributes is less than one, the request is
+      ignored and the function succeeds.  If res
+      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.
+     
+    
+   
+
+   
+    
+     PQsetvalue
+     
+      PQsetvalue
+     
+    
+
+    
+     
+      Sets a tuple field value of a PGresult object.
+      
+       int PQsetvalue(PGresult *res, int tup_num, int field_num, char *value, int len);
+      
+     
+
+     
+      The function will automatically grow the result's internal tuples array
+      as needed.  However, the tup_num argument must be
+      less than or equal to PQntuples, meaning this
+      function can only grow the tuples array one tuple at a time.  But any
+      field of any existing tuple can be modified in any order.  If a value at
+      field_num already exists, it will be overwritten.
+      If len is -1 or
+      value is NULL, the field value
+      will be set to an SQL NULL.  The
+      value is copied into the result's private storage,
+      thus is no longer needed after the function
+      returns.  If the function fails, the return value is zero.  If the
+      function succeeds, the return value is non-zero.
+     
+    
+   
+
+   
+    
+     PQresultAlloc
+     
+      PQresultAlloc
+     
+    
+
+    
+     
+      Allocate subsidiary storage for a PGresult object.
+      
+       void *PQresultAlloc(PGresult *res, size_t nBytes);
+      
+     
+
+     
+      Any memory allocated with this function will be freed when
+      res is cleared.  If the function fails,
+      the return value is NULL.  The result is
+      guaranteed to be adequately aligned for any type of data,
+      just as for malloc.
+     
+    
+   
+
   
 
  
@@ -4711,6 +4843,551 @@ defaultNoticeProcessor(void *arg, const char *message)
 
  
 
+  Event System
+
+  
+   libpq's event system is designed to notify
+   registered event handlers about interesting
+   libpq events, such as the creation or
+   destruction of PGconn and
+   PGresult objects.  A principal use case is that
+   this allows applications to associate their own data with a
+   PGconn or PGresult
+   and ensure that that data is freed at an appropriate time.
+  
+
+  
+   Each registered event handler is associated with two pieces of data,
+   known to libpq only as opaque void *
+   pointers.  There is a passthrough pointer that is provided
+   by the application when the event handler is registered with a
+   PGconn.  The passthrough pointer never changes for the
+   life of the PGconn and all PGresults
+   generated from it; so if used, it must point to long-lived data.
+   In addition there is an instance data pointer, which starts
+   out NULL in every PGconn and PGresult.
+   This pointer can be manipulated using the
+   PQinstanceData,
+   PQsetInstanceData,
+   PQresultInstanceData and
+   PQsetResultInstanceData functions.  Note that
+   unlike the passthrough pointer, instance data of a PGconn
+   is not automatically inherited by PGresults created from
+   it.  libpq does not know what passthrough
+   and instance data pointers point to (if anything) and will never attempt
+   to free them — that is the responsibility of the event handler.
+  
+
+  
+   Event Types
+
+   
+    The enum PGEventId names the types of events handled by
+    the event system.  All its values have names beginning with
+    PGEVT.  For each event type, there is a corresponding
+    event info structure that carries the parameters passed to the event
+    handlers.  The event types are:
+   
+
+   
+    
+     PGEVT_REGISTER
+     
+      
+       The register event occurs when PQregisterEventProc
+       is called.  It is the ideal time to initialize any
+       instanceData an event procedure may need.  Only one
+       register event will be fired per event handler per connection.  If the
+       event procedure fails, the registration is aborted.
+
+      
+typedef struct
+{
+    const PGconn *conn;
+} PGEventRegister;
+      
+
+       When a PGEVT_REGISTER event is received, the
+       evtInfo pointer should be cast to a
+       PGEventRegister *.  This structure contains a
+       PGconn that should be in the
+       CONNECTION_OK status; guaranteed if one calls
+       PQregisterEventProc right after obtaining a good
+       PGconn.
+      
+     
+    
+
+    
+     PGEVT_CONNRESET
+     
+      
+       The connection reset event is fired on completion of
+       PQreset or PQresetPoll.  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
+       PGconn is put into
+       CONNECTION_BAD status and
+       PQresetPoll will return
+       PGRES_POLLING_FAILED.
+
+      
+typedef struct
+{
+    const PGconn *conn;
+} PGEventConnReset;
+      
+
+       When a PGEVT_CONNRESET event is received, the
+       evtInfo pointer should be cast to a
+       PGEventConnReset *.  Although the contained
+       PGconn was just reset, all event data remains
+       unchanged.  This event should be used to reset/reload/requery any
+       associated instanceData.
+      
+     
+    
+
+    
+     PGEVT_CONNDESTROY
+     
+      
+       The connection destroy event is fired in response to
+       PQfinish.  It is the event procedure's
+       responsibility to properly clean up its event data as libpq has no
+       ability to manage this memory.  Failure to clean up will lead
+       to memory leaks.
+
+      
+typedef struct
+{
+    const PGconn *conn;
+} PGEventConnDestroy;
+      
+
+       When a PGEVT_CONNDESTROY event is received, the
+       evtInfo pointer should be cast to a
+       PGEventConnDestroy *.  This event is fired
+       prior to PQfinish performing any other cleanup.
+       The return value of the event procedure is ignored since there is no
+       way of indicating a failure from PQfinish.  Also,
+       an event procedure failure should not abort the process of cleaning up
+       unwanted memory.
+      
+     
+    
+
+    
+     PGEVT_RESULTCREATE
+     
+      
+       The result creation event is fired in response to any query execution
+       function that generates a result, including
+       PQgetResult.  This event will only be fired after
+       the result has been created successfully.
+
+      
+typedef struct
+{
+    const PGconn *conn;
+    PGresult *result;
+} PGEventResultCreate;
+      
+
+       When a PGEVT_RESULTCREATE event is received, the
+       evtInfo pointer should be cast to a
+       PGEventResultCreate *.  The
+       conn is the connection used to generate the
+       result.  This is the ideal place to initialize any
+       instanceData 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
+       PQclear the result object for itself.
+      
+     
+    
+
+    
+     PGEVT_RESULTCOPY
+     
+      
+       The result copy event is fired in response to
+       PQcopyResult.  This event will only be fired after
+       the copy is complete.
+
+      
+typedef struct
+{
+    const PGresult *src;
+    PGresult *dest;
+} PGEventResultCopy;
+      
+
+       When a PGEVT_RESULTCOPY event is received, the
+       evtInfo pointer should be cast to a
+       PGEventResultCopy *.  The
+       src result is what was copied while the
+       dest result is the copy destination.  This event
+       can be used to provide a deep copy of instanceData,
+       since PQcopyResult cannot do that.  If the event
+       procedure fails, the entire copy operation will fail and the
+       dest result will be cleared.
+      
+     
+    
+
+    
+     PGEVT_RESULTDESTROY
+     
+      
+       The result destroy event is fired in response to a
+       PQclear.  It is the event procedure's
+       responsibility to properly clean up its event data as libpq has no
+       ability to manage this memory.  Failure to clean up will lead
+       to memory leaks.
+
+      
+typedef struct
+{
+    const PGresult *result;
+} PGEventResultDestroy;
+      
+
+       When a PGEVT_RESULTDESTROY event is received, the
+       evtInfo pointer should be cast to a
+       PGEventResultDestroy *.  This event is fired
+       prior to PQclear performing any other cleanup.
+       The return value of the event procedure is ignored since there is no
+       way of indicating a failure from PQclear.  Also,
+       an event procedure failure should not abort the process of cleaning up
+       unwanted memory.
+      
+     
+    
+   
+  
+
+  
+   Event Callback Procedure
+
+   
+    
+     
+      PGEventProc
+      
+       PGEventProc
+      
+     
+
+     
+      
+       PGEventProc is a typedef for a pointer to an
+       event procedure, that is, the user callback function that receives
+       events from libpq.  The signature of an event procedure must be
+
+      
+int eventproc(PGEventId evtId, void *evtInfo, void *passThrough)
+      
+
+       The evtId parameter indicates which
+       PGEVT event occurred.  The
+       evtInfo pointer must be cast to the appropriate
+       structure type to obtain further information about the event.
+       The passThrough parameter is the pointer
+       provided to PQregisterEventProc when the event
+       procedure was registered.  The function should return a non-zero value
+       if it succeeds and zero if it fails.
+      
+
+      
+       A particular event procedure can be registered only once in any
+       PGconn.  This is because the address of the procedure
+       is used as a lookup key to identify the associated instance data.
+      
+     
+    
+   
+  
+
+  
+   Event Support Functions
+
+    
+    
+     
+      PQregisterEventProc
+      
+       PQregisterEventProc
+      
+     
+
+     
+      
+       Registers an event callback procedure with libpq.
+
+       
+        int PQregisterEventProc(PGconn *conn, PGEventProc proc,
+                                const char *name, void *passThrough);
+       
+      
+
+      
+       An event procedure must be registered once on each
+       PGconn you want to receive events about.  There is no
+       limit, other than memory, 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.
+      
+
+      
+       The proc argument will be called when a libpq
+       event is fired.  Its memory address is also used to lookup
+       instanceData.  The name
+       argument is used to refer to the event procedure in error messages.
+       This value cannot be NULL or a zero-length string.  The name string is
+       copied into the PGconn, so what is passed need not be
+       long-lived.  The passThrough pointer is passed
+       to the proc whenever an event occurs. This
+       argument can be NULL.
+      
+     
+    
+
+    
+     
+      PQsetInstanceData
+      
+       PQsetInstanceData
+      
+     
+     
+      
+       Sets the conn's instanceData for proc to data.  This returns non-zero
+       for success and zero for failure.  (Failure is only possible if
+       the proc has not been properly registered in the conn.)
+
+       
+        int PQsetInstanceData(PGconn *conn, PGEventProc proc, void *data);
+       
+      
+     
+    
+
+    
+     
+      PQinstanceData
+      
+       PQinstanceData
+      
+     
+     
+      
+       Returns the conn's instanceData associated with proc, or NULL
+       if there is none.
+
+       
+        void *PQinstanceData(const PGconn *conn, PGEventProc proc);
+       
+      
+     
+    
+
+    
+     
+      PQresultSetInstanceData
+      
+       PQresultSetInstanceData
+      
+     
+     
+      
+       Sets the result's instanceData for proc to data.  This returns non-zero
+       for success and zero for failure.  (Failure is only possible if the
+       proc has not been properly registered in the result.)
+
+       
+        int PQresultSetInstanceData(PGresult *res, PGEventProc proc, void *data);
+       
+      
+     
+    
+
+    
+     
+      PQresultInstanceData
+      
+       PQresultInstanceData
+      
+     
+     
+      
+       Returns the result's instanceData associated with proc, or NULL
+       if there is none.
+
+       
+        void *PQresultInstanceData(const PGresult *res, PGEventProc proc);
+       
+      
+     
+    
+   
+  
+
+  
+   Event Example
+
+   
+    Here is a skeleton example of managing private data associated with
+    libpq connections and results.
+   
+
+   
+/* required header for libpq events (note: includes libpq-fe.h) */
+#include <libpq-events.h>
+
+/* The instanceData */
+typedef struct
+{
+    int n;
+    char *str;
+} mydata;
+
+/* PGEventProc */
+static 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_RESULTDESTROY to myEventProc */
+    PQclear(res);
+    PQclear(res_copy);
+
+    /* Sends a PGEVT_CONNDESTROY to myEventProc */
+    PQfinish(conn);
+
+    return 0;
+}
+
+static 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 */
+}
+
+  
+
  
   Environment Variables
 
@@ -5263,7 +5940,7 @@ defaultNoticeProcessor(void *arg, const char *message)
    to inside libpq), you can use
    PQinitSSL(int) to tell libpq
    that the SSL library has already been initialized by your
-   application.  
+   application.
    
    See 
    url="http://h71000.www7.hp.com/doc/83final/BA554_90007/ch04.html">
index 62885fbb5e01ed6ede699355720c32b001208949..7bace86345d2bf07f451c937caf3f2a37a545190 100644 (file)
@@ -5,7 +5,7 @@
 # Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group
 # Portions Copyright (c) 1994, Regents of the University of California
 #
-# $PostgreSQL: pgsql/src/interfaces/libpq/Makefile,v 1.166 2008/04/16 14:19:56 adunstan Exp $
+# $PostgreSQL: pgsql/src/interfaces/libpq/Makefile,v 1.167 2008/09/17 04:31:08 tgl Exp $
 #
 #-------------------------------------------------------------------------
 
@@ -32,6 +32,7 @@ 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 \
+   libpq-events.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))
 
@@ -106,6 +107,7 @@ $(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'
@@ -114,7 +116,11 @@ 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'
+   rm -f '$(DESTDIR)$(includedir)/libpq-fe.h'
+   rm -f '$(DESTDIR)$(includedir)/libpq-events.h'
+   rm -f '$(DESTDIR)$(includedir_internal)/libpq-int.h'
+   rm -f '$(DESTDIR)$(includedir_internal)/pqexpbuffer.h'
+   rm -f '$(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.c strlcpy.c thread.c md5.c ip.c encnames.c wchar.c win32error.c pgsleep.c pthread.h libpq.rc
index f8809f841e4ad1a866380d692b11fbe5347f5712..c720efce4b97994cd28679531b46538228baf6f0 100644 (file)
@@ -1,4 +1,4 @@
-# $PostgreSQL: pgsql/src/interfaces/libpq/exports.txt,v 1.19 2008/03/19 00:39:33 ishii Exp $
+# $PostgreSQL: pgsql/src/interfaces/libpq/exports.txt,v 1.20 2008/09/17 04:31:08 tgl Exp $
 # Functions to be exported by libpq DLLs
 PQconnectdb               1
 PQsetdbLogin              2
@@ -140,4 +140,13 @@ lo_truncate               137
 PQconnectionUsedPassword  138
 pg_valid_server_encoding_id 139
 PQconnectionNeedsPassword 140
-lo_import_with_oid       141
+lo_import_with_oid        141
+PQcopyResult              142
+PQsetResultAttrs          143
+PQsetvalue                144
+PQresultAlloc             145
+PQregisterEventProc       146
+PQinstanceData            147
+PQsetInstanceData         148
+PQresultInstanceData      149
+PQresultSetInstanceData   150
index 5e687c15585fca08c01ab7452503aab500f44ad6..7e77c9a5c7ac62184900a37f179f7ce84a52fd0d 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *   $PostgreSQL: pgsql/src/interfaces/libpq/fe-connect.c,v 1.359 2008/05/29 22:02:44 tgl Exp $
+ *   $PostgreSQL: pgsql/src/interfaces/libpq/fe-connect.c,v 1.360 2008/09/17 04:31:08 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -1974,6 +1974,21 @@ makeEmptyPGconn(void)
 static void
 freePGconn(PGconn *conn)
 {
+   int         i;
+
+   /* let any event procs clean up their state data */
+   for (i = 0; i < conn->nEvents; i++)
+   {
+       PGEventConnDestroy evt;
+
+       evt.conn = conn;
+       (void) conn->events[i].proc(PGEVT_CONNDESTROY, &evt,
+                                   conn->events[i].passThrough);
+       free(conn->events[i].name);
+   }
+
+   if (conn->events)
+       free(conn->events);
    if (conn->pghost)
        free(conn->pghost);
    if (conn->pghostaddr)
@@ -2155,8 +2170,30 @@ PQreset(PGconn *conn)
    {
        closePGconn(conn);
 
-       if (connectDBStart(conn))
-           (void) connectDBComplete(conn);
+       if (connectDBStart(conn) && connectDBComplete(conn))
+       {
+           /*
+            * Notify event procs of successful reset.  We treat an event
+            * proc failure as disabling the connection ... good idea?
+            */
+           int i;
+
+           for (i = 0; i < conn->nEvents; i++)
+           {
+               PGEventConnReset evt;
+
+               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;
+               }
+           }
+       }
    }
 }
 
@@ -2190,7 +2227,36 @@ PostgresPollingStatusType
 PQresetPoll(PGconn *conn)
 {
    if (conn)
-       return PQconnectPoll(conn);
+   {
+       PostgresPollingStatusType status = PQconnectPoll(conn);
+
+       if (status == PGRES_POLLING_OK)
+       {
+           /*
+            * Notify event procs of successful reset.  We treat an event
+            * proc failure as disabling the connection ... good idea?
+            */
+           int i;
+
+           for (i = 0; i < conn->nEvents; i++)
+           {
+               PGEventConnReset evt;
+
+               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;
 }
index 149a0b73f6bb4efa4b8a0a696b0cab2b0ece94bd..7db303ce0085d00dfa0d8fb869403059e9307b08 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *   $PostgreSQL: pgsql/src/interfaces/libpq/fe-exec.c,v 1.197 2008/09/10 17:01:07 tgl Exp $
+ *   $PostgreSQL: pgsql/src/interfaces/libpq/fe-exec.c,v 1.198 2008/09/17 04:31:08 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -48,6 +48,7 @@ static int    static_client_encoding = PG_SQL_ASCII;
 static bool static_std_strings = false;
 
 
+static PGEvent *dupEvents(PGEvent *events, int count);
 static bool PQsendQueryStart(PGconn *conn);
 static int PQsendQueryGuts(PGconn *conn,
                const char *command,
@@ -63,6 +64,7 @@ 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);
 
 
 /* ----------------
@@ -128,13 +130,8 @@ static int PQsendDescribe(PGconn *conn, char desc_type,
  * 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.
+ *  errorMessage is copied.  Also, any PGEvents are copied from the conn.
  */
-
 PGresult *
 PQmakeEmptyPGresult(PGconn *conn, ExecStatusType status)
 {
@@ -154,6 +151,8 @@ PQmakeEmptyPGresult(PGconn *conn, ExecStatusType status)
    result->resultStatus = status;
    result->cmdStatus[0] = '\0';
    result->binary = 0;
+   result->events = NULL;
+   result->nEvents = 0;
    result->errMsg = NULL;
    result->errFields = NULL;
    result->null_field[0] = '\0';
@@ -181,6 +180,18 @@ PQmakeEmptyPGresult(PGconn *conn, ExecStatusType status)
                pqSetResultError(result, conn->errorMessage.data);
                break;
        }
+
+       /* copy events last; result must be valid if we need to PQclear */
+       if (conn->nEvents > 0)
+       {
+           result->events = dupEvents(conn->events, conn->nEvents);
+           if (!result->events)
+           {
+               PQclear(result);
+               return NULL;
+           }
+           result->nEvents = conn->nEvents;
+       }
    }
    else
    {
@@ -195,6 +206,301 @@ PQmakeEmptyPGresult(PGconn *conn, ExecStatusType status)
    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 no-op 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));
+
+   /* deep-copy the attribute names, and determine format */
+   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;
+
+       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, use 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, seeeing 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)
+{
+   PGresult *dest;
+   int i;
+
+   if (!src)
+       return NULL;
+
+   dest = PQmakeEmptyPGresult(NULL, PGRES_TUPLES_OK);
+   if (!dest)
+       return NULL;
+
+   /* Always copy these over.  Is cmdStatus really useful here? */
+   dest->client_encoding = src->client_encoding;
+   strcpy(dest->cmdStatus, src->cmdStatus);
+
+   /* Wants attrs? */
+   if (flags & (PG_COPYRES_ATTRS | PG_COPYRES_TUPLES))
+   {
+       if (!PQsetResultAttrs(dest, src->numAttributes, src->attDescs))
+       {
+           PQclear(dest);
+           return NULL;
+       }
+   }
+
+   /* Wants to copy tuples? */
+   if (flags & PG_COPYRES_TUPLES)
+   {
+       int tup, field;
+
+       for (tup = 0; tup < src->ntups; tup++)
+       {
+           for (field = 0; field < src->numAttributes; field++)
+           {
+               if (!PQsetvalue(dest, tup, field,
+                               src->tuples[tup][field].value,
+                               src->tuples[tup][field].len))
+               {
+                   PQclear(dest);
+                   return NULL;
+               }
+           }
+       }
+   }
+
+   /* Wants to copy notice hooks? */
+   if (flags & PG_COPYRES_NOTICEHOOKS)
+       dest->noticeHooks = src->noticeHooks;
+
+   /*
+    * Wants to copy PGEvents?  NB: this should be last, as we don't want
+    * to trigger RESULTDESTROY events on a useless PGresult.
+    */
+   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;
+   }
+
+   /* 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;
+}
+
+/*
+ * Copy an array of PGEvents (with no extra space for more)
+ * Does not duplicate the event instance data, sets this to NULL
+ */
+static PGEvent *
+dupEvents(PGEvent *events, int count)
+{
+   PGEvent *newEvents;
+   int i;
+
+   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 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;
+       }
+   }
+
+   return newEvents;
+}
+
+
+/*
+ * Sets the value for a tuple field.  The tup_num must be less than or
+ * equal to PQntuples(res).  If it is equal, a new tuple is created and
+ * added to the result.
+ * Returns a non-zero value for success and zero for failure.
+ */
+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 < 0 || 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])
+   {
+       PGresAttValue *tup;
+       int i;
+
+       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];
+
+   /* treat either NULL_LEN or NULL value pointer as a NULL field */
+   if (len == NULL_LEN || value == NULL)
+   {
+       attval->len = NULL_LEN;
+       attval->value = res->null_field;
+   }
+   else if (len <= 0)
+   {
+       attval->len = 0;
+       attval->value = res->null_field;
+   }
+   else
+   {
+       attval->value = (char *) pqResultAlloc(res, len + 1, TRUE);
+       if (!attval->value)
+           return FALSE;
+       attval->len = len;
+       memcpy(attval->value, value, len);
+       attval->value[len] = '\0';
+   }
+
+   return TRUE;
+}
+
+/*
+ * pqResultAlloc - exported routine to allocate local storage in a PGresult.
+ *
+ * We force all such allocations to be maxaligned, since we don't know
+ * whether the value might be binary.
+ */
+void *
+PQresultAlloc(PGresult *res, size_t nBytes)
+{
+   return pqResultAlloc(res, nBytes, TRUE);
+}
+
 /*
  * pqResultAlloc -
  *     Allocate subsidiary storage for a PGresult.
@@ -353,10 +659,24 @@ void
 PQclear(PGresult *res)
 {
    PGresult_data *block;
+   int i;
 
    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);
+
    /* Free all the subsidiary blocks */
    while ((block = res->curBlock) != NULL)
    {
@@ -373,6 +693,8 @@ PQclear(PGresult *res)
    res->tuples = NULL;
    res->paramDescs = NULL;
    res->errFields = NULL;
+   res->events = NULL;
+   res->nEvents = 0;
    /* res->curBlock was zeroed out earlier */
 
    /* Free the PGresult structure itself */
@@ -1270,6 +1592,29 @@ PQgetResult(PGconn *conn)
            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;
+           }
+       }
+   }
+
    return res;
 }
 
diff --git a/src/interfaces/libpq/libpq-events.c b/src/interfaces/libpq/libpq-events.c
new file mode 100644 (file)
index 0000000..7d3d1cb
--- /dev/null
@@ -0,0 +1,176 @@
+/*-------------------------------------------------------------------------
+ *
+ * libpq-events.c
+ *   functions for supporting the libpq "events" API
+ *
+ * Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *   $PostgreSQL: pgsql/src/interfaces/libpq/libpq-events.c,v 1.1 2008/09/17 04:31:08 tgl Exp $
+ *
+ *-------------------------------------------------------------------------
+ */
+#include "postgres_fe.h"
+
+#include "libpq-fe.h"
+#include "libpq-int.h"
+
+
+/*
+ * Registers an event proc with the given PGconn.
+ *
+ * The same proc can't be registered more than once in a PGconn.  This
+ * restriction is required because we use the proc address to identify
+ * the event for purposes such as PQinstanceData().
+ *
+ * The name argument is used within error messages to aid in debugging.
+ * A name must be supplied, but it needn't be unique.  The string is
+ * copied, so the passed value needn't be long-lived.
+ *
+ * The passThrough argument is an application specific pointer and can be set
+ * to NULL if not required.  It is passed through to the event proc whenever
+ * the event proc is called, and is not otherwise touched by libpq.
+ *
+ * The function returns a non-zero if successful.  If the function fails,
+ * zero is returned.
+ */
+int
+PQregisterEventProc(PGconn *conn, PGEventProc proc,
+                   const char *name, void *passThrough)
+{
+   int i;
+   PGEventRegister regevt;
+
+   if (!proc || !conn || !name || !*name)
+       return FALSE;           /* bad arguments */
+
+   for (i = 0; i < conn->nEvents; i++)
+   {
+       if (conn->events[i].proc == proc)
+           return FALSE;       /* already registered */
+   }
+
+   if (conn->nEvents >= conn->eventArraySize)
+   {
+       PGEvent *e;
+       int newSize;
+
+       newSize = conn->eventArraySize ? conn->eventArraySize * 2 : 8;
+       if (conn->events)
+           e = (PGEvent *) realloc(conn->events, newSize * sizeof(PGEvent));
+       else
+           e = (PGEvent *) malloc(newSize * sizeof(PGEvent));
+
+       if (!e)
+           return FALSE;
+
+       conn->eventArraySize = newSize;
+       conn->events = e;
+   }
+
+   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->nEvents++;
+
+   regevt.conn = conn;
+   if (!proc(PGEVT_REGISTER, ®evt, passThrough))
+   {
+       conn->nEvents--;
+       free(conn->events[conn->nEvents].name);
+       return FALSE;
+   }
+
+   return TRUE;
+}
+
+/*
+ * Set some "instance data" for an event within a PGconn.
+ * Returns nonzero on success, zero on failure.
+ */
+int
+PQsetInstanceData(PGconn *conn, PGEventProc proc, void *data)
+{
+   int i;
+
+   if (!conn || !proc)
+       return FALSE;
+
+   for (i = 0; i < conn->nEvents; i++)
+   {
+       if (conn->events[i].proc == proc)
+       {
+           conn->events[i].data = data;
+           return TRUE;
+       }
+   }
+
+   return FALSE;
+}
+
+/*
+ * Obtain the "instance data", if any, for the event.
+ */
+void *
+PQinstanceData(const PGconn *conn, PGEventProc proc)
+{
+   int i;
+
+   if (!conn || !proc)
+       return NULL;
+
+   for (i = 0; i < conn->nEvents; i++)
+   {
+       if (conn->events[i].proc == proc)
+           return conn->events[i].data;
+   }
+
+   return NULL;
+}
+
+/*
+ * Set some "instance data" for an event within a PGresult.
+ * Returns nonzero on success, zero on failure.
+ */
+int
+PQresultSetInstanceData(PGresult *result, PGEventProc proc, void *data)
+{
+   int i;
+
+   if (!result || !proc)
+       return FALSE;
+
+   for (i = 0; i < result->nEvents; i++)
+   {
+       if (result->events[i].proc == proc)
+       {
+           result->events[i].data = data;
+           return TRUE;
+       }
+   }
+
+   return FALSE;
+}
+
+/*
+ * Obtain the "instance data", if any, for the event.
+ */
+void *
+PQresultInstanceData(const PGresult *result, PGEventProc proc)
+{
+   int i;
+
+   if (!result || !proc)
+       return NULL;
+
+   for (i = 0; i < result->nEvents; i++)
+       if (result->events[i].proc == proc)
+           return result->events[i].data;
+
+   return NULL;
+}
diff --git a/src/interfaces/libpq/libpq-events.h b/src/interfaces/libpq/libpq-events.h
new file mode 100644 (file)
index 0000000..33e2d5b
--- /dev/null
@@ -0,0 +1,91 @@
+/*-------------------------------------------------------------------------
+ *
+ * libpq-events.h
+ *   This file contains definitions that are useful to applications
+ *   that invoke the libpq "events" API, but are not interesting to
+ *   ordinary users of libpq.
+ *
+ * Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * $PostgreSQL: pgsql/src/interfaces/libpq/libpq-events.h,v 1.1 2008/09/17 04:31:08 tgl Exp $
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#ifndef LIBPQ_EVENTS_H
+#define LIBPQ_EVENTS_H
+
+#include "libpq-fe.h"
+
+#ifdef __cplusplus
+extern     "C"
+{
+#endif
+
+/* Callback Event Ids */
+typedef enum
+{
+   PGEVT_REGISTER,
+   PGEVT_CONNRESET,
+   PGEVT_CONNDESTROY,
+   PGEVT_RESULTCREATE,
+   PGEVT_RESULTCOPY,
+   PGEVT_RESULTDESTROY
+} PGEventId;
+
+typedef struct
+{
+   const PGconn *conn;
+} PGEventRegister;
+
+typedef struct
+{
+   const PGconn *conn;
+} PGEventConnReset;
+
+typedef struct
+{
+   const PGconn *conn;
+} PGEventConnDestroy;
+
+typedef struct
+{
+   const PGconn *conn;
+   PGresult *result;
+} PGEventResultCreate;
+
+typedef struct
+{
+   const PGresult *src;
+   PGresult *dest;
+} PGEventResultCopy;
+
+typedef struct
+{
+   const PGresult *result;
+} PGEventResultDestroy;
+
+typedef int (*PGEventProc) (PGEventId evtId, void *evtInfo, void *passThrough);
+
+/* Registers an event proc with the given PGconn. */
+extern int PQregisterEventProc(PGconn *conn, PGEventProc proc,
+                               const char *name, void *passThrough);
+
+/* Sets the PGconn instance data for the provided proc to data. */
+extern int PQsetInstanceData(PGconn *conn, PGEventProc proc, void *data);
+
+/* Gets the PGconn instance data for the provided proc. */
+extern void *PQinstanceData(const PGconn *conn, PGEventProc proc);
+
+/* 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);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LIBPQ_EVENTS_H */
index 53d79b059f6228083cc397436c4d3e732e4c1357..f923b9684085df9846aebc0db34e62336db3c63e 100644 (file)
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/interfaces/libpq/libpq-fe.h,v 1.142 2008/03/19 00:39:33 ishii Exp $
+ * $PostgreSQL: pgsql/src/interfaces/libpq/libpq-fe.h,v 1.143 2008/09/17 04:31:08 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -28,6 +28,14 @@ extern       "C"
  */
 #include "postgres_ext.h"
 
+/*
+ * Option flags 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
@@ -192,6 +200,21 @@ typedef struct
    }           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
  * ----------------
@@ -430,13 +453,12 @@ extern void PQfreemem(void *ptr);
 /* Note: depending on this is deprecated; use PQconnectionNeedsPassword(). */
 #define PQnoPasswordSupplied   "fe_sendauth: no password supplied\n"
 
-/*
- * 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.
- */
+/* Create and manipulate PGresults */
 extern PGresult *PQmakeEmptyPGresult(PGconn *conn, ExecStatusType status);
-
+extern PGresult *PQcopyResult(const PGresult *src, int flags);
+extern int PQsetResultAttrs(PGresult *res, int numAttributes, PGresAttDesc *attDescs);
+extern void *PQresultAlloc(PGresult *res, size_t nBytes);
+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,
index fd94952f1802e2f294a50c73801b7f162d9f1d9e..fd29c092148f15010bc206281ba19b7fbbd83269 100644 (file)
@@ -12,7 +12,7 @@
  * Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/interfaces/libpq/libpq-int.h,v 1.131 2008/05/29 22:02:44 tgl Exp $
+ * $PostgreSQL: pgsql/src/interfaces/libpq/libpq-int.h,v 1.132 2008/09/17 04:31:08 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -22,6 +22,7 @@
 
 /* We assume libpq-fe.h has already been included. */
 #include "postgres_fe.h"
+#include "libpq-events.h"
 
 #include 
 #include 
@@ -100,19 +101,6 @@ union pgresult_data
    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
 {
@@ -162,6 +150,14 @@ typedef struct
    void       *noticeProcArg;
 } PGNoticeHooks;
 
+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 */
+} PGEvent;
+
 struct pg_result
 {
    int         ntups;
@@ -182,6 +178,8 @@ struct pg_result
     * on the PGresult don't have to reference the PGconn.
     */
    PGNoticeHooks noticeHooks;
+   PGEvent    *events;
+   int         nEvents;
    int         client_encoding;    /* encoding id */
 
    /*
@@ -303,6 +301,11 @@ struct pg_conn
    /* Callback procedures for notice message processing */
    PGNoticeHooks noticeHooks;
 
+   /* Event procs registered via PQregisterEventProc */
+   PGEvent    *events;         /* expandable array of event data */
+   int         nEvents;        /* number of active events */
+   int         eventArraySize; /* allocated array size */
+
    /* Status indicators */
    ConnStatusType status;
    PGAsyncStatusType asyncStatus;
index 4355ec9a28b847a3df98ee46bf90aafd1138e73c..dcbe5b5e01b103d997c5edbe2f8a36cd0abaa089 100644 (file)
@@ -3,7 +3,7 @@ package Install;
 #
 # Package that provides 'make install' functionality for msvc builds
 #
-# $PostgreSQL: pgsql/src/tools/msvc/Install.pm,v 1.30 2008/09/05 16:54:39 momjian Exp $
+# $PostgreSQL: pgsql/src/tools/msvc/Install.pm,v 1.31 2008/09/17 04:31:08 tgl Exp $
 #
 use strict;
 use warnings;
@@ -393,7 +393,9 @@ sub CopyIncludeFiles
     lcopy('src/include/libpq/libpq-fs.h', $target . '/include/libpq/')
       || croak 'Could not copy libpq-fs.h';
 
-    CopyFiles('Libpq headers', $target . '/include/', 'src/interfaces/libpq/', 'libpq-fe.h');
+    CopyFiles('Libpq headers',
+         $target . '/include/', 'src/interfaces/libpq/',
+         'libpq-fe.h', 'libpq-events.h');
     CopyFiles(
         'Libpq internal headers',
         $target .'/include/internal/',