summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--doc/src/sgml/libpq.sgml745
-rw-r--r--src/interfaces/libpq/Makefile10
-rw-r--r--src/interfaces/libpq/exports.txt13
-rw-r--r--src/interfaces/libpq/fe-connect.c74
-rw-r--r--src/interfaces/libpq/fe-exec.c359
-rw-r--r--src/interfaces/libpq/libpq-events.c176
-rw-r--r--src/interfaces/libpq/libpq-events.h91
-rw-r--r--src/interfaces/libpq/libpq-fe.h36
-rw-r--r--src/interfaces/libpq/libpq-int.h31
-rw-r--r--src/tools/msvc/Install.pm6
10 files changed, 1469 insertions, 72 deletions
diff --git a/doc/src/sgml/libpq.sgml b/doc/src/sgml/libpq.sgml
index deb052e5c4..2db369e906 100644
--- a/doc/src/sgml/libpq.sgml
+++ b/doc/src/sgml/libpq.sgml
@@ -1,4 +1,4 @@
-<!-- $PostgreSQL: pgsql/doc/src/sgml/libpq.sgml,v 1.260 2008/06/27 02:44:31 momjian Exp $ -->
+<!-- $PostgreSQL: pgsql/doc/src/sgml/libpq.sgml,v 1.261 2008/09/17 04:31:08 tgl Exp $ -->
<chapter id="libpq">
<title><application>libpq</application> - C Library</title>
@@ -2063,38 +2063,6 @@ PGresult *PQdescribePortal(PGconn *conn, const char *portalName);
</para>
</listitem>
</varlistentry>
-
- <varlistentry>
- <term>
- <function>PQmakeEmptyPGresult</function>
- <indexterm>
- <primary>PQmakeEmptyPGresult</primary>
- </indexterm>
- </term>
-
- <listitem>
- <para>
- Constructs an empty <structname>PGresult</structname> object with the given status.
- <synopsis>
- PGresult *PQmakeEmptyPGresult(PGconn *conn, ExecStatusType status);
- </synopsis>
- </para>
-
- <para>
- This is <application>libpq</>'s internal function to allocate and
- initialize an empty <structname>PGresult</structname> 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
- <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>.
- 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>
</variablelist>
</para>
</sect2>
@@ -4598,6 +4566,170 @@ char *pg_encoding_to_char(int <replaceable>encoding_id</replaceable>);
</para>
</listitem>
</varlistentry>
+
+ <varlistentry>
+ <term>
+ <function>PQmakeEmptyPGresult</function>
+ <indexterm>
+ <primary>PQmakeEmptyPGresult</primary>
+ </indexterm>
+ </term>
+
+ <listitem>
+ <para>
+ Constructs an empty <structname>PGresult</structname> object with the given status.
+ <synopsis>
+ PGresult *PQmakeEmptyPGresult(PGconn *conn, ExecStatusType status);
+ </synopsis>
+ </para>
+
+ <para>
+ This is <application>libpq</>'s internal function to allocate and
+ initialize an empty <structname>PGresult</structname> 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
+ <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>
+
+ <varlistentry>
+ <term>
+ <function>PQcopyResult</function>
+ <indexterm>
+ <primary>PQcopyResult</primary>
+ </indexterm>
+ </term>
+
+ <listitem>
+ <para>
+ Makes a copy of a <structname>PGresult</structname> object. The copy is
+ not linked to the source result in any way and
+ <function>PQclear</function> must be called when the copy is no longer
+ needed. If the function fails, NULL is returned.
+
+ <synopsis>
+ PGresult *PQcopyResult(const PGresult *src, int flags);
+ </synopsis>
+ </para>
+
+ <para>
+ This is not intended to make an exact copy. The returned result is
+ always put into <literal>PGRES_TUPLES_OK</literal> status, and does not
+ copy any error message in the source. (It does copy the command status
+ string, however.) The <parameter>flags</parameter> argument determines
+ what else is copied. It is a bitwise OR of several flags.
+ <literal>PG_COPYRES_ATTRS</literal> specifies copying the source
+ result's attributes (column definitions).
+ <literal>PG_COPYRES_TUPLES</literal> specifies copying the source
+ result's tuples. (This implies copying the attributes, too.)
+ <literal>PG_COPYRES_NOTICEHOOKS</literal> specifies
+ copying the source result's notify hooks.
+ <literal>PG_COPYRES_EVENTS</literal> specifies copying the source
+ result's events. (But any instance data associated with the source
+ is not copied.)
+ </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.
+ If the <parameter>attDescs</parameter> pointer is 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. However, 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 one tuple at a time. But any
+ field of any existing tuple can be modified in any order. If a value at
+ <parameter>field_num</parameter> already exists, 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 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.
+ </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>. The result is
+ guaranteed to be adequately aligned for any type of data,
+ just as for <function>malloc</>.
+ </para>
+ </listitem>
+ </varlistentry>
+
</variablelist>
</sect1>
@@ -4711,6 +4843,551 @@ defaultNoticeProcessor(void *arg, const char *message)
</sect1>
+ <sect1 id="libpq-events">
+ <title>Event System</title>
+
+ <para>
+ <application>libpq</application>'s event system is designed to notify
+ registered event handlers about interesting
+ <application>libpq</application> events, such as the creation or
+ destruction of <structname>PGconn</structname> and
+ <structname>PGresult</structname> objects. A principal use case is that
+ this allows applications to associate their own data with a
+ <structname>PGconn</structname> or <structname>PGresult</structname>
+ and ensure that that data is freed at an appropriate time.
+ </para>
+
+ <para>
+ Each registered event handler is associated with two pieces of data,
+ known to <application>libpq</application> only as opaque <literal>void *</>
+ pointers. There is a <firstterm>passthrough</> pointer that is provided
+ by the application when the event handler is registered with a
+ <structname>PGconn</>. The passthrough pointer never changes for the
+ life of the <structname>PGconn</> and all <structname>PGresult</>s
+ generated from it; so if used, it must point to long-lived data.
+ In addition there is an <firstterm>instance data</> pointer, which starts
+ out NULL in every <structname>PGconn</> and <structname>PGresult</>.
+ This pointer can be manipulated using the
+ <function>PQinstanceData</function>,
+ <function>PQsetInstanceData</function>,
+ <function>PQresultInstanceData</function> and
+ <function>PQsetResultInstanceData</function> functions. Note that
+ unlike the passthrough pointer, instance data of a <structname>PGconn</>
+ is not automatically inherited by <structname>PGresult</>s created from
+ it. <application>libpq</application> does not know what passthrough
+ and instance data pointers point to (if anything) and will never attempt
+ to free them &mdash; that is the responsibility of the event handler.
+ </para>
+
+ <sect2 id="libpq-events-types">
+ <title>Event Types</title>
+
+ <para>
+ The enum <literal>PGEventId</> names the types of events handled by
+ the event system. All its values have names beginning with
+ <literal>PGEVT</literal>. For each event type, there is a corresponding
+ event info structure that carries the parameters passed to the event
+ handlers. The event types are:
+ </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 event handler 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 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>
+ <listitem>
+ <para>
+ The connection reset event is fired on completion of
+ <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;
+} PGEventConnReset;
+ </synopsis>
+
+ 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>
+ <listitem>
+ <para>
+ The connection destroy event is fired in response to
+ <function>PQfinish</function>. 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.
+
+ <synopsis>
+typedef struct
+{
+ const PGconn *conn;
+} PGEventConnDestroy;
+ </synopsis>
+
+ When a <literal>PGEVT_CONNDESTROY</literal> event is received, the
+ <parameter>evtInfo</parameter> pointer should be cast to a
+ <structname>PGEventConnDestroy *</structname>. This event is fired
+ prior to <function>PQfinish</function> performing any other 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 creation event is fired in response to any query execution
+ function that generates a result, including
+ <function>PQgetResult</function>. This event will only be fired after
+ the result has been created successfully.
+
+ <synopsis>
+typedef struct
+{
+ const PGconn *conn;
+ PGresult *result;
+} PGEventResultCreate;
+ </synopsis>
+
+ When a <literal>PGEVT_RESULTCREATE</literal> event is received, the
+ <parameter>evtInfo</parameter> pointer should be cast to a
+ <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;
+} PGEventResultCopy;
+ </synopsis>
+
+ When a <literal>PGEVT_RESULTCOPY</literal> event is received, the
+ <parameter>evtInfo</parameter> pointer should be cast to a
+ <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>
+ <listitem>
+ <para>
+ The result destroy event is fired in response to a
+ <function>PQclear</function>. 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.
+
+ <synopsis>
+typedef struct
+{
+ const PGresult *result;
+} PGEventResultDestroy;
+ </synopsis>
+
+ When a <literal>PGEVT_RESULTDESTROY</literal> event is received, the
+ <parameter>evtInfo</parameter> pointer should be cast to a
+ <structname>PGEventResultDestroy *</structname>. This event is fired
+ prior to <function>PQclear</function> performing any other 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>
+ </sect2>
+
+ <sect2 id="libpq-events-proc">
+ <title>Event Callback Procedure</title>
+
+ <variablelist>
+ <varlistentry>
+ <term>
+ <literal>PGEventProc</literal>
+ <indexterm>
+ <primary>PGEventProc</primary>
+ </indexterm>
+ </term>
+
+ <listitem>
+ <para>
+ <literal>PGEventProc</literal> 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
+
+ <synopsis>
+int eventproc(PGEventId evtId, void *evtInfo, void *passThrough)
+ </synopsis>
+
+ The <parameter>evtId</parameter> parameter indicates which
+ <literal>PGEVT</literal> event occurred. The
+ <parameter>evtInfo</parameter> pointer must be cast to the appropriate
+ structure type to obtain further information about the event.
+ The <parameter>passThrough</parameter> parameter is the pointer
+ provided to <function>PQregisterEventProc</function> when the event
+ procedure was registered. The function should return a non-zero value
+ if it succeeds and zero if it fails.
+ </para>
+
+ <para>
+ A particular event procedure can be registered only once in any
+ <structname>PGconn</>. This is because the address of the procedure
+ is used as a lookup key to identify the associated instance data.
+ </para>
+ </listitem>
+ </varlistentry>
+ </variablelist>
+ </sect2>
+
+ <sect2 id="libpq-events-funcs">
+ <title>Event Support 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>
+ An event procedure must be registered once on each
+ <structname>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.
+ </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 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 <structname>PGconn</>, so what is passed need not be
+ long-lived. 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>PQsetInstanceData</function>
+ <indexterm>
+ <primary>PQsetInstanceData</primary>
+ </indexterm>
+ </term>
+ <listitem>
+ <para>
+ 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.)
+
+ <synopsis>
+ int PQsetInstanceData(PGconn *conn, PGEventProc proc, void *data);
+ </synopsis>
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term>
+ <function>PQinstanceData</function>
+ <indexterm>
+ <primary>PQinstanceData</primary>
+ </indexterm>
+ </term>
+ <listitem>
+ <para>
+ Returns the conn's instanceData associated with proc, or NULL
+ if there is none.
+
+ <synopsis>
+ void *PQinstanceData(const PGconn *conn, 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 returns non-zero
+ for success and zero for failure. (Failure is only possible if the
+ proc has not been properly registered in the result.)
+
+ <synopsis>
+ int PQresultSetInstanceData(PGresult *res, 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 with proc, or NULL
+ if there is none.
+
+ <synopsis>
+ void *PQresultInstanceData(const PGresult *res, PGEventProc proc);
+ </synopsis>
+ </para>
+ </listitem>
+ </varlistentry>
+ </variablelist>
+ </sect2>
+
+ <sect2 id="libpq-events-example">
+ <title>Event Example</title>
+
+ <para>
+ Here is a skeleton example of managing private data associated with
+ libpq connections and results.
+ </para>
+
+ <programlisting>
+/* required header for libpq events (note: includes libpq-fe.h) */
+#include &lt;libpq-events.h&gt;
+
+/* 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-&gt;conn);
+
+ /* associate app specific data with connection */
+ PQsetInstanceData(e-&gt;conn, myEventProc, data);
+ break;
+ }
+
+ case PGEVT_CONNRESET:
+ {
+ PGEventConnReset *e = (PGEventConnReset *)evtInfo;
+ mydata *data = PQinstanceData(e-&gt;conn, myEventProc);
+
+ if (data)
+ memset(data, 0, sizeof(mydata));
+ break;
+ }
+
+ case PGEVT_CONNDESTROY:
+ {
+ PGEventConnDestroy *e = (PGEventConnDestroy *)evtInfo;
+ mydata *data = PQinstanceData(e-&gt;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-&gt;conn, myEventProc);
+ mydata *res_data = dup_mydata(conn_data);
+
+ /* associate app specific data with result (copy it from conn) */
+ PQsetResultInstanceData(e-&gt;result, myEventProc, res_data);
+ break;
+ }
+
+ case PGEVT_RESULTCOPY:
+ {
+ PGEventResultCopy *e = (PGEventResultCopy *)evtInfo;
+ mydata *src_data = PQresultInstanceData(e-&gt;src, myEventProc);
+ mydata *dest_data = dup_mydata(src_data);
+
+ /* associate app specific data with result (copy it from a result) */
+ PQsetResultInstanceData(e-&gt;dest, myEventProc, dest_data);
+ break;
+ }
+
+ case PGEVT_RESULTDESTROY:
+ {
+ PGEventResultDestroy *e = (PGEventResultDestroy *)evtInfo;
+ mydata *data = PQresultInstanceData(e-&gt;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-envars">
<title>Environment Variables</title>
@@ -5263,7 +5940,7 @@ defaultNoticeProcessor(void *arg, const char *message)
to inside <application>libpq</application>), you can use
<function>PQinitSSL(int)</> to tell <application>libpq</application>
that the <acronym>SSL</> library has already been initialized by your
- application.
+ application.
<!-- If this URL changes replace it with a URL to www.archive.org. -->
See <ulink
url="http://h71000.www7.hp.com/doc/83final/BA554_90007/ch04.html"></ulink>
diff --git a/src/interfaces/libpq/Makefile b/src/interfaces/libpq/Makefile
index 62885fbb5e..7bace86345 100644
--- a/src/interfaces/libpq/Makefile
+++ b/src/interfaces/libpq/Makefile
@@ -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
diff --git a/src/interfaces/libpq/exports.txt b/src/interfaces/libpq/exports.txt
index f8809f841e..c720efce4b 100644
--- a/src/interfaces/libpq/exports.txt
+++ b/src/interfaces/libpq/exports.txt
@@ -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
diff --git a/src/interfaces/libpq/fe-connect.c b/src/interfaces/libpq/fe-connect.c
index 5e687c1558..7e77c9a5c7 100644
--- a/src/interfaces/libpq/fe-connect.c
+++ b/src/interfaces/libpq/fe-connect.c
@@ -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;
}
diff --git a/src/interfaces/libpq/fe-exec.c b/src/interfaces/libpq/fe-exec.c
index 149a0b73f6..7db303ce00 100644
--- a/src/interfaces/libpq/fe-exec.c
+++ b/src/interfaces/libpq/fe-exec.c
@@ -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
{
@@ -196,6 +207,301 @@ PQmakeEmptyPGresult(PGconn *conn, ExecStatusType status)
}
/*
+ * 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
index 0000000000..7d3d1cb26c
--- /dev/null
+++ b/src/interfaces/libpq/libpq-events.c
@@ -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, &regevt, 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
index 0000000000..33e2d5b046
--- /dev/null
+++ b/src/interfaces/libpq/libpq-events.h
@@ -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 */
diff --git a/src/interfaces/libpq/libpq-fe.h b/src/interfaces/libpq/libpq-fe.h
index 53d79b059f..f923b96840 100644
--- a/src/interfaces/libpq/libpq-fe.h
+++ b/src/interfaces/libpq/libpq-fe.h
@@ -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
@@ -193,6 +201,21 @@ typedef struct
} 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,
diff --git a/src/interfaces/libpq/libpq-int.h b/src/interfaces/libpq/libpq-int.h
index fd94952f18..fd29c09214 100644
--- a/src/interfaces/libpq/libpq-int.h
+++ b/src/interfaces/libpq/libpq-int.h
@@ -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 <time.h>
#include <sys/types.h>
@@ -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;
diff --git a/src/tools/msvc/Install.pm b/src/tools/msvc/Install.pm
index 4355ec9a28..dcbe5b5e01 100644
--- a/src/tools/msvc/Install.pm
+++ b/src/tools/msvc/Install.pm
@@ -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/',