libpq-events.zh.md 14.0 KB
Newer Older
李少辉-开发者's avatar
李少辉-开发者 已提交
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313
## 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 <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)
    {
        /* 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 */
}
```