任意語言訪問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兩個函數。



發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章