Bug 727264 - Add an extra argument to pass more SQLite query results. Cleanup & simplify bridge. r=blassey
authorGian-Carlo Pascutto <gpascutto@mozilla.com>
Mon, 27 Feb 2012 12:28:21 +0100
changeset 89345 ff36792efde97c8409f92fa4ad5644a9caa6a9ef
parent 89329 86f8ab61e7558e76cbf810e532fcc9bafd165942
child 89346 3e6935243310b57940c140238306dab8fefafa51
push idunknown
push userunknown
push dateunknown
reviewersblassey
bugs727264
milestone13.0a1
Bug 727264 - Add an extra argument to pass more SQLite query results. Cleanup & simplify bridge. r=blassey
mobile/android/base/sqlite/SQLiteBridge.java
mozglue/android/SQLiteBridge.cpp
--- a/mobile/android/base/sqlite/SQLiteBridge.java
+++ b/mobile/android/base/sqlite/SQLiteBridge.java
@@ -21,67 +21,64 @@ import java.util.Set;
 
 /*
  * This class allows using the mozsqlite3 library included with Firefox
  * to read SQLite databases, instead of the Android SQLiteDataBase API,
  * which might use whatever outdated DB is present on the Android system.
  */
 public class SQLiteBridge {
     private static final String LOGTAG = "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;
 
     // 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)
         throws SQLiteBridgeException;
 
     // Takes the path to the database we want to access.
     public SQLiteBridge(String aDb) throws SQLiteBridgeException {
         mDb = aDb;
     }
 
     // Executes a simple line of sql.
     public void execSQL(String sql)
                 throws SQLiteBridgeException {
-        try {
-            query(sql, null);
-        } catch(SQLiteBridgeException ex) {
-            throw ex;
-        }
+        query(sql, null);
     }
 
     // Executes a simple line of sql. Allow you to bind arguments
     public void execSQL(String sql, String[] bindArgs)
                 throws SQLiteBridgeException {
-        try {
-            query(sql, bindArgs);
-        } catch(SQLiteBridgeException ex) {
-            throw ex;
-        }
+        query(sql, bindArgs);
     }
 
     // Executes a DELETE statement on the database
     public int delete(String table, String whereClause, String[] whereArgs)
                throws SQLiteBridgeException {
         StringBuilder sb = new StringBuilder("DELETE from ");
         sb.append(table);
         if (whereClause != null) {
             sb.append(" WHERE " + whereClause);
         }
 
-        try {
-            return getIntResult(sb.toString(), whereArgs, 1);
-        } catch(SQLiteBridgeException ex) {
-            throw ex;
-        }
+        query(sb.toString(), whereArgs);
+        return mQueryResults[kResultRowsChanged].intValue();
     }
 
     public Cursor query(String table,
                         String[] columns,
                         String selection,
                         String[] selectionArgs,
                         String groupBy,
                         String having,
@@ -113,24 +110,17 @@ public class SQLiteBridge {
             sb.append(" ORDER BY " + orderBy);
         }
 
         if (limit != null) {
             sb.append(" " + limit);
         }
 
         ArrayList<Object[]> results;
-        try {
-            mColumns = null;
-
-            results = query(sb.toString(), selectionArgs);
-
-        } catch(SQLiteBridgeException ex) {
-            throw ex;
-        }
+        results = query(sb.toString(), selectionArgs);
 
         MatrixCursor cursor = new MatrixCursor(mColumns.toArray(new String[0]));
         try {
             for (Object resultRow: results) {
                 Object[] resultColumns = (Object[])resultRow;
                 if (resultColumns.length == mColumns.size())
                     cursor.addRow(resultColumns);
             }
@@ -165,21 +155,18 @@ public class SQLiteBridge {
 
         // XXX - Do we need to bind these values?
         sb.append(" VALUES (");
         sb.append(TextUtils.join(", ", valueNames));
         sb.append(") ");
 
         String[] binds = new String[valueBinds.size()];
         valueBinds.toArray(binds);
-        try {
-            return getIntResult(sb.toString(), binds, 0);
-        } catch (SQLiteBridgeException ex) {
-            throw ex;
-        }
+        query(sb.toString(), binds);
+        return mQueryResults[kResultInsertRowId];
     }
 
     public int update(String table, ContentValues values, String whereClause, String[] whereArgs)
                throws SQLiteBridgeException {
         Set<Entry<String, Object>> valueSet = values.valueSet();
         Iterator<Entry<String, Object>> valueIterator = valueSet.iterator();
         ArrayList<String> valueNames = new ArrayList<String>();
 
@@ -202,81 +189,56 @@ public class SQLiteBridge {
             sb.append(whereClause);
             for (int i = 0; i < whereArgs.length; i++) {
                 valueNames.add(whereArgs[i]);
             }
         }
 
         String[] binds = new String[valueNames.size()];
         valueNames.toArray(binds);
-        try {
-            return getIntResult(sb.toString(), binds, 1);
-        } catch (SQLiteBridgeException ex) {
-            throw ex;
-        }
-    }
 
-    private int getIntResult(String query, String[] params, int resultIndex)
-               throws SQLiteBridgeException {
-        ArrayList<Object[]> results = null;
-        try {
-            mColumns = null;
-            results = query(query, params);
-        } catch(SQLiteBridgeException ex) {
-            throw ex;
-        }
-
-        if (results != null) {
-            for (Object resultRow: results) {
-                Object[] resultColumns = (Object[])resultRow;
-                return ((Number)resultColumns[resultIndex]).intValue();
-            }
-        }
-        return -1;
+        query(sb.toString(), binds);
+        return mQueryResults[kResultRowsChanged].intValue();
     }
 
     public int getVersion()
                throws SQLiteBridgeException {
         ArrayList<Object[]> results = null;
-        try {
-            mColumns = null;
-            results = query("PRAGMA user_version");
-        } catch(SQLiteBridgeException ex) {
-            throw ex;
-        }
+        results = query("PRAGMA user_version");
         int ret = -1;
         if (results != null) {
             for (Object resultRow: results) {
                 Object[] resultColumns = (Object[])resultRow;
                 String version = (String)resultColumns[0];
                 ret = Integer.parseInt(version);
             }
         }
         return ret;
     }
 
-
     // Do an SQL query without parameters
     public ArrayList<Object[]> query(String aQuery) throws SQLiteBridgeException {
-        String[] params = new String[0];
-        return query(aQuery, params);
+        return query(aQuery, null);
     }
 
     // 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).
     public ArrayList<Object[]> query(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, result);
+
+        sqliteCall(mDb, aQuery, aParams, mColumns, mQueryResults, result);
+
         return result;
     }
 
     // Gets the index in the row Object[] for the given column name.
     // Returns -1 if not found.
     public int getColumnIndex(String aColumnName) {
         return mColumns.lastIndexOf(aColumnName);
     }
--- a/mozglue/android/SQLiteBridge.cpp
+++ b/mozglue/android/SQLiteBridge.cpp
@@ -90,20 +90,22 @@ void setup_sqlite_functions(void *sqlite
   GETFUNC(sqlite3_changes);
   GETFUNC(sqlite3_last_insert_rowid);
 #undef GETFUNC
 }
 
 static bool initialized = false;
 static jclass stringClass;
 static jclass objectClass;
+static jclass longClass;
 static jclass byteBufferClass;
 static jclass arrayListClass;
 static jmethodID jByteBufferAllocateDirect;
 static jmethodID jArrayListAdd;
+static jmethodID jLongConstructor;
 static jobject jNull;
 
 static void
 JNI_Throw(JNIEnv* jenv, const char* name, const char* msg)
 {
     jclass cls = jenv->FindClass(name);
     if (cls == NULL) {
         LOG("Couldn't find exception class (or exception pending)\n");
@@ -118,51 +120,59 @@ JNI_Throw(JNIEnv* jenv, const char* name
 
 static void
 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);
 
     if (stringClass == NULL || objectClass == NULL
-        || byteBufferClass == NULL || arrayListClass == NULL) {
+        || byteBufferClass == NULL || arrayListClass == NULL
+        || longClass == NULL) {
         LOG("Error finding classes");
         JNI_Throw(jenv, "org/mozilla/gecko/sqlite/SQLiteBridgeException",
                   "FindClass error");
         return;
     }
 
     // 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");
 
-    if (jByteBufferAllocateDirect == NULL || jArrayListAdd == NULL) {
+    if (jByteBufferAllocateDirect == NULL
+        || jArrayListAdd == NULL
+        || jLongConstructor == NULL) {
         LOG("Error finding methods");
         JNI_Throw(jenv, "org/mozilla/gecko/sqlite/SQLiteBridgeException",
                   "GetMethodId error");
         return;
     }
 
     initialized = true;
 }
 
 extern "C" NS_EXPORT void JNICALL
 Java_org_mozilla_gecko_sqlite_SQLiteBridge_sqliteCall(JNIEnv* jenv, jclass,
                                                       jstring jDb,
                                                       jstring jQuery,
                                                       jobjectArray jParams,
                                                       jobject jColumns,
+                                                      jobjectArray jQueryRes,
                                                       jobject jArrayList)
 {
     JNI_Setup(jenv);
 
     char* errorMsg;
     jsize numPars = 0;
 
     const char* queryStr;
@@ -239,39 +249,27 @@ Java_org_mozilla_gecko_sqlite_SQLiteBrid
     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);
     }
 
-    // if the statement doesn't return any results, instead return the id and number of changed rows
-    if (rc == SQLITE_DONE) {
-        jclass integerClass = jenv->FindClass("java/lang/Integer");
-        jmethodID intConstructor = jenv->GetMethodID(integerClass, "<init>", "(I)V");
-        
-        jobjectArray jRow = jenv->NewObjectArray(2, objectClass, NULL);
-        if (jRow == NULL) {
-            asprintf(&errorMsg, "Can't allocate jRow Object[]\n");
-            goto error_close;
-        }
-
-        int id = f_sqlite3_last_insert_rowid(db);
-        jobject jId = jenv->NewObject(integerClass, intConstructor, id);
-        jenv->SetObjectArrayElement(jRow, 0, jId);
+    // 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);
 
-        int changed = f_sqlite3_changes(db);
-        jobject jChanged = jenv->NewObject(integerClass, intConstructor, changed);
-        jenv->SetObjectArrayElement(jRow, 1, jChanged);
+        long changed = f_sqlite3_changes(db);
+        jobject jChanged = jenv->NewObject(longClass, jLongConstructor, changed);
+        jenv->SetObjectArrayElement(jQueryRes, 1, jChanged);
         jenv->DeleteLocalRef(jChanged);
-
-        jenv->CallBooleanMethod(jArrayList, jArrayListAdd, jRow);
-        jenv->DeleteLocalRef(jRow);
     }
 
     // For each row, add an Object[] to the passed ArrayList,
     // with that containing either String or ByteArray objects
     // containing the columns
     while (rc != SQLITE_DONE) {
         // Process row
         // Construct Object[]