本篇博客分析PostgreSQL数据库网络层——libpq Event System中提及的示例,以学习libpq Event System的使用。
PQregisterEventProc向 libpq 注册事件回调过程。必须在每个要接收事件的 PGconn 上注册一次事件过程。 除了内存之外,可以向连接注册的事件过程的数量没有限制。 如果成功,该函数返回一个非零值,如果失败则返回零。触发 libpq 事件时将调用 proc 参数。 它的内存地址也用于查找instanceData。 name 参数用于引用错误消息中的事件过程。 此值不能为 NULL 或零长度字符串。 名称字符串被复制到 PGconn 中,因此传递的内容不需要长期存在。 每当事件发生时,passThrough 指针就会传递给 proc。 此参数可以为 NULL。
/* 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结构体中注册myEventProc回调函数。首先我们看一下PGconn中Event相关字段,eventArraySize是events数组大小,nEvents是events已经使用的元素的数量。
/* Event procs registered via PQregisterEventProc */
PGEvent *events; /* expandable array of event data */
int nEvents; /* number of active events */
int eventArraySize; /* allocated array size */
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 */
bool resultInitialized; /* T if RESULTCREATE/COPY succeeded */
} PGEvent;
PQregisterEventProc函数就是
int PQregisterEventProc(PGconn *conn, PGEventProc proc, const char *name, void *passThrough) {
int i;
PGEventRegister regevt; // 这个结构体就包含了一个成员就是PGconn指针
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->events[conn->nEvents].resultInitialized = false;
conn->nEvents++;
regevt.conn = conn;
if (!proc(PGEVT_REGISTER, ®evt, passThrough)) { // 调用回调函数,PGEVT_REGISTER代表注册事件在调用PQregisterEventProc时发生
conn->nEvents--;
free(conn->events[conn->nEvents].name);
return false;
}
return true;
}
myEventProc回调函数对于PGEVT_REGISTER类型的操作就是调用PQsetInstanceData函数将过程 proc 的连接 conn 的 instanceData 设置为数据。
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;
}
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;
}