diff options
-rw-r--r-- | doc/src/sgml/libpq.sgml | 745 | ||||
-rw-r--r-- | src/interfaces/libpq/Makefile | 10 | ||||
-rw-r--r-- | src/interfaces/libpq/exports.txt | 13 | ||||
-rw-r--r-- | src/interfaces/libpq/fe-connect.c | 74 | ||||
-rw-r--r-- | src/interfaces/libpq/fe-exec.c | 359 | ||||
-rw-r--r-- | src/interfaces/libpq/libpq-events.c | 176 | ||||
-rw-r--r-- | src/interfaces/libpq/libpq-events.h | 91 | ||||
-rw-r--r-- | src/interfaces/libpq/libpq-fe.h | 36 | ||||
-rw-r--r-- | src/interfaces/libpq/libpq-int.h | 31 | ||||
-rw-r--r-- | src/tools/msvc/Install.pm | 6 |
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 — 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 <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 */ +} +</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, ®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 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/', |