异步命令处理
PQexec函数对于在普通的同步应用中提交命令是足以胜任的。不过,它的一些缺点可能对某些用户很重要:
1、PQexec会等待命令完成。该应用可能有其他的工作要做(例如维护用户界面),这时它将不希望阻塞等待回应。
2、因为客户端应用的执行在它等待结果时会被挂起,对于应用来说很难决定要不要尝试取消正在进行的命令(这可以在一个信号处理器中完成,但别无他法)。
3、PQexec只能返回一个PGresult结构。如果提交的命令串包含多个SQL命令, 除了最后一个PGresult之外都会被PQexec丢弃。
4、PQexec总是收集命令的整个结果,把它缓存在一个单一的PGresult中。虽然这简化了应用的错误处理逻辑,它对于包含很多行的结果并不现实。 不想受到这些限制的应用可以改用构建PQexec的底层函数:PQsendQuery以及PQgetResult。还有 PQsendQueryParams、 PQsendPrepare、 PQsendQueryPrepared、 PQsendDescribePrepared以及 PQsendDescribePortal, 它们可以与PQgetResult一起使用来分别复制PQexecParams、 PQprepare、 PQexecPrepared、 PQdescribePrepared和 PQdescribePortal的功能。
使用预备语句
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 协议的连接上工作。
提交命令
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 协议的连接上工作,并且它只允许在查询字符串中有一条命令。
获取结果
使用PQgetResult函数等待来自于一个之前的 PQsendQuery、 PQsendQueryParams、 PQsendPrepare、 PQsendQueryPrepared、 PQsendDescribePrepared或 PQsendDescribePortal调用的结果,并且返回它。当该命令完成并且没有更多结果时,将返回一个空指针。
PGresult *PQgetResult(PGconn *conn);
PQgetResult必须被反复调用直到它返回一个空指针,空指针表示该命令完成(如果在没有命令活动时被调用,PQgetResult将立即返回一个空指针)。
示例
异步命令处理示例如下:
#include <stdio.h>
#include <stdlib.h>
#include "libpq-fe.h"
static void
exit_nicely(PGconn *conn)
{
PQfinish(conn);
exit(1);
}
int
main(int argc, char **argv)
{
const char *conninfo;
PGconn *conn;
PGresult *res;
int nFields;
int i, j;
const char *stmt;
const char *sql;
/*
* 如果用户在命令行上提供了一个参数,将它用作连接信息串。
*/
if (argc > 1)
conninfo = argv[1];
else
conninfo = "user = vbadmin password = 1234Abcde dbname = vastbase";
/* 建立到数据库的一个连接 */
conn = PQconnectdb(conninfo);
/* 检查看后端连接是否成功建立 */
if (PQstatus(conn) != CONNECTION_OK)
{
fprintf(stderr, "Connection to database failed: %s",
PQerrorMessage(conn));
exit_nicely(conn);
}
/* 设置总是安全的搜索路径,这样恶意用户就无法取得控制。 */
if (!PQsendQuery(conn, "SELECT pg_catalog.set_config('search_path', '', false)"))
{
fprintf(stderr, "SET failed: %s", PQerrorMessage(conn));
exit_nicely(conn);
}
/* 获取PQsendPrepare调用结果,PQgetResult必须被反复调用直到它返回一个空指针,空指针表示该命令完成 */
res = PQgetResult(conn);
while(res != NULL)
{ PQclear(res);
res = PQgetResult(conn);
}
/*
* 任何时候不再需要 PGresult 时,应该 PQclear 它来避免内存泄露
*/
PQclear(res);
/*
* 我们的测试案例这里涉及使用一个游标,对它我们必须用在一个事务块内。
* 我们可以在一个单一的 "select * from pg_database" 的 PQexec() 中做整个事情,
* 但是作为一个好的例子它太琐碎。
*/
/* 开始一个事务块 */
res = PQexec(conn, "BEGIN");
if (PQresultStatus(res) != PGRES_COMMAND_OK)
{
fprintf(stderr, "BEGIN command failed: %s", PQerrorMessage(conn));
PQclear(res);
exit_nicely(conn);
}
PQclear(res);
/*
* 从 pg_database 取得行,它是数据库的系统目录
*/
stmt = "getdb";
sql = "select * from pg_database";
/* 创建一个预备语句 */
if (!PQsendPrepare(conn, stmt, sql, 0, NULL))
{
fprintf(stderr, "Send Prepare statement select * from pg_database failed: %s", PQerrorMessage(conn));
exit_nicely(conn);
}
/* 获取PQsendPrepare调用结果,PQgetResult必须被反复调用直到它返回一个空指针,空指针表示该命令完成 */
res = PQgetResult(conn);
while(res != NULL)
{ PQclear(res);
res = PQgetResult(conn);
}
/* 执行预备语句 */
if (!PQsendQueryPrepared(conn, stmt, 0, NULL, NULL,NULL, 0))
{
fprintf(stderr, "Send select * from pg_database failed: %s", PQerrorMessage(conn));
exit_nicely(conn);
}
/* 获取执行结果 */
res = PQgetResult(conn);
if (PQresultStatus(res) != PGRES_TUPLES_OK)
{
fprintf(stderr, "Get results failed: %s", PQerrorMessage(conn));
PQclear(res);
exit_nicely(conn);
}
/* 首先,打印出属性名 */
nFields = PQnfields(res);
for (i = 0; i < nFields; i++)
printf("%-15s", PQfname(res, i));
printf("\n\n");
/* 接下来,打印出行 */
for (i = 0; i < PQntuples(res); i++)
{
for (j = 0; j < nFields; j++)
printf("%-15s", PQgetvalue(res, i, j));
printf("\n");
}
PQclear(res);
/* 关闭入口,我们不需要考虑检查错误 */
res = PQexec(conn, "CLOSE myportal");
PQclear(res);
/* 结束事务 */
res = PQexec(conn, "END");
PQclear(res);
/* 关闭到数据库的连接并且清理 */
PQfinish(conn);
return 0;
}