SQLite3 API編程手冊(cè)Admin 2012年1月3日 前序: 這里要注明,我是一個(gè)跨平臺(tái)專注者,并不喜好只用 windows 平臺(tái)。我以前的工作就是為 unix 平臺(tái)寫代碼。下面我所寫的器材,固然沒有驗(yàn)證,然則我已盡量不應(yīng)用任何 windows 的器材,只應(yīng)用標(biāo)準(zhǔn) C 或標(biāo)準(zhǔn)C++。然則,我沒有測(cè)驗(yàn)測(cè)驗(yàn)過在此外體系、此外編譯器下編譯,是以下面的論述若是不正確,則留待今后批改。 下面我的代碼仍然用 VC 編寫,因?yàn)槲腋杏XVC是一個(gè)很不錯(cuò)的IDE,可以加快代碼編寫速度(例如共同 Vassist )。下面我所說的編譯景象,是VC2003。若是讀者感覺本身習(xí)慣于 unix 下用 vi 編寫代碼速度較快,可以不消管我的申明,只須要合適本身習(xí)慣即可,因?yàn)槲矣玫氖菢?biāo)準(zhǔn) C 或 C++ 。不會(huì)給任何人帶來不便。 一、 版本 好久沒有去下載 sqlite 新代碼,是以也不知道 sqlite 變更這么大。以前很多文件,如今全部歸并成一個(gè) sqlite3.c 文件。若是零丁用此文件,是挺好的,省去拷貝一堆文件還愁悶有沒有漏掉。然則也帶來一個(gè)題目:此文件太大,快接近7萬行代碼,VC開它全部機(jī)械都慢下來 了。若是不須要改它代碼,也就不須要打開 sqlite3.c 文件,機(jī)械不會(huì)慢。然則,下面我要寫經(jīng)由過程批改 sqlite 代碼完成加密功能,那時(shí)辰就斗勁疾苦了。若是小我程度較高,建議用些簡單的編輯器來編輯,例如 UltraEdit 或 Notepad 。速度會(huì)快很多。 二、 根蒂根基編譯 extern "C" 為什么要 extern “C” ?若是問這個(gè)題目,我不想說太多,這是C++的根蒂根基。要在 C++ 里應(yīng)用一段 C 的代碼,必必要用 extern “C” 括起來。C++跟 C固然語法上有重疊,然則它們是兩個(gè)不合的器材,內(nèi)存里的布局是完全不合的,在C++編譯器里不消extern “C”括起C代碼,會(huì)導(dǎo)致編譯器不知道該如何為 C 代碼描述內(nèi)存布局。 可能在 sqlite3.c 里人家已經(jīng)把整段代碼都 extern “C” 括起來了,然則你碰到一個(gè) .c 文件就自發(fā)的再括一次,也沒什么不好。 根蒂根基工程就如許建樹起來了。編譯,可以經(jīng)由過程。然則有一堆的 warning。可以不管它。 三、 SQLITE操縱入門 sqlite 跟MS的access一樣是文件型數(shù)據(jù)庫,就是說,一個(gè)數(shù)據(jù)庫就是一個(gè)文件,此數(shù)據(jù)庫里可以建樹很多的表,可以建樹索引、觸發(fā)器等等,然則,它實(shí)際上獲得的就是一個(gè)文件。備份這個(gè)文件就備份了全部數(shù)據(jù)庫。 sqlite 不須要任何數(shù)據(jù)庫引擎,這意味著若是你須要 sqlite 來保存一些用戶數(shù)據(jù),甚至都不須要安裝數(shù)據(jù)庫(若是你做個(gè)小軟件還請(qǐng)求人家必須裝了sqlserver 才干運(yùn)行,那也太黑心了)。 下面開端介紹數(shù)據(jù)庫根蒂根基操縱。 (1) 根蒂根基流程 sqlite 里最常用到的是 sqlite3 * 類型。從數(shù)據(jù)庫打開開端,sqlite就要為這個(gè)類型籌辦好內(nèi)存,直到數(shù)據(jù)庫封閉,全部過程都須要用到這個(gè)類型。當(dāng)數(shù)據(jù)庫打開時(shí)開端,這個(gè)類型的變量就代表了你要操縱的數(shù)據(jù)庫。下面再具體介紹。 i.2 打開數(shù)據(jù)庫 int sqlite3_open( 文件名, sqlite3 ** ); 用這個(gè)函數(shù)開端數(shù)據(jù)庫操縱。 須要傳入兩個(gè)參數(shù),一是數(shù)據(jù)庫文件名,比如:c://DongChunGuang_Database.db。 文件名不須要必然存在,若是此文件不存在,sqlite 會(huì)主動(dòng)建樹它。若是它存在,就測(cè)驗(yàn)測(cè)驗(yàn)把它當(dāng)數(shù)據(jù)庫文件來打開。 sqlite3 ** 參數(shù)即前面提到的關(guān)鍵數(shù)據(jù)布局。這個(gè)布局底層細(xì)節(jié)如何,你不要關(guān)它。 函數(shù)返回值默示操縱是否正確,若是是 SQLITE_OK 則默示操縱正常。相干的返回值sqlite定義了一些宏。具體這些宏的含義可以參考 sqlite3.h 文件。里面有具體定義(趁便說一下,sqlite3 的代碼注釋率自稱是很是高的,實(shí)際上也的確很高。只要你會(huì)看英文,sqlite 可以讓你學(xué)到不少器材)。 下面介紹封閉數(shù)據(jù)庫后,再給一段參考代碼。 i.3 封閉數(shù)據(jù)庫 int sqlite3_close(sqlite3 *); 前面若是用 sqlite3_open 開啟了一個(gè)數(shù)據(jù)庫,結(jié)尾時(shí)不要忘了用這個(gè)函數(shù)封閉數(shù)據(jù)庫。 下面給段簡單的代碼: extern "C" 這就是一次數(shù)據(jù)庫操縱過程。 (2) SQL語句操縱 i.1 履行sql語句 這就是履行一條 sql 語句的函數(shù)。 第1個(gè)參數(shù)不再說了,是前面open函數(shù)獲得的指針。說了是關(guān)鍵數(shù)據(jù)布局。 第2個(gè)參數(shù)const char *sql 是一條 sql 語句,以/0結(jié)尾。 第3個(gè)參數(shù)sqlite3_callback 是回調(diào),當(dāng)這條語句履行之后,sqlite3會(huì)去調(diào)用你供給的這個(gè)函數(shù)。(什么是回調(diào)函數(shù),本身找此外材料進(jìn)修) 第4個(gè)參數(shù)void * 是你所供給的指針,你可以傳遞任何一個(gè)指針參數(shù)到這里,這個(gè)參數(shù)終極會(huì)傳到回調(diào)函數(shù)里面,若是不須要傳遞指針給回調(diào)函數(shù),可以填NULL。等下我們?cè)倏椿卣{(diào)函數(shù)的寫法,以及這個(gè)參數(shù)的應(yīng)用。 第5個(gè)參數(shù)char ** errmsg 是錯(cuò)誤信息。重視是指針的指針。sqlite3里面有很多固定的錯(cuò)誤信息。履行 sqlite3_exec 之后,履行失敗時(shí)可以查閱這個(gè)指針(直接 printf(“%s/n”,errmsg))獲得一串字符串信息,這串信息告訴你錯(cuò)在什么處所。sqlite3_exec函數(shù)經(jīng)由過程批改你傳入的指針 的指針,把你供給的指針指向錯(cuò)誤提示信息,如許sqlite3_exec函數(shù)外面就可以經(jīng)由過程這個(gè) char*獲得具體錯(cuò)誤提示。 申明:凡是,sqlite3_callback 和它后面的 void * 這兩個(gè)地位都可以填 NULL。填NULL默示你不須要回調(diào)。比如你做 操縱,做 操縱,就沒有須要應(yīng)用回調(diào)。而當(dāng)你做 時(shí),就要應(yīng)用回調(diào),因?yàn)?sqlite3 把數(shù)據(jù)查出來,得經(jīng)由過程回調(diào)告訴你查出了什么數(shù)據(jù)。 i.2 exec 的回調(diào) typedef int (*sqlite3_callback)(void*,int,char**, char**); 你的回調(diào)函數(shù)必須定義成上方這個(gè)函數(shù)的類型。下面給個(gè)簡單的例子: //sqlite3的回調(diào)函數(shù) //數(shù)據(jù)庫操縱代碼 //插入一些記錄 //開端查詢數(shù)據(jù)庫 //封閉數(shù)據(jù)庫 經(jīng)由過程上方的例子,應(yīng)當(dāng)可以知道如何打開一個(gè)數(shù)據(jù)庫,如何做數(shù)據(jù)庫根蒂根基操縱。 有這些常識(shí),根蒂根基上可以敷衍很多半據(jù)庫操縱了。 i.3 不應(yīng)用回查詢拜訪詢數(shù)據(jù)庫 上方介紹的 sqlite3_exec 是應(yīng)用回調(diào)來履行 操縱。還有一個(gè)辦法可以直接查詢而不須要回調(diào)。然則,我小我感觸感染還是回調(diào)好,因?yàn)榇a可以加倍整潔,只不過用回調(diào)很麻煩,你得聲明一個(gè)函數(shù),若是這個(gè) 函數(shù)是類成員函數(shù),你還不得不把它聲明成 static 的(要問為什么?這又是C++根蒂根基了。C++成員函數(shù)實(shí)際上隱蔽了一個(gè)參數(shù):this,C++調(diào)用類的成員函數(shù)的時(shí)辰,隱含把類指針當(dāng)成函數(shù)的第一個(gè) 參數(shù)傳遞進(jìn)去。成果,這造成跟前面說的 sqlite 回調(diào)函數(shù)的參數(shù)不相符。只有當(dāng)把成員函數(shù)聲明成 static 時(shí),它才沒有多余的隱含的this參數(shù))。 固然回調(diào)顯得代碼整潔,但有時(shí)辰你還是想要非回調(diào)的 查詢。這可以經(jīng)由過程 sqlite3_get_table 函數(shù)做到。 int sqlite3_get_table(sqlite3*, const char *sql, char ***resultp, int *nrow, int *ncolumn, char **errmsg ); 第1個(gè)參數(shù)不再多說,看前面的例子。 下面給個(gè)簡單例子: int main( int , char ** ) //數(shù)據(jù)庫打開失敗 //數(shù)據(jù)庫操縱代碼 index = nColumn; //前面說過 dbResult 前面第一行數(shù)據(jù)是字段名稱,從 nColumn 索引開端才是真正的數(shù)據(jù) //封閉數(shù)據(jù)庫 到這個(gè)例子為止,sqlite3 的常用用法都介紹完了。 用以上的辦法,再配上 sql 語句,完全可以敷衍絕大多半數(shù)據(jù)庫需求。 但有一種景象,用上方辦法是無法實(shí)現(xiàn)的:須要、 二進(jìn)制。當(dāng)須要處理懲罰二進(jìn)制數(shù)據(jù)時(shí),上方的辦法就沒辦法做到。下面這一節(jié)申明如何插入二進(jìn)制數(shù)據(jù) (2) 操縱二進(jìn)制 這個(gè)數(shù)據(jù)類型記錄了一個(gè)“sql語句”。為什么我把 “sql語句” 用雙引號(hào)引起來?因?yàn)槟憧梢园?sqlite3_stmt * 所默示的內(nèi)容算作是 sql語句,然則實(shí)際上它不是我們所熟知的sql語句。它是一個(gè)已經(jīng)把sql語句解析了的、用sqlite本身標(biāo)識(shí)表記標(biāo)幟記錄的內(nèi)部數(shù)據(jù)布局。 正因?yàn)檫@個(gè)布局已經(jīng)被解析了,所以你可以往這個(gè)語句里插入二進(jìn)制數(shù)據(jù)。當(dāng)然,把二進(jìn)制數(shù)據(jù)插到 sqlite3_stmt 布局里可不克不及直接 memcpy ,也不克不及像 std::string 那樣用 + 號(hào)。必須用 sqlite 供給的函數(shù)來插入。
i.1 寫入二進(jìn)制 下面說寫二進(jìn)制的步調(diào)。 要插入二進(jìn)制,前提是這個(gè)表的字段的類型是 blob 類型。我假設(shè)有這么一張表: create table Tbl_2( ID integer, file_content blob ) 起首聲明 sqlite3_stmt * stat; 然后,把一個(gè) sql 語句解析到 stat 布局里去: sqlite3_prepare( db, “ into Tbl_2( ID, file_content) values( 10, ? )”, -1, &stat, 0 ); 上方的函數(shù)完成 sql 語句的解析。第一個(gè)參數(shù)跟前面一樣,是個(gè) sqlite3 * 類型變量,第二個(gè)參數(shù)是一個(gè) sql 語句。 這個(gè) sql 語句希罕之處在于 values 里面有個(gè) ? 號(hào)。在sqlite3_prepare函數(shù)里,?號(hào)默示一個(gè)不決的值,它的值等下才插入。 第三個(gè)參數(shù)我寫的是-1,這個(gè)參數(shù)含義是前面 sql 語句的長度。若是小于0,sqlite會(huì)主動(dòng)策畫它的長度(把sql語句當(dāng)成以/0結(jié)尾的字符串)。 若是這個(gè)函數(shù)履行成功(返回值是 SQLITE_OK 且 stat 不為NULL ),那么下面就可以開端插入二進(jìn)制數(shù)據(jù)。 sqlite3_bind_blob( stat, 1, pdata, (int)(length_of_data_in_bytes), NULL ); // pdata為數(shù)據(jù)緩沖區(qū),length_of_data_in_bytes為數(shù)據(jù)大小,以字節(jié)為單位 這個(gè)函數(shù)一共有5個(gè)參數(shù)。 第1個(gè)參數(shù):是前面prepare獲得的 sqlite3_stmt * 類型變量。 bind完了之后,二進(jìn)制數(shù)據(jù)就進(jìn)入了你的“sql語句”里了。你如今可以把它保存到數(shù)據(jù)庫里: int result = sqlite3_step( stat ); 經(jīng)由過程這個(gè)語句,stat 默示的sql語句就被寫到了數(shù)據(jù)庫里。 最后,要把 sqlite3_stmt 布局給開釋: sqlite3_finalize( stat ); //把剛才分派的內(nèi)容析構(gòu)掉
i.2 讀出二進(jìn)制 下面說讀二進(jìn)制的步調(diào)。 跟前面一樣,先聲明 sqlite3_stmt * 類型變量: sqlite3_stmt * stat; 然后,把一個(gè) sql 語句解析到 stat 布局里去: sqlite3_prepare( db, “ * Tbl_2”, -1, &stat, 0 ); 當(dāng) prepare 成功之后(返回值是 SQLITE_OK ),開端查詢數(shù)據(jù)。 int result = sqlite3_step( stat ); 這一句的返回值是 SQLITE_ROW 時(shí)默示成功(不是 SQLITE_OK )。 你可以輪回履行 sqlite3_step 函數(shù),一次 step 查詢出一筆記錄。直到返回值不為 SQLITE_ROW 時(shí)默示查詢停止。 然后開端獲取第一個(gè)字段:ID 的值。ID是個(gè)整數(shù),用下面這個(gè)語句獲取它的值: int id = sqlite3_column_int( stat, 0 ); //第2個(gè)參數(shù)默示獲取第幾個(gè)字段內(nèi)容,從0開端策畫,因?yàn)槲业谋淼腎D字段是第一個(gè)字段,是以這里我填0 下面開端獲取 file_content 的值,因?yàn)?file_content 是二進(jìn)制,是以我須要獲得它的指針,還有它的長度: const void * pFileContent = sqlite3_column_blob( stat, 1 ); int len = sqlite3_column_bytes( stat, 1 ); 如許就獲得了二進(jìn)制的值。 把 pFileContent 的內(nèi)容保存出來之后,不要忘了開釋 sqlite3_stmt 布局: sqlite3_finalize( stat ); //把剛才分派的內(nèi)容析構(gòu)掉 i.3 反復(fù)應(yīng)用 sqlite3_stmt 布局 若是你須要反復(fù)應(yīng)用 sqlite3_prepare 解析好的 sqlite3_stmt 布局,須要用函數(shù): sqlite3_reset。 result = sqlite3_reset(stat); 如許, stat 布局又成為 sqlite3_prepare 完成時(shí)的狀況,你可以從頭為它 bind 內(nèi)容。 (4) 事務(wù)處理懲罰 sqlite 是支撐事務(wù)處理懲罰的。若是你知道你要同步刪除很多半據(jù),不仿把它們做成一個(gè)同一的事務(wù)。 凡是一次 sqlite3_exec 就是一次事務(wù),若是你要?jiǎng)h除1萬條數(shù)據(jù),sqlite就做了1萬次:開端新事務(wù)->刪除一條數(shù)據(jù)->提交事務(wù)->開端新事務(wù)->… 的過程。這個(gè)操縱是很慢的。因?yàn)闀r(shí)候都花在了開端事務(wù)、提交事務(wù)上。 你可以把這些同類操縱做成一個(gè)事務(wù),如許若是操縱錯(cuò)誤,還可以或許回滾事務(wù)。 事務(wù)的操縱沒有特此外接口函數(shù),它就是一個(gè)通俗的 sql 語句罷了: 分別如下: int result; result = sqlite3_exec( db, "begin transaction", 0, 0, &zErrorMsg ); //開端一個(gè)事務(wù) result = sqlite3_exec( db, "commit transaction", 0, 0, &zErrorMsg ); //提交事務(wù) result = sqlite3_exec( db, "rollback transaction", 0, 0, &zErrorMsg ); //回滾事務(wù) 一、 給數(shù)據(jù)庫加密 前面所說的內(nèi)容網(wǎng)上已經(jīng)有很多材料,固然斗勁零散,然則花點(diǎn)時(shí)候也還是可以找到的。如今要說的這個(gè)——數(shù)據(jù)庫加密,材料就很難找。也可能是 我操縱程度不敷,找不到對(duì)應(yīng)材料。但不管如許,我還是經(jīng)由過程網(wǎng)上能找到的很有限的材料,摸索出了給sqlite數(shù)據(jù)庫加密的完全步調(diào)。 這里要提一下,固然 sqlite 很好用,速度快、體積小巧。然則它保存的文件倒是明文的。若不信可以用 NotePad 打開數(shù)據(jù)庫文件瞧瞧,里面 的內(nèi)容幾乎一目了然。如許赤裸裸的顯現(xiàn)本身,可不是我們的初志。當(dāng)然,若是你在嵌入式體系、智妙手機(jī)上應(yīng)用 sqlite,最好是不加密,因?yàn)檫@些體系運(yùn)算才能有限,你做為一個(gè)新功能供給者,不克不及把用戶有限的運(yùn)算才能全部花掉。 Sqlite為了速度而出生。是以Sqlite本身不合錯(cuò)誤數(shù)據(jù)庫加密,要知道,若是你選擇標(biāo)準(zhǔn)AES算法加密,那么必然有接近50%的時(shí) 候消費(fèi)在加解密算法上,甚至更多(機(jī)能首要取決于你算法編寫程度以及你是否能應(yīng)用cpu供給的底層運(yùn)算才能,比如MMX或sse系列指令可以大幅度提拔運(yùn) 算速度)。 Sqlite免費(fèi)版本是不供給加密功能的,當(dāng)然你也可以選擇他們的收費(fèi)版本,那你得付出2000塊錢,并且是USD。我這里也不是說付出錢 不好,若是只為了數(shù)據(jù)庫加密就去付出2000塊,我感覺劃不來。因?yàn)橄旅嫖覍⒁嬖V你如何為免費(fèi)的Sqlite擴(kuò)大出加密模塊——本身下手?jǐn)U大,這是 Sqlite容許,也是它倡導(dǎo)的。 那么,就讓我們一路開端為 sqlite3.c 文件擴(kuò)大出加密模塊。
經(jīng)由過程瀏覽 Sqlite 代碼(當(dāng)然沒有全部瀏覽完,6萬多行代碼,沒有一行是我習(xí)慣的風(fēng)格,我可沒那么多眼神去看),我搞清楚了兩件事: Sqlite是支撐加密擴(kuò)大的; 須要 #define 一個(gè)宏才干應(yīng)用加密擴(kuò)大。 這個(gè)宏就是 SQLITE_HAS_CODEC。
#ifndef SQLITE_HAS_CODEC #define SQLITE_HAS_CODEC #endif
定義了這個(gè)宏,一些被 Sqlite 有心樊籬掉的代碼就被應(yīng)用了。這些代碼就是加解密的接口。 測(cè)驗(yàn)測(cè)驗(yàn)編譯,vc會(huì)提示你有一些函數(shù)無法鏈接,因?yàn)檎也坏剿麄兊膶?shí)現(xiàn)。 若是你也用的是VC2003,那么會(huì)獲得下面的提示: error LNK2019: 無法解析的外部符號(hào) _sqlite3CodecGetKey ,該符號(hào)在函數(shù) _attachFunc 中被引用 error LNK2019: 無法解析的外部符號(hào) _sqlite3CodecAttach ,該符號(hào)在函數(shù) _attachFunc 中被引用 error LNK2019: 無法解析的外部符號(hào) _sqlite3_activate_see,該符號(hào)在函數(shù) _sqlite3Pragma 中被引用
fatal error LNK1120: 4 個(gè)無法解析的外部號(hào)令
下面就讓我來實(shí)現(xiàn)這些接口。
若是真要我從一份 www.sqlite.org 網(wǎng)上down下來的 sqlite3.c 文件,直接摸索出這些接口的實(shí)現(xiàn),我認(rèn)為我還沒有這個(gè)才能。 好在網(wǎng)上還有一些代碼已經(jīng)實(shí)現(xiàn)了這個(gè)功能。經(jīng)由過程參照他們的代碼以及絡(luò)續(xù)編譯中vc給出的錯(cuò)誤提示,終極我把全部接口收拾出來。 實(shí)現(xiàn)這些預(yù)留接口不是那么輕易,要重頭說一次怎么回事很艱苦。我把代碼都寫好了,直接把他們按我下面的申明拷貝到 sqlite3.c 文件對(duì)應(yīng)處所即可。我鄙人面也供給了sqlite3.c 文件,可以直接參考或取下來應(yīng)用。
此中crypt.h如此定義: #ifndef DCG_SQLITE_CRYPT_FUNC_
***********/ int My_DeEncrypt_Func( unsigned char * pData, unsigned int data_len, const char * key, unsigned int len_of_key );
此中的 crypt.c 如此定義: #include "./crypt.h" #include "memory.h" int My_Encrypt_Func( unsigned char * pData, unsigned int data_len, const char * key, unsigned int len_of_key )
return 0; }
int My_DeEncrypt_Func( unsigned char * pData, unsigned int data_len, const char * key, unsigned int len_of_key ) { return 0; } 這個(gè)文件很輕易看,就兩函數(shù),一個(gè)加密一個(gè)解密。傳進(jìn)來的參數(shù)分別是待處理懲罰的數(shù)據(jù)、數(shù)據(jù)長度、密鑰、密鑰長度。 處理懲罰時(shí)直接把成果感化于 pData 指針指向的內(nèi)容。 你須要定義本身的加解密過程,就批改這兩個(gè)函數(shù),其它項(xiàng)目組不消動(dòng)。擴(kuò)大起來很簡單。 這里有個(gè)特點(diǎn),data_len 一般老是 1024 字節(jié)。正因?yàn)槿绱?,你可以在你的算法里?yīng)用一些特定長度的加密算法,比如AES請(qǐng)求被加密數(shù)據(jù)必然是128位(16字節(jié))長。這個(gè)1024不是碰勁,而是 Sqlite 的頁定義是1024字節(jié),在sqlite3.c文件里有定義: # define SQLITE_DEFAULT_PAGE_SIZE 1024 你可以批改這個(gè)值,不過還是建議沒有須要不要去改它。
分3個(gè)步調(diào)。 起首,在 sqlite3.c 文件頂部,添加下面內(nèi)容:
#include "./crypt.h" void sqlite3pager_free_codecarg(void *pArg); #endif 這個(gè)函數(shù)之所以要在 sqlite3.c 開首聲明,是因?yàn)橄旅嬖?sqlite3.c 里面某些函數(shù)里要插入這個(gè)函數(shù)調(diào)用。所以要提前聲明。
實(shí)現(xiàn)代碼里一開端是: #ifdef SQLITE_ENABLE_MEMORY_MANAGEMENT
assert( pPager ); assert( pTsd && pTsd->nAlloc ); #endif
sqlite3pager_free_codecarg(pPager->pCodecArg); #endif
類似的還有“sqlite3pager_get”、“sqlite3pager_unref”、 “sqlite3pager_write”、“sqlite3pager_pagecount”等都是老版本函數(shù),它們?cè)?pager.h 文件里定義。新版本對(duì)應(yīng)函數(shù)是在 sqlite3.h 里定義(因?yàn)槎細(xì)w并到 sqlite3.c和sqlite3.h兩文件了)。所以,若是你在應(yīng)用老版本的sqlite,先看看 pager.h 文件,這些函數(shù)不是消散了,也不是新蹦出來的,而是老版本函數(shù)改名獲得的。
最后,往sqlite3.c 文件下找。找到最后一行:
這些代碼很長,我不再申明,直接接上去就得了。 獨(dú)一要提的是 DeriveKey 函數(shù)。這個(gè)函數(shù)是對(duì)密鑰的擴(kuò)大。比如,你請(qǐng)求密鑰是128位,便是16字節(jié),然則若是用戶只輸入 1個(gè)字節(jié)呢?2個(gè)字節(jié)呢?或輸入50個(gè)字節(jié)呢?你得對(duì)密鑰進(jìn)行擴(kuò)大,使之合適16字節(jié)的請(qǐng)求。 DeriveKey 函數(shù)就是做這個(gè)擴(kuò)大的。有人把接管到的密鑰求md5,這也是一個(gè)辦法,因?yàn)閙d5運(yùn)算成果固定16字節(jié),非論你有幾許字符,最后就是16字節(jié)。這是md5 算法的特點(diǎn)。然則我不想用md5,因?yàn)檫€得為它添加包含一些 md5 的.c或.cpp文件。我不想這么做。我本身寫了一個(gè)算法來擴(kuò)大密鑰,很簡單的算法。當(dāng)然,你也可以應(yīng)用你的擴(kuò)大辦法,也而可以應(yīng)用 md5 算法。只要批改 DeriveKey 函數(shù)就可以了。 在 DeriveKey 函數(shù)里,盡管申請(qǐng)空間機(jī)關(guān)所須要的密鑰,不須要開釋,因?yàn)樵诹硪粋€(gè)函數(shù)里有開釋過程,而那個(gè)函數(shù)會(huì)在數(shù)據(jù)庫封閉時(shí)被調(diào)用。參考我的 DeriveKey 函數(shù)來申請(qǐng)內(nèi)存。
若是太懶,就直接應(yīng)用這兩個(gè)文件,編譯必然能經(jīng)由過程,運(yùn)行也正常。當(dāng)然,你必須按我前面提的,新建 crypt.h 和 crypt.c 文件,并且函數(shù)要按我前面定義的請(qǐng)求來做。 i.3 加密應(yīng)用辦法: 如今,你代碼已經(jīng)有了加密功能。 你要把加密功能給用上,除了改 sqlite3.c 文件、給你工程添加 SQLITE_HAS_CODEC 宏,還得批改你的數(shù)據(jù)庫調(diào)用函數(shù)。 前面提到過,要開端一個(gè)數(shù)據(jù)庫操縱,必須先 sqlite3_open 。 加解密過程就在 sqlite3_open 后面操縱。 假設(shè)你已經(jīng) sqlite3_open 成功了,緊接著寫下面的代碼: int i; //添加、應(yīng)用暗碼 i = sqlite3_key( db, "dcg", 3 ); //批改暗碼 i = sqlite3_rekey( db, "dcg", 0 );
第1個(gè)參數(shù)是 sqlite3 * 類型變量,代表著用 sqlite3_open 打開的數(shù)據(jù)庫(或新建數(shù)據(jù)庫)。 第2個(gè)參數(shù)是密鑰。 第3個(gè)參數(shù)是密鑰長度。 用 sqlite3_rekey 來批改暗碼。參數(shù)含義同 sqlite3_key。
然則若是你沒有設(shè)置暗碼,而數(shù)據(jù)庫之前是有暗碼的,那么你做任何操縱都邑獲得一個(gè)返回值:SQLITE_NOTADB,并且獲得錯(cuò)誤提示:“file is encrypted or is not a database”。 只有當(dāng)你用 sqlite3_key 設(shè)置了正確的暗碼,數(shù)據(jù)庫才會(huì)正常工作。 若是你要批改暗碼,前提是你必須先 sqlite3_open 打開數(shù)據(jù)庫成功,然后 sqlite3_key 設(shè)置密鑰成功,之后才干用 sqlite3_rekey 來批改暗碼。 若是數(shù)據(jù)庫有暗碼,但你沒有效 sqlite3_key 設(shè)置暗碼,那么當(dāng)你測(cè)驗(yàn)測(cè)驗(yàn)用 sqlite3_rekey 來批改暗碼時(shí)會(huì)獲得 SQLITE_NOTADB 返回值。 若是你須要清空暗碼,可以應(yīng)用: //批改暗碼 i = sqlite3_rekey( db, NULL, 0 ); 來完成暗碼清空功能。
i.4 sqlite3.c 最后添加代碼段
#ifdef SQLITE_HAS_CODEC #define CRYPT_OFFSET 8 typedef struct _CryptBlock { BYTE* ReadKey; // 讀數(shù)據(jù)庫和寫入事務(wù)的密鑰 BYTE* WriteKey; // 寫入數(shù)據(jù)庫的密鑰 int PageSize; // 頁的大小 BYTE* Data; } CryptBlock, *LPCryptBlock; #ifndef DB_KEY_LENGTH_BYTE #define DB_KEY_LENGTH_BYTE 16 #endif
#define DB_KEY_PADDING 0 x33 #endif
void sqlite3CodecGetKey(sqlite3* db, int nDB, void** Key, int* nKey) { return ; }
int sqlite3CodecAttach(sqlite3 *db, int nDb, const void *pKey, int nKeyLen); void sqlite3_activate_see(const char* right ) { return; }
int sqlite3_rekey(sqlite3 *db, const void *pKey, int nKey);
// 用戶供給的密鑰可能位數(shù)上滿足不了請(qǐng)求,應(yīng)用這個(gè)函數(shù)來完成密鑰擴(kuò)大 static unsigned char * DeriveKey(const void *pKey, int nKeyLen); //創(chuàng)建或更新一個(gè)頁的加密算法索引.此函數(shù)會(huì)申請(qǐng)緩沖區(qū).
//加密/解密函數(shù), 被pager調(diào)用 void * sqlite3Codec(void *pArg, unsigned char *data, Pgno nPageNum, int nMode); //設(shè)置暗碼函數(shù) int __stdcall sqlite3_key_interop(sqlite3 *db, const void *pKey, int nKeySize); // 批改暗碼函數(shù) int __stdcall sqlite3_rekey_interop(sqlite3 *db, const void *pKey, int nKeySize); //燒毀一個(gè)加密塊及相干的緩沖區(qū),密鑰. static void DestroyCryptBlock(LPCryptBlock pBlock);
void sqlite3pager_set_codec(Pager *pPager,void *(*xCodec)(void*,void*,Pgno,int),void *pCodecArg );
void * sqlite3Codec(void *pArg, unsigned char *data, Pgno nPageNum, int nMode) { LPCryptBlock pBlock = (LPCryptBlock)pArg; unsigned int dwPageSize = 0; if (!pBlock) return data;
if (nMode != 2) { PgHdr *pageHeader; pageHeader = DATA_TO_PGHDR(data); if (pageHeader->pPager->pageSize != pBlock->PageSize) { CreateCryptBlock(0, pageHeader->pPager, pBlock); } }
switch(nMode) { case 0: // Undo a "case 7" journal file encryption case 2: //重載一個(gè)頁 case 3: //載入一個(gè)頁 if (!pBlock->ReadKey) break;
dwPageSize = pBlock->PageSize; My_DeEncrypt_Func(data, dwPageSize, pBlock->ReadKey, DB_KEY_LENGTH_BYTE );
break; case 6: //加密一個(gè)主數(shù)據(jù)庫文件的頁 if (!pBlock->WriteKey) break;
data = pBlock->Data CRYPT_OFFSET;
dwPageSize = pBlock->PageSize; My_Encrypt_Func(data , dwPageSize, pBlock->WriteKey, DB_KEY_LENGTH_BYTE ); break;
if (!pBlock->ReadKey) break;
data = pBlock->Data CRYPT_OFFSET; dwPageSize = pBlock->PageSize; My_Encrypt_Func( data, dwPageSize, pBlock->ReadKey, DB_KEY_LENGTH_BYTE ); break; }
}
// 燒毀一個(gè)加密塊及相干的緩沖區(qū),密鑰.
{ //燒毀讀密鑰. if (pBlock->ReadKey){ sqliteFree(pBlock->ReadKey); }
if (pBlock->WriteKey && pBlock->WriteKey != pBlock->ReadKey){ sqliteFree(pBlock->WriteKey); } if(pBlock->Data){ sqliteFree(pBlock->Data); }
sqliteFree(pBlock); }
{ return (pPager->xCodec) ? pPager->pCodecArg: NULL; } // 從用戶供給的緩沖區(qū)中獲得一個(gè)加密密鑰
{ unsigned char * hKey = NULL; int j;
{ return NULL; }
if( hKey == NULL )
return NULL; } hKey[ DB_KEY_LENGTH_BYTE ] = 0; if( nKeyLen < DB_KEY_LENGTH_BYTE ) { memcpy( hKey, pKey, nKeyLen ); //先拷貝獲得密鑰前面的項(xiàng)目組 j = DB_KEY_LENGTH_BYTE - nKeyLen; //補(bǔ)充密鑰后面的項(xiàng)目組 memset( hKey nKeyLen, DB_KEY_PADDING, j ); } else
memcpy( hKey, pKey, DB_KEY_LENGTH_BYTE ); }
}
//創(chuàng)建或更新一個(gè)頁的加密算法索引.此函數(shù)會(huì)申請(qǐng)緩沖區(qū). static LPCryptBlock CreateCryptBlock(unsigned char* hKey, Pager *pager, LPCryptBlock pExisting) { LPCryptBlock pBlock;
if (!pExisting) //創(chuàng)建新加密塊 { pBlock = sqliteMalloc(sizeof(CryptBlock)); memset(pBlock, 0, sizeof(CryptBlock)); pBlock->ReadKey = hKey; pBlock->WriteKey = hKey; pBlock->PageSize = pager->pageSize; pBlock->Data = (unsigned char*)sqliteMalloc(pBlock->PageSize CRYPT_OFFSET); } else //更新存在的加密塊 {
if ( pBlock->PageSize != pager->pageSize && !pBlock->Data){ sqliteFree(pBlock->Data); pBlock->PageSize = pager->pageSize; pBlock->Data = (unsigned char*)sqliteMalloc(pBlock->PageSize CRYPT_OFFSET); } }
memset(pBlock->Data, 0, pBlock->PageSize CRYPT_OFFSET);
}
void sqlite3pager_set_codec( Pager *pPager, void *(*xCodec)(void*,void*,Pgno,int), void *pCodecArg ) { pPager->xCodec = xCodec; pPager->pCodecArg = pCodecArg; } int sqlite3_key(sqlite3 *db, const void *pKey, int nKey) { return sqlite3_key_interop(db, pKey, nKey); }
{ return sqlite3_rekey_interop(db, pKey, nKey); }
int sqlite3CodecAttach(sqlite3 *db, int nDb, const void *pKey, int nKeyLen)
int rc = SQLITE_ERROR; unsigned char* hKey = 0;
if (!pKey || !nKeyLen) { if (!nDb) { return SQLITE_OK; //主數(shù)據(jù)庫, 沒有指定密鑰所以沒有加密. }
{ //獲取主數(shù)據(jù)庫的加密塊并復(fù)制密鑰給附加數(shù)據(jù)庫應(yīng)用 LPCryptBlock pBlock = (LPCryptBlock)sqlite3pager_get_codecarg(sqlite3BtreePager(db->aDb[0].pBt));
if (!pBlock->ReadKey) return SQLITE_OK; //沒有加密
} }
{ hKey = DeriveKey(pKey, nKeyLen); }
if (hKey) { LPCryptBlock pBlock = CreateCryptBlock(hKey, sqlite3BtreePager(db->aDb[nDb].pBt), NULL); sqlite3pager_set_codec(sqlite3BtreePager(db->aDb[nDb].pBt), sqlite3Codec, pBlock); rc = SQLITE_OK; }
}
int __stdcall sqlite3_rekey_interop(sqlite3 *db, const void *pKey, int nKeySize) { Btree *pbt = db->aDb[0].pBt; Pager *p = sqlite3BtreePager(pbt); LPCryptBlock pBlock = (LPCryptBlock)sqlite3pager_get_codecarg(p); unsigned char * hKey = DeriveKey(pKey, nKeySize); int rc = SQLITE_ERROR; if (!pBlock && !hKey) return SQLITE_OK;
if (!pBlock) //加密一個(gè)未加密的數(shù)據(jù)庫 { pBlock = CreateCryptBlock(hKey, p, NULL); pBlock->ReadKey = 0; // 原始數(shù)據(jù)庫未加密 sqlite3pager_set_codec(sqlite3BtreePager(pbt), sqlite3Codec, pBlock); } else // 改變已加密數(shù)據(jù)庫的寫密鑰
pBlock->WriteKey = hKey; }
rc = sqlite3BtreeBeginTrans(pbt, 1);
{ // 用新密鑰重寫所有的頁到數(shù)據(jù)庫。 Pgno nPage = sqlite3PagerPagecount(p); Pgno nSkip = PAGER_MJ_PGNO(p);
Pgno n;
{ if (n == nSkip) continue; rc = sqlite3PagerGet(p, n, &pPage); if(!rc) { rc = sqlite3PagerWrite(pPage); sqlite3PagerUnref(pPage); } }
if (!rc) { rc = sqlite3BtreeCommit(pbt); }
if (rc) { sqlite3BtreeRollback(pbt); }
// 若是成功,燒毀先前的讀密鑰。并使讀密鑰便是當(dāng)前的寫密鑰。 if (!rc) { if (pBlock->ReadKey) { sqliteFree(pBlock->ReadKey); } pBlock->ReadKey = pBlock->WriteKey; } else// 若是失敗,燒毀當(dāng)前的寫密鑰,并恢復(fù)為當(dāng)前的讀密鑰。
if (pBlock->WriteKey) { sqliteFree(pBlock->WriteKey); } pBlock->WriteKey = pBlock->ReadKey; }
// 燒毀加密塊并移除頁的編解碼器
{ sqlite3pager_set_codec(p, NULL, NULL); DestroyCryptBlock(pBlock); }
} int __stdcall sqlite3_key_interop(sqlite3 *db, const void *pKey, int nKeySize) { return sqlite3CodecAttach(db, 0, pKey, nKeySize); }
// 開釋與一個(gè)頁相干的加密塊 void sqlite3pager_free_codecarg(void *pArg)
if (pArg) DestroyCryptBlock((LPCryptBlock)pArg); }
|
|