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 5 Sep 2008 19:00:56 -0000
***************
*** 2092,2103 ****
--- 2092,2222 ----
Note that PQclear should eventually be called
on the object, just as with a PGresult
returned by libpq itself.
+
+
+
+ PQcopyResult
+
+ PQcopyResult
+
+
+
+
+
+ Makes a copy of a PGresult object.
+
+ PGresult *PQcopyResult(const PGresult *src, int flags);
+
+
+
+
+ The returned result is always put into PGRES_TUPLES_OK status.
+ It is not linked to the source result in any way and
+ PQclear must be called when the result is no longer needed.
+ If the function fails, NULL is returned.
+
+
+
+ Optionally, the flags argument can be used to copy
+ more parts of the result. PG_COPYRES_ATTRS will copy the
+ source result's attributes. PG_COPYRES_TUPLES will copy
+ the source result's tuples. This implies copying the attrs, being how the
+ attrs are needed by the tuples. PG_COPYRES_EVENTS will
+ copy the source result's events. PG_COPYRES_NOTICEHOOKS
+ will copy the source result's notify hooks.
+
+
+
+
+
+
+ PQsetResultAttrs
+
+ PQsetResultAttrs
+
+
+
+
+
+ Sets the attributes of a PGresult object.
+
+ int PQsetResultAttrs(PGresult *res, int numAttributes, PGresAttDesc *attDescs);
+
+
+
+
+ The provided attDescs are copied into the result, thus the
+ attDescs are no longer needed by res after
+ the function returns. If the attDescs are NULL or
+ numAttributes is less than one, the request is ignored and
+ the function succeeds. If res already contains attributes,
+ the function will fail. If the function fails, the return value is zero. If the
+ function succeeds, the return value is non-zero.
+
+
+
+
+
+
+ PQsetvalue
+
+ PQsetvalue
+
+
+
+
+
+ Sets a tuple field value of a PGresult object.
+
+ int PQsetvalue(PGresult *res, int tup_num, int field_num, char *value, int len);
+
+
+
+
+ The function will automatically grow the result's internal tuples array as needed.
+ The tup_num argument must be less than or equal to
+ PQntuples, 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 field_num
+ already eixsts, it will be overwritten. If len is -1 or
+ value is NULL, the field value will
+ be set to an SQL NULL. The value is copied
+ into result's private storage, thus no longer needed by res after
+ the function returns. If the function fails, the return value
+ is zero. If the function succeeds, the return value is non-zero.
+
+
+
+
+
+
+ PQresultAlloc
+
+ PQresultAlloc
+
+
+
+
+
+ Allocate subsidiary storage for a PGresult object.
+
+ void *PQresultAlloc(PGresult *res, size_t nBytes);
+
+
+
+
+ Any memory allocated with this function, will be freed when
+ res is cleared. If the function fails,
+ the return value is NULL.
+
+
+
Retrieving Query Result Information
***************
*** 4558,4569 ****
--- 4677,5158 ----
+
+ Event System
+
+
+ The event system is designed to notify registered event handlers
+ about particular libpq events; such as the creation or destruction
+ of PGconn and PGresult
+ objects. One use case is that this allows applications to associate
+ their own data with a PGconn and
+ PGresult, that can retrieved via
+ PQinstanceData and PQresultInstanceData.
+ Basically, its like adding members to the opaque conn or result
+ structures at runtime.
+
+
+
+ Below is a list of event system concepts:
+
+
+
+ eventId - Identifies which event was fired by libpq. All
+ events begin with PGEVT_.
+
+
+
+
+ eventInfo - Every eventId has a corresponding
+ event info structure, that contains data to aid in processing the event.
+
+
+
+
+ instanceData - The instance data is memory associated with a
+ PGconn or PGresult. The data is
+ created and destroyed along with with the connection or result.
+ Typically, instance data is created in response to
+ a PGEVT_REGISTER event, but can be created at any time or
+ not at all. Management of instance data is done using the PQinstanceData,
+ PQsetInstanceData, PQresultInstanceData and
+ PQsetResultInstanceData functions.
+
+
+
+
+
+
+
+ Event Types
+
+
+
+ PGEVT_REGISTER
+
+
+ The register event occurs when PQregisterEventProc is
+ called. It is the ideal time to initialize any instanceData
+ an event procedure may need. Only one register event will be fired per connection.
+ If the event procedure fails, the registration is aborted.
+
+
+ typedef struct
+ {
+ const PGconn *conn;
+ } PGEventRegister;
+
+ When a PGEVT_REGISTER event is received, the evtInfo
+ pointer should be casted to a PGEventRegister*. This structure
+ contains a PGconn that should be in the CONNECTION_OK
+ status; guaranteed if one calls PQregisterEventProc right after obtaining
+ a good PGconn. The connection can be used to initialize any
+ application specific needs: like allocating structures as the event instanceData
+ or executing SQL statements.
+
+
+
+
+
+ PGEVT_CONNRESET
+
+
+ The connection reset event is fired in response to a PQreset or PQresetPoll.
+ In both cases, the event is only fired if the reset was successful. If the event procedure fails,
+ the entire connection reset will fail; the PGconn is put into
+ CONNECTION_BAD status and PQresetPoll will return
+ PGRES_POLLING_FAILED.
+
+ typedef struct
+ {
+ const PGconn *conn;
+ } PGEventReset;
+
+ When a PGEVT_CONNRESET event is received, the evtInfo
+ pointer should be casted to a PGEventReset*. Although the contained
+ PGconn was just reset, all event data remains unchanged. This event
+ should be used to reset/reload/requery any assocaited instanceData.
+
+
+
+
+
+ PGEVT_CONNDESTROY
+
+
+ The connection destroy event is fired in response to a PQfinish.
+ 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.
+
+ typedef struct
+ {
+ const PGconn *conn;
+ } PGEventConnDestroy;
+
+ When a PGEVT_CONNDESTROY event is received, the evtInfo
+ pointer should be casted to a PGEventConnDestroy*. This event is fired
+ prior to PQfinish performing any cleanup. The return value of the event
+ procedure is ignored since there is no way of indicating a failure from PQfinish.
+ Also, an event procedure failure should not abort the process of cleaning up unwanted memory.
+
+
+
+
+
+ PGEVT_RESULTCREATE
+
+
+ The result create event is fired in response to any exec function that generates a result,
+ including PQgetResult. This event will only be fired after the result
+ has been created successfully, with a result status of either PGRES_COMMAND_OK,
+ PGRES_TUPLES_OK or PGRES_EMPTY_QUERY.
+
+ typedef struct
+ {
+ const PGconn *conn;
+ const PGresult *result;
+ } PGEventResultCreate;
+
+ When a PGEVT_RESULTCREATE event is received, the evtInfo
+ pointer should be casted to a PGEventResultCreate*. The conn
+ was the connection used to generate the result. This is the ideal place to initialize
+ any instanceData that needs to be associated with the result. If the event procedure fails,
+ the result will be cleared and the failure will be propagated. Do not attempt to
+ clear the result object contained in the PGEventResultCreate structure.
+
+
+
+
+
+ PGEVT_RESULTCOPY
+
+
+ The result copy event is fired in response to PQcopyResult.
+ This event will only be fired after the copy is complete.
+
+ typedef struct
+ {
+ const PGresult *src;
+ PGresult *dest;
+ } PGEventResultCopy;
+
+ When a PGEVT_RESULTCOPY event is received, the evtInfo
+ pointer should be casted to a PGEventResultCopy*. The src
+ result is what is being copied while the dest result is the copy destination.
+ This event can be used to provide a deep copy of instanceData, since
+ PQcopyResult cannot do that. If the event procedure fails, the entire copy operation
+ will fail and the dest result will be cleared.
+
+
+
+
+
+ PGEVT_RESULTDESTROY
+
+
+ The result destroy event is fired in response to a PQclear.
+ It is the event procedure's responsibility to properly cleanup its event data as libpq
+ has no ability to manage this memory. Failure to properly cleanup will lead to memory
+ leaks.
+
+ typedef struct
+ {
+ const PGresult *result;
+ } PGEventResultDestroy;
+
+ When a PGEVT_RESULTDESTROY event is received, the evtInfo
+ pointer should be casted to a PGEventResultDestroy*. This event is fired
+ prior to PQclear performing any cleanup. The return value of the event
+ procedure is ignored since there is no way of indicating a failure from PQclear.
+ Also, an event procedure failure should not abort the process of cleaning up unwanted memory.
+
+
+
+
+
+
+
+
+ Event Callback Procedure
+
+
+
+ PGEventProc
+
+ PGEventProc
+
+
+
+
+
+ User callback function that receives events from libpq. The address of the event
+ proc is also used to lookup the instanceData
+ associated with a PGconn or PGresult.
+ typedef int (*PGEventProc)(PGEventId evtId, void *evtInfo, void *passThrough);
+
+
+
+ The evtId inidcates which PGEVT_ event occurred.
+ The evtInfo must be casted to the appropriate structure: ex. PGEVT_REGISTER
+ means cast evtInfo to a PGEventRegister pointer. The passThrough is the
+ pointer provided to the PQregisterEventProc, which can be NULL. The function
+ should return a non-zero value if it succeeds and zero if it fails.
+
+
+
+
+
+
+
+ Event Functions
+
+
+
+ PQregisterEventProc
+
+ PQregisterEventProc
+
+
+
+
+
+ Registers an event callback procedure with libpq.
+ int PQregisterEventProc(PGconn *conn, PGEventProc proc, const char *name, void *passThrough);
+
+
+
+ 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.
+
+
+
+ The proc argument will be called when a libpq event is fired. Its
+ memory address is also used to lookup instanceData.
+ The name argument is used to contruct error messages to aid
+ in debugging. This value can be NULL if not needed, in which case the proc
+ address will be used in error messages.
+ The passThrough pointer is passed to the proc whenever
+ an event occurs. This argument can be NULL.
+
+
+
+
+
+
+ PQinstanceData
+
+ PQinstanceData
+
+
+
+
+ Returns the conn's instanceData associated with proc.
+ void *PQinstanceData(const PGconn *conn, PGEventProc proc);
+
+
+
+
+
+
+ PQsetInstanceData
+
+ PQsetInstanceData
+
+
+
+
+ Sets the conn's instanceData for proc to data. This reutrns non-zero for
+ succees and zero for failure.
+ int PQsetInstanceData(PGconn *conn, PGEventProc proc, void *data);
+
+
+
+
+
+
+ PQresultInstanceData
+
+ PQresultInstanceData
+
+
+
+
+ Returns the result's instanceData associated by proc.
+ void *PQresultInstanceData(const PGresult *res, PGEventProc proc);
+
+
+
+
+
+
+ PQresultSetInstanceData
+
+ PQresultSetInstanceData
+
+
+
+
+ Sets the result's instanceData for proc to data. This reutrns non-zero for
+ succees and zero for failure.
+ int PQresultSetInstanceData(PGresult *res, PGEventProc proc, void *data);
+
+
+
+
+
+
+
+ Event Example
+
+ /* 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 */
+ }
+
+
+
Miscellaneous Functions
As always, there are some functions that just don't fit anywhere.
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 5 Sep 2008 19:00:56 -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 5 Sep 2008 19:00:56 -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 5 Sep 2008 19:00:57 -0000
***************
*** 1971,1982 ****
--- 1971,2002 ----
* 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);
+ if (conn->events[i].name)
+ 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:
--- 2172,2207 ----
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;
! if (conn->events[i].name)
! printfPQExpBuffer(&conn->errorMessage,
! libpq_gettext("PGEventProc \"%s\" failed during PGEVT_CONNRESET event\n"),
! conn->events[i].name);
! else
! printfPQExpBuffer(&conn->errorMessage,
! libpq_gettext("PGEventProc \"addr:%p\" failed during PGEVT_CONNRESET event\n"),
! conn->events[i].proc);
! 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.
--- 2229,2270 ----
* 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;
! if (conn->events[i].name)
! printfPQExpBuffer(&conn->errorMessage,
! libpq_gettext("PGEventProc \"%s\" failed during PGEVT_CONNRESET event\n"),
! conn->events[i].name);
! else
! printfPQExpBuffer(&conn->errorMessage,
! libpq_gettext("PGEventProc \"addr:%p\" failed during PGEVT_CONNRESET event\n"),
! conn->events[i].proc);
! 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.196
diff -C6 -r1.196 fe-exec.c
*** src/interfaces/libpq/fe-exec.c 23 Jun 2008 21:10:49 -0000 1.196
--- src/interfaces/libpq/fe-exec.c 5 Sep 2008 19:00:57 -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 ****
--- 121,171 ----
#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++)
+ {
+ if (newEvents[i].name)
+ newEvents[i].name = strdup(newEvents[i].name);
+ 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.
+ *
+ * Updated April 2008 - If conn is not NULL, event states will be copied
+ * from the PGconn to the created PGresult.
*/
PGresult *
PQmakeEmptyPGresult(PGconn *conn, ExecStatusType status)
{
PGresult *result;
***************
*** 157,175 ****
--- 187,220 ----
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 ****
--- 237,495 ----
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 ****
--- 641,674 ----
* 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);
+ if (res->events[i].name)
+ 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);
--- 1501,1513 ----
* memory).
*/
PGresult *
PQgetResult(PGconn *conn)
{
! PGresult *res=NULL;
if (!conn)
return NULL;
/* Parse any available data, if our state permits. */
parseInput(conn);
***************
*** 1267,1278 ****
--- 1576,1620 ----
libpq_gettext("unexpected asyncStatus: %d\n"),
(int) conn->asyncStatus);
res = PQmakeEmptyPGresult(conn, PGRES_FATAL_ERROR);
break;
}
+ if (res && res->nEvents > 0 &&
+ (res->resultStatus == PGRES_COMMAND_OK ||
+ res->resultStatus == PGRES_TUPLES_OK ||
+ res->resultStatus == PGRES_EMPTY_QUERY))
+ {
+ 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];
+
+ if (res->events[i].name)
+ sprintf(msg,
+ "PGEventProc \"%s\" failed during PGEVT_RESULTCREATE event",
+ res->events[i].name);
+ else
+ sprintf(msg,
+ "PGEventProc \"addr:%p\" failed during PGEVT_RESULTCREATE event",
+ res->events[i].proc);
+
+ 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 5 Sep 2008 19:00:57 -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 5 Sep 2008 19:00:57 -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
#include
#ifndef WIN32
#include
#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 */