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, может помешать ходу операции, выполняемой через данное подключение.