Add an "events" system to libpq, whereby applications can get callbacks that

enable them to manage private data associated with PGconns and PGresults.

Andrew Chernow and Merlin Moncure
This commit is contained in:
Tom Lane 2008-09-17 04:31:08 +00:00
parent b73c0c2a51
commit 32f159cc55
10 changed files with 1469 additions and 72 deletions

View File

@ -1,4 +1,4 @@
<!-- $PostgreSQL: pgsql/doc/src/sgml/libpq.sgml,v 1.260 2008/06/27 02:44:31 momjian Exp $ -->
<!-- $PostgreSQL: pgsql/doc/src/sgml/libpq.sgml,v 1.261 2008/09/17 04:31:08 tgl Exp $ -->
<chapter id="libpq">
<title><application>libpq</application> - C Library</title>
@ -2063,38 +2063,6 @@ PGresult *PQdescribePortal(PGconn *conn, const char *portalName);
</para>
</listitem>
</varlistentry>
<varlistentry>
<term>
<function>PQmakeEmptyPGresult</function>
<indexterm>
<primary>PQmakeEmptyPGresult</primary>
</indexterm>
</term>
<listitem>
<para>
Constructs an empty <structname>PGresult</structname> object with the given status.
<synopsis>
PGresult *PQmakeEmptyPGresult(PGconn *conn, ExecStatusType status);
</synopsis>
</para>
<para>
This is <application>libpq</>'s internal function to allocate and
initialize an empty <structname>PGresult</structname> object. This
function returns NULL if memory could not be allocated. It is
exported because some applications find it useful to generate result
objects (particularly objects with error status) themselves. If
<parameter>conn</parameter> is not null and <parameter>status</>
indicates an error, the current error message of the specified
connection is copied into the <structname>PGresult</structname>.
Note that <function>PQclear</function> should eventually be called
on the object, just as with a <structname>PGresult</structname>
returned by <application>libpq</application> itself.
</para>
</listitem>
</varlistentry>
</variablelist>
</para>
</sect2>
@ -4598,6 +4566,170 @@ char *pg_encoding_to_char(int <replaceable>encoding_id</replaceable>);
</para>
</listitem>
</varlistentry>
<varlistentry>
<term>
<function>PQmakeEmptyPGresult</function>
<indexterm>
<primary>PQmakeEmptyPGresult</primary>
</indexterm>
</term>
<listitem>
<para>
Constructs an empty <structname>PGresult</structname> object with the given status.
<synopsis>
PGresult *PQmakeEmptyPGresult(PGconn *conn, ExecStatusType status);
</synopsis>
</para>
<para>
This is <application>libpq</>'s internal function to allocate and
initialize an empty <structname>PGresult</structname> object. This
function returns NULL if memory could not be allocated. It is
exported because some applications find it useful to generate result
objects (particularly objects with error status) themselves. If
<parameter>conn</parameter> is not null and <parameter>status</>
indicates an error, the current error message of the specified
connection is copied into the <structname>PGresult</structname>.
Also, if <parameter>conn</parameter> is not null, any event handlers
registered in the connection are copied into the
<structname>PGresult</structname> (but they don't get
<literal>PGEVT_RESULTCREATE</> calls).
Note that <function>PQclear</function> should eventually be called
on the object, just as with a <structname>PGresult</structname>
returned by <application>libpq</application> itself.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term>
<function>PQcopyResult</function>
<indexterm>
<primary>PQcopyResult</primary>
</indexterm>
</term>
<listitem>
<para>
Makes a copy of a <structname>PGresult</structname> object. The copy is
not linked to the source result in any way and
<function>PQclear</function> must be called when the copy is no longer
needed. If the function fails, NULL is returned.
<synopsis>
PGresult *PQcopyResult(const PGresult *src, int flags);
</synopsis>
</para>
<para>
This is not intended to make an exact copy. The returned result is
always put into <literal>PGRES_TUPLES_OK</literal> status, and does not
copy any error message in the source. (It does copy the command status
string, however.) The <parameter>flags</parameter> argument determines
what else is copied. It is a bitwise OR of several flags.
<literal>PG_COPYRES_ATTRS</literal> specifies copying the source
result's attributes (column definitions).
<literal>PG_COPYRES_TUPLES</literal> specifies copying the source
result's tuples. (This implies copying the attributes, too.)
<literal>PG_COPYRES_NOTICEHOOKS</literal> specifies
copying the source result's notify hooks.
<literal>PG_COPYRES_EVENTS</literal> specifies copying the source
result's events. (But any instance data associated with the source
is not copied.)
</para>
</listitem>
</varlistentry>
<varlistentry>
<term>
<function>PQsetResultAttrs</function>
<indexterm>
<primary>PQsetResultAttrs</primary>
</indexterm>
</term>
<listitem>
<para>
Sets the attributes of a <structname>PGresult</structname> object.
<synopsis>
int PQsetResultAttrs(PGresult *res, int numAttributes, PGresAttDesc *attDescs);
</synopsis>
</para>
<para>
The provided <parameter>attDescs</parameter> are copied into the result.
If the <parameter>attDescs</parameter> pointer is NULL or
<parameter>numAttributes</parameter> is less than one, the request is
ignored and the function succeeds. If <parameter>res</parameter>
already contains attributes, the function will fail. If the function
fails, the return value is zero. If the function succeeds, the return
value is non-zero.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term>
<function>PQsetvalue</function>
<indexterm>
<primary>PQsetvalue</primary>
</indexterm>
</term>
<listitem>
<para>
Sets a tuple field value of a <structname>PGresult</structname> object.
<synopsis>
int PQsetvalue(PGresult *res, int tup_num, int field_num, char *value, int len);
</synopsis>
</para>
<para>
The function will automatically grow the result's internal tuples array
as needed. However, the <parameter>tup_num</parameter> argument must be
less than or equal to <function>PQntuples</function>, meaning this
function can only grow the tuples array one tuple at a time. But any
field of any existing tuple can be modified in any order. If a value at
<parameter>field_num</parameter> already exists, it will be overwritten.
If <parameter>len</parameter> is <literal>-1</literal> or
<parameter>value</parameter> is <literal>NULL</literal>, the field value
will be set to an SQL <literal>NULL</literal>. The
<parameter>value</parameter> is copied into the result's private storage,
thus is no longer needed after the function
returns. If the function fails, the return value is zero. If the
function succeeds, the return value is non-zero.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term>
<function>PQresultAlloc</function>
<indexterm>
<primary>PQresultAlloc</primary>
</indexterm>
</term>
<listitem>
<para>
Allocate subsidiary storage for a <structname>PGresult</structname> object.
<synopsis>
void *PQresultAlloc(PGresult *res, size_t nBytes);
</synopsis>
</para>
<para>
Any memory allocated with this function will be freed when
<parameter>res</parameter> is cleared. If the function fails,
the return value is <literal>NULL</literal>. The result is
guaranteed to be adequately aligned for any type of data,
just as for <function>malloc</>.
</para>
</listitem>
</varlistentry>
</variablelist>
</sect1>
@ -4711,6 +4843,551 @@ defaultNoticeProcessor(void *arg, const char *message)
</sect1>
<sect1 id="libpq-events">
<title>Event System</title>
<para>
<application>libpq</application>'s event system is designed to notify
registered event handlers about interesting
<application>libpq</application> events, such as the creation or
destruction of <structname>PGconn</structname> and
<structname>PGresult</structname> objects. A principal use case is that
this allows applications to associate their own data with a
<structname>PGconn</structname> or <structname>PGresult</structname>
and ensure that that data is freed at an appropriate time.
</para>
<para>
Each registered event handler is associated with two pieces of data,
known to <application>libpq</application> only as opaque <literal>void *</>
pointers. There is a <firstterm>passthrough</> pointer that is provided
by the application when the event handler is registered with a
<structname>PGconn</>. The passthrough pointer never changes for the
life of the <structname>PGconn</> and all <structname>PGresult</>s
generated from it; so if used, it must point to long-lived data.
In addition there is an <firstterm>instance data</> pointer, which starts
out NULL in every <structname>PGconn</> and <structname>PGresult</>.
This pointer can be manipulated using the
<function>PQinstanceData</function>,
<function>PQsetInstanceData</function>,
<function>PQresultInstanceData</function> and
<function>PQsetResultInstanceData</function> functions. Note that
unlike the passthrough pointer, instance data of a <structname>PGconn</>
is not automatically inherited by <structname>PGresult</>s created from
it. <application>libpq</application> does not know what passthrough
and instance data pointers point to (if anything) and will never attempt
to free them &mdash; that is the responsibility of the event handler.
</para>
<sect2 id="libpq-events-types">
<title>Event Types</title>
<para>
The enum <literal>PGEventId</> names the types of events handled by
the event system. All its values have names beginning with
<literal>PGEVT</literal>. For each event type, there is a corresponding
event info structure that carries the parameters passed to the event
handlers. The event types are:
</para>
<variablelist>
<varlistentry>
<term><literal>PGEVT_REGISTER</literal></term>
<listitem>
<para>
The register event occurs when <function>PQregisterEventProc</function>
is called. It is the ideal time to initialize any
<literal>instanceData</literal> an event procedure may need. Only one
register event will be fired per event handler per connection. If the
event procedure fails, the registration is aborted.
<synopsis>
typedef struct
{
const PGconn *conn;
} PGEventRegister;
</synopsis>
When a <literal>PGEVT_REGISTER</literal> event is received, the
<parameter>evtInfo</parameter> pointer should be cast to a
<structname>PGEventRegister *</structname>. This structure contains a
<structname>PGconn</structname> that should be in the
<literal>CONNECTION_OK</literal> status; guaranteed if one calls
<function>PQregisterEventProc</function> right after obtaining a good
<structname>PGconn</structname>.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term><literal>PGEVT_CONNRESET</literal></term>
<listitem>
<para>
The connection reset event is fired on completion of
<function>PQreset</function> or <function>PQresetPoll</function>. In
both cases, the event is only fired if the reset was successful. If
the event procedure fails, the entire connection reset will fail; the
<structname>PGconn</structname> is put into
<literal>CONNECTION_BAD</literal> status and
<function>PQresetPoll</function> will return
<literal>PGRES_POLLING_FAILED</literal>.
<synopsis>
typedef struct
{
const PGconn *conn;
} PGEventConnReset;
</synopsis>
When a <literal>PGEVT_CONNRESET</literal> event is received, the
<parameter>evtInfo</parameter> pointer should be cast to a
<structname>PGEventConnReset *</structname>. Although the contained
<structname>PGconn</structname> was just reset, all event data remains
unchanged. This event should be used to reset/reload/requery any
associated <literal>instanceData</literal>.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term><literal>PGEVT_CONNDESTROY</literal></term>
<listitem>
<para>
The connection destroy event is fired in response to
<function>PQfinish</function>. It is the event procedure's
responsibility to properly clean up its event data as libpq has no
ability to manage this memory. Failure to clean up will lead
to memory leaks.
<synopsis>
typedef struct
{
const PGconn *conn;
} PGEventConnDestroy;
</synopsis>
When a <literal>PGEVT_CONNDESTROY</literal> event is received, the
<parameter>evtInfo</parameter> pointer should be cast to a
<structname>PGEventConnDestroy *</structname>. This event is fired
prior to <function>PQfinish</function> performing any other cleanup.
The return value of the event procedure is ignored since there is no
way of indicating a failure from <function>PQfinish</function>. Also,
an event procedure failure should not abort the process of cleaning up
unwanted memory.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term><literal>PGEVT_RESULTCREATE</literal></term>
<listitem>
<para>
The result creation event is fired in response to any query execution
function that generates a result, including
<function>PQgetResult</function>. This event will only be fired after
the result has been created successfully.
<synopsis>
typedef struct
{
const PGconn *conn;
PGresult *result;
} PGEventResultCreate;
</synopsis>
When a <literal>PGEVT_RESULTCREATE</literal> event is received, the
<parameter>evtInfo</parameter> pointer should be cast to a
<structname>PGEventResultCreate *</structname>. The
<parameter>conn</parameter> is the connection used to generate the
result. This is the ideal place to initialize any
<literal>instanceData</literal> that needs to be associated with the
result. If the event procedure fails, the result will be cleared and
the failure will be propagated. The event procedure must not try to
<function>PQclear</> the result object for itself.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term><literal>PGEVT_RESULTCOPY</literal></term>
<listitem>
<para>
The result copy event is fired in response to
<function>PQcopyResult</function>. This event will only be fired after
the copy is complete.
<synopsis>
typedef struct
{
const PGresult *src;
PGresult *dest;
} PGEventResultCopy;
</synopsis>
When a <literal>PGEVT_RESULTCOPY</literal> event is received, the
<parameter>evtInfo</parameter> pointer should be cast to a
<structname>PGEventResultCopy *</structname>. The
<parameter>src</parameter> result is what was copied while the
<parameter>dest</parameter> result is the copy destination. This event
can be used to provide a deep copy of <literal>instanceData</literal>,
since <literal>PQcopyResult</literal> cannot do that. If the event
procedure fails, the entire copy operation will fail and the
<parameter>dest</parameter> result will be cleared.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term><literal>PGEVT_RESULTDESTROY</literal></term>
<listitem>
<para>
The result destroy event is fired in response to a
<function>PQclear</function>. It is the event procedure's
responsibility to properly clean up its event data as libpq has no
ability to manage this memory. Failure to clean up will lead
to memory leaks.
<synopsis>
typedef struct
{
const PGresult *result;
} PGEventResultDestroy;
</synopsis>
When a <literal>PGEVT_RESULTDESTROY</literal> event is received, the
<parameter>evtInfo</parameter> pointer should be cast to a
<structname>PGEventResultDestroy *</structname>. This event is fired
prior to <function>PQclear</function> performing any other cleanup.
The return value of the event procedure is ignored since there is no
way of indicating a failure from <function>PQclear</function>. Also,
an event procedure failure should not abort the process of cleaning up
unwanted memory.
</para>
</listitem>
</varlistentry>
</variablelist>
</sect2>
<sect2 id="libpq-events-proc">
<title>Event Callback Procedure</title>
<variablelist>
<varlistentry>
<term>
<literal>PGEventProc</literal>
<indexterm>
<primary>PGEventProc</primary>
</indexterm>
</term>
<listitem>
<para>
<literal>PGEventProc</literal> is a typedef for a pointer to an
event procedure, that is, the user callback function that receives
events from libpq. The signature of an event procedure must be
<synopsis>
int eventproc(PGEventId evtId, void *evtInfo, void *passThrough)
</synopsis>
The <parameter>evtId</parameter> parameter indicates which
<literal>PGEVT</literal> event occurred. The
<parameter>evtInfo</parameter> pointer must be cast to the appropriate
structure type to obtain further information about the event.
The <parameter>passThrough</parameter> parameter is the pointer
provided to <function>PQregisterEventProc</function> when the event
procedure was registered. The function should return a non-zero value
if it succeeds and zero if it fails.
</para>
<para>
A particular event procedure can be registered only once in any
<structname>PGconn</>. This is because the address of the procedure
is used as a lookup key to identify the associated instance data.
</para>
</listitem>
</varlistentry>
</variablelist>
</sect2>
<sect2 id="libpq-events-funcs">
<title>Event Support Functions</title>
<variablelist>
<varlistentry>
<term>
<function>PQregisterEventProc</function>
<indexterm>
<primary>PQregisterEventProc</primary>
</indexterm>
</term>
<listitem>
<para>
Registers an event callback procedure with libpq.
<synopsis>
int PQregisterEventProc(PGconn *conn, PGEventProc proc,
const char *name, void *passThrough);
</synopsis>
</para>
<para>
An event procedure must be registered once on each
<structname>PGconn</> you want to receive events about. There is no
limit, other than memory, on the number of event procedures that
can be registered with a connection. The function returns a non-zero
value if it succeeds and zero if it fails.
</para>
<para>
The <parameter>proc</parameter> argument will be called when a libpq
event is fired. Its memory address is also used to lookup
<literal>instanceData</literal>. The <parameter>name</parameter>
argument is used to refer to the event procedure in error messages.
This value cannot be NULL or a zero-length string. The name string is
copied into the <structname>PGconn</>, so what is passed need not be
long-lived. The <parameter>passThrough</parameter> pointer is passed
to the <parameter>proc</parameter> whenever an event occurs. This
argument can be NULL.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term>
<function>PQsetInstanceData</function>
<indexterm>
<primary>PQsetInstanceData</primary>
</indexterm>
</term>
<listitem>
<para>
Sets the conn's instanceData for proc to data. This returns non-zero
for success and zero for failure. (Failure is only possible if
the proc has not been properly registered in the conn.)
<synopsis>
int PQsetInstanceData(PGconn *conn, PGEventProc proc, void *data);
</synopsis>
</para>
</listitem>
</varlistentry>
<varlistentry>
<term>
<function>PQinstanceData</function>
<indexterm>
<primary>PQinstanceData</primary>
</indexterm>
</term>
<listitem>
<para>
Returns the conn's instanceData associated with proc, or NULL
if there is none.
<synopsis>
void *PQinstanceData(const PGconn *conn, PGEventProc proc);
</synopsis>
</para>
</listitem>
</varlistentry>
<varlistentry>
<term>
<function>PQresultSetInstanceData</function>
<indexterm>
<primary>PQresultSetInstanceData</primary>
</indexterm>
</term>
<listitem>
<para>
Sets the result's instanceData for proc to data. This returns non-zero
for success and zero for failure. (Failure is only possible if the
proc has not been properly registered in the result.)
<synopsis>
int PQresultSetInstanceData(PGresult *res, PGEventProc proc, void *data);
</synopsis>
</para>
</listitem>
</varlistentry>
<varlistentry>
<term>
<function>PQresultInstanceData</function>
<indexterm>
<primary>PQresultInstanceData</primary>
</indexterm>
</term>
<listitem>
<para>
Returns the result's instanceData associated with proc, or NULL
if there is none.
<synopsis>
void *PQresultInstanceData(const PGresult *res, PGEventProc proc);
</synopsis>
</para>
</listitem>
</varlistentry>
</variablelist>
</sect2>
<sect2 id="libpq-events-example">
<title>Event Example</title>
<para>
Here is a skeleton example of managing private data associated with
libpq connections and results.
</para>
<programlisting>
/* required header for libpq events (note: includes libpq-fe.h) */
#include &lt;libpq-events.h&gt;
/* The instanceData */
typedef struct
{
int n;
char *str;
} mydata;
/* PGEventProc */
static int myEventProc(PGEventId evtId, void *evtInfo, void *passThrough);
int
main(void)
{
mydata *data;
PGresult *res;
PGconn *conn = PQconnectdb("dbname = postgres");
if (PQstatus(conn) != CONNECTION_OK)
{
fprintf(stderr, "Connection to database failed: %s",
PQerrorMessage(conn));
PQfinish(conn);
return 1;
}
/* called once on any connection that should receive events.
* Sends a PGEVT_REGISTER to myEventProc.
*/
if (!PQregisterEventProc(conn, myEventProc, "mydata_proc", NULL))
{
fprintf(stderr, "Cannot register PGEventProc\n");
PQfinish(conn);
return 1;
}
/* conn instanceData is available */
data = PQinstanceData(conn, myEventProc);
/* Sends a PGEVT_RESULTCREATE to myEventProc */
res = PQexec(conn, "SELECT 1 + 1");
/* result instanceData is available */
data = PQresultInstanceData(res, myEventProc);
/* If PG_COPYRES_EVENTS is used, sends a PGEVT_RESULTCOPY to myEventProc */
res_copy = PQcopyResult(res, PG_COPYRES_TUPLES | PG_COPYRES_EVENTS);
/* result instanceData is available if PG_COPYRES_EVENTS was
* used during the PQcopyResult call.
*/
data = PQresultInstanceData(res_copy, myEventProc);
/* Both clears send a PGEVT_RESULTDESTROY to myEventProc */
PQclear(res);
PQclear(res_copy);
/* Sends a PGEVT_CONNDESTROY to myEventProc */
PQfinish(conn);
return 0;
}
static int
myEventProc(PGEventId evtId, void *evtInfo, void *passThrough)
{
switch (evtId)
{
case PGEVT_REGISTER:
{
PGEventRegister *e = (PGEventRegister *)evtInfo;
mydata *data = get_mydata(e-&gt;conn);
/* associate app specific data with connection */
PQsetInstanceData(e-&gt;conn, myEventProc, data);
break;
}
case PGEVT_CONNRESET:
{
PGEventConnReset *e = (PGEventConnReset *)evtInfo;
mydata *data = PQinstanceData(e-&gt;conn, myEventProc);
if (data)
memset(data, 0, sizeof(mydata));
break;
}
case PGEVT_CONNDESTROY:
{
PGEventConnDestroy *e = (PGEventConnDestroy *)evtInfo;
mydata *data = PQinstanceData(e-&gt;conn, myEventProc);
/* free instance data because the conn is being destroyed */
if (data)
free_mydata(data);
break;
}
case PGEVT_RESULTCREATE:
{
PGEventResultCreate *e = (PGEventResultCreate *)evtInfo;
mydata *conn_data = PQinstanceData(e-&gt;conn, myEventProc);
mydata *res_data = dup_mydata(conn_data);
/* associate app specific data with result (copy it from conn) */
PQsetResultInstanceData(e-&gt;result, myEventProc, res_data);
break;
}
case PGEVT_RESULTCOPY:
{
PGEventResultCopy *e = (PGEventResultCopy *)evtInfo;
mydata *src_data = PQresultInstanceData(e-&gt;src, myEventProc);
mydata *dest_data = dup_mydata(src_data);
/* associate app specific data with result (copy it from a result) */
PQsetResultInstanceData(e-&gt;dest, myEventProc, dest_data);
break;
}
case PGEVT_RESULTDESTROY:
{
PGEventResultDestroy *e = (PGEventResultDestroy *)evtInfo;
mydata *data = PQresultInstanceData(e-&gt;result, myEventProc);
/* free instance data because the result is being destroyed */
if (data)
free_mydata(data);
break;
}
/* unknown event id, just return TRUE. */
default:
break;
}
return TRUE; /* event processing succeeded */
}
</programlisting>
</sect2>
</sect1>
<sect1 id="libpq-envars">
<title>Environment Variables</title>
@ -5263,7 +5940,7 @@ defaultNoticeProcessor(void *arg, const char *message)
to inside <application>libpq</application>), you can use
<function>PQinitSSL(int)</> to tell <application>libpq</application>
that the <acronym>SSL</> library has already been initialized by your
application.
application.
<!-- If this URL changes replace it with a URL to www.archive.org. -->
See <ulink
url="http://h71000.www7.hp.com/doc/83final/BA554_90007/ch04.html"></ulink>

View File

@ -5,7 +5,7 @@
# Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group
# Portions Copyright (c) 1994, Regents of the University of California
#
# $PostgreSQL: pgsql/src/interfaces/libpq/Makefile,v 1.166 2008/04/16 14:19:56 adunstan Exp $
# $PostgreSQL: pgsql/src/interfaces/libpq/Makefile,v 1.167 2008/09/17 04:31:08 tgl Exp $
#
#-------------------------------------------------------------------------
@ -32,6 +32,7 @@ LIBS := $(LIBS:-lpgport=)
OBJS= fe-auth.o fe-connect.o fe-exec.o fe-misc.o fe-print.o fe-lobj.o \
fe-protocol2.o fe-protocol3.o pqexpbuffer.o pqsignal.o fe-secure.o \
libpq-events.o \
md5.o ip.o wchar.o encnames.o noblock.o pgstrcasecmp.o thread.o \
$(filter crypt.o getaddrinfo.o inet_aton.o open.o snprintf.o strerror.o strlcpy.o win32error.o, $(LIBOBJS))
@ -106,6 +107,7 @@ $(top_builddir)/src/port/pg_config_paths.h:
install: all installdirs install-lib
$(INSTALL_DATA) $(srcdir)/libpq-fe.h '$(DESTDIR)$(includedir)'
$(INSTALL_DATA) $(srcdir)/libpq-events.h '$(DESTDIR)$(includedir)'
$(INSTALL_DATA) $(srcdir)/libpq-int.h '$(DESTDIR)$(includedir_internal)'
$(INSTALL_DATA) $(srcdir)/pqexpbuffer.h '$(DESTDIR)$(includedir_internal)'
$(INSTALL_DATA) $(srcdir)/pg_service.conf.sample '$(DESTDIR)$(datadir)/pg_service.conf.sample'
@ -114,7 +116,11 @@ installdirs: installdirs-lib
$(mkinstalldirs) '$(DESTDIR)$(includedir)' '$(DESTDIR)$(includedir_internal)'
uninstall: uninstall-lib
rm -f '$(DESTDIR)$(includedir)/libpq-fe.h' '$(DESTDIR)$(includedir_internal)/libpq-int.h' '$(DESTDIR)$(includedir_internal)/pqexpbuffer.h' '$(DESTDIR)$(datadir)/pg_service.conf.sample'
rm -f '$(DESTDIR)$(includedir)/libpq-fe.h'
rm -f '$(DESTDIR)$(includedir)/libpq-events.h'
rm -f '$(DESTDIR)$(includedir_internal)/libpq-int.h'
rm -f '$(DESTDIR)$(includedir_internal)/pqexpbuffer.h'
rm -f '$(DESTDIR)$(datadir)/pg_service.conf.sample'
clean distclean: clean-lib
rm -f $(OBJS) pg_config_paths.h crypt.c getaddrinfo.c inet_aton.c noblock.c open.c pgstrcasecmp.c snprintf.c strerror.c strlcpy.c thread.c md5.c ip.c encnames.c wchar.c win32error.c pgsleep.c pthread.h libpq.rc

View File

@ -1,4 +1,4 @@
# $PostgreSQL: pgsql/src/interfaces/libpq/exports.txt,v 1.19 2008/03/19 00:39:33 ishii Exp $
# $PostgreSQL: pgsql/src/interfaces/libpq/exports.txt,v 1.20 2008/09/17 04:31:08 tgl Exp $
# Functions to be exported by libpq DLLs
PQconnectdb 1
PQsetdbLogin 2
@ -140,4 +140,13 @@ lo_truncate 137
PQconnectionUsedPassword 138
pg_valid_server_encoding_id 139
PQconnectionNeedsPassword 140
lo_import_with_oid 141
lo_import_with_oid 141
PQcopyResult 142
PQsetResultAttrs 143
PQsetvalue 144
PQresultAlloc 145
PQregisterEventProc 146
PQinstanceData 147
PQsetInstanceData 148
PQresultInstanceData 149
PQresultSetInstanceData 150

View File

@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/interfaces/libpq/fe-connect.c,v 1.359 2008/05/29 22:02:44 tgl Exp $
* $PostgreSQL: pgsql/src/interfaces/libpq/fe-connect.c,v 1.360 2008/09/17 04:31:08 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -1974,6 +1974,21 @@ makeEmptyPGconn(void)
static void
freePGconn(PGconn *conn)
{
int i;
/* let any event procs clean up their state data */
for (i = 0; i < conn->nEvents; i++)
{
PGEventConnDestroy evt;
evt.conn = conn;
(void) conn->events[i].proc(PGEVT_CONNDESTROY, &evt,
conn->events[i].passThrough);
free(conn->events[i].name);
}
if (conn->events)
free(conn->events);
if (conn->pghost)
free(conn->pghost);
if (conn->pghostaddr)
@ -2155,8 +2170,30 @@ PQreset(PGconn *conn)
{
closePGconn(conn);
if (connectDBStart(conn))
(void) connectDBComplete(conn);
if (connectDBStart(conn) && connectDBComplete(conn))
{
/*
* Notify event procs of successful reset. We treat an event
* proc failure as disabling the connection ... good idea?
*/
int i;
for (i = 0; i < conn->nEvents; i++)
{
PGEventConnReset evt;
evt.conn = conn;
if (!conn->events[i].proc(PGEVT_CONNRESET, &evt,
conn->events[i].passThrough))
{
conn->status = CONNECTION_BAD;
printfPQExpBuffer(&conn->errorMessage,
libpq_gettext("PGEventProc \"%s\" failed during PGEVT_CONNRESET event\n"),
conn->events[i].name);
break;
}
}
}
}
}
@ -2190,7 +2227,36 @@ PostgresPollingStatusType
PQresetPoll(PGconn *conn)
{
if (conn)
return PQconnectPoll(conn);
{
PostgresPollingStatusType status = PQconnectPoll(conn);
if (status == PGRES_POLLING_OK)
{
/*
* Notify event procs of successful reset. We treat an event
* proc failure as disabling the connection ... good idea?
*/
int i;
for (i = 0; i < conn->nEvents; i++)
{
PGEventConnReset evt;
evt.conn = conn;
if (!conn->events[i].proc(PGEVT_CONNRESET, &evt,
conn->events[i].passThrough))
{
conn->status = CONNECTION_BAD;
printfPQExpBuffer(&conn->errorMessage,
libpq_gettext("PGEventProc \"%s\" failed during PGEVT_CONNRESET event\n"),
conn->events[i].name);
return PGRES_POLLING_FAILED;
}
}
}
return status;
}
return PGRES_POLLING_FAILED;
}

View File

@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/interfaces/libpq/fe-exec.c,v 1.197 2008/09/10 17:01:07 tgl Exp $
* $PostgreSQL: pgsql/src/interfaces/libpq/fe-exec.c,v 1.198 2008/09/17 04:31:08 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -48,6 +48,7 @@ static int static_client_encoding = PG_SQL_ASCII;
static bool static_std_strings = false;
static PGEvent *dupEvents(PGEvent *events, int count);
static bool PQsendQueryStart(PGconn *conn);
static int PQsendQueryGuts(PGconn *conn,
const char *command,
@ -63,6 +64,7 @@ static bool PQexecStart(PGconn *conn);
static PGresult *PQexecFinish(PGconn *conn);
static int PQsendDescribe(PGconn *conn, char desc_type,
const char *desc_target);
static int check_field_number(const PGresult *res, int field_num);
/* ----------------
@ -128,13 +130,8 @@ static int PQsendDescribe(PGconn *conn, char desc_type,
* PQmakeEmptyPGresult
* returns a newly allocated, initialized PGresult with given status.
* If conn is not NULL and status indicates an error, the conn's
* errorMessage is copied.
*
* Note this is exported --- you wouldn't think an application would need
* to build its own PGresults, but this has proven useful in both libpgtcl
* and the Perl5 interface, so maybe it's not so unreasonable.
* errorMessage is copied. Also, any PGEvents are copied from the conn.
*/
PGresult *
PQmakeEmptyPGresult(PGconn *conn, ExecStatusType status)
{
@ -154,6 +151,8 @@ PQmakeEmptyPGresult(PGconn *conn, ExecStatusType status)
result->resultStatus = status;
result->cmdStatus[0] = '\0';
result->binary = 0;
result->events = NULL;
result->nEvents = 0;
result->errMsg = NULL;
result->errFields = NULL;
result->null_field[0] = '\0';
@ -181,6 +180,18 @@ PQmakeEmptyPGresult(PGconn *conn, ExecStatusType status)
pqSetResultError(result, conn->errorMessage.data);
break;
}
/* copy events last; result must be valid if we need to PQclear */
if (conn->nEvents > 0)
{
result->events = dupEvents(conn->events, conn->nEvents);
if (!result->events)
{
PQclear(result);
return NULL;
}
result->nEvents = conn->nEvents;
}
}
else
{
@ -195,6 +206,301 @@ PQmakeEmptyPGresult(PGconn *conn, ExecStatusType status)
return result;
}
/*
* PQsetResultAttrs
*
* Set the attributes for a given result. This function fails if there are
* already attributes contained in the provided result. The call is
* ignored if numAttributes is is zero or attDescs is NULL. If the
* function fails, it returns zero. If the function succeeds, it
* returns a non-zero value.
*/
int
PQsetResultAttrs(PGresult *res, int numAttributes, PGresAttDesc *attDescs)
{
int i;
/* If attrs already exist, they cannot be overwritten. */
if (!res || res->numAttributes > 0)
return FALSE;
/* ignore no-op request */
if (numAttributes <= 0 || !attDescs)
return TRUE;
res->attDescs = (PGresAttDesc *)
PQresultAlloc(res, numAttributes * sizeof(PGresAttDesc));
if (!res->attDescs)
return FALSE;
res->numAttributes = numAttributes;
memcpy(res->attDescs, attDescs, numAttributes * sizeof(PGresAttDesc));
/* deep-copy the attribute names, and determine format */
res->binary = 1;
for (i = 0; i < res->numAttributes; i++)
{
if (res->attDescs[i].name)
res->attDescs[i].name = pqResultStrdup(res, res->attDescs[i].name);
else
res->attDescs[i].name = res->null_field;
if (!res->attDescs[i].name)
return FALSE;
if (res->attDescs[i].format == 0)
res->binary = 0;
}
return TRUE;
}
/*
* PQcopyResult
*
* Returns a deep copy of the provided 'src' PGresult, which cannot be NULL.
* The 'flags' argument controls which portions of the result will or will
* NOT be copied. The created result is always put into the
* PGRES_TUPLES_OK status. The source result error message is not copied,
* although cmdStatus is.
*
* To set custom attributes, use PQsetResultAttrs. That function requires
* that there are no attrs contained in the result, so to use that
* function you cannot use the PG_COPYRES_ATTRS or PG_COPYRES_TUPLES
* options with this function.
*
* Options:
* PG_COPYRES_ATTRS - Copy the source result's attributes
*
* PG_COPYRES_TUPLES - Copy the source result's tuples. This implies
* copying the attrs, seeeing how the attrs are needed by the tuples.
*
* PG_COPYRES_EVENTS - Copy the source result's events.
*
* PG_COPYRES_NOTICEHOOKS - Copy the source result's notice hooks.
*/
PGresult *
PQcopyResult(const PGresult *src, int flags)
{
PGresult *dest;
int i;
if (!src)
return NULL;
dest = PQmakeEmptyPGresult(NULL, PGRES_TUPLES_OK);
if (!dest)
return NULL;
/* Always copy these over. Is cmdStatus really useful here? */
dest->client_encoding = src->client_encoding;
strcpy(dest->cmdStatus, src->cmdStatus);
/* Wants attrs? */
if (flags & (PG_COPYRES_ATTRS | PG_COPYRES_TUPLES))
{
if (!PQsetResultAttrs(dest, src->numAttributes, src->attDescs))
{
PQclear(dest);
return NULL;
}
}
/* Wants to copy tuples? */
if (flags & PG_COPYRES_TUPLES)
{
int tup, field;
for (tup = 0; tup < src->ntups; tup++)
{
for (field = 0; field < src->numAttributes; field++)
{
if (!PQsetvalue(dest, tup, field,
src->tuples[tup][field].value,
src->tuples[tup][field].len))
{
PQclear(dest);
return NULL;
}
}
}
}
/* Wants to copy notice hooks? */
if (flags & PG_COPYRES_NOTICEHOOKS)
dest->noticeHooks = src->noticeHooks;
/*
* Wants to copy PGEvents? NB: this should be last, as we don't want
* to trigger RESULTDESTROY events on a useless PGresult.
*/
if ((flags & PG_COPYRES_EVENTS) && src->nEvents > 0)
{
dest->events = dupEvents(src->events, src->nEvents);
if (!dest->events)
{
PQclear(dest);
return NULL;
}
dest->nEvents = src->nEvents;
}
/* Okay, trigger PGEVT_RESULTCOPY event */
for (i = 0; i < dest->nEvents; i++)
{
PGEventResultCopy evt;
evt.src = src;
evt.dest = dest;
if (!dest->events[i].proc(PGEVT_RESULTCOPY, &evt,
dest->events[i].passThrough))
{
PQclear(dest);
return NULL;
}
}
return dest;
}
/*
* Copy an array of PGEvents (with no extra space for more)
* Does not duplicate the event instance data, sets this to NULL
*/
static PGEvent *
dupEvents(PGEvent *events, int count)
{
PGEvent *newEvents;
int i;
if (!events || count <= 0)
return NULL;
newEvents = (PGEvent *) malloc(count * sizeof(PGEvent));
if (!newEvents)
return NULL;
memcpy(newEvents, events, count * sizeof(PGEvent));
/* NULL out the data pointers and deep copy names */
for (i = 0; i < count; i++)
{
newEvents[i].data = NULL;
newEvents[i].name = strdup(newEvents[i].name);
if (!newEvents[i].name)
{
while (--i >= 0)
free(newEvents[i].name);
free(newEvents);
return NULL;
}
}
return newEvents;
}
/*
* Sets the value for a tuple field. The tup_num must be less than or
* equal to PQntuples(res). If it is equal, a new tuple is created and
* added to the result.
* Returns a non-zero value for success and zero for failure.
*/
int
PQsetvalue(PGresult *res, int tup_num, int field_num, char *value, int len)
{
PGresAttValue *attval;
if (!check_field_number(res, field_num))
return FALSE;
/* Invalid tup_num, must be <= ntups */
if (tup_num < 0 || tup_num > res->ntups)
return FALSE;
/* need to grow the tuple table? */
if (res->ntups >= res->tupArrSize)
{
int n = res->tupArrSize ? res->tupArrSize * 2 : 128;
PGresAttValue **tups;
if (res->tuples)
tups = (PGresAttValue **) realloc(res->tuples, n * sizeof(PGresAttValue *));
else
tups = (PGresAttValue **) malloc(n * sizeof(PGresAttValue *));
if (!tups)
return FALSE;
memset(tups + res->tupArrSize, 0,
(n - res->tupArrSize) * sizeof(PGresAttValue *));
res->tuples = tups;
res->tupArrSize = n;
}
/* need to allocate a new tuple? */
if (tup_num == res->ntups && !res->tuples[tup_num])
{
PGresAttValue *tup;
int i;
tup = (PGresAttValue *)
pqResultAlloc(res, res->numAttributes * sizeof(PGresAttValue),
TRUE);
if (!tup)
return FALSE;
/* initialize each column to NULL */
for (i = 0; i < res->numAttributes; i++)
{
tup[i].len = NULL_LEN;
tup[i].value = res->null_field;
}
res->tuples[tup_num] = tup;
res->ntups++;
}
attval = &res->tuples[tup_num][field_num];
/* treat either NULL_LEN or NULL value pointer as a NULL field */
if (len == NULL_LEN || value == NULL)
{
attval->len = NULL_LEN;
attval->value = res->null_field;
}
else if (len <= 0)
{
attval->len = 0;
attval->value = res->null_field;
}
else
{
attval->value = (char *) pqResultAlloc(res, len + 1, TRUE);
if (!attval->value)
return FALSE;
attval->len = len;
memcpy(attval->value, value, len);
attval->value[len] = '\0';
}
return TRUE;
}
/*
* pqResultAlloc - exported routine to allocate local storage in a PGresult.
*
* We force all such allocations to be maxaligned, since we don't know
* whether the value might be binary.
*/
void *
PQresultAlloc(PGresult *res, size_t nBytes)
{
return pqResultAlloc(res, nBytes, TRUE);
}
/*
* pqResultAlloc -
* Allocate subsidiary storage for a PGresult.
@ -353,10 +659,24 @@ void
PQclear(PGresult *res)
{
PGresult_data *block;
int i;
if (!res)
return;
for (i = 0; i < res->nEvents; i++)
{
PGEventResultDestroy evt;
evt.result = res;
(void) res->events[i].proc(PGEVT_RESULTDESTROY, &evt,
res->events[i].passThrough);
free(res->events[i].name);
}
if (res->events)
free(res->events);
/* Free all the subsidiary blocks */
while ((block = res->curBlock) != NULL)
{
@ -373,6 +693,8 @@ PQclear(PGresult *res)
res->tuples = NULL;
res->paramDescs = NULL;
res->errFields = NULL;
res->events = NULL;
res->nEvents = 0;
/* res->curBlock was zeroed out earlier */
/* Free the PGresult structure itself */
@ -1270,6 +1592,29 @@ PQgetResult(PGconn *conn)
break;
}
if (res)
{
int i;
for (i = 0; i < res->nEvents; i++)
{
PGEventResultCreate evt;
evt.conn = conn;
evt.result = res;
if (!res->events[i].proc(PGEVT_RESULTCREATE, &evt,
res->events[i].passThrough))
{
printfPQExpBuffer(&conn->errorMessage,
libpq_gettext("PGEventProc \"%s\" failed during PGEVT_RESULTCREATE event\n"),
res->events[i].name);
pqSetResultError(res, conn->errorMessage.data);
res->resultStatus = PGRES_FATAL_ERROR;
break;
}
}
}
return res;
}

View File

@ -0,0 +1,176 @@
/*-------------------------------------------------------------------------
*
* libpq-events.c
* functions for supporting the libpq "events" API
*
* Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/interfaces/libpq/libpq-events.c,v 1.1 2008/09/17 04:31:08 tgl Exp $
*
*-------------------------------------------------------------------------
*/
#include "postgres_fe.h"
#include "libpq-fe.h"
#include "libpq-int.h"
/*
* Registers an event proc with the given PGconn.
*
* The same proc can't be registered more than once in a PGconn. This
* restriction is required because we use the proc address to identify
* the event for purposes such as PQinstanceData().
*
* The name argument is used within error messages to aid in debugging.
* A name must be supplied, but it needn't be unique. The string is
* copied, so the passed value needn't be long-lived.
*
* The passThrough argument is an application specific pointer and can be set
* to NULL if not required. It is passed through to the event proc whenever
* the event proc is called, and is not otherwise touched by libpq.
*
* The function returns a non-zero if successful. If the function fails,
* zero is returned.
*/
int
PQregisterEventProc(PGconn *conn, PGEventProc proc,
const char *name, void *passThrough)
{
int i;
PGEventRegister regevt;
if (!proc || !conn || !name || !*name)
return FALSE; /* bad arguments */
for (i = 0; i < conn->nEvents; i++)
{
if (conn->events[i].proc == proc)
return FALSE; /* already registered */
}
if (conn->nEvents >= conn->eventArraySize)
{
PGEvent *e;
int newSize;
newSize = conn->eventArraySize ? conn->eventArraySize * 2 : 8;
if (conn->events)
e = (PGEvent *) realloc(conn->events, newSize * sizeof(PGEvent));
else
e = (PGEvent *) malloc(newSize * sizeof(PGEvent));
if (!e)
return FALSE;
conn->eventArraySize = newSize;
conn->events = e;
}
conn->events[conn->nEvents].proc = proc;
conn->events[conn->nEvents].name = strdup(name);
if (!conn->events[conn->nEvents].name)
return FALSE;
conn->events[conn->nEvents].passThrough = passThrough;
conn->events[conn->nEvents].data = NULL;
conn->nEvents++;
regevt.conn = conn;
if (!proc(PGEVT_REGISTER, &regevt, passThrough))
{
conn->nEvents--;
free(conn->events[conn->nEvents].name);
return FALSE;
}
return TRUE;
}
/*
* Set some "instance data" for an event within a PGconn.
* Returns nonzero on success, zero on failure.
*/
int
PQsetInstanceData(PGconn *conn, PGEventProc proc, void *data)
{
int i;
if (!conn || !proc)
return FALSE;
for (i = 0; i < conn->nEvents; i++)
{
if (conn->events[i].proc == proc)
{
conn->events[i].data = data;
return TRUE;
}
}
return FALSE;
}
/*
* Obtain the "instance data", if any, for the event.
*/
void *
PQinstanceData(const PGconn *conn, PGEventProc proc)
{
int i;
if (!conn || !proc)
return NULL;
for (i = 0; i < conn->nEvents; i++)
{
if (conn->events[i].proc == proc)
return conn->events[i].data;
}
return NULL;
}
/*
* Set some "instance data" for an event within a PGresult.
* Returns nonzero on success, zero on failure.
*/
int
PQresultSetInstanceData(PGresult *result, PGEventProc proc, void *data)
{
int i;
if (!result || !proc)
return FALSE;
for (i = 0; i < result->nEvents; i++)
{
if (result->events[i].proc == proc)
{
result->events[i].data = data;
return TRUE;
}
}
return FALSE;
}
/*
* Obtain the "instance data", if any, for the event.
*/
void *
PQresultInstanceData(const PGresult *result, PGEventProc proc)
{
int i;
if (!result || !proc)
return NULL;
for (i = 0; i < result->nEvents; i++)
if (result->events[i].proc == proc)
return result->events[i].data;
return NULL;
}

View File

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

View File

@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* $PostgreSQL: pgsql/src/interfaces/libpq/libpq-fe.h,v 1.142 2008/03/19 00:39:33 ishii Exp $
* $PostgreSQL: pgsql/src/interfaces/libpq/libpq-fe.h,v 1.143 2008/09/17 04:31:08 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -28,6 +28,14 @@ extern "C"
*/
#include "postgres_ext.h"
/*
* Option flags for PQcopyResult
*/
#define PG_COPYRES_ATTRS 0x01
#define PG_COPYRES_TUPLES 0x02 /* Implies PG_COPYRES_ATTRS */
#define PG_COPYRES_EVENTS 0x04
#define PG_COPYRES_NOTICEHOOKS 0x08
/* Application-visible enum types */
typedef enum
@ -192,6 +200,21 @@ typedef struct
} u;
} PQArgBlock;
/* ----------------
* PGresAttDesc -- Data about a single attribute (column) of a query result
* ----------------
*/
typedef struct pgresAttDesc
{
char *name; /* column name */
Oid tableid; /* source table, if known */
int columnid; /* source column, if known */
int format; /* format code for value (text/binary) */
Oid typid; /* type id */
int typlen; /* type size */
int atttypmod; /* type-specific modifier info */
} PGresAttDesc;
/* ----------------
* Exported functions of libpq
* ----------------
@ -430,13 +453,12 @@ extern void PQfreemem(void *ptr);
/* Note: depending on this is deprecated; use PQconnectionNeedsPassword(). */
#define PQnoPasswordSupplied "fe_sendauth: no password supplied\n"
/*
* Make an empty PGresult with given status (some apps find this
* useful). If conn is not NULL and status indicates an error, the
* conn's errorMessage is copied.
*/
/* Create and manipulate PGresults */
extern PGresult *PQmakeEmptyPGresult(PGconn *conn, ExecStatusType status);
extern PGresult *PQcopyResult(const PGresult *src, int flags);
extern int PQsetResultAttrs(PGresult *res, int numAttributes, PGresAttDesc *attDescs);
extern void *PQresultAlloc(PGresult *res, size_t nBytes);
extern int PQsetvalue(PGresult *res, int tup_num, int field_num, char *value, int len);
/* Quoting strings before inclusion in queries. */
extern size_t PQescapeStringConn(PGconn *conn,

View File

@ -12,7 +12,7 @@
* Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* $PostgreSQL: pgsql/src/interfaces/libpq/libpq-int.h,v 1.131 2008/05/29 22:02:44 tgl Exp $
* $PostgreSQL: pgsql/src/interfaces/libpq/libpq-int.h,v 1.132 2008/09/17 04:31:08 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -22,6 +22,7 @@
/* We assume libpq-fe.h has already been included. */
#include "postgres_fe.h"
#include "libpq-events.h"
#include <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;

View File

@ -3,7 +3,7 @@ package Install;
#
# Package that provides 'make install' functionality for msvc builds
#
# $PostgreSQL: pgsql/src/tools/msvc/Install.pm,v 1.30 2008/09/05 16:54:39 momjian Exp $
# $PostgreSQL: pgsql/src/tools/msvc/Install.pm,v 1.31 2008/09/17 04:31:08 tgl Exp $
#
use strict;
use warnings;
@ -393,7 +393,9 @@ sub CopyIncludeFiles
lcopy('src/include/libpq/libpq-fs.h', $target . '/include/libpq/')
|| croak 'Could not copy libpq-fs.h';
CopyFiles('Libpq headers', $target . '/include/', 'src/interfaces/libpq/', 'libpq-fe.h');
CopyFiles('Libpq headers',
$target . '/include/', 'src/interfaces/libpq/',
'libpq-fe.h', 'libpq-events.h');
CopyFiles(
'Libpq internal headers',
$target .'/include/internal/',