任意语言访问PostgreSQL:C语言接口

转载:http://tech.it168.com/a2012/0311/1323/000001323168.shtml

【IT168专稿】作为一名系统管理员,可能每天都要通过某段程序连接到数据库。开源软件的一个好处是可以根据需要修改程序代码。如果程序后台使用PostgreSQL数据库,可以很容易用各种语言对其进行访问。本文作为系列文章的第一篇,会陆续介绍C、C++、PHP、Tcl、Python及Perl等编程语言对PostgreSQL的访问方法。

  需要说明的是,本文不太适合完全没有编程经验的读者。不过只要读者有基本的SQL知识,以及懂得至少一种编程语言即可看懂。也许读者正在使用的系统是Linux,但是只要使用的客户端API接口在其他系统可用,则程序代码不需更改即可在其他操作系统上正常使用。本文所示的代码全部基于Postgre 9.1,是目前最新的稳定版本,已经安装并经过测试。在本文的操作环境中,客户端与服务端位于不同的计算机,但位于同一局域网内,这种情况下可以在客户端通过命令gpsql -U postgre -h x.x.x.x很容易地连接到服务端。

  首先要确保在客户端机器上有安装软件的权限,并且最好确保当前使用的数据库只做当前测试用,以免任何因不小心的操作导致重要数据的损坏。

  C语言接口

  在与数据库连接方面,C语言是一种常用语言,许多数据库都是用C写的。该语言高效灵活,因此如果想要写一款客户端接口,不管是仅仅包含控制台还是基于GUI的,并且不想用到浏览器,C语言也许是最好的选择。

  连接C语言与Postgres使用的库名为libpq,它与PostgreSQL源码树绑定在一起。如果安装数据库系统时使用的是二进制文件而不是从源码编译安装,libpq可被单独安装,但也要记得需要使用选项-dev package(或-devel,取决于Linux系统的版本)。在Debian及其分发版上,安装libpq的命令为 #aptitude install libpq-dev。在基于RedHat的系统上,如CentOS,可以在postgresqlxx-libs包中找到libpq,其中xx为主要和次要版本号。因此要连接到PostgreSQL 9.1数据库,如果使用Fedora16,则需要安装postgresql91-lib或postgresql-devel。由于RHEL/CentOS用户大多使用Postgres库,导致程序的名称可能会有一些不同,而且由于Fedora用户能够使用更多最新的包,因此仅仅在使用一个旧版本时才需要第三方库。简而言之,任何支持PostgreSQL的系统均有其可用的libpq。

  除了C语言,libpq同时也是C++、Perl和Tcl的API引擎,提供基本的函数对数据库进行连接、查询和修改。许多常用的函数都包含“PQ”前缀,如PQconnectdb或PQerrormessage。更多示例可查看PostgreSQL文档或查看src/test/examples。在C程序文件中,包含libpq-fe.h头文件并在编译时添加相应的链接标记-lpq。

  安装并设置好之后,要做的第一件事就是连接数据库。PQconnectdb()函数带一个char *conninfo格式的参数,参数格式如dbname=[database_name],当然也可以是其他内容,只要格式对即可,最常用的关键字为host,hostaddr(numeric格式,以避免无用的DNS查询),port,user,password与sslmode。如果不使用参数,则会使用默认选项。假设服务器地址为192.168.0.101,username为postgres,数据库名为testdb1,尝试连接数据库的代码如下所示:

#include < stdio.h >
#include
< libpq - fe.h >
int main( int argc, char argv[])
{
const char * conninfo;
PGconn
* conn;
if (argc > 1 )
conninfo
= argc[ 1 ];
else
{
printf(
" Not enough arguments,exiting... " );
return 1 ;
}
conn
= PQconnectdb(conninfo);
/* Check to see how I did */
if (PQstatus(conn) = CONNECTION_OK)
printf(
" Connection succeeded.\n " );
else
{
/* Do something to deal with the error */
}
}

  将代码保存为testlibpq并编译,编译后的程序用法如下:

$ testlibpg " hostaddr=192.168.0.101 user=postgres dbname=testdb1 "

  如果不出错误,会在屏幕上看到提示“Connection succeeded.”,表示连接数据库成功,不过上文并没有什么实际用处。那么何为实际用处呢----对数据进行查询,但这里先介绍如何断开连接,即调用PQfinish,该函数只有一个参数PGconn *conn,并返回void。

  PQexec函数执行查询,参数为PGconn *conn与const char *command,返回一个PGresult类型的对象。在如下示例中,声明一个PGresult变量,并向服务器发送一个命令。读者可自行编写检查连接与否以及错误处理的代码。

  PGresult * res;

  res
= PQexec(conn, " SELECT * FROM mydatabase " );

  PQclear(res);

  显然这段代码无法编译,只是为了向读者展示libpq库提供的功能,而不是直接提供可用代码。不过在这段代码中,res包含了查询结果,读者可以任意对其进行解析。PQresultStatus可以查询命令的状态,该函数返回PGRES_COMMAND_OK或 PGRES_FATAL_ERROR.。可在PostgreSQL项目页面找到exec函数的一个综合列表。

  下面介绍一些有用的函数,例如,PQntuples函数将给定的res作为参数,以整数类型返回表中列的数量。如果查询的状态为PGRES_TUPLES_OK,则它以PGresult对象为参数,并返回一个整型值。PQnfields函数给出每行的列数。PQfname函数返回与某数字关联的列的名字,PQfnumber函数功能则完全相反。要得到某一单元的值,需要将PGresult以及单元的列号和行号传给PQgetvalue函数。

  可以看到这些函数都比较简单,但PQexec不能同时处理多个SQL命令,因为该函数只能返回一个结构,如果有多个命令,则只能返回最后一个命令的结果。另一个不足是,PQexec在执行一个命令时会一直等到命令返回,因此读者在使用该命令遇到阻塞执行时要分外小心。如果这些不足影响到读者用户的使用,可以使用其它函数代替,如PQsendQuery和PQgetResult等,可使用这些函数来进行异步查询处理。

【IT168技术】在之前的文章《任意语言访问PostgreSQL:C语言接口》中,介绍了如何使用C语言访问PostgreSQL,今天将介绍如何使用C++访问PostgreSQL。

  官方PostgreSQL客户端的C++接口API称为libpqxx,但没有与PostgreSQL源码进行绑定,用户可以从产品分发库或单独下载进行安装。README文件提到用户可以在Linux、BSD、Solaris、Irix、HP-UX、AIX和安装Cygwin的Windows上使用libpqxx。安装libpqxx前要先安装libpq,因为前者是基于后者的。

  在C++中,namespace被命名为pqxx,用户必须包含以该名称命名的头文件。以下为一个简单连接与查询的例子:

#include < iostream >
#include
< pqxx / pqxx >

using namespace std;

int main()
{
pqxx::connection conn;
pqxx::work w(conn);
pqxx::result res
= w.exec( " SELECT 1 " );
w.commit();

cout
<< res[ 0 ][ 0 ]. as < int > () << endl;
}

  如果运行正确,则该程序会打印出“1”,可以使用try/catch关键字来处理连接中可能出现的错误。如果要像在C语言中那样使用argv[x],可以像如下方法连接字符串:

pqxx::result res = w.exec( " SELECT " + w.quote(argv[ 1 ]));

  那么,该如何对数据进行查询与返回呢?由于保存数据的res变量是一个数组,因此可以利用循环来读取res中的数据元素:

for ( int rownr = 0 ; rownr < res.size(); ++ rownr)
{
const result::tuple row = res[rownr];
for ( int colnr = 0 ; colnr < row.size(); ++ colnr)
{
const result::field = row[colnr];
cout
<< field.c_str() << \t;
}
cout
<< endl;
}

  要记得在写代码的时候加上using namespace std;,如果不加的话在编译时会在使用cout与endl的行显示警告。

  libpqxx在字符串转换方面提供大量函数,最有意思的函数为from_string与to_string,第一个函数只有一个字符串参数和一个 T& obj参数,T表示已存在的内置类型,第二个函数只有一个T& obj变量,函数将其转换为字符串。更多详细内容可以阅读相关文档。

  连接,查询与事务处理

  本节的其余部分代码,读者最好使用using namespace pqxx;,因为可以在写代码时免去写一些数据类型,省去一些不必要的麻烦,如可以不必写pqxx::...。现在连接一个数据库,执行一次事务处理,之后进行一次查询,读者会看到代码与前文的C代码不太一样。

  可以使用只包含一个参数的结构,如定义了连接选项(数据库名、用户名等)的字符串。由于默认用户名为postgres,因此本例代码没有包含用户名:

connection Conn( " dbname=testdb1 hostaddr=192.168.0.101 " );

  现在已经打开了一个连接,执行没有出错。不过到目前为止还不能进行查询,还需要使用transactor来打开一个事务处理,这样可以在连接突然断开时省去一些麻烦与时间。transactor是一个functor,如果在创建连接时需要知道连接的状态,则该运算符非常有效,而不能仅仅使用简单的函数或函数指针。

  functor是一个函数对象,可以将其看成一个状态查询函数。例如在C++中操作符“()”可以被重载,因此可以将任意数量的数据元素放到括号中。也许有读者会认为functor是定义操作符()的类,如果想要设计一个函数使两个给定的作为参数的值相加,则必须要对这些值进行硬编码。functor考虑到硬编码的限制,允许用户使用一个结构体,并将一个新对象“覆盖”要相加的第一个值,给开发者更多的灵活性。

  下面看如何使用transactor:

  // 假设已经打开一个连接

class Transaction : public transactor <>
{
public :
void operator () (transaction <> & t)
{
t.exec (
" INSERT INTO mytable VALUES(val1,val2) " );

}

void on_abort ( const string & msg)
{
cout
<< " Transaction failed with message: " << msg << endl;
}
};

  //在 main()函数中

conn.perform (Transaction());
  通过本节内容可以看到,用C++代码对数据库进行查询并不难,用户可以通过定义事务处理(transactions)并适当规范化查询结果(参考stringstream),其余部分可以使用循环来实现。



确保已经安装了PHP及其PostgreSQL模块,在Debian系统上该模块名为php5-pgsql,CentOS系统上该模块名为php-pgsql。如果是从源码编译,则编译时要带上--with-pgsql=[DIR]配置选项,DIR为postgres存放的位置。可以在php页面上使用phpinfo()函数检查是否已经打开PostgreSQL支持,以及什么选项具有什么值。注意下面的例子运行在PHP5环境下,不一定使用旧版本。

  与使用C/C++相比,使用PHP打开一个与数据库的连接、查询数据并打印结果更简单容易:

< ?php
$dbconn
= pg_connect("host = 192.168 . 0.101 dbname = testdb1 user = postgres password = xxx")
or die( ' Could not connect: ' . pg_last_error());
$query
= ' SELECT * FROM mytable ' ;
$result
= pg_query($query) or die( ' Query failed: ' . pg_last_error());
echo "
\n";
while ($line = pg_fetch_array($result, null ,PGSQL_ASSOC)) {
echo "\t
\n";
foreach ($line
as $col_value) {
echo "\t\t
$col_value \n";
}
echo "\t \n";
}
echo "

\n";
// I printed the result in a nice table ; thanks to the PHP manual
pg_free_result($result);
pg_close($dbconn);
?
>

  如读者所见,连接、查询及使用结果都很简单,不过需要注意的是,当交互地使用用户名/密码信息时,可能会产生SQL注入攻击来恶意访问数据库,因此务必要特别注意可能引起的相关安全问题。

  使用数组时,可以先按如下方式使用pg_fetch_array():

   // 假设已经连接数据库,并返回适当的(查询)结果
  $arr
= pg_fetch_array($result);
  echo $arr
[ 0 ] ;
  读者可以查看其他pg_fetch*函数并选择合适的函数。如果想要根据查询结果的大小改变显示的方式,则可以使用pg_num_fields和pg_num_rows两个函数。

相关文章

文章浏览阅读601次。Oracle的数据导入导出是一项基本的技能,...
文章浏览阅读553次。开头还是介绍一下群,如果感兴趣polardb...
文章浏览阅读3.5k次,点赞3次,收藏7次。折腾了两个小时多才...
文章浏览阅读2.7k次。JSON 代表 JavaScript Object Notation...
文章浏览阅读2.9k次,点赞2次,收藏6次。navicat 连接postgr...
文章浏览阅读1.4k次。postgre进阶sql,包含分组排序、JSON解...