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