カスタムSQLite Android関数または独自のLOWER_FNC()

SELECT * WHERE LOWER_FNC(名前)like '% "+ filterText +"%' "



Androidアプリケーションの開発中に、ロシア語の文字でSQLiteフィルターを照会する際に問題が発生しました。 英語のローカライズに問題はありません。 他の国際レイアウトでは、リクエストの大文字が正しく処理されませんでした。

少し理解して、次の説明に出くわしました。



(18)Unicode文字の大文字と小文字を区別しないマッチングは機能しません。



SQLiteのデフォルト設定は、ASCII文字の大文字と小文字を区別しない比較のみをサポートします。 これは、Unicodeの大文字と小文字を区別しない完全な比較と大文字と小文字の変換を行うには、SQLiteライブラリのサイズをほぼ2倍にするテーブルとロジックが必要だからです。 SQLite開発者は、Unicodeの完全なケースサポートを必要とするアプリケーションにはおそらく必要なテーブルと関数が既にあるため、SQLiteはこの機能を複製するためにスペースをとるべきではないと考えています。



SQLiteは、デフォルトでUnicodeの完全なケースサポートを提供する代わりに、外部のUnicode比較および変換ルーチンに対してリンクする機能を提供します。



おそらく、SQLite Androidの現在の実装は
ASCII文字の大文字と小文字を区別しない比較のみをサポートします




CursorWrapperで解決策を見ましたが、それでも私のバージョンのSQLiteを収集してaddCustomFunctionを使用することにしました



その由来、カットの下で読む





アイデアは、独自の(カスタム)アセンブリのSQLiteライブラリで直接および逆のデータ交換を使用することです

開始するには、 SQLite Android Bindingsステートメントをご覧ください

Android APIレベル15(Android 4.0.3)のバージョンを使用しました。 追加のフォルダーまたはパッケージパッケージorg.sqlite.osのわずかな違い。



次に、NDK sqliteXライブラリを通じて標準ライブラリを収集します。 プロジェクトに接続します。 ライブラリを出荷します



System.loadLibrary("sqliteX");
      
      





次に、SQLクエリから呼び出されるカスタム関数を定義します



  private final SQLiteDatabase.CustomFunction mLowerFnc = new SQLiteDatabase.CustomFunction() { @Override public void callback(String[] args) { String text = args[0]; text = text.toLowerCase(); Log.d(LOG, "LOWER_FNC : " + text); return; } };
      
      





関数自体は次のように接続されます



 public class DataBase extends SQLiteOpenHelper { ... public DataBase(Context context) { super(context, context.getDatabasePath(DATABASE_NAME).getPath(), null, DATABASE_VERSION); context.openOrCreateDatabase(context.getDatabasePath(DATABASE_NAME).getPath(), context.MODE_PRIVATE, null); } public void open() throws SQLException { database = getWritableDatabase(); database.addCustomFunction("LOWER_FNC", 1, mLowerFnc); }
      
      





パラメータ:SQLiteクエリ文字列から呼び出される関数の名前。 引数の数、この場合は入力文字列と実際の関数自体



データベースを完全に開く必要があることに注意してください。 フルパスを取得するオプション:



 DB_PATH = getApplicationContext().getDatabasePath("test.db"); DB_PATH.mkdirs();
      
      





ログには、関数LOWER_FNCの呼び出しとリクエストからの行があります。 いいね!

次は? これらの行を使用して、処理済みのフォームに戻す方法は?



SQLiteのソースコードを見てみましょう。



 // Called each time a custom function is evaluated. static void sqliteCustomFunctionCallback(sqlite3_context *context, int argc, sqlite3_value **argv) { ... // TODO: Support functions that return values. env->CallVoidMethod(functionObj, gSQLiteCustomFunctionClassInfo.dispatchCallback, argsArray); ...
      
      





CallVoidMethodとさらにTODOを見る:値を返すサポート関数

いいね 著者は終わりませんでした。 自分に...

このアプローチはすぐには見つかりませんでした。 2日間過ごしましたが、結果は達成されました。 そしてこれが主なものです



  result = env->CallObjectMethod( functionObj, gSQLiteCustomFunctionClassInfo.dispatchCallback, argsArray); char_result = env->GetStringUTFChars( (jstring) result, NULL); sqlite3_result_text(context, char_result, -1, SQLITE_TRANSIENT);
      
      





CallVoidMethodの代わりに、Androidから行を削除するCallObjectMethodを作成します

フルバージョン機能
 // Called each time a custom function is evaluated. static void sqliteCustomFunctionCallback(sqlite3_context *context, int argc, sqlite3_value **argv) { jobject result; JNIEnv* env = 0; const char* char_result; gpJavaVM->GetEnv((void**)&env, JNI_VERSION_1_4); // Get the callback function object. // Create a new local reference to it in case the callback tries to do something // dumb like unregister the function (thereby destroying the global ref) while it is running. jobject functionObjGlobal = reinterpret_cast<jobject>(sqlite3_user_data(context)); jobject functionObj = env->NewLocalRef(functionObjGlobal); jobjectArray argsArray = env->NewObjectArray(argc, gStringClassInfo.clazz, NULL); if (argsArray) { for (int i = 0; i < argc; i++) { const jchar* arg = static_cast<const jchar*>(sqlite3_value_text16(argv[i])); if (!arg) { ALOGW("NULL argument in custom_function_callback. This should not happen."); } else { size_t argLen = sqlite3_value_bytes16(argv[i]) / sizeof(jchar); jstring argStr = env->NewString(arg, argLen); if (!argStr) { goto error; // out of memory error } env->SetObjectArrayElement(argsArray, i, argStr); env->DeleteLocalRef(argStr); } } // TODO: Support functions that return values. //env->CallVoidMethod(functionObj, // gSQLiteCustomFunctionClassInfo.dispatchCallback, argsArray); result = env->CallObjectMethod( functionObj, gSQLiteCustomFunctionClassInfo.dispatchCallback, argsArray); char_result = env->GetStringUTFChars( (jstring) result, NULL); sqlite3_result_text(context, char_result, -1, SQLITE_TRANSIENT); error: env->DeleteLocalRef(argsArray); } env->DeleteLocalRef(functionObj); env->DeleteLocalRef(result); if (env->ExceptionCheck()) { ALOGE("An exception was thrown by custom SQLite function."); /* LOGE_EX(env); */ env->ExceptionClear(); } }
      
      







もう1つポイントがあります。 register_android_database_SQLiteConnectionを変更する必要があります。



  GET_METHOD_ID(gSQLiteCustomFunctionClassInfo.dispatchCallback, clazz, "dispatchCallback", "([Ljava/lang/String;)Ljava/lang/String;");
      
      





Ljava / lang / Stringを追加することにより; パラメータとして。 これは、Androidアプリケーションから返される文字列です。 そうしないと、Android OSは新しい実装を見つけられません。



register_android_database_SQLiteConnection(JNIEnv * env)
 int register_android_database_SQLiteConnection(JNIEnv *env) { jclass clazz; FIND_CLASS(clazz, "org/sqlite/database/sqlite/SQLiteCustomFunction"); GET_FIELD_ID(gSQLiteCustomFunctionClassInfo.name, clazz, "name", "Ljava/lang/String;"); GET_FIELD_ID(gSQLiteCustomFunctionClassInfo.numArgs, clazz, "numArgs", "I"); GET_METHOD_ID(gSQLiteCustomFunctionClassInfo.dispatchCallback, clazz, "dispatchCallback", "([Ljava/lang/String;)Ljava/lang/String;"); FIND_CLASS(clazz, "java/lang/String"); gStringClassInfo.clazz = jclass(env->NewGlobalRef(clazz)); return jniRegisterNativeMethods(env, "org/sqlite/database/sqlite/SQLiteConnection", sMethods, NELEM(sMethods) ); }
      
      







最終段階。 Stringを返すようにコールバックとインターフェイスを変更します

非表示のテキスト
  private final SQLiteDatabase.CustomFunction mLowerFnc = new SQLiteDatabase.CustomFunction() { @Override public String callback(String[] args) { String text = args[0]; text = text.toLowerCase(); Log.d(LOG, "LOWER_FNC : " + text); return text; } }; ... /** * A callback interface for a custom sqlite3 function. * This can be used to create a function that can be called from * sqlite3 database triggers. * @hide */ public interface CustomFunction { public String callback(String[] args); }
      
      







したがって、任意の機能をオーバーライドしたり、独自の機能を追加したり、独自の機能を作成したりできます。 これはすべて、 航空券プロジェクトで見つかったアプリケーションです。

フィードAviasalesを使用しましたが、それは別の話です。



この記事がお役に立てば幸いです。 独自の機能を備えたSQLiteクエリを作成してください!



記事の資料:

SQLite Androidバインディング

Android NDK



All Articles