27.3. 命令執行函數

一旦與數據庫服務器的連接成功建立,便可用這裡描述的函數執行 SQL 查詢和命令。

27.3.1. 主函數

PQexec

給服務器提交一條命令並且等待結果。

PGresult *PQexec(PGconn *conn, const char *command);

返回一個PGresult指針或者也可能是一個 NULL 指針。 通常返回一個非空(non-NULL)的指針, 除非沒有內存或發生了象不能把命令發送到服務器這樣的嚴重錯誤。 如果返回的是 NULL,它應該被當作PGRES_FATAL_ERROR結果處理。 用PQerrorMessage獲取有關錯誤的更多信息。

我們可以在命令行字串裡包含多個 SQL 命令(用分號分隔)。在一次 PQexec 調用中發送的多個查詢是在一個事務裡處理的,除非在查詢字串裡有明確的 BEGIN/COMMIT 命令用于把整個字串分隔成多個事務。請注意這樣返回的 PGresult 結構只描述字串裡執行的最後一條命令的結果。 如果有一個命令失敗,那麼字串處理的過程就會停止並且返回的 PGresult 會描述錯誤條件。

PQexecParams

向服務器提交一條命令並且等待結果,還有額外的傳遞與 SQL 命令文本獨立的參數的能力。

PGresult *PQexecParams(PGconn *conn,
                       const char *command,
                       int nParams,
                       const Oid *paramTypes,
                       const char * const *paramValues,
                       const int *paramLengths,
                       const int *paramFormats,
                       int resultFormat);

PQexecParams 類似 PQexec,但是提供了額外的功能: 參數值可以獨立于命令串進行聲明,並且可以要求查詢結果的格式是文本或者二進制格式。 PQexecParams 只是在協議 3.0 以及以後的版本中支持;在使用 2.0 的版本的時候會失敗。

如果使用了參數,那麼它們是以 $1$2,等等在命令字串中引用的。 nParams 是提供的參數的個數;它是數組 paramTypes[]paramValues[]paramLengths[],和 paramFormats[] 的長度。 (如果 nParams 是零,那麼數組指針可以是 NULL。) paramTypes[] 用 OID 的形式聲明了賦與參數符號的數據類型。 如果 paramTypesNULL,或者數組中任意元素是零, 那麼服務器給對應的參數符號賦與和無類型文本串一樣的數據類型。 paramValues[] 聲明該參數的實際數值。這個數組中的空指針意味著對應的參數是空; 否則,這個指針指向一個空零結尾的文本字串(文本格式)或者服務器期待的格式的二進制數據(用于二進制格式)。 paramLengths[] 聲明二進制格式參數的實際數據長度。 對于空參數和文本格式的參數會忽略這個參數。如果沒有二進制參數,那麼這個數組指針可以是空。 paramFormats[] 聲明某個參數是文本(在數組中放一個零)還是二進制(在數組中放一個1)。 如果這個數組指針是空,那麼所有參數都認為是文本的。 resultFormat 為零則獲取以文本方式返回的結果,為一則獲取以二進制形式返回的結果。 (目前不能規定從不同的字段獲取不同格式的結果,盡管對下層的協議是可能的。)

PQexecParamsPQexec 最主要的優勢是我們可以和命令串分開聲明參數值, 這樣就可以避免枯燥無聊並且很容易出錯的引起和逃逸。 和 PQexec 不同的是,PQexecParams 在一個給出的字串裡最多允許一個 SQL 命令。 (裡面可以有分號,但是不得超過一個非空的命令。)這是下層的協議的一個限制, 但是也有些額外的好處,比如可以有另外一層防止 SQL 注射攻擊的層次。

PQexecPrepared

發送一個請求,執行一個帶有給出參數的準備好的語句,並且等待結果。

PGresult *PQexecPrepared(PGconn *conn,
                         const char *stmtName,
                         int nParams,
                         const char * const *paramValues,
                         const int *paramLengths,
                         const int *paramFormats,
                         int resultFormat);

PQexecPreparedPQexecParams 類似, 但是要執行的命令是通過命名一個前面準備好的語句聲明的,而不是給出一個查詢字串。 這個特性允許那些要重復使用的命令只進行一次分析和規劃,而不是每次執行都來一遍。 PQexecPrepared 只在協議 3.0 和以後的版本裡支持;在使用 2.0 版本的協議的時候,它們會失敗。

參數和 PQexecParams 一樣,只是給出的是一個準備好語句的名字,而不是一個查詢字串, 並且沒有 paramTypes[] 參數(沒必要,因為準備好語句的參數類型是在創建的時候確定的)。

目前,和 PQexecPrepared 一起用的準備好語句必須通過執行 PREPARE 命令來建立,通常是用 PQexec 發送(當然,任何 libpq 的查詢提交函數都可以使用)。 在將來的版本裡可能會提供一個低層次的準備語句的接口。

PGresult 結構封裝了服務器返回的結果。libpq 應該小心維護 PGresult 的抽象。 使用下面的訪問函數獲取 PGresult 的內容。避免直接引用 PGresult 裡面的字段, 因為它們在未來版本裡可能會被修改。

PQresultStatus

返回命令的結果狀態。

ExecStatusType PQresultStatus(const PGresult *res);

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

PGRES_EMPTY_QUERY

發送給服務器的字串是空的

PGRES_COMMAND_OK

成功完成一個不返回數據的命令

PGRES_TUPLES_OK

成功執行一個返回數據的查詢查詢(比如 SELECT 或者 SHOW)。

PGRES_COPY_OUT

(從服務器)Copy Out (拷貝出)數據傳輸開始

PGRES_COPY_IN

Copy In (拷貝入)(到服務器)數據傳輸開始

PGRES_BAD_RESPONSE

服務器的響應無法理解

PGRES_NONFATAL_ERROR

發生了一個非致命錯誤(通知或者警告)

PGRES_FATAL_ERROR

發生了一個致命錯誤

如果結果狀態是 PGRES_TUPLES_OK, 那麼可以用下面的函數從查詢的返回中抽取元組信息。 注意一個碰巧檢索了零條元組的SELECT仍然顯示 PGRES_TUPLES_OKPGRES_COMMAND_OK用于不返回元組的命令(INSERTUPDATE,等)。 返回 PGRES_EMPTY_QUERY 的響應通常意味著暴露了客戶端軟件裡面的臭蟲。

狀態為 PGRES_NONFATAL_ERROR 的結果永遠不會直接由 PQexec 或者其它查詢執行函數返回;這類的結果會被傳遞給通知處理器(參閱 Section 27.9)。

PQresStatus

PQresultStatus返回的枚舉類型轉換成一個描述狀態碼的字符串常量。

char *PQresStatus(ExecStatusType status);

PQresultErrorMessage

返回與查詢關聯的錯誤信息,或在沒有錯誤時返回一個空字符串。

char *PQresultErrorMessage(const PGresult *res);

如果有錯誤,那麼返回的字串將包括一個結尾的新行。

緊跟在一個 PQexecPQgetResult 調用後面,PQerrorMessage (對連接)將返回與 PQresultErrorMessage (對結果)一樣的字符串。 不過,一個PGresult將保有其錯誤信息直到被刪除, 而連結的錯誤信息將在後續的操作完成時被改變。當你想知道與某個 PGresult相關聯的狀態時用 PQresultErrorMessage;當你想知道與連接的最近一個操作相關聯的狀態時用 PQerrorMessage

PQclear

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一樣。

27.3.2. 檢索查詢結果信息

這些函數用于從一個代表著成功查詢結果(也就是說,狀態為 PGRES_TUPLES_OK 的查詢) 的 PGresult 對象。對于其它狀態值的對象,他們的行為會好像他們有零行和零列一樣。

PQntuples

返回查詢結果裡的行(元組)個數。

int PQntuples(const PGresult *res);

PQnfields

返回查詢結果裡每個元組的數據域(字段)的個數。

int PQnfields(const PGresult *res);

PQfname

返回與給出的數據域編號相關聯的數據域(字段)的名稱。數據域編號從 0 開始

char *PQfname(const PGresult *res,
	    int column_number);

如果字段編號超出範圍,那麼返回NULL

PQfnumber

返回與給出的數據域名稱相關聯的數據域(字段)的編號。

int PQfnumber(const PGresult *res,
	      const char *column_name);

如果給出的名字不匹配任何字段,返回 -1。

給出的名字是當作 SQL 命令裡的一個標識符看待的,也就是說,如果沒有加雙引號, 那麼會轉換為小寫。比如,如果我們有一個從 SQL 命令裡生成的查詢結果

select 1 as FOO, 2 as "BAR";

那麼我們會有下面的結果:

PQfname(res, 0)              foo
PQfname(res, 1)              BAR
PQfnumber(res, "FOO")        0
PQfnumber(res, "foo")        0
PQfnumber(res, "BAR")        -1
PQfnumber(res, "\"BAR\"")    1

PQftable

返回我們抓取的字段所在的表的 OID。字段編號從 0 開始。

Oid PQftable(const PGresult *res,
             int column_number);

如果字段編號超出了範圍,或者聲明的字段不是一個指向某個表的字段的簡單引用, 或者使用了 3.0 版本之前的協議,那麼就會返回 InvalidOid。 你可以查詢系統表 pg_class 來判斷究竟引用了哪個表。

在你包含 libpq 頭文件的時候, 就會定義類型 Oid 和常量 InvalidOid。 他們都是相同的整數類型。

PQftablecol

返回組成聲明的查詢結果字段的字段號(在它的表內部)。結果字段號從 0 開始計數。

int PQftablecol(const PGresult *res,
                int column_number);

如果字段編號超出範圍,或者聲明的字段並不是一個表字段的簡單引用,或者使用的是 3.0 之前的協議,那麼返回零。

PQfformat

返回說明給出字段的格式的格式代碼。字段編號從 0 開始。

int PQfformat(const PGresult *res,
              int column_number);

格式碼為 0 表示文本數據,而格式碼是一表示二進制數據。(其它編碼保留給將來定義。)

PQftype

返回與給定數據域編號關聯的數據域類型。 返回的整數是一個該類型的內部 OID 號。數據域編號從0 開始。

Oid PQftype(const PGresult *res,
	    int column_number);

你可以查詢系統表 pg_type 以獲取各種數據類型的名稱和屬性。 內建的數據類型的 OID 在源碼樹的 src/include/catalog/pg_type.h 文件裡定義。

PQfmod

返回與給定字段編號相關聯的類型修飾詞。 字段編號從 0 開始。

int PQfmod(const PGresult *res,
	   int column_number);

類型修飾符的值是類型相關的;他們通常包括精度或者尺寸限制。數值 -1 用于表示"沒有可用信息"。 大多數數據類型不用修飾詞,這種情況下該值總是 -1。

PQfsize

返回與給定字段編號關聯的字段以字節計的大小。 字段編號從0 開始。

int PQfsize(const PGresult *res,
	    int column_number);

PQfsize返回在數據庫行裡面給該數據字段分配的空間, 換句話說就是該數據類型在服務器裡的二進制形式的大小(尺寸)。 (因此,這個對客戶端沒有什麼用。) 如果該數據域是可變尺寸,返回 -1。

PQbinaryTuples

如果PGresult包含二進制元組數據時返回 1, 如果包含 ASCII 數據返回 0。

int PQbinaryTuples(const PGresult *res);

這個函數已經廢棄了(除了還用于與 COPY 連接之外),因為我們可能在一個 PGresult 的某些字段裡包含文本數據,而另外一些字段包含二進制數據。 更好的是使用 PQfformatPQbinaryTuples 只有在結果中的所有字段都是二進制(格式 1)的時候才返回 1。

PQgetvalue

返回一個PGresult 裡面一行的單獨的一個字段的值。 行和字段編號從 0 開始。

char* PQgetvalue(const PGresult *res,
		 int row_number,
		 int column_number);

對于文本格式的數據, PQgetvalue 返回的值是一個表示字段值的空(NULL)結尾的字符串。 對于二進制格式, 返回的值就是由該數據類型的 typsendtypreceive 決定的二進制表現形式。 (在這種情況下,數值實際上也跟著一個字節零,但是通常這個字節沒什麼用處,因為數值本身很可能包含內嵌的空。)

如果字段值是空,則返回一個空字串。參閱 PQgetisnull 來區別空值和空字串值。

PQgetvalue 返回的指針指向一個本身是 PGresult結構的一部分的存儲區域。我們不能更改它, 並且如果我們要在PGresult結構的生存期後還要使用它的話, 我們必須明確地把該數值拷貝到其他存儲器中。

PQgetisnull

測試一個字段是否為空(NULL)。行和字段編號從 0 開始。

int PQgetisnull(const PGresult *res,
		int row_number,
		int column_number);

如果該域包含 NULL,函數返回 1,如果包含非空(non-null )值,返回 0。 (注意,對一個 NULL 數據域,PQgetvalue 將返回一個空字符串, 不是一個空指針。)

PQgetlength

返回以字節計的字段的長度。行和字段編號從 0 開始。

int PQgetlength(const PGresult *res,
		int row_number,
		int column_number);

這是某一特定數據值的實際數據長度。 行和字段編號從 0 開始。

int PQgetlength(const PGresult *res,
                int row_number,
                int column_number);

這是特定數值的實際數據長度,也就是說,PQgetvalue 指向的對象的大小。 對于文本數據格式,它和 strlen() 相同。對于二進制格式,這是潛在的信息。 請注意我們應該依靠 PQfsize 獲取實際數據長度。

PQprint

向指定的輸出流打印所有的行和(可選的)字段名稱。

void PQprint(FILE* fout,      /* 輸出流 */
	     const PGresult *res,
	     const PQprintOpt *po);

struct {
    pqbool  header;      /* 打印輸出域頭和行計數 */
    pqbool  align;       /* 填充對齊各字段 */
    pqbool  standard;    /* 舊的傻格式 */
    pqbool  html3;       /* 輸出 HTML 表 */
    pqbool  expanded;    /* 擴展表 */
    pqbool  pager;       /* 必要時在輸出中使用分頁器 */
    char    *fieldSep;   /* 字段分隔符 */
    char    *tableOpt;   /* 在 HTML 中插入 table ... */
    char    *caption;    /* HTML caption */
    char    **fieldName; /* 替換字段名組成的空零結尾的數組 */
} PQprintOpt;

這個函數以前被 psql 用于打印查詢結果,但是現在已經不用這個函數了。請注意它假設所有的數據都是文本格式。

27.3.3. 檢索其它命令的結果信息

這些函數用于從 PGresult 對象裡檢索那些非 SELECT 結果的信息。

PQcmdStatus

返回產生PGresult的 SQL 命令的命令狀態字符串。

char * PQcmdStatus(PGresult *res);

通常這只是命令的名字,但是它可能包括額外的數據,比如處理過的行數。

PQcmdTuples

返回被 SQL 命令影響的行的數量。

char * PQcmdTuples(PGresult *res);

如果產生PGresultSQL 命令是 INSERTUPDATEDELETEMOVE,或者 FETCH, 那麼這裡返回涉及行的行數。如果是其他命令返回一個空字符串。

PQoidValue

返回一個插入的元組的對象標識(OID)── 如果SQL 命令是INSERT,而且剛好向帶 OID 的表插入了一行。否則,返回 InvalidOid

Oid PQoidValue(const PGresult *res);

PQoidStatus

如果 SQL 命令是INSERT, 返回一個被插入的元組的對象ID的字串。 (如果 INSERT 並非恰好插入一行,或者目標表沒有OID,那麼字串將是 0。) 如果命令不是INSERT,則返回一個空字串。

char * PQoidStatus(const PGresult *res);

這個函數已經廢棄樂,因為有了 PQoidValue,而且它也不是線程安全的。

27.3.4. 逃逸包含在 SQL 命令中的字串

PQescapeString為在 SQL 命令中使用字串而對之進行逃逸處理。 在我們向 SQL 命令裡把數據值當作文本常量插入的時候很有用。有些字符(比如單引號和反斜槓)必須被逃逸, 以避免他們被 SQL 分析器作為特殊字符解析。

提示: 如果我們從一個不可信的來源收到一個字串的話,那麼做恰當的逃逸就更重要了。 否則就有安全性危險:你會收到"SQL 注射"攻擊,這個時候會有你不想看到的 SQL 喂給你的數據庫。

請注意,如果一個數據值是作為 PQexecParams 或者同族函數的一個獨立參數傳遞的, 那麼逃逸就既不必要,也不正確。

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

參數 from 指向將要逃逸的字串的第一個字符, length 參數給出在這個字串裡的字符數量。字串結尾的字節零不是必須的,也不計入 length。 (如果在處理 length 個字節之前出現了一個字節零, 那麼 PQescapeString 在這個字節零處停止; 這個行為類似 strncpy。) to 應該指向一個緩衝區,這個緩衝區至少能保存 length 數值的兩倍還多一個的字符,否則該函數行為將不可預測。 調用 PQescapeString 就會把逃逸的 from 字串轉換到 to 緩衝區,把特殊字符以免它們導致任何問題, 並且追加終止的字節零。那些必須包圍在PostgreSQL 字串文本週圍的單引號不算結果字串的一部分;你應該在把單引號放在插入這個處理結果的SQL命令週圍。

PQescapeString 返回寫到 to 裡面的字符數目, 不包括結尾的字節零。

如果 tofrom 字串相互重疊,那麼其行為不可預測。

27.3.5. 逃逸包含在 SQL 命令中的二進制字串

PQescapeBytea

逃逸那些在 SQL 命令中使用的用 bytea 表示的二進制數據。 和 PQescapeString 一樣,這個函數只有在直接向 SQL 字串插入數據的時候使用。

unsigned char *PQescapeBytea(const unsigned char *from,
				 size_t from_length,
				 size_t *to_length);

SQL 語句中用做 bytea 字串文本的一部分的時候, 有些字節值必需逃逸(但是對于所有字節而言是可以逃逸)。 通常,要逃逸一個字節,它是被轉換成一個三位八進制數字, 該數字數值等于該字節的數值,然後前綴兩個反斜扛。 單引號(')和反斜扛字符(\)有自己特殊的逃逸序列。參閱 Section 8.4 獲取更多信息。 PQescapeBytea 執行這個操作,它只逃逸需要逃逸的最少的字符。

from 參數指向需要逃逸的字串的第一個字節, from_length 參數反映在這個二進制字串(結尾的字節零既不必要也不計算在內的字串)裡字節的個數。 to_length 參數應該是一個指向某個緩衝區的指針, 它的空間應該能夠保存逃逸後的結果字串長度。 結果字串長度不包括結果結尾的字節零。

PQescapeBytea 在內存重返回一個 from 參數的二進制字串的逃逸後的版本,這片內存是用 malloc() 分配的, 在結果不再需要的時候,必須用 PQfreemem() 釋放。 返回的字串已經把所有特殊的字符替換掉了,這樣他們就可以由 PostgreSQL的字串文本分析器以及 bytea 的輸入函數正確地處理。 同時還追加了一個結尾的字節零。那些必需包圍在 PostgreSQL字串文本週圍的單引號不算結果字串的一部分。

PQunescapeBytea

把一個二進制數據的逃逸後的字串表現形式轉換成二進制數據 --- PQescapeBytea 的反作用。 在以文本格式抽取 bytea 數據的時候是必須的, 但是在以二進制格式抽取的時候是不必要的。

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

from 參數指向一個逃逸後的字串, 比如 PQgetvalue 處理過一個 bytea 字段後返回的。PQunescapeBytea 把它的字串表現形式轉換成二進制形式, 它返回一個用 malloc() 分配的指向該緩衝區的指針, 或者是出錯時返回空, 緩衝區的尺寸放在 to_length 裡。 在不再需要這個結果之後,這片內存必須用 PQfreemem() 釋放。

PQfreemem

釋放 libpq 分配的內存。

void PQfreemem(void *ptr);

釋放由 libpq 分配的內存, 特別是 PQescapeByteaPQunescapeBytea, 和 PQnotifies。這是 Microsoft Windows 必須的, 因為它不能跨越 DLL 釋放內存,除非使用了多線程的 DLL (VC6 中的 /MD)。 在其它平台上,這個函數和標準的庫函數free()一樣。