On this page
33.13. 事件系统
libpq 的事件系统旨在向注册事件处理程序通知有关有趣的 libpq 事件,例如PGconn
和PGresult
对象的创建或销毁。一个主要的用例是,它允许应用程序将自己的数据与PGconn
或PGresult
关联,并确保在适当的时间释放数据。
每个注册的事件处理程序都与两个数据相关联,libpq 仅将其称为不透明的void *
指针。当事件处理程序向PGconn
注册时,应用程序会提供* passthrough 指针。直通指针在PGconn
以及从其生成的所有PGresult
的生命周期内永远不会改变;因此,如果使用它,它必须指向长期存在的数据。此外,还有一个 instance data *指针,它在PGconn
和PGresult
的每一个中以NULL
开头。可以使用PQinstanceData
,PQsetInstanceData
,PQresultInstanceData
和PQsetResultInstanceData
函数来操纵该指针。请注意,与直通指针不同,PGconn
的实例数据不会被从其创建的PGresult
自动继承。 libpq 不知道传递和实例数据指针指向什么(如果有的话),并且永远不会尝试释放它们-这是事件处理程序的责任。
33 .13.1. 活动类型
枚举PGEventId
命名事件系统处理的事件的类型。其所有值的名称都以PGEVT
开头。对于每种事件类型,都有一个相应的事件信息结构,该结构包含传递给事件处理程序的参数。事件类型为:
PGEVT_REGISTER
- 调用
PQregisterEventProc
时发生注册事件。现在是初始化事件过程可能需要的任何instanceData
的理想时间。每个连接的每个事件处理程序将仅触发一个注册事件。如果事件过程失败,注册将中止。
- 调用
typedef struct
{
PGconn *conn;
} PGEventRegister;
收到PGEVT_REGISTER
事件时,* evtInfo
*指针应强制转换为PGEventRegister *
。此结构包含PGconn
,该状态应为CONNECTION_OK
。保证如果一个人获得好的PGconn
之后马上打电话PQregisterEventProc
。返回失败代码时,必须执行所有清除操作,因为不会发送PGEVT_CONNDESTROY
事件。
PGEVT_CONNRESET
- 完成
PQreset
或PQresetPoll
会触发连接重置事件。在这两种情况下,仅在重置成功后才触发该事件。如果事件过程失败,则整个连接重置将失败;PGconn
处于CONNECTION_BAD
状态,PQresetPoll
将返回PGRES_POLLING_FAILED
。
- 完成
typedef struct
{
PGconn *conn;
} PGEventConnReset;
收到PGEVT_CONNRESET
事件时,* evtInfo
*指针应强制转换为PGEventConnReset *
。尽管所包含的PGconn
刚刚被重置,但所有事件数据均保持不变。此事件应用于重置/重新加载/重新查询任何关联的instanceData
。请注意,即使事件过程无法处理PGEVT_CONNRESET
,但在关闭连接时它将仍然收到PGEVT_CONNDESTROY
事件。
PGEVT_CONNDESTROY
- 响应
PQfinish
触发连接销毁事件。正确清理事件数据是事件过程的责任,因为 libpq 无法 Management 此内存。清理失败将导致内存泄漏。
- 响应
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
的理想位置。如果事件过程失败,则将清除结果并传播故障。事件过程一定不能尝试PQclear
本身的结果对象。返回失败代码时,必须执行所有清除操作,因为不会发送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
无法做到这一点。如果事件过程失败,则整个复制操作将失败并且 dest
*结果将被清除。返回失败代码时,必须执行所有清除操作,因为不会为目标结果发送PGEVT_RESULTDESTROY
事件。
PGEVT_RESULTDESTROY
- 响应
PQclear
触发结果 destroy 事件。正确清理事件数据是事件过程的责任,因为 libpq 无法 Management 此内存。清理失败将导致内存泄漏。
- 响应
typedef struct
{
PGresult *result;
} PGEventResultDestroy;
收到PGEVT_RESULTDESTROY
事件时,* evtInfo
*指针应强制转换为PGEventResultDestroy *
。 PQclear
执行任何其他清除之前会触发此事件。由于无法从PQclear
指示失败,因此将忽略事件过程的返回值。同样,事件过程失败不应使清理不必要的内存的过程中止。
33 .13.2. 事件回呼程序
int eventproc(PGEventId evtId, void *evtInfo, void *passThrough)
evtId
*参数指示发生了哪个PGEVT
事件。 *evtInfo
*指针必须强制转换为适当的结构类型,以获取有关事件的更多信息。 *passThrough
*参数是注册事件过程时提供给PQregisterEventProc
的指针。如果成功,该函数应返回非零值;如果失败,则应返回零。
特定事件过程只能在PGconn
中注册一次。这是因为该过程的地址用作查找键,以标识关联的实例数据。
Caution
在 Windows 上,函数可以具有两个不同的地址:一个从 DLL 外部可见,而另一个从 DLL 内部可见。应该注意的是,只有这些地址之一与 libpq 的事件过程函数一起使用,否则会引起混淆。编写将起作用的代码的最简单规则是确保将事件过程声明为static
。如果该过程的地址必须在其自身的源文件之外可用,则公开一个单独的函数以返回该地址。
33 .13.3. 活动支持功能
int PQregisterEventProc(PGconn *conn, PGEventProc proc,
const char *name, void *passThrough);
必须在要接收事件的每个PGconn
上注册一次事件过程。除了内存,对连接可以注册的事件过程的数量没有限制。如果成功,该函数将返回非零值;如果失败,则返回零。
触发 libpq 事件时,将调用* proc
*参数。其内存地址也用于查找instanceData
。 * name
参数用于引用错误消息中的事件过程。该值不能为NULL
或长度为零的字符串。名称字符串将被复制到PGconn
中,因此传递的内容不必长期存在。每当发生事件时, passThrough
指针就会传递给 proc
*。该参数可以是NULL
。
PQsetInstanceData
- 将过程*
proc
的连接conn
的instanceData
设置为data
。对于成功,返回非零;对于失败,返回零。 (仅当proc
未在conn
*中正确注册时,才有可能失败.)
- 将过程*
int PQsetInstanceData(PGconn *conn, PGEventProc proc, void *data);
void *PQinstanceData(const PGconn *conn, PGEventProc proc);
PQresultSetInstanceData
- 将*
proc
的结果instanceData
设置为data
。对于成功,返回非零;对于失败,返回零。 (仅当proc
*未在结果中正确注册时才可能失败.)
- 将*
int PQresultSetInstanceData(PGresult *res, PGEventProc proc, void *data);
void *PQresultInstanceData(const PGresult *res, PGEventProc proc);
33 .13.4. 活动范例
这是 Management 与 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)
{
fprintf(stderr, "Connection to database failed: %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 */
}