pgsql.cc 提供对 postgresql.org 官网内容的中文翻译,由 Pigsty 团队维护。
PQexec函数足以满足普通同步应用程序提交命令的需要。不过,它有两个缺点,对某些用户可能很重要:
PQexec会等待命令完成。应用可能有其他工作要做(例如维护一个用户界面),这种情况下它不会想要阻塞等待响应。
因为客户端应用的执行在它等待结果时会被挂起,对于应用来说很难决定要不要尝试取消正在进行的命令(这可以在一个信号处理器中完成,但别无他法)。
PQexec只能返回一个PGresult结构体。 如果提交的命令串包含多个SQL命令, 除了最后一个PGresult之外都会被PQexec丢弃。
不喜欢这些限制的应用可以改用PQexec构建所基于的底层函数:PQsendQuery和PQgetResult。还有PQsendQueryParams、PQsendPrepare、PQsendQueryPrepared、PQsendDescribePrepared和PQsendDescribePortal,它们可以与PQgetResult一起使用来复制PQexecParams、PQprepare、PQexecPrepared、PQdescribePrepared和PQdescribePortal的功能 respectively.
PQsendQuery #向服务器提交命令,不等待结果。命令发送成功时返回 1,否则返回 0(此时可使用 PQerrorMessage 获取更多失败信息)。
int PQsendQuery(PGconn *conn, const char *command);
成功调用 PQsendQuery 后,应调用 PQgetResult 一次或多次来获取结果。在 PQgetResult 返回空指针、表明命令已完成之前,不得在同一连接上再次调用 PQsendQuery。
PQsendQueryParams #向服务器提交命令及独立指定的参数,不等待结果。
int PQsendQueryParams(PGconn *conn,
const char *command,
int nParams,
const Oid *paramTypes,
const char * const *paramValues,
const int *paramLengths,
const int *paramFormats,
int resultFormat);
该函数等价于 PQsendQuery,但查询参数可以与查询字符串分开指定。函数参数的处理方式与 PQexecParams 相同。与 PQexecParams 一样,它不能用于协议 2.0 的连接,并且查询字符串中只允许包含一条命令。
PQsendPrepare #发送按给定参数创建预备语句的请求,不等待完成。
int PQsendPrepare(PGconn *conn,
const char *stmtName,
const char *query,
int nParams,
const Oid *paramTypes);
这是 PQprepare 的异步版本:请求发送成功时返回 1,否则返回 0。调用成功后,再调用 PQgetResult,确定服务器是否成功创建了预备语句。函数参数的处理方式与 PQprepare 相同。与 PQprepare 一样,它不能用于协议 2.0 的连接。
PQsendQueryPrepared #发送使用给定参数执行预备语句的请求,不等待结果。
int PQsendQueryPrepared(PGconn *conn,
const char *stmtName,
int nParams,
const char * const *paramValues,
const int *paramLengths,
const int *paramFormats,
int resultFormat);
该函数类似于 PQsendQueryParams,但通过已创建的预备语句的名称指定要执行的命令,而非提供查询字符串。函数参数的处理方式与 PQexecPrepared 相同。与 PQexecPrepared 一样,它不能用于协议 2.0 的连接。
PQsendDescribePrepared #提交获取指定预备语句信息的请求,不等待完成。
int PQsendDescribePrepared(PGconn *conn, const char *stmtName);
这是PQdescribePrepared的异步版本:请求发送成功时返回 1,否则返回 0。调用成功后,再调用PQgetResult获取结果。函数参数的处理方式与PQdescribePrepared相同。与PQdescribePrepared一样,它不能用于协议 2.0 的连接。
PQsendDescribePortal #提交获取指定 portal 信息的请求,不等待完成。
int PQsendDescribePortal(PGconn *conn, const char *portalName);
这是PQdescribePortal的异步版本:请求发送成功时返回 1,否则返回 0。调用成功后,再调用PQgetResult获取结果。函数参数的处理方式与PQdescribePortal相同。与PQdescribePortal一样,它不能用于协议 2.0 的连接。
PQgetResult #等待先前的PQsendQuery、PQsendQueryParams、PQsendPrepare或PQsendQueryPrepared调用产生的下一个结果,并返回该结果。当命令执行完毕且不再有其他结果时,返回空指针。
PGresult *PQgetResult(PGconn *conn);
必须反复调用 PQgetResult,直到它返回空指针,表明命令已经完成。(如果当前没有正在执行的命令,调用 PQgetResult 会立即返回空指针。)对于 PQgetResult 返回的非空指针,应使用前文介绍的 PGresult 访问函数处理相应结果。使用完毕后,不要忘记调用 PQclear 释放每个结果对象。注意,只有存在正在执行的命令,且所需响应数据尚未被 PQconsumeInput 读取时,PQgetResult 才会阻塞。
使用PQsendQuery和PQgetResult解决了PQexec的一个问题:如果命令字符串包含多个SQL命令,就能分别获取这些命令的结果。(这也支持一种简单的重叠处理方式:客户端可以处理某条命令的结果,同时服务器继续处理同一命令字符串中后面的查询。)然而,调用PQgetResult仍会导致客户端阻塞直到服务器完成下一条SQL命令。通过恰当使用另外两个函数可以避免这一点:
PQconsumeInput #如果服务器的输入可用,消费它。
int PQconsumeInput(PGconn *conn);
PQconsumeInput 通常返回 1,表示“没有错误”;发生问题时则返回 0(此时可查看 PQerrorMessage)。注意,返回值并不说明是否实际读取了输入数据。调用 PQconsumeInput 后,应用程序可以检查 PQisBusy 和/或 PQnotifies,以确定其状态是否发生变化。
即使应用程序尚未准备好处理结果或通知,也可以调用 PQconsumeInput。此函数会读取可用数据并将其保存在缓冲区中,从而清除 select() 的可读就绪指示。因此,应用程序可以用 PQconsumeInput 立即清除 select() 的就绪条件,随后在合适的时候检查结果。
PQisBusy #如果命令仍在忙碌,则返回 1,意味着PQgetResult会阻塞等待输入。返回 0 则表示可以调用PQgetResult,并保证不会阻塞。
int PQisBusy(PGconn *conn);
PQisBusy本身将不会尝试从服务器读取数据,因此必须先调用PQconsumeInput,否则繁忙状态将永远不会结束。
使用这些函数的典型应用程序会在主循环中通过 select() 或 poll() 等待需要响应的各种条件。其中一个条件是服务器有可读取的输入;对于 select(),这意味着 PQsocket 标识的文件描述符上有可读数据。主循环检测到输入就绪时,应调用 PQconsumeInput 读取输入,然后调用 PQisBusy。如果 PQisBusy 返回假(0),就可以接着调用 PQgetResult。还可以调用 PQnotifies 检测 NOTIFY 消息(见 第 31.7 节)。
一个使用PQsendQuery/PQgetResult的客户端也可以尝试取消一个正在被服务器处理的命令,见第 31.5 节。 但是,不管PQcancel的返回值是什么,应用都必须继续使用PQgetResult进行正常的结果读取序列。一次成功的取消只会导致命令比不取消时更快终止。
使用上述函数可以避免在等待数据库服务器输入时阻塞。不过,应用程序仍可能在等待向服务器发送输出时阻塞。这种情况较少见,但发送很长的 SQL 命令或数据值时可能发生。(如果应用程序通过 COPY IN 发送数据,发生的可能性则大得多。)为了防止这种情况,实现完全非阻塞的数据库操作,可以使用以下附加函数。
PQsetnonblocking #设置连接的非阻塞状态。
int PQsetnonblocking(PGconn *conn, int arg);
如果arg为1,则将连接状态设置为非阻塞,如果 arg为0,则设置为阻塞。如果成功返回0,出错返回-1。
在非阻塞状态下,对 PQsendQuery、PQputline、PQputnbytes 和 PQendcopy 的调用不会阻塞;如果需要再次调用,它们会返回错误。
请注意,PQexec不遵守非阻塞模式;如果调用它,它将以阻塞方式执行。
PQisnonblocking #返回数据库连接的阻塞状态。
int PQisnonblocking(const PGconn *conn);
如果连接设置为非阻塞模式,则返回1,如果为阻塞,则返回0。
PQflush #尝试将发送队列中的输出数据发送到服务器。成功(或发送队列为空)时返回 0;因某种原因失败时返回 -1;如果尚未能发送队列中的全部数据,则返回 1(这种情况只可能发生在非阻塞连接上)。
int PQflush(PGconn *conn);
在非阻塞连接上发送命令或数据后,应调用 PQflush。如果返回 1,就等待套接字变为可读或可写。套接字可写时,再次调用 PQflush;可读时,先调用 PQconsumeInput,再调用 PQflush。重复上述步骤,直到 PQflush 返回 0。(必须检查套接字是否可读,并用 PQconsumeInput 读完输入,因为服务器可能在尝试向客户端发送数据时阻塞,例如发送 NOTICE 消息;在客户端读取这些数据之前,服务器不会读取客户端发送的数据。)当 PQflush 返回 0 后,等待套接字变为可读,再按前述方法读取响应。
译文有误、术语不当或页面显示问题,请到译文仓库 pgsty/pgdoc 报告译文问题。 英文原文本身的问题,请在当前版本的对应页面向上游反馈;上游不再修订已结束维护的版本。