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
, что может нарушить текущую операцию на соединении.