diff options
author | Jeff Brown <jeffbrown@google.com> | 2011-10-07 15:08:24 -0700 |
---|---|---|
committer | Android (Google) Code Review <android-gerrit@google.com> | 2011-10-07 15:08:24 -0700 |
commit | 270928bd4a1db1dc0d989f4e9897a81ab865e30e (patch) | |
tree | f9188005feb5a9756922f584d40cdc38bf9103ce /core/jni | |
parent | 4cc8bafb4b10614dc881ed8121ce4754811e161d (diff) | |
parent | 715311fa5aeb39fd0904209e1428a3656c721c3d (diff) | |
download | frameworks_base-270928bd4a1db1dc0d989f4e9897a81ab865e30e.zip frameworks_base-270928bd4a1db1dc0d989f4e9897a81ab865e30e.tar.gz frameworks_base-270928bd4a1db1dc0d989f4e9897a81ab865e30e.tar.bz2 |
Merge changes Idbfeb3cc,I03e8e2e7,Iff9eed78
* changes:
Fix regression in CursorWindow.getString() Bug: 5332296
Clean up CursorWindow lifetime. Bug: 5332296
Fix regression in CursorWindow.copyStingToBuffer. Bug: 5332296
Diffstat (limited to 'core/jni')
-rw-r--r-- | core/jni/android_database_CursorWindow.cpp | 12 | ||||
-rw-r--r-- | core/jni/android_database_SQLiteQuery.cpp | 328 |
2 files changed, 108 insertions, 232 deletions
diff --git a/core/jni/android_database_CursorWindow.cpp b/core/jni/android_database_CursorWindow.cpp index 9ff2cb2..fe1aca0 100644 --- a/core/jni/android_database_CursorWindow.cpp +++ b/core/jni/android_database_CursorWindow.cpp @@ -205,8 +205,14 @@ static jstring nativeGetString(JNIEnv* env, jclass clazz, jint windowPtr, if (type == FIELD_TYPE_STRING) { uint32_t size = fieldSlot->data.buffer.size; #if WINDOW_STORAGE_UTF8 - return size > 1 ? env->NewStringUTF(window->getFieldSlotValueString(fieldSlot)) - : gEmptyString; + if (size <= 1) { + return gEmptyString; + } + // Convert to UTF-16 here instead of calling NewStringUTF. NewStringUTF + // doesn't like UTF-8 strings with high codepoints. It actually expects + // Modified UTF-8 with encoded surrogate pairs. + String16 utf16(window->getFieldSlotValueString(fieldSlot), size - 1); + return env->NewString(reinterpret_cast<const jchar*>(utf16.string()), utf16.size()); #else size_t chars = size / sizeof(char16_t); return chars ? env->NewString(reinterpret_cast<jchar*>( @@ -267,7 +273,7 @@ static void fillCharArrayBufferUTF(JNIEnv* env, jobject bufferObj, if (dataObj) { if (size) { jchar* data = static_cast<jchar*>(env->GetPrimitiveArrayCritical(dataObj, NULL)); - utf8_to_utf16(reinterpret_cast<const uint8_t*>(str), len, + utf8_to_utf16_no_null_terminator(reinterpret_cast<const uint8_t*>(str), len, reinterpret_cast<char16_t*>(data)); env->ReleasePrimitiveArrayCritical(dataObj, data, 0); } diff --git a/core/jni/android_database_SQLiteQuery.cpp b/core/jni/android_database_SQLiteQuery.cpp index fe62256..022a64c 100644 --- a/core/jni/android_database_SQLiteQuery.cpp +++ b/core/jni/android_database_SQLiteQuery.cpp @@ -35,99 +35,20 @@ namespace android { -static jfieldID gHandleField; -static jfieldID gStatementField; - - -#define GET_STATEMENT(env, object) \ - (sqlite3_stmt *)env->GetIntField(object, gStatementField) -#define GET_HANDLE(env, object) \ - (sqlite3 *)env->GetIntField(object, gHandleField) - -static int skip_rows(sqlite3_stmt *statement, int maxRows) { - int retryCount = 0; - for (int i = 0; i < maxRows; i++) { - int err = sqlite3_step(statement); - if (err == SQLITE_ROW){ - // do nothing - } else if (err == SQLITE_DONE) { - return i; - } else if (err == SQLITE_LOCKED || err == SQLITE_BUSY) { - // The table is locked, retry - LOG_WINDOW("Database locked, retrying"); - if (retryCount > 50) { - LOGE("Bailing on database busy rety"); - break; - } - // Sleep to give the thread holding the lock a chance to finish - usleep(1000); - retryCount++; - continue; - } else { - return -1; - } - } - LOG_WINDOW("skip_rows row %d", maxRows); - return maxRows; -} - -static int finish_program_and_get_row_count(sqlite3_stmt *statement) { - int numRows = 0; - int retryCount = 0; - while (true) { - int err = sqlite3_step(statement); - if (err == SQLITE_ROW){ - numRows++; - } else if (err == SQLITE_LOCKED || err == SQLITE_BUSY) { - // The table is locked, retry - LOG_WINDOW("Database locked, retrying"); - if (retryCount > 50) { - LOGE("Bailing on database busy rety"); - break; - } - // Sleep to give the thread holding the lock a chance to finish - usleep(1000); - retryCount++; - continue; - } else { - // no need to throw exception - break; - } - } - sqlite3_reset(statement); - LOG_WINDOW("finish_program_and_get_row_count row %d", numRows); - return numRows; -} - -static jint native_fill_window(JNIEnv* env, jobject object, jint windowPtr, - jint startPos, jint offsetParam, jint maxRead, jint lastPos) -{ - int err; - sqlite3_stmt * statement = GET_STATEMENT(env, object); - int numRows = lastPos; - maxRead += lastPos; - int numColumns; - int retryCount; - int boundParams; - CursorWindow * window; - bool gotAllRows = true; - bool gotException = false; - - if (statement == NULL) { - LOGE("Invalid statement in fillWindow()"); - jniThrowException(env, "java/lang/IllegalStateException", - "Attempting to access a deactivated, closed, or empty cursor"); - return 0; - } +static jint nativeFillWindow(JNIEnv* env, jclass clazz, jint databasePtr, + jint statementPtr, jint windowPtr, jint startPos, jint offsetParam) { + sqlite3* database = reinterpret_cast<sqlite3*>(databasePtr); + sqlite3_stmt* statement = reinterpret_cast<sqlite3_stmt*>(statementPtr); + CursorWindow* window = reinterpret_cast<CursorWindow*>(windowPtr); // Only do the binding if there is a valid offsetParam. If no binding needs to be done - // offsetParam will be set to 0, an invliad value. - if(offsetParam > 0) { + // offsetParam will be set to 0, an invalid value. + if (offsetParam > 0) { // Bind the offset parameter, telling the program which row to start with - err = sqlite3_bind_int(statement, offsetParam, startPos); + int err = sqlite3_bind_int(statement, offsetParam, startPos); if (err != SQLITE_OK) { LOGE("Unable to bind offset position, offsetParam = %d", offsetParam); - throw_sqlite3_exception(env, GET_HANDLE(env, object)); + throw_sqlite3_exception(env, database); return 0; } LOG_WINDOW("Bound to startPos %d", startPos); @@ -135,136 +56,123 @@ static jint native_fill_window(JNIEnv* env, jobject object, jint windowPtr, LOG_WINDOW("Not binding to startPos %d", startPos); } - // Get the native window - window = reinterpret_cast<CursorWindow*>(windowPtr); - if (!window) { - LOGE("Invalid CursorWindow"); - jniThrowException(env, "java/lang/IllegalArgumentException", - "Bad CursorWindow"); - return 0; - } - LOG_WINDOW("Window: numRows = %d, size = %d, freeSpace = %d", window->getNumRows(), window->size(), window->freeSpace()); + // We assume numRows is initially 0. + LOG_WINDOW("Window: numRows = %d, size = %d, freeSpace = %d", + window->getNumRows(), window->size(), window->freeSpace()); - numColumns = sqlite3_column_count(statement); + int numColumns = sqlite3_column_count(statement); if (!window->setNumColumns(numColumns)) { LOGE("Failed to change column count from %d to %d", window->getNumColumns(), numColumns); jniThrowException(env, "java/lang/IllegalStateException", "numColumns mismatch"); return 0; } - retryCount = 0; - if (startPos > 0) { - int num = skip_rows(statement, startPos); - if (num < 0) { - throw_sqlite3_exception(env, GET_HANDLE(env, object)); - return 0; - } else if (num < startPos) { - LOGE("startPos %d > actual rows %d", startPos, num); - return num; - } - } - - while(startPos != 0 || numRows < maxRead) { - err = sqlite3_step(statement); + int retryCount = 0; + int totalRows = 0; + int addedRows = 0; + bool windowFull = false; + bool gotException = false; + const bool countAllRows = (startPos == 0); // when startPos is 0, we count all rows + while (!gotException && (!windowFull || countAllRows)) { + int err = sqlite3_step(statement); if (err == SQLITE_ROW) { - LOG_WINDOW("\nStepped statement %p to row %d", statement, startPos + numRows); + LOG_WINDOW("Stepped statement %p to row %d", statement, totalRows); retryCount = 0; + totalRows += 1; + + // Skip the row if the window is full or we haven't reached the start position yet. + if (startPos >= totalRows || windowFull) { + continue; + } // Allocate a new field directory for the row. This pointer is not reused - // since it mey be possible for it to be relocated on a call to alloc() when + // since it may be possible for it to be relocated on a call to alloc() when // the field data is being allocated. - { - field_slot_t * fieldDir = window->allocRow(); - if (!fieldDir) { - LOG_WINDOW("Failed allocating fieldDir at startPos %d row %d", startPos, numRows); - gotAllRows = false; - goto return_count; - } + field_slot_t* fieldDir = window->allocRow(); + if (!fieldDir) { + LOG_WINDOW("Failed allocating fieldDir at startPos %d row %d", + startPos, addedRows); + windowFull = true; + continue; } - // Pack the row into the window - int i; - for (i = 0; i < numColumns; i++) { + // Pack the row into the window. + for (int i = 0; i < numColumns; i++) { int type = sqlite3_column_type(statement, i); if (type == SQLITE_TEXT) { // TEXT data #if WINDOW_STORAGE_UTF8 - uint8_t const * text = (uint8_t const *)sqlite3_column_text(statement, i); + const uint8_t* text = reinterpret_cast<const uint8_t*>( + sqlite3_column_text(statement, i)); // SQLite does not include the NULL terminator in size, but does // ensure all strings are NULL terminated, so increase size by // one to make sure we store the terminator. size_t size = sqlite3_column_bytes(statement, i) + 1; #else - uint8_t const * text = (uint8_t const *)sqlite3_column_text16(statement, i); + const uint8_t* text = reinterpret_cast<const uint8_t*>( + sqlite3_column_text16(statement, i)); size_t size = sqlite3_column_bytes16(statement, i); #endif int offset = window->alloc(size); if (!offset) { - window->freeLastRow(); LOG_WINDOW("Failed allocating %u bytes for text/blob at %d,%d", size, - startPos + numRows, i); - gotAllRows = false; - goto return_count; + startPos + addedRows, i); + windowFull = true; + break; } - window->copyIn(offset, text, size); - // This must be updated after the call to alloc(), since that - // may move the field around in the window - field_slot_t * fieldSlot = window->getFieldSlot(numRows, i); + field_slot_t* fieldSlot = window->getFieldSlot(addedRows, i); fieldSlot->type = FIELD_TYPE_STRING; fieldSlot->data.buffer.offset = offset; fieldSlot->data.buffer.size = size; - - LOG_WINDOW("%d,%d is TEXT with %u bytes", startPos + numRows, i, size); + LOG_WINDOW("%d,%d is TEXT with %u bytes", startPos + addedRows, i, size); } else if (type == SQLITE_INTEGER) { // INTEGER data int64_t value = sqlite3_column_int64(statement, i); - if (!window->putLong(numRows, i, value)) { - window->freeLastRow(); + if (!window->putLong(addedRows, i, value)) { LOG_WINDOW("Failed allocating space for a long in column %d", i); - gotAllRows = false; - goto return_count; + windowFull = true; + break; } - LOG_WINDOW("%d,%d is INTEGER 0x%016llx", startPos + numRows, i, value); + LOG_WINDOW("%d,%d is INTEGER 0x%016llx", startPos + addedRows, i, value); } else if (type == SQLITE_FLOAT) { // FLOAT data double value = sqlite3_column_double(statement, i); - if (!window->putDouble(numRows, i, value)) { - window->freeLastRow(); + if (!window->putDouble(addedRows, i, value)) { LOG_WINDOW("Failed allocating space for a double in column %d", i); - gotAllRows = false; - goto return_count; + windowFull = true; + break; } - LOG_WINDOW("%d,%d is FLOAT %lf", startPos + numRows, i, value); + LOG_WINDOW("%d,%d is FLOAT %lf", startPos + addedRows, i, value); } else if (type == SQLITE_BLOB) { // BLOB data uint8_t const * blob = (uint8_t const *)sqlite3_column_blob(statement, i); size_t size = sqlite3_column_bytes16(statement, i); int offset = window->alloc(size); if (!offset) { - window->freeLastRow(); LOG_WINDOW("Failed allocating %u bytes for blob at %d,%d", size, - startPos + numRows, i); - gotAllRows = false; - goto return_count; + startPos + addedRows, i); + windowFull = true; + break; } - window->copyIn(offset, blob, size); - // This must be updated after the call to alloc(), since that - // may move the field around in the window - field_slot_t * fieldSlot = window->getFieldSlot(numRows, i); + field_slot_t* fieldSlot = window->getFieldSlot(addedRows, i); fieldSlot->type = FIELD_TYPE_BLOB; fieldSlot->data.buffer.offset = offset; fieldSlot->data.buffer.size = size; - - LOG_WINDOW("%d,%d is Blob with %u bytes @ %d", startPos + numRows, i, size, offset); + LOG_WINDOW("%d,%d is Blob with %u bytes @ %d", + startPos + addedRows, i, size, offset); } else if (type == SQLITE_NULL) { // NULL field - window->putNull(numRows, i); + if (!window->putNull(addedRows, i)) { + LOG_WINDOW("Failed allocating space for a null in column %d", i); + windowFull = true; + break; + } - LOG_WINDOW("%d,%d is NULL", startPos + numRows, i); + LOG_WINDOW("%d,%d is NULL", startPos + addedRows, i); } else { // Unknown data LOGE("Unknown column type when filling database window"); @@ -274,14 +182,12 @@ static jint native_fill_window(JNIEnv* env, jobject object, jint windowPtr, } } - if (i < numColumns) { - // Not all the fields fit in the window - // Unknown data error happened - break; + // Update the final row tally. + if (windowFull || gotException) { + window->freeLastRow(); + } else { + addedRows += 1; } - - // Mark the row as complete in the window - numRows++; } else if (err == SQLITE_DONE) { // All rows processed, bail LOG_WINDOW("Processed all rows"); @@ -290,63 +196,41 @@ static jint native_fill_window(JNIEnv* env, jobject object, jint windowPtr, // The table is locked, retry LOG_WINDOW("Database locked, retrying"); if (retryCount > 50) { - LOGE("Bailing on database busy rety"); - throw_sqlite3_exception(env, GET_HANDLE(env, object), "retrycount exceeded"); + LOGE("Bailing on database busy retry"); + throw_sqlite3_exception(env, database, "retrycount exceeded"); gotException = true; - break; + } else { + // Sleep to give the thread holding the lock a chance to finish + usleep(1000); + retryCount++; } - - // Sleep to give the thread holding the lock a chance to finish - usleep(1000); - - retryCount++; - continue; } else { - throw_sqlite3_exception(env, GET_HANDLE(env, object)); + throw_sqlite3_exception(env, database); gotException = true; - break; } } - LOG_WINDOW("Resetting statement %p after fetching %d rows in %d bytes\n\n\n\n", statement, - numRows, window->size() - window->freeSpace()); - LOG_WINDOW("Filled window with %d rows in %d bytes", numRows, - window->size() - window->freeSpace()); - if (err == SQLITE_ROW) { - // there is more data to be returned. let the caller know by returning -1 - return -1; - } - return_count: - if (startPos) { - sqlite3_reset(statement); - LOG_WINDOW("Not doing count(*) because startPos %d is non-zero", startPos); - return 0; - } else if (gotAllRows) { - sqlite3_reset(statement); - LOG_WINDOW("Not doing count(*) because we already know the count(*)"); - return numRows; - } else if (gotException) { - return 0; - } else { - // since startPos == 0, we need to get the count(*) of the result set - return numRows + 1 + finish_program_and_get_row_count(statement); + LOG_WINDOW("Resetting statement %p after fetching %d rows and adding %d rows" + "to the window in %d bytes", + statement, totalRows, addedRows, window->size() - window->freeSpace()); + sqlite3_reset(statement); + + // Report the total number of rows on request. + if (startPos > totalRows) { + LOGE("startPos %d > actual rows %d", startPos, totalRows); } + return countAllRows ? totalRows : 0; } -static jint native_column_count(JNIEnv* env, jobject object) -{ - sqlite3_stmt * statement = GET_STATEMENT(env, object); - +static jint nativeColumnCount(JNIEnv* env, jclass clazz, jint statementPtr) { + sqlite3_stmt* statement = reinterpret_cast<sqlite3_stmt*>(statementPtr); return sqlite3_column_count(statement); } -static jstring native_column_name(JNIEnv* env, jobject object, jint columnIndex) -{ - sqlite3_stmt * statement = GET_STATEMENT(env, object); - char const * name; - - name = sqlite3_column_name(statement, columnIndex); - +static jstring nativeColumnName(JNIEnv* env, jclass clazz, jint statementPtr, + jint columnIndex) { + sqlite3_stmt* statement = reinterpret_cast<sqlite3_stmt*>(statementPtr); + const char* name = sqlite3_column_name(statement, columnIndex); return env->NewStringUTF(name); } @@ -354,30 +238,16 @@ static jstring native_column_name(JNIEnv* env, jobject object, jint columnIndex) static JNINativeMethod sMethods[] = { /* name, signature, funcPtr */ - {"native_fill_window", "(IIIII)I", - (void *)native_fill_window}, - {"native_column_count", "()I", (void*)native_column_count}, - {"native_column_name", "(I)Ljava/lang/String;", (void *)native_column_name}, + { "nativeFillWindow", "(IIIII)I", + (void*)nativeFillWindow }, + { "nativeColumnCount", "(I)I", + (void*)nativeColumnCount}, + { "nativeColumnName", "(II)Ljava/lang/String;", + (void*)nativeColumnName}, }; int register_android_database_SQLiteQuery(JNIEnv * env) { - jclass clazz; - - clazz = env->FindClass("android/database/sqlite/SQLiteQuery"); - if (clazz == NULL) { - LOGE("Can't find android/database/sqlite/SQLiteQuery"); - return -1; - } - - gHandleField = env->GetFieldID(clazz, "nHandle", "I"); - gStatementField = env->GetFieldID(clazz, "nStatement", "I"); - - if (gHandleField == NULL || gStatementField == NULL) { - LOGE("Error locating fields"); - return -1; - } - return AndroidRuntime::registerNativeMethods(env, "android/database/sqlite/SQLiteQuery", sMethods, NELEM(sMethods)); } |