一区二区三区日韩精品-日韩经典一区二区三区-五月激情综合丁香婷婷-欧美精品中文字幕专区

分享

SQLite3 API編程手冊(cè),上海軟件開發(fā)|上海軟件開發(fā)公司|軟件定制|ERP開發(fā)|IT...

 漢江秋月夜 2012-07-03

SQLite3 API編程手冊(cè)


Admin
2012年1月3日

前序:
Sqlite3 的確很好用。小巧、速度快。然則因?yàn)榉俏④浀漠a(chǎn)品,幫助文檔總感覺不敷。這些天再次研究它,又有一些收成,這里把我對(duì) sqlite3 的研究列出來,以備忘懷。


這里要注明,我是一個(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ì)給任何人帶來不便。


一、 版本
從 www.sqlite.org 網(wǎng)站可下載到最新的 sqlite 代碼和編譯版本。我寫此文章時(shí),最新代碼是 3.3.17 版本。


好久沒有去下載 sqlite 新代碼,是以也不知道 sqlite 變更這么大。以前很多文件,如今全部歸并成一個(gè) sqlite3.c 文件。若是零丁用此文件,是挺好的,省去拷貝一堆文件還愁悶有沒有漏掉。然則也帶來一個(gè)題目:此文件太大,快接近7萬行代碼,VC開它全部機(jī)械都慢下來 了。若是不須要改它代碼,也就不須要打開 sqlite3.c 文件,機(jī)械不會(huì)慢。然則,下面我要寫經(jīng)由過程批改 sqlite 代碼完成加密功能,那時(shí)辰就斗勁疾苦了。若是小我程度較高,建議用些簡單的編輯器來編輯,例如 UltraEdit 或 Notepad 。速度會(huì)快很多。


二、 根蒂根基編譯
這個(gè)不想多說了,在 VC 里新建 dos 把握臺(tái)空白工程,把 sqlite3.c 和 sqlite3.h 添加到工程,再新建一個(gè) main.cpp 文件。在里面寫:


extern "C"
{
#include "./sqlite3.h"
};
int main( int , char** )
{
return 0;
}


為什么要 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供給的是一些C函數(shù)接口,你可以用這些函數(shù)操縱數(shù)據(jù)庫。經(jīng)由過程應(yīng)用這些接口,傳遞一些標(biāo)準(zhǔn) sql 語句(以 char * 類型)給 sqlite 函數(shù),sqlite 就會(huì)為你操縱數(shù)據(jù)庫。


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) 根蒂根基流程
i.1 關(guān)鍵數(shù)據(jù)布局


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"
{
#include "./sqlite3.h"
};
int main( int , char** )
{
sqlite3 * db = NULL; //聲明sqlite關(guān)鍵布局指針
int result;
//打開數(shù)據(jù)庫
//須要傳入 db 這個(gè)指針的指針,因?yàn)?sqlite3_open 函數(shù)要為這個(gè)指針分派內(nèi)存,還要讓db指針指向這個(gè)內(nèi)存區(qū)
result = sqlite3_open( “c://Dcg_database.db”, &db );
if( result != SQLITE_OK )
{
//數(shù)據(jù)庫打開失敗
return -1;
}
//數(shù)據(jù)庫操縱代碼
//…
//數(shù)據(jù)庫打開成功
//封閉數(shù)據(jù)庫
sqlite3_close( db );
return 0;
}


這就是一次數(shù)據(jù)庫操縱過程。


(2) SQL語句操縱
本節(jié)介紹如何用sqlite 履行標(biāo)準(zhǔn) sql 語法。


i.1 履行sql語句
int sqlite3_exec(sqlite3*, const char *sql, sqlite3_callback, void *, char **errmsg );


這就是履行一條 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ù)
// sqlite 每查到一筆記錄,就調(diào)用一次這個(gè)回調(diào)
int LoadMyInfo( void * para, int n_column, char ** column_value, char ** column_name )
{
//para是你在 sqlite3_exec 里傳入的 void * 參數(shù)
//經(jīng)由過程para參數(shù),你可以傳入一些特別的指針(比如類指針、布局指針),然后在這里面強(qiáng)迫轉(zhuǎn)換成對(duì)應(yīng)的類型(這里面是void*類型,必須強(qiáng)迫轉(zhuǎn)換成你的類型才可用)。然后操縱這些數(shù)據(jù)
//n_column是這一筆記錄有幾許個(gè)字段 (即這筆記錄有幾許列)
// char ** column_value 是個(gè)關(guān)鍵值,查出來的數(shù)據(jù)都保存在這里,它實(shí)際上是個(gè)1維數(shù)組(不要認(rèn)為是2維數(shù)組),每一個(gè)元素都是一個(gè) char * 值,是一個(gè)字段內(nèi)容(用字符串來默示,以/0結(jié)尾)
//char ** column_name 跟 column_value是對(duì)應(yīng)的,默示這個(gè)字段的字段名稱
//這里,我不應(yīng)用 para 參數(shù)。忽視它的存在.
int i;
printf( “記錄包含 %d 個(gè)字段/n”, n_column );
for( i = 0 ; i < n_column; i ++ )
{
printf( “字段名:%s ?> 字段值:%s/n”, column_name[i], column_value[i] );
}
printf( “------------------/n“ );
return 0;
}
int main( int , char ** )
{
sqlite3 * db;
int result;
char * errmsg = NULL;
result = sqlite3_open( “c://Dcg_database.db”, &db );
if( result != SQLITE_OK )
{
//數(shù)據(jù)庫打開失敗
return -1;
}


//數(shù)據(jù)庫操縱代碼
//創(chuàng)建一個(gè)測(cè)試表,表名叫 MyTable_1,有2個(gè)字段: ID 和 name。此中ID是一個(gè)主動(dòng)增長的類型,今后時(shí)可以不去指定這個(gè)字段,它會(huì)本身從0開端增長
result = sqlite3_exec( db, “create table MyTable_1( ID integer primary key autoincrement, name nvarchar(32) )”, NULL, NULL, errmsg );
if(result != SQLITE_OK )
{
printf( “創(chuàng)建表失敗,錯(cuò)誤碼:%d,錯(cuò)誤原因:%s/n”, result, errmsg );
}


//插入一些記錄
result = sqlite3_exec( db, “ into MyTable_1( name ) values ( ‘走路’ )”, 0, 0, errmsg );
if(result != SQLITE_OK )
{
printf( “插入記錄失敗,錯(cuò)誤碼:%d,錯(cuò)誤原因:%s/n”, result, errmsg );
}
result = sqlite3_exec( db, “ into MyTable_1( name ) values ( ‘騎單車’ )”, 0, 0, errmsg );
if(result != SQLITE_OK )
{
printf( “插入記錄失敗,錯(cuò)誤碼:%d,錯(cuò)誤原因:%s/n”, result, errmsg );
}
result = sqlite3_exec( db, “ into MyTable_1( name ) values ( ‘坐汽車’ )”, 0, 0, errmsg );
if(result != SQLITE_OK )
{
printf( “插入記錄失敗,錯(cuò)誤碼:%d,錯(cuò)誤原因:%s/n”, result, errmsg );
}


//開端查詢數(shù)據(jù)庫
result = sqlite3_exec( db, “ * MyTable_1”, LoadMyInfo, NULL, errmsg );


//封閉數(shù)據(jù)庫
sqlite3_close( db );
return 0;
}


經(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ù)不再多說,看前面的例子。
第2個(gè)參數(shù)是 sql 語句,跟 sqlite3_exec 里的 sql 是一樣的。是一個(gè)很通俗的以/0結(jié)尾的char *字符串。
第3個(gè)參數(shù)是查詢成果,它依然一維數(shù)組(不要認(rèn)為是二維數(shù)組,更不要認(rèn)為是三維數(shù)組)。它內(nèi)存布局是:第一行是字段名稱,后面是緊接著是每個(gè)字段的值。下面用例子來說事。
第4個(gè)參數(shù)是查詢出幾許筆記錄(即查出幾許行)。
第5個(gè)參數(shù)是幾許個(gè)字段(幾許列)。
第6個(gè)參數(shù)是錯(cuò)誤信息,跟前面一樣,這里不久不多說了。


下面給個(gè)簡單例子:


int main( int , char ** )
{
sqlite3 * db;
int result;
char * errmsg = NULL;
char **dbResult; //是 char ** 類型,兩個(gè)*號(hào)
int nRow, nColumn;
int i , j;
int index;
result = sqlite3_open( “c://Dcg_database.db”, &db );
if( result != SQLITE_OK )
{


//數(shù)據(jù)庫打開失敗
return -1;
}


//數(shù)據(jù)庫操縱代碼
//假設(shè)前面已經(jīng)創(chuàng)建了 MyTable_1 表
//開端查詢,傳入的 dbResult 已經(jīng)是 char **,這里又加了一個(gè) & 取地址符,傳遞進(jìn)去的就成了 char ***
result = sqlite3_get_table( db, “ * MyTable_1”, &dbResult, &nRow, &nColumn, &errmsg );
if( SQLITE_OK == result )
{
//查詢成功


index = nColumn; //前面說過 dbResult 前面第一行數(shù)據(jù)是字段名稱,從 nColumn 索引開端才是真正的數(shù)據(jù)
printf( “查到%d筆記錄/n”, nRow );
for( i = 0; i < nRow ; i++ )
{
printf( “第 %d 筆記錄/n”, i+1 );
for( j = 0 ; j < nColumn; j++ )
{
printf( “字段名:%s ?> 字段值:%s/n”, dbResult[j], dbResult [index] );
++index; // dbResult 的字段值是連氣兒的,從第0索引到第 nColumn - 1索引都是字段名稱,從第 nColumn 索引開端,后面都是字段值,它把一個(gè)二維的表(傳統(tǒng)的行列默示法)用一個(gè)扁平的情勢(shì)來默示
}
printf( “-------/n” );
}
}
//到這里,非論數(shù)據(jù)庫查詢是否成功,都開釋 char** 查詢成果,應(yīng)用 sqlite 供給的功能來開釋
sqlite3_free_table( dbResult );


//封閉數(shù)據(jù)庫
sqlite3_close( db );
return 0;
}


到這個(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)制
sqlite 操縱二進(jìn)制數(shù)據(jù)須要用一個(gè)幫助的數(shù)據(jù)類型:sqlite3_stmt * 。


這個(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ù)是 sqlite3_stmt 的指針的指針。解析今后的sql語句就放在這個(gè)布局里。
第五個(gè)參數(shù)我也不知道是干什么的。為0就可以了。


若是這個(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 * 類型變量。
第2個(gè)參數(shù):?號(hào)的索引。前面prepare 的sql語句里有一個(gè)?號(hào),假如有多個(gè)?號(hào)怎么插入?辦法就是改變 bind_blob 函數(shù)第2個(gè)參數(shù)。這個(gè)參數(shù)我寫1,默示這里插入的值要調(diào)換 stat 的第一個(gè)?號(hào)(這里的索引從1開端計(jì)數(shù),而非從0開端)。若是你有多個(gè)?號(hào),就寫多個(gè) bind_blob 語句,并改變它們的第2個(gè)參數(shù)就調(diào)換到不合的?號(hào)。若是有?號(hào)沒有調(diào)換,sqlite為它取值null。
第3個(gè)參數(shù):二進(jìn)制數(shù)據(jù)肇端指針。
第4個(gè)參數(shù):二進(jìn)制數(shù)據(jù)的長度,以字節(jié)為單位。
第5個(gè)參數(shù):是個(gè)析夠回調(diào)函數(shù),告訴sqlite當(dāng)把數(shù)據(jù)處理懲罰完后調(diào)用此函數(shù)來析夠你的數(shù)據(jù)。這個(gè)參數(shù)我還沒有應(yīng)用過,是以懂得也不深切。然則一般都填NULL,須要開釋的內(nèi)存本身用代碼來開釋。


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ò)大出加密模塊。



i.1 須要的宏


經(jīng)由過程瀏覽 Sqlite 代碼(當(dāng)然沒有全部瀏覽完,6萬多行代碼,沒有一行是我習(xí)慣的風(fēng)格,我可沒那么多眼神去看),我搞清楚了兩件事:


Sqlite是支撐加密擴(kuò)大的;


須要 #define 一個(gè)宏才干應(yīng)用加密擴(kuò)大。


這個(gè)宏就是


SQLITE_HAS_CODEC。



你在代碼最前面(也可以在 sqlite3.h 文件第一行)定義:


#ifndef SQLITE_HAS_CODEC


#define SQLITE_HAS_CODEC


#endif



若是你在代碼里定義了此宏,然則還可以或許正常編譯,那么應(yīng)當(dāng)是操縱沒有成功。因?yàn)槟銘?yīng)當(dāng)會(huì)被編譯器提示有一些函數(shù)無法鏈接才對(duì)。 若是你用的是 VC 2003,你可以在“解決規(guī)劃”里右鍵點(diǎn)擊你的工程,然后選“屬性”,找到“C/C ”,再找到“號(hào)令行”,在里面手工添加“/D "SQLITE_HAS_CODEC"”。


定義了這個(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 中被引用



error LNK2019: 無法解析的外部符號(hào) _sqlite3_key ,該符號(hào)在函數(shù) _sqlite3Pragma 中被引用


fatal error LNK1120: 4 個(gè)無法解析的外部號(hào)令



這是正常的,因?yàn)镾qlite只留了接口罷了,并沒有給出實(shí)現(xiàn)。


下面就讓我來實(shí)現(xiàn)這些接口。



i.2 本身實(shí)現(xiàn)加解密接口函數(shù)


若是真要我從一份 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)用。



這里要說一點(diǎn)的是,我別的新建了兩個(gè)文件:crypt.c和crypt.h。


此中crypt.h如此定義:


#ifndef DCG_SQLITE_CRYPT_FUNC_



#define DCG_SQLITE_CRYPT_FUNC_


 


***********/


int My_DeEncrypt_Func( unsigned char * pData, unsigned int data_len, const char * key, unsigned int len_of_key );



#endif


 


此中的 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è)值,不過還是建議沒有須要不要去改它。



上方寫了兩個(gè)擴(kuò)大函數(shù),如何把擴(kuò)大函數(shù)跟 Sqlite 掛接起來,這個(gè)過程說起來斗勁麻煩。我直接貼代碼。


分3個(gè)步調(diào)。


起首,在 sqlite3.c 文件頂部,添加下面內(nèi)容:



#ifdef SQLITE_HAS_CODEC


#include "./crypt.h"


void sqlite3pager_free_codecarg(void *pArg);


#endif


這個(gè)函數(shù)之所以要在 sqlite3.c 開首聲明,是因?yàn)橄旅嬖?sqlite3.c 里面某些函數(shù)里要插入這個(gè)函數(shù)調(diào)用。所以要提前聲明。



其次,在sqlite3.c文件里搜刮“sqlite3PagerClose”函數(shù),要找到它的實(shí)現(xiàn)代碼(而不是聲明代碼)。


實(shí)現(xiàn)代碼里一開端是:


#ifdef SQLITE_ENABLE_MEMORY_MANAGEMENT



ThreadData *pTsd = sqlite3ThreadData();


assert( pPager );


assert( pTsd && pTsd->nAlloc );


#endif



須要在這項(xiàng)目組后面緊接著插入:



#ifdef SQLITE_HAS_CODEC


sqlite3pager_free_codecarg(pPager->pCodecArg);


#endif



這里要重視,sqlite3PagerClose 函數(shù)可能也是 3.3.17版本閣下才改名的,以前版本里是叫 “sqlite3pager_close”。是以你在老版本sqlite代碼里搜刮“sqlite3PagerClose”是搜不到的。


類似的還有“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)存。



這里我給出我已經(jīng)批改好的 sqlite3.c 和 sqlite3.h 文件。


若是太懶,就直接應(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 );



用 sqlite3_key 函數(shù)來提交暗碼。


第1個(gè)參數(shù)是 sqlite3 * 類型變量,代表著用 sqlite3_open 打開的數(shù)據(jù)庫(或新建數(shù)據(jù)庫)。


第2個(gè)參數(shù)是密鑰。


第3個(gè)參數(shù)是密鑰長度。


用 sqlite3_rekey 來批改暗碼。參數(shù)含義同 sqlite3_key。



實(shí)際上,你可以在sqlite3_open函數(shù)之后,到 sqlite3_close 函數(shù)之前隨便率性地位調(diào)用 sqlite3_key 來設(shè)置暗碼。


然則若是你沒有設(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



#ifndef DB_KEY_PADDING


#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_key(sqlite3 *db, const void *pKey, int nKey);


int sqlite3_rekey(sqlite3 *db, const void *pKey, int nKey);


 


 



// 從用戶供給的緩沖區(qū)中獲得一個(gè)加密密鑰


// 用戶供給的密鑰可能位數(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ū).



static LPCryptBlock CreateCryptBlock(unsigned char* hKey, Pager *pager, LPCryptBlock pExisting);


//加密/解密函數(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);



static void * sqlite3pager_get_codecarg(Pager *pPager);


void sqlite3pager_set_codec(Pager *pPager,void *(*xCodec)(void*,void*,Pgno,int),void *pCodecArg );


 



//加密/解密函數(shù), 被pager調(diào)用


void * sqlite3Codec(void *pArg, unsigned char *data, Pgno nPageNum, int nMode)


{


LPCryptBlock pBlock = (LPCryptBlock)pArg;


unsigned int dwPageSize = 0;


if (!pBlock) return data;



// 確保pager的頁長度和加密塊的頁長度相等.若是改變,就須要調(diào)劑.


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;



memcpy(pBlock->Data CRYPT_OFFSET, data, pBlock->PageSize);


data = pBlock->Data CRYPT_OFFSET;


 


dwPageSize = pBlock->PageSize;


My_Encrypt_Func(data , dwPageSize, pBlock->WriteKey, DB_KEY_LENGTH_BYTE );


break;



case 7: //加密事務(wù)文件的頁


if (!pBlock->ReadKey) break;



memcpy(pBlock->Data CRYPT_OFFSET, data, pBlock->PageSize);


data = pBlock->Data CRYPT_OFFSET;


dwPageSize = pBlock->PageSize;


My_Encrypt_Func( data, dwPageSize, pBlock->ReadKey, DB_KEY_LENGTH_BYTE );


break;


}



return data;


}


 


//


燒毀一個(gè)加密塊及相干的緩沖區(qū),密鑰.



static void DestroyCryptBlock(LPCryptBlock pBlock)


{


//燒毀讀密鑰.


if (pBlock->ReadKey){


sqliteFree(pBlock->ReadKey);


}



//若是寫密鑰存在并且不便是讀密鑰,也燒毀.


if (pBlock->WriteKey && pBlock->WriteKey != pBlock->ReadKey){


sqliteFree(pBlock->WriteKey);


}


if(pBlock->Data){


sqliteFree(pBlock->Data);


}



//開釋加密塊.


sqliteFree(pBlock);


}



static void * sqlite3pager_get_codecarg(Pager *pPager)


{


return (pPager->xCodec) ? pPager->pCodecArg: NULL;


}


// 從用戶供給的緩沖區(qū)中獲得一個(gè)加密密鑰



static unsigned char * DeriveKey(const void *pKey, int nKeyLen)


{


unsigned char * hKey = NULL;


int j;



if( pKey == NULL || nKeyLen == 0 )


{


return NULL;


}



hKey = sqliteMalloc( DB_KEY_LENGTH_BYTE 1 );


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



{ //密鑰位數(shù)已經(jīng)足夠,直接把密鑰取過來


memcpy( hKey, pKey, DB_KEY_LENGTH_BYTE );


}



return hKey;


}


 


//創(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 //更新存在的加密塊


{



pBlock = pExisting;


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);



return pBlock;


}


 


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);


}



int sqlite3_rekey(sqlite3 *db, const void *pKey, int 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;



//若是沒有指定密匙,可能標(biāo)識(shí)用了主數(shù)據(jù)庫的加密或沒加密.


if (!pKey || !nKeyLen)


{


if (!nDb)


{


return SQLITE_OK; //主數(shù)據(jù)庫, 沒有指定密鑰所以沒有加密.


}



else //附加數(shù)據(jù)庫,應(yīng)用主數(shù)據(jù)庫的密鑰.


{


//獲取主數(shù)據(jù)庫的加密塊并復(fù)制密鑰給附加數(shù)據(jù)庫應(yīng)用


LPCryptBlock pBlock = (LPCryptBlock)sqlite3pager_get_codecarg(sqlite3BtreePager(db->aDb[0].pBt));



if (!pBlock) return SQLITE_OK; //主數(shù)據(jù)庫沒有加密


if (!pBlock->ReadKey) return SQLITE_OK; //沒有加密



memcpy(pBlock->ReadKey, &hKey, 16);


}


}



else //用戶供給了暗碼,從中創(chuàng)建密鑰.


{


hKey = DeriveKey(pKey, nKeyLen);


}



//創(chuàng)建一個(gè)新的加密塊,并將解碼器指向新的附加數(shù)據(jù)庫.


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;


}



return rc;


}



// Changes the encryption key for an existing database.


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;



//從頭加密一個(gè)數(shù)據(jù)庫,改變pager的寫密鑰, 讀密鑰依舊保存.


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;


}



// 開端一個(gè)事務(wù)


rc = sqlite3BtreeBeginTrans(pbt, 1);



if (!rc)


{


// 用新密鑰重寫所有的頁到數(shù)據(jù)庫。


Pgno nPage = sqlite3PagerPagecount(p);


Pgno nSkip = PAGER_MJ_PGNO(p);



void *pPage;


Pgno n;



for(n = 1; rc == SQLITE_OK && n <= nPage; n )


{


if (n == nSkip) continue;


rc = sqlite3PagerGet(p, n, &pPage);


if(!rc)


{


rc = sqlite3PagerWrite(pPage);


sqlite3PagerUnref(pPage);


}


}



}



// 若是成功,提交事務(wù)。


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;


}


 



// 若是讀密鑰和寫密鑰皆為空,就不須要再對(duì)頁進(jìn)行編解碼。


// 燒毀加密塊并移除頁的編解碼器



if (!pBlock->ReadKey && !pBlock->WriteKey)


{


sqlite3pager_set_codec(p, NULL, NULL);


DestroyCryptBlock(pBlock);


}



return rc;


}


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);


}



#endif //#ifdef SQLITE_HAS_CODEC

    本站是提供個(gè)人知識(shí)管理的網(wǎng)絡(luò)存儲(chǔ)空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點(diǎn)。請(qǐng)注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購買等信息,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請(qǐng)點(diǎn)擊一鍵舉報(bào)。
    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評(píng)論

    發(fā)表

    請(qǐng)遵守用戶 評(píng)論公約

    類似文章 更多

    国内精品伊人久久久av高清| 国产日韩久久精品一区| 大香蕉网国产在线观看av| 日韩精品你懂的在线观看| 少妇高潮呻吟浪语91| 亚洲中文字幕在线视频频道| 开心五月激情综合婷婷色| 亚洲一区二区三区精选| 精品国产一区二区欧美| 大尺度剧情国产在线视频| 国产三级黄片在线免费看| 亚洲欧美精品伊人久久| 亚洲清纯一区二区三区| 日韩高清中文字幕亚洲| 欧美一级内射一色桃子| 区一区二区三中文字幕| 日韩精品区欧美在线一区| 老熟妇乱视频一区二区| 人人妻人人澡人人夜夜| 亚洲中文字幕在线综合视频| 肥白女人日韩中文视频| 欧美午夜一区二区福利视频| 国产精品白丝久久av| 国产精品欧美激情在线| 亚洲精品偷拍视频免费观看| 伊人久久五月天综合网| 冬爱琴音一区二区中文字幕| 久久91精品国产亚洲| 国产日韩熟女中文字幕| 老司机激情五月天在线不卡 | 精品日韩视频在线观看| 青青操视频在线观看国产| 激情图日韩精品中文字幕| 国产精品内射婷婷一级二级| 人人爽夜夜爽夜夜爽精品视频| 国产精品免费不卡视频| 国产成人亚洲欧美二区综| 夜色福利久久精品福利| 午夜精品福利视频观看| 亚洲国产精品久久精品成人| 欧美大胆美女a级视频|