32.7. Отмена запросов в процессе выполнения #
32.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.Успешная отправка отмены ещё не является гарантией того, что запрос будет иметь какой-то эффект. Если отмена сработала, текущая команда завершится досрочно и возвратит в качестве результата ошибку. Если же отмена не получится (например, потому, что сервер уже завершил обработку команды), тогда видимого результата не будет вовсе.
PQcancelStartPQcancelPoll#Выдаёт запрос на то, чтобы сервер прерывал выполнение текущей команды неблокирующим способом.
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.
32.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, может помешать ходу операции, выполняемой через данное подключение.