使用libpq調用PostgreSQL的自定義函數


  在我的項目中,連接oracle數據庫並執行各種增刪改查操作,主要是通過oracle的存儲過程,這比直接執行SQL語句要簡單並靈活多變。因為項目需要,要遷移到PostgreSQL下,因為考慮到各個平台的兼容性,采用libpq庫來達到目的,在開發的過程中碰到了一些問題,在這里記錄一下。

  業務需求:pg中有個照片表,需要將照片信息及數據插入到該表中。並可能伴隨增刪改查動作。本節只處理插入操作。

  照片表創建:

1 CREATE TABLE ist_image
2 (
3   isc_imgid bigint NOT NULL,
4   isc_imgname character varying(64),
5   isc_updatetime timestamp without time zone,
6   isc_id bigint, -- 插入順序編號(由序列生成)
7   isc_imgdata bytea,
8   CONSTRAINT ist_image_pkey PRIMARY KEY (isc_imgid)
9 )

  自定義函數創建:

 1 create or replace function func_insertimage(in nImgID bigint, in szImgName varchar, in pImgData bytea) 
 2 returns integer     --必須有返回值
 3 as 
 4 $$
 5 begin
 6     insert into ist_image(isc_imgid, isc_imgname, isc_updatetime, isc_id, isc_imgdata) 
 7     values(nImgID, szImgName, now()::timestamp(0), nextval('iss_seq_isc_id'), pImgData);
 8     return 0;
 9 end;
10 $$
11 language plpgsql;

  照片數據結構:

1 class ImageInfo {
2 public:
3     __int64        nImgNumber;
4     char        szImgName[64];
5     int            nImgDataLen;
6     unsigned char*    pImgData;
7 }

  插入函數:

 1 //注意該函數是有BUG的
 2 unsigned long long htonll(unsigned long long val) {  
 3     return (((unsigned long long )htonl((int)((val << 32) >> 32))) << 32) | (unsigned int)htonl((int)(val >> 32));   
 4 }
 5 
 6 int GP_TestInsert(deque<ImageInfo>& deqImages, char* szConnStr) {
 7     int nParamNum = 3;                    //參數個數
 8     int paramLens[3] = {0};                //參數長度
 9     int paramFormats[3] = {1, 1, 1};    //參數是二進制格式(1表示二進制格式 0表示文本格式)
10     int nReturnForm = 0;                //返回值是文本格式
11     unsigned long long ullHID = 0, ullNID = 0;
12 
13     //函數調用語句
14     TCHAR szSQL[1024] = {0};
15     _stprintf_s(szSQL, 1024, _T("select func_insertimage($1::bigint, $2::character varying, $3::bytea)"));
16     
17     //連接GP數據庫
18     PGConn* pConn = PQconnectdb(szConnStr);
19     if (CONNECTION_OK != PQstatus(pConn)) {
20         printf("GP_TestInsert Connect failed. ErrMsg: %s", PQerrorMessage(pConn));
21         return -1;
22     }
23 
24     //開始批量插入事務(顯式BEGIN會開始一個事務)
25     PGresult* pRes = PQexec(pConn, "BEGIN");
26     if (PQresultStatus(pRes) != PGRES_COMMAND_OK) {
27         printf("GP_TestInsert Exec BEGIN command failed. ErrMsg: %s", PQerrorMessage(pConn));
28         PQclear(pRes);
29         return -1;
30     }
31     PQclear(pRes);    //任何時候不再需要 PGresult 時,應該PQclear它來避免內存泄露
32 
33     //循環插入數據
34     for (deque<ImageInfo>::iterator it = deqImages.begin(); it != deqImages.end(); ++it) 
35     {
36         ullHID = (unsigned long long)(it->nImgNumber);
37         ullNID = htonll(ullHID);
38 
39         //value
40         const char* const pszParamValue[3] = { (char*)&ullNID, 
41                                                 it->szImgName, 
42                                                 (char*)it->pImgData };
43 
44         //Length
45         paramLens[0] = sizeof(__int64);
46         paramLens[1] = (int)strlen(it->szImgName);    //字符串類型長度要注意
47         paramLens[2] = it->nImgDataLen;
48 
49         //execse
50         pRes = PQexecParams(pConn, szSQL, 3, NULL, (const char**)pszParamValue, paramLens, paramFormats, nReturnForm);
51         ExecStatusType resState = PQresultStatus(pRes);
52         if (PGRES_TUPLES_OK != resState || strcmp(PQgetvalue(pRes, 0, 0), "0") != 0) {
53             printf("GP_TestInsert Exec function failed. ErrCode: %d ErrInfo: %s ErrDesc: %s"), 
54                 resState, PQresStatus(resState), PQresultErrorMessage(pRes));
55             return -1;
56         }
57 
58         PQclear(pRes);
59     }
60 
61     //結束事務(做完此步才會進行commit)
62     pRes = PQexec(pConn, "END");
63     PQclear(pRes);
64 
65     return 0;
66 }

  這里面有幾個要注意的地方:

  1.每次連接、執行SQL語句要注意檢查狀態,判斷是否執行成功;

  2.對於整形數據的插入,要進行字節序轉換,並根據整形結構的長度區別使用不同的字節序轉換函數;

  3.PostgreSQL數據庫好像是默認開啟AutoCommit的,若不想其自動commit,可以采用以下兩種辦法:

    1)關閉數據庫的自動commit屬性,不過這種方式會導致管理時,出現一些煩人的提示;

    2)每次顯式調用BEGIN命令,一個事務(很多次條語句執行過后)結束后,執行END命令來commit,這種方式更靈活,個人更喜歡這種方式;

  4.任何時候不再需要PGresult時,應該調用PQclear來清理它,避免內存泄露,具體請參見libpq說明文檔;

  當然,使用insert方式插入數據,對於PostgreSQL來說,並不是高效的方式,這里只是用這個例子來簡單說明使用libpq庫調用自定義函數的方式,后續再補充使用更高效方式來插入數據的例子,對於PostgreSQL來說,我也僅僅是初學者,若文中有錯誤煩請指正。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM