libpq 的事件系统旨在通知已注册的事件处理程序关于有趣的 libpq 事件,例如 PGconn 和 PGresult 对象的创建或销毁。一个主要用例是,这允许应用程序将它们自己的数据与 PGconn 或 PGresult 关联,并确保该数据在适当的时候被释放。
每个注册的事件处理程序都与两个数据片段相关联,libpq 只知道它们是不透明的 void * 指针。有一个传递指针,当事件处理程序在 PGconn 注册时,由应用程序提供。对于 PGconn 及其生成的所有 PGresult 的生命周期,传递指针永远不会改变;因此,如果使用,它必须指向长期存在的数据。此外,还有一个实例数据指针,它在每个 PGconn 和 PGresult 中都以 NULL 开始。可以使用 PQinstanceData、PQsetInstanceData、PQresultInstanceData 和 PQresultSetInstanceData 函数来操作此指针。请注意,与传递指针不同,PGconn 的实例数据不会自动被它创建的 PGresult 继承。libpq 不知道传递指针和实例数据指针指向什么(如果有),并且永远不会尝试释放它们 —— 这是事件处理程序的责任。
枚举 PGEventId 命名了事件系统处理的事件类型。其所有值都以 PGEVT 开头。对于每种事件类型,都有一个相应的事件信息结构,该结构携带传递给事件处理程序的参数。事件类型是
PGEVT_REGISTER #当调用 PQregisterEventProc 时,会发生注册事件。这是初始化事件过程可能需要的任何 instanceData 的理想时间。每个连接的每个事件处理程序只会触发一个注册事件。如果事件过程失败(返回零),则会取消注册。
typedef struct
{
PGconn *conn;
} PGEventRegister;
当收到 PGEVT_REGISTER 事件时,evtInfo 指针应强制转换为 PGEventRegister *。此结构包含一个应处于 CONNECTION_OK 状态的 PGconn;如果在获得良好的 PGconn 后立即调用 PQregisterEventProc,则可以保证。当返回失败代码时,必须执行所有清理,因为不会发送 PGEVT_CONNDESTROY 事件。
PGEVT_CONNRESET #连接重置事件在 PQreset 或 PQresetPoll 完成时触发。在这两种情况下,只有在重置成功时才会触发该事件。在 PostgreSQL v15 及更高版本中,事件过程的返回值将被忽略。但是,在早期版本中,返回成功(非零)非常重要,否则连接将被中止。
typedef struct
{
PGconn *conn;
} PGEventConnReset;
当收到 PGEVT_CONNRESET 事件时,evtInfo 指针应强制转换为 PGEventConnReset *。尽管所包含的 PGconn 刚刚重置,但所有事件数据保持不变。此事件应用于重置/重新加载/重新查询任何关联的 instanceData。请注意,即使事件过程未能处理 PGEVT_CONNRESET,当连接关闭时,它仍会收到 PGEVT_CONNDESTROY 事件。
PGEVT_CONNDESTROY #连接销毁事件响应 PQfinish 而触发。事件过程有责任正确清理其事件数据,因为 libpq 没有能力管理此内存。未能清理将导致内存泄漏。
typedef struct
{
PGconn *conn;
} PGEventConnDestroy;
当收到 PGEVT_CONNDESTROY 事件时,evtInfo 指针应强制转换为 PGEventConnDestroy *。此事件在 PQfinish 执行任何其他清理之前触发。事件过程的返回值将被忽略,因为无法从 PQfinish 指示失败。此外,事件过程失败不应中止清理不需要的内存的过程。
PGEVT_RESULTCREATE #结果创建事件响应任何生成结果的查询执行函数而触发,包括 PQgetResult。只有在成功创建结果后才会触发此事件。
typedef struct
{
PGconn *conn;
PGresult *result;
} PGEventResultCreate;
当收到 PGEVT_RESULTCREATE 事件时,evtInfo 指针应强制转换为 PGEventResultCreate *。conn 是用于生成结果的连接。这是初始化需要与结果关联的任何 instanceData 的理想位置。如果事件过程失败(返回零),则该事件过程将在结果的剩余生命周期内被忽略;也就是说,它将不会收到此结果或从其复制的结果的 PGEVT_RESULTCOPY 或 PGEVT_RESULTDESTROY 事件。
PGEVT_RESULTCOPY #结果复制事件响应 PQcopyResult 而触发。只有在复制完成后才会触发此事件。只有成功处理了源结果的 PGEVT_RESULTCREATE 或 PGEVT_RESULTCOPY 事件的事件过程才会收到 PGEVT_RESULTCOPY 事件。
typedef struct
{
const PGresult *src;
PGresult *dest;
} PGEventResultCopy;
当收到 PGEVT_RESULTCOPY 事件时,evtInfo 指针应强制转换为 PGEventResultCopy *。src 结果是被复制的,而 dest 结果是复制目标。此事件可用于提供 instanceData 的深拷贝,因为 PQcopyResult 无法做到这一点。如果事件过程失败(返回零),则该事件过程将在新结果的剩余生命周期内被忽略;也就是说,它将不会收到该结果或从其复制的结果的 PGEVT_RESULTCOPY 或 PGEVT_RESULTDESTROY 事件。
PGEVT_RESULTDESTROY #结果销毁事件响应 PQclear 而触发。事件过程有责任正确清理其事件数据,因为 libpq 没有能力管理此内存。未能清理将导致内存泄漏。
typedef struct
{
PGresult *result;
} PGEventResultDestroy;
当收到 PGEVT_RESULTDESTROY 事件时,evtInfo 指针应强制转换为 PGEventResultDestroy *。此事件在 PQclear 执行任何其他清理之前触发。事件过程的返回值将被忽略,因为无法从 PQclear 指示失败。此外,事件过程失败不应中止清理不需要的内存的过程。
PGEventProc #PGEventProc 是指向事件过程的指针的类型定义,即接收来自 libpq 的事件的用户回调函数。事件过程的签名必须是:
int eventproc(PGEventId evtId, void *evtInfo, void *passThrough)
evtId 参数指示发生了哪个 PGEVT 事件。 evtInfo 指针必须强制转换为适当的结构类型,以获取有关该事件的进一步信息。 passThrough 参数是在注册事件过程时提供给 PQregisterEventProc 的指针。如果函数成功,则应返回非零值;如果失败,则应返回零。
特定的事件过程在任何 PGconn 中只能注册一次。这是因为过程的地址用作查找密钥来标识关联的实例数据。
在 Windows 上,函数可以有两个不同的地址:一个在 DLL 外部可见,另一个在 DLL 内部可见。应注意,在 libpq 的事件过程函数中只能使用其中一个地址,否则会导致混乱。编写代码以使其工作的最简单规则是确保将事件过程声明为 static。如果过程的地址必须在其自身的源文件外部可用,请公开一个单独的函数来返回该地址。
PQregisterEventProc #向 libpq 注册事件回调过程。
int PQregisterEventProc(PGconn *conn, PGEventProc proc,
const char *name, void *passThrough);
对于您希望接收事件的每个 PGconn,必须注册一次事件过程。 除内存外,可以注册到连接的事件过程的数量没有限制。 如果函数成功,则返回非零值;如果失败,则返回零。
当触发 libpq 事件时,将调用 proc 参数。它的内存地址也用于查找 instanceData。 name 参数用于在错误消息中引用事件过程。此值不能为 NULL 或零长度字符串。名称字符串将复制到 PGconn 中,因此传递的内容无需长期存在。每当发生事件时, passThrough 指针都会传递给 proc 。此参数可以为 NULL。
PQsetInstanceData #将连接 conn 的过程 proc 的 instanceData 设置为 data。成功则返回非零值,失败则返回零。(仅当 proc 未在 conn 中正确注册时才可能失败。)
int PQsetInstanceData(PGconn *conn, PGEventProc proc, void *data);
PQinstanceData #返回与过程 proc 关联的连接 conn 的 instanceData,如果不存在,则返回 NULL。
void *PQinstanceData(const PGconn *conn, PGEventProc proc);
PQresultSetInstanceData #将结果的 instanceData 的 proc 设置为 data。 成功则返回非零值,失败则返回零。(仅当 proc 未在结果中正确注册时才可能失败。)
int PQresultSetInstanceData(PGresult *res, PGEventProc proc, void *data);
请注意,除非使用 PQresultAlloc 分配,否则 data 所表示的任何存储空间都不会被 PQresultMemorySize 所计算。(建议这样做,因为它消除了在销毁结果时显式释放此类存储空间的需要。)
PQresultInstanceData #返回与 proc 关联的结果的 instanceData,如果不存在,则返回 NULL。
void *PQresultInstanceData(const PGresult *res, PGEventProc proc);
这是一个管理与 libpq 连接和结果关联的私有数据的骨架示例。
/* 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) */
PQresultSetInstanceData(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) */
PQresultSetInstanceData(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 */
}
如果您在文档中发现任何不正确,与您使用特定功能的经验不符或需要进一步澄清的内容,请使用此表格报告文档问题。