PostgreSQL 中文参考文档
🌐 英文原版 官方 ↗

32.7. 取消正在执行的查询 #

32.7.1. 发送取消请求的函数
32.7.2. 已过时的取消请求函数

32.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 #

Negotiating SSL encryption.

CONNECTION_GSS_STARTUP #

Negotiating GSS encryption.

注意,虽然这些常量会保留 (为了保持兼容性), 但应用程序永远不应当依赖它们以某个特定顺序出现, 甚至不应当依赖它们出现, 也不应当依赖状态始终是这些已记录的值之一。 应用程序可能会这样做:

switch(PQcancelStatus(conn))
{
        case CONNECTION_STARTED:
            feedback = "Connecting...";
            break;

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

在使用 PQcancelPoll 时, connect_timeout 连接参数会被忽略; 判断是否已经过去了过多的时间是应用程序的责任。 否则, PQcancelStart 加上一个 PQcancelPoll 循环等同于 PQcancelBlocking。

PQcancelStatus #

返回取消连接的状态。

ConnStatusType PQcancelStatus(const PGcancelConn *cancelConn);

状态可以是许多值之一。 然而, 在异步取消过程之外只会看到其中的三个: CONNECTION_ALLOCATED、 CONNECTION_OK 和 CONNECTION_BAD。 使用 PQcancelCreate 成功创建的一个 PGcancelConn 的初始状态是 CONNECTION_ALLOCATED。 被成功分派的取消请求具有状态 CONNECTION_OK。 一次失败的取消尝试由状态 CONNECTION_BAD 指示。 OK 状态会保持到 PQcancelFinish 或 PQcancelReset 被调用为止。

有关可能返回的其他状态码, 请参见 PQcancelStart 的条目。

然而, 取消被成功分派并不保证该请求会产生任何效果。 如果取消是有效的, 被取消的命令将提前终止并返回一个错误结果。 如果取消失败 (例如因为服务器已经完成了对该命令的处理), 则完全不会有任何可见的结果。

PQcancelSocket #

获得到服务器的取消连接套接字的文件描述符编号。

int PQcancelSocket(const PGcancelConn *cancelConn);

一个有效的描述符将大于等于 0; 结果为 -1 表示当前没有打开的服务器连接。 当对本节中 PGcancelConn 的任何函数进行调用时 ( PQcancelErrorMessage 和 PQcancelSocket 本身除外), 这一点都可能改变。

PQcancelErrorMessage #

返回当前连接对象上发生的最近一次错误消息字符串。 an operation on the cancel connection.

char *PQcancelErrorMessage(const PGcancelConn *cancelconn);

几乎所有接受 PGcancelConn 的 libpq 函数在失败时都会为 PQcancelErrorMessage 设置一条消息。 注意,按照 libpq 的惯例, 一个非空的 PQcancelErrorMessage 结果可能由多行组成, 并且会包括一个结尾的换行符。 调用者不应当直接释放该结果。 当相关的 PGcancelConn 句柄被传递给 PQcancelFinish 时它会被释放。 不应当期望该结果字符串在 PGcancelConn 结构上的多次操作之间保持不变。

PQcancelFinish #

关闭取消连接 (如果它尚未完成取消请求的发送)。 同时释放 PGcancelConn 对象使用的内存。

void PQcancelFinish(PGcancelConn *cancelConn);

注意,即使取消尝试失败 (如 PQcancelStatus 所指示的那样), 应用程序也应当调用 PQcancelFinish 来释放 PGcancelConn 对象使用的内存。 在 PQcancelFinish 被调用之后, 不得再使用该 PGcancelConn 指针。

PQcancelReset #

重置这个 PGcancelConn, 使它可以被复用于一条新的取消连接。

void PQcancelReset(PGcancelConn *cancelConn);

如果这个 PGcancelConn 当前正被用于发送一个取消请求, 那么这条连接会被关闭。 随后它会准备好这个 PGcancelConn 对象, 使它可以被用于发送一个新的取消请求。

这可以用来为一个 PGconn 创建一个 PGcancelConn, 并在原始 PGconn 的整个生命周期内多次复用它。

32.7.2. 已过时的取消请求函数 #

这些函数代表发送取消请求的较老方法。 虽然它们仍然可以工作, 但它们已被弃用, 因为它们不以加密的方式发送取消请求, 即使原始连接指定了 sslmode 或 gssencmode 来要求加密也是如此。 因此, 强烈不鼓励在新代码中使用这些较老的方法, 并且建议把现有代码改为使用新函数。

PQgetCancel #

创建一个数据结构, 其中包含用 PQcancel 取消一条命令所需的信息。

PGcancel *PQgetCancel(PGconn *conn);

PQgetCancel 在给定一个 PGconn 连接对象的情况下创建一个 PGcancel 对象。 如果给定的 conn 为 NULL 或一条无效连接, 它将返回 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 是信号处理器中的一个局部变量)。 然而, 人们通常不认为这一点足以抵消这个函数存在的安全问题。

就 PQcancel 而言, PGcancel 对象是只读的, 因此它也可以从一个与操纵 PGconn 对象的线程相分离的线程中被调用。

如果取消请求被成功分派, PQcancel 的返回值为 1, 否则为 0。 如果不成功, errbuf 会被填入一条解释性的错误消息。 errbuf 必须是一个大小为 errbufsize 的 char 数组 (推荐的大小为 256 字节)。

PQrequestCancel #

PQrequestCancel 是 PQcancelBlocking 的一个已弃用且不安全的变体。

int PQrequestCancel(PGconn *conn);

PQrequestCancel 之所以存在只是出于向后兼容的原因。 应当改用 PQcancelBlocking。 与 PQcancelBlocking 相比, 使用 PQrequestCancel 没有任何好处。

请求服务器放弃对当前命令的处理。 它直接在 PGconn 对象上操作, 并且在失败的情况下把错误消息存储在 PGconn 对象中 (可以通过 PQerrorMessage 检索它)。 虽然功能相同, 但这种方法在多线程程序或信号处理器中是不安全的, 因为覆盖 PGconn 的错误消息有可能破坏连接上当前正在进行的操作。