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

This commit is contained in:
Gian-Carlo Pascutto 2012-02-27 12:28:22 +01:00
parent 64a5da6afc
commit 8995700bac
2 changed files with 75 additions and 95 deletions

View File

@ -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,
String[] aParams,
ArrayList<String> aColumns,
Long[] aUpdateResult,
ArrayList<Object[]> aRes)
private static native MatrixBlobCursor sqliteCall(String aDb, String aQuery,
String[] aParams,
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,20 +183,17 @@ 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];
ret = Integer.parseInt(version);
}
if (cursor != null) {
cursor.moveToFirst();
String version = cursor.getString(0);
ret = Integer.parseInt(version);
}
return ret;
}
@ -223,19 +201,10 @@ public class SQLiteBridge {
// 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.

View File

@ -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)
@ -123,16 +121,14 @@ JNI_Setup(JNIEnv* jenv)
{
if (initialized) return;
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);
objectClass = jenv->FindClass("java/lang/Object");
stringClass = jenv->FindClass("java/lang/String");
byteBufferClass = jenv->FindClass("java/nio/ByteBuffer");
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);
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);
{
// 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->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);
jlong id = f_sqlite3_last_insert_rowid(db);
jenv->SetLongArrayRegion(jQueryRes, 0, 1, &id);
long changed = f_sqlite3_changes(db);
jobject jChanged = jenv->NewObject(longClass, jLongConstructor, changed);
jenv->SetObjectArrayElement(jQueryRes, 1, jChanged);
jenv->DeleteLocalRef(jChanged);
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;
}