5 32.14.2. Event Callback Procedure
6 32.14.3. Event Support Functions
9 libpq's event system is designed to notify registered event handlers
10 about interesting libpq events, such as the creation or destruction of
11 PGconn and PGresult objects. A principal use case is that this allows
12 applications to associate their own data with a PGconn or PGresult and
13 ensure that that data is freed at an appropriate time.
15 Each registered event handler is associated with two pieces of data,
16 known to libpq only as opaque void * pointers. There is a pass-through
17 pointer that is provided by the application when the event handler is
18 registered with a PGconn. The pass-through pointer never changes for
19 the life of the PGconn and all PGresults generated from it; so if used,
20 it must point to long-lived data. In addition there is an instance data
21 pointer, which starts out NULL in every PGconn and PGresult. This
22 pointer can be manipulated using the PQinstanceData, PQsetInstanceData,
23 PQresultInstanceData and PQresultSetInstanceData functions. Note that
24 unlike the pass-through pointer, instance data of a PGconn is not
25 automatically inherited by PGresults created from it. libpq does not
26 know what pass-through and instance data pointers point to (if
27 anything) and will never attempt to free them — that is the
28 responsibility of the event handler.
30 32.14.1. Event Types #
32 The enum PGEventId names the types of events handled by the event
33 system. All its values have names beginning with PGEVT. For each event
34 type, there is a corresponding event info structure that carries the
35 parameters passed to the event handlers. The event types are:
38 The register event occurs when PQregisterEventProc is called. It
39 is the ideal time to initialize any instanceData an event
40 procedure may need. Only one register event will be fired per
41 event handler per connection. If the event procedure fails
42 (returns zero), the registration is canceled.
49 When a PGEVT_REGISTER event is received, the evtInfo pointer
50 should be cast to a PGEventRegister *. This structure contains a
51 PGconn that should be in the CONNECTION_OK status; guaranteed if
52 one calls PQregisterEventProc right after obtaining a good
53 PGconn. When returning a failure code, all cleanup must be
54 performed as no PGEVT_CONNDESTROY event will be sent.
57 The connection reset event is fired on completion of PQreset or
58 PQresetPoll. In both cases, the event is only fired if the reset
59 was successful. The return value of the event procedure is
60 ignored in PostgreSQL v15 and later. With earlier versions,
61 however, it's important to return success (nonzero) or the
62 connection will be aborted.
69 When a PGEVT_CONNRESET event is received, the evtInfo pointer
70 should be cast to a PGEventConnReset *. Although the contained
71 PGconn was just reset, all event data remains unchanged. This
72 event should be used to reset/reload/requery any associated
73 instanceData. Note that even if the event procedure fails to
74 process PGEVT_CONNRESET, it will still receive a
75 PGEVT_CONNDESTROY event when the connection is closed.
78 The connection destroy event is fired in response to PQfinish.
79 It is the event procedure's responsibility to properly clean up
80 its event data as libpq has no ability to manage this memory.
81 Failure to clean up will lead to memory leaks.
88 When a PGEVT_CONNDESTROY event is received, the evtInfo pointer
89 should be cast to a PGEventConnDestroy *. This event is fired
90 prior to PQfinish performing any other cleanup. The return value
91 of the event procedure is ignored since there is no way of
92 indicating a failure from PQfinish. Also, an event procedure
93 failure should not abort the process of cleaning up unwanted
97 The result creation event is fired in response to any query
98 execution function that generates a result, including
99 PQgetResult. This event will only be fired after the result has
100 been created successfully.
106 } PGEventResultCreate;
108 When a PGEVT_RESULTCREATE event is received, the evtInfo pointer
109 should be cast to a PGEventResultCreate *. The conn is the
110 connection used to generate the result. This is the ideal place
111 to initialize any instanceData that needs to be associated with
112 the result. If an event procedure fails (returns zero), that
113 event procedure will be ignored for the remaining lifetime of
114 the result; that is, it will not receive PGEVT_RESULTCOPY or
115 PGEVT_RESULTDESTROY events for this result or results copied
119 The result copy event is fired in response to PQcopyResult. This
120 event will only be fired after the copy is complete. Only event
121 procedures that have successfully handled the PGEVT_RESULTCREATE
122 or PGEVT_RESULTCOPY event for the source result will receive
123 PGEVT_RESULTCOPY events.
131 When a PGEVT_RESULTCOPY event is received, the evtInfo pointer
132 should be cast to a PGEventResultCopy *. The src result is what
133 was copied while the dest result is the copy destination. This
134 event can be used to provide a deep copy of instanceData, since
135 PQcopyResult cannot do that. If an event procedure fails
136 (returns zero), that event procedure will be ignored for the
137 remaining lifetime of the new result; that is, it will not
138 receive PGEVT_RESULTCOPY or PGEVT_RESULTDESTROY events for that
139 result or results copied from it.
141 PGEVT_RESULTDESTROY #
142 The result destroy event is fired in response to a PQclear. It
143 is the event procedure's responsibility to properly clean up its
144 event data as libpq has no ability to manage this memory.
145 Failure to clean up will lead to memory leaks.
150 } PGEventResultDestroy;
152 When a PGEVT_RESULTDESTROY event is received, the evtInfo
153 pointer should be cast to a PGEventResultDestroy *. This event
154 is fired prior to PQclear performing any other cleanup. The
155 return value of the event procedure is ignored since there is no
156 way of indicating a failure from PQclear. Also, an event
157 procedure failure should not abort the process of cleaning up
160 32.14.2. Event Callback Procedure #
163 PGEventProc is a typedef for a pointer to an event procedure,
164 that is, the user callback function that receives events from
165 libpq. The signature of an event procedure must be
167 int eventproc(PGEventId evtId, void *evtInfo, void *passThrough)
169 The evtId parameter indicates which PGEVT event occurred. The
170 evtInfo pointer must be cast to the appropriate structure type
171 to obtain further information about the event. The passThrough
172 parameter is the pointer provided to PQregisterEventProc when
173 the event procedure was registered. The function should return a
174 non-zero value if it succeeds and zero if it fails.
176 A particular event procedure can be registered only once in any
177 PGconn. This is because the address of the procedure is used as
178 a lookup key to identify the associated instance data.
182 On Windows, functions can have two different addresses: one
183 visible from outside a DLL and another visible from inside the
184 DLL. One should be careful that only one of these addresses is
185 used with libpq's event-procedure functions, else confusion will
186 result. The simplest rule for writing code that will work is to
187 ensure that event procedures are declared static. If the
188 procedure's address must be available outside its own source
189 file, expose a separate function to return the address.
191 32.14.3. Event Support Functions #
193 PQregisterEventProc #
194 Registers an event callback procedure with libpq.
196 int PQregisterEventProc(PGconn *conn, PGEventProc proc,
197 const char *name, void *passThrough);
199 An event procedure must be registered once on each PGconn you
200 want to receive events about. There is no limit, other than
201 memory, on the number of event procedures that can be registered
202 with a connection. The function returns a non-zero value if it
203 succeeds and zero if it fails.
205 The proc argument will be called when a libpq event is fired.
206 Its memory address is also used to lookup instanceData. The name
207 argument is used to refer to the event procedure in error
208 messages. This value cannot be NULL or a zero-length string. The
209 name string is copied into the PGconn, so what is passed need
210 not be long-lived. The passThrough pointer is passed to the proc
211 whenever an event occurs. This argument can be NULL.
214 Sets the connection conn's instanceData for procedure proc to
215 data. This returns non-zero for success and zero for failure.
216 (Failure is only possible if proc has not been properly
219 int PQsetInstanceData(PGconn *conn, PGEventProc proc, void *data);
222 Returns the connection conn's instanceData associated with
223 procedure proc, or NULL if there is none.
225 void *PQinstanceData(const PGconn *conn, PGEventProc proc);
227 PQresultSetInstanceData #
228 Sets the result's instanceData for proc to data. This returns
229 non-zero for success and zero for failure. (Failure is only
230 possible if proc has not been properly registered in the
233 int PQresultSetInstanceData(PGresult *res, PGEventProc proc, void *data);
235 Beware that any storage represented by data will not be
236 accounted for by PQresultMemorySize, unless it is allocated
237 using PQresultAlloc. (Doing so is recommendable because it
238 eliminates the need to free such storage explicitly when the
239 result is destroyed.)
241 PQresultInstanceData #
242 Returns the result's instanceData associated with proc, or NULL
245 void *PQresultInstanceData(const PGresult *res, PGEventProc proc);
247 32.14.4. Event Example #
249 Here is a skeleton example of managing private data associated with
250 libpq connections and results.
252 /* required header for libpq events (note: includes libpq-fe.h) */
253 #include <libpq-events.h>
255 /* The instanceData */
263 static int myEventProc(PGEventId evtId, void *evtInfo, void *passThrough);
271 PQconnectdb("dbname=postgres options=-csearch_path=");
273 if (PQstatus(conn) != CONNECTION_OK)
275 /* PQerrorMessage's result includes a trailing newline */
276 fprintf(stderr, "%s", PQerrorMessage(conn));
281 /* called once on any connection that should receive events.
282 * Sends a PGEVT_REGISTER to myEventProc.
284 if (!PQregisterEventProc(conn, myEventProc, "mydata_proc", NULL))
286 fprintf(stderr, "Cannot register PGEventProc\n");
291 /* conn instanceData is available */
292 data = PQinstanceData(conn, myEventProc);
294 /* Sends a PGEVT_RESULTCREATE to myEventProc */
295 res = PQexec(conn, "SELECT 1 + 1");
297 /* result instanceData is available */
298 data = PQresultInstanceData(res, myEventProc);
300 /* If PG_COPYRES_EVENTS is used, sends a PGEVT_RESULTCOPY to myEventProc */
301 res_copy = PQcopyResult(res, PG_COPYRES_TUPLES | PG_COPYRES_EVENTS);
303 /* result instanceData is available if PG_COPYRES_EVENTS was
304 * used during the PQcopyResult call.
306 data = PQresultInstanceData(res_copy, myEventProc);
308 /* Both clears send a PGEVT_RESULTDESTROY to myEventProc */
312 /* Sends a PGEVT_CONNDESTROY to myEventProc */
319 myEventProc(PGEventId evtId, void *evtInfo, void *passThrough)
325 PGEventRegister *e = (PGEventRegister *)evtInfo;
326 mydata *data = get_mydata(e->conn);
328 /* associate app specific data with connection */
329 PQsetInstanceData(e->conn, myEventProc, data);
333 case PGEVT_CONNRESET:
335 PGEventConnReset *e = (PGEventConnReset *)evtInfo;
336 mydata *data = PQinstanceData(e->conn, myEventProc);
339 memset(data, 0, sizeof(mydata));
343 case PGEVT_CONNDESTROY:
345 PGEventConnDestroy *e = (PGEventConnDestroy *)evtInfo;
346 mydata *data = PQinstanceData(e->conn, myEventProc);
348 /* free instance data because the conn is being destroyed */
354 case PGEVT_RESULTCREATE:
356 PGEventResultCreate *e = (PGEventResultCreate *)evtInfo;
357 mydata *conn_data = PQinstanceData(e->conn, myEventProc);
358 mydata *res_data = dup_mydata(conn_data);
360 /* associate app specific data with result (copy it from conn) */
361 PQresultSetInstanceData(e->result, myEventProc, res_data);
365 case PGEVT_RESULTCOPY:
367 PGEventResultCopy *e = (PGEventResultCopy *)evtInfo;
368 mydata *src_data = PQresultInstanceData(e->src, myEventProc);
369 mydata *dest_data = dup_mydata(src_data);
371 /* associate app specific data with result (copy it from a result) *
373 PQresultSetInstanceData(e->dest, myEventProc, dest_data);
377 case PGEVT_RESULTDESTROY:
379 PGEventResultDestroy *e = (PGEventResultDestroy *)evtInfo;
380 mydata *data = PQresultInstanceData(e->result, myEventProc);
382 /* free instance data because the result is being destroyed */
388 /* unknown event ID, just return true. */
393 return true; /* event processing succeeded */