-
+
-
-
-
- PQmakeEmptyPGresult
-
-
-
-
-
- Constructs an empty PGresult object with the given status.
-
- PGresult *PQmakeEmptyPGresult(PGconn *conn, ExecStatusType status);
-
-
-
- This is
libpq>'s internal function to allocate and
- initialize an empty PGresult 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
-
conn is not null and
status>
- indicates an error, the current error message of the specified
- connection is copied into the PGresult.
- Note that PQclear should eventually be called
- on the object, just as with a PGresult
- returned by
libpq itself.
-
-
-
+
+
+
+ PQmakeEmptyPGresult
+
+
+
+
+
+ Constructs an empty PGresult object with the given status.
+
+ PGresult *PQmakeEmptyPGresult(PGconn *conn, ExecStatusType status);
+
+
+
+ This is
libpq>'s internal function to allocate and
+ initialize an empty PGresult 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
+
conn is not null and
status>
+ indicates an error, the current error message of the specified
+ connection is copied into the PGresult.
+ Also, if
conn is not null, any event handlers
+ registered in the connection are copied into the
+ PGresult (but they don't get
+ PGEVT_RESULTCREATE> calls).
+ Note that PQclear should eventually be called
+ on the object, just as with a PGresult
+ returned by
libpq itself.
+
+
+
+
+
+
+ PQcopyResult
+
+
+
+
+
+ Makes a copy of a PGresult object. The copy is
+ not linked to the source result in any way and
+ PQclear must be called when the copy is no longer
+ needed. If the function fails, NULL is returned.
+
+
+ PGresult *PQcopyResult(const PGresult *src, int flags);
+
+
+
+ This is not intended to make an exact copy. The returned result is
+ always put into PGRES_TUPLES_OK status, and does not
+ copy any error message in the source. (It does copy the command status
+ string, however.) The
flags argument determines
+ what else is copied. It is a bitwise OR of several flags.
+ PG_COPYRES_ATTRS specifies copying the source
+ result's attributes (column definitions).
+ PG_COPYRES_TUPLES specifies copying the source
+ result's tuples. (This implies copying the attributes, too.)
+ PG_COPYRES_NOTICEHOOKS specifies
+ copying the source result's notify hooks.
+ PG_COPYRES_EVENTS specifies copying the source
+ result's events. (But any instance data associated with the source
+ is not copied.)
+
+
+
+
+
+
+ PQsetResultAttrs
+
+
+
+
+
+ Sets the attributes of a PGresult object.
+
+ int PQsetResultAttrs(PGresult *res, int numAttributes, PGresAttDesc *attDescs);
+
+
+
+ The provided
attDescs are copied into the result.
+ If the
attDescs pointer is 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
+
+
+
+
+
+ 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. However, the
tup_num argument must be
+ less than or equal to PQntuples, 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
+
field_num already exists, it will be overwritten.
+
value is
NULL, the field value
+ will be set to an SQL NULL. The
+
value 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.
+
+
+
+
+
+
+ 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. The result is
+ guaranteed to be adequately aligned for any type of data,
+ just as for malloc>.
+
+
+
+
+
+
Event System
+
+
libpq's event system is designed to notify
+ registered event handlers about interesting
+
libpq events, such as the creation or
+ destruction of PGconn and
+ PGresult objects. A principal use case is that
+ this allows applications to associate their own data with a
+ PGconn or PGresult
+ and ensure that that data is freed at an appropriate time.
+
+
+ Each registered event handler is associated with two pieces of data,
+ known to
libpq only as opaque
void *>
+ pointers. There is a passthrough> pointer that is provided
+ by the application when the event handler is registered with a
+ PGconn>. The passthrough pointer never changes for the
+ life of the PGconn> and all PGresult>s
+ generated from it; so if used, it must point to long-lived data.
+ In addition there is an instance data> pointer, which starts
+ out NULL in every PGconn> and PGresult>.
+ This pointer can be manipulated using the
+ PQinstanceData,
+ PQsetInstanceData,
+ PQresultInstanceData and
+ PQsetResultInstanceData functions. Note that
+ unlike the passthrough pointer, instance data of a PGconn>
+ is not automatically inherited by PGresult>s created from
+ it.
libpq does not know what passthrough
+ and instance data pointers point to (if anything) and will never attempt
+ to free them — that is the responsibility of the event handler.
+
+
+
+
Event Types
+
+ The enum PGEventId> names the types of events handled by
+ the event system. All its values have names beginning with
+ PGEVT. For each event type, there is a corresponding
+ event info structure that carries the parameters passed to the event
+ handlers. The event types are:
+
+
+
+
+ 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 event handler 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 cast 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.
+
+
+
+
+
+ PGEVT_CONNRESET
+
+ The connection reset event is fired on completion of
+ 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;
+} PGEventConnReset;
+
+
+ When a PGEVT_CONNRESET event is received, the
+
evtInfo pointer should be cast to a
+ PGEventConnReset *. Although the contained
+ PGconn was just reset, all event data remains
+ unchanged. This event should be used to reset/reload/requery any
+ associated instanceData.
+
+
+
+
+
+ PGEVT_CONNDESTROY
+
+ The connection destroy event is fired in response to
+ PQfinish. 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.
+
+
+typedef struct
+{
+ const PGconn *conn;
+} PGEventConnDestroy;
+
+
+ When a PGEVT_CONNDESTROY event is received, the
+
evtInfo pointer should be cast to a
+ PGEventConnDestroy *. This event is fired
+ prior to PQfinish performing any other 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 creation event is fired in response to any query execution
+ function that generates a result, including
+ PQgetResult. This event will only be fired after
+ the result has been created successfully.
+
+
+typedef struct
+{
+ const PGconn *conn;
+ PGresult *result;
+} PGEventResultCreate;
+
+
+ When a PGEVT_RESULTCREATE event is received, the
+
evtInfo pointer should be cast to a
+ PGEventResultCreate *. The
+
conn is 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. The event procedure must not try to
+ PQclear> the result object for itself.
+
+
+
+
+
+ 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 cast to a
+ PGEventResultCopy *. The
+
src result is what was 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 clean up its event data as libpq has no
+ ability to manage this memory. Failure to clean up will lead
+ to memory leaks.
+
+
+typedef struct
+{
+ const PGresult *result;
+} PGEventResultDestroy;
+
+
+ When a PGEVT_RESULTDESTROY event is received, the
+
evtInfo pointer should be cast to a
+ PGEventResultDestroy *. This event is fired
+ prior to PQclear performing any other 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 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
+
+
+int eventproc(PGEventId evtId, void *evtInfo, void *passThrough)
+
+
+ The
evtId parameter indicates which
+ PGEVT event occurred. The
+
evtInfo pointer must be cast to the appropriate
+ structure type to obtain further information about the event.
+ The
passThrough parameter is the pointer
+ provided to PQregisterEventProc when the event
+ procedure was registered. The function should return a non-zero value
+ if it succeeds and zero if it fails.
+
+
+ A particular event procedure can be registered only once in any
+ PGconn>. This is because the address of the procedure
+ is used as a lookup key to identify the associated instance data.
+
+
+
+
+
+
+
+
Event Support Functions
+
+
+
+
+ PQregisterEventProc
+
+
+
+
+
+ Registers an event callback procedure with libpq.
+
+
+ int PQregisterEventProc(PGconn *conn, PGEventProc proc,
+ const char *name, void *passThrough);
+
+
+
+ An event procedure must be registered once on each
+ 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.
+
+
+ The
proc argument will be called when a libpq
+ event is fired. Its memory address is also used to lookup
+ 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 PGconn>, so what is passed need not be
+ long-lived. The
passThrough pointer is passed
+ to the
proc whenever an event occurs. This
+ argument can be NULL.
+
+
+
+
+
+
+ PQsetInstanceData
+
+
+
+
+ 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.)
+
+
+ int PQsetInstanceData(PGconn *conn, PGEventProc proc, void *data);
+
+
+
+
+
+
+
+ PQinstanceData
+
+
+
+
+ Returns the conn's instanceData associated with proc, or NULL
+ if there is none.
+
+
+ void *PQinstanceData(const PGconn *conn, PGEventProc proc);
+
+
+
+
+
+
+
+ PQresultSetInstanceData
+
+
PQresultSetInstanceData
+
+
+
+ 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.)
+
+
+ int PQresultSetInstanceData(PGresult *res, PGEventProc proc, void *data);
+
+
+
+
+
+
+
+ PQresultInstanceData
+
+
+
+
+ Returns the result's instanceData associated with proc, or NULL
+ if there is none.
+
+
+ void *PQresultInstanceData(const PGresult *res, PGEventProc proc);
+
+
+
+
+
+
+
+
+
Event Example
+
+ Here is a skeleton example of managing private data associated with
+ libpq connections and results.
+
+
+/* required header for libpq events (note: includes libpq-fe.h) */
+#include <libpq-events.h>
+
+/* The instanceData */
+typedef struct
+{
+ int n;
+ char *str;
+} mydata;
+
+/* PGEventProc */
+static int myEventProc(PGEventId evtId, void *evtInfo, void *passThrough);
+
+int
+main(void)
+{
+ mydata *data;
+ PGresult *res;
+ PGconn *conn = PQconnectdb("dbname = postgres");
+
+ if (PQstatus(conn) != CONNECTION_OK)
+ {
+ fprintf(stderr, "Connection to database failed: %s",
+ PQerrorMessage(conn));
+ PQfinish(conn);
+ return 1;
+ }
+
+ /* called once on any connection that should receive events.
+ * Sends a PGEVT_REGISTER to myEventProc.
+ */
+ if (!PQregisterEventProc(conn, myEventProc, "mydata_proc", NULL))
+ {
+ fprintf(stderr, "Cannot register PGEventProc\n");
+ PQfinish(conn);
+ return 1;
+ }
+
+ /* conn instanceData is available */
+ data = PQinstanceData(conn, myEventProc);
+
+ /* Sends a PGEVT_RESULTCREATE to myEventProc */
+ res = PQexec(conn, "SELECT 1 + 1");
+
+ /* result instanceData is available */
+ data = PQresultInstanceData(res, myEventProc);
+
+ /* If PG_COPYRES_EVENTS is used, sends a PGEVT_RESULTCOPY to myEventProc */
+ res_copy = PQcopyResult(res, PG_COPYRES_TUPLES | PG_COPYRES_EVENTS);
+
+ /* result instanceData is available if PG_COPYRES_EVENTS was
+ * used during the PQcopyResult call.
+ */
+ data = PQresultInstanceData(res_copy, myEventProc);
+
+ /* Both clears send a PGEVT_RESULTDESTROY to myEventProc */
+ PQclear(res);
+ PQclear(res_copy);
+
+ /* Sends a PGEVT_CONNDESTROY to myEventProc */
+ PQfinish(conn);
+
+ return 0;
+}
+
+static int
+myEventProc(PGEventId evtId, void *evtInfo, void *passThrough)
+{
+ switch (evtId)
+ {
+ case PGEVT_REGISTER:
+ {
+ PGEventRegister *e = (PGEventRegister *)evtInfo;
+ mydata *data = get_mydata(e->conn);
+
+ /* associate app specific data with connection */
+ PQsetInstanceData(e->conn, myEventProc, data);
+ break;
+ }
+
+ case PGEVT_CONNRESET:
+ {
+ PGEventConnReset *e = (PGEventConnReset *)evtInfo;
+ mydata *data = PQinstanceData(e->conn, myEventProc);
+
+ if (data)
+ memset(data, 0, sizeof(mydata));
+ break;
+ }
+
+ case PGEVT_CONNDESTROY:
+ {
+ PGEventConnDestroy *e = (PGEventConnDestroy *)evtInfo;
+ mydata *data = PQinstanceData(e->conn, myEventProc);
+
+ /* free instance data because the conn is being destroyed */
+ if (data)
+ free_mydata(data);
+ break;
+ }
+
+ case PGEVT_RESULTCREATE:
+ {
+ PGEventResultCreate *e = (PGEventResultCreate *)evtInfo;
+ mydata *conn_data = PQinstanceData(e->conn, myEventProc);
+ mydata *res_data = dup_mydata(conn_data);
+
+ /* associate app specific data with result (copy it from conn) */
+ PQsetResultInstanceData(e->result, myEventProc, res_data);
+ break;
+ }
+
+ case PGEVT_RESULTCOPY:
+ {
+ PGEventResultCopy *e = (PGEventResultCopy *)evtInfo;
+ mydata *src_data = PQresultInstanceData(e->src, myEventProc);
+ mydata *dest_data = dup_mydata(src_data);
+
+ /* associate app specific data with result (copy it from a result) */
+ PQsetResultInstanceData(e->dest, myEventProc, dest_data);
+ break;
+ }
+
+ case PGEVT_RESULTDESTROY:
+ {
+ PGEventResultDestroy *e = (PGEventResultDestroy *)evtInfo;
+ mydata *data = PQresultInstanceData(e->result, myEventProc);
+
+ /* free instance data because the result is being destroyed */
+ if (data)
+ free_mydata(data);
+ break;
+ }
+
+ /* unknown event id, just return TRUE. */
+ default:
+ break;
+ }
+
+ return TRUE; /* event processing succeeded */
+}
+
+
+
+
Environment Variables
to inside
libpq), you can use
PQinitSSL(int)> to tell libpq
that the
SSL> library has already been initialized by your
- application.
+ application.
See
url="http://h71000.www7.hp.com/doc/83final/BA554_90007/ch04.html">
# 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 $
#
#-------------------------------------------------------------------------
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))
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'
$(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
-# $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
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
*
*
* 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 $
*
*-------------------------------------------------------------------------
*/
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)
{
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;
+ }
+ }
+ }
}
}
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;
}
*
*
* 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 $
*
*-------------------------------------------------------------------------
*/
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,
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);
/* ----------------
* 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)
{
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';
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
{
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.
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)
{
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 */
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;
}
--- /dev/null
+/*-------------------------------------------------------------------------
+ *
+ * libpq-events.c
+ * functions for supporting the libpq "events" API
+ *
+ * Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ * $PostgreSQL: pgsql/src/interfaces/libpq/libpq-events.c,v 1.1 2008/09/17 04:31:08 tgl Exp $
+ *
+ *-------------------------------------------------------------------------
+ */
+#include "postgres_fe.h"
+
+#include "libpq-fe.h"
+#include "libpq-int.h"
+
+
+/*
+ * Registers an event proc with the given PGconn.
+ *
+ * The same proc can't be registered more than once in a PGconn. This
+ * restriction is required because we use the proc address to identify
+ * the event for purposes such as PQinstanceData().
+ *
+ * The name argument is used within error messages to aid in debugging.
+ * A name must be supplied, but it needn't be unique. The string is
+ * copied, so the passed value needn't be long-lived.
+ *
+ * The passThrough argument is an application specific pointer and can be set
+ * to NULL if not required. It is passed through to the event proc whenever
+ * the event proc is called, and is not otherwise touched by libpq.
+ *
+ * The function returns a non-zero if successful. If the function fails,
+ * zero is returned.
+ */
+int
+PQregisterEventProc(PGconn *conn, PGEventProc proc,
+ const char *name, void *passThrough)
+{
+ int i;
+ PGEventRegister regevt;
+
+ if (!proc || !conn || !name || !*name)
+ return FALSE; /* bad arguments */
+
+ for (i = 0; i < conn->nEvents; i++)
+ {
+ if (conn->events[i].proc == proc)
+ return FALSE; /* already registered */
+ }
+
+ if (conn->nEvents >= conn->eventArraySize)
+ {
+ PGEvent *e;
+ int newSize;
+
+ newSize = conn->eventArraySize ? conn->eventArraySize * 2 : 8;
+ if (conn->events)
+ e = (PGEvent *) realloc(conn->events, newSize * sizeof(PGEvent));
+ else
+ e = (PGEvent *) malloc(newSize * sizeof(PGEvent));
+
+ if (!e)
+ return FALSE;
+
+ conn->eventArraySize = newSize;
+ conn->events = e;
+ }
+
+ conn->events[conn->nEvents].proc = proc;
+ conn->events[conn->nEvents].name = strdup(name);
+ if (!conn->events[conn->nEvents].name)
+ return FALSE;
+ conn->events[conn->nEvents].passThrough = passThrough;
+ conn->events[conn->nEvents].data = NULL;
+ conn->nEvents++;
+
+ regevt.conn = conn;
+ if (!proc(PGEVT_REGISTER, ®evt, passThrough))
+ {
+ conn->nEvents--;
+ free(conn->events[conn->nEvents].name);
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+/*
+ * Set some "instance data" for an event within a PGconn.
+ * Returns nonzero on success, zero on failure.
+ */
+int
+PQsetInstanceData(PGconn *conn, PGEventProc proc, void *data)
+{
+ int i;
+
+ if (!conn || !proc)
+ return FALSE;
+
+ for (i = 0; i < conn->nEvents; i++)
+ {
+ if (conn->events[i].proc == proc)
+ {
+ conn->events[i].data = data;
+ return TRUE;
+ }
+ }
+
+ return FALSE;
+}
+
+/*
+ * Obtain the "instance data", if any, for the event.
+ */
+void *
+PQinstanceData(const PGconn *conn, PGEventProc proc)
+{
+ int i;
+
+ if (!conn || !proc)
+ return NULL;
+
+ for (i = 0; i < conn->nEvents; i++)
+ {
+ if (conn->events[i].proc == proc)
+ return conn->events[i].data;
+ }
+
+ return NULL;
+}
+
+/*
+ * Set some "instance data" for an event within a PGresult.
+ * Returns nonzero on success, zero on failure.
+ */
+int
+PQresultSetInstanceData(PGresult *result, PGEventProc proc, void *data)
+{
+ int i;
+
+ if (!result || !proc)
+ return FALSE;
+
+ for (i = 0; i < result->nEvents; i++)
+ {
+ if (result->events[i].proc == proc)
+ {
+ result->events[i].data = data;
+ return TRUE;
+ }
+ }
+
+ return FALSE;
+}
+
+/*
+ * Obtain the "instance data", if any, for the event.
+ */
+void *
+PQresultInstanceData(const PGresult *result, PGEventProc proc)
+{
+ int i;
+
+ if (!result || !proc)
+ return NULL;
+
+ for (i = 0; i < result->nEvents; i++)
+ if (result->events[i].proc == proc)
+ return result->events[i].data;
+
+ return NULL;
+}
--- /dev/null
+/*-------------------------------------------------------------------------
+ *
+ * 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 */
* 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 $
*
*-------------------------------------------------------------------------
*/
*/
#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
} 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
* ----------------
/* 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,
* 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 $
*
*-------------------------------------------------------------------------
*/
/* We assume libpq-fe.h has already been included. */
#include "postgres_fe.h"
+#include "libpq-events.h"
#include
#include
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
{
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;
* on the PGresult don't have to reference the PGconn.
*/
PGNoticeHooks noticeHooks;
+ PGEvent *events;
+ int nEvents;
int client_encoding; /* encoding id */
/*
/* 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;
#
# 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;
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/',