Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit c1aea2f3 authored by Mike Lockwood's avatar Mike Lockwood Committed by Android (Google) Code Review
Browse files

Merge "SQLiteDatabase: Add mechanism for registering custom sqlite3 functions"

parents 5ba2e745 9d9c1be2
Loading
Loading
Loading
Loading
+60 −6
Original line number Diff line number Diff line
@@ -1047,6 +1047,7 @@ public class SQLiteDatabase extends SQLiteClosable {
            closeClosable();
            // finalize ALL statements queued up so far
            closePendingStatements();
            releaseCustomFunctions();
            // close this database instance - regardless of its reference count value
            dbclose();
            if (mConnectionPool != null) {
@@ -1082,6 +1083,54 @@ public class SQLiteDatabase extends SQLiteClosable {
     */
    private native void dbclose();

    /**
     * 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 void callback(String[] args);
    }

    /**
     * Registers a CustomFunction callback as a function that can be called from
     * sqlite3 database triggers.
     * @param name the name of the sqlite3 function
     * @param numArgs the number of arguments for the function
     * @param function callback to call when the function is executed
     * @hide
     */
    public void addCustomFunction(String name, int numArgs, CustomFunction function) {
        verifyDbIsOpen();
        synchronized (mCustomFunctions) {
            int ref = native_addCustomFunction(name, numArgs, function);
            if (ref != 0) {
                // save a reference to the function for cleanup later
                mCustomFunctions.add(new Integer(ref));
            } else {
                throw new SQLiteException("failed to add custom function " + name);
            }
        }
    }

    private void releaseCustomFunctions() {
        synchronized (mCustomFunctions) {
            for (int i = 0; i < mCustomFunctions.size(); i++) {
                Integer function = mCustomFunctions.get(i);
                native_releaseCustomFunction(function.intValue());
            }
            mCustomFunctions.clear();
        }
    }

    // list of CustomFunction references so we can clean up when the database closes
    private final ArrayList<Integer> mCustomFunctions =
            new ArrayList<Integer>();

    private native int native_addCustomFunction(String name, int numArgs, CustomFunction function);
    private native void native_releaseCustomFunction(int function);

    /**
     * Gets the database version.
     *
@@ -1959,12 +2008,17 @@ public class SQLiteDatabase extends SQLiteClosable {
    }

    @Override
    protected void finalize() {
    protected void finalize() throws Throwable {
        try {
            if (isOpen()) {
                Log.e(TAG, "close() was never explicitly called on database '" +
                        mPath + "' ", mStackTrace);
                closeClosable();
                onAllReferencesReleased();
                releaseCustomFunctions();
            }
        } finally {
            super.finalize();
        }
    }

+72 −0
Original line number Diff line number Diff line
@@ -62,6 +62,7 @@ enum {
};

static jfieldID offset_db_handle;
static jmethodID method_custom_function_callback;

static char *createStr(const char *path, short extra) {
    int len = strlen(path) + extra;
@@ -458,6 +459,62 @@ static void native_finalize(JNIEnv* env, jobject object, jint statementId)
    }
}

static void custom_function_callback(sqlite3_context * context, int argc, sqlite3_value ** argv) {
    JNIEnv* env = AndroidRuntime::getJNIEnv();
    if (!env) {
        LOGE("custom_function_callback cannot call into Java on this thread");
        return;
    }

    // pack up the arguments into a string array
    jobjectArray strArray = env->NewObjectArray(argc, env->FindClass("java/lang/String"), NULL);
    if (!strArray) {
        jniThrowException(env, "java/lang/OutOfMemoryError", NULL);
        return;
    }
    for (int i = 0; i < argc; i++) {
        char* arg = (char *)sqlite3_value_text(argv[i]);
        jobject obj = env->NewStringUTF(arg);
        if (!obj) {
            jniThrowException(env, "java/lang/OutOfMemoryError", NULL);
            return;
        }
        env->SetObjectArrayElement(strArray, i, obj);
        env->DeleteLocalRef(obj);
    }

    // get global ref to CustomFunction object from our user data
    jobject function = (jobject)sqlite3_user_data(context);
    env->CallVoidMethod(function, method_custom_function_callback, strArray);
}

static jint native_addCustomFunction(JNIEnv* env, jobject object,
        jstring name, jint numArgs, jobject function)
{
    sqlite3 * handle = (sqlite3 *)env->GetIntField(object, offset_db_handle);
    char const *nameStr = env->GetStringUTFChars(name, NULL);
    jobject ref = env->NewGlobalRef(function);
    LOGD("native_addCustomFunction %s ref: %d", nameStr, ref);
    int err = sqlite3_create_function(handle, nameStr, numArgs, SQLITE_UTF8,
            (void *)ref, custom_function_callback, NULL, NULL);
    env->ReleaseStringUTFChars(name, nameStr);

    if (err == SQLITE_OK)
        return (int)ref;
    else {
        LOGE("sqlite3_create_function returned %d", err);
        env->DeleteGlobalRef(ref);
        throw_sqlite3_exception(env, handle);
        return 0;
     }
}

static void native_releaseCustomFunction(JNIEnv* env, jobject object, jint ref)
{
    LOGD("native_releaseCustomFunction %d", ref);
    env->DeleteGlobalRef((jobject)ref);
}

static JNINativeMethod sMethods[] =
{
    /* name, signature, funcPtr */
@@ -472,6 +529,10 @@ static JNINativeMethod sMethods[] =
    {"native_getDbLookaside", "()I", (void *)native_getDbLookaside},
    {"releaseMemory", "()I", (void *)native_releaseMemory},
    {"native_finalize", "(I)V", (void *)native_finalize},
    {"native_addCustomFunction",
                    "(Ljava/lang/String;ILandroid/database/sqlite/SQLiteDatabase$CustomFunction;)I",
                    (void *)native_addCustomFunction},
    {"native_releaseCustomFunction", "(I)V", (void *)native_releaseCustomFunction},
};

int register_android_database_SQLiteDatabase(JNIEnv *env)
@@ -490,6 +551,17 @@ int register_android_database_SQLiteDatabase(JNIEnv *env)
        return -1;
    }

    clazz = env->FindClass("android/database/sqlite/SQLiteDatabase$CustomFunction");
    if (clazz == NULL) {
        LOGE("Can't find android/database/sqlite/SQLiteDatabase$CustomFunction\n");
        return -1;
    }
    method_custom_function_callback = env->GetMethodID(clazz, "callback", "([Ljava/lang/String;)V");
    if (method_custom_function_callback == NULL) {
        LOGE("Can't find method SQLiteDatabase.CustomFunction.callback\n");
        return -1;
    }

    return AndroidRuntime::registerNativeMethods(env, "android/database/sqlite/SQLiteDatabase",
            sMethods, NELEM(sMethods));
}