VastbaseG100

基于openGauss内核开发的企业级关系型数据库。

Menu

事件系统

概览

libpq的事件系统,允许开发者按照接口要求自行编写时间处理器并进行注册,事件系统可将libpq事件通知已注册的事件处理器,例如PGconn以及PGresult对象的创建和析构。典型的使用情况是应用可以将自己的数据与一个PGconn或者PGresult对象关联在一起,并且提供恰当的时机释放这些数据。

每一个已注册的事件处理器与两部分数据相关,对于libpq它们只是透明的void *指针。当事件处理器被注册到一个PGconn时,会有一个应用提供的转移指针。该转移指针在PGconn及其产生的所有PGresult的生命期内都不会改变。因此,如果使用它,它必须指向长期存在的数据。此外,还有一个instance data指针,在每一个PGconn和PGresult对象中默认都为NULL。这个指针可以使用PQinstanceData、PQsetInstanceData、PQresultInstanceData和PQresultSetInstanceData函数操纵。注意和转移指针不同,一个PGconn的实例数据不会被从它创建的PGresult自动继承。libpq不知道转移指针和实例数据指针实际指向什么,这些数据的释放也由开发者自行编写的事件处理器来负责。

事件类型和触发函数

枚举PGEventId命名了事件系统处理的事件类型。它的所有值的名称都以PGEVT开始。对于每一种事件类型,都有一个相应的事件信息结构用来承载传递给事件处理器的参数。事件类型及其触发函数和相关说明如下表:

事件类型 触发函数和说明
PGEVT_REGISTER 当PQregisterEventProc被调用时,注册事件会发生
PGEVT_CONNRESET 连接重置事件在PQreset或PQresetPoll完成时被触发,只有重置成功才会触发该事件。
PGEVT_CONNDESTROY 连接销毁事件在调用PQfinish时会被触发。
PGEVT_RESULTCREATE 在任何生成一个结果的查询执行函数被执行时,结果创建事件会被触发。这些函数包括PQgetResult。这个事件只有在结果被成功地创建之后才会被触发。
PGEVT_RESULTCOPY 结果复制事件在调用PQcopyResult时会被触发,这个事件只会在复制完成后才被触发。
PGEVT_RESULTDESTROY 结果销毁事件在调用PQclear时会被触发

示例

/* libpq 事件的头文件(注意:libpq-events.h已包括 libpq-fe.h) */
#include <libpq-events.h>
#include <stdlib.h>
#include <memory.h>
#define DATALENGTH 10
/* instanceData */
typedef struct
{
    int n;
    char *str;
} mydata;

/* 事件处理器,接口如下 */
static int myEventProc(PGEventId evtId, void *evtInfo, void *passThrough);

mydata *get_mydata(PGconn *conn)
{
   int i;
   mydata *data = (mydata *)malloc(sizeof(mydata));
	data->str = (char *)malloc(DATALENGTH);
	data->n = DATALENGTH;
	for (i = 0;i<DATALENGTH; ++i)
        data->str[i] = ('a' + i);
   return data;
}

mydata *dup_mydata(mydata *src_data)
{
   int i;
   mydata *data = (mydata *)malloc(sizeof(mydata));
	data->str = (char *)malloc(src_data->n);
	data->n = src_data->n;
	for (i = 0;i<data->n; ++i)
	    data->str[i] = src_data->str[i];
   return data;
}

void free_mydata(mydata *data)
{
	free(data->str);
	free(data);
}

int
main(void)
{
    mydata *data;
    PGresult *res, *res_copy;
    PGconn *conn =
        PQconnectdb("dbname=vastbase");

    if (PQstatus(conn) != CONNECTION_OK)
    {
        fprintf(stderr, "Connection to database failed: %s",
                PQerrorMessage(conn));
        PQfinish(conn);
        return 1;
    }

    /* 在任何应该接收事件的连接上调用一次。
     * 发送一个 PGEVT_REGISTER 给 myEventProc。
     */
    if (!PQregisterEventProc(conn, myEventProc, "mydata_proc", NULL))
    {
        fprintf(stderr, "Cannot register PGEventProc\n");
        PQfinish(conn);
        return 1;
    }

    /* conn 的 instanceData 可用 */
    data = PQinstanceData(conn, myEventProc);

    /* 发送一个 PGEVT_RESULTCREATE 给 myEventProc */
    res = PQexec(conn, "SELECT 1 + 1");

    /* 结果的 instanceData 可用 */
    data = PQresultInstanceData(res, myEventProc);

    /* 如果使用了 PG_COPYRES_EVENTS,发送一个 PGEVT_RESULTCOPY 给 myEventProc */
    res_copy = PQcopyResult(res, PG_COPYRES_TUPLES | PG_COPYRES_EVENTS);

    /* 如果在 PQcopyResult 调用时使用了 PG_COPYRES_EVENTS,结果的 instanceData 可用。*/
    data = PQresultInstanceData(res_copy, myEventProc);

    /* 两个清除都发送一个 PGEVT_RESULTDESTROY 给 myEventProc */
    PQclear(res);
    PQclear(res_copy);

    /* 发送一个 PGEVT_CONNDESTROY 给 myEventProc */
    PQfinish(conn);

    return 0;
}

static int
myEventProc(PGEventId evtId, void *evtInfo, void *passThrough)
{
    switch (evtId)
    {
        case PGEVT_REGISTER:   /* PQregisterEventProc调用时触发 */
        {
            PGEventRegister *e = (PGEventRegister *)evtInfo;
            mydata *data = get_mydata(e->conn);

            /* 将应用相关的数据与连接关联起来 */
            PQsetInstanceData(e->conn, myEventProc, data);
            break;
        }

        case PGEVT_CONNRESET:  /* 调用PQreset或PQresetPoll且完成时被触发,本例不触发 */
        {
            PGEventConnReset *e = (PGEventConnReset *)evtInfo;
            mydata *data = PQinstanceData(e->conn, myEventProc);

            if (data)
              memset(data, 0, sizeof(mydata));
            break;
        }

        case PGEVT_CONNDESTROY:    /* 调用PQfinish时触发 */
        {
            PGEventConnDestroy *e = (PGEventConnDestroy *)evtInfo;
            mydata *data = PQinstanceData(e->conn, myEventProc);

            /* 因为连接正在被销毁,释放示例数据 */
            if (data)
              free_mydata(data);
            break;
        }

        case PGEVT_RESULTCREATE: /* 调用PQexec时触发 */
        {
            PGEventResultCreate *e = (PGEventResultCreate *)evtInfo;
            mydata *conn_data = PQinstanceData(e->conn, myEventProc);
            mydata *res_data = dup_mydata(conn_data);

            /* 把应用相关的数据与结果(从 conn 复制过来)关联起来 */
            PQresultSetInstanceData(e->result, myEventProc, res_data);
            break;
        }

        case PGEVT_RESULTCOPY: /* 调用PQcopyResult,并指定了PG_COPYRES_EVENTS时触发 */
        {
            PGEventResultCopy *e = (PGEventResultCopy *)evtInfo;
            mydata *src_data = PQresultInstanceData(e->src, myEventProc);
            mydata *dest_data = dup_mydata(src_data);

            /* 把应用相关的数据与结果(从一个结果复制过来)关联起来 */
            PQresultSetInstanceData(e->dest, myEventProc, dest_data);
            break;
        }

        case PGEVT_RESULTDESTROY:  /* 调用PQclear时触发 */
        {
            PGEventResultDestroy *e = (PGEventResultDestroy *)evtInfo;
            mydata *data = PQresultInstanceData(e->result, myEventProc);

            /* 因为结果正在被销毁,释放实例数据 */
            if (data)
              free_mydata(data);
            break;
        }

        /* 未知事件 ID,只返回true。 */
        default:
            break;
    }

    return true; /* 事件处理成功 */
}