33.13. Система событий
Система событий libpq разработана для уведомления функций-обработчиков об интересных событиях libpq, например, о создании и уничтожении объектов PGconn и PGresult. Основное их предназначение в том, чтобы позволить приложениям связать собственные данные с объектами PGconn и PGresult и обеспечить их освобождение в нужное время.
Каждый зарегистрированный обработчик событий связывается с двумя элементами данных, которые известны libpq только как скрытые указатели void *. Первый сквозной указатель передаётся приложением, когда обработчик событий регистрируется в PGconn. Этот указатель никогда не меняется на протяжении жизни PGconn и все объекты PGresult создаются с ним; поэтому, если он используется, он должен указывать на долгоживущие данные. В дополнение к нему имеется указатель данных экземпляра, который изначально равен NULL во всех объектах PGconn и PGresult. Этим указателем можно управлять с помощью функций 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 *. Эта структура содержит объектPGconn, который должен быть в состоянииCONNECTION_OK; это гарантируется, еслиPQregisterEventProcвызывается сразу после получения рабочего объектаPGconn. В случае выдачи кода ошибки всю очистку необходимо провести самостоятельно, так как событиеPGEVT_CONNDESTROYне поступит.PGEVT_CONNRESETСобытие сброса соединения происходит при завершении
PQresetилиPQresetPoll. В обоих случаях это событие вызывается, только если сброс был успешным. Если обработка события завершается ошибкой, происходит сбой всей операции сброса соединения; объект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_RESULTCREATEилиPGEVT_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— это определение типа для указателя на обработчик событий, то есть функцию обратного вызова, получающую события от 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, события которого представляют интерес. Число обработчиков событий, которые можно зарегистрировать для соединения, не ограничивается ничем, кроме объёма памяти. Эта функция возвращает ненулевое значение в случае успеха или ноль в противном случае.Процедура, переданная в аргументе
proc, будет вызываться, когда произойдёт событие libpq. Её адрес в памяти также применяется для поиска данныхinstanceData. Аргументnameиспользуется при упоминании обработчика событий в сообщениях об ошибках. Это значение не может быть равноNULLили указывать на строку нулевой длины. Эта строка имени копируется вPGconn, так что переданная строка может быть временной. Сквозной указатель (passThrough) будет передаваться обработчикуprocпри каждом вызове события. Этот аргумент может равнятьсяNULL.-
PQsetInstanceData Устанавливает для подключения
connуказательinstanceDataдля обработчикаprocравнымdata. Эта функция возвращает ненулевое значение в случае успеха или ноль в противном случае. (Ошибка возможна, только если обработчикprocне был корректно зарегистрирован для соединенияconn.)int PQsetInstanceData(PGconn *conn, PGEventProc proc, void *data);
-
PQinstanceData Возвращает для соединения
connуказатель наinstanceData, связанный с обработчикомproc, либоNULL, если такого обработчика нет.void *PQinstanceData(const PGconn *conn, PGEventProc proc);
-
PQresultSetInstanceData Устанавливает для объекта результата (
res) указательinstanceDataдля обработчикаprocравнымdata. Эта функция возвращает ненулевое значение в случае успеха или ноль в противном случае. (Ошибка возможна, только если обработчикprocне был корректно зарегистрирован для объекта результата.)int PQresultSetInstanceData(PGresult *res, PGEventProc proc, void *data);
Имейте в виду, что память, представленная параметром
data, не будет учитываться вPQresultMemorySize, если только она не была выделена функциейPQresultAlloc. (Этой функцией рекомендуется пользоваться, так как это избавляет от необходимости явно освобождать память после уничтожения результата.)-
PQresultInstanceData Возвращает для объекта результата (
res) указатель наinstanceData, связанный с обработчикомproc, либоNULL, если такого обработчика нет.void *PQresultInstanceData(const PGresult *res, PGEventProc proc);
33.13.4. Пример обработки событий
Ниже показан схематичный пример управления внутренними данными, связанными с подключениями и результатами libpq.
/* 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)
{
fprintf(stderr, "Connection to database failed: %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 */
}
33.13. Event System
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 passthrough pointer that is provided by the application when the event handler is registered with a PGconn. The passthrough pointer never changes for the life of the PGconn and all PGresults 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, PQsetInstanceData, PQresultInstanceData and PQsetResultInstanceData functions. Note that unlike the passthrough pointer, instance data of a PGconn is not automatically inherited by PGresults created from it. libpq does not know what passthrough and instance data pointers point to (if anything) and will never attempt to free them — that is the responsibility of the event handler.
33.13.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_REGISTERThe register event occurs when
PQregisterEventProcis called. It is the ideal time to initialize anyinstanceDataan event procedure may need. Only one register event will be fired per event handler per connection. If the event procedure fails, the registration is aborted.typedef struct { PGconn *conn; } PGEventRegister;When a
PGEVT_REGISTERevent is received, theevtInfopointer should be cast to aPGEventRegister *. This structure contains aPGconnthat should be in theCONNECTION_OKstatus; guaranteed if one callsPQregisterEventProcright after obtaining a goodPGconn. When returning a failure code, all cleanup must be performed as noPGEVT_CONNDESTROYevent will be sent.PGEVT_CONNRESETThe connection reset event is fired on completion of
PQresetorPQresetPoll. In both cases, the event is only fired if the reset was successful. If the event procedure fails, the entire connection reset will fail; thePGconnis put intoCONNECTION_BADstatus andPQresetPollwill returnPGRES_POLLING_FAILED.typedef struct { PGconn *conn; } PGEventConnReset;When a
PGEVT_CONNRESETevent is received, theevtInfopointer should be cast to aPGEventConnReset *. Although the containedPGconnwas just reset, all event data remains unchanged. This event should be used to reset/reload/requery any associatedinstanceData. Note that even if the event procedure fails to processPGEVT_CONNRESET, it will still receive aPGEVT_CONNDESTROYevent when the connection is closed.PGEVT_CONNDESTROYThe connection destroy event is fired in response to
PQfinish. It is the event procedure's responsibility to properly clean up its event data as libpq has no ability to manage this memory. Failure to clean up will lead to memory leaks.typedef struct { PGconn *conn; } PGEventConnDestroy;When a
PGEVT_CONNDESTROYevent is received, theevtInfopointer should be cast to aPGEventConnDestroy *. This event is fired prior toPQfinishperforming any other cleanup. The return value of the event procedure is ignored since there is no way of indicating a failure fromPQfinish. Also, an event procedure failure should not abort the process of cleaning up unwanted memory.PGEVT_RESULTCREATEThe result creation event is fired in response to any query execution function that generates a result, including
PQgetResult. This event will only be fired after the result has been created successfully.typedef struct { PGconn *conn; PGresult *result; } PGEventResultCreate;When a
PGEVT_RESULTCREATEevent is received, theevtInfopointer should be cast to aPGEventResultCreate *. Theconnis the connection used to generate the result. This is the ideal place to initialize anyinstanceDatathat needs to be associated with the result. If the event procedure fails, the result will be cleared and the failure will be propagated. The event procedure must not try toPQclearthe result object for itself. When returning a failure code, all cleanup must be performed as noPGEVT_RESULTDESTROYevent will be sent.PGEVT_RESULTCOPYThe result copy event is fired in response to
PQcopyResult. This event will only be fired after the copy is complete. Only event procedures that have successfully handled thePGEVT_RESULTCREATEorPGEVT_RESULTCOPYevent for the source result will receivePGEVT_RESULTCOPYevents.typedef struct { const PGresult *src; PGresult *dest; } PGEventResultCopy;When a
PGEVT_RESULTCOPYevent is received, theevtInfopointer should be cast to aPGEventResultCopy *. Thesrcresult is what was copied while thedestresult is the copy destination. This event can be used to provide a deep copy ofinstanceData, sincePQcopyResultcannot do that. If the event procedure fails, the entire copy operation will fail and thedestresult will be cleared. When returning a failure code, all cleanup must be performed as noPGEVT_RESULTDESTROYevent will be sent for the destination result.PGEVT_RESULTDESTROYThe result destroy event is fired in response to a
PQclear. It is the event procedure's responsibility to properly clean up its event data as libpq has no ability to manage this memory. Failure to clean up will lead to memory leaks.typedef struct { PGresult *result; } PGEventResultDestroy;When a
PGEVT_RESULTDESTROYevent is received, theevtInfopointer should be cast to aPGEventResultDestroy *. This event is fired prior toPQclearperforming any other cleanup. The return value of the event procedure is ignored since there is no way of indicating a failure fromPQclear. Also, an event procedure failure should not abort the process of cleaning up unwanted memory.
33.13.2. Event Callback Procedure
-
PGEventProc PGEventProcis a typedef for a pointer to an event procedure, that is, the user callback function that receives events from libpq. The signature of an event procedure must beint eventproc(PGEventId evtId, void *evtInfo, void *passThrough)
The
evtIdparameter indicates whichPGEVTevent occurred. TheevtInfopointer must be cast to the appropriate structure type to obtain further information about the event. ThepassThroughparameter is the pointer provided toPQregisterEventProcwhen the event procedure was registered. The function should return a non-zero value if it succeeds and zero if it fails.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.
33.13.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
PGconnyou 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
procargument will be called when a libpq event is fired. Its memory address is also used to lookupinstanceData. Thenameargument is used to refer to the event procedure in error messages. This value cannot beNULLor a zero-length string. The name string is copied into thePGconn, so what is passed need not be long-lived. ThepassThroughpointer is passed to theprocwhenever an event occurs. This argument can beNULL.-
PQsetInstanceData Sets the connection
conn'sinstanceDatafor procedureproctodata. This returns non-zero for success and zero for failure. (Failure is only possible ifprochas not been properly registered inconn.)int PQsetInstanceData(PGconn *conn, PGEventProc proc, void *data);
-
PQinstanceData Returns the connection
conn'sinstanceDataassociated with procedureproc, orNULLif there is none.void *PQinstanceData(const PGconn *conn, PGEventProc proc);
-
PQresultSetInstanceData Sets the result's
instanceDataforproctodata. This returns non-zero for success and zero for failure. (Failure is only possible ifprochas not been properly registered in the result.)int PQresultSetInstanceData(PGresult *res, PGEventProc proc, void *data);
Beware that any storage represented by
datawill not be accounted for byPQresultMemorySize, unless it is allocated usingPQresultAlloc. (Doing so is recommendable because it eliminates the need to free such storage explicitly when the result is destroyed.)-
PQresultInstanceData Returns the result's
instanceDataassociated withproc, orNULLif there is none.void *PQresultInstanceData(const PGresult *res, PGEventProc proc);
33.13.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)
{
fprintf(stderr, "Connection to database failed: %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 */
}