选择 打开 改范围 完整检索页

pgsql.cc 提供对 postgresql.org 官网内容的中文翻译,由 Pigsty 团队维护。

受支持版本: 当前版本 (18) / 17 / 16 / 15 / 14
测试与开发版本: 19 / devel
不受支持的版本: 13 / 12 / 11 / 10 / 9.6 / 9.5 / 9.4 / 9.3 / 9.2 / 9.1 / 9.0
历史版本PostgreSQL 9.4 已于 2020 年 2 月结束社区维护,本页译文保留供仍在使用旧版本的读者参考。新系统请看当前版本

31.13. 事件系统 #

libpq的事件系统被设计为通知已注册的事件处理器它感兴趣的libpq事件,例如PGconn以及PGresult对象的创建和销毁。一个主要用途是允许应用将自己的数据与一个PGconn或者PGresult关联在一起,并且确保那些数据在适当的时候被释放。

每个注册的事件处理程序都与两项数据相关联,libpq仅将其视为不透明的void *指针。 有一个透传指针,由应用程序在向 PGconn 注册事件处理程序时提供。 透传指针在PGconn及其生成的所有PGresult的生命周期内永远不会更改; 因此,如果使用,它必须指向长期存在的数据。 此外,还有一个实例数据指针,在每个PGconnPGresult中一开始都是NULL。 可以使用PQinstanceDataPQsetInstanceDataPQresultInstanceDataPQresultSetInstanceData函数来操作此指针。 请注意,与透传指针不同,PGconn的实例数据不会自动继承到从中创建的PGresultlibpq不知道透传和实例数据指针指向的内容(如果有的话),也永远不会尝试释放它们 — 这是事件处理程序的责任。

31.13.1. 事件类型 #

枚举PGEventId命名了事件系统处理的事件类型。它的所有值的名称都以PGEVT开始。对于每一种事件类型,都有一个相应的事件信息结构体用来承载传递给事件处理器的参数。事件类型是:

PGEVT_REGISTER #

注册事件在PQregisterEventProc被调用时触发。此时最适合初始化事件过程可能需要的instanceData。每个连接中的每个事件过程只会触发一次注册事件。如果事件过程失败,则中止注册。

typedef struct
{
    PGconn *conn;
} PGEventRegister;

收到PGEVT_REGISTER事件时,应将evtInfo指针强制转换为PGEventRegister *。此结构体包含一个PGconn,它应处于CONNECTION_OK状态;如果调用PQregisterEventProc紧接在取得正常的PGconn之后,就能保证这一点。返回失败代码时,必须完成全部清理工作,因为不会发送PGEVT_CONNDESTROY事件。

PGEVT_CONNRESET #

连接重置事件会在完成以下调用时触发:PQresetPQresetPoll。在这两种情况下,只有重置成功才会触发该事件。如果事件过程失败,整个连接重置就会失败;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 无法管理这部分内存。如果不清理,就会造成内存泄漏。

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_RESULTCREATEPGEVT_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。事件过程负责正确清理其事件数据,因为 libpq 无法管理这部分内存。如果不清理,就会造成内存泄漏。

typedef struct
{
    PGresult *result;
} PGEventResultDestroy;

收到PGEVT_RESULTDESTROY事件时,应将evtInfo指针强制转换为PGEventResultDestroy *。该事件触发于以下函数执行任何其他清理工作之前:PQclear。事件过程的返回值会被忽略,因为无法通过以下函数报告失败:PQclear。此外,事件过程失败不应中止清理不再使用的内存的过程。

31.13.2. 事件回调函数 #

PGEventProc #

PGEventProc 是通过 typedef 定义的事件过程指针类型,也就是接收 libpq 事件的用户回调函数的指针类型。事件过程的签名必须为:

int eventproc(PGEventId evtId, void *evtInfo, void *passThrough)

evtId 参数指示发生了哪一种 PGEVT 事件。必须将 evtInfo 指针强制转换为适当的结构体类型,以获取关于该事件的更多信息。passThrough 参数是在注册事件过程时传给 PQregisterEventProc 的指针。函数应在成功时返回非零值,在失败时返回零。

在任何一个PGconn中,一个特定事件过程只能被注册一次。这是因为该过程的地址被用作查找键来标识相关的实例数据。

小心

在 Windows 上,函数可能有两个不同的地址:一个在 DLL 外部可见,另一个在 DLL 内部可见。使用 libpq 的事件过程函数时,务必始终使用其中同一个地址,否则会产生混淆。确保代码正常工作的最简单做法,是将事件过程声明为 static。如果需要在过程所在的源文件之外取得其地址,应提供一个单独的函数来返回该地址。

31.13.3. 事件支持函数 #

PQregisterEventProc #

为 libpq 注册一个事件回调过程。

int PQregisterEventProc(PGconn *conn, PGEventProc proc,
                        const char *name, void *passThrough);

对于希望接收其事件的每个 PGconn,都必须注册一次事件过程。一个连接可注册的事件过程数量只受内存限制。函数成功时返回非零值,失败时返回零。

当一个 libpq 事件被触发时,proc参数将被调用。它的内存地址也被用来查找instanceDataname参数被用来在错误消息中引用该事件过程。这个值不能是NULL或一个零长度串。名字串被复制到PGconn中,因此传递进来的东西不需要长期存在。当一个事件发生时,passThrough指针被传递给proc。这个参数可以是NULL

PQsetInstanceData #

设置连接conn的用于过程procinstanceDatadata。它在成功时返回非零值,失败时返回零(只有proc没有被正确地注册在conn中,才可能会失败)。

int PQsetInstanceData(PGconn *conn, PGEventProc proc, void *data);
PQinstanceData #

返回连接conn的与过程proc相关的instanceData,如果没有则返回NULL

void *PQinstanceData(const PGconn *conn, PGEventProc proc);
PQresultSetInstanceData #

将结果中针对 procinstanceData 设置为 data。成功时返回非零值,失败时返回零。(只有当 proc 未在结果中正确注册时,才可能失败。)

int PQresultSetInstanceData(PGresult *res, PGEventProc proc, void *data);
PQresultInstanceData #

返回结果的与过程proc相关的instanceData,如果没有则返回NULL

void *PQresultInstanceData(const PGresult *res, PGEventProc proc);

31.13.4. 事件实例 #

下面给出一个示例框架,用于管理与 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 */
}

提交更正

译文有误、术语不当或页面显示问题,请到译文仓库 pgsty/pgdoc 报告译文问题。 英文原文本身的问题,请在当前版本的对应页面向上游反馈;上游不再修订已结束维护的版本。