libpq SDK 發(fā)送 SQL 和解析結(jié)果
本次技術(shù)貼將詳細(xì)講解:當(dāng)用戶建立連接后,如何發(fā)送 SQL 語句、獲取結(jié)果對象、查看錯(cuò)誤信息等。
一、PGconn 對象
當(dāng)用戶通過 PQconnectdb, PQconnectdbParams, PQsetdbLogin 嘗試與 PostgreSQL 服務(wù)器建立連接后,無論成功與否,libpq 會(huì)返回一個(gè) PGconn 對象給用戶,該對象封裝了連接的信息,比如:
dbName — 數(shù)據(jù)庫名稱
pguser — 用戶名
status — 連接狀態(tài)
errorMessage — 錯(cuò)誤信息
用戶可以通過 PQstatus (<PGconn對象>) 獲取到連接狀態(tài),如果狀態(tài)為 CONNECTION_OK ,即說明已經(jīng)成功連接數(shù)據(jù)庫,并且狀態(tài)健康;若連接失敗,可以通過 PQerrorMessage (<PGconn對象>) 獲取到錯(cuò)誤的具體信息。
在之后發(fā)送 SQL Command 等操作中,都需要將 PGconn 對象作為入?yún)⒉趴梢赃M(jìn)行。
二、命令執(zhí)行函數(shù)
在成功連接數(shù)據(jù)庫并且獲取到 PGconn 對象后,用戶即可使用 PQexec 上傳命令并且等待結(jié)果:
PGresult *PQexec(PGconn *conn, const char *command)
通過 PQexec 返回來的 PGresult 對象和 PGconn 是類似的邏輯:PGresult 里面封裝了單個(gè) SQL 命令的查詢結(jié)果,比如:
Tuples — 元組
resultStatus — 結(jié)果狀態(tài)
errMsg — 錯(cuò)誤信息
用戶可以通過 PQresultStatus (<PGresult 對象>) 獲取到結(jié)果狀態(tài),如果狀態(tài)為 PGRES_COMMAND_OK,說明已經(jīng)成功執(zhí)行了命令,但是沒有返回任何的值;如果狀態(tài)為 PGRES_TUPLES_OK,說明成功執(zhí)行命令,并且返回值已經(jīng)存在了 tuples 中。
三、SQL 注入 PQExecParam
當(dāng)數(shù)據(jù)庫服務(wù)器被錯(cuò)誤地引導(dǎo),將查詢的動(dòng)態(tài)參數(shù)視為查詢文本的一部分時(shí),就會(huì)發(fā)生 SQL 注入。比如查詢文本內(nèi)容本身就是“DROP TABLE STUFENTS”,可能會(huì)產(chǎn)生 SQL 注入。
為避免這種情況,PostgreSQL 通過協(xié)議將動(dòng)態(tài)參數(shù)作為單獨(dú)的實(shí)體發(fā)送,PQExecParam 就是其中一種方法:
PQExecParam 與 PQExec 很像,但提供了額外的功能:參數(shù)值可以與命令字符串本身分開指定,查詢的結(jié)果也可以被指定為文本或者二進(jìn)制。
conn:PGconn 對象
command: SQL 字符串命令
nParams: 提供的參數(shù)數(shù)量
paramTypes: 參數(shù)符號(hào)的數(shù)據(jù)類型
paramValues: 指定參數(shù)的實(shí)際值
paramLengths: 指定二進(jìn)制格式參數(shù)的實(shí)際數(shù)據(jù)長度
paramFormats: 指定參數(shù)是文本還是二進(jìn)制
resultFormat: 獲取文本格式還是二進(jìn)制格式結(jié)果
除了避免容易出錯(cuò)的引用和轉(zhuǎn)義之外,PQExecParam 只允許在字符串中有最多一個(gè) SQL 命令。
四、錯(cuò)誤處理
當(dāng)出現(xiàn)錯(cuò)誤的時(shí)候,除了上文提到的 PQerrorMessage,也可以通過 PQresultErrorField 獲取到 PGresult 的相應(yīng)錯(cuò)誤信息:
char *PQresultErrorField(const PGresult *res, int fieldcode)
fieldcode 是 libpq 定義的錯(cuò)誤消息字段的標(biāo)識(shí)符:
比如用戶可以通過 PQresultErrorField (pqres, PG_DIAG_SQLSTATE) 獲取到返回的 PostgreSQL error code。
五、獲取結(jié)果
如果獲取到的結(jié)果對象狀態(tài)良好,用戶即可使用 PQntuples 獲取列數(shù),PQnfields 獲取行數(shù), PQfname 獲取列名,PQgetvalue 獲取某一行某一列的結(jié)果。
在此次 PGresult 操作結(jié)束之后,無論是否成功獲取到結(jié)果,都需要使用 PQclear (<PGresult對象>) 清理結(jié)果,以免出現(xiàn)內(nèi)存泄露的問題。
六、總結(jié)
通過 libpq 與 PostgreSQL 建立連接之后,可以通過 libpq 所在的 client 端發(fā)送 SQL 接收結(jié)果,或獲取錯(cuò)誤信息。為了避免 SQL 注入的問題,用戶可以使用相應(yīng)的協(xié)議或者是函數(shù)避免錯(cuò)誤的產(chǎn)生。