## 34.14. Event System [34.14.1. Event Types](libpq-events.html#LIBPQ-EVENTS-TYPES) [34.14.2. Event Callback Procedure](libpq-events.html#LIBPQ-EVENTS-PROC) [34.14.3. Event Support Functions](libpq-events.html#LIBPQ-EVENTS-FUNCS) [34.14.4. Event Example](libpq-events.html#LIBPQ-EVENTS-EXAMPLE) libpq's event system is designed to notify registered event handlers about interesting libpq events, such as the creation or destruction of`PGconn`and`PGresult`objects. A principal use case is that this allows applications to associate their own data with a`PGconn`or`PGresult`and ensure that that data is freed at an appropriate time. Each registered event handler is associated with two pieces of data, known to libpq only as opaque`void *`pointers. There is a*pass-through*pointer that is provided by the application when the event handler is registered with a`PGconn`. The pass-through pointer never changes for the life of the`PGconn`and all`PGresult`s generated from it; so if used, it must point to long-lived data. In addition there is an*instance data*pointer, which starts out`NULL`in every`PGconn`and`PGresult`. This pointer can be manipulated using the[`PQinstanceData`](libpq-events.html#LIBPQ-PQINSTANCEDATA),[`PQsetInstanceData`](libpq-events.html#LIBPQ-PQSETINSTANCEDATA),[`PQresultInstanceData`](libpq-events.html#LIBPQ-PQRESULTINSTANCEDATA)and`PQsetResultInstanceData`functions. Note that unlike the pass-through pointer, instance data of a`PGconn`is not automatically inherited by`PGresult`s created from it. libpq does not know what pass-through and instance data pointers point to (if anything) and will never attempt to free them — that is the responsibility of the event handler. ### 34.14.1. Event Types The enum`PGEventId`names the types of events handled by the event system. All its values have names beginning with`PGEVT`. For each event type, there is a corresponding event info structure that carries the parameters passed to the event handlers. The event types are: `PGEVT_REGISTER` The register event occurs when[`PQregisterEventProc`](libpq-events.html#LIBPQ-PQREGISTEREVENTPROC)is called. It is the ideal time to initialize any`instanceData`可能需要一个事件过程。每个连接的每个事件处理程序只会触发一个注册事件。如果事件过程失败,则中止注册。 ``` typedef struct { PGconn *conn; } PGEventRegister; ``` 当一个`PGEVT_REGISTER`事件被接收,*`事件信息`*指针应转换为`PGEventRegister *`.这个结构包含一个`PGconn`那应该在`CONNECTION_OK`地位;保证如果有人打电话[`PQregisterEventProc`](libpq-events.html#LIBPQ-PQREGISTEREVENTPROC)在获得好东西之后`PGconn`.返回失败代码时,所有清理必须执行为 no`PGEVT_CONNDESTROY`事件将被发送。 `PGEVT_CONNRESET` 连接重置事件在完成时触发[`复位`](libpq-connect.html#LIBPQ-PQRESET)或者`PQresetPoll`.在这两种情况下,只有在重置成功时才会触发事件。如果事件过程失败,则整个连接重置失败;这`PGconn`被放入`CONNECTION_BAD`状态和`PQresetPoll`将返回`PGRES_POLLING_FAILED`. ``` typedef struct { PGconn *conn; } PGEventConnReset; ``` 当一个`PGEVT_CONNRESET`事件被接收,*`事件信息`*指针应转换为`PGEventConnReset *`.虽然包含`PGconn`刚刚重置,所有事件数据保持不变。此事件应用于重置/重新加载/重新查询任何关联的`实例数据`.请注意,即使事件过程无法处理`PGEVT_CONNRESET`,它仍然会收到一个`PGEVT_CONNDESTROY`连接关闭时的事件。 `PGEVT_CONNDESTROY` 连接销毁事件被触发以响应[`PQ完成`](libpq-connect.html#LIBPQ-PQFINISH).正确清理其事件数据是事件过程的责任,因为 libpq 无法管理此内存。清理失败会导致内存泄漏。 ``` typedef struct { PGconn *conn; } PGEventConnDestroy; ``` 当一个`PGEVT_CONNDESTROY`事件被接收,*`事件信息`*指针应转换为`PGEventConnDestroy *`.此事件在之前触发[`PQ完成`](libpq-connect.html#LIBPQ-PQFINISH)执行任何其他清理。事件过程的返回值被忽略,因为无法从[`PQ完成`](libpq-connect.html#LIBPQ-PQFINISH).此外,事件过程失败不应中止清理不需要的内存的过程。 `PGEVT_RESULTCREATE` 结果创建事件被触发以响应任何生成结果的查询执行函数,包括[`PQgetResult`](libpq-async.html#LIBPQ-PQGETRESULT).只有在成功创建结果后才会触发此事件。 ``` typedef struct { PGconn *conn; PGresult *result; } PGEventResultCreate; ``` 当一个`PGEVT_RESULTCREATE`事件被接收,*`事件信息`*指针应转换为`PGEventResultCreate *`.这*`康恩`*是用于生成结果的连接。这是初始化任何`实例数据`需要与结果相关联。如果事件过程失败,结果将被清除并传播失败。事件过程不能试图[`PQclear`](libpq-exec.html#LIBPQ-PQCLEAR)结果对象本身。返回失败代码时,所有清理必须执行为 no`PGEVT_RESULTDESTROY`事件将被发送。 `PGEVT_RESULTCOPY` 结果复制事件被触发以响应[`PQcopyResult`](libpq-misc.html#LIBPQ-PQCOPYRESULT).只有在复制完成后才会触发此事件。只有已成功处理的事件过程`PGEVT_RESULTCREATE`要么`PGEVT_RESULTCOPY`源结果的事件将收到`PGEVT_RESULTCOPY`事件。 ``` typedef struct { const PGresult *src; PGresult *dest; } PGEventResultCopy; ``` 当一个`PGEVT_RESULTCOPY`事件被接收,*`事件信息`*指针应转换为`PGEventResultCopy *`.这*`源代码`*结果是复制的内容,而*`目的地`*结果是复制目标。此事件可用于提供`实例数据`, 自从`PQcopyResult`不可以这样做。如果事件过程失败,整个复制操作将失败,并且*`目的地`*结果将被清除。返回失败代码时,所有清理必须执行为 no`PGEVT_RESULTDESTROY`将针对目标结果发送事件。 `PGEVT_RESULTDESTROY` 结果销毁事件被触发以响应[`PQclear`](libpq-exec.html#LIBPQ-PQCLEAR).由于libpq无法管理该内存,因此事件程序有责任正确清理其事件数据。未能清理将导致内存泄漏。 ``` typedef struct { PGresult *result; } PGEventResultDestroy; ``` 当`PGEVT_结果演示`事件收到后*`埃夫廷福`*指针应投射到`PGEventResultDestroy*`.此事件在[`PQclear`](libpq-exec.html#LIBPQ-PQCLEAR)执行任何其他清理。事件过程的返回值将被忽略,因为无法从中指示故障[`PQclear`](libpq-exec.html#LIBPQ-PQCLEAR)。此外,事件过程失败不应中止清除不需要的内存的过程。 ### 34.14.2.事件回调过程 `PGEventProc`[](<>) `PGEventProc`是指向事件过程的指针的typedef,即从libpq接收事件的用户回调函数。事件程序的签名必须是 ``` int eventproc(PGEventId evtId, void *evtInfo, void *passThrough) ``` 这个*`埃夫蒂德`*参数指示`PGEVT`事件发生了。这个*`埃夫廷福`*指针必须强制转换为适当的结构类型,才能获得有关事件的进一步信息。这个*`穿越`*参数是提供给[`PQregisterEventProc`](libpq-events.html#LIBPQ-PQREGISTEREVENTPROC)当事件过程被注册时。如果函数成功,则返回非零值;如果函数失败,则返回零。 A particular event procedure can be registered only once in any`PGconn`. This is because the address of the procedure is used as a lookup key to identify the associated instance data. ### Caution On Windows, functions can have two different addresses: one visible from outside a DLL and another visible from inside the DLL. One should be careful that only one of these addresses is used with libpq's event-procedure functions, else confusion will result. The simplest rule for writing code that will work is to ensure that event procedures are declared`static`. If the procedure's address must be available outside its own source file, expose a separate function to return the address. ### 34.14.3. Event Support Functions `PQregisterEventProc`[](<>) Registers an event callback procedure with libpq. ``` int PQregisterEventProc(PGconn *conn, PGEventProc proc, const char *name, void *passThrough); ``` An event procedure must be registered once on each`PGconn`you want to receive events about. There is no limit, other than memory, on the number of event procedures that can be registered with a connection. The function returns a non-zero value if it succeeds and zero if it fails. The*`proc`*argument will be called when a libpq event is fired. Its memory address is also used to lookup`instanceData`. The*`name`*argument is used to refer to the event procedure in error messages. This value cannot be`NULL`or a zero-length string. The name string is copied into the`PGconn`, so what is passed need not be long-lived. The*`passThrough`*pointer is passed to the*`proc`*whenever an event occurs. This argument can be`空值`. `PQsetInstanceData`[](<>) 设置连接*`康恩`*的`实例数据`办理手续*`过程`*到*`数据`*.这将返回非零表示成功,返回零表示失败。(只有在*`过程`*没有正确注册*`康恩`*.) ``` int PQsetInstanceData(PGconn *conn, PGEventProc proc, void *data); ``` `PQinstanceData`[](<>) 返回连接*`康恩`*的`实例数据`与程序相关*`过程`*, 或者`空值`如果没有。 ``` void *PQinstanceData(const PGconn *conn, PGEventProc proc); ``` `PQresultSetInstanceData`[](<>) 设置结果的`instanceData`for*`proc`*to*`data`*. This returns non-zero for success and zero for failure. (Failure is only possible if*`proc`*has not been properly registered in the result.) ``` int PQresultSetInstanceData(PGresult *res, PGEventProc proc, void *data); ``` Beware that any storage represented by*`data`*will not be accounted for by[`PQresultMemorySize`](libpq-misc.html#LIBPQ-PQRESULTMEMORYSIZE), unless it is allocated using[`PQresultAlloc`](libpq-misc.html#LIBPQ-PQRESULTALLOC). (Doing so is recommendable because it eliminates the need to free such storage explicitly when the result is destroyed.) `PQresultInstanceData`[](<>) Returns the result's`instanceData`associated with*`proc`*, or`NULL`if there is none. ``` void *PQresultInstanceData(const PGresult *res, PGEventProc proc); ``` ### 34.14.4. Event Example Here is a skeleton example of managing private data associated with libpq connections and results. ``` /* required header for libpq events (note: includes libpq-fe.h) */ #include /* 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) */ 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 */ } ```