1. 程式人生 > >循序漸進PostgreSQL: 學習pqxx C++ API訪問

循序漸進PostgreSQL: 學習pqxx C++ API訪問

1. 簡介

習慣了c++來開發的人,可能更傾向於使用c++庫來訪問PG。libpqxx很早以前就推出了。目前已經到了4.0版。本文就簡單的介紹如何使用它,linux平臺編譯相對簡單,這裡就介紹一下windows平臺下的使用。

由於libpqxx對libpq的C-API進行了很好的封裝,從而可以節省大量的編碼時間。

2. 編譯過程

將libpqxx解壓到一固定目錄。你需要提前編譯好或安裝好的客戶端庫,通常為了方便,這裡假定你已經有一個PG的安裝版或者解壓縮版,在c:\pgsql裡頭。以PG9.x為例。

1. 進入libpqxx原始碼目錄,備份並修改win32\common檔案,將PGSQLSRC的值指向正確的地方:

    這裡是PGSQLSRC="c:\pgsql"

    後邊還有很多目錄位置需要調整,針對我們這種情況,通常都是保留安裝版本的設定,如:LIBPQINC=$(PGSQLSRC)\include, 而註釋掉#LIBPQINC=$(PGSQLSRC)\interfaces\libpq, 其它幾個值都如此類推。

2. 拷貝一些編譯相關的標頭檔案

    針對具體版本,9.0的,將config\sample-headers\libpq\9.0\pqxx複製到include下邊

    針對VS2008的,將config\sample-headers\compiler\VisualStudio2008\pqxx複製到include目錄下邊, 如果是VS2005,以此類推。

3. 編譯

    program files->VS2008-->Visual studio tools->Visual Studio 2008 Command Prompt, 進入命令列, 在此進入libpqxx的原始碼根目錄,執行:

    nmake /f win32\vc-libpqxx.mak ALL,

    得到如下中間過程:

        link.exe kernel32.lib ws2_32.lib advapi32.lib /nologo /dll /machine:I386
 shell32.lib secur32.lib wldap32.lib /libpath:"C:\hisql-x86-2.0.1"\lib libpq.lib
  "ObjDllRelease\binarystring.obj"  "ObjDllRelease\connection.obj"  "ObjDllRelea
se\connection_base.obj"  "ObjDllRelease\cursor.obj"  "ObjDllRelease\dbtransactio
n.obj"  "ObjDllRelease\errorhandler.obj"  "ObjDllRelease\except.obj"  "ObjDllRel
ease\field.obj"  "ObjDllRelease\largeobject.obj"  "ObjDllRelease\nontransaction.
obj"  "ObjDllRelease\notification.obj"  "ObjDllRelease\notify-listen.obj"  "ObjD
llRelease\pipeline.obj"  "ObjDllRelease\prepared_statement.obj"  "ObjDllRelease\
result.obj"  "ObjDllRelease\robusttransaction.obj"  "ObjDllRelease\statement_par
ameters.obj"  "ObjDllRelease\strconv.obj"  "ObjDllRelease\subtransaction.obj"  "
ObjDllRelease\tablereader.obj"  "ObjDllRelease\tablestream.obj"  "ObjDllRelease\
tablewriter.obj"  "ObjDllRelease\transaction.obj"  "ObjDllRelease\transaction_ba
se.obj"  "ObjDllRelease\tuple.obj"  "ObjDllRelease\util.obj"  "ObjDllRelease\lib
pqxx.obj" /out:"lib\libpqxx.dll" /implib:"lib\libpqxx.lib"
   Creating library lib\libpqxx.lib and object lib\libpqxx.exp
   最終會在lib目錄下生成:
D:\Projects\hisql.svn\trunk\learning\libpqxx-4.0>dir/b/s lib
D:\Projects\hisql.svn\trunk\learning\libpqxx-4.0\lib\libpq.dll
D:\Projects\hisql.svn\trunk\learning\libpqxx-4.0\lib\libpq.lib
D:\Projects\hisql.svn\trunk\learning\libpqxx-4.0\lib\libpqxx.dll
D:\Projects\hisql.svn\trunk\learning\libpqxx-4.0\lib\libpqxx.dll.manifest
D:\Projects\hisql.svn\trunk\learning\libpqxx-4.0\lib\libpqxx.exp
D:\Projects\hisql.svn\trunk\learning\libpqxx-4.0\lib\libpqxx.lib
D:\Projects\hisql.svn\trunk\learning\libpqxx-4.0\lib\libpqxxD.dll
D:\Projects\hisql.svn\trunk\learning\libpqxx-4.0\lib\libpqxxD.dll.manifest
D:\Projects\hisql.svn\trunk\learning\libpqxx-4.0\lib\libpqxxD.exp
D:\Projects\hisql.svn\trunk\learning\libpqxx-4.0\lib\libpqxxD.ilk
D:\Projects\hisql.svn\trunk\learning\libpqxx-4.0\lib\libpqxxD.lib
D:\Projects\hisql.svn\trunk\learning\libpqxx-4.0\lib\libpqxxD.pdb
D:\Projects\hisql.svn\trunk\learning\libpqxx-4.0\lib\libpqxx_static.lib
D:\Projects\hisql.svn\trunk\learning\libpqxx-4.0\lib\libpqxx_staticD.lib
D:\Projects\hisql.svn\trunk\learning\libpqxx-4.0\lib\msdia80.dll

3. 簡單示例

下邊來看看最簡單的示例, 我們先建立示例使用者foo, 密碼為foo1, 並建立一個簡單的表t (id, col), 插入一條包含中文的記錄,編碼,可以看出,客戶端為gbk。

iihero=# create user foo password 'foo1';
CREATE ROLE

iihero=> create table t(id int primary key, col2 varchar(32));
注意:  CREATE TABLE / PRIMARY KEY 將要為表 "t" 建立隱含索引 "t_pkey"
CREATE TABLE
iihero=> \encoding
GBK
iihero=> insert into t values(1 ,'不同型別的列表');
INSERT 0 1
iihero=> select * from t;
 id |      col2
----+----------------
  1 | 不同型別的列表
(1 行記錄)

將編譯出來後的include, lib目錄提取到固定的目錄,與你原來的postgresql的include和lib目錄,可以合到一起。給開發使用。

下邊是一個最簡單的示例程式碼,只為體會一下連線及取一行最簡單的結果(1):

#include <iostream>

#include <pqxx/pqxx>

int main()
{
	pqxx::connection conn("dbname=iihero hostaddr=127.0.0.1 user=foo password=foo1" );
	if(conn.is_open())
	{
		std::cout << "Connection succesful!" << std::endl;
		std::cout << conn.options()<<std::endl;
	}
	else
	{
		std::cout << "Something went wrong... oops" << std::endl;
	}
	pqxx::work w(conn);
    pqxx::result res = w.exec("SELECT 1");
    w.commit();

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


需要注意的是,要將libpq.lib, libpqxx.lib, 以及標頭檔案都設定上。如果動態庫連結方式出現問題,可以嘗試使用libpqxx的靜態庫。

上邊的pqxx::connection物件, 要傳入一個字串作為連線串,這個連線串要求以空格將關鍵字串隔開,這與libpq的連線串是一致的。

dbname=<database> hostaddr=<IP地址或主機名> user=<usrename> password=<password>

關鍵字不能弄錯。否則會報異常。當然,上邊的程式碼寫的非常不規範,只是作為演示。正常情況下,需要加入異常處理。

好,下邊我們將程式碼稍改進一下。

4. 改進的程式碼

#include <iostream>

#include <pqxx/pqxx>


int main()
{
	try 
	{
		pqxx::connection conn("dbname=iihero hostaddr=127.0.0.1 user=foo password=foo1" );
		if(conn.is_open())
		{
			std::cout << "Connection succesful!" << std::endl;
			std::cout << conn.options()<<std::endl;
		}
		else
		{
			std::cout << "Something went wrong... oops" << std::endl;
		}
		pqxx::work w(conn);
		pqxx::result res = w.exec("SELECT 1");
		// w.commit();

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

		// 接著示例 訪問表t中的資料, 先設定client端的字符集編碼, 否則會出現亂碼
		conn.set_client_encoding("GBK");
		pqxx::result r = w.exec("SELECT * FROM t");

		for (pqxx::result::const_iterator row = r.begin(); row != r.end(); ++row)
		{	 
			for (pqxx::tuple::const_iterator field = row->begin();
				field != row->end();
				++field)
				std::cout << field->c_str() << '\t';
			std::cout << std::endl;
		}

	}
	catch (std::exception& e) 
	{
		std::cerr << e.what() << std::endl;
		return 1;
	}
	return 0;
}

上邊,我們看到,需要設定客戶端的編碼。通過一個迭代器來訪問一個查詢中的資料。

需要說明的是,libpqxx的線上文件寫的非常的爛,示例程式碼居然是錯誤的。裡邊,

pqxx::result::tuple::const_iterator
壓根就不能編譯。應該是pqxx::tuple.
<script type="text/javascript" src="http://pagead2.googlesyndication.com/pagead/show_ads.js"></script>