PQexec
函数对简单的同步应用里提交命令已经是足够用的了.
但是它却有几个主要的缺陷:
-
PQexec
等待命令结束.应用可能有其他工作要做
(例如维护用户界面),
这时它可不希望阻塞在这里等待返回.
-
因为控制是藏在
PQexec
内部,前端很难取消掉正进行着的命令.
(可以通过信号控制器进行,但没有别的方法.)
-
PQexec
只能返回一个
PGresult
结构.
如果提交的命令字符串包含多个
SQL
命令,除了最后一个
PGresult
以外都会被
PQexec
丢弃。
不想受到这些限制的应用可以改用下面的函数,这些函数也是构造
PQexec
的函数:
PQsendQuery
和
PQgetResult
。
使用这些(异步)功能以及
PQputline
和
PQputnbytes
的老一些的程序可能在等待数据发送给后端时阻塞住,
为解决这样的问题,增加了函数
PQsetnonblocking
.
旧应用可以忽略
PQsetnonblocking
的使用,
维持原有的阻塞特征。
新的程序可以利用
PQsetnonblocking
获得与后端完全非阻塞的联接。
-
PQsetnonblocking
把该联接的状态设置为非阻塞。
int PQsetnonblocking(PGconn *conn, int arg)
如果
arg
为 TRUE,把联接状态设置为非阻塞,
如果
arg
为 FALSE,
把联接状态设置为阻塞.如果 OK 返回 0,如果错误返回 -1.
此函数将确保对
在非阻塞状态,调用
PQputline
,
PQputnbytes
,
PQsendQuery
和
PQendcopy
的时候不被阻塞,
而是如果需要再次它们时将是返回一个错误(而不是阻塞)。
当把一个数据库的联接设置为非阻塞的模式并且调用了
PQexec
,它将暂时把联接状态设置为阻塞模式直到
PQexec
完成。
在不久的将来将有更多的
libpq
会设计成在
PQsetnonblocking
方式下是安全的。
-
PQisnonblocking
返回数据库联接的阻塞状态。
int PQisnonblocking(const PGconn *conn)
如果联接设置为非阻塞状态,返回 1,如果是阻塞状态返回 0。
-
PQsendQuery
向服务器提交一个命令而不等待结果.
如果查询成功发送则返回 1,否则返回
0.此时,可以用
PQerrorMessage
获取关于失败的信息).
int PQsendQuery(PGconn *conn,
const char *query);
在成功调用
PQsendQuery
后,调用
PQgetResult
一次或者多次获取结果.
可以不再调用
PQsendQuery
(在同一次联接里)
直到
PQgetResult
返回 NULL,表明命令完成.
-
PQgetResult
等待从前面
PQsendQuery
调用返回的下一个结果,
然后返回之.当查询结束并且没有更多结果后返回 NULL.
PGresult *PQgetResult(PGconn *conn);
必须重复的调用
PQgetResult
,直到它返回 NULL,
表明该命令结束.(如果在没有活跃的命令时调用,
PQgetResult
将只是立即返回 NULL.)
每个
PQgetResult
返回的非 NULL 结果都应该用前面
描述的 PGresult 访问函数进行分析.
不要忘了在结束分析后用
PQclear
释放每个结果对象.
注意,
PQgetResult
只是在有查询激活而且必须的返回数据还没有被
PQconsumeInput
读取时阻塞.
使用
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
,否则忙状态将永远不会消除.
-
PQflush
试图把任何正在排队的数据冲刷到后端,
如果成功(或者发送队列为空)返回 0,如果因某种原因失败返回
EOF
。
int PQflush(PGconn *conn);
在一个非阻塞的联接调用
select()
判断是否有响应到达之前需要调用一个
PQflush
。如果返回 0 则保证了
与后端的发送队列里面没有待发送的数据。只有使用了
PQsetnonblocking
的应用需要这个。
-
PQsocket
获取用于后端联接套接字的文件描述符号.
一个有效的描述符应该是
>= 0;一个 -1 表明当前没有打开与后端的联接.
int PQsocket(const PGconn *conn);
PQsocket
应该用于获取准备调用
select()
的后端套接字描述符.这就允许一个应用使用阻塞的联接等待后端的响应或者其他条件.
如果
select()
的结果表明可以从后端套接字读取数据,
那么应该调用
PQconsumeInput
读取数据;之后,
,
PQisBusy
,
PQgetResult
,
和/或
PQnotifies
可用于处理返回信息.
非阻塞的联接(那些使用了
PQsetnonblocking
的联接)
在
PQflush
返回 0 之前,
(这表明没有数据缓冲着等待发送给后端)
不应该使用
select()
。
一个使用这些函数的典型的前端将有一个主循环使用
select()
等待所有它必须处理的条件.其中一个条件将会是后端来的数据已准备好,
从
select()
的角度来看就是
PQsocket
标识的文件描述符上已经有可读取的数据.
当主循环侦测到输入准备好,它将调用
PQconsumeInput
读取输入.然后可以调用
PQisBusy
返回 false (0)后面可以跟着
PQgetResult
。同样它(用户应用)可以调用
PQnotifies
测 NOTIFY 信息(参阅下面的 "异步通知").
例子程序节里面给出了一个例子程序.
一个使用
PQsendQuery
/
PQgetResult
的前端同样也可以试图取消一个正在被后端处理的命令.
-
PQrequestCancel
要求
PostgreSQL
放弃处理当前命令.
int PQrequestCancel(PGconn *conn);
如果取消的请求成功发送,返回值是 1,否则是 0.
(如果为假,
PQerrorMessage
会告之为什么.)
不过,取消请求的成功发送将不保证请求将产生作用.不管
PQrequestCancel
的返回值是什么,应用都必须继续使用
PQgetResult
进行通常的后续的结果读取工作.
如果取消动作生效,当前的命令将提前退出并返回一个错误结果.
如果取消动作失败(也就是后端已经处理完命令了),那么将没有可见的结果.
注意:如果当前的命令是事务的一部分,取消动作将退出整个事务.
PQrequestCancel
可以很安全地从一个信号句柄里调用.
所以,如果取消动作可以从信号句柄里发出的话,它也可以与简单的
PQexec
一起使用.例如,
psql
从一个
SIGINT
信号句柄里调用
PQrequestCancel
,因此允许交互地取消通过
PQexec
发出的查询.
注意,如果没有与后端建立联接或者后端当前没有处理命令,
PQrequestCancel
将不发生做用.
|