事件系统
概览
libpq的事件系统,允许开发者按照接口要求自行编写时间处理器并进行注册,事件系统可将libpq事件通知已注册的事件处理器,例如PGconn以及PGresult对象的创建和析构。典型的使用情况是应用可以将自己的数据与一个PGconn或者PGresult对象关联在一起,并且提供恰当的时机释放这些数据。
每一个已注册的事件处理器与两部分数据相关,对于libpq它们只是透明的void *指针。当事件处理器被注册到一个PGconn时,会有一个应用提供的转移指针。该转移指针在PGconn及其产生的所有PGresult的生命期内都不会改变。因此,如果使用它,它必须指向长期存在的数据。此外,还有一个instance data指针,在每一个PGconn和PGresult对象中默认都为NULL。这个指针可以使用PQinstanceData、PQsetInstanceData、PQresultInstanceData和PQresultSetInstanceData函数操纵。注意和转移指针不同,一个PGconn的实例数据不会被从它创建的PGresult自动继承。libpq不知道转移指针和实例数据指针实际指向什么,这些数据的释放也由开发者自行编写的事件处理器来负责。
事件类型和触发函数
枚举PGEventId命名了事件系统处理的事件类型。它的所有值的名称都以PGEVT开始。对于每一种事件类型,都有一个相应的事件信息结构用来承载传递给事件处理器的参数。事件类型及其触发函数和相关说明如下表:
事件类型 | 触发函数和说明 |
---|---|
PGEVT_REGISTER | 当PQregisterEventProc被调用时,注册事件会发生 |
PGEVT_CONNRESET | 连接重置事件在PQreset或PQresetPoll完成时被触发,只有重置成功才会触发该事件。 |
PGEVT_CONNDESTROY | 连接销毁事件在调用PQfinish时会被触发。 |
PGEVT_RESULTCREATE | 在任何生成一个结果的查询执行函数被执行时,结果创建事件会被触发。这些函数包括PQgetResult。这个事件只有在结果被成功地创建之后才会被触发。 |
PGEVT_RESULTCOPY | 结果复制事件在调用PQcopyResult时会被触发,这个事件只会在复制完成后才被触发。 |
PGEVT_RESULTDESTROY | 结果销毁事件在调用PQclear时会被触发 |
示例
/* libpq 事件的头文件(注意:libpq-events.h已包括 libpq-fe.h) */
#include <libpq-events.h>
#include <stdlib.h>
#include <memory.h>
#define DATALENGTH 10
/* instanceData */
typedef struct
{
int n;
char *str;
} mydata;
/* 事件处理器,接口如下 */
static int myEventProc(PGEventId evtId, void *evtInfo, void *passThrough);
mydata *get_mydata(PGconn *conn)
{
int i;
mydata *data = (mydata *)malloc(sizeof(mydata));
data->str = (char *)malloc(DATALENGTH);
data->n = DATALENGTH;
for (i = 0;i<DATALENGTH; ++i)
data->str[i] = ('a' + i);
return data;
}
mydata *dup_mydata(mydata *src_data)
{
int i;
mydata *data = (mydata *)malloc(sizeof(mydata));
data->str = (char *)malloc(src_data->n);
data->n = src_data->n;
for (i = 0;i<data->n; ++i)
data->str[i] = src_data->str[i];
return data;
}
void free_mydata(mydata *data)
{
free(data->str);
free(data);
}
int
main(void)
{
mydata *data;
PGresult *res, *res_copy;
PGconn *conn =
PQconnectdb("dbname=vastbase");
if (PQstatus(conn) != CONNECTION_OK)
{
fprintf(stderr, "Connection to database failed: %s",
PQerrorMessage(conn));
PQfinish(conn);
return 1;
}
/* 在任何应该接收事件的连接上调用一次。
* 发送一个 PGEVT_REGISTER 给 myEventProc。
*/
if (!PQregisterEventProc(conn, myEventProc, "mydata_proc", NULL))
{
fprintf(stderr, "Cannot register PGEventProc\n");
PQfinish(conn);
return 1;
}
/* conn 的 instanceData 可用 */
data = PQinstanceData(conn, myEventProc);
/* 发送一个 PGEVT_RESULTCREATE 给 myEventProc */
res = PQexec(conn, "SELECT 1 + 1");
/* 结果的 instanceData 可用 */
data = PQresultInstanceData(res, myEventProc);
/* 如果使用了 PG_COPYRES_EVENTS,发送一个 PGEVT_RESULTCOPY 给 myEventProc */
res_copy = PQcopyResult(res, PG_COPYRES_TUPLES | PG_COPYRES_EVENTS);
/* 如果在 PQcopyResult 调用时使用了 PG_COPYRES_EVENTS,结果的 instanceData 可用。*/
data = PQresultInstanceData(res_copy, myEventProc);
/* 两个清除都发送一个 PGEVT_RESULTDESTROY 给 myEventProc */
PQclear(res);
PQclear(res_copy);
/* 发送一个 PGEVT_CONNDESTROY 给 myEventProc */
PQfinish(conn);
return 0;
}
static int
myEventProc(PGEventId evtId, void *evtInfo, void *passThrough)
{
switch (evtId)
{
case PGEVT_REGISTER: /* PQregisterEventProc调用时触发 */
{
PGEventRegister *e = (PGEventRegister *)evtInfo;
mydata *data = get_mydata(e->conn);
/* 将应用相关的数据与连接关联起来 */
PQsetInstanceData(e->conn, myEventProc, data);
break;
}
case PGEVT_CONNRESET: /* 调用PQreset或PQresetPoll且完成时被触发,本例不触发 */
{
PGEventConnReset *e = (PGEventConnReset *)evtInfo;
mydata *data = PQinstanceData(e->conn, myEventProc);
if (data)
memset(data, 0, sizeof(mydata));
break;
}
case PGEVT_CONNDESTROY: /* 调用PQfinish时触发 */
{
PGEventConnDestroy *e = (PGEventConnDestroy *)evtInfo;
mydata *data = PQinstanceData(e->conn, myEventProc);
/* 因为连接正在被销毁,释放示例数据 */
if (data)
free_mydata(data);
break;
}
case PGEVT_RESULTCREATE: /* 调用PQexec时触发 */
{
PGEventResultCreate *e = (PGEventResultCreate *)evtInfo;
mydata *conn_data = PQinstanceData(e->conn, myEventProc);
mydata *res_data = dup_mydata(conn_data);
/* 把应用相关的数据与结果(从 conn 复制过来)关联起来 */
PQresultSetInstanceData(e->result, myEventProc, res_data);
break;
}
case PGEVT_RESULTCOPY: /* 调用PQcopyResult,并指定了PG_COPYRES_EVENTS时触发 */
{
PGEventResultCopy *e = (PGEventResultCopy *)evtInfo;
mydata *src_data = PQresultInstanceData(e->src, myEventProc);
mydata *dest_data = dup_mydata(src_data);
/* 把应用相关的数据与结果(从一个结果复制过来)关联起来 */
PQresultSetInstanceData(e->dest, myEventProc, dest_data);
break;
}
case PGEVT_RESULTDESTROY: /* 调用PQclear时触发 */
{
PGEventResultDestroy *e = (PGEventResultDestroy *)evtInfo;
mydata *data = PQresultInstanceData(e->result, myEventProc);
/* 因为结果正在被销毁,释放实例数据 */
if (data)
free_mydata(data);
break;
}
/* 未知事件 ID,只返回true。 */
default:
break;
}
return true; /* 事件处理成功 */
}