SQLite剖析之C/C++接口


前言

  SQLite3是SQLite一個全新的版本,它雖然是在SQLite2的代碼基礎之上開發的,但是使用了和之前的版本不兼容的數據庫格式和API。SQLite3是為了滿足以下的需求而開發的:支持UTF-16編碼、用戶自定義的文本比較方法、可以對BLOBs字段建立索引。SQLite 3.X版的和SQLite 2.X版的API非常相似,但是有一些重要的改變需要注意。3.X版的API增加到超過185個,所有API接口函數和數據結構的前綴都由"sqlite_"改為了"sqlite3_",這是為了避免同時使用SQLite 2.X和SQLite 3.X這兩個版本的時候發生鏈接沖突。這里概要地介紹一下SQLite的核心API,詳細的API指南參考http://sqlite.com/capi3ref.html。
  由於對於C語言應用什么數據類型來存放UTF-16編碼的字符串並沒有一致的規范,因此SQLite使用了普通的void*類型來指向UTF-16編碼的字符串。客戶端使用過程中可以把void*映射成適合他們的系統的任何數據類型。

  一個SQL數據庫引擎的首要任務是執行SQL語句以獲得我們想要的數據。為了完成這個任務,開發需要知道兩個對象數據庫連接對象sqlite3和SQL預處理語句對象sqlite3_stmt,定義如下:
    typedef struct sqlite3 sqlite3;
    typedef struct sqlite3_stmt sqlite3_stmt;
  嚴格地說,SQL預處理語句對象不是必需的,因為有使用方便的包裝函數sqlite3_exec或sqlite3_get_table,它們封裝並且隱藏了SQL語句對象。不過理解SQL語句對象能更好地使用SQLite。

  數據庫連接對象和SQL語句對象由下面幾個核心的C/C++接口來控制:sqlite3_open()、sqlite3_prepare()、sqlite3_step()、sqlite3_column()、sqlite3_finalize()、sqlite3_close()。
  使用SQLite3時根據以上函數大概分為幾個過程,這幾個過程是概念上的說法,而不完全是程序運行的過程,如sqlite3_column()表示的是對查詢獲得一行里面的數據的列的各個操作統稱,實際上在sqlite中並不存在這個函數。在SQLite提供的C/C++接口中,其中5個API屬於核心接口。相比於其它數據庫引擎提供的API,如OCI、MySQL API等,SQLite提供的接口易於理解和掌握。以上六個C/C++接口及上面的兩個對象構成SQLite的核心功能。注意這些接口有些有多個版本,例如sqlite3_open()有三個獨立的版本:sqlite3_open(), sqlite3_open16()和sqlite3_open_v2(),它們以稍微不同的方式完成同樣的事情。sqlite3_column()代表一個家族系列:sqlite_column_int(), sqlite_column_blob()等等,用於提取結果集中各種類型的列數據。  

核心對象和接口:
1. 核心對象:
  在SQLite中最主要的兩個對象是:database_connectionprepared_statement。database_connection對象是由sqlite3_open()接口函數創建並返回的,在應用程序使用任何其他SQLite接口函數之前,必須先調用該函數以便獲得database_connnection對象,在隨后的其他API調用中,都需要該對象作為輸入參數以完成相應的工作。至於prepare_statement,我們可以簡單地將它視為編譯后的SQL語句,因此,所有和SQL語句執行相關的函數也都需要該對象作為輸入參數以完成指定的SQL操作。
2. 核心接口:
    1). sqlite3_open
    上面已經提到過這個函數了,它是操作SQLite數據庫的入口函數。該函數返回的database_connection對象是很多其他SQLite API的句柄參數。注意,我們通過該函數既可以打開已經存在的數據庫文件,也可以創建新的數據庫文件。對於該函數返回的database_connection對象,我們可以在多個線程之間共享該對象的指針,以便完成和數據庫相關的任意操作。然而在多線程情況下,更為推薦的使用方式是,為每個線程創建獨立的database_connection對象。對於該函數還有一點也需額外說明,我們沒有必要為了訪問多個數據庫而創建多個數據庫連接對象,因為通過SQLite自帶的ATTACH命令可以在一個連接中方便的訪問多個數據庫。
    2). sqlite3_prepare
    該函數將SQL文本轉換為prepared_statement對象,並在函數執行后返回該對象的指針。事實上,該函數並不會評估參數指定SQL語句,它僅僅是將SQL文本初始化為待執行的狀態。最后需要指出的,對於新的應用程序,可以使用sqlite3_prepare_v2接口函數來替代該函數以完成相同的工作。
    3). sqlite3_step
    該函數用於評估sqlite3_prepare函數返回的prepared_statement對象,在執行完該函數之后,prepared_statement對象的內部指針將指向其返回的結果集的第一行。如果打算進一步迭代其后的數據行,就需要不斷的調用該函數,直到所有的數據行都遍歷完畢。然而對於INSERT、UPDATE和DELETE等DML語句,該函數執行一次即可完成。
    4). sqlite3_column
    該函數用於獲取當前行指定列的數據,然而嚴格意義上講,此函數在SQLite的接口函數中並不存在,而是由一組相關的接口函數來完成該功能,其中每個函數都返回不同類型的數據,如:
    sqlite3_column_blob
    sqlite3_column_bytes
    sqlite3_column_bytes16
    sqlite3_column_double
    sqlite3_column_int
    sqlite3_column_int64
    sqlite3_column_text
    sqlite3_column_text16
    sqlite3_column_type
    sqlite3_column_value
    sqlite3_column_count
    其中,sqlite3_column_count函數用於獲取當前結果集中的字段數目。下面是使用sqlite3_step和sqlite3_column函數迭代結果集中每行數據的偽代碼(注意這里作為示例代碼簡化了對字段類型的判斷): 

int fieldCount = sqlite3_column_count(...);
while (sqlite3_step(...) <> EOF) {
    for (int i = 0; i < fieldCount; ++i) {
        int v = sqlite3_column_int(...,i);
    }
}

  5). sqlite3_finalize
    該函數用於銷毀prepared statement對象,否則將會造成內存泄露。
    6). sqlite3_close
    該函數用於關閉之前打開的database_connection對象,其中所有和該對象相關的prepared_statements對象都必須在此之前先被銷毀。

參數綁定:
  和大多數關系型數據庫一樣,SQLite的SQL文本也支持變量綁定,以便減少SQL語句被動態解析的次數,從而提高數據查詢和數據操作的效率。要完成該操作,我們需要使用SQLite提供的另外兩個接口API,sqlite3_resetsqlite3_bind。見如下示例:

void test_parameter_binding() {
  //1. 不帶參數綁定的情況下插入多條數據。
  char strSQL[128];
  for (int i = 0; i < MAX_ROWS; ++i) {
    sprintf(strSQL,"insert into testtable values(%d)",i);
    sqlite3_prepare_v2(..., strSQL);
    sqlite3_step(prepared_stmt);
    sqlite3_finalize(prepared_stmt);
  }
  //2. 參數綁定的情況下插入多條數據。
  string strSQLWithParameter = "insert into testtable values(?)";
  sqlite3_prepare_v2(..., strSQLWithParameter);
  for (int i = 0; i < MAX_ROWS; ++i) {
    sqlite3_bind(...,i);
    sqlite3_step(prepared_stmt);
    sqlite3_reset(prepared_stmt);
  }
  sqlite3_finalize(prepared_stmt);
}

  這里首先需要說明的是,SQL語句"insert into testtable values(?)"中的問號(?)表示參數變量的占位符,該規則在很多關系型數據庫中都是一致的,因此這對於數據庫移植操作還是比較方便的。

  通過上面的示例代碼,明顯可以看出參數綁定寫法的執行效率要高於每次生成不同的SQL語句的寫法,即(2)在效率上要明顯優於(1),下面是針對這兩種寫法的具體比較:
   1.單單從程序表面來看,前者在for循環中執行了更多的任務,比如字符串的填充、SQL語句的prepare,以及prepared_statement對象的釋放。
   2.在SQLite的官方文檔中明確的指出,sqlite3_prepare_v2的執行效率往往要低於sqlite3_step的效率。
   3.當插入的數據量較大時,后者帶來的效率提升還是相當可觀的。

一、打開和關閉數據庫連接

int sqlite3_open(  
  const char *filename,   /* Database filename (UTF-8) */  
  sqlite3 **ppDb          /* OUT: SQLite db handle */  
);  
int sqlite3_open16(  
  const void *filename,   /* Database filename (UTF-16) */  
  sqlite3 **ppDb          /* OUT: SQLite db handle */  
);  
int sqlite3_open_v2(  
  const char *filename,   /* Database filename (UTF-8) */  
  sqlite3 **ppDb,         /* OUT: SQLite db handle */  
  int flags,              /* Flags */  
  const char *zVfs        /* Name of VFS module to use */  
);  
int sqlite3_close(sqlite3*);  
int sqlite3_close_v2(sqlite3*);  
int sqlite3_errcode(sqlite3 *db);  
int sqlite3_extended_errcode(sqlite3 *db);  
const char *sqlite3_errmsg(sqlite3*);  
const void *sqlite3_errmsg16(sqlite3*); 

  建立到一個SQLite數據庫文件的連接,返回連接對象。如果數據庫文件不存在,則創建這個文件,函數返回一個整數錯誤代碼。許多SQLite接口需要一個指向連接對象的指針作為第一個參數,這個函數用來創建一個數據庫連接對象。sqlite3_open()和sqlite3_open16()的不同之處在於sqlite3_open16()使用UTF-16編碼(使用本地主機字節順序)傳遞數據庫文件名。如果要創建新數據庫,sqlite3_open16()將內部文本轉換為UTF-16編碼,反之sqlite3_open()將文本轉換為UTF-8編碼。打開或者創建數據庫的命令會被緩存,直到這個數據庫真正被調用的時候才會被執行。而且允許使用PRAGMA聲明來設置如本地文本編碼或默認內存頁面大小等選項和參數。
  sqlite3_close()關閉數據庫連接,在關閉之前所有准備好的SQL語句對象都要被銷毀。
  sqlite3_errcode()通常用來獲取最近調用的API接口返回的錯誤代碼。sqlite3_errmsg()則用來得到這些錯誤代碼所對應的文字說明。這些錯誤信息將以UTF-8的編碼返回,並且在下一次調用任何SQLiteAPI函數的時候被清除。sqlite3_errmsg16()和sqlite3_errmsg()大體上相同,除了返回的錯誤信息將以UTF-16本機字節順序編碼。
  SQLite的返回碼定義如下:

#define SQLITE_OK           0   /* Successful result */  
/* beginning-of-error-codes */  
#define SQLITE_ERROR        1   /* SQL error or missing database */  
#define SQLITE_INTERNAL     2   /* Internal logic error in SQLite */  
#define SQLITE_PERM         3   /* Access permission denied */  
#define SQLITE_ABORT        4   /* Callback routine requested an abort */  
#define SQLITE_BUSY         5   /* The database file is locked */  
#define SQLITE_LOCKED       6   /* A table in the database is locked */  
#define SQLITE_NOMEM        7   /* A malloc() failed */  
#define SQLITE_READONLY     8   /* Attempt to write a readonly database */  
#define SQLITE_INTERRUPT    9   /* Operation terminated by sqlite3_interrupt()*/  
#define SQLITE_IOERR       10   /* Some kind of disk I/O error occurred */  
#define SQLITE_CORRUPT     11   /* The database disk image is malformed */  
#define SQLITE_NOTFOUND    12   /* Unknown opcode in sqlite3_file_control() */  
#define SQLITE_FULL        13   /* Insertion failed because database is full */  
#define SQLITE_CANTOPEN    14   /* Unable to open the database file */  
#define SQLITE_PROTOCOL    15   /* Database lock protocol error */  
#define SQLITE_EMPTY       16   /* Database is empty */  
#define SQLITE_SCHEMA      17   /* The database schema changed */  
#define SQLITE_TOOBIG      18   /* String or BLOB exceeds size limit */  
#define SQLITE_CONSTRAINT  19   /* Abort due to constraint violation */  
#define SQLITE_MISMATCH    20   /* Data type mismatch */  
#define SQLITE_MISUSE      21   /* Library used incorrectly */  
#define SQLITE_NOLFS       22   /* Uses OS features not supported on host */  
#define SQLITE_AUTH        23   /* Authorization denied */  
#define SQLITE_FORMAT      24   /* Auxiliary database format error */  
#define SQLITE_RANGE       25   /* 2nd parameter to sqlite3_bind out of range */  
#define SQLITE_NOTADB      26   /* File opened that is not a database file */  
#define SQLITE_ROW         100  /* sqlite3_step() has another row ready */  
#define SQLITE_DONE        101  /* sqlite3_step() has finished executing */  
/* end-of-error-codes */  

二、編譯SQL語句

int sqlite3_prepare(  
  sqlite3 *db,            /* Database handle */  
  const char *zSql,       /* SQL statement, UTF-8 encoded */  
  int nByte,              /* Maximum length of zSql in bytes. */  
  sqlite3_stmt **ppStmt,  /* OUT: Statement handle */  
  const char **pzTail     /* OUT: Pointer to unused portion of zSql */  
);  
int sqlite3_prepare_v2(  
  sqlite3 *db,            /* Database handle */  
  const char *zSql,       /* SQL statement, UTF-8 encoded */  
  int nByte,              /* Maximum length of zSql in bytes. */  
  sqlite3_stmt **ppStmt,  /* OUT: Statement handle */  
  const char **pzTail     /* OUT: Pointer to unused portion of zSql */  
);  
int sqlite3_prepare16(  
  sqlite3 *db,            /* Database handle */  
  const void *zSql,       /* SQL statement, UTF-16 encoded */  
  int nByte,              /* Maximum length of zSql in bytes. */  
  sqlite3_stmt **ppStmt,  /* OUT: Statement handle */  
  const void **pzTail     /* OUT: Pointer to unused portion of zSql */  
);  
int sqlite3_prepare16_v2(  
  sqlite3 *db,            /* Database handle */  
  const void *zSql,       /* SQL statement, UTF-16 encoded */  
  int nByte,              /* Maximum length of zSql in bytes. */  
  sqlite3_stmt **ppStmt,  /* OUT: Statement handle */  
  const void **pzTail     /* OUT: Pointer to unused portion of zSql */  
);  

  把SQL文本編譯成一個SQL語句對象並返回這個對象的指針。它只是把含有SQL語句的字符串編譯成字節碼,並不執行SQL語句。sqlite3_prepare()處理的SQL語句應該是UTF-8編碼的,而sqlite3_prepare16()則要求是UTF-16編碼的。輸入的參數中只有第一個SQL語句會被編譯。第四個參數則用來指向輸入參數中下一個需要編譯的SQL語句存放的SQLite statement對象的指針。任何時候如果調用sqlite3_finalize()將銷毀一個准備好的SQL聲明。在數據庫關閉之前,所有准備好的聲明都必須被釋放銷毀。sqlite3_reset()函數用來重置一個SQL聲明的狀態,使得它可以被再次執行。

  注意現在sqlite3_prepare()已經不被推薦使用了,在新的應用中推薦使用sqlite3_prepare_v2()。

三、執行SQL語句

int sqlite3_step(sqlite3_stmt*); 

  在SQL聲明准備好之后,就可以調用sqlite3_step()來執行這個SQL聲明。如果SQL返回了一個單行結果集,sqlite3_step()函數將返回SQLITE_ROW,若要得到結果集的第二行、第三行 ...,則要繼續調用sqlite3_step()。如果SQL語句執行成功或者正常將返回SQLITE_DONE,否則將返回錯誤代碼。如果不能打開數據庫文件則會返回SQLITE_BUSY。

  執行SQL語句還可以直接便捷的包裝函數,這樣就無需預先編譯SQL語句。如下:

typedef int (*sqlite3_callback)(void*,int,char**, char**);  
int sqlite3_exec(  
  sqlite3*,                                  /* An open database */  
  const char *sql,                           /* SQL to be evaluated */  
  int (*callback)(void*,int,char**,char**),  /* Callback function */  
  void *,                                    /* 1st argument to callback */  
  char **errmsg                              /* Error msg written here */  
);  

  sqlite3_exec函數依然像它在SQLite 2中一樣承擔着很多的工作。該函數的第二個參數中可以指定零個或多個SQL語句,查詢的結果返回給回調函數,回調函數會作用在結果集的每條記錄上。sqlite3_exec函數實際上封裝了sqlite3_prepare_v2(),sqlite3_step()和sqlite3_finalize(),因此可以通過一個調用直接執行多條SQL語句,讓應用程序省略大量代碼,因此在實際應用中一般使用這個函數。

四、獲取結果集數據

const void *sqlite3_column_blob(sqlite3_stmt*, int iCol);  
int sqlite3_column_bytes(sqlite3_stmt*, int iCol);  
int sqlite3_column_bytes16(sqlite3_stmt*, int iCol);  
const char *sqlite3_column_decltype(sqlite3_stmt *, int iCol);  
const void *sqlite3_column_decltype16(sqlite3_stmt *, int iCol);  
double sqlite3_column_double(sqlite3_stmt*, int iCol);  
int sqlite3_column_int(sqlite3_stmt*, int iCol);  
sqlite3_int64 sqlite3_column_int64(sqlite3_stmt*, int iCol);  
const unsigned char *sqlite3_column_text(sqlite3_stmt*, int iCol);  
const void *sqlite3_column_text16(sqlite3_stmt*, int iCol);  
int sqlite3_column_type(sqlite3_stmt*, int iCol);  
sqlite3_value *sqlite3_column_value(sqlite3_stmt*, int iCol);  
const char *sqlite3_column_name(sqlite3_stmt*, int N);  
const void *sqlite3_column_name16(sqlite3_stmt*, int N);  
int sqlite3_column_count(sqlite3_stmt *pStmt);  
int sqlite3_data_count(sqlite3_stmt *pStmt);  

  如果函數sqlite3_step()的返回值是SQLITE_ROW,那么用以上方法可以獲得記錄集中的數據。
  sqlite3_column_count()函數返回結果集中包含的列數,sqlite3_column_count()可以在執行了sqlite3_prepare()之后的任何時刻調用。sqlite3_data_count()除了必須在sqlite3_step()之后調用之外,其他跟sqlite3_column_count()大同小異。如果調用sqlite3_step()返回值是SQLITE_DONE或者一個錯誤代碼,則此時調用sqlite3_data_count()將返回0,然而sqlite3_column_count()仍然會返回結果集中包含的列數。
  返回的記錄集通過使用其它幾個sqlite3_column_***()函數來提取,所有的這些函數都把列的編號作為第二個參數。列編號從左到右以零起始,和之前那些從1起始的參數不同。
  sqlite3_column_type()函數返回第N列的值的數據類型,具體的返回值如下:

#define SQLITE_INTEGER  1  
#define SQLITE_FLOAT    2  
#define SQLITE_TEXT     3  
#define SQLITE_BLOB     4  
#define SQLITE_NULL     5  

  sqlite3_column_decltype()則用來返回該列在CREATE TABLE語句中聲明的類型,它可以用在當返回類型是空字符串的時候。
  sqlite3_column_name()返回第N列的字段名。
  sqlite3_column_bytes()用來返回UTF-8編碼的BLOB列的字節數或者TEXT字符串的字節數。sqlite3_column_bytes16()對於BLOB列返回同樣的結果,但是對於TEXT字符串則按UTF-16的編碼來計算字節數。
  sqlite3_column_blob()返回BLOB數據。
  sqlite3_column_text()返回UTF-8編碼的TEXT數據。sqlite3_column_text16()返回UTF-16編碼的TEXT數據。
  sqlite3_column_int()以本地主機的整數格式返回一個整數值。sqlite3_column_int64()返回一個64位的整數。
  sqlite3_column_double()返回浮點數。
  不一定非要按照sqlite3_column_type()接口返回的數據類型來獲取數據,數據類型不同時軟件將自動轉換。

五、SQL聲明對象的銷毀和重用

int sqlite3_finalize(sqlite3_stmt*);  
int sqlite3_reset(sqlite3_stmt*); 

  函數sqlite3_finalize()銷毀由sqlite3_prepare()創建的SQL聲明對象。在數據庫關閉之前每個准備好的聲明都必須被銷毀,以避免內存泄露。sqlite3_reset()則用來重置一個SQL聲明的狀態,使得它可以被再次執行。例如用sqlite3_step()執行完編譯好的SQL聲明后,還想再執行它,則可用sqlite3_reset()重置它即可,而無需用sqlite3_prepare()再來編譯一個新SQL聲明,因為很多SQL聲明的編譯時間甚至超過執行時間。

六、給SQl語句綁定參數

  SQL語句聲明中可以包含如下形式的參數:
    ?
    ?NNN
    :AAA
    $AAA
    @AAA
  其中"NNN"是一個整數,"AAA"是一個字符串,這些標記代表一些不確定的字符值(或者說是通配符)。在首次調用sqlite3_step()之前或者剛調用sqlite3_reset()之后,應用程序可以用sqlite3_bind接口來填充這些參數。每一個通配符都被分配了一個編號(由它在SQL聲明中的位置決定,從1開始),此外也可以用"NNN"來表示"?NNN"這種情況。允許相同的通配符在同一個SQL聲明中出現多次,在這種情況下所有相同的通配符都會被替換成相同的值。沒有被綁定的通配符將自動取NULL值。

int sqlite3_bind_blob(sqlite3_stmt*, int, const void*, int n, void(*)(void*));  
int sqlite3_bind_double(sqlite3_stmt*, int, double);  
int sqlite3_bind_int(sqlite3_stmt*, int, int);  
int sqlite3_bind_int64(sqlite3_stmt*, int, sqlite3_int64);  
int sqlite3_bind_null(sqlite3_stmt*, int);  
int sqlite3_bind_text(sqlite3_stmt*, int, const char*, int n, void(*)(void*));  
int sqlite3_bind_text16(sqlite3_stmt*, int, const void*, int, void(*)(void*));  
int sqlite3_bind_value(sqlite3_stmt*, int, const sqlite3_value*);  
int sqlite3_bind_zeroblob(sqlite3_stmt*, int, int n);  

  以上是sqlite3_bind所包含的全部接口,用來給SQL聲明中的通配符賦值。沒有綁定的通配符則被認為是空值。已綁定的值不會被sqlite3_reset()函數重置。但是在調用了sqlite3_reset()之后所有的通配符都可以被重新賦值。注意綁定操作是可選的。

七、擴展SQL

(1)創建自定義的比較序列:

int sqlite3_create_collation(  
  sqlite3*,   
  const char *zName,   
  int eTextRep,   
  void *pArg,  
  int(*xCompare)(void*,int,const void*,int,const void*)  
);  
int sqlite3_create_collation_v2(  
  sqlite3*,   
  const char *zName,   
  int eTextRep,   
  void *pArg,  
  int(*xCompare)(void*,int,const void*,int,const void*),  
  void(*xDestroy)(void*)  
);  
int sqlite3_create_collation16(  
  sqlite3*,   
  const void *zName,  
  int eTextRep,   
  void *pArg,  
  int(*xCompare)(void*,int,const void*,int,const void*)  
);  
int sqlite3_collation_needed(  
  sqlite3*,   
  void*,   
  void(*)(void*,sqlite3*,int eTextRep,const char*)  
);  
int sqlite3_collation_needed16(  
  sqlite3*,   
  void*,  
  void(*)(void*,sqlite3*,int eTextRep,const void*)  
);  

  這些函數在數據庫連接上,為要比較的文本添加、刪除或者修改自定義比較規則。第三個參數eTextRep表示SQLite支持的字符編碼類型,必須是以下常量之一:

#define SQLITE_UTF8           1  
#define SQLITE_UTF16LE        2  
#define SQLITE_UTF16BE        3  
#define SQLITE_UTF16          4    /* Use native byte order */  
#define SQLITE_ANY            5    /* sqlite3_create_function only */  
#define SQLITE_UTF16_ALIGNED  8    /* sqlite3_create_collation only */  

  sqlite3_create_collation()函數用來聲明一個比較序列和實現它的比較函數,比較函數只能用來做文本的比較。同一個自定義的比較規則的同一個比較函數可以有UTF-8、UTF-16LE和UTF-16BE等多個編碼的版本。sqlite3_create_collation16()和sqlite3_create_collation()的區別也僅僅在於比較名稱的編碼是UTF-16還是UTF-8。
  可以使用sqlite3_collation_needed()函數來注冊一個回調函數,當數據庫引擎遇到未知的比較規則時會自動調用該函數。在回調函數中可以查找一個相似的比較函數,並激活相應的sqlite_3_create_collation()函數。回調函數的第四個參數是比較規則的名稱。同樣sqlite3_collation_needed采用UTF-8編碼,sqlite3_collation_need16()采用UTF-16編碼。

(2)創建自定義的SQL函數:

int sqlite3_create_function(  
  sqlite3 *db,  
  const char *zFunctionName,  
  int nArg,  
  int eTextRep,  
  void *pApp,  
  void (*xFunc)(sqlite3_context*,int,sqlite3_value**),  
  void (*xStep)(sqlite3_context*,int,sqlite3_value**),  
  void (*xFinal)(sqlite3_context*)  
);  
int sqlite3_create_function16(  
  sqlite3 *db,  
  const void *zFunctionName,  
  int nArg,  
  int eTextRep,  
  void *pApp,  
  void (*xFunc)(sqlite3_context*,int,sqlite3_value**),  
  void (*xStep)(sqlite3_context*,int,sqlite3_value**),  
  void (*xFinal)(sqlite3_context*)  
);  
int sqlite3_create_function_v2(  
  sqlite3 *db,  
  const char *zFunctionName,  
  int nArg,  
  int eTextRep,  
  void *pApp,  
  void (*xFunc)(sqlite3_context*,int,sqlite3_value**),  
  void (*xStep)(sqlite3_context*,int,sqlite3_value**),  
  void (*xFinal)(sqlite3_context*),  
  void(*xDestroy)(void*)  
);  

  nArg參數用來表明自定義函數的參數個數。如果參數值為0,則表示接受任意個數的參數。用eTextRep參數來表明傳入參數的編碼形式。SQLite 3允許同一個自定義函數有多種不同的編碼參數的版本。數據庫引擎會自動選擇轉換參數編碼個數最少的版本使用。
  普通的函數只需要設置xFunc參數,而把xStep和xFinal設為NULL。聚合函數則需要設置xStep和xFinal參數,然后把xFunc設為NULL。該方法和使用sqlite3_create_aggregate() API一樣。
  sqlite3_create_function16()和sqlite_create_function()的不同就在於自定義的函數名一個要求是UTF-16編碼,而另一個則要求是UTF-8。
  自定函數的參數目前使用sqlite3_value結構體指針替代了SQLite version 2.X中的字符串指針。

  下面的函數用來從sqlite3_value結構體中提取數據,以獲得SQL函數的參數值:

const void *sqlite3_value_blob(sqlite3_value*);  
int sqlite3_value_bytes(sqlite3_value*);  
int sqlite3_value_bytes16(sqlite3_value*);  
double sqlite3_value_double(sqlite3_value*);  
int sqlite3_value_int(sqlite3_value*);  
sqlite3_int64 sqlite3_value_int64(sqlite3_value*);  
const unsigned char *sqlite3_value_text(sqlite3_value*);  
const void *sqlite3_value_text16(sqlite3_value*);  
const void *sqlite3_value_text16le(sqlite3_value*);  
const void *sqlite3_value_text16be(sqlite3_value*);  
int sqlite3_value_type(sqlite3_value*);  
int sqlite3_value_numeric_type(sqlite3_value*); 

  上面的函數調用以下的API來獲得上下文內容和返回結果:

void *sqlite3_aggregate_context(sqlite3_context*, int nBytes);  
void sqlite3_result_blob(sqlite3_context*, const void*, int, void(*)(void*));  
void sqlite3_result_double(sqlite3_context*, double);  
void sqlite3_result_error(sqlite3_context*, const char*, int);  
void sqlite3_result_error16(sqlite3_context*, const void*, int);  
void sqlite3_result_error_toobig(sqlite3_context*);  
void sqlite3_result_error_nomem(sqlite3_context*);  
void sqlite3_result_error_code(sqlite3_context*, int);  
void sqlite3_result_int(sqlite3_context*, int);  
void sqlite3_result_int64(sqlite3_context*, sqlite3_int64);  
void sqlite3_result_null(sqlite3_context*);  
void sqlite3_result_text(sqlite3_context*, const char*, int, void(*)(void*));  
void sqlite3_result_text16(sqlite3_context*, const void*, int, void(*)(void*));  
void sqlite3_result_text16le(sqlite3_context*, const void*, int,void(*)(void*));  
void sqlite3_result_text16be(sqlite3_context*, const void*, int,void(*)(void*));  
void sqlite3_result_value(sqlite3_context*, sqlite3_value*);  
void sqlite3_result_zeroblob(sqlite3_context*, int n);  
void *sqlite3_user_data(sqlite3_context*);  
void *sqlite3_get_auxdata(sqlite3_context*, int N);  
void sqlite3_set_auxdata(sqlite3_context*, int N, void*, void (*)(void*));  

(3)注冊自定義的虛擬表模塊:

int sqlite3_create_module(  
  sqlite3 *db,               /* SQLite connection to register module with */  
  const char *zName,         /* Name of the module */  
  const sqlite3_module *p,   /* Methods for the module */  
  void *pClientData          /* Client data for xCreate/xConnect */  
);  
int sqlite3_create_module_v2(  
  sqlite3 *db,               /* SQLite connection to register module with */  
  const char *zName,         /* Name of the module */  
  const sqlite3_module *p,   /* Methods for the module */  
  void *pClientData,         /* Client data for xCreate/xConnect */  
  void(*xDestroy)(void*)     /* Module destructor function */  
);  

  其中第二個參數指定虛擬表模塊名稱,第三個參數指向虛擬表模塊,第四個參數為傳給虛擬表模塊xCreate/xConnect方法的客戶數據。sqlite3_create_module_v2()還有第五個參數,指定對pClientData數據進行析構的函數。若指定析構函數為NULL,則該函數與sqlite3_create_module()等價。
  SQLite的所有內建SQL函數都使用上面這些接口來創建,特別是date.c和func.c中的SQL函數代碼。

程序示例

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sqlite3.h>
int main( int argc, char **argv )
{
  sqlite3 *db;
  sqlite3_stmt * stmt;
  const char *zTail;
  //打開數據庫
  int r = sqlite3_open("mysqlite.db",&db);
  if(r){
    printf("%s",sqlite3_errmsg(db));
  }
  else
    printf("open db sccess!\n");
  //創建Table
  //sqlite3_prepare()將SQL語句編譯為sqlite內部一個結構體(sqlite3_stmt),
  //該結構體中包含了將要執行的SQL語句的信息
  //第四個參數用來指向輸入參數中下一個需要編譯的SQL語句存放的 SQLite statement 對象的指針
  sqlite3_prepare(db,"CREATE TABLE players ( ID INTEGER PRIMARY KEY, name TEXT, age INTEGER);",-1,&stmt,&zTail);
  printf("prepared has done\n");
  //調用sqlite3_step(),此時SQL語句才真正執行,執行成功,返回SQLITE_DONE或SQLITE_ROW.
  //每次調用sqlite3_step(),只返回一行數據,使用sqlite3_column_XXX()函數來取出這些數據
  //要取出全部的數據,則需要反復調用sqlite3_step()
  sqlite3_step(stmt);
  //調用sqlite3_finalize(),釋放stmt占用的內存,該內存是在sqlite3_prepare()時分配的
  //如果SQL語句要重復使用,可以調用sqlite3_reset()來清除已經綁定的參數
  sqlite3_finalize(stmt);
  printf("create table success!\n");
  //插入數據
  sqlite3_prepare(db,"INSERT INTO players (name,age) VALUES(?,?);",-1,&stmt,&zTail);
  char str[] = "Kevin";
  int n = 23;
  //sqlite3_bind_xxx的第四個參數為負,則字符串長度由第一個0終止的位數決定
  //SQLITE_STATIC表示命令執行完后的信息為static類型,不能被改動,而且不需要被free
  sqlite3_bind_text(stmt,1,str,-1,SQLITE_STATIC);
  sqlite3_bind_int(stmt,2,n);
  r = sqlite3_step(stmt);
  if( r!=SQLITE_DONE){
    printf("%s",sqlite3_errmsg(db));
  }
  //清除已經綁定的參數
  sqlite3_reset(stmt);
  //插入第二個數據
  char str2[] = "Jack";
  int n2 = 16;
  sqlite3_bind_text(stmt,1,str2,-1,SQLITE_STATIC);
  sqlite3_bind_int(stmt,2,n2);
  r = sqlite3_step(stmt);
  if( r!=SQLITE_DONE){
    printf("%s",sqlite3_errmsg(db));
  }
  sqlite3_finalize(stmt); //釋放stmt所占的內存
  //查詢所有數據
  sqlite3_prepare(db,"SELECT ID, name, age FROM players ORDER BY age",-1,&stmt,&zTail);
  r = sqlite3_step(stmt);
  int number;
  int id;
  const unsigned char * name;
  while( r == SQLITE_ROW ){
    id = sqlite3_column_int( stmt, 0 );
    name = sqlite3_column_text( stmt,1 );
    number = sqlite3_column_int( stmt, 2 );
    printf("ID: %d Name: %s Age: %d \n",id,name,number);
    sleep(1);
    r = sqlite3_step(stmt);
  }
  sqlite3_finalize(stmt);
  //關閉數據庫
  sqlite3_close(db);
  return 0;
}

編譯運行:
$ gcc sqlite.c –o sqlite –lsqlite3 $ ./sqlite

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM