From: Andrew Chernow <ac@esilo.com>
To: Tom Lane <tgl@sss.pgh.pa.us>
Cc: Alvaro Herrera <alvherre@commandprompt.com>
Cc: PostgreSQL-development <pgsql-hackers@postgresql.org>
Cc: Merlin Moncure <mmoncure@gmail.com>
Subject: Re: [PATCHES] libpq events patch (with sgml docs)
Date: Tue, 16 Sep 2008 23:59:44 -0400
Message-ID: <48D080B0.6070104@esilo.com> (raw)
In-Reply-To: <28673.1221622817@sss.pgh.pa.us>
References: <48C18533.2000204@esilo.com>
<20080905192215.GK4353@alvh.no-ip.org>
<48C18B48.509@esilo.com>
<20080905194853.GL4353@alvh.no-ip.org>
<48C191A7.3000205@esilo.com>
<20080905201504.GM4353@alvh.no-ip.org>
<48C19759.90402@esilo.com>
<27615.1221619326@sss.pgh.pa.us>
<48D072E3.6010703@esilo.com>
<28673.1221622817@sss.pgh.pa.us>
Tom Lane wrote:
>> It is odd. Actually, it looks like a bug to me. PQgetResult is the
>> behavior we were after, don't trigger the event if the creation
>> failed. Same thing occurs during a conn reset. Looks like PQclear
>> needs to check resultStatus before it triggers RESULTDESTROY events.
>
> If you did that, then you WOULD have a bug. Consider case where you
> successfully init two events, and the third one fails. You'll now
> change the result's status to ERROR. If you don't RESULTDESTROY
> then the first two events will leak whatever storage they allocated.
>
> The reason I suggested not being conditional about the init call was
> to reduce the probability of bugs of similar ilks associated with
> an event proc assuming that it could only get a DESTROY call for
> something it had seen CREATEd --- for example, if it were frobbing
> a reference count for some long-lived data, it could very easily
> screw up the reference count that way. I suppose that the risk of
> an earlier event proc failing means it has to cope with that case
> anyway, but I don't think it's a particularly good idea to have
> arbitrary asymmetries increasing the odds of a problem.
>
> regards, tom lane
>
>
Yeah. Good point. I fixed it to always fire events.
--
Andrew Chernow
eSilo, LLC
every bit counts
http://www.esilo.com/Attachments:
libpq-events.tgz (application/x-compressed, 13.1K) downloadlibpq-events.patch (text/plain, 51.0K) download
Index: doc/src/sgml/libpq.sgml
===================================================================
RCS file: /projects/cvsroot/pgsql/doc/src/sgml/libpq.sgml,v
retrieving revision 1.260
diff -C6 -r1.260 libpq.sgml
*** doc/src/sgml/libpq.sgml 27 Jun 2008 02:44:31 -0000 1.260
--- doc/src/sgml/libpq.sgml 17 Sep 2008 03:54:20 -0000
***************
*** 2092,2103 ****
--- 2092,2222 ----
Note that <function>PQclear</function> should eventually be called
on the object, just as with a <structname>PGresult</structname>
returned by <application>libpq</application> itself.
</para>
</listitem>
</varlistentry>
+ + <varlistentry>+ <term>+ <function>PQcopyResult</function>+ <indexterm>+ <primary>PQcopyResult</primary>+ </indexterm>+ </term>+ + <listitem>+ <para>+ Makes a copy of a <structname>PGresult</structname> object.+ <synopsis>+ PGresult *PQcopyResult(const PGresult *src, int flags);+ </synopsis>+ </para>+ + <para>+ The returned result is always put into <literal>PGRES_TUPLES_OK</literal> status.+ It is not linked to the source result in any way and+ <function>PQclear</function> must be called when the result is no longer needed.+ If the function fails, NULL is returned.+ </para>+ + <para>+ Optionally, the <parameter>flags</parameter> argument can be used to copy+ more parts of the result. <literal>PG_COPYRES_ATTRS</literal> will copy the+ source result's attributes. <literal>PG_COPYRES_TUPLES</literal> will copy+ the source result's tuples. This implies copying the attrs, being how the+ attrs are needed by the tuples. <literal>PG_COPYRES_EVENTS</literal> will+ copy the source result's events. <literal>PG_COPYRES_NOTICEHOOKS</literal>+ will copy the source result's notify hooks.+ </para>+ </listitem>+ </varlistentry>+ + <varlistentry>+ <term>+ <function>PQsetResultAttrs</function>+ <indexterm>+ <primary>PQsetResultAttrs</primary>+ </indexterm>+ </term>+ + <listitem>+ <para>+ Sets the attributes of a <structname>PGresult</structname> object.+ <synopsis>+ int PQsetResultAttrs(PGresult *res, int numAttributes, PGresAttDesc *attDescs);+ </synopsis>+ </para>+ + <para>+ The provided <parameter>attDescs</parameter> are copied into the result, thus the+ <parameter>attDescs</parameter> are no longer needed by <parameter>res</parameter> after+ the function returns. If the <parameter>attDescs</parameter> are NULL or+ <parameter>numAttributes</parameter> is less than one, the request is ignored and+ the function succeeds. If <parameter>res</parameter> already contains attributes,+ the function will fail. If the function fails, the return value is zero. If the+ function succeeds, the return value is non-zero.+ </para>+ </listitem>+ </varlistentry>+ + <varlistentry>+ <term>+ <function>PQsetvalue</function>+ <indexterm>+ <primary>PQsetvalue</primary>+ </indexterm>+ </term>+ + <listitem>+ <para>+ Sets a tuple field value of a <structname>PGresult</structname> object.+ <synopsis>+ int PQsetvalue(PGresult *res, int tup_num, int field_num, char *value, int len);+ </synopsis>+ </para>+ + <para>+ The function will automatically grow the result's internal tuples array as needed.+ The <parameter>tup_num</parameter> argument must be less than or equal to+ <function>PQntuples</function>, meaning this function can only grow the+ tuples array in order one tuple at a time. Although, any field from any+ existing tuple can be modified in any order. If a value at <parameter>field_num</parameter>+ already eixsts, it will be overwritten. If <parameter>len</parameter> is <literal>-1</literal> or+ <parameter>value</parameter> is <literal>NULL</literal>, the field value will+ be set to an SQL <literal>NULL</literal>. The <parameter>value</parameter> is copied+ into result's private storage, thus no longer needed by <parameter>res</parameter> after+ the function returns. If the function fails, the return value+ is zero. If the function succeeds, the return value is non-zero.+ </para>+ </listitem>+ </varlistentry>+ + <varlistentry>+ <term>+ <function>PQresultAlloc</function>+ <indexterm>+ <primary>PQresultAlloc</primary>+ </indexterm>+ </term>+ + <listitem>+ <para>+ Allocate subsidiary storage for a <structname>PGresult</structname> object.+ <synopsis>+ void *PQresultAlloc(PGresult *res, size_t nBytes);+ </synopsis>+ </para>+ + <para>+ Any memory allocated with this function, will be freed when+ <parameter>res</parameter> is cleared. If the function fails,+ the return value is <literal>NULL</literal>.+ </para>+ </listitem>+ </varlistentry>
</variablelist>
</para>
</sect2>
<sect2 id="libpq-exec-select-info">
<title>Retrieving Query Result Information</title>
***************
*** 4558,4569 ****
--- 4677,5157 ----
</listitem>
</varlistentry>
</variablelist>
</sect1>
+ <sect1 id="libpq-events">+ <title>Event System</title>+ + <para>+ The event system is designed to notify registered event handlers+ about particular libpq events; such as the creation or destruction+ of <structname>PGconn</structname> and <structname>PGresult</structname>+ objects. One use case is that this allows applications to associate+ their own data with a <structname>PGconn</structname> and+ <structname>PGresult</structname>, that can retrieved via+ <function>PQinstanceData</function> and <function>PQresultInstanceData</function>.+ Basically, its like adding members to the opaque conn or result+ structures at runtime.+ </para>+ + <para>+ Below is a list of event system concepts:+ <itemizedlist mark='bullet'>+ <listitem>+ <para>+ <literal>eventId</literal> - Identifies which event was fired by libpq. All+ events begin with <literal>PGEVT_</literal>.+ </para>+ </listitem>+ <listitem>+ <para>+ <literal>eventInfo</literal> - Every <literal>eventId</literal> has a corresponding+ event info structure, that contains data to aid in processing the event.+ </para>+ </listitem>+ <listitem>+ <para>+ <literal>instanceData</literal> - The instance data is memory associated with a+ <structname>PGconn</structname> or <structname>PGresult</structname>. The data is+ created and destroyed along with with the connection or result.+ Typically, instance data is created in response to+ a <literal>PGEVT_REGISTER</literal> event, but can be created at any time or+ not at all. Management of instance data is done using the <function>PQinstanceData</function>,+ <function>PQsetInstanceData</function>, <function>PQresultInstanceData</function> and+ <function>PQsetResultInstanceData</function> functions.+ </para>+ </listitem>+ </itemizedlist>+ </para>+ + + <sect2 id="libpq-events-types">+ <title>Event Types</title>+ <para>+ <variablelist>+ <varlistentry>+ <term><literal>PGEVT_REGISTER</literal></term>+ <listitem>+ <para>+ The register event occurs when <function>PQregisterEventProc</function> is+ called. It is the ideal time to initialize any <literal>instanceData</literal>+ an event procedure may need. Only one register event will be fired per connection.+ If the event procedure fails, the registration is aborted.+ + <synopsis>+ typedef struct+ {+ const PGconn *conn;+ } PGEventRegister;</synopsis>+ + When a <literal>PGEVT_REGISTER</literal> event is received, the <parameter>evtInfo</parameter>+ pointer should be casted to a <structname>PGEventRegister*</structname>. This structure+ contains a <structname>PGconn</structname> that should be in the <literal>CONNECTION_OK</literal>+ status; guaranteed if one calls <function>PQregisterEventProc</function> right after obtaining+ a good <structname>PGconn</structname>. The connection can be used to initialize any+ application specific needs: like allocating structures as the event <literal>instanceData</literal>+ or executing SQL statements.+ </para>+ </listitem>+ </varlistentry>+ + <varlistentry>+ <term><literal>PGEVT_CONNRESET</literal></term>+ <listitem>+ <para>+ The connection reset event is fired in response to a <function>PQreset</function> or <function>PQresetPoll</function>.+ In both cases, the event is only fired if the reset was successful. If the event procedure fails,+ the entire connection reset will fail; the <structname>PGconn</structname> is put into+ <literal>CONNECTION_BAD</literal> status and <function>PQresetPoll</function> will return+ <literal>PGRES_POLLING_FAILED</literal>.+ <synopsis>+ typedef struct+ {+ const PGconn *conn;+ } PGEventReset;</synopsis>+ + When a <literal>PGEVT_CONNRESET</literal> event is received, the <parameter>evtInfo</parameter>+ pointer should be casted to a <structname>PGEventReset*</structname>. Although the contained+ <structname>PGconn</structname> was just reset, all event data remains unchanged. This event+ should be used to reset/reload/requery any assocaited <literal>instanceData</literal>.+ </para>+ </listitem>+ </varlistentry>+ + <varlistentry>+ <term><literal>PGEVT_CONNDESTROY</literal></term>+ <listitem>+ <para>+ The connection destroy event is fired in response to a <function>PQfinish</function>.+ It is the event procedure's responsibility to properly cleanup its event data as libpq+ has no ability to manage this memory. Failure to properly cleanup will lead to memory+ leaks.+ <synopsis>+ typedef struct+ {+ const PGconn *conn;+ } PGEventConnDestroy;</synopsis>+ + When a <literal>PGEVT_CONNDESTROY</literal> event is received, the <parameter>evtInfo</parameter>+ pointer should be casted to a <structname>PGEventConnDestroy*</structname>. This event is fired+ prior to <function>PQfinish</function> performing any cleanup. The return value of the event+ procedure is ignored since there is no way of indicating a failure from <function>PQfinish</function>.+ Also, an event procedure failure should not abort the process of cleaning up unwanted memory.+ </para>+ </listitem>+ </varlistentry>+ + <varlistentry>+ <term><literal>PGEVT_RESULTCREATE</literal></term>+ <listitem>+ <para>+ The result create event is fired in response to any exec function that generates a result,+ including <function>PQgetResult</function>. This event will only be fired after the result+ has been created successfully, with a result status of either <literal>PGRES_COMMAND_OK</literal>,+ <literal>PGRES_TUPLES_OK</literal> or <literal>PGRES_EMPTY_QUERY</literal>.+ <synopsis>+ typedef struct+ {+ const PGconn *conn;+ const PGresult *result;+ } PGEventResultCreate;</synopsis>+ + When a <literal>PGEVT_RESULTCREATE</literal> event is received, the <parameter>evtInfo</parameter>+ pointer should be casted to a <structname>PGEventResultCreate*</structname>. The <parameter>conn</parameter>+ was the connection used to generate the result. This is the ideal place to initialize+ any <literal>instanceData</literal> that needs to be associated with the result. If the event procedure fails,+ the result will be cleared and the failure will be propagated. Do not attempt to+ clear the result object contained in the <structname>PGEventResultCreate</structname> structure.+ </para>+ </listitem>+ </varlistentry>+ + <varlistentry>+ <term><literal>PGEVT_RESULTCOPY</literal></term>+ <listitem>+ <para>+ The result copy event is fired in response to <function>PQcopyResult</function>.+ This event will only be fired after the copy is complete.+ <synopsis>+ typedef struct+ {+ const PGresult *src;+ PGresult *dest;+ } PGEventResultCopy;</synopsis>+ + When a <literal>PGEVT_RESULTCOPY</literal> event is received, the <parameter>evtInfo</parameter>+ pointer should be casted to a <structname>PGEventResultCopy*</structname>. The <parameter>src</parameter>+ result is what is being copied while the <parameter>dest</parameter> result is the copy destination.+ This event can be used to provide a deep copy of <literal>instanceData</literal>, since+ <literal>PQcopyResult</literal> cannot do that. If the event procedure fails, the entire copy operation+ will fail and the <parameter>dest</parameter> result will be cleared.+ </para>+ </listitem>+ </varlistentry>+ + <varlistentry>+ <term><literal>PGEVT_RESULTDESTROY</literal></term>+ <listitem>+ <para>+ The result destroy event is fired in response to a <function>PQclear</function>.+ It is the event procedure's responsibility to properly cleanup its event data as libpq+ has no ability to manage this memory. Failure to properly cleanup will lead to memory+ leaks.+ <synopsis>+ typedef struct+ {+ const PGresult *result;+ } PGEventResultDestroy;</synopsis>+ + When a <literal>PGEVT_RESULTDESTROY</literal> event is received, the <parameter>evtInfo</parameter>+ pointer should be casted to a <structname>PGEventResultDestroy*</structname>. This event is fired+ prior to <function>PQclear</function> performing any cleanup. The return value of the event+ procedure is ignored since there is no way of indicating a failure from <function>PQclear</function>.+ Also, an event procedure failure should not abort the process of cleaning up unwanted memory.+ </para>+ </listitem>+ </varlistentry>+ </variablelist>+ </para>+ </sect2>+ + <sect2 id="libpq-events-proc">+ <title>Event Callback Procedure</title>+ <variablelist>+ <varlistentry>+ <term>+ <function>PGEventProc</function>+ <indexterm>+ <primary>PGEventProc</primary>+ </indexterm>+ </term>+ + <listitem>+ <para>+ User callback function that receives events from libpq. The address of the event+ proc is also used to lookup the <literal>instanceData</literal>+ associated with a <structname>PGconn</structname> or <structname>PGresult</structname>.+ <synopsis>typedef int (*PGEventProc)(PGEventId evtId, void *evtInfo, void *passThrough);</synopsis>+ </para>+ + <para>+ The <parameter>evtId</parameter> inidcates which <literal>PGEVT_</literal> event occurred.+ The <parameter>evtInfo</parameter> must be casted to the appropriate structure: ex. PGEVT_REGISTER+ means cast evtInfo to a PGEventRegister pointer. The <parameter>passThrough</parameter> is the+ pointer provided to the <function>PQregisterEventProc</function>, which can be NULL. The function+ should return a non-zero value if it succeeds and zero if it fails.+ </para>+ </listitem>+ </varlistentry>+ </variablelist>+ </sect2>+ + <sect2 id="libpq-events-funcs">+ <title>Event Functions</title>+ <variablelist>+ <varlistentry>+ <term>+ <function>PQregisterEventProc</function>+ <indexterm>+ <primary>PQregisterEventProc</primary>+ </indexterm>+ </term>+ + <listitem>+ <para>+ Registers an event callback procedure with libpq.+ <synopsis>int PQregisterEventProc(PGconn *conn, PGEventProc proc, const char *name, void *passThrough);</synopsis>+ </para>+ + <para>+ Registration must be called once on every PGconn you want to receive events about.+ There is no limit on the number of event procedures that can be registered with a connection.+ The function returns a non-zero value if it succeeds and zero if it fails.+ </para>+ + <para>+ The <parameter>proc</parameter> argument will be called when a libpq event is fired. Its+ memory address is also used to lookup <literal>instanceData</literal>.+ The <parameter>name</parameter> argument is used to contruct error messages to aid+ in debugging. This value cannot be NULL or a zero-length string.+ The <parameter>passThrough</parameter> pointer is passed to the <parameter>proc</parameter> whenever+ an event occurs. This argument can be NULL.+ </para>+ </listitem>+ </varlistentry>+ + <varlistentry>+ <term>+ <function>PQinstanceData</function>+ <indexterm>+ <primary>PQinstanceData</primary>+ </indexterm>+ </term>+ <listitem>+ <para>+ Returns the conn's instanceData associated with proc.+ <synopsis>void *PQinstanceData(const PGconn *conn, PGEventProc proc);</synopsis>+ </para>+ </listitem>+ </varlistentry>+ + <varlistentry>+ <term>+ <function>PQsetInstanceData</function>+ <indexterm>+ <primary>PQsetInstanceData</primary>+ </indexterm>+ </term>+ <listitem>+ <para>+ Sets the conn's instanceData for proc to data. This reutrns non-zero for+ succees and zero for failure.+ <synopsis>int PQsetInstanceData(PGconn *conn, PGEventProc proc, void *data);</synopsis>+ </para>+ </listitem>+ </varlistentry>+ + <varlistentry>+ <term>+ <function>PQresultInstanceData</function>+ <indexterm>+ <primary>PQresultInstanceData</primary>+ </indexterm>+ </term>+ <listitem>+ <para>+ Returns the result's instanceData associated by proc.+ <synopsis>void *PQresultInstanceData(const PGresult *res, PGEventProc proc);</synopsis>+ </para>+ </listitem>+ </varlistentry>+ + <varlistentry>+ <term>+ <function>PQresultSetInstanceData</function>+ <indexterm>+ <primary>PQresultSetInstanceData</primary>+ </indexterm>+ </term>+ <listitem>+ <para>+ Sets the result's instanceData for proc to data. This reutrns non-zero for+ succees and zero for failure.+ <synopsis>int PQresultSetInstanceData(PGresult *res, PGEventProc proc, void *data);</synopsis>+ </para>+ </listitem>+ </varlistentry>+ </variablelist>+ </sect2>+ + <sect2 id="libpq-events-example">+ <title>Event Example</title>+ <programlisting>+ /* required header for libpq events (note: includes libpq-fe.h)*/+ #include <libpq-events.h>+ + /* The instanceData */+ typedef struct+ {+ int n;+ char *str;+ } mydata;+ + /* PGEventProc */+ int myEventProc(PGEventId evtId, void *evtInfo, void *passThrough);+ + int main(void)+ {+ mydata *data;+ PGresult *res;+ PGconn *conn = PQconnectdb("dbname = postgres");+ + if (PQstatus(conn) != CONNECTION_OK)+ {+ fprintf(stderr, "Connection to database failed: %s",+ PQerrorMessage(conn));+ PQfinish(conn);+ return 1;+ }+ + /* called once on any connection that should receive events.+ * Sends a PGEVT_REGISTER to myEventProc.+ */+ if (!PQregisterEventProc(conn, myEventProc, "mydata_proc", NULL))+ {+ fprintf(stderr, "Cannot register PGEventProc\n");+ PQfinish(conn);+ return 1;+ }+ + /* conn instanceData is available */+ data = PQinstanceData(conn, myEventProc);+ + /* Sends a PGEVT_RESULTCREATE to myEventProc */+ res = PQexec(conn, "SELECT 1 + 1");+ + /* result instanceData is available */+ data = PQresultInstanceData(res, myEventProc);+ + /* If PG_COPYRES_EVENTS is used, sends a PGEVT_RESULTCOPY to myEventProc */+ res_copy = PQcopyResult(res, PG_COPYRES_TUPLES | PG_COPYRES_EVENTS);+ + /* result instanceData is available if PG_COPYRES_EVENTS was+ * used during the PQcopyResult call.+ */+ data = PQresultInstanceData(res_copy, myEventProc);+ + /* Both clears send a PGEVT_RESULTDESTORY to myEventProc */+ PQclear(res);+ PQclear(res_copy);+ + /* Sends a PGEVT_CONNDESTROY to myEventProc */+ PQfinish(conn);+ + return 0;+ }+ + int myEventProc(PGEventId evtId, void *evtInfo, void *passThrough)+ {+ switch (evtId)+ {+ case PGEVT_REGISTER:+ {+ PGEventRegister *e = (PGEventRegister *)evtInfo;+ mydata *data = get_mydata(e->conn);+ + /* associate app specific data with connection */+ PQsetInstanceData(e->conn, myEventProc, data);+ break;+ }+ + case PGEVT_CONNRESET:+ {+ PGEventConnReset *e = (PGEventConnReset *)evtInfo;+ mydata *data = PQinstanceData(e->conn, myEventProc);+ if (data)+ memset(data, 0, sizeof(mydata));+ break;+ }+ + case PGEVT_CONNDESTROY:+ {+ PGEventConnDestroy *e = (PGEventConnDestroy *)evtInfo;+ mydata *data = PQinstanceData(e->conn, myEventProc);+ + /* free instance data because the conn is being destroyed */+ if (data)+ free_mydata(data);+ break;+ }+ + case PGEVT_RESULTCREATE:+ {+ PGEventResultCreate *e = (PGEventResultCreate *)evtInfo;+ mydata *conn_data = PQinstanceData(e->conn, myEventProc);+ mydata *res_data = dup_mydata(conn_data);+ + /* associate app specific data with result (copy it from conn) */+ PQsetResultInstanceData(e->result, myEventProc, res_data);+ break;+ }+ + case PGEVT_RESULTCOPY:+ {+ PGEventResultCopy *e = (PGEventResultCopy *)evtInfo;+ mydata *src_data = PQresultInstanceData(e->src, myEventProc);+ mydata *dest_data = dup_mydata(src_data);+ + /* associate app specific data with result (copy it from a result) */+ PQsetResultInstanceData(e->dest, myEventProc, dest_data);+ break;+ }+ + case PGEVT_RESULTDESTROY:+ {+ PGEventResultDestroy *e = (PGEventResultDestroy *)evtInfo;+ mydata *data = PQresultInstanceData(e->result, myEventProc);+ + /* free instance data because the result is being destroyed */+ if( data)+ free_mydata(data);+ break;+ }+ + /* unknown event id, just return TRUE. */+ default:+ break;+ }+ + return TRUE; /* event processing succeeded */+ } </programlisting>+ </sect2>+ </sect1>+
<sect1 id="libpq-misc">
<title>Miscellaneous Functions</title>
<para>
As always, there are some functions that just don't fit anywhere.
</para>
Index: src/interfaces/libpq/Makefile
===================================================================
RCS file: /projects/cvsroot/pgsql/src/interfaces/libpq/Makefile,v
retrieving revision 1.166
diff -C6 -r1.166 Makefile
*** src/interfaces/libpq/Makefile 16 Apr 2008 14:19:56 -0000 1.166
--- src/interfaces/libpq/Makefile 17 Sep 2008 03:54:20 -0000
***************
*** 29,41 ****
# the object files from libpgport, this would not be true on all
# platforms.
LIBS := $(LIBS:-lpgport=)
OBJS= fe-auth.o fe-connect.o fe-exec.o fe-misc.o fe-print.o fe-lobj.o \
fe-protocol2.o fe-protocol3.o pqexpbuffer.o pqsignal.o fe-secure.o \
! md5.o ip.o wchar.o encnames.o noblock.o pgstrcasecmp.o thread.o \
$(filter crypt.o getaddrinfo.o inet_aton.o open.o snprintf.o strerror.o strlcpy.o win32error.o, $(LIBOBJS))
ifeq ($(PORTNAME), cygwin)
override shlib = cyg$(NAME)$(DLSUFFIX)
endif
--- 29,41 ----
# the object files from libpgport, this would not be true on all
# platforms.
LIBS := $(LIBS:-lpgport=)
OBJS= fe-auth.o fe-connect.o fe-exec.o fe-misc.o fe-print.o fe-lobj.o \
fe-protocol2.o fe-protocol3.o pqexpbuffer.o pqsignal.o fe-secure.o \
! md5.o ip.o wchar.o encnames.o noblock.o pgstrcasecmp.o thread.o libpq-events.o \
$(filter crypt.o getaddrinfo.o inet_aton.o open.o snprintf.o strerror.o strlcpy.o win32error.o, $(LIBOBJS))
ifeq ($(PORTNAME), cygwin)
override shlib = cyg$(NAME)$(DLSUFFIX)
endif
***************
*** 103,123 ****
$(top_builddir)/src/port/pg_config_paths.h:
$(MAKE) -C $(top_builddir)/src/port pg_config_paths.h
install: all installdirs install-lib
$(INSTALL_DATA) $(srcdir)/libpq-fe.h '$(DESTDIR)$(includedir)'
$(INSTALL_DATA) $(srcdir)/libpq-int.h '$(DESTDIR)$(includedir_internal)'
$(INSTALL_DATA) $(srcdir)/pqexpbuffer.h '$(DESTDIR)$(includedir_internal)'
$(INSTALL_DATA) $(srcdir)/pg_service.conf.sample '$(DESTDIR)$(datadir)/pg_service.conf.sample'
installdirs: installdirs-lib
$(mkinstalldirs) '$(DESTDIR)$(includedir)' '$(DESTDIR)$(includedir_internal)'
uninstall: uninstall-lib
! rm -f '$(DESTDIR)$(includedir)/libpq-fe.h' '$(DESTDIR)$(includedir_internal)/libpq-int.h' '$(DESTDIR)$(includedir_internal)/pqexpbuffer.h' '$(DESTDIR)$(datadir)/pg_service.conf.sample'
clean distclean: clean-lib
rm -f $(OBJS) pg_config_paths.h crypt.c getaddrinfo.c inet_aton.c noblock.c open.c pgstrcasecmp.c snprintf.c strerror.c strlcpy.c thread.c md5.c ip.c encnames.c wchar.c win32error.c pgsleep.c pthread.h libpq.rc
# Might be left over from a Win32 client-only build
rm -f pg_config_paths.h
--- 103,124 ----
$(top_builddir)/src/port/pg_config_paths.h:
$(MAKE) -C $(top_builddir)/src/port pg_config_paths.h
install: all installdirs install-lib
$(INSTALL_DATA) $(srcdir)/libpq-fe.h '$(DESTDIR)$(includedir)'
+ $(INSTALL_DATA) $(srcdir)/libpq-events.h '$(DESTDIR)$(includedir)'
$(INSTALL_DATA) $(srcdir)/libpq-int.h '$(DESTDIR)$(includedir_internal)'
$(INSTALL_DATA) $(srcdir)/pqexpbuffer.h '$(DESTDIR)$(includedir_internal)'
$(INSTALL_DATA) $(srcdir)/pg_service.conf.sample '$(DESTDIR)$(datadir)/pg_service.conf.sample'
installdirs: installdirs-lib
$(mkinstalldirs) '$(DESTDIR)$(includedir)' '$(DESTDIR)$(includedir_internal)'
uninstall: uninstall-lib
! rm -f '$(DESTDIR)$(includedir)/libpq-fe.h' '$(DESTDIR)$(includedir)/libpq-events.h' '$(DESTDIR)$(includedir_internal)/libpq-int.h' '$(DESTDIR)$(includedir_internal)/pqexpbuffer.h' '$(DESTDIR)$(datadir)/pg_service.conf.sample'
clean distclean: clean-lib
rm -f $(OBJS) pg_config_paths.h crypt.c getaddrinfo.c inet_aton.c noblock.c open.c pgstrcasecmp.c snprintf.c strerror.c strlcpy.c thread.c md5.c ip.c encnames.c wchar.c win32error.c pgsleep.c pthread.h libpq.rc
# Might be left over from a Win32 client-only build
rm -f pg_config_paths.h
Index: src/interfaces/libpq/exports.txt
===================================================================
RCS file: /projects/cvsroot/pgsql/src/interfaces/libpq/exports.txt,v
retrieving revision 1.19
diff -C6 -r1.19 exports.txt
*** src/interfaces/libpq/exports.txt 19 Mar 2008 00:39:33 -0000 1.19
--- src/interfaces/libpq/exports.txt 17 Sep 2008 03:54:20 -0000
***************
*** 138,143 ****
--- 138,152 ----
PQsendDescribePortal 136
lo_truncate 137
PQconnectionUsedPassword 138
pg_valid_server_encoding_id 139
PQconnectionNeedsPassword 140
lo_import_with_oid 141
+ PQcopyResult 142+ PQsetResultAttrs 143+ PQsetvalue 144+ PQresultAlloc 145+ PQregisterEventProc 146+ PQinstanceData 147+ PQsetInstanceData 148+ PQresultInstanceData 149+ PQresultSetInstanceData 150
\ No newline at end of file
Index: src/interfaces/libpq/fe-connect.c
===================================================================
RCS file: /projects/cvsroot/pgsql/src/interfaces/libpq/fe-connect.c,v
retrieving revision 1.359
diff -C6 -r1.359 fe-connect.c
*** src/interfaces/libpq/fe-connect.c 29 May 2008 22:02:44 -0000 1.359
--- src/interfaces/libpq/fe-connect.c 17 Sep 2008 03:54:20 -0000
***************
*** 1971,1982 ****
--- 1971,2001 ----
* release data that is to be held for the life of the PGconn structure.
* If a value ought to be cleared/freed during PQreset(), do it there not here.
*/
static void
freePGconn(PGconn *conn)
{
+ int i;+ PGEventConnDestroy evt;+ + /* Let the event procs cleanup their state data */+ for (i = 0; i < conn->nEvents; i++)+ {+ evt.conn = conn;+ (void)conn->events[i].proc(PGEVT_CONNDESTROY, &evt, conn->events[i].passThrough);+ free(conn->events[i].name);+ }+ + /* free the PGEvent array */+ if (conn->events)+ {+ free(conn->events);+ conn->events = NULL;+ conn->nEvents = conn->eventArrSize = 0;+ }+
if (conn->pghost)
free(conn->pghost);
if (conn->pghostaddr)
free(conn->pghostaddr);
if (conn->pgport)
free(conn->pgport);
***************
*** 2152,2165 ****
PQreset(PGconn *conn)
{
if (conn)
{
closePGconn(conn);
! if (connectDBStart(conn))
! (void) connectDBComplete(conn);
}
}
/*
* PQresetStart:
--- 2171,2201 ----
PQreset(PGconn *conn)
{
if (conn)
{
closePGconn(conn);
! if (connectDBStart(conn) && connectDBComplete(conn))
! {
! int i;
! PGEventConnReset evt;
!
! for (i = 0; i < conn->nEvents; i++)
! {
! evt.conn = conn;
!
! if (!conn->events[i].proc(PGEVT_CONNRESET, &evt, conn->events[i].passThrough))
! {
! conn->status = CONNECTION_BAD;
! printfPQExpBuffer(&conn->errorMessage,
! libpq_gettext("PGEventProc \"%s\" failed during PGEVT_CONNRESET event\n"),
! conn->events[i].name);
! break;
! }
! }
! }
}
}
/*
* PQresetStart:
***************
*** 2187,2199 ****
* closes the existing connection and makes a new one
*/
PostgresPollingStatusType
PQresetPoll(PGconn *conn)
{
if (conn)
! return PQconnectPoll(conn);
return PGRES_POLLING_FAILED;
}
/*
* PQcancelGet: get a PGcancel structure corresponding to a connection.
--- 2223,2259 ----
* closes the existing connection and makes a new one
*/
PostgresPollingStatusType
PQresetPoll(PGconn *conn)
{
if (conn)
! {
! PostgresPollingStatusType status = PQconnectPoll(conn);
!
! if (status == PGRES_POLLING_OK)
! {
! int i;
! PGEventConnReset evt;
!
! for (i = 0; i < conn->nEvents; i++)
! {
! evt.conn = conn;
!
! if (!conn->events[i].proc(PGEVT_CONNRESET, &evt, conn->events[i].passThrough))
! {
! conn->status = CONNECTION_BAD;
! printfPQExpBuffer(&conn->errorMessage,
! libpq_gettext("PGEventProc \"%s\" failed during PGEVT_CONNRESET event\n"),
! conn->events[i].name);
! return PGRES_POLLING_FAILED;
! }
! }
! }
!
! return status;
! }
return PGRES_POLLING_FAILED;
}
/*
* PQcancelGet: get a PGcancel structure corresponding to a connection.
Index: src/interfaces/libpq/fe-exec.c
===================================================================
RCS file: /projects/cvsroot/pgsql/src/interfaces/libpq/fe-exec.c,v
retrieving revision 1.197
diff -C6 -r1.197 fe-exec.c
*** src/interfaces/libpq/fe-exec.c 10 Sep 2008 17:01:07 -0000 1.197
--- src/interfaces/libpq/fe-exec.c 17 Sep 2008 03:54:20 -0000
***************
*** 60,72 ****
int resultFormat);
static void parseInput(PGconn *conn);
static bool PQexecStart(PGconn *conn);
static PGresult *PQexecFinish(PGconn *conn);
static int PQsendDescribe(PGconn *conn, char desc_type,
const char *desc_target);
!
/* ----------------
* Space management for PGresult.
*
* Formerly, libpq did a separate malloc() for each field of each tuple
* returned by a query. This was remarkably expensive --- malloc/free
--- 60,72 ----
int resultFormat);
static void parseInput(PGconn *conn);
static bool PQexecStart(PGconn *conn);
static PGresult *PQexecFinish(PGconn *conn);
static int PQsendDescribe(PGconn *conn, char desc_type,
const char *desc_target);
! static int check_field_number(const PGresult *res, int field_num);
/* ----------------
* Space management for PGresult.
*
* Formerly, libpq did a separate malloc() for each field of each tuple
* returned by a query. This was remarkably expensive --- malloc/free
***************
*** 121,141 ****
#define PGRESULT_DATA_BLOCKSIZE 2048
#define PGRESULT_ALIGN_BOUNDARY MAXIMUM_ALIGNOF /* from configure */
#define PGRESULT_BLOCK_OVERHEAD Max(sizeof(PGresult_data), PGRESULT_ALIGN_BOUNDARY)
#define PGRESULT_SEP_ALLOC_THRESHOLD (PGRESULT_DATA_BLOCKSIZE / 2)
/*
* PQmakeEmptyPGresult
* returns a newly allocated, initialized PGresult with given status.
* If conn is not NULL and status indicates an error, the conn's
* errorMessage is copied.
*
* Note this is exported --- you wouldn't think an application would need
* to build its own PGresults, but this has proven useful in both libpgtcl
! * and the Perl5 interface, so maybe it's not so unreasonable.
*/
PGresult *
PQmakeEmptyPGresult(PGconn *conn, ExecStatusType status)
{
PGresult *result;
--- 121,177 ----
#define PGRESULT_DATA_BLOCKSIZE 2048
#define PGRESULT_ALIGN_BOUNDARY MAXIMUM_ALIGNOF /* from configure */
#define PGRESULT_BLOCK_OVERHEAD Max(sizeof(PGresult_data), PGRESULT_ALIGN_BOUNDARY)
#define PGRESULT_SEP_ALLOC_THRESHOLD (PGRESULT_DATA_BLOCKSIZE / 2)
+ /* Does not duplicate the event instance data, sets this to NULL */+ static PGEvent *+ dupEvents(PGEvent *events, int count)+ {+ int i;+ PGEvent *newEvents;+ + if (!events || count <= 0)+ return NULL;+ + newEvents = (PGEvent *) malloc(count * sizeof(PGEvent));+ if (!newEvents)+ return NULL;+ + memcpy(newEvents, events, count * sizeof(PGEvent));+ + /* NULL out the data pointer and deep copy name */+ for (i = 0; i < count; i++)+ {+ newEvents[i].name = strdup(newEvents[i].name);+ if (!newEvents[i].name)+ {+ while (--i >= 0)+ free(newEvents[i].name);+ free(newEvents);+ return NULL;+ }+ + newEvents[i].data = NULL;+ }+ + return newEvents;+ }+
/*
* PQmakeEmptyPGresult
* returns a newly allocated, initialized PGresult with given status.
* If conn is not NULL and status indicates an error, the conn's
* errorMessage is copied.
*
* Note this is exported --- you wouldn't think an application would need
* to build its own PGresults, but this has proven useful in both libpgtcl
! * and the Perl5 interface, so maybe it's not so unreasonable. If conn is
! * not NULL, the PGevents array will be copied from the PGconn to the
! * created PGresult.
*/
PGresult *
PQmakeEmptyPGresult(PGconn *conn, ExecStatusType status)
{
PGresult *result;
***************
*** 157,175 ****
--- 193,226 ----
result->errMsg = NULL;
result->errFields = NULL;
result->null_field[0] = '\0';
result->curBlock = NULL;
result->curOffset = 0;
result->spaceLeft = 0;
+ result->nEvents = 0;+ result->events = NULL;
if (conn)
{
/* copy connection data we might need for operations on PGresult */
result->noticeHooks = conn->noticeHooks;
result->client_encoding = conn->client_encoding;
+ /* copy events from connection */+ if (conn->nEvents > 0)+ {+ result->events = dupEvents(conn->events, conn->nEvents);+ if (!result->events)+ {+ PQclear(result);+ return NULL;+ }+ + result->nEvents = conn->nEvents;+ }+
/* consider copying conn's errorMessage */
switch (status)
{
case PGRES_EMPTY_QUERY:
case PGRES_COMMAND_OK:
case PGRES_TUPLES_OK:
***************
*** 192,203 ****
--- 243,501 ----
result->client_encoding = PG_SQL_ASCII;
}
return result;
}
+ /* PQsetResultAttrs+ * Set the attributes for a given result. This function fails if there are+ * already attributes contained in the provided result. The call is+ * ignored if numAttributes is is zero or attDescs is NULL. If the+ * function fails, it returns zero. If the function succeeds, it+ * returns a non-zero value.+ */+ int+ PQsetResultAttrs(PGresult *res, int numAttributes, PGresAttDesc *attDescs)+ {+ int i;+ + /* If attrs already exist, they cannot be overwritten. */+ if (!res || res->numAttributes > 0)+ return FALSE;+ + /* ignore request */+ if (numAttributes <= 0 || !attDescs)+ return TRUE;+ + res->attDescs = (PGresAttDesc *) PQresultAlloc(res,+ numAttributes * sizeof(PGresAttDesc));+ + if (!res->attDescs)+ return FALSE;+ + res->numAttributes = numAttributes;+ memcpy(res->attDescs, attDescs,+ numAttributes * sizeof(PGresAttDesc));+ + /* resultalloc the attribute names. The above memcpy has the attr+ * names pointing at the callers provided attDescs memory.+ */+ res->binary = 1;+ for (i = 0; i < res->numAttributes; i++)+ {+ if (res->attDescs[i].name)+ res->attDescs[i].name = pqResultStrdup(res, res->attDescs[i].name);+ else+ res->attDescs[i].name = res->null_field;+ + if (!res->attDescs[i].name)+ return FALSE;+ + /* Although deprecated, because results can have text+binary columns,+ * its easy enough to deduce so set it for completeness.+ */+ if (res->attDescs[i].format == 0)+ res->binary = 0;+ }+ + return TRUE;+ }+ + /*+ * PQcopyResult+ * Returns a deep copy of the provided 'src' PGresult, which cannot be NULL.+ * The 'flags' argument controls which portions of the result will or will+ * NOT be copied. The created result is always put into the+ * PGRES_TUPLES_OK status. The source result error message is not copied,+ * although cmdStatus is.+ *+ * To set custom attributes, see PQsetResultAttrs. That function requires+ * that there are no attrs contained in the result, so to use that+ * function you cannot use the PG_COPYRES_ATTRS or PG_COPYRES_TUPLES+ * options with this function.+ *+ * Options:+ * PG_COPYRES_ATTRS - Copy the source result's attributes+ *+ * PG_COPYRES_TUPLES - Copy the source result's tuples. This implies+ * copying the attrs, being how the attrs are needed by the tuples.+ *+ * PG_COPYRES_EVENTS - Copy the source result's events.+ *+ * PG_COPYRES_NOTICEHOOKS - Copy the source result's notice hooks.+ */+ + PGresult *+ PQcopyResult(const PGresult *src, int flags)+ {+ int i;+ PGresult *dest;+ PGEventResultCopy evt;+ + if (!src)+ return NULL;+ + /* Automatically turn on attrs flags because you can't copy tuples+ * without copying the attrs. _TUPLES implies _ATTRS.+ */+ if (flags & PG_COPYRES_TUPLES)+ flags |= PG_COPYRES_ATTRS;+ + dest = PQmakeEmptyPGresult((PGconn *)NULL, PGRES_TUPLES_OK);+ if (!dest)+ return NULL;+ + /* always copy these over. Is cmdStatus useful here? */+ dest->client_encoding = src->client_encoding;+ strcpy(dest->cmdStatus, src->cmdStatus);+ + /* Wants to copy notice hooks */+ if (flags & PG_COPYRES_NOTICEHOOKS)+ dest->noticeHooks = src->noticeHooks;+ + /* Wants attrs */+ if ((flags & PG_COPYRES_ATTRS) &&+ !PQsetResultAttrs(dest, src->numAttributes, src->attDescs))+ {+ PQclear(dest);+ return NULL;+ }+ + /* Wants to copy result tuples: use PQsetvalue(). */+ if ((flags & PG_COPYRES_TUPLES) && src->ntups > 0)+ {+ int tup, field;+ for (tup = 0; tup < src->ntups; tup++)+ for (field = 0; field < src->numAttributes; field++)+ PQsetvalue(dest, tup, field, src->tuples[tup][field].value,+ src->tuples[tup][field].len);+ }+ + /* Wants to copy PGEvents. */+ if ((flags & PG_COPYRES_EVENTS) && src->nEvents > 0)+ {+ dest->events = dupEvents(src->events, src->nEvents);+ if (!dest->events)+ {+ PQclear(dest);+ return NULL;+ }+ + dest->nEvents = src->nEvents;+ }+ + /* Trigger PGEVT_RESULTCOPY event */+ for (i = 0; i < dest->nEvents; i++)+ {+ evt.src = src;+ evt.dest = dest;+ if (!dest->events[i].proc(PGEVT_RESULTCOPY, &evt, dest->events[i].passThrough))+ {+ PQclear(dest);+ return NULL;+ }+ }+ + return dest;+ }+ + int+ PQsetvalue(PGresult *res, int tup_num, int field_num,+ char *value, int len)+ {+ PGresAttValue *attval;+ + if (!check_field_number(res, field_num))+ return FALSE;+ + /* Invalid tup_num, must be <= ntups */+ if (tup_num > res->ntups)+ return FALSE;+ + /* need to grow the tuple table */+ if (res->ntups >= res->tupArrSize)+ {+ int n = res->tupArrSize ? res->tupArrSize * 2 : 128;+ PGresAttValue **tups;+ + if (res->tuples)+ tups = (PGresAttValue **) realloc(res->tuples, n * sizeof(PGresAttValue *));+ else+ tups = (PGresAttValue **) malloc(n * sizeof(PGresAttValue *));+ + if (!tups)+ return FALSE;+ + memset(tups + res->tupArrSize, 0,+ (n - res->tupArrSize) * sizeof(PGresAttValue *));+ res->tuples = tups;+ res->tupArrSize = n;+ }+ + /* need to allocate a new tuple */+ if (tup_num == res->ntups && !res->tuples[tup_num])+ {+ int i;+ PGresAttValue *tup;+ + tup = (PGresAttValue *) pqResultAlloc(res,+ res->numAttributes * sizeof(PGresAttValue), TRUE);+ + if (!tup)+ return FALSE;+ + /* initialize each column to NULL */+ for (i = 0; i < res->numAttributes; i++)+ {+ tup[i].len = NULL_LEN;+ tup[i].value = res->null_field;+ }+ + res->tuples[tup_num] = tup;+ res->ntups++;+ }+ + attval = &res->tuples[tup_num][field_num];+ + /* On top of NULL_LEN, treat a NULL value as a NULL field */+ if (len == NULL_LEN || value == NULL)+ {+ attval->len = NULL_LEN;+ attval->value = res->null_field;+ }+ else+ {+ if (len < 0)+ len = 0;+ + if (len == 0)+ {+ attval->len = 0;+ attval->value = res->null_field;+ }+ else+ {+ attval->value = (char *) PQresultAlloc(res, len + 1);+ if (!attval->value)+ return FALSE;+ + attval->len = len;+ memcpy(attval->value, value, len);+ attval->value[len] = '\0';+ }+ }+ + return TRUE;+ }+ + void *+ PQresultAlloc(PGresult *res, size_t nBytes)+ {+ return pqResultAlloc(res, nBytes, TRUE);+ }+
/*
* pqResultAlloc -
* Allocate subsidiary storage for a PGresult.
*
* nBytes is the amount of space needed for the object.
* If isBinary is true, we assume that we need to align the object on
***************
*** 349,365 ****
--- 647,679 ----
* PQclear -
* free's the memory associated with a PGresult
*/
void
PQclear(PGresult *res)
{
+ int i;
PGresult_data *block;
+ PGEventResultDestroy evt;
if (!res)
return;
+ for (i = 0; i < res->nEvents; i++)+ {+ evt.result = res;+ (void)res->events[i].proc(PGEVT_RESULTDESTROY, &evt, res->events[i].passThrough);+ free(res->events[i].name);+ }+ + if (res->events)+ {+ free(res->events);+ res->events = NULL;+ res->nEvents = 0;+ }+
/* Free all the subsidiary blocks */
while ((block = res->curBlock) != NULL)
{
res->curBlock = block->next;
free(block);
}
***************
*** 1192,1204 ****
* memory).
*/
PGresult *
PQgetResult(PGconn *conn)
{
! PGresult *res;
if (!conn)
return NULL;
/* Parse any available data, if our state permits. */
parseInput(conn);
--- 1506,1518 ----
* memory).
*/
PGresult *
PQgetResult(PGconn *conn)
{
! PGresult *res=NULL;
if (!conn)
return NULL;
/* Parse any available data, if our state permits. */
parseInput(conn);
***************
*** 1267,1278 ****
--- 1581,1617 ----
libpq_gettext("unexpected asyncStatus: %d\n"),
(int) conn->asyncStatus);
res = PQmakeEmptyPGresult(conn, PGRES_FATAL_ERROR);
break;
}
+ if (res)+ {+ int i;+ PGEventResultCreate evt;+ + for (i = 0; i < res->nEvents; i++)+ {+ evt.conn = conn;+ evt.result = res;+ + if (!res->events[i].proc(PGEVT_RESULTCREATE, &evt, res->events[i].passThrough))+ {+ char msg[256];+ + sprintf(msg,+ "PGEventProc \"%s\" failed during PGEVT_RESULTCREATE event",+ res->events[i].name);+ + pqSetResultError(res, msg);+ res->resultStatus = PGRES_FATAL_ERROR;+ break;+ }+ }+ }+
return res;
}
/*
* PQexec
Index: src/interfaces/libpq/libpq-fe.h
===================================================================
RCS file: /projects/cvsroot/pgsql/src/interfaces/libpq/libpq-fe.h,v
retrieving revision 1.142
diff -C6 -r1.142 libpq-fe.h
*** src/interfaces/libpq/libpq-fe.h 19 Mar 2008 00:39:33 -0000 1.142
--- src/interfaces/libpq/libpq-fe.h 17 Sep 2008 03:54:20 -0000
***************
*** 25,36 ****
--- 25,45 ----
/*
* postgres_ext.h defines the backend's externally visible types,
* such as Oid.
*/
#include "postgres_ext.h"
+ /* -----------------------+ * Options for PQcopyResult+ */+ + #define PG_COPYRES_ATTRS 0x01+ #define PG_COPYRES_TUPLES 0x02 /* Implies PG_COPYRES_ATTRS */+ #define PG_COPYRES_EVENTS 0x04+ #define PG_COPYRES_NOTICEHOOKS 0x08+
/* Application-visible enum types */
typedef enum
{
/*
* Although it is okay to add to this list, values which become unused
***************
*** 190,201 ****
--- 199,225 ----
int *ptr; /* can't use void (dec compiler barfs) */
int integer;
} u;
} PQArgBlock;
/* ----------------
+ * PGresAttDesc -- Data about a single attribute (column) of a query result+ * ----------------+ */+ typedef struct pgresAttDesc+ {+ char *name; /* column name */+ Oid tableid; /* source table, if known */+ int columnid; /* source column, if known */+ int format; /* format code for value (text/binary) */+ Oid typid; /* type id */+ int typlen; /* type size */+ int atttypmod; /* type-specific modifier info */+ } PGresAttDesc;+ + /* ----------------
* Exported functions of libpq
* ----------------
*/
/* === in fe-connect.c === */
***************
*** 434,445 ****
--- 458,487 ----
* Make an empty PGresult with given status (some apps find this
* useful). If conn is not NULL and status indicates an error, the
* conn's errorMessage is copied.
*/
extern PGresult *PQmakeEmptyPGresult(PGconn *conn, ExecStatusType status);
+ /* makes a copy of a result */+ extern PGresult *PQcopyResult(const PGresult *src, int flags);+ + /* Sets the attributes of a result, no attributes can already exist. */+ extern int PQsetResultAttrs(PGresult *res, int numAttributes, PGresAttDesc *attDescs);+ + /* Allocate subsidiary storage for a PGresult. */+ extern void *PQresultAlloc(PGresult *res, size_t nBytes);+ + /*+ * Sets the value for a tuple field. The tup_num must be less than or+ * equal to PQntuples(res). This function will generate tuples as needed.+ * A new tuple is generated when tup_num equals PQntuples(res) and there+ * are no fields defined for that tuple.+ *+ * Returns a non-zero value for success and zero for failure.+ */+ extern int PQsetvalue(PGresult *res, int tup_num, int field_num, char *value, int len);
/* Quoting strings before inclusion in queries. */
extern size_t PQescapeStringConn(PGconn *conn,
char *to, const char *from, size_t length,
int *error);
extern unsigned char *PQescapeByteaConn(PGconn *conn,
Index: src/interfaces/libpq/libpq-int.h
===================================================================
RCS file: /projects/cvsroot/pgsql/src/interfaces/libpq/libpq-int.h,v
retrieving revision 1.131
diff -C6 -r1.131 libpq-int.h
*** src/interfaces/libpq/libpq-int.h 29 May 2008 22:02:44 -0000 1.131
--- src/interfaces/libpq/libpq-int.h 17 Sep 2008 03:54:20 -0000
***************
*** 19,30 ****
--- 19,31 ----
#ifndef LIBPQ_INT_H
#define LIBPQ_INT_H
/* We assume libpq-fe.h has already been included. */
#include "postgres_fe.h"
+ #include "libpq-events.h"
#include <time.h>
#include <sys/types.h>
#ifndef WIN32
#include <sys/time.h>
#endif
***************
*** 97,121 ****
union pgresult_data
{
PGresult_data *next; /* link to next block, or NULL */
char space[1]; /* dummy for accessing block as bytes */
};
- /* Data about a single attribute (column) of a query result */- - typedef struct pgresAttDesc- {- char *name; /* column name */- Oid tableid; /* source table, if known */- int columnid; /* source column, if known */- int format; /* format code for value (text/binary) */- Oid typid; /* type id */- int typlen; /* type size */- int atttypmod; /* type-specific modifier info */- } PGresAttDesc;-
/* Data about a single parameter of a prepared statement */
typedef struct pgresParamDesc
{
Oid typid; /* type id */
} PGresParamDesc;
--- 98,109 ----
***************
*** 159,170 ****
--- 147,166 ----
PQnoticeReceiver noticeRec; /* notice message receiver */
void *noticeRecArg;
PQnoticeProcessor noticeProc; /* notice message processor */
void *noticeProcArg;
} PGNoticeHooks;
+ typedef struct+ {+ char *name; /* for error messages */+ void *passThrough; /* pointer supplied by user */+ void *data; /* state (instance) data, optionally generated by event proc */+ PGEventProc proc; /* the function to call on events */+ } PGEvent;+
struct pg_result
{
int ntups;
int numAttributes;
PGresAttDesc *attDescs;
PGresAttValue **tuples; /* each PGresTuple is an array of
***************
*** 181,192 ****
--- 177,192 ----
* These fields are copied from the originating PGconn, so that operations
* on the PGresult don't have to reference the PGconn.
*/
PGNoticeHooks noticeHooks;
int client_encoding; /* encoding id */
+ /* registered events, copied from conn */+ int nEvents;+ PGEvent *events;+
/*
* Error information (all NULL if not an error result). errMsg is the
* "overall" error message returned by PQresultErrorMessage. If we have
* per-field info then it is stored in a linked list.
*/
char *errMsg; /* error message, or NULL if no error */
***************
*** 300,311 ****
--- 300,316 ----
/* Optional file to write trace info to */
FILE *Pfdebug;
/* Callback procedures for notice message processing */
PGNoticeHooks noticeHooks;
+ /* registered events via PQregisterEventProc */+ int nEvents;+ int eventArrSize;+ PGEvent *events;+
/* Status indicators */
ConnStatusType status;
PGAsyncStatusType asyncStatus;
PGTransactionStatusType xactStatus; /* never changes to ACTIVE */
PGQueryClass queryclass;
char *last_query; /* last SQL command, or NULL if unknown */
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Reply to all the recipients using the --to and --cc options:
reply via email
To: pgsql-patches@postgresql.org
Cc: ac@esilo.com, tgl@sss.pgh.pa.us, alvherre@commandprompt.com, pgsql-hackers@postgresql.org, mmoncure@gmail.com
Subject: Re: [PATCHES] libpq events patch (with sgml docs)
In-Reply-To: <48D080B0.6070104@esilo.com>
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
This inbox is served by DDX for PostgreSQL; see mirroring instructions
for how to clone and mirror all data and code used for this inbox