34.7. Отмена запросов в процессе выполнения #
34.7.1. Функции для отправки запросов отмены #
PQcancelCreate
#Готовит соединение для отправки запросов отмены.
PGcancelConn *PQcancelCreate(PGconn *conn);
Функция
PQcancelCreate
создаёт объектPGcancelConn
, однако отправка запросов отмены не начинается сразу. Запрос отмены можно отправить через это соединение блокирующим способом, используяPQcancelBlocking
, и неблокирующим, используяPQcancelStart
. Полученное значение можно передать черезPQcancelStatus
для проверки, что объектPGcancelConn
создан успешно. ОбъектPGcancelConn
является непрозрачной структурой, которая не предполагает доступ напрямую через приложение. Этот объектPGcancelConn
можно использовать для потокобезопасной отмены запроса, который выполняется на исходном соединении.При настройке соединения для отправки запроса отмены переиспользуются многие параметры соединения исходного клиента. Важно помнить, что если исходное соединение требует шифрования соединения и/или проверки целевого узла (через
sslmode
илиgssencmode
), то соединение для запроса отмены создаётся с такими же требованиями. Любые параметры соединения, используемые только во время или сразу же после аутентификации клиента, игнорируются, поскольку запросы отмены не требуют аутентификации, а соединение закрывается сразу после отправки запроса отмены.Заметьте, что если функция
PQconnectStart
возвращает ненулевой указатель, то, закончив его использование, вы должны вызватьPQcancelFinish
, чтобы освободить полученную структуру и все связанные с ней блоки памяти. Это нужно сделать, даже если попытка подключения не последует или окажется неуспешной.PQcancelBlocking
#Требует, чтобы сервер прекратил обработку текущей команды блокирующим способом.
int PQcancelBlocking(PGcancelConn *cancelConn);
Запрос отправляется по заданному соединению
PGcancelConn
, которое необходимо создать функциейPQcancelCreate
.PQcancelBlocking
возвращает 1, если запрос отмены успешно отправлен, и 0 в противном случае. В случае неудачи сообщение об ошибке можно получить с помощью функцииPQcancelErrorMessage
.Успешная отправка отмены ещё не является гарантией того, что запрос будет иметь какой-то эффект. Если отмена сработала, текущая команда завершится досрочно и возвратит в качестве результата ошибку. Если же отмена не получится (например, потому, что сервер уже завершил обработку команды), тогда видимого результата не будет вовсе.
PQcancelStart
PQcancelPoll
#Выдаёт запрос на то, чтобы сервер прерывал выполнение текущей команды неблокирующим способом.
int PQcancelStart(PGcancelConn *cancelConn); PostgresPollingStatusType PQcancelPoll(PGcancelConn *cancelConn);
Запрос отправляется по заданному соединению
PGcancelConn
, которое необходимо создать функциейPQcancelCreate
.PQcancelStart
возвращает 1, если запрос отмены может быть отправлен, и 0 в противном случае. В случае неудачи сообщение об ошибке можно получить с помощью функцииPQcancelErrorMessage
.Если вызов
PQcancelStart
оказался успешным, нужно опросить libpq для продолжения процедуры подключения. ВызовитеPQcancelSocket
для получения дескриптора нижележащего сокета, через который устанавливается соединение с базой данных. (Внимание: этот сокет может меняться от вызова к вызовуPQcancelPoll
.) Организуйте цикл таким образом: еслиPQcancelPoll(cancelConn)
при последнем вызове возвращаетPGRES_POLLING_READING
, ожидайте, пока сокет не окажется готовым для чтения (это покажет функцияselect()
,poll()
или подобная системная функция). Затем снова вызовитеPQcancelPoll(cancelConn)
. Если жеPQcancelPoll(cancelConn)
при последнем вызове возвратилаPGRES_POLLING_WRITING
, дождитесь готовности сокета к записи, а затем снова вызовитеPQcancelPoll(cancelConn)
. На первой итерации, то есть когда вы ещё не вызывалиPQcancelPoll(cancelConn)
, реализуйте то же поведение, что и после полученияPGRES_POLLING_WRITING
. Продолжайте этот цикл, покаPQcancelPoll(cancelConn)
не выдаст значениеPGRES_POLLING_FAILED
, сигнализирующее об ошибке при установлении соединения, илиPGRES_POLLING_OK
, показывающее, что соединение установлено успешно.Успешная отправка отмены ещё не является гарантией того, что запрос будет иметь какой-то эффект. Если отмена сработала, текущая команда завершится досрочно и возвратит в качестве результата ошибку. Если же отмена не получится (например, потому, что сервер уже завершил обработку команды), тогда видимого результата не будет вовсе.
В любое время в процессе подключения его состояние можно проверить, вызвав
PQcancelStatus
. Если этот вызов возвратитCONNECTION_BAD
, значит, процедура отмены завершилась сбоем; если вызов возвратитCONNECTION_OK
, значит, запрос отмены успешно отправлен. Оба эти состояния можно определить на основе возвращаемого значения функцииPQcancelPoll
, описанной выше. Другие состояния могут также иметь место в течение (и только в течение) асинхронной процедуры подключения. Они показывают текущую стадию процедуры подключения и могут быть полезны, например, для предоставления обратной связи пользователю. Вот эти состояния:CONNECTION_ALLOCATED
#Ожидание, когда вызов функции
PQcancelStart
илиPQcancelBlocking
фактически откроет сокет. Это этап соединения сразу после вызоваPQcancelCreate
илиPQcancelReset
, на котором соединение с сервером ещё не инициализировано. Чтобы фактически начать отправку запроса отмены, используйтеPQcancelStart
илиPQcancelBlocking
.CONNECTION_STARTED
#Ожидание, пока соединение будет установлено.
CONNECTION_MADE
#Соединение установлено; ожидание отправки.
CONNECTION_AWAITING_RESPONSE
#Ожидание ответа от сервера.
CONNECTION_SSL_STARTUP
#Согласование SSL-шифрования.
CONNECTION_GSS_STARTUP
#Согласование GSS-шифрования.
Заметьте, что, хотя эти константы и сохранятся (для поддержания совместимости), приложение никогда не должно полагаться на то, что они появятся в каком-то конкретном порядке или вообще появятся, а также на то, что состояние всегда примет одно из этих документированных значений. Приложение может сделать что-то наподобие:
switch(PQcancelStatus(conn)) { case CONNECTION_STARTED: feedback = "Подключение..."; break; case CONNECTION_MADE: feedback = "Подключён к серверу..."; break; . . . default: feedback = "Подключение..."; }
Параметр подключения
connect_timeout
игнорируется, когда используетсяPQcancelPoll
; именно приложение отвечает за принятие решения о том, является ли истекшее время чрезмерным. В противном случае вызовPQcancelStart
с последующим вызовомPQcancelPoll
в цикле будут эквивалентны вызовуPQcancelBlocking
.PQcancelStatus
#Возвращает состояние подключения для отмены.
ConnStatusType PQcancelStatus(const PGcancelConn *cancelConn);
Статус может принимать одно из ряда значений. Однако только три из них видны извне процедуры асинхронной отмены:
CONNECTION_ALLOCATED
,CONNECTION_OK
иCONNECTION_BAD
. Изначальное состояние подключенияPGcancelConn
, успешно созданного черезPQcancelCreate
, —CONNECTION_ALLOCATED
. Успешно отправленный запрос отмены имеет статусCONNECTION_OK
. О неудачной попытке отмены сигнализирует статусCONNECTION_BAD
. Обычно статус OK остаётся таковым до вызоваPQcancelFinish
илиPQcancelReset
.О других кодах состояния, которые могут выдать эти функции, можно узнать в описании
PQcancelStart
.Успешная отправка отмены ещё не является гарантией того, что запрос будет иметь какой-то эффект. Если отмена сработала, текущая команда завершится досрочно и возвратит в качестве результата ошибку. Если же отмена не получится (например, потому, что сервер уже завершил обработку команды), тогда видимого результата не будет вовсе.
PQcancelSocket
#Получает номер файлового дескриптора для сокета отмены соединения с сервером.
int PQcancelSocket(const PGcancelConn *cancelConn);
Действительный дескриптор будет больше или равен 0; значение -1 показывает, что в данный момент не открыто ни одного соединения с сервером. Значение может измениться в результате вызова любой функции в этом разделе в
PGcancelConn
(кромеPQcancelErrorMessage
и самойPQcancelSocket
).-
PQcancelErrorMessage
# Возвращает сообщение об ошибке, наиболее недавно сгенерированное операцией отмены соединения.
char *PQcancelErrorMessage(const PGcancelConn *cancelconn);
Почти все функции библиотеки libpq, принимающие
PGcancelConn
, в случае сбоя сформируют сообщение дляPQcancelErrorMessage
. Обратите внимание, что по соглашениям, принятым в libpq, непустой результат функцииPQcancelErrorMessage
может состоять из нескольких строк и будет включать завершающий символ новой строки. Вызывающая функция не должна освобождать память, на которую указывает возвращаемое значение, напрямую. Она будет освобождена, когда связанный с ней дескрипторPGcancelConn
будет передан функцииPQcancelFinish
. Не стоит ожидать, что результирующая строка останется той же самой при выполнении нескольких операций со структуройPGcancelConn
.PQcancelFinish
#Закрывает соединение для отмены (если отправка запроса отмены ещё не завершена). Также освобождает память, используемую объектом
PGcancelConn
.void PQcancelFinish(PGcancelConn *cancelConn);
Обратите внимание, что даже если попытка отмены потерпела неудачу (как показывает
PQcancelStatus
), приложение все равно должно вызватьPQcancelFinish
, чтобы освободить память, используемую объектомPGcancelConn
. УказательPGcancelConn
не должен использоваться повторно после того, как была вызвана функцияPQcancelFinish
.PQcancelReset
#Переустанавливает
PGcancelConn
в целях повторного использования для нового соединения для отмены.void PQcancelReset(PGcancelConn *cancelConn);
Если
PGcancelConn
используется для отправки запроса отмены, соединение закрывается. Позже будет подготовлен объектPGcancelConn
, чтобы через него можно было отправить новый запрос отмены.Это можно использовать для создания
PGcancelConn
дляPGconn
и многократно его переиспользовать на протяжении работы исходногоPGconn
.
34.7.2. Устаревшие функции для отправки запросов отмены #
Через данные функции раньше отправлялись запросы отмены. Хотя эти функции всё ещё работают, они признаны устаревшими, поскольку отправляют запросы отмены незашифрованными, даже если в исходном соединении указано требование шифрования sslmode
или gssencmode
. По этой причине крайне не рекомендуется использовать их в новом коде, а так же лучше изменить существующий код и вписать в него новые функции.
PQgetCancel
#Создаёт структуру данных, содержащую информацию, необходимую для отмены команды, запущенной через
PQcancel
.PGcancel *PQgetCancel(PGconn *conn);
Функция
PQgetCancel
создаёт объектPGcancel
, получив объектPGconn
, описывающий подключение. Она возвратитNULL
, если параметрconn
равенNULL
или представляет недействительное подключение. ОбъектPGcancel
является непрозрачной структурой, которая не предназначена для того, чтобы приложение обращалось к ней напрямую; её можно только передавать функцииPQcancel
илиPQfreeCancel
.PQfreeCancel
#Освобождает память, занимаемую структурой данных, созданной функцией
PQgetCancel
.void PQfreeCancel(PGcancel *cancel);
PQfreeCancel
освобождает память, занимаемую объектом, предварительно созданным функциейPQgetCancel
.PQcancel
#PQcancel
является устаревшим и небезопасным аналогом функцииPQcancelBlocking
, но её можно безопасно использовать в рамках обработчика сигналов.int PQcancel(PGcancel *cancel, char *errbuf, int errbufsize);
Функция
PQcancel
существует только в целях обратной совместимости. Вместо неё необходимо использоватьPQcancelBlocking
. Единственное преимуществоPQcancel
состоит в том, что её можно безопасно вызывать из обработчика сигнала, еслиerrbuf
является локальной переменной в таком обработчике. Однако это преимущество не исключает проблем с безопасностью, связанных с этой функцией, и не является весомой причиной для её использования.Объект
PGcancel
доступен только в режиме чтения, пока речь идёт о функцииPQcancel
, поэтому её можно также вызывать из потока, отдельного от того, который управляет объектомPGconn
.Возвращаемое функцией
PQcancel
значение равно 1, если запрос на отмену был успешно отправлен, и 0 в противном случае. В случае неудачной отправкиerrbuf
заполняется пояснительным сообщением об ошибке.errbuf
должен быть массивом символов, имеющим размерerrbufsize
(рекомендуемый размер составляет 256 байт).
PQrequestCancel
#PQrequestCancel
является устаревшим и небезопасным аналогом функцииPQcancelBlocking
.int PQrequestCancel(PGconn *conn);
Функция
PQrequestCancel
существует только в целях обратной совместимости. Вместо неё необходимо использоватьPQcancelBlocking
. Нет никаких преимуществ использованияPQrequestCancel
вместоPQcancelBlocking
.Выдаёт запрос на то, чтобы сервер прекратил обработку текущей команды. Функция работает напрямую с объектом
PGconn
и в случае сбоя сохраняет сообщение об ошибке в объектеPGconn
(откуда его можно извлечь с помощьюPQerrorMessage
). Хотя функциональность та же самая, этот подход небезопасен для многопоточных программ или обработчиков сигналов, поскольку перезапись сообщения об ошибке, хранящегося в объектеPGconn
, может помешать ходу операции, выполняемой через данное подключение.