PostgreSQL数据库学习手册之libpq - C 库--介绍

 

PostgreSQL数据库介绍

 

PostgreSQL是一种运行在Unix和Linux操作系统(在NT平台借助Cygnus也可以运行)平台上的免费的开放源码的关系数据库。最早是由美国加州大学伯克利分校开发的,开始只是作为一个演示系统发表,但是随着时间的推移,逐步分发,得到很多实际的应用,才逐步流行起来。现在版本发展到了7.3.3。

荣誉:

  • 1999年获得Linux World杂志的该年度"最佳数据库产品"称号。
  • 2000年荣获Linux Journal杂志编辑选择的"最佳数据库"奖。
  • 2003年,也就是最近再一次荣获Linux Journal杂志编辑选择的" 最佳数据库"奖。

2.主要功能和特性

  • 支持SQL。作为关系数据库,它支持SQL89标准也支持部分SQL92大部分功能。
  • 有丰富的数据类型。许多数据类型是一些商业数据库都没有提供的。
  • 面向对象。它包含了一些面向对象的技术,如继承和类。
  • 支持大数据库,它不同于一般的桌面数据库,能够支持几乎不受限制大小的数据库,而且性能稳定。
  • 方便集成web,提供一些接口方便 PHP,Perl等语言操作数据库。
  • 事务处理。相对一些其他免费数据库如MySQL,他提供了事务处理,可以满足一些商业领域的数据需要。

3.数据类型

PostgreSQL支持丰富的数据类型,许多数据类型在商业数据库中都无法提供。

  • 数值类型。包括整数、任意精度、浮点类型和序列类型数值。
  • 货币类型。范围-21474836.48 到 +21474836.47。
  • 字符类型。分为定长类型,不定长有限制类型和不定长不限制长度类型。
  • 二进制字符串。
  • 日期和时间类型
  • 布尔类型。
  • 几何类型。包括点、线、方形、路径、多边形和圆。
  • 网络地址类型。
  • 位串类型。就是包含1和0的串。
  • 对象标识符类型
  • 数组类型。

4.数据库极限

参数名称最大范围
数据库大小无限制
表大小16TB(所有系统)
一条记录大小1.6GB
字段大小1GB
表记录条数无限制
表字段数250-1600(取决于字段的数据类型)
表索引无限制

事实上不可能存在无限制的情况。支持16TB大小的表不是单一文件保存,而是分为多个文件存储的,所以不受限制于操作系统。

4.下载安装

我们可以到 http://www.postgresql.org网站下载,具体怎么安装我就不赘述,大家可以看文档,也可以看 参考中的中文网站的文档。在这里我就说说安装中需要注意的几个问题。

注意

  • 不能以root登陆Linux来操作数据库(启动数据库服务除外),请以root身份建立postgres用户,设置密码后登录。
  • 安装完postgreSQL,可能需要初始化数据库,需要以postgres用户登录系统,运行
    initdb -pqlib=/usr/lib/pqsql -pqdata=/var/lib/pqsal

    如果你的pqlib目录不同则需要按照你的实际路径修改,pqdata也是这样的。
  • 数据库用户与Linux用户是不同的概念,只有数据库用户才能操作数据库,以postgres登录Linux,然后是使用createuser来添加用户,dropuser来删除用户,也可以使用SQL命令CREATE USER来创建。

5.常用命令

PostgreSQL数据库常用操作命令:

Createdb创建数据库
Createuser创建数据库用户
Dropdb删除数据库
Dropuser删除数据库用户
Psql交互式PostgreSQL前端工具,可以用它来操作数据库
Initdb初始化postgreSQL数据库

这里不是介绍怎样使用PostgreSQL数据库,所以对这些命令也不详细解释,可以参见文档说明和帮助。我在这里是假设读者已经安装并初始化好了数据库,并且会使用psql和熟练SQL来交互操作数据。

回页首

PostgreSQL 的C/C++接口介绍

PostgreSQL提供很多不同语言的接口,有C、C++、Perl、Tcl等等,这里主要是介绍C/C++怎样连接到数据库,对数据进行添加,查询等操作。

1.C语言接口

  • 头文件libqp-fe.h 连接时需要添加连接参数-lpq也就是连接libpq库。
  • 建立数据库连接函数
    PGconn  *PQconnectdb( const char *conninfo) 
    PGconn *PQsetdbLogin(const char *pghost,
                         		 	   const char *pgport,
                         		       const char *pgoptions,
                         		       const char *pgtty,
                         		       const char *dbName,
                         		       const char *login,
                         		       const char *pwd)

    建立非阻塞连接函数

    PGconn *PQconnectStart(const char *conninfo)
    PostgresPollingStatusType *PQconnectPoll(PQconn *conn)
  • 执行查询函数 
    PGresult *PQexec(PGconn *conn,const char *query) 返回查询的结构集。
    int PQntuples(const Pgresult *res) 返回查询结果里的记录个数。
    int PQnfields(const Pgresult *res) 返回记录中的字段的个数。
    char *PQfname(const Pgresult *res,int field_index)返回结构集中某一字段的名称。
    Oid PQftype(const Pgresult *res, int field_index) 返回结构集中某一字段的数据类型。
    char *PQgetvalue(const Pgresult *res,int tup_num, int field_index)
    返回结构集中某一条记录中某一个字段的值。

    这里只列举最常用的一些函数,具体说明可以参见文档。

  • 大对象数据操作,主要使用下列函数在SQL语句中操作大对象数据。
    Oid lo_creat(PGconn *conn,

    int mode)

    创建一个新的大对象.mode 是一个位掩码,描述新对象的不同属性
    Oid lo_import(PGconn *conn,

    const char *filename)

    把文件作为大对象导入数据库
    int lo_export(PGconn *conn,

    Oid lobjId, const char *filename)

    大对象导入出数据库,保存为文件
    int lo_open(PGconn *conn,

    Oid lobjId, int mode)

    打开一个现存的大对象
    int lo_write(PGconn *conn, int fd,

    const char *buf, size_t len)

    大对象写入数据
    int lo_read(PGconn *conn, int fd,

    char *buf, size_t len)

    从大对象读取数据
    int lo_lseek(PGconn *conn, int fd,

    int offset, int whence)

    对大对象数据进行查找
    int lo_close(PGconn *conn, int fd)关闭大对象描述符

    函数在fe-lobj.c文件中定义,具体使用参见 Accessing Large Objects from libpq

    上面函数的具体介绍请参见 PostgreSQL v7.0文档 在这里就不详细介绍了。这里省略列举状态检查函数,参见参考文档。

    使用C语言访问数据库的除了libpq库,还有其他如 libpqeasy,ecpg 等接口。

2.C++语言接口

  • 头文件 libpq++.h 连接需要的库位 pq++,连接参数是 -lpq++
  • libpq++ 类继承关系图

  • 类简介 
    PgConnection成员函数
    PgConnection(const char* conninfo)参数同下
    Connect(const char* conninfo)同 PQconnectdb
    Exec(const char* query)同 PQexec
    ExecCommandOk(const char* query)运行命令并检查状态是否PGRES_COMMAND_OK
    ExecTuplesOk(const char* query)运行查询并检查状态是否PGRES_TUPLES_OK
    IntToString(int n)数字转换为字符串
    ConnectionBad()连接是否失败
    ErrorMessage()返回错误信息
    DBName()同 PQdb

    详细说明参见文档

    PgDatabase 的常用成员函数
    PgDatabase(const char* conninfo)构造函数连接到数据库,跟 Pqconnectdb参数相同
    Tuples()同 PQntuples();
    CmdTuples()同 PQcmdTuples
    Fields()同 PQnfields
    FieldName(int field_num)同 PQfname
    FieldNum(const char* field_name)同 PQfnumber
    FieldType(int ield_num)

    FieldType(const char* field_name)

    同 PQftype
    FieldSize(int field_num)

    FieldSize(const char* field_name)

    同 PQfsize
    GetValue(int tup_num, int field_num)

    GetValue(int tup_num, const char* field_name)

    同 PQgetvalue
    GetIsNull(int tup_num, int field_num)

    GetIsNull(int tup_num, const char* field_name)

    同 PQgetisnull
    GetLength(int tup_num, int field_num)

    GetLength(int tup_num, const char* field_name)

    同 PQgetlength
    GetLine(char* string, int length)同 PQgetline
    PutLine(const char* string)同 PQputline
    EndCopy()同 PQendcopy
  • 大对象操作使用 PgLargeObject 类来操作。

回页首

例子程序

以 postgres 用户或者具有建立数据库用户权限的用户登录 Linux,先建立数据库。

[zuojin@itpark85 zuojin]$createdb test_db

然后使用 psql 连接到数据库

[zuojin@itpark85 zuojin]$psql test_db

连接到数据库建立表,如下图所示

插入测试用数据,如图:

(注意:psql中输入SQL语句要以"; "结束才执行,帮助命令是"\\?",退出命令是"\\q")。

1)C语言例子

#include <stdio.h>
#include <libpq-fe.h>
 
int main() {
   PGconn *conn;
   PGresult *res;
   char *pghost = NULL;
   char *pgport =NULL;
   char *pgoptions =NULL;
   char *pgtty = NULL;
   char *dbname ="test_db";
   /**数据库名*/ 
   int i = 0,t = 0,s,k;
   conn = PQsetdb(pghost,pgport,pgoptions,pgtty,dbname);
   if (PQstatus(conn) == CONNECTION_BAD) {
       fprintf(stderr,"Connection to database '%s' failed!\\n",dbname);
       PQfinish(conn);
	   eturn 0;
   }
   res = PQexec(conn,"SELECT * FROM test");
   
   /**运行查询命令*/
   if(  PQresultStatus(res)  !=  PGRES_TUPLES_OK) {
       fprintf(stderr,"Exec Query Fauled!\\n");
       PQclear(res);
	   return 0;
   }
     
   i = PQntuples(res);
   /**取得查询的结果的记录的数量*/ 
   t = PQnfields(res);
   /**取得字段数量*/
   for(s=0; s<i;s++) {
       for (k = 0; k<t; k++) {
           printf("%s",PQgetvalue(res,s,k));
           printf("  ");
       }
       printf("\\n");
   }
   PQfinish(conn);
   PQclear(res);
   return 0;
}

运行结果如图所示

2)C++例子

#include <iostream.h>
#include <libpq++.h> 
int  main() {
	char query_string[256]= "SELECT * FROM test;";
	PgDatabase data("dbname = test_db");
	if (data.ConnectionBad()) {
		cout <<"connected failed" << endl;
  		cout <<"Error is "<<data.ErrorMessage() << endl;
        exit(1);
    }
    if (! data.ExecTuplesOk(query_string)) {
		cout<<"Query Failed!" << endl;
		exit(1);
    }
    for(int k=0; k<data.Fields(); k++) /**显示字段名称*/ {
        cout<<data.FieldName(k);
        cout <<"      "     ;
    }
    cout<<endl;
    for (int i = 0; i < data.Tuples(); i++) /**取得查询结果的记录数量*/  {
       	for(int k=0; k<data.Fields(); k++) {
        	cout << data.GetValue(i,k);
        	cout <<" | "     ;
        }
        cout<<endl;
    }
    return 0 ;
}

运行结果如图所示

 

Chapter 1. libpq - C 库

 

 

 

Table of Contents

 

1.1. 介绍

 

1.2. 数据库联接函数

 

1.3. 命令执行函数

 

 

1.3.1. 主过程

 

1.3.2. 为包含在 SQL 查询中逃逸字串

 

1.3.3. 逃逸包含在 SQL 查询中的二进制字串

 

1.3.4. 检索 SELECT 的结果信息

 

1.3.5. 检索 SELECT 结果数值

 

1.3.6. 检索非-SELECT 结果信息

 

 

1.4. 异步查询处理

 

1.5. 捷径接口

 

1.6. 异步通知

 

1.7. 与 COPY 命令相关的函数

 

1.8. libpq 跟踪函数

 

1.9. libpq 控制函数

 

1.10. 环境变量

 

1.11. 文件

 

1.12. 线程特性

 

1.13. 制作 Libpq 程序

 

1.14. 例子程序

 

 

1.1. 介绍

 

 

libpq 是 PostgreSQL的 C 应用程序员的接口. libpq 是一套允许客户程序向 PostgreSQL 后端服务进程发送查询 并且获得查询返回的库过程. libpq 同时也是其他几个 PostgreSQL 应用接口下面的引擎, 包括 libpq++ (C++), libpgtcl(Tcl),Perl,和 ecpg.所以如果你使用这些软件包, libpq某些方面的特性会对你非常重要.

 

 

本节末尾有三个小程序显示如何利用 libpq书写程序. 在下面目录里面有几个完整的 libpq 应用的例子:

 

 

src/test/examples

 

src/bin/psql

 

 

使用 libpq 的前端程序必须包括头文件 libpq-fe.h 并且必须与 libpq 库链接.

1.2. 数据库联接函数

 

 

下面的过程处理与 PostgreSQL 后端服务器联接的事情. 一个应用程序一次可以与多个后端建立联接. (这么做的原因之一是访问多于一个数据库.) 每个连接都是用一个从PQconnectdb()或 PQsetdbLogin() 获得的PGconn对象表示. 注意,这些函数总是返回一个非空的对象指针, 除非存储器少得连个PGconn对象都分配不出来. 在把查询发送给联接对象之前,可以调用PQstatus 函数来检查一下联接是否成功.

 

 

*

 

 

PQconnectdb 与后端数据库服务器建立一个新的联接.

 

 

PGconn *PQconnectdb(const char *conninfo)

 

 

 

这个过程用从一个字符串 conninfo 来的参数与数据库打开一个新的联接. 与下面的PQsetdbLogin()不同的是, 我们可以不必更换函数签名(名字)就可以扩展参数集, 所以我们建议应用程序中使用这个函数或者是它的非阻塞的相似函数 PQconnectStart 和 PQconnectPoll. 传入的参数可以为空,表明使用所有缺省的参数, 或者可以包含一个或更多个用空白间隔的参数设置.

 

 

每个参数以 关键字 = 数值的形式设置. (要写一个空值或者一个包含空白的值, 你可以用一对单引号包围它们,例如, keyword = 'a value' . 数值内部的单引号和反斜扛必须用一个反斜扛逃逸, 也就是说, '或.) 等号周围的空白是可选的.目前可识别的参数键字是:

 

 

host

 

 

要联接的主机(host ). 如果主机名以斜扛开头, 则它声明使用 Unix 域套接字通讯而不是 TCP/IP 通讯; 该值就是套接字文件所存储的目录. 缺省时是与位于 /tmp 里面的 Unix-域套接字联接.

 

hostaddr

 

 

与之联接的主机的 IP 地址。这个可以是标准的数字-点的形式, 象在 BSD 函数inet_aton等里面用的那样。 如果声明了一个非零长的字符串,那么使用 TCP/IP 通讯机制。

 

 

使用hostaddr取代 host 可以让应用避免一次主机名查找, 这一点对于那些有时间约束的应用来说可能是非常重要的。 不过,Kerberos 认证系统要求主机(host)名。因此,应用下面的规则。 如果声明了不带hostaddr的 host 那么就强制进行主机名查找。 如果声明中没有 host,hostaddr 的值给出远端的地址; 如果使用了 Kerberos, 将导致一次反向名字查询。如果同时声明了 host 和hostaddr, 除非使用了 Kerberos,否则将使用hostaddr的值作为远端地址; host 的值将被忽略,如果使用了 Kerberos,host 的值用于 Kerberos 认证。 要注意如果传递给libpq的主机名(host) 不是地址hostaddr处的机器名,那么认证很有可能失败。

 

 

如果主机名(host)和主机地址都没有, 那么libpq将使用一个本地的 Unix 域套接字进行通讯。

 

port

 

 

主机服务器的端口号,或者在 Unix 域套接字联接时的套接字扩展文件名.

 

dbname

 

 

数据库名.

 

user

 

 

要联接的用户名。

 

password

 

 

如果后端要求口令认证,所用的口令.

 

connect_timeout

 

 

给连接过程设置的时间范围,以秒计。零或者不设置表示无穷。

 

options

 

 

发给后端的跟踪/调试选项.

 

tty

 

 

文件或控制台(tty),用于从后端的可选调试输出.

 

requiressl

 

 

设为 '1' 要求与后端进行 SSL 联接.如果服务器不支持 SSL, 那么Libpq将马上拒绝联接. 设置为 '0' (缺省)与服务器进行协商.

 

 

如果有任何没有声明的参数,那么将检查对应的环境变量(参阅Section 1.10 小节)。 如果环境变量也没有设置,那么使用编译时的硬代码。 返回的值是一个指向代表与后端联接的抽象 struct指针。

 

*

 

 

PQsetdbLogin 与后端数据库服务器建立一个新的联接.

 

 

PGconn *PQsetdbLogin(const char *pghost,

 

const char *pgport,

 

const char *pgoptions,

 

const char *pgtty,

 

const char *dbName,

 

const char *login,

 

const char *pwd)

 

 

这个函数是 PQconnectdb 前身, 它有固定个数的参数,但是有相同的功能。

 

*

 

 

PQsetdb 与后端数据库服务器建立一个新的联接.

 

 

PGconn *PQsetdb(char *pghost,

 

char *pgport,

 

char *pgoptions,

 

char *pgtty,

 

char *dbName)

 

 

这是一个调用 PQsetdbLogin() 的宏,只是login和pwd参数用空(null )代替. 提供这个函数主要是为了与老版本的程序兼容.

 

*

 

 

PQconnectStart, PQconnectPoll 与数据库服务器建立一次非阻塞的联接。

 

 

PGconn *PQconnectStart(const char *conninfo)

 

 

PostgreSQLPollingStatusType PQconnectPoll(PGconn *conn)

 

 

这两个过程用于打开一个与数据库服务器之间的非阻塞的联接: 你的应用的执行线索在执行它的时候不会因远端的 I/O 而阻塞。

 

 

数据库联接是用从 conninfo字符串里取得的参数传递给 PQconnectStart进行的。 这个字符串的格式与上面PQconnectdb里描述的一样。

 

 

PQconnectStart 和PQconnectPoll 都不会阻塞(进程),不过有一些条件:

 

 

o

 

 

必须正确提供hostaddr和host 参数以确保不会发生正向或者反向的名字查找。 参阅上面PQconnectdb里的这些参数的文档获取细节。

 

o

 

 

如果你调用了PQtrace, 确保你跟踪进入的流对象不会阻塞。

 

o

 

 

你必须在调用PQconnectPoll之前确保 socket 处于正确的状态,象下面描述的那样。

 

 

要开始(联接),调用 conn=PQconnectStart("connection_info_string") . 如果conn是NULL, 表明libpq无法分配一个新的 PGconn 结构。 否则,返回一个有效的PGconn指针 (尽管还不一定代表一个与数据库有效联接)。 PQconnectStart 一返回,调用 status=PQstatus(conn)。 如果 status 等于CONNECTION_BAD, PQconnectStart 失败。

 

 

如果PQconnectStart成功了,下一个阶段是轮询 libpq, 这样它就可以继续进行后继的联接动作。 象这样循环:认为一个联接缺省时是"不活跃"的。 如果PQconnectPoll的最后一个返回是 PGRES_POLLING_ACTIVE,则认为它是"活跃的"。 如果PQconnectPoll(conn)的最后一个返回是 PGRES_POLLING_READING,执行一个对 PQsocket(conn)的读select()。 如果最后一个返回是PGRES_POLLING_WRITING, 执行一个对PQsocket(conn)的写select()。 如果还要调用PQconnectPoll, 也就是调用完PQconnectStart之后, 把它当作最后返回PGRES_POLLING_WRITING那样对待。 如果select()显示 socket 已经准备好, 那么认为它是"活跃的"。如果认为一个联接是 "活跃的", 再次调用PQconnectPoll(conn)。 如果这次调用返回PGRES_POLLING_FAILED, 联接过程失败,如果这次调用 返回PGRES_POLLING_OK,联接成功。

 

 

要注意上面用select()来确保一个 socket 准备好只是一个(近似)的例子; 还可以用其他方法,比如一个poll()调用,来代替 select。

 

 

在联接的任意时刻,我们都可以通过调用PQstatus 来检查联接的状态。 如果这是CONNECTION_BAD, 那么联接过程失败;如果是CONNECTION_OK, 那么联接已经做好。 这两种状态同样也可以从上面的PQconnectPoll 的返回值里检测到。 其他状态可能(也只能)在一次异步联接过程中看到。 这些标识联接过程 的当前状态,因而可能对给用户提供反馈有帮助。这些状态可能包括:

 

 

CONNECTION_STARTED

 

 

等待进行联接。

 

CONNECTION_MADE

 

 

联接成功;等待发送。

 

CONNECTION_AWAITING_RESPONSE

 

 

等待来自服务器的响应。

 

CONNECTION_AUTH_OK

 

 

已收到认证;等待联接启动继续进行。

 

CONNECTION_SETENV

 

 

协商环境(联接启动的一部分)。

 

 

注意,尽管这些常量将保持下去(为了维持兼容性), 应用决不应该依赖于这些常量的某种特定顺序, 或者是根本不应依赖于这些常量, 或者是不应该依赖于这些状态总是某个文档声明的值。 一个应用可能象象下面这样:

 

 

switch(PQstatus(conn))

 

{

 

case CONNECTION_STARTED:

 

feedback = "Connecting...";

 

break;

 

 

case CONNECTION_MADE:

 

feedback = "Connected to server...";

 

break;

 

.

 

.

 

.

 

default:

 

feedback = "Connecting...";

 

}

 

 

要注意如果PQconnectStart返回一个非空的指针, 你必须在使用完它(指针)之后调用PQfinish, 以处理那些结构和所有相关的存储块。 甚至调用PQconnectStart或者 PQconnectPoll失败时也要这样处理。

 

 

如果编译libpq时定义了USE_SSL 那么目前的PQconnectPoll将阻塞住。 这个限制可能在将来的版本移除。

 

 

这些函数把 socket 置于一个非阻塞的状态,就好象调用了 PQsetnonblocking 一样。

 

*

 

 

PQconndefaults 返回缺省的联接选项。

 

 

PQconninfoOption *PQconndefaults(void)

 

 

struct PQconninfoOption

 

{

 

char *keyword; /* The keyword of the option */

 

char *envvar; /* Fallback environment variable name */

 

char *compiled; /* Fallback compiled in default value */

 

char *val; /* Option's current value, or NULL */

 

char *label; /* Label for field in connect dialog */

 

char *dispchar; /* Character to display for this field

 

in a connect dialog. Values are:

 

"" Display entered value as is

 

"*" Password field - hide value

 

"D" Debug option - don't show by default */

 

int dispsize; /* Field size in characters for dialog */

 

}

 

 

返回联接选项结构的地址. 可以用于获取所有可能的PQconnectdb选项和它们的当前缺省值. 返回值指向一个PQconninfoOption struct 的数组, 该数组以一个有 NULL 关键字指针的条目结束.注意缺省值(val 域)将 依赖于环境变量和其他上下文.调用者必须把联接选项当作只读对待.

 

 

在处理完选项数组后,把数组交给PQconninfoFree()释放. 如果没有这么做,每次调用PQconndefaults() 都会有一小部分内存泄漏.

 

 

在PostgreSQL7.0 以前的版本, PQconndefaults() 返回一个指向静态数组的指针, 而不是一个动态分配的数组.这样做是线程不安全的,因此这个特点被修改了.

 

*

 

 

PQfinish 关闭与后端的联接.同时释放被PGconn 对象使用的存储器.

 

 

void PQfinish(PGconn *conn)

 

 

注意,即使与后端的联接尝试失败(可由PQstatus判断), 应用也要调用PQfinish释放被PGconn 对象使用的存储器.不应该在调用 PQfinish后再使用PGconn 指针.

 

*

 

 

PQreset 重置与后端的通讯端口.

 

 

void PQreset(PGconn *conn)

 

 

此函数将关闭与后端的联接并且试图与同一个服务器重建新的联接, 使用所有前面使用过的参数.这在 失去工作联接后进行故障恢复时很有用.

 

*

 

 

PQresetStart PQresetPoll 以非阻塞模式重置与后端的通讯端口。

 

 

int PQresetStart(PGconn *conn);

 

 

PostgreSQLPollingStatusType PQresetPoll(PGconn *conn);

 

 

此函数将关闭与后端的联接并且试图与同一个服务器重建新的联接, 使用所有前面使用过的参数.这在 失去工作联接后进行故障恢复时很有用. 它们和上面的PQreset的区别是它们工作在非阻塞模式。 这些函数的使用有与上面PQconnectStart和 PQconnectPoll一样的限制。

 

 

调用PQresetStart。如果它返回 0,那么重置失败。 如果返回 1,用与使用PQconnectPoll 建立联接的同样的 方法使用PQresetPoll重置联接。

 

 

libpq应用程序员应该仔细维护PGconn结构.使用下面的访问函数来获取PGconn的内容.避免直接引用PGconn结构里的字段,因为这些字段在今后可能被改变.(从 PostgreSQL 版本 6.4 开始, struct PGconn的定义甚至没有放在 libpq-fe.h里.如果你有一些直接访问PGconn数据域的旧代码,你可以通过包含 libpq-int.h 来访问它们,但我们鼓励你赶快修改那些代码.)

 

 

*

 

 

PQdb 返回联接的数据库名.

 

 

char *PQdb(const PGconn *conn)

 

 

PQdb 和下面几个函数返回联接时建立起来的几个值.这些值在PGconn对象的生存期内是固定的.

 

*

 

 

PQuser 返回联接的用户名.

 

 

char *PQuser(const PGconn *conn)

 

 

*

 

 

PQpass 返回联接的口令.

 

 

char *PQpass(const PGconn *conn)

 

 

*

 

 

PQhost 返回联接的服务器主机名.

 

 

char *PQhost(const PGconn *conn)

 

 

*

 

 

PQport 返回联接的端口号.

 

 

char *PQport(const PGconn *conn)

 

 

*

 

 

PQtty 返回联接的调试控制台( tty ).

 

 

char *PQtty(const PGconn *conn)

 

 

*

 

 

PQoptions 返回联接中使用的后端选项.

 

 

char *PQoptions(const PGconn *conn)

 

 

*

 

 

PQstatus 返回联接的状态.

 

 

ConnStatusType PQstatus(const PGconn *conn)

 

 

这个状态可以是一些值之一。 不过,如果不是一次异步联接过程的话,我们只能看到其中的两个 - CONNECTION_OK 或 CONNECTION_BAD。一个与数据库的成功的联接返回状态 CONNECTION_OK。 一次失败的企图用状态 CONNECTION_BAD 标识。 通常,一个 OK 状态保持到 PQfinish,但是一个通讯失败可能会导致状态过早地改变为 CONNECTION_BAD 。这时应用可以试着调用 PQreset 来恢复.

 

 

参阅PQconnectStart和PQconnectPoll 条目看看可能出现的其他的状态码。

 

*

 

 

PQerrorMessage 返回联接中操作产生的最近的错误信息.

 

 

char *PQerrorMessage(const PGconn* conn);

 

 

 

几乎所有libpq函数在失败时都会设置 PQerrorMessage.注意libpq的传统是, 一个非空的 PQerrorMessage 将在结尾包含一个新行.

 

*

 

 

PQbackendPID 返回控制此联接的后端服务器的进程号ID。

 

 

int PQbackendPID(const PGconn *conn);

 

 

 

这个后端PID 在调试和对比 NOTIFY 信息( 包含发出通知的后端的 PID )时很有用. 注意该 PID 属于运行数据库服务器的主机的进程, 而不是本地主机!

 

*

 

 

PQgetssl 返回联接使用的 SSL 结构,或者如果 SSL 没有使用的话返回 NULL.

 

 

SSL *PQgetssl(const PGconn *conn);

 

 

 

这个结构可以用于核实加密级别,检查服务器认证等信息.参考 SSL 文档获取关于这个结构的更多信息.

 

 

为了获取这个函数的原形,你必须定义 USE_SSL. 这样做会自动包含来自OpenSSL的 ssl.h.

 

1.3. 命令执行函数

 

 

 

一旦与数据库服务器的联接成功建立,便可用这里描述的函数执行 SQL 查询和命令。

 

1.3.1. 主过程

 

 

*

 

 

PQexec 给服务器提交一条命令并且等待结果.

 

 

PGresult *PQexec(PGconn *conn,

 

const char *query);

 

 

返回一个PGresult指针或者也可能是一个 NULL 指针. 通常返回一个非空(non-NULL)的指针, 除非没有内存或发生了象不能把命令发送到后端这样的严重错误. 如果返回的是 NULL,它应该被当作PGRES_FATAL_ERROR结果处理. 用PQerrorMessage获取有关错误的更多信息.

 

 

PGresult 结构封装了后端返回的结果. libpq 应用程序员应该仔细维护PGresult抽象.用下面的访问函数来获取PGresult的内容.避免直接引用 PGresult结构的数据域,因为这个结构可能会在未来被改变.(从 PostgreSQL 版本 6.4 开始, struct PGresult的定义甚至都没有放在 libpq-fe.h里.如果你有一些直接访问 PGresult数据域的老代码,你可以通过包含libpq-int.h继续使用它们,但是我们鼓励你立刻修改代码.)

 

 

*

 

 

PQresultStatus 返回命令的结果状态.

 

 

ExecStatusType PQresultStatus(const PGresult *res)

 

 

PQresultStatus可以返回下面数值之一:

 

 

o

 

 

PGRES_EMPTY_QUERY -- 发送给后端的字串是空的

 

o

 

 

PGRES_COMMAND_OK -- 成功完成一个没有返回数据的命令

 

o

 

 

PGRES_TUPLES_OK -- 成功执行查询

 

o

 

 

PGRES_COPY_OUT -- (从服务器)Copy Out (拷贝出)数据传输开始

 

o

 

 

PGRES_COPY_IN -- Copy In (拷贝入)(到服务器)数据传输开始

 

o

 

 

PGRES_BAD_RESPONSE -- 服务器的响应无法理解

 

o

 

 

PGRES_NONFATAL_ERROR

 

o

 

 

PGRES_FATAL_ERROR

 

 

如果结果状态是 PGRES_TUPLES_OK ,那么可以用下面的过程从查询的返回中抽取元组信息.注意一个碰巧检索了零条元组的 SELECT 仍然显示 PGRES_TUPLES_OK。 PGRES_COMMAND_OK用于不返回元组的命令(INSERT,UPDATE,等)。返回 PGRES_EMPTY_QUERY 的响应通常意味着客户端软件里面的臭虫。

 

*

 

 

PQresStatus 把PQresultStatus返回的枚举类型转换成一个描述状态码的字符串常量。

 

 

char *PQresStatus(ExecStatusType status);

 

 

*

 

 

PQresultErrorMessage 返回与查询关联的错误信息,或在没有错误时返回一个空字符串.

 

 

char *PQresultErrorMessage(const PGresult *res);

 

 

紧跟在一个 PQexec 或 PQgetResult 调用后面,PQerrorMessage (对联接)将返回与 PQresultErrorMessage (对结果)一样的字符串.不过,一个PGresult将保有其错误信息直到被删除,而连结的错误信息将在后续的操作完成时被改变.当你想知道与某个 PGresult相关联的状态时用 PQresultErrorMessage ;当你想知道与联接的最近一个操作相关联的状态时用 PQerrorMessage;

 

*

 

 

PQclear 释放于PGresult相关联的存储空间. 任何不再需要的查询结果在不需要的时候都应该用 PQclear释放掉.

 

 

void PQclear(PQresult *res);

 

 

你可以保留PGresult对象任意长的时间; 当你提交新的查询时它并不消失, 甚至你断开联接后也是这样.要删除它,你必须调用 PQclear.不这么做将导致前端的存储器泄漏.

 

*

 

 

PQmakeEmptyPGresult 构造一个给出状态的为空的PGresult对象.

 

 

PGresult* PQmakeEmptyPGresult(PGconn *conn, ExecStatusType status);

 

 

这是libpq的内部过程, 用于分配和初始化一个空PGresult对象. 它被输出是因为一些应用需要自行生成结 果对象(尤其是特定的带有错误状态的对象). 如果conn非空(NULL)并且状态指示一个错误, 联接当前的错误信息被拷贝到PGresult. 注意最终对该对象要调用PQclear, 正如libpq本身返回的PGresult一样.

 

 

1.3.2. 为包含在 SQL 查询中逃逸字串

 

 

PQescapeString 为在 SQL 查询中使用逃逸一个字串.

 

 

size_t PQescapeString (char *to, const char *from, size_t length);

 

 

如果你需要在查询字串中包含一个从不可靠的源头接收的字串 (比如,它们是随机用户输入的),那么,出于安全原因,你不能把它们直接包含在 SQL 查询里.你应该把特殊字符引起来,否则它们就会被 SQL 分析器代换.

 

 

PQescapeString 执行这个操作.from 指向将要逃逸的字串的第一个字符,length 参数计算在这个字串里的字符数量(字串结尾的字节零不是必须的,也不计入长度). to 应该指向一个缓冲区,这个缓冲区至少能保存 length 数值的两倍还多一个的字符,否则该函数行为将不可预测.调用 PQescapeString 就会把逃逸的 from 字串转换到 to 缓冲区,把特殊字符替换掉以免发生意外,并且追加终止的字节零.那些必须包围在PostgreSQL 字串文本周围的单引号不算结果字串的一部分.

 

 

PQescapeString 返回写到 to 里面的字符数目,不包括结尾的字节零.如果 to 和 from 字串相互重叠,那么其行为不可预测.

 

1.3.3. 逃逸包含在 SQL 查询中的二进制字串

 

 

PQescapeBytea 逃逸那些在 SQL 查询中使用二进制字串(bytea 类型).

 

 

unsigned char *PQescapeBytea(const unsigned char *from,

 

size_t from_length,

 

size_t *to_length);

 

 

 

在 SQL 语句中用做 BYTEA 字串文本的 一部分的时候, 有些 ASCII 字符必需被逃逸 (但是对于所有字符而言是可以逃逸). 通常,要逃逸一个字符,它是被转换成一个三位八进制数字, 该数字数值等于相应的十进制 ASCII 数值,然后前缀 两个反斜扛.单引号(')和反斜扛字符()有自己特殊的逃逸序列.参阅 用户手册获取更多信息. PQescapeBytea 执行这个操作,它只逃逸需要逃逸 的最少的字符.

 

 

from 参数指向需要逃逸的字串的第一个字符, from_length 参数反映在这个二进制字串 (那种字节零既不必要也不计算在内的字串)里字符的个数. to_length 参数应该是一个指向某个缓冲区的指针, 它的空间应该能够保存逃逸后的结果字串长度. 结果字串长度不包括结果结尾的字节零.

 

 

PQescapeBytea 返回一个 from 参数的二进制字串的逃逸后的版本,返回给调用者提供的缓冲区. 返回的字串已经把所有特殊的字符替换调了,这样他们就可以由 PostgreSQL的字串文本分析器以及 bytea 的输入函数 正确地处理.同时还追加了一个结尾的字节零.那些必需包围在 PostgreSQL字串文本周围的单引号不算结果字串的一部分.

 

 

PQunescapeBytea 把一个二进制数据的逃逸后的字串表现形式转换成二进制数据 - PQescapeBytea 的反作用.

 

 

unsigned char *PQunescapeBytea(const unsigned char *from, size_t *to_length);

 

 

 

from 参数指向一个逃逸后的字串, 比如 PQgetvalue 从一个 BYTEA 字段返回的.PQunescapeBytea 把 它的字串表现形式转换成二进制形式,填充到一个缓冲区. 它返回一个指向该缓冲区的指针,若为 NULL 则出错, 缓冲区的尺寸放在 to_length 里. 该指针随后可以用做 free(3) 的参数.

 

1.3.4. 检索 SELECT 的结果信息

 

 

*

 

 

PQntuples 返回查询结果里的元组(元组)个数.

 

 

int PQntuples(const PGresult *res);

 

 

*

 

 

PQnfields 返回查询结果里每个元组的数据域(字段)的个数.

 

 

int PQnfields(const PGresult *res);

 

 

*

 

 

PQfname 返回与给出的数据域编号相关联的数据域(字段)的名称.数据域编号从 0 开始

 

 

char *PQfname(const PGresult *res,

 

int field_index);

 

 

*

 

 

PQfnumber 返回与给出的数据域名称相关联的数据域(字段)的编号.

 

 

int PQfnumber(const PGresult *res,

 

const char *field_name);

 

 

如果给出的名字不匹配任何域,返回-1.

 

*

 

 

PQftype 返回与给定数据域编号关联的数据域类型. 整数返回值是一个该类型的内部编码.数据域编号从0 开始.

 

 

Oid PQftype(const PGresult *res,

 

int field_index);

 

 

你可以查询系统表 pg_type 以获取各种数据类型的名称和属性。内建的数据类型的 OID 在源码树的 src/include/catalog/pg_type.h 文件里定义。

 

*

 

 

PQfmod 返回与给定数据域编号相关联的类型相关的修正数据(??). 数据域编号从 0 开始.

 

 

int PQfmod(const PGresult *res,

 

int field_index);

 

 

*

 

 

PQfsize 返回一个PGresult 里面的一条元组的单独的一个数据域(字段)的值. 元组和数据域编号从0 开始.

 

 

int PQfsize(const PGresult *res,

 

int field_index);

 

 

PQfsize返回在数据库元组里面给该数据域分配的空间, 换句话说就是该数据类型在服务器里的二进制形式的大小(尺寸). 如果该数据域是可变尺寸,返回 -1.

 

*

 

 

PQbinaryTuples 如果PGresult包含二进制元组数据时返回 1, 如果包含 ASCII 数据返回 0.

 

 

int PQbinaryTuples(const PGresult *res);

 

 

目前,二进制元组数据只能从一个二进制游标里抽取数据的查询返回.

 

 

1.3.5. 检索 SELECT 结果数值

 

 

*

 

 

PQgetvalue 返回一个PGresult 里面一个元组单独的一个数据域(字段)的值. 元组和数据域编号从 0 开始.

 

 

char* PQgetvalue(const PGresult *res,

 

int tup_num,

 

int field_num);

 

 

对大多数查询而言, PQgetvalue 返回的值是一个表示字段值的空(NULL)结尾的字符串.但是如果 PQbinaryTuples() 为 1, PQgetvalue 返回的值就是该类型在后端服务器内部的二进制表现形式(但是不包括尺寸字--如果数据域是变长的).这样,把数据转换成对应的 C 类型就是程序员的责任了. PQgetvalue 返回的指针指向一个本身是 PGresult结构的一部分的存储区域.我们不能更改它,并且如果 我们要在PGresult结构的生存期后还要使用它的话, 我们必须明确地把该数值拷贝到其他存储器中.

 

*

 

 

PQgetisnull 测试一个数据域是否为空(NULL).元组和数据域编号从 0 开始.

 

 

int PQgetisnull(const PGresult *res,

 

int tup_num,

 

int field_num);

 

 

如果该域包含 NULL,函数返回 1,如果包含非空(non-null )值,返回 0. (注意,对一个 NULL 数据域,PQgetvalue 将返回一个空字符串, 不是一个空指针.)

 

*

 

 

PQgetlength 返回以字节计的数据域(字段)的长度.元组和数据域编号从 0 开始.

 

 

int PQgetlength(const PGresult *res,

 

int tup_num,

 

int field_num);

 

 

这是某一特定数据值的实际数据长度,也就是由PQgetvalue 指向的对象的尺寸.注意,对于 ASCII 表示的数值,这个尺寸与PQfsize 报告的二进制尺寸无关.

 

*

 

 

PQprint 向指定的输出流打印所有的元组和(可选的)字段名称.

 

 

void PQprint(FILE* fout, /* output stream */

 

const PGresult *res,

 

const PQprintOpt *po);

 

 

struct {

 

pqbool header; /* print output field headings and row count */

 

pqbool align; /* fill align the fields */

 

pqbool standard; /* old brain dead format */

 

pqbool html3; /* output html tables */

 

pqbool expanded; /* expand tables */

 

pqbool pager; /* use pager for output if needed */

 

char *fieldSep; /* field separator */

 

char *tableOpt; /* insert to HTML table ... */

 

char *caption; /* HTML caption */

 

char **fieldName; /* null terminated array of replacement field names */

 

} PQprintOpt;

 

 

 

这个函数以前被 psql 用于打印查询结果,但是现在已经不用这个函数了,并且此函数不再有活跃的支持。

 

 

1.3.6. 检索非-SELECT 结果信息

 

 

*

 

 

PQcmdStatus 返回产生PGresult的 SQL 命令的命令状态字符串.

 

 

char * PQcmdStatus(PGresult *res);

 

 

*

 

 

PQcmdTuples 返回被 SQL 命令影响的行的数量.

 

 

char * PQcmdTuples(PGresult *res);

 

 

如果产生PGresult的SQL 命令是 INSERT, UPDATE 或 DELETE, 这里返回涉及行的行数.如果是其他命令返回一个空字符串.

 

*

 

 

PQoidValue 返回一个插入的元组的对象标识(OID)——如果 SQL 命令是 INSERT.否则,返回 InvalidOid.

 

 

Oid PQoidValue(const PGresult *res);

 

 

如果你包含了 libpq头文件,那么 Oid 和常量 InvalidOid 的类型将被定义。他们都是某种整型类型。

 

*

 

 

PQoidStatus 返回一个被插入的元组的对象标识的字串, 如果 SQL 命令是 INSERT。 否则. 返回一个空字串。

 

 

char * PQoidStatus(const PGresult *res);

 

 

因为有了 PQoidValue ,我们不建议使用这个函数,而且它在线程里使用也是不安全的

 

1.4. 异步查询处理

 

 

 

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 将不发生做用

 

1.5. 捷径接口

 

 

 

PostgreSQL 提供一个发往后端的函数调用的捷径接口.这是一个通向系统内部的后门,因而可能存在安全漏洞.大多数用户应该不需要这个特性.

 

 

*

 

 

PQfn 请求允许通过捷径接口执行后端函数.

 

 

PGresult* PQfn(PGconn* conn,

 

int fnid,

 

int *result_buf,

 

int *result_len,

 

int result_is_int,

 

const PQArgBlock *args,

 

int nargs);

 

 

fnid参数是待执行的函数的对象标识(OID). result_buf是放置返回值的缓冲区. 调用者必须为返回值分配足够的空间(这里没有检查!). 实际的返回值长度将被放在result_len指向的整数里返回. 如果预期返回值是 4-字节整数,把result_is_int设为 1; 否则设为 0.(把result_is_int设为 1 告诉 libpq必要时交换数值字节序, 这样就可以正确地传输成客户机上的整数值.当 result_is_int是 0 时, 后端发送回来的字节串不做修改.) args和nargs声明要传入函数的参数.

 

 

typedef struct {

 

int len;

 

int isint;

 

union {

 

int *ptr;

 

int integer;

 

} u;

 

} PQArgBlock;

 

 

PQfn 总是返回一个有效的PGresult*. 在使用结果之前应该检查结果状态。 当结果不再使用后,调用者有义务使用 PQclear 释放PGresult

 

1.6. 异步通知

 

 

 

PostgreSQL 支持通过LISTEN和NOTIFY命令产生的异步通知.一个后端用LISTEN命令注册一个它感兴趣的通知条件(也可以用UNLISTEN去掉这个注册).所有正在监听某一通知条件的后端在该条件名的 NOTIFY(通知)被任何后端执行后都将被异步地通知.通知发出者不会传递附加的信息到监听者.因此,很典型地是,任何实际的需要被传递的数据都是通过一个数据库关系传递的.通常,条件名与相关联的关系同名,但是并不是一定要与某个关系相关才行.

 

 

libpq 应用把LISTEN和 UNLISTEN 命令作为通常的 SQL 命令提交.因此,通过调用PQnotifies()可以侦测到 NOTIFY 消息的到达.

 

 

*

 

 

PQnotifies 从一个来自后端的未处理的通知信息列表中返回下一条通知. 如果没有未处理的信息则返回NULL. 一旦PQnotifies返回一条通知, 该通知会被认为已处理并且将被从通知列表中删除.

 

 

PGnotify* PQnotifies(PGconn *conn);

 

 

typedef struct pgNotify {

 

char *relname; /* 包含数据的关系(表)名字*/

 

int be_pid; /* 后端进程 id*/

 

} PGnotify;

 

 

在处理完 PQnotifies 返回的PGnotify对象后,别忘了用 free() 把它释放,以避免内存泄漏。

 

 

注意: 在 PostgreSQL 6.4 和更高的版本里, be_pid 是正在通知的后端的 PID,而在早些的版本里它总是你自己的后端的 PID。

 

 

第二个样本程序给出一个使用异步通知的例子.

 

 

PQnotifies() 实际上并不读取后端数据;它只是返回被前面的另一个libpq函数吸收的信息.在以前的 libpq的版本里,周期性的收到通知(NOTIFY)信息的唯一方法是持续的提交查询,即使是空查询也可以,并且在每次 PQexec()后检查 PQnotifies() 。现在这个方法也能还工作,不过我们认为它太浪费处理器时间而废弃了它。

 

 

在你没有可用的查询提交时检查 NOTIFY 消息的更好的方法是调用 PQconsumeInput(),然后检查 PQnotifies().你可以使用 select() 来等待后端数据的到达,这样在没有数据可处理时可以不浪费 CPU 时间.(参阅 PQsocket() 获取用于 select() 的文件描述符.)注意这种方法不管你使用 PQsendQuery/PQgetResult 还是简单的 PQexec来执行查询都能工作.不过,你应该记住在每次 PQgetResult 或 PQexec后检查 PQnotifies() ,看看在处理查询的过程中是否有通知到达

 

 

 

1.7. 与 COPY 命令相关的函数

 

 

PostgreSQL 里的 COPY 命令里有用于 libpq 里从网络联接读出或者写入的选项.因此,这些函数有必要直接访问网络联接,以便应用可以充分利用这个功能.

 

 

这些函数应该只在从 PQexec 或 PQgetResult 获得了 PGRES_COPY_OUT 或 PGRES_COPY_IN 结果对象的情况下执行.

 

 

*

 

 

PQgetline 读取一个以回车符(换行符)结尾的字符行中指定字节数的字符 (由后端服务器传输)到一个字符串缓冲区.

 

 

int PQgetline(PGconn *conn,

 

char *string,

 

int length)

 

 

类似 fgets,这个过程拷贝最多 length-1 个字符到字符串里.但是它会象 gets那样把结尾的换行符转换成一个字节零. PQgetline 在输入结束时返回 EOF ,如果整行都被读取了返回 0,如果缓冲区填满了而还没有遇到结束的换行符则返回 1.

 

 

注意,应用程序必须检查新行是否包含两个字符 ".",这表明后端服务器已经完成了 copy 命令的结果集的发送.如果应用可能收到超过 length-1 字符长的字符,我们就应该确保正确识别"."行(例如,不要把一个长的行的结束当作一个终止行). src/bin/psql/copy.c 里的代码包含正确控制 copy 协议的过程.

 

*

 

 

PQgetlineAsync 不做阻塞地读取一行以换行符结尾的字符(由后端服务器传输)到一个缓冲区中.

 

 

int PQgetlineAsync(PGconn *conn,

 

char *buffer,

 

int bufsize)

 

 

这个过程类似于 PQgetline,但是可以用于那些必须异步地读取 COPY 数据的应用,因而是不阻塞的.在使用了 COPY 命令和获取了 PGRES_COPY_OUT响应之后,应用应该调用 PQconsumeInput 和 PQgetlineAsync 直到收到数据结束的信号.不象PQgetline,这个过程负责检测数据结束.在每次调用时,如果libpq 的输入缓冲区内有可用的一个完整的换行符结尾的数据行或者调用者声明的缓冲区小于到来的数据行的长度, PQgetlineAsync 都将返回数据.否则,在其他数据到达之前不会返回数据.

 

 

如果见到了拷贝数据结束的信号,此过程返回 -1,如果没有可用数据返回 0,或者是给出一个正数表明返回的数据的字节数.如果返回 -1,调用者下一步必须调用 PQendcopy,然后回到正常处理.返回的数据将不会超出换行符的范围.如果可能,每次将返回一个完整行.但如果调用者提供的缓冲区太小,无法容下后端发出的整行,那么将返回部分行.这个可以通过测试返回的最后一个字节是否 " " 来确认.返回的字符串不是空结尾的.(如果你想得到一个空结尾的字串,确保你传递了一个 bufsize 比实际大小少一字节的缓冲区.)

 

*

 

 

PQputline 向后端服务器发送一个空结尾的字符串.成功时返回 0,如果不能发送字符串返回 EOF.

 

 

int PQputline(PGconn *conn,

 

const char *string);

 

 

注意,应用在最后一行时必须明确的发送两个字符 "." ,通知后端它已经完成数据发送.

 

*

 

 

PQputnbytes 向后端服务器发送一个非空结尾的字符串.成功时返回 0,如果不能发送字符串返回 EOF.

 

 

int PQputnbytes(PGconn *conn,

 

const char *buffer,

 

int nbytes);

 

 

此函数类似 PQputline ,除了数据缓冲区不需要是空结尾的之外,因为要发送的字节数是直接声明的.

 

*

 

 

PQendcopy 与后端同步.这个函数等到后端完成拷贝(才返回?).你可以在用 PQputline 向后端发送完最后一个字符串后或者用 PGgetline从后端获取最后一行字符串后调用它.我们必须调用这个函数,否则后端可能会和前端"同步丢失"。在这个函数返回后,后端就已经准备好接收下一个 SQL 命令了.成功时返回0,否则返回非零值.

 

 

int PQendcopy(PGconn *conn);

 

 

一个例子:

 

 

PQexec(conn, "CREATE TABLE foo (a int4, b char(16), d double precision)");

 

PQexec(conn, "COPY foo FROM STDIN");

 

PQputline(conn, "3 hello world 4.5 ");

 

PQputline(conn,"4 goodbye world 7.11 ");

 

...

 

PQputline(conn,". ");

 

PQendcopy(conn);

 

 

在使用 PQgetResult时,应用应该对 PGRES_COPY_OUT 的结果做出反应:重复调用 PQgetline ,并且在收到结束行时调用 PQendcopy.然后应该返回到 PQgetResult 循环直到 PQgetResult 返回 NULL.类似地, PGRES_COPY_IN 结果是用一系列 PQputline 调用最后跟着 PQendcopy,然后返回到 PQgetResult 循环.这样的排列将保证嵌入到一系列 SQL 命令里的 copy in 或 copy out 命令将被正确执行.

 

 

旧的应用大多通过 PQexec 提交一个 copy in 或 copy out 命令并且假设在 PQendcopy 后事务完成.这样只有在copy in/out 是命令字串里的唯一的 SQL 命令时才能正确工作

 

 

 

1.8. libpq 跟踪函数

 

 

*

 

 

PQtrace 打开对前端/后端通讯的跟踪,把调试信息输出到一个文件流里.

 

 

void PQtrace(PGconn *conn

 

FILE *debug_port)

 

 

*

 

 

PQuntrace 关闭PQtrace打开的跟踪

 

 

void PQuntrace(PGconn *conn)

 

 

1.9. libpq 控制函数

 

 

*

 

 

PQsetNoticeProcessor 控制libpq生成的通知和警告信息的汇报.

 

 

typedef void (*PQnoticeProcessor) (void *arg, const char *message);

 

 

PQnoticeProcessor

 

PQsetNoticeProcessor(PGconn *conn,

 

PQnoticeProcessor proc,

 

void *arg);

 

 

缺省时,libpq 在后端往 stderr 上打印“通知”信息和一些它自身生成的错误信息.这个特性可以通过提供一个对信息进行某种加工的回叫函数来更改.我们向这个回叫函数传递错误信息的文本(包括文本结尾的换行符),和一个空(void)指针,该指针与传递给 PQsetNoticeProcessor 的完全一样.(如果需要,这个指针可以用于访问应用相关的状态.)缺省的通知处理器只是简单的

 

 

static void

 

defaultNoticeProcessor(void * arg, const char * message)

 

{

 

fprintf(stderr, "%s", message);

 

}

 

 

要使用特殊的通知处理器,在创建完新的PGconn对象后马上调用 PQsetNoticeProcessor。

 

 

返回值是指向以前的通知处理器的指针。如果你提供了一个 NULL 做为回调指针,那么不会发生任何动作,只是返回当前的指针。

 

 

一旦你设置了一个通知处理器,你就应该预料到该函数可能在PGconn对象或 PGresult对象从开始存在时起就可能被调用.当创建一个PGresult时,PGconn 的当前的通知指针被拷贝到 PGresult里提供给可能需要的过程,如 PQgetvalue 使用.

 

 

1.10. 环境变量

 

 

下面的环境变量可以用于选择缺省的联接参数值,这些值将被 PQconnectdb 或 PQsetdbLogin 使用--如果调用代码没有直接声明相应值的话.这些(环境变量)可以避免把麻烦的数据库名强加入简单的应用程序的硬代码里面.

 

 

*

 

 

PGHOST 设置缺省的服务器名.如果它以一个斜扛开头,那么它声明一个 Unix 域套接字而不是 TCP/IP 通讯;其值就是该套接字文件存储的目录(缺省为 /tmp).

 

*

 

 

PGPORT 设置缺省的 TCP 端口号或者设置与 PostgreSQL 的 Unix 域套接字的文件扩展(文件标识符).

 

*

 

 

PGDATABASE 设置缺省的 PostgreSQL 数据库名.

 

*

 

 

PGUSER 设置用于与数据库联接和用于认证的用户名.

 

*

 

 

PGPASSWORD 如果后端要求口令认证,设置使用的口令.因为安全原因,这个功能已经废弃了;请考虑使用 $HOME/.pgpass文件。

 

*

 

 

PGREALM 设置与 PostgreSQL一起使用的 Kerberos 领域--如果该域与本地域不同的话。如果设置了 PGREALM ,PostgreSQL 应用将试图用这个域(realm)与服务器进行认证并且使用独立的门票文件(ticket files)以避免与本地的门票文件冲突.只有在后端选择了 Kerberos 认证时才使用这个环境变量.(译注:门票文件是 Kerberos认证协议中用于交换密钥的一个文件/服务器。)

 

*

 

 

PGOPTIONS 为 PostgreSQL 后端设置附加的运行时选项.

 

*

 

 

PGTTY 设置后端调试信息显示输出的文件或者控制台(tty).

 

 

下面的环境变量可以用于为每个PostgreSQL 会话声明用户级别的缺省特性:

 

 

*

 

 

PGDATESTYLE 设置缺省的日期/时间表现形式.

 

*

 

 

PGTZ 设置缺省的时区.

 

*

 

 

PGCLIENTENCODING 设置缺省的客户端编码(如果配制PostgreSQL 时选择了多字节支持).

 

 

下面的环境变量可以用于为每个PostgreSQL 会话声明缺省的内部特性:

 

 

*

 

 

PGGEQO 为基因优化器设置缺省模式.

 

 

参阅 SET SQL 命令获取这些环境变量的正确值的信息.

 

 

1.11. 文件

 

 

> 家目录中的.pgpass 是一个可以包含口令的文件。如果连接要求口令,那么可以用它。这个文件的格式应该是:

 

 

hostname:port:database:username:password

 

 

这些行可以是一个文本名字,或者 *,它匹配所有的东西。第一个匹配的东西将得以使用,因此我们应该把最具体的记录放在前面。有 : 或者 的记录应该用 逃逸。

 

 

.pgpass 的权限必须不允许任何全局或者同组的用户访问;我们可以用命令 chmod 0600 .pgaccess 实现这个目的。如果权限比这个松,这个文件将被忽略。

 

1.12. 线程特性

 

 

到 PostgreSQL 7.0 时 libpq 是线程安全的, --只要不是两个线程试图同时操作同一个PGconn对象.实际上,你无法从不同的线程向同一个联接对象发出并发的查询.(如果你需要运行并行查询,请启动多个联接.)

 

 

PGresult对象在创建后是只读的,因此可以自由地在线程之间传递.

 

 

过时了的函数 PQoidStatus 和 fe_setauthsvc 都是线程不安全的,因此不应该在一个多线程的程序里面使用. PQoidStatus 可以由 PQoidValue代替.而我们觉得根本没有调用 fe_setauthsvc 的必要.

 

 

Libpq 客户端使用的 crypt 加密方法倚赖 crypt() 系统函数,它通常不是线程安全的.我们最好使用 MD5 加密,它在所有平台上是线程安全的.

 

 

1.13. 制作 Libpq 程序

 

 

要制作(也就是说编译和链接)你的 libpq 程序,你需要做下面的一些事情∶

 

 

*

 

 

包含 libpq-fe.h 头文件∶

 

 

#include <libpq-fe.h>

 

 

如果你没干这件事,那么你通常会看到类似下面这样的来自编译器的信息∶

 

 

foo.c: In function `main':

 

foo.c:34: `PGconn' undeclared (first use in this function)

 

foo.c:35: `PGresult' undeclared (first use in this function)

 

foo.c:54: `CONNECTION_BAD' undeclared (first use in this function)

 

foo.c:68: `PGRES_COMMAND_OK' undeclared (first use in this function)

 

foo.c:95: `PGRES_TUPLES_OK' undeclared (first use in this function)

 

 

*

 

 

告诉你的编译器PostgreSQL头文件的安装位置, 方法是给你的编译器提供 -Idirectory 选项. (有些时候编译器会查找缺省的目录,因此你可以忽略这些选项.) 比如,你的命令行看起来象∶

 

 

cc -c -I/usr/local/pgsql/include testprog.c

 

 

如果你在使用制作文件(makefile),那么向 CPPFLAGS 变量中增加下面的选项∶

 

 

CPPFLAGS += -I/usr/local/pgsql/include

 

 

如果你的程序可能会被别人编译,那么你应该避免象上面那样把目录路径 写成硬编码.取而代之的是你可以运行 pg_config 工具找出头文件在系统的什么地方∶

 

 

$ pg_config --includedir

 

/usr/local/include

 

 

如果没能给编译器提供正确的选项将产生类似下面这样的错误信息

 

 

testlibpq.c:8:22: libpq-fe.h: No such file or directory

 

 

*

 

 

在链接最后的程序的时候,声明选项 -lpq, 这样就可以把 libpq 库链接进来, 还要声明 -Ldirectory 以指向 libpq 所处的目录. (同样,编译器也会搜索一些缺省的目录.) 为了尽可能提高可移植性,你应该把 -L 选项放在 -lpq 选项前面.比如∶

 

 

cc -o testprog testprog1.o testprog2.o -L/usr/local/pgsql/lib -lpq

 

 

你也可以用 pg_config 找出库目录∶

 

 

$ pg_config --libdir

 

/usr/local/pgsql/lib

 

 

指向这类问题的错误信息会是类似下面这个样子.

 

 

testlibpq.o: In function `main':

 

testlibpq.o(.text+0x60): undefined reference to `PQsetdbLogin'

 

testlibpq.o(.text+0x71): undefined reference to `PQstatus'

 

testlibpq.o(.text+0xa4): undefined reference to `PQerrorMessage'

 

 

这意味着你忘记 -lpq 了.

 

 

/usr/bin/ld: cannot find -lpq

 

 

这意味着你忘记这-L 或者没有声明正确的路径.

 

 

如果你的代码引用了头文件 libpq-int.h, 而且你不原意修补你的代码以避免使用它,那么从 PostgreSQL7.2 开始, 该文件将在 includedir/postgresql/internal/libpq-int.h 里出现, 因此你需要给你的编译器命令行增加合适的 -I 选项.

 

1.14. 例子程序

 

 

 

Example 1-1. libpq 例子程序 1

 

[myphp]

 

/*

 

* testlibpq.c

 

*

 

* Test the C version of libpq, thePostgreSQLfrontend

 

* library.

 

* 测试 PostgreSQL 前端库 libpq 的 C 版本

 

*/

 

#include <stdio.h>

 

#include <libpq-fe.h>

 

 

void

 

exit_nicely(PGconn *conn)

 

{

 

PQfinish(conn);

 

exit(1);

 

}

 

 

main()

 

{

 

char *pghost,

 

*pgport,

 

*pgoptions,

 

*pgtty;

 

char *dbName;

 

int nFields;

 

int i,

 

j;

 

 

/* FILE *debug; */

 

 

PGconn *conn;

 

PGresult *res;

 

 

/*

 

* begin, by setting the parameters for a backend connection if the

 

* parameters are null, then the system will try to use reasonable

 

* defaults by looking up environment variables or, failing that,

 

* using hardwired constants

 

*/

 

pghost = NULL; /* host name of the backend server */

 

pgport = NULL; /* port of the backend server */

 

pgoptions = NULL; /* special options to start up the backend

 

* server */

 

pgtty = NULL; /* debugging tty for the backend server */

 

dbName = "template1";

 

 

/* make a connection to the database */

 

conn = PQsetdb(pghost, pgport, pgoptions, pgtty, dbName);

 

 

/*

 

* check to see that the backend connection was successfully made

 

*/

 

if (PQstatus(conn) == CONNECTION_BAD)

 

{

 

fprintf(stderr, "Connection to database '%s' failed. ", dbName);

 

fprintf(stderr, "%s", PQerrorMessage(conn));

 

exit_nicely(conn);

 

}

 

 

/* debug = fopen("/tmp/trace.out","w"); */

 

/* PQtrace(conn, debug); */

 

 

/* start a transaction block */

 

res = PQexec(conn, "BEGIN");

 

if (!res || PQresultStatus(res) != PGRES_COMMAND_OK)

 

{

 

fprintf(stderr, "BEGIN command failed ");

 

PQclear(res);

 

exit_nicely(conn);

 

}

 

 

/*

 

* should PQclear PGresult whenever it is no longer needed to avoid

 

* memory leaks

 

*/

 

PQclear(res);

 

 

/*

 

* fetch rows from the pg_database, the system catalog of

 

* databases

 

*/

 

res = PQexec(conn, "DECLARE mycursor CURSOR FOR SELECT * FROM pg_database");

 

if (!res || PQresultStatus(res) != PGRES_COMMAND_OK)

 

{

 

fprintf(stderr, "DECLARE CURSOR command failed ");

 

PQclear(res);

 

exit_nicely(conn);

 

}

 

PQclear(res);

 

res = PQexec(conn, "FETCH ALL in mycursor");

 

if (!res || PQresultStatus(res) != PGRES_TUPLES_OK)

 

{

 

fprintf(stderr, "FETCH ALL command didn't return tuples properly ");

 

PQclear(res);

 

exit_nicely(conn);

 

}

 

 

/* first, print out the attribute names */

 

nFields = PQnfields(res);

 

for (i = 0; i < nFields; i++)

 

printf("%-15s", PQfname(res, i));

 

printf(" ");

 

 

/* next, print out the rows */

 

for (i = 0; i < PQntuples(res); i++)

 

{

 

for (j = 0; j < nFields; j++)

 

printf("%-15s", PQgetvalue(res, i, j));

 

printf(" ");

 

}

 

PQclear(res);

 

 

/* close the cursor */

 

res = PQexec(conn, "CLOSE mycursor");

 

PQclear(res);

 

 

/* commit the transaction */

 

res = PQexec(conn, "COMMIT");

 

PQclear(res);

 

 

/* close the connection to the database and cleanup */

 

PQfinish(conn);

 

 

/* fclose(debug); */

 

return 0;

 

 

}

 

[/myphp]

 

 

Example 1-2. libpq 例子程序 2

 

 

 

[myphp]

 

/*

 

* testlibpq2.c

 

* 测试异步通知接口

 

*

 

* 运行此程序,然后从另外一个窗口的 psql 里运行 NOTIFY TBL2;

 

*

 

* 或者,如果你想好玩一点,尝试下面动作∶

 

* 用下面的语句填充一个数据库∶

 

*

 

* CREATE TABLE TBL1 (i int4);

 

*

 

* CREATE TABLE TBL2 (i int4);

 

*

 

* CREATE RULE r1 AS ON INSERT TO TBL1 DO

 

* (INSERT INTO TBL2 values (new.i); NOTIFY TBL2);

 

*

 

* 然后∶

 

*

 

* INSERT INTO TBL1 values (10);

 

*

 

*/

 

#include <stdio.h>

 

#include "libpq-fe.h"

 

 

void

 

exit_nicely(PGconn *conn)

 

{

 

PQfinish(conn);

 

exit(1);

 

}

 

 

main()

 

{

 

char *pghost,

 

*pgport,

 

*pgoptions,

 

*pgtty;

 

char *dbName;

 

int nFields;

 

int i,

 

j;

 

 

PGconn *conn;

 

PGresult *res;

 

PGnotify *notify;

 

 

/*

 

* begin, by setting the parameters for a backend connection if the

 

* parameters are null, then the system will try to use reasonable

 

* defaults by looking up environment variables or, failing that,

 

* using hardwired constants

 

*/

 

pghost = NULL; /* host name of the backend server */

 

pgport = NULL; /* port of the backend server */

 

pgoptions = NULL; /* special options to start up the backend

 

* server */

 

pgtty = NULL; /* debugging tty for the backend server */

 

dbName = getenv("USER"); /* change this to the name of your test

 

* database */

 

 

/* make a connection to the database */

 

conn = PQsetdb(pghost, pgport, pgoptions, pgtty, dbName);

 

 

/*

 

* check to see that the backend connection was successfully made

 

*/

 

if (PQstatus(conn) == CONNECTION_BAD)

 

{

 

fprintf(stderr, "Connection to database '%s' failed. ", dbName);

 

fprintf(stderr, "%s", PQerrorMessage(conn));

 

exit_nicely(conn);

 

}

 

 

res = PQexec(conn, "LISTEN TBL2");

 

if (!res || PQresultStatus(res) != PGRES_COMMAND_OK)

 

{

 

fprintf(stderr, "LISTEN command failed ");

 

PQclear(res);

 

exit_nicely(conn);

 

}

 

 

/*

 

* should PQclear PGresult whenever it is no longer needed to avoid

 

* memory leaks

 

*/

 

PQclear(res);

 

 

while (1)

 

{

 

 

/*

 

* wait a little bit between checks; waiting with select()

 

* would be more efficient.

 

*/

 

sleep(1);

 

/* collect any asynchronous backend messages */

 

PQconsumeInput(conn);

 

/* check for asynchronous notify messages */

 

while ((notify = PQnotifies(conn)) != NULL)

 

{

 

fprintf(stderr,

 

"ASYNC NOTIFY of '%s' from backend pid '%d' received ",

 

notify->relname, notify->be_pid);

 

free(notify);

 

}

 

}

 

 

/* close the connection to the database and cleanup */

 

PQfinish(conn);

 

 

return 0;

 

}

 

 

Example 1-3. libpq 例子程序 3

 

 

/*

 

* testlibpq3.c 测试 C 版本的 Libpq --PostgreSQL的前端库.

 

* 测试二进制游标接口

 

*

 

*

 

*

 

* 用下面语句填充一个数据库∶

 

*

 

* CREATE TABLE test1 (i int4, d real, p polygon);

 

*

 

* INSERT INTO test1 values (1, 3.567, polygon '(3.0, 4.0, 1.0, 2.0)');

 

*

 

* INSERT INTO test1 values (2, 89.05, polygon '(4.0, 3.0, 2.0, 1.0)');

 

*

 

* 预期的输出是∶

 

*

 

* tuple 0: got i = (4 bytes) 1, d = (4 bytes) 3.567000, p = (4

 

* bytes) 2 points boundbox = (hi=3.000000/4.000000, lo =

 

* 1.000000,2.000000) tuple 1: got i = (4 bytes) 2, d = (4 bytes)

 

* 89.050003, p = (4 bytes) 2 points boundbox =

 

* (hi=4.000000/3.000000, lo = 2.000000,1.000000)

 

*

 

*

 

*/

 

#include <stdio.h>

 

#include "libpq-fe.h"

 

#include "utils/geo_decls.h" /* for the POLYGON type */

 

 

void

 

exit_nicely(PGconn *conn)

 

{

 

PQfinish(conn);

 

exit(1);

 

}

 

 

main()

 

{

 

char *pghost,

 

*pgport,

 

*pgoptions,

 

*pgtty;

 

char *dbName;

 

int nFields;

 

int i,

 

j;

 

int i_fnum,

 

d_fnum,

 

p_fnum;

 

PGconn *conn;

 

PGresult *res;

 

 

/*

 

* begin, by setting the parameters for a backend connection if the

 

* parameters are null, then the system will try to use reasonable

 

* defaults by looking up environment variables or, failing that,

 

* using hardwired constants

 

*/

 

pghost = NULL; /* host name of the backend server */

 

pgport = NULL; /* port of the backend server */

 

pgoptions = NULL; /* special options to start up the backend

 

* server */

 

pgtty = NULL; /* debugging tty for the backend server */

 

 

dbName = getenv("USER"); /* change this to the name of your test

 

* database */

 

 

/* make a connection to the database */

 

conn = PQsetdb(pghost, pgport, pgoptions, pgtty, dbName);

 

 

/*

 

* check to see that the backend connection was successfully made

 

*/

 

if (PQstatus(conn) == CONNECTION_BAD)

 

{

 

fprintf(stderr, "Connection to database '%s' failed. ", dbName);

 

fprintf(stderr, "%s", PQerrorMessage(conn));

 

exit_nicely(conn);

 

}

 

 

/* start a transaction block */

 

res = PQexec(conn, "BEGIN");

 

if (!res || PQresultStatus(res) != PGRES_COMMAND_OK)

 

{

 

fprintf(stderr, "BEGIN command failed ");

 

PQclear(res);

 

exit_nicely(conn);

 

}

 

 

/*

 

* should PQclear PGresult whenever it is no longer needed to avoid

 

* memory leaks

 

*/

 

PQclear(res);

 

 

/*

 

* fetch rows from the pg_database, the system catalog of

 

* databases

 

*/

 

res = PQexec(conn, "DECLARE mycursor BINARY CURSOR FOR SELECT * FROM test1");

 

if (!res || PQresultStatus(res) != PGRES_COMMAND_OK)

 

{

 

fprintf(stderr, "DECLARE CURSOR command failed ");

 

PQclear(res);

 

exit_nicely(conn);

 

}

 

PQclear(res);

 

 

res = PQexec(conn, "FETCH ALL in mycursor");

 

if (!res || PQresultStatus(res) != PGRES_TUPLES_OK)

 

{

 

fprintf(stderr, "FETCH ALL command didn't return tuples properly ");

 

PQclear(res);

 

exit_nicely(conn);

 

}

 

 

i_fnum = PQfnumber(res, "i");

 

d_fnum = PQfnumber(res, "d");

 

p_fnum = PQfnumber(res, "p");

 

 

for (i = 0; i < 3; i++)

 

{

 

printf("type[%d] = %d, size[%d] = %d ",

 

i, PQftype(res, i),

 

i, PQfsize(res, i));

 

}

 

for (i = 0; i < PQntuples(res); i++)

 

{

 

int *ival;

 

float *dval;

 

int plen;

 

POLYGON *pval;

 

 

/* we hard-wire this to the 3 fields we know about */

 

ival = (int *) PQgetvalue(res, i, i_fnum);

 

dval = (float *) PQgetvalue(res, i, d_fnum);

 

plen = PQgetlength(res, i, p_fnum);

 

 

/*

 

* plen doesn't include the length field so need to

 

* increment by VARHDSZ

 

*/

 

pval = (POLYGON *) malloc(plen + VARHDRSZ);

 

pval->size = plen;

 

memmove((char *) &pval->npts, PQgetvalue(res, i, p_fnum), plen);

 

printf("tuple %d: got ", i);

 

printf(" i = (%d bytes) %d, ",

 

PQgetlength(res, i, i_fnum), *ival);

 

printf(" d = (%d bytes) %f, ",

 

PQgetlength(res, i, d_fnum), *dval);

 

printf(" p = (%d bytes) %d points boundbox = (hi=%f/%f, lo = %f,%f) ",

 

PQgetlength(res, i, d_fnum),

 

pval->npts,

 

pval->boundbox.xh,

 

pval->boundbox.yh,

 

pval->boundbox.xl,

 

pval->boundbox.yl);

 

}

 

PQclear(res);

 

 

/* close the cursor */

 

res = PQexec(conn, "CLOSE mycursor");

 

PQclear(res);

 

 

/* commit the transaction */

 

res = PQexec(conn, "COMMIT");

 

PQclear(res);

 

 

/* close the connection to the database and cleanup */

 

PQfinish(conn);

 

 

return 0;

 

}

 

 

 

 

 

 

  • 1
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值