若要編譯SQLite庫,需要下載單一檔案版本sqlite-amalgamation-3071400.zip。它把sqlite3庫的所有源碼檔案內容放到一個檔案sqlite3.c中了(不包含管理工具shell.c),這個檔案大概有110000多行,如果除去空白行和注釋,則有65000多行的代碼!這樣做的好處是很容易應用在你的項目中,只需拷貝這一個源檔案到你項目中即可。另外,編譯器在編譯單一的檔案時能做一些額外的最佳化,因為只有一個編譯單元。通過測試發現大概有5%-10%的效能提升。
SQLite庫可以不編譯,直接把單一檔案sqlite3.c(或者再加上sqlite3.h)拷貝到你的項目中使用即可。但作為一個單獨的庫,一般建議編譯成獨立的二進位檔案格式的庫,如Linux下的.so動態連結程式庫,Windows下的DLL動態連結程式庫,然後在項目中通過標頭檔sqlite3.h來使用這個庫,這樣能使軟體更加地模組化。
1、Linux下編譯SQLite
(1)編譯命令列管理工具:gcc shell.c sqlite3.c -lpthread -ldl -o sqlite3
將產生sqlit3命令列管理工具。
(2)編譯SQLite為單獨的動態連結程式庫:gcc sqlite3.c -lpthread -ldl -fPIC -shared -o libsqlite3.so
-fPIC:表示編譯為位置獨立的代碼,不用此選項的話編譯後的代碼是位置相關的所以動態載入時是通過代碼拷貝的方式來滿足不同進程的需要,而不能達到真正程式碼片段共用的目的。
-shared:表示產生一個共用目標檔案(讓連接器產生T類型的匯出符號表,有時候也產生弱串連W類型的匯出符號),即我們所說的動態連結程式庫。它可以和其他目標檔案串連產生可執行檔。只有部分系統支援該選項。
pthread系統庫用於確保SQLite是安全執行緒的。但因為命令列工具是單線程的,對命令列工具則可編譯成非安全執行緒的,以忽略pthread庫。命令為gcc -DSQLITE_THREADSAFE=0 shell.c sqlite3.c -ldl -o sqlite3。dl系統庫用於支援動態裝載,sqlite3_load_extension()介面和SQL函數load_extension()需要用到它。如果不需要這些特性,可以使用SQLITE_OMIT_LOAD_EXTENSION編譯選項來忽略,如gcc -DSQLITE_THREADSAFE=0
-DSQLITE_OMIT_LOAD_EXTENSION shell.c sqlite3.c -o sqlite3。
使用動態庫libsqlite3.so:在你的程式中(例如test.c)通過包含標頭檔sqlite3.h來使用庫中的函數,編譯器的命令為gcc test.c -L. -lsqlite3 -o test。其中-L.表示讓連結庫的搜尋路徑包含目前的目錄,-lsqlite3指明編譯器尋找動態庫libsqlite3.so,編譯器尋找動態串連庫時有隱含的命名規則,即在給出的名字前面加上lib,後面加上.so來確定庫的名稱。通過ldd test可查看test程式是如何調用動態庫中的函數的。
調用動態庫時有幾個問題會經常碰到。有時明明已經將庫的標頭檔所在目錄通過 “-I” include進來了,庫所在檔案通過 “-L”參數引導,並指定了“-l”的庫名,但通過ldd命令察看時,就是死活找不到你指定連結的so檔案,這時你要作的就是修改LD_LIBRARY_PATH,這個環境變數指示動態連接器可以裝載動態庫的路徑。或者修改/etc/ld.so.conf檔案,然後調用/sbin/ldconfig來達到同樣的目的。通常這樣做就可以解決庫無法連結的問題了。
(3)編譯成靜態庫:gcc -c sqlite3.c -lpthread -ldl -o sqlite3.o編譯成目標檔案,ar -r libsqlite3.a sqlite3.o將列出的各個目標檔案一起打包成一個靜態庫libsqlite3.a。
連結靜態庫:gcc test.c -L. -lsqlite3 -static -o test。也可不加-static選項。
2、Windows下編譯SQLite
(1)把SQLite編譯成動態連結程式庫:
開啟Visual Studio 2010,建立一個名為sqlite3的Visual C++ Win32工程,在工程嚮導頁中選擇工程的類型為 "DLL", 並且把建立為空白項目的複選框鉤上。通過工程--->添加現有項...,把單一檔案sqlite3.c添加到工程中。為了產生在連結sqlite3.dll時需要用到的lib檔案,需要在工程中添加模組定義檔案。根據sqlite3.h中列出的匯出函數名,我們可以自己寫.def檔案,例如:
EXPORTS
sqlite3_aggregate_context
sqlite3_aggregate_count
sqlite3_auto_extension
sqlite3_backup_finish
sqlite3_backup_init
sqlite3_backup_pagecount
sqlite3_backup_remaining
sqlite3_backup_step
sqlite3_bind_blob
sqlite3_bind_double
;......
也可以使用已寫好的.def檔案,下載已編譯好的SQLite DLL庫sqlite-dll-win32-x86-3071400.zip,裡面有sqlite3.def,把它拷貝到我們的sqlite3項目中,在工程屬性的Linker--->Input--->Module Definition File中輸入sqlite3.def。設定項目編譯成Release版本,編譯後產生sqlite3.dll和sqlite3.lib。
編譯命令列管理工具:如果想編譯出sqlite.exe命令列程式,則需要建立一個空的Win32 控制台程式,然後在往工程裡添加檔案sqlite3.c和shell.c,直接編譯即可。
使用動態連結程式庫sqlite3.dll:
本例子整理自http://sqlite.org/quickstart.html。建立一個空項目test,把sqlite3.dll, sqlite3.lib, sqlite3.h拷貝到項目目錄下,把sqlite3.h添加到項目中。建立主程式源檔案test.cpp,如下:
[cpp]
view plaincopyprint?
- #include <stdio.h>
- #include "sqlite3.h"
- #pragma comment(lib,"sqlite3")
- /* print a record from table outputed by sql statement */
- static int callback(void *NotUsed,
int argc, char **argv,
char **azColName){
- int i;
- for(i=0; i<argc; i++){
- printf("%s = %s\n", azColName[i], argv[i]?argv[i]:"NULL");
- }
- printf("\n");
- return 0;
- }
- int main(int argc,
char **argv){
- sqlite3 *db;
- char *zErrMsg=0;
- int rc;
- if(argc!=3){
- fprintf(stderr, "Usage: %s DATABASE SQL-STATEMENT\n", argv[0]);
- return(1);
- }
- rc=sqlite3_open(argv[1],&db); /* open database */
- if(rc){
- fprintf(stderr, "Can't open database: %s\n", sqlite3_errmsg(db));
- sqlite3_close(db);
- return(1);
- }
- rc=sqlite3_exec(db,argv[2],callback,0,&zErrMsg); /* execute SQL statement */
- if(rc!=SQLITE_OK){
- fprintf(stderr,"SQL error: %s\n",zErrMsg);
- sqlite3_free(zErrMsg);
- }
- sqlite3_close(db); /* close database */
- return 0;
- }
#include <stdio.h>#include "sqlite3.h"#pragma comment(lib,"sqlite3")/* print a record from table outputed by sql statement */static int callback(void *NotUsed, int argc, char **argv, char **azColName){int i;for(i=0; i<argc; i++){printf("%s = %s\n", azColName[i], argv[i]?argv[i]:"NULL");}printf("\n");return 0;}int main(int argc, char **argv){sqlite3 *db;char *zErrMsg=0;int rc;if(argc!=3){fprintf(stderr, "Usage: %s DATABASE SQL-STATEMENT\n", argv[0]);return(1);}rc=sqlite3_open(argv[1],&db); /* open database */if(rc){fprintf(stderr, "Can't open database: %s\n", sqlite3_errmsg(db));sqlite3_close(db);return(1);}rc=sqlite3_exec(db,argv[2],callback,0,&zErrMsg); /* execute SQL statement */if(rc!=SQLITE_OK){fprintf(stderr,"SQL error: %s\n",zErrMsg);sqlite3_free(zErrMsg);}sqlite3_close(db); /* close database */return 0;}
編譯後產生test.exe程式,它的運行依賴於sqlite3.dll。注意程式中也可以不使用pragma指令匯入sqlite3.lib,而是在test項目屬性中添加對上面的dll項目sqlit3的引用。
這個C程式的例子顯示怎麼使用sqlite的C/C++介面。資料庫的名字由第一個參數取得,第二個參數是一條或更多的SQL執行語句。這個程式調用sqlite3_open()開啟指定的資料庫,調用sqlite3_exec()對資料庫執行SQL語句,callback函數會作用在SQL語句結果集的每條記錄上。最後用sqlite3_close()關閉資料庫連接。可以用前面的alf.db資料庫來測試:test.exe alf.db "select * from mytable"。
(2)把SQLite編譯成靜態連結庫:
建立一個名為sqlite3static,空的Visual C++ Win32工程,在工程嚮導頁中選擇工程的類型為 "Static library",去年"Precompiled header"複選框,匯入源檔案sqlite3.c,編譯產生sqlite3static.lib,這是靜態連結庫版本,它要比動態連結程式庫sqlite3.dll大的多。
使用靜態連結庫:
建立一個空項目sqlite3statictest,添加對項目sqlite3static的引用,同樣使用上面的test.cpp,編譯產生sqlite3statictest.exe。可以發現它是一個獨立的可執行檔,不依賴靜態連結庫,刪除靜態連結庫後仍然可以運行。
實際上,靜態連結庫不存在匯出的概念,在連結過程中,靜態連結庫LIB中的指令都全部被直接包含在最終產生的EXE檔案中了,因此如果用的是靜態連結庫,那麼也就不存在“匯出某個函數提供給使用者使用”的情況。對於使用該靜態庫的EXE檔案來說,直接使用LIB中的函數和全域變數即可,而不管它們有沒有匯出聲明。另外一點在使用中需要注意的是:靜態連結庫中不能再包含其他的動態連結程式庫或者靜態庫,而在動態連結程式庫中還可以再包含。
3、多線程環境中使用SQLite
內容整理自http://sqlite.org/threadsafe.html。SQLite支援三種不同的線程模式:
* 單線程。在這種模式下所有的互斥鎖都被禁用,在多個線程中同時使用SQLite時是不安全的。
* 多線程。在這種模式下,只要沒有單個資料庫連接被同時用在多個線程中的情況,SQLite就可以在多線程環境中安全地使用。
* 序列化。在這種模式中,SQLite可以無限制地在多線程環境中安全地使用。
線程模式可以在編譯時間(把SQLite原始碼編譯成庫時)、啟動時(使用SQLite的應用程式初始化時)或運行時(一個新的SQLite資料庫連接建立時)指定。一般來說,運行時參數會覆蓋掉啟動時參數,啟動時參數會覆蓋掉編譯時間參數,但是單線程模式一旦被指定後,就不能被覆蓋。預設的模式是串列模式。
(1)編譯時間的線程模式選項
通過SQLITE_THREADSAFE編譯時間參數來選擇線程模式。如果沒有指定SQLITE_THREADSAFE參數,則使用序列化模式。也可以顯式地使用-DSQLITE_THREADSAFE=1來指定序列化模式。-DSQLITE_THREADSAFE=0表示單線程模式,-DSQLITE_THREADSAFE=2表示多線程模式。
sqlite3_threadsafe()介面的傳回值由編譯時間線程模式選項來確定。如果編譯時間指定單線程模式,則sqlite3_threadsafe()返回false。如果指定多線程模式或序列化模式,則sqlite3_threadsafe()返回true。sqlite3_threadsafe()不能區分多線程模式和序列化模式,也不能報告啟動時或運行時的模式更改。
如果編譯時間指定單線程模式,則編譯庫時關鍵的互斥邏輯會被忽略,因此不可能在啟動時或運行不可能再啟用多線程或序列化模式。
(2)啟動時的線程模式選項
如果編譯時間沒有指定單線程模式,則在使用sqlite3_config()介面進行初始化時可以改變線程模式。SQLITE_CONFIG_SINGLETHREAD謂詞把SQLite設定成單線程模式,SQLITE_CONFIG_MULTITHREAD設定多線程模式,SQLITE_CONFIG_SERIALIZED設定序列化模式。
(3)運行時的線程模式選項
如果編譯時間或啟動時沒有指定單線程模式,則單個資料連線可以被建立為多線程或序列化模式,不可能將單個資料庫連接降級為單線程模式。如果編譯時間或啟動時指定單線程模式,則不可能將單個資料庫連接升級為多線程或序列化模式。
單個資料庫連接的線程模式由sqlite3_open_v2()的第三個參數給定的標誌來確定。SQLITE_OPEN_NOMUTEX標誌表示資料庫連接為多線程模式,SQLITE_OPEN_FULLMUTEX表示該串連為序列化模式。如果沒有指定標誌,或者使用sqlite3_open(), sqlite3_open16(),而不是sqlite3_open_v2(),則使用編譯時間或啟動時指定的線程模式。