SQLite 加入自定義函數


簡介

SQLite 內置函數比較有限,有時通過添加自定義函數(User-Defined Fuctions)的方式可以實現一些通過普通 SQL 操作無法實現或者實現很麻煩的功能;也可以替換 SQLite 原有的內置函數,使其符合我們的要求。本文側重說明在 Android 環境下的做法。

現在假設我們現在要在 Android 系統的視頻播放器增加一個按照文件擴展名排序的功能,如果不是用自定義函數,就需要先從多媒體數據庫中查詢出來視頻的路徑,然后取出視頻文件的擴展名並進行排序,因為查詢得到的 Cursor 對象不可寫,所以需要生成一個 MatrixCursor,把排序后的數據寫入,最后返回這個 MatrixCursor。偽代碼表示如下:

1 public Cursor getSortedByFileExtensionCursor() {
2     Cursor rawCursor = queryVideoFileNameFromDb(); // 從數據庫查詢出 id 和路徑
3     HashMap<int,String> idAndExtensionMap = getVideoFileExtension(rawCursor); // 獲取 id 和擴展名的 HashMap
4     Cursor result = sortAndCreateNewMatrixCursor(idAndExtensionMap); // 對擴展名進行排序,生成 MatrixCursor 作為結果
5     return result;
6 }

而如果我們能夠向 SQLite 注冊一個自定義函數,很多類似問題就要簡單不少。

1. 通過 C 語言接口添加自定義函數

如果使用的是 Android 提供的 sqlite 程序,需要修改 Android 源碼目錄下的 external/sqlite/android/sqlite3_android.cpp 文件。注冊一個自定義函數,函數名稱是 get_file_ext,在 SQL 語句中的函數名稱是 "_GET_FILE_EXT",簡要代碼如下:

 1 extern "C" int register_android_functions(sqlite3 * handle, int utf16Storage)
 2 {
 3     // ...
 4     
 5     // 注冊自定義函數
 6     err = sqlite3_create_function(handle, "_GET_FILE_EXT", 1, SQLITE_UTF8, NULL, get_file_ext, NULL, NULL);
 7     if (err != SQLITE_OK) {
 8         return err;
 9     }
10     
11     // ...
12 }
13 
14 // 返回文件擴展名函數的具體實現
15 static void get_file_ext(sqlite3_context * context, int argc, sqlite3_value ** argv) 
16 {
17     // ...
18 }

然后編譯 external/sqlite,把編譯出來的 libsqlite.so 替換手機 /system/lib/ 下面的庫。使用該自定義函數也很簡單:

1 public Cursor getSortedByFileExtensionCursor() {
2     // 在 sort by 那里使用我們的 SQLite 自定義函數
3     Cursor result = query("SELECT * FROM video SORT BY _GET_FILE_EXT(_data)");
4 }

Note:如果不是在 Android 環境下,或者無法修改 Android 相關代碼,則需要自行通過 c 語言的接口操作數據庫,然后同樣使用 sqlite3_create_function 這個函數注冊自定義函數。如果使用的是 C#/Python/PHP 等語言,這些語言都有相應的 SQLite wrapper,可以類似的操作數據庫並注冊自定義函數。

2. 通過 Java 語言接口添加自定義函數

Android SQLiteDatabase 類里面有一個添加自定義函數的隱藏接口(@hide),在 Android 源碼中編譯的話可以使用該接口,使用方法大致如下:

 1 public class DatabaseHelper extends SQLiteOpenHelper {
 2     private String mFileExt;
 3     public DatabaseHelper(Context context, String name, SQLiteDatabase.CursorFactory factory, int version) {
 4         super(context, name, factory, version);
 5     }
 6     
 7     @Override
 8     public void onOpen(SQLiteDatabase db) {
 9         db.addCustomFunction("_GET_FILE_EXT", 1, mGetFileExtension);
10     }
11     
12     private final SQLiteDatabase.CustomFunction mGetFileExtension =
13             new SQLiteDatabase.CustomFunction() {
14         @Override
15         public void callback(String[] args) {
16             String file = args[0];
17             int index = file.lastIndexOf(".");
18             if (index != -1 && file.length() > index + 1) {
19                 mFileExt = file.substring(index);  // 獲取到擴展名
20             }
21         }
22     };
23 }

然后對這個數據庫進行查詢操作時,就可以使用我們添加的 _GET_FILE_EXT 函數了。Android 源碼目錄下的 pacakges/providers/MediaProvider 這個程序就使用了此接口,可以參考。

Note: 對外發布的程序最好不要依賴隱藏的 Java 接口,兼容性難以保證。

擴展閱讀:

轉載請注明出處:http://www.cnblogs.com/imouto/archive/2013/06/14/how-to-add-user-defined-functions-for-sqlite.html

本文外部鏡像:http://oteku.blogspot.com/2013/06/how-to-add-user-defined-functions-for-sqlite.html


免責聲明!

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



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