Commit 8995700b authored by Gian-Carlo Pascutto's avatar Gian-Carlo Pascutto
Browse files

Bug 726821 - Push Cursor usage down into native bridge code. r=blassey

parent 64a5da6a
Loading
Loading
Loading
Loading
+19 −50
Original line number Diff line number Diff line
@@ -30,20 +30,16 @@ public class SQLiteBridge {
    // Path to the database. We reopen it every query.
    private String mDb;

    // Remember column names from last query result.
    private ArrayList<String> mColumns;
    private Long[] mQueryResults;

    // Values remembered after a query.
    private int kResultInsertRowId = 0;
    private int kResultRowsChanged = 1;
    private long[] mQueryResults;

    private static final int RESULT_INSERT_ROW_ID = 0;
    private static final int RESULT_ROWS_CHANGED = 1;

    // JNI code in $(topdir)/mozglue/android/..
    private static native void sqliteCall(String aDb, String aQuery,
    private static native MatrixBlobCursor sqliteCall(String aDb, String aQuery,
                                                      String[] aParams,
                                          ArrayList<String> aColumns,
                                          Long[] aUpdateResult,
                                          ArrayList<Object[]> aRes)
                                                      long[] aUpdateResult)
        throws SQLiteBridgeException;

    // Takes the path to the database we want to access.
@@ -73,7 +69,7 @@ public class SQLiteBridge {
        }

        internalQuery(sb.toString(), whereArgs);
        return mQueryResults[kResultRowsChanged].intValue();
        return (int)mQueryResults[RESULT_ROWS_CHANGED];
    }

    public Cursor query(String table,
@@ -119,22 +115,7 @@ public class SQLiteBridge {

    public Cursor rawQuery(String sql, String[] selectionArgs)
        throws SQLiteBridgeException {
        ArrayList<Object[]> results;
        results = internalQuery(sql, selectionArgs);

        MatrixBlobCursor cursor =
            new MatrixBlobCursor(mColumns.toArray(new String[0]));
        try {
            for (Object resultRow: results) {
                Object[] resultColumns = (Object[])resultRow;
                if (resultColumns.length == mColumns.size())
                    cursor.addRow(resultColumns);
            }
        } catch(IllegalArgumentException ex) {
            Log.e(LOGTAG, "Error getting rows", ex);
        }

        return cursor;
        return internalQuery(sql, selectionArgs);
    }

    public long insert(String table, String nullColumnHack, ContentValues values)
@@ -167,7 +148,7 @@ public class SQLiteBridge {
        String[] binds = new String[valueBinds.size()];
        valueBinds.toArray(binds);
        internalQuery(sb.toString(), binds);
        return mQueryResults[kResultInsertRowId];
        return mQueryResults[RESULT_INSERT_ROW_ID];
    }

    public int update(String table, ContentValues values, String whereClause, String[] whereArgs)
@@ -202,40 +183,28 @@ public class SQLiteBridge {
        valueNames.toArray(binds);

        internalQuery(sb.toString(), binds);
        return mQueryResults[kResultRowsChanged].intValue();
        return (int)mQueryResults[RESULT_ROWS_CHANGED];
    }

    public int getVersion()
               throws SQLiteBridgeException {
        ArrayList<Object[]> results = null;
        results = internalQuery("PRAGMA user_version", null);
        Cursor cursor = internalQuery("PRAGMA user_version", null);
        int ret = -1;
        if (results != null) {
            for (Object resultRow: results) {
                Object[] resultColumns = (Object[])resultRow;
                String version = (String)resultColumns[0];
        if (cursor != null) {
            cursor.moveToFirst();
            String version = cursor.getString(0);
            ret = Integer.parseInt(version);
        }
        }
        return ret;
    }

    // Do an SQL query, substituting the parameters in the query with the passed
    // parameters. The parameters are subsituded in order, so named parameters
    // are not supported.
    // The result is returned as an ArrayList<Object[]>, with each
    // row being an entry in the ArrayList, and each column being one Object
    // in the Object[] array. The columns are of type null,
    // direct ByteBuffer (BLOB), or String (everything else).
    private ArrayList<Object[]> internalQuery(String aQuery, String[] aParams)
    private Cursor internalQuery(String aQuery, String[] aParams)
        throws SQLiteBridgeException {
        ArrayList<Object[]> result = new ArrayList<Object[]>();
        mQueryResults = new Long[2];
        mColumns = new ArrayList<String>();

        sqliteCall(mDb, aQuery, aParams, mColumns, mQueryResults, result);

        return result;
        mQueryResults = new long[2];
        return sqliteCall(mDb, aQuery, aParams, mQueryResults);
    }

    // nop, provided for API compatibility with SQLiteDatabase.
+57 −46
Original line number Diff line number Diff line
@@ -95,13 +95,11 @@ void setup_sqlite_functions(void *sqlite_handle)
static bool initialized = false;
static jclass stringClass;
static jclass objectClass;
static jclass longClass;
static jclass byteBufferClass;
static jclass arrayListClass;
static jclass cursorClass;
static jmethodID jByteBufferAllocateDirect;
static jmethodID jArrayListAdd;
static jmethodID jLongConstructor;
static jobject jNull;
static jmethodID jCursorConstructor;
static jmethodID jCursorAddRow;

static void
JNI_Throw(JNIEnv* jenv, const char* name, const char* msg)
@@ -125,14 +123,12 @@ JNI_Setup(JNIEnv* jenv)

    objectClass       = jenv->FindClass("java/lang/Object");
    stringClass       = jenv->FindClass("java/lang/String");
    longClass       = jenv->FindClass("java/lang/Long");
    byteBufferClass   = jenv->FindClass("java/nio/ByteBuffer");
    arrayListClass  = jenv->FindClass("java/util/ArrayList");
    jNull           = jenv->NewGlobalRef(NULL);
    cursorClass       = jenv->FindClass("org/mozilla/gecko/sqlite/MatrixBlobCursor");

    if (stringClass == NULL || objectClass == NULL
        || byteBufferClass == NULL || arrayListClass == NULL
        || longClass == NULL) {
        || byteBufferClass == NULL
        || cursorClass == NULL) {
        LOG("Error finding classes");
        JNI_Throw(jenv, "org/mozilla/gecko/sqlite/SQLiteBridgeException",
                  "FindClass error");
@@ -142,16 +138,16 @@ JNI_Setup(JNIEnv* jenv)
    // public static ByteBuffer allocateDirect(int capacity)
    jByteBufferAllocateDirect =
        jenv->GetStaticMethodID(byteBufferClass, "allocateDirect", "(I)Ljava/nio/ByteBuffer;");
    // boolean add(Object o)
    jArrayListAdd =
        jenv->GetMethodID(arrayListClass, "add", "(Ljava/lang/Object;)Z");
    // new Long(long i)
    jLongConstructor =
        jenv->GetMethodID(longClass, "<init>", "(J)V");
    // new MatrixBlobCursor(String [])
    jCursorConstructor =
        jenv->GetMethodID(cursorClass, "<init>", "([Ljava/lang/String;)V");
    // public void addRow (Object[] columnValues)
    jCursorAddRow =
        jenv->GetMethodID(cursorClass, "addRow", "([Ljava/lang/Object;)V");

    if (jByteBufferAllocateDirect == NULL
        || jArrayListAdd == NULL
        || jLongConstructor == NULL) {
        || jCursorConstructor == NULL
        || jCursorAddRow == NULL) {
        LOG("Error finding methods");
        JNI_Throw(jenv, "org/mozilla/gecko/sqlite/SQLiteBridgeException",
                  "GetMethodId error");
@@ -161,17 +157,16 @@ JNI_Setup(JNIEnv* jenv)
    initialized = true;
}

extern "C" NS_EXPORT void JNICALL
extern "C" NS_EXPORT jobject JNICALL
Java_org_mozilla_gecko_sqlite_SQLiteBridge_sqliteCall(JNIEnv* jenv, jclass,
                                                      jstring jDb,
                                                      jstring jQuery,
                                                      jobjectArray jParams,
                                                      jobject jColumns,
                                                      jobjectArray jQueryRes,
                                                      jobject jArrayList)
                                                      jlongArray jQueryRes)
{
    JNI_Setup(jenv);

    jobject jCursor = NULL;
    char* errorMsg;
    jsize numPars = 0;

@@ -244,27 +239,44 @@ Java_org_mozilla_gecko_sqlite_SQLiteBridge_sqliteCall(JNIEnv* jenv, jclass,
        goto error_close;
    }

    // Get the column names
    // Get the column count and names
    int cols;
    cols = f_sqlite3_column_count(ppStmt);

    {
        // Allocate a String[cols]
        jobjectArray jStringArray = jenv->NewObjectArray(cols,
                                                         stringClass,
                                                         NULL);
        if (jStringArray == NULL) {
            asprintf(&errorMsg, "Can't allocate String[]\n");
            goto error_close;
        }

        // Assign column names to the String[]
        for (int i = 0; i < cols; i++) {
            const char* colName = f_sqlite3_column_name(ppStmt, i);
            jstring jStr = jenv->NewStringUTF(colName);
        jenv->CallBooleanMethod(jColumns, jArrayListAdd, jStr);
        jenv->DeleteLocalRef(jStr);
            jenv->SetObjectArrayElement(jStringArray, i, jStr);
        }

        // Construct the MatrixCursor(String[]) with given column names
        jCursor = jenv->NewObject(cursorClass,
                                  jCursorConstructor,
                                  jStringArray);
        if (jCursor == NULL) {
            asprintf(&errorMsg, "Can't allocate MatrixBlobCursor\n");
            goto error_close;
        }
    }

    // Return the id and number of changed rows in jQueryRes
    {
        long id = f_sqlite3_last_insert_rowid(db);
        jobject jId = jenv->NewObject(longClass, jLongConstructor, id);
        jenv->SetObjectArrayElement(jQueryRes, 0, jId);
        jenv->DeleteLocalRef(jId);

        long changed = f_sqlite3_changes(db);
        jobject jChanged = jenv->NewObject(longClass, jLongConstructor, changed);
        jenv->SetObjectArrayElement(jQueryRes, 1, jChanged);
        jenv->DeleteLocalRef(jChanged);
        jlong id = f_sqlite3_last_insert_rowid(db);
        jenv->SetLongArrayRegion(jQueryRes, 0, 1, &id);

        jlong changed = f_sqlite3_changes(db);
        jenv->SetLongArrayRegion(jQueryRes, 1, 1, &changed);
    }

    // For each row, add an Object[] to the passed ArrayList,
@@ -308,7 +320,7 @@ Java_org_mozilla_gecko_sqlite_SQLiteBridge_sqliteCall(JNIEnv* jenv, jclass,
                jenv->SetObjectArrayElement(jRow, i, jByteBuffer);
                jenv->DeleteLocalRef(jByteBuffer);
            } else if (colType == SQLITE_NULL) {
                jenv->SetObjectArrayElement(jRow, i, jNull);
                jenv->SetObjectArrayElement(jRow, i, NULL);
            } else {
                // Treat everything else as text
                const char* txt = (const char*)f_sqlite3_column_text(ppStmt, i);
@@ -318,9 +330,8 @@ Java_org_mozilla_gecko_sqlite_SQLiteBridge_sqliteCall(JNIEnv* jenv, jclass,
            }
        }

        // Append Object[] to ArrayList<Object[]>
        // JNI doesn't know about the generic, so use Object[] as Object
        jenv->CallBooleanMethod(jArrayList, jArrayListAdd, jRow);
        // Append Object[] to Cursor
        jenv->CallVoidMethod(jCursor, jCursorAddRow, jRow);

        // Clean up
        jenv->DeleteLocalRef(jRow);
@@ -341,12 +352,12 @@ Java_org_mozilla_gecko_sqlite_SQLiteBridge_sqliteCall(JNIEnv* jenv, jclass,
    }

    f_sqlite3_close(db);
    return;
    return jCursor;

error_close:
    f_sqlite3_close(db);
    LOG("Error in SQLiteBridge: %s\n", errorMsg);
    JNI_Throw(jenv, "org/mozilla/gecko/sqlite/SQLiteBridgeException", errorMsg);
    free(errorMsg);
    return;
    return jCursor;
}