31.7. Отмена выполняющихся запросов#

31.7. Отмена выполняющихся запросов

31.7. Отмена выполняющихся запросов #

31.7.1. Функции для отправки запросов на отмену #

PQcancelCreate #

Подготавливает соединение, по которому может быть отправлен запрос на отмену.

PGcancelConn *PQcancelCreate(PGconn *conn);

PQcancelCreate создает PGcancelConn объект, но он не начнет мгновенно отправлять запрос на отмену по этому соединению. Запрос на отмену может быть отправлен по этому соединению блокирующим способом с использованием PQcancelBlocking и неблокирующим способом с использованием PQcancelStart. Возвращаемое значение может быть передано в PQcancelStatus для проверки, был ли объект PGcancelConn успешно создан. Объект PGcancelConn является непрозрачной структурой, к которой приложение не должно обращаться напрямую. Этот объект PGcancelConn может быть использован для отмены запроса, выполняющегося на исходном соединении, в потокобезопасном режиме.

Многие параметры подключения оригинального клиента будут повторно использованы при установке соединения для запроса отмены. Важно, что если оригинальное соединение требует шифрования соединения и/или проверки целевого хоста (с использованием sslmode или gssencmode), то соединение для запроса отмены устанавливается с этими же требованиями. Любые параметры подключения, которые используются только во время аутентификации или после аутентификации клиента, игнорируются, так как запросы на отмену не требуют аутентификации, и соединение закрывается сразу после отправки запроса на отмену.

Обратите внимание, что когда PQcancelCreate возвращает ненулевой указатель, вы должны вызвать 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 = "Connecting...";
            break;

        case CONNECTION_MADE:
            feedback = "Connected to server...";
            break;
.
.
.
        default:
            feedback = "Connecting...";
}

Параметр подключения 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.

31.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 должен быть массивом char размером errbufsize (рекомендуемый размер - 256 байт).

PQrequestCancel #

PQrequestCancel является устаревшим и небезопасным вариантом PQcancelBlocking.

int PQrequestCancel(PGconn *conn);

PQrequestCancel существует только по причинам обратной совместимости. Вместо этого следует использовать PQcancelBlocking. Нет никаких преимуществ в использовании PQrequestCancel по сравнению с PQcancelBlocking.

Запрос серверу о прекращении обработки текущей команды. Он работает непосредственно с объектом PGconn и в случае ошибки сохраняет сообщение об ошибке в объекте PGconn (откуда его можно получить с помощью PQerrorMessage ). Хотя функциональность одинакова, такой подход не безопасен в многопоточных программах или обработчиках сигналов, так как возможно перезапись сообщения об ошибке PGconn, что может нарушить текущую операцию на соединении.