summaryrefslogtreecommitdiffstats
path: root/core/jni
diff options
context:
space:
mode:
authorJeff Brown <jeffbrown@google.com>2011-10-07 15:08:24 -0700
committerAndroid (Google) Code Review <android-gerrit@google.com>2011-10-07 15:08:24 -0700
commit270928bd4a1db1dc0d989f4e9897a81ab865e30e (patch)
treef9188005feb5a9756922f584d40cdc38bf9103ce /core/jni
parent4cc8bafb4b10614dc881ed8121ce4754811e161d (diff)
parent715311fa5aeb39fd0904209e1428a3656c721c3d (diff)
downloadframeworks_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.cpp12
-rw-r--r--core/jni/android_database_SQLiteQuery.cpp328
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));
}