VastbaseG100

基于openGauss内核开发的企业级关系型数据库。

Menu

异步命令处理

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;
}