From 32f159cc55bacad6a4737d3584cb69698c33fc86 Mon Sep 17 00:00:00 2001 From: Tom Lane Date: Wed, 17 Sep 2008 04:31:08 +0000 Subject: 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 --- doc/src/sgml/libpq.sgml | 745 +++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 711 insertions(+), 34 deletions(-) (limited to 'doc/src') diff --git a/doc/src/sgml/libpq.sgml b/doc/src/sgml/libpq.sgml index deb052e5c49..2db369e906d 100644 --- a/doc/src/sgml/libpq.sgml +++ b/doc/src/sgml/libpq.sgml @@ -1,4 +1,4 @@ - + <application>libpq</application> - C Library @@ -2063,38 +2063,6 @@ PGresult *PQdescribePortal(PGconn *conn, const char *portalName); - - - - PQmakeEmptyPGresult - - 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. - - - @@ -4598,6 +4566,170 @@ char *pg_encoding_to_char(int encoding_id); + + + + PQmakeEmptyPGresult + + 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 + + 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 + + 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 + + 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. + If len is -1 or + 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 + + 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. + + + + @@ -4711,6 +4843,551 @@ defaultNoticeProcessor(void *arg, const char *message) + + 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 PGresults + 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 PGresults 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 + + + + + + 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 + + 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 + instanceData. The name + 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 + + 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 + + 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 + + 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 @@ -5263,7 +5940,7 @@ defaultNoticeProcessor(void *arg, const char *message) to inside libpq), you can use PQinitSSL(int) to tell libpq that the SSL library has already been initialized by your - application. + application. See -- cgit v1.2.3