libpqсистема событий предназначена для уведомления зарегистрированных обработчиков о значимых
libpq событиях, таких как создание или уничтожение PGconn и
PGresult объектов. Основной сценарий использования заключается в том, что это позволяет приложениям связывать собственные данные с объектом
PGconn или PGresult
и гарантировать, что эти данные будут освобождены в соответствующее время.
С каждым зарегистрированным обработчиком событий связаны два элемента данных, известных libpq только как непрозрачные void *
указатели. Существует передаваемый указатель, предоставляемый приложением при регистрации обработчика событий с помощью
PGconn. Передаваемый указатель не изменяется в течение всего времени существования объекта PGconn и всех PGresults
сгенерировано из него; поэтому в случае использования он должен указывать на долгоживущие данные.
Кроме того, имеется данные экземпляра указатель, который изначально значение NULL в каждом PGconn и PGresult.
Данным указателем можно управлять с помощью
PQinstanceData,
PQsetInstanceData,
PQresultInstanceData и
PQresultSetInstanceData функций. Обратите внимание, что в отличие от передаваемого указателя, данные экземпляра PGconn
не наследуются автоматически PGresultсоздается на его основе. libpq не имеет информации о том, на что ссылаются передаваемый указатель и указатель данных экземпляра (если они вообще ссылаются на что-либо), и никогда не предпринимает попыток освободить занимаемую ими память — это является обязанностью обработчика событий.
Перечисление PGEventId определяет типы событий, обрабатываемых системой событий. Все его значения имеют имена, начинающиеся с
PGEVT. Для каждого типа событий предусмотрена соответствующая структура данных о событии, содержащая передаваемые обработчикам событий параметры. Существуют следующие типы событий:
PGEVT_REGISTER #
Событие регистрации (register event) происходит в тот момент, когда PQregisterEventProc
вызывается функция. Данный момент является оптимальным для инициализации любых данных
instanceData необходимых процедуре события. Для каждого обработчика событий в рамках одного соединения генерируется только одно
событие регистрации. Если
процедура события завершается с ошибкой (возвращает ноль), регистрация отменяется.
typedef struct
{
PGconn *conn;
} PGEventRegister;
При получении PGEVT_REGISTER события
evtInfo следует привести к типу
PGEventRegister *. Данная структура содержит объект conn,
PGconn который должен находиться в состоянии
CONNECTION_OK status; это гарантируется при вызове функции
PQregisterEventProc сразу после получения корректного значения
PGconn. При возврате кода ошибки вся
очистка должна быть выполнена самостоятельно, так как никакое PGEVT_CONNDESTROY
событие отправлено не будет.
PGEVT_CONNRESET #
Событие сброса соединения инициируется по завершении функции
PQreset или PQresetPoll. В
обоих случаях событие инициируется только при успешном выполнении операции сброса.
Возвращаемое значение процедуры обработки событий игнорируется
в Digital Q.DataBase в версии v15 и более поздних.
Однако в более ранних версиях важно возвращать признак успеха
(ненулевое значение), иначе соединение будет прервано.
typedef struct
{
PGconn *conn;
} PGEventConnReset;
При получении PGEVT_CONNRESET события
evtInfo следует привести к типу
PGEventConnReset *. Хотя содержащийся
PGconn объект conn был только что сброшен, все данные события остаются
неизменными. Данное событие следует использовать для сброса, перезагрузки или повторного запроса любых
связанного с ним объекта 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
объект conn представляет собой соединение, используемое для формирования
результата. Это наиболее подходящее место для инициализации любых данных,
instanceData которые должны быть связаны с типом PGresult.
результата. Если процедура обработки событий завершается со сбоем (возвращает ноль), то эта
процедура будет игнорироваться в течение всего оставшегося времени существования результата;
то есть она не будет получать PGEVT_RESULTCOPY
или PGEVT_RESULTDESTROY события для данного результата или
результаты, скопированные из него.
PGEVT_RESULTCOPY #
Событие копирования результата инициируется в ответ на
PQcopyResult. Данное событие будет инициировано только после того, как
завершение операции копирования. Только те процедуры событий, которые
успешно обработали PGEVT_RESULTCREATE
или PGEVT_RESULTCOPY событие для исходного результата,
будут получать PGEVT_RESULTCOPY события.
typedef struct
{
const PGresult *src;
PGresult *dest;
} PGEventResultCopy;
При получении PGEVT_RESULTCOPY события
evtInfo следует привести к типу
PGEventResultCopy *. Поле conn
src результат представляет собой объект, который был скопирован, в то время как
dest результат является целевым объектом копирования. Данное событие
может использоваться для выполнения глубокого копирования, instanceData,
поскольку PQcopyResult не может этого сделать. Если процедура
события завершается с ошибкой (возвращает ноль), то эта процедура события будет
игнорируется в течение оставшегося времени существования нового результата; то есть он
не будет получать PGEVT_RESULTCOPY
или PGEVT_RESULTDESTROY события для данного результата или
результаты, скопированные из него.
PGEVT_RESULTDESTROY #
Событие удаления результата инициируется в ответ на вызов функции
PQclear. Процедура обработки событий
обязана корректно очищать свои данные событий, так как библиотека libpq не имеет
возможности управлять данной памятью. Отсутствие очистки приведёт
к утечкам памяти.
typedef struct
{
PGresult *result;
} PGEventResultDestroy;
При получении PGEVT_RESULTDESTROY события
evtInfo следует привести к типу
PGEventResultDestroy *. Данное событие генерируется
перед PQclear выполнением всех остальных операций по очистке.
Возвращаемое значение процедуры обработки событий игнорируется, так как не существует
способа указать на ошибку из функции PQclear. Кроме того,
сбой процедуры обработки событий не должен прерывать процесс очистки
неиспользуемой памяти.
PGEventProc #
PGEventProc представляет собой определение типа для указателя на
процедуру обработки событий, то есть пользовательскую функцию обратного вызова, которая принимает
события от библиотеки libpq. Сигнатура процедуры обработки событий должна быть следующей:
int eventproc(PGEventId evtId, void *evtInfo, void *passThrough)
Параметр evtId параметр указывает, какое именно
PGEVT событие произошло. Указатель
evtInfo evtInfo необходимо привести к соответствующему
типу структуры для получения дополнительной информации о событии.
Параметр passThrough данный параметр представляет собой указатель
передаваемый в PQregisterEventProc в тот момент, когда
была зарегистрирована процедура. Функция должна возвращать ненулевое значение
в случае успешного завершения и ноль — в случае ошибки.
Конкретная процедура обработки событий может быть зарегистрирована в любом объекте только один раз.
PGconn. Это объясняется тем, что адрес процедуры
используется в качестве ключа поиска для идентификации связанных с ней данных экземпляра.
В операционной системе Windows функции могут иметь два различных адреса: один, видимый
извне библиотеки DLL, и другой, видимый изнутри неё. Следует
следить за тем, чтобы с функциями процедур событий использовался только один из этих адресов,
libpqиначе возникнет неопределенность.
результата. Простейшее правило для написания корректно работающего кода состоит в том, чтобы
объявлять процедуры обработки событий как static. Если
адрес процедуры должен быть доступен за пределами её собственного исходного файла,
необходимо предоставить отдельную функцию для возврата адреса.
PQregisterEventProc #Функция регистрирует процедуру обратного вызова для событий в библиотеке libpq.
int PQregisterEventProc(PGconn *conn, PGEventProc proc,
const char *name, void *passThrough);
Процедуру обработки событий необходимо регистрировать один раз для каждого
PGconn объекта PGconn, от которого требуется получать события. Не существует
каких-либо ограничений, кроме объёма памяти, на количество процедур обработки событий, которые
могут быть зарегистрированы для одного соединения. Данная функция возвращает ненулевое
значение в случае успешного выполнения и ноль — в случае ошибки.
Параметр proc будет вызываться при возникновении
события в библиотеке libpq. Её адрес в памяти также используется для поиска
instanceData. The name
аргумент используется для ссылки на процедуру обработки событий в сообщениях об ошибках.
Данное значение не может иметь значение значение NULL или быть строкой нулевой длины. Строка имени
копируется в PGconn, поэтому передаваемый параметр не обязательно должен быть
долгоживущим. passThrough указатель передается
в proc при каждом возникновении события. Данный
аргумент может быть значение NULL.
PQsetInstanceData #
Устанавливает для объекта conn объект conn's instanceData
для процедуры proc в значение данные. Это
возвращает ненулевое значение при успешном завершении и ноль при возникновении ошибки. (Ошибка
возможна только в том случае, если proc не была должным образом
зарегистрирована в объект conn.)
int PQsetInstanceData(PGconn *conn, PGEventProc proc, void *data);
PQinstanceData #
Возвращает
соединение объект conn's instanceData
связанные с процедурой proc,
или значение NULL в случае их отсутствия.
void *PQinstanceData(const PGconn *conn, PGEventProc proc);
PQresultSetInstanceData #
Устанавливает для результата instanceData
для proc в значение данные. Данная функция возвращает
ненулевое значение при успешном выполнении и ноль при сбое. (Сбой возможен
только в том случае, если proc не была надлежащим образом зарегистрирована
в объекте PGresult.)
int PQresultSetInstanceData(PGresult *res, PGEventProc proc, void *data);
Следует учитывать, что любой объем памяти, представленный параметром данные
не будет учитываться функцией PQresultMemorySize,
если только она не была выделена с помощью PQresultAlloc.
(Данный подход рекомендуется, так как он исключает необходимость явного освобождения
такой области памяти при уничтожении объекта PGresult.)
PQresultInstanceData #
Возвращает данные объекта PGresult, instanceData связанные с proc, или значение NULL
если таковые отсутствуют.
void *PQresultInstanceData(const PGresult *res, PGEventProc proc);
Ниже приведен упрощенный пример управления частными данными, связанными с соединениями и результатами libpq.
/* 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 включает завершающий символ новой строки */ fprintf(stderr, "%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 */ data = PQinstanceData(conn, myEventProc); /* Отправляет идентификатор PGEVT_RESULTCREATE в процедуру myEventProc */ res = PQexec(conn, "SELECT 1 + 1"); /* доступны данные экземпляра результата */ data = PQresultInstanceData(res, myEventProc); /* Если используется флаг PG_COPYRES_EVENTS, в процедуру myEventProc отправляется идентификатор PGEVT_RESULTCOPY */ res_copy = PQcopyResult(res, PG_COPYRES_TUPLES | PG_COPYRES_EVENTS); /* данные экземпляра результата доступны, если флаг PG_COPYRES_EVENTS * использовался при вызове функции PQcopyResult. */ 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); /* освобождение данных экземпляра, так как объект conn уничтожается */ 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) */ PQresultSetInstanceData(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); /* связывание специфичных для приложения данных с результатом (копирование их из другого результата) */ PQresultSetInstanceData(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; } /* неизвестный идентификатор события, просто возвращается значение true. */ default: break; } return true; /* обработка события завершена успешно */ }