# 34.14. Event System
34.14.2. Event Callback Procedure
34.14.3. Event Support Functions
libpq's event system is designed to notify registered event handlers about interesting libpq events, such as the creation or destruction ofPGconn
andPGresult
objects. A principal use case is that this allows applications to associate their own data with aPGconn
orPGresult
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 opaquevoid *
pointers. There is apass-throughpointer that is provided by the application when the event handler is registered with aPGconn
. The pass-through pointer never changes for the life of thePGconn
and allPGresult
s generated from it; so if used, it must point to long-lived data. In addition there is aninstance datapointer, which starts outNULL
in everyPGconn
andPGresult
. This pointer can be manipulated using thePQinstanceData
,PQsetInstanceData
,PQresultInstanceData
andPQsetResultInstanceData
functions. Note that unlike the pass-through pointer, instance data of aPGconn
is not automatically inherited byPGresult
s created from it. libpq does not know what pass-through and instance data pointers point to (if anything) and will never attempt to free them — that is the responsibility of the event handler.
# 34.14.1. Event Types
The enumPGEventId
names the types of events handled by the event system. All its values have names beginning withPGEVT
. 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 whenPQregisterEventProc
is called. It is the ideal time to initialize anyinstanceData
可能需要一个事件过程。每个连接的每个事件处理程序只会触发一个注册事件。如果事件过程失败,则中止注册。
typedef struct
{
PGconn *conn;
} PGEventRegister;
当一个PGEVT_REGISTER
事件被接收,*事件信息
*指针应转换为PGEventRegister *
.这个结构包含一个PGconn
那应该在CONNECTION_OK
地位;保证如果有人打电话PQregisterEventProc
在获得好东西之后PGconn
.返回失败代码时,所有清理必须执行为 noPGEVT_CONNDESTROY
事件将被发送。
PGEVT_CONNRESET
连接重置事件在完成时触发复位
或者PQresetPoll
.在这两种情况下,只有在重置成功时才会触发事件。如果事件过程失败,则整个连接重置失败;这PGconn
被放入CONNECTION_BAD
状态和PQresetPoll
将返回PGRES_POLLING_FAILED
.
typedef struct
{
PGconn *conn;
} PGEventConnReset;
当一个PGEVT_CONNRESET
事件被接收,*事件信息
*指针应转换为PGEventConnReset *
.虽然包含PGconn
刚刚重置,所有事件数据保持不变。此事件应用于重置/重新加载/重新查询任何关联的实例数据
.请注意,即使事件过程无法处理PGEVT_CONNRESET
,它仍然会收到一个PGEVT_CONNDESTROY
连接关闭时的事件。
PGEVT_CONNDESTROY
连接销毁事件被触发以响应PQ完成
.正确清理其事件数据是事件过程的责任,因为 libpq 无法管理此内存。清理失败会导致内存泄漏。
typedef struct
{
PGconn *conn;
} PGEventConnDestroy;
当一个PGEVT_CONNDESTROY
事件被接收,*事件信息
*指针应转换为PGEventConnDestroy *
.此事件在之前触发PQ完成
执行任何其他清理。事件过程的返回值被忽略,因为无法从PQ完成
.此外,事件过程失败不应中止清理不需要的内存的过程。
PGEVT_RESULTCREATE
结果创建事件被触发以响应任何生成结果的查询执行函数,包括PQgetResult
.只有在成功创建结果后才会触发此事件。
typedef struct
{
PGconn *conn;
PGresult *result;
} PGEventResultCreate;
当一个PGEVT_RESULTCREATE
事件被接收,*事件信息
指针应转换为PGEventResultCreate *
.这康恩
*是用于生成结果的连接。这是初始化任何实例数据
需要与结果相关联。如果事件过程失败,结果将被清除并传播失败。事件过程不能试图PQclear
结果对象本身。返回失败代码时,所有清理必须执行为 noPGEVT_RESULTDESTROY
事件将被发送。
PGEVT_RESULTCOPY
结果复制事件被触发以响应PQcopyResult
.只有在复制完成后才会触发此事件。只有已成功处理的事件过程PGEVT_RESULTCREATE
要么PGEVT_RESULTCOPY
源结果的事件将收到PGEVT_RESULTCOPY
事件。
typedef struct
{
const PGresult *src;
PGresult *dest;
} PGEventResultCopy;
当一个PGEVT_RESULTCOPY
事件被接收,*事件信息
指针应转换为PGEventResultCopy *
.这源代码
结果是复制的内容,而目的地
结果是复制目标。此事件可用于提供实例数据
, 自从PQcopyResult
不可以这样做。如果事件过程失败,整个复制操作将失败,并且目的地
*结果将被清除。返回失败代码时,所有清理必须执行为 noPGEVT_RESULTDESTROY
将针对目标结果发送事件。
PGEVT_RESULTDESTROY
结果销毁事件被触发以响应PQclear
.由于libpq无法管理该内存,因此事件程序有责任正确清理其事件数据。未能清理将导致内存泄漏。
typedef struct
{
PGresult *result;
} PGEventResultDestroy;
当PGEVT_结果演示
事件收到后*埃夫廷福
*指针应投射到PGEventResultDestroy*
.此事件在PQclear
执行任何其他清理。事件过程的返回值将被忽略,因为无法从中指示故障PQclear
。此外,事件过程失败不应中止清除不需要的内存的过程。
# 34.14.2.事件回调过程
PGEventProc
是指向事件过程的指针的typedef,即从libpq接收事件的用户回调函数。事件程序的签名必须是
int eventproc(PGEventId evtId, void *evtInfo, void *passThrough)
这个*埃夫蒂德
参数指示PGEVT
事件发生了。这个埃夫廷福
指针必须强制转换为适当的结构类型,才能获得有关事件的进一步信息。这个穿越
*参数是提供给PQregisterEventProc
当事件过程被注册时。如果函数成功,则返回非零值;如果函数失败,则返回零。
A particular event procedure can be registered only once in anyPGconn
. This is because the address of the procedure is used as a lookup key to identify the associated instance data.
# Caution
On Windows, functions can have two different addresses: one visible from outside a DLL and another visible from inside the DLL. One should be careful that only one of these addresses is used with libpq's event-procedure functions, else confusion will result. The simplest rule for writing code that will work is to ensure that event procedures are declaredstatic
. If the procedure's address must be available outside its own source file, expose a separate function to return the address.
# 34.14.3. Event Support Functions
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 eachPGconn
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 lookupinstanceData
. Thename
argument is used to refer to the event procedure in error messages. This value cannot beNULL
or a zero-length string. The name string is copied into thePGconn
, so what is passed need not be long-lived. ThepassThrough
pointer is passed to theproc
*whenever an event occurs. This argument can be空值
.
设置连接*康恩
的实例数据
办理手续过程
到数据
.这将返回非零表示成功,返回零表示失败。(只有在过程
没有正确注册康恩
*.)
int PQsetInstanceData(PGconn *conn, PGEventProc proc, void *data);
返回连接*康恩
的实例数据
与程序相关过程
*, 或者空值
如果没有。
void *PQinstanceData(const PGconn *conn, PGEventProc proc);
设置结果的instanceData
for*proc
todata
. This returns non-zero for success and zero for failure. (Failure is only possible ifproc
*has not been properly registered in the result.)
int PQresultSetInstanceData(PGresult *res, PGEventProc proc, void *data);
Beware that any storage represented by*data
*will not be accounted for byPQresultMemorySize
, unless it is allocated usingPQresultAlloc
. (Doing so is recommendable because it eliminates the need to free such storage explicitly when the result is destroyed.)
Returns the result'sinstanceData
associated with*proc
*, orNULL
if there is none.
void *PQresultInstanceData(const PGresult *res, PGEventProc proc);
# 34.14.4. 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 options=-csearch_path=");
if (PQstatus(conn) != CONNECTION_OK)
{
/* PQerrorMessage's result includes a trailing newline */
fprintf(stderr, "%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 */
}