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
是指向事件过程的指针的 typedef,即从 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
#返回连接 conn
与过程 proc
关联的 instanceData
,如果没有,则返回 NULL
。
void *PQinstanceData(const PGconn *conn, PGEventProc proc);
PQresultSetInstanceData
#将结果的 proc
的 instanceData
设置为 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 */ }
如果您在文档中看到任何不正确、与您对特定功能的体验不符或需要进一步澄清的内容,请使用此表单报告文档问题。