Check for out-of-memory errors and throw exception on native SQLite drivers.
Bug: 304297717
Test: ./sqlite/scripts/runConformanceTest.sh
Change-Id: Ieff2dd0e67bd32ed8aa1382d2d5d0b1baa5f0448
diff --git a/sqlite/sqlite-bundled/src/jvmAndroidMain/jni/sqlite_bindings.cpp b/sqlite/sqlite-bundled/src/jvmAndroidMain/jni/sqlite_bindings.cpp
index ffc8b89..47e170a 100644
--- a/sqlite/sqlite-bundled/src/jvmAndroidMain/jni/sqlite_bindings.cpp
+++ b/sqlite/sqlite-bundled/src/jvmAndroidMain/jni/sqlite_bindings.cpp
@@ -41,6 +41,20 @@
return false;
}
+static bool throwOutOfMemoryError(JNIEnv *env) {
+ jclass exceptionClass = env->FindClass("java/lang/OutOfMemoryError");
+ int throwResult = env->ThrowNew(exceptionClass, nullptr);
+ return throwResult == 0;
+}
+
+static bool throwIfOutOfMemory(JNIEnv *env, sqlite3_stmt *stmt) {
+ int lastRc = sqlite3_errcode(sqlite3_db_handle(stmt));
+ if (lastRc == SQLITE_NOMEM) {
+ return throwOutOfMemoryError(env);
+ }
+ return false;
+}
+
extern "C" JNIEXPORT jint JNICALL
Java_androidx_sqlite_driver_bundled_BundledSQLiteDriverKt_nativeThreadSafeMode(
JNIEnv* env,
@@ -196,8 +210,9 @@
if (throwIfNoRow(env, stmt)) return nullptr;
if (throwIfInvalidColumn(env, stmt, index)) return nullptr;
const void *blob = sqlite3_column_blob(stmt, index);
+ if (blob == nullptr && throwIfOutOfMemory(env, stmt)) return nullptr;
int size = sqlite3_column_bytes(stmt, index);
- // TODO(b/304297717): Use sqlite3_errcode() to check for out-of-memory
+ if (size == 0 && throwIfOutOfMemory(env, stmt)) return nullptr;
jbyteArray byteArray = env->NewByteArray(size);
if (size > 0) {
env->SetByteArrayRegion(byteArray, 0, size, static_cast<const jbyte*>(blob));
@@ -240,12 +255,10 @@
if (throwIfInvalidColumn(env, stmt, index)) return nullptr;
// Java / jstring represents a string in UTF-16 encoding.
const jchar *text = static_cast<const jchar*>(sqlite3_column_text16(stmt, index));
- if (text) {
- int length = sqlite3_column_bytes16(stmt, index) / sizeof(jchar);
- return env->NewString(text, length);
- }
- // TODO: Use sqlite3_errcode() to check for out-of-memory
- return nullptr;
+ if (text == nullptr && throwIfOutOfMemory(env, stmt)) return nullptr;
+ size_t length = sqlite3_column_bytes16(stmt, index) / sizeof(jchar);
+ if (length == 0 && throwIfOutOfMemory(env, stmt)) return nullptr;
+ return env->NewString(text, length);
}
extern "C" JNIEXPORT jint JNICALL
@@ -266,8 +279,9 @@
sqlite3_stmt *stmt = reinterpret_cast<sqlite3_stmt*>(stmtPointer);
if (throwIfInvalidColumn(env, stmt, index)) return nullptr;
const char *name = sqlite3_column_name(stmt, index);
- if (name == NULL) {
- // TODO: throw out-of-memory exception
+ if (name == nullptr) {
+ throwOutOfMemoryError(env);
+ return nullptr;
}
return env->NewStringUTF(name);
}
diff --git a/sqlite/sqlite-framework/src/nativeMain/kotlin/androidx/sqlite/driver/NativeSQLiteStatement.kt b/sqlite/sqlite-framework/src/nativeMain/kotlin/androidx/sqlite/driver/NativeSQLiteStatement.kt
index b339344..957158ca 100644
--- a/sqlite/sqlite-framework/src/nativeMain/kotlin/androidx/sqlite/driver/NativeSQLiteStatement.kt
+++ b/sqlite/sqlite-framework/src/nativeMain/kotlin/androidx/sqlite/driver/NativeSQLiteStatement.kt
@@ -130,9 +130,7 @@
// sqlite3_column_text16(), even empty strings, are always zero-terminated. Thus we use
// toKStringFromUtf16() that returns a kotlin.String from a zero-terminated C string.
val value = sqlite3_column_text16(stmtPointer, index)
- if (sqlite3_errcode(dbPointer) == SQLITE_NOMEM) {
- throw OutOfMemoryError()
- }
+ if (value == null) throwIfOutOfMemory()
return value!!.reinterpret<UShortVar>().toKStringFromUtf16()
}
@@ -148,10 +146,9 @@
throwIfNoRow()
throwIfInvalidColumn(index)
val blob = sqlite3_column_blob(stmtPointer, index)
- if (sqlite3_errcode(dbPointer) == SQLITE_NOMEM) {
- throw OutOfMemoryError()
- }
+ if (blob == null) throwIfOutOfMemory()
val size = sqlite3_column_bytes(stmtPointer, index)
+ if (size == 0) throwIfOutOfMemory()
return if (blob != null && size > 0) {
blob.readBytes(size)
} else {
@@ -241,4 +238,11 @@
throwSQLiteException(SQLITE_RANGE, "column index out of range")
}
}
+
+ private fun throwIfOutOfMemory() {
+ val lastResultCode = sqlite3_errcode(dbPointer)
+ if (lastResultCode == SQLITE_NOMEM) {
+ throw OutOfMemoryError()
+ }
+ }
}