33.13. 事件系统

33.13.1. 事件类型33.13.2. 事件回调函数33.13.3. 事件支持函数33.13.4. 事件实例

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

每一个已注册的事件处理器与两部分数据相关,对于libpq它们只是透明的void *指针。当事件处理器被注册到一个PGconn时,会有一个应用提供的转移指针。该转移指针在PGconn及其产生的所有PGresult的生命期内都不会改变。因此,如果使用它,它必须指向长期存在的数据。此外,还有一个instance data指针,它在每一个PGconnPGresult中都开始于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 *。这个结构包含一个状态应该为CONNECTION_OKPGconn,保证在得到一个良好的PGconn之后能马上调用PQregisterEventProc。当返回一个失败代码时,所有的清理都必须被执行而不会发送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的失败。还有,一个事件过程失败不该中断不需要的内存的清理过程。

33.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。如果过程的地址必须对它自己的源代码文件之外可见,提供一个单独的函数来返回该地址。

33.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没有被正确地注册在conn中,才可能会失败)。

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

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

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

33.13.4. 事件实例

这里是一个管理与 libpq 连接和结果相关的私有数据的例子的框架。

/* 要求 libpq 事件的头文件(注意:包括 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");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:{PGEventRegister *e = (PGEventRegister *)evtInfo;mydata *data = get_mydata(e->conn);/* 将应用相关的数据与连接关联起来 */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);/* 因为连接正在被销毁,释放示例数据 */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);/* 把应用相关的数据与结果(从 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);/* 把应用相关的数据与结果(从一个结果复制过来)关联起来 */PQsetResultInstanceData(e->dest, myEventProc, dest_data);break;}case PGEVT_RESULTDESTROY:{PGEventResultDestroy *e = (PGEventResultDestroy *)evtInfo;mydata *data = PQresultInstanceData(e->result, myEventProc);/* 因为结果正在被销毁,释放实例数据 */if (data)free_mydata(data);break;}/* 未知事件 ID,只返回 TRUE。 */default:break;}return TRUE; /* 事件处理成功 */
}

本文转自PostgreSQL中文社区,原文链接:33.13. 事件系统

PostgreSQL 10.1 手册_部分 IV. 客户端接口_第 33 章 libpq - C 库_33.13. 事件系统相关推荐

  1. PostgreSQL 10.1 手册_部分 IV. 客户端接口_第 33 章 libpq - C 库_33.11. 杂项函数

    33.11. 杂项函数 一如往常,总有一些函数不适合放在任何其他地方. PQfreemem 释放libpq分配的内存. void PQfreemem(void *ptr); 释放libpq分配的内存, ...

  2. PostgreSQL 10.1 手册_部分 IV. 客户端接口_第 34 章 大对象_34.2. 实现特性

    34.2. 实现特性 大对象的实现将大对象分解成很多"数据块"并且将这些数据块存储在数据库的行中.一个B-tree索引用来保证在进行随机访问读写时能够根据数据块号快速地搜索到正确的 ...

  3. PostgreSQL 10.1 手册_部分 III. 服务器管理_第 20 章 客户端认证

    第 20 章 客户端认证 目录 20.1. pg_hba.conf文件20.2. 用户名映射20.3. 认证方法 20.3.1. 信任认证20.3.2. 口令认证20.3.3. GSSAPI 认证20 ...

  4. PostgreSQL 10.1 手册_部分 II. SQL 语言_第 9 章 函数和操作符_9.4. 字符串函数和操作符...

    9.4. 字符串函数和操作符 9.4.1. format 本节描述了用于检查和操作字符串值的函数和操作符.在这个环境中的串包括所有类型character.character varying和text的 ...

  5. PostgreSQL 10.1 手册_部分 III. 服务器管理_第 16 章 从源代码安装_16.5. 安装后设置...

    16.5. 安装后设置 16.5.1. 共享库16.5.2. 环境变量 16.5.1. 共享库 在一些有共享库的系统里,你需要告诉你的系统如何找到新安装的共享库.那些并不是必须做这个工作的系统包括 F ...

  6. PostgreSQL 10.1 手册_部分 II. SQL 语言_第 14 章 性能提示_14.1. 使用EXPLAIN

    14.1. 使用EXPLAIN 14.1.1. EXPLAIN基础 14.1.2. EXPLAIN ANALYZE 14.1.3. 警告 PostgreSQL为每个收到查询产生一个查询计划. 选择正确 ...

  7. PostgreSQL 10.1 手册_部分 III. 服务器管理_第 19 章 服务器配置_19.11. 客户端连接默认值...

    19.11. 客户端连接默认值 19.11.1. 语句行为 19.11.2. 区域和格式化 19.11.3. 共享库预载入 19.11.4. 其他默认值 19.11.1. 语句行为 search_pa ...

  8. PostgreSQL 10.1 手册_部分 II. SQL 语言_第 8 章 数据类型_8.10. 位串类型

    8.10. 位串类型 位串就是一串 1 和 0 的串.它们可以用于存储和可视化位掩码.我们有两种类型的 SQL 位类型:bit(n)和bit varying(n),其中 n是一个正整数. bit类型的 ...

  9. PostgreSQL 10.1 手册_部分 III. 服务器管理_第 19 章 服务器配置_19.8. 错误报告和日志...

    19.8. 错误报告和日志 19.8.1. 在哪里做日志19.8.2. 什么时候记录日志19.8.3. 记录什么到日志19.8.4. 使用 CSV 格式的日志输出19.8.5. 进程标题 19.8.1 ...

最新文章

  1. Tungsten Fabric SDN — VNC API — API Client 的 Python SDK
  2. 对函数指针与typedef的理解:typedef void (*sighandler_t)(int)
  3. ITK:笛卡尔方位角高程
  4. QT 4.8.5支持电容触摸屏 和 鼠标
  5. bzoj1833: [ZJOI2010]count 数字计数USACO37 Cow Queueing 数数的梦(数位DP)
  6. python的zip方法_python zip()函数使用方法解析
  7. Django框架——模型(数据库操作)
  8. SQLAlchemy 增删改查
  9. python处理json文件_python读取json文件转成excel
  10. C语言如何输出100以内的质数?(带注释)
  11. 什么方法可以显著提高程序员工作效率
  12. Gnome3桌面美化
  13. 常见的SSL证书错误代码及解决方法
  14. php弱口令总结,web漏洞之弱口令
  15. 微信小程序手把手教你实现类似Android中ViewPager控件效果
  16. hopfileld神经网络_图卷积神经网络
  17. SketchUp插件可视化开发工具SketchUp Ruby Code Editor
  18. 基于Maven+SpringMVC+Spring+MyBatis+Layui整合框架,超详细的SSM整合❤️
  19. 组FreeNas11.3的一点心得
  20. 电子调谐器的主要引脚及作用

热门文章

  1. 一场360容器圈的武林大会“360互联网技术训练营第九期—360容器技术解密与实践” (附PPT与视频)...
  2. mysql 大数据量插入遇到瓶颈 可行性方案探究
  3. left join后边跟on...and 和where...and的区别
  4. mysql 的 sql_mode.only_full_group_by属性解析
  5. LeetCode-计数质数
  6. 读者专属福利: Git面试宝典分享
  7. Dubbo管理控制台dubbo-admin搭建
  8. 字节JAVA研发面试
  9. springbatch导出mysql数据到外部文件
  10. 22. C# -- 抽象类和接口