diff options
| -rw-r--r-- | core/jni/android_database_SQLiteRawStatement.cpp | 13 | ||||
| -rw-r--r-- | core/tests/coretests/src/android/database/sqlite/SQLiteRawStatementTest.java | 95 |
2 files changed, 105 insertions, 3 deletions
diff --git a/core/jni/android_database_SQLiteRawStatement.cpp b/core/jni/android_database_SQLiteRawStatement.cpp index b6b788114d22..8fc13a82e74e 100644 --- a/core/jni/android_database_SQLiteRawStatement.cpp +++ b/core/jni/android_database_SQLiteRawStatement.cpp @@ -41,6 +41,11 @@ */ namespace android { +// A zero-length byte array that can be returned by getColumnBlob(). The theory is that +// zero-length blobs are common enough that it is worth having a single, global instance. The +// object is created in the jni registration function. It is never destroyed. +static jbyteArray emptyArray = nullptr; + // Helper functions. static sqlite3 *db(long statementPtr) { return sqlite3_db_handle(reinterpret_cast<sqlite3_stmt*>(statementPtr)); @@ -226,7 +231,7 @@ static jbyteArray columnBlob(JNIEnv* env, jclass, jlong stmtPtr, jint col) { throwIfInvalidColumn(env, stmtPtr, col); const void* blob = sqlite3_column_blob(stmt(stmtPtr), col); if (blob == nullptr) { - return NULL; + return (sqlite3_column_type(stmt(stmtPtr), col) == SQLITE_NULL) ? NULL : emptyArray; } size_t size = sqlite3_column_bytes(stmt(stmtPtr), col); jbyteArray result = env->NewByteArray(size); @@ -316,8 +321,10 @@ static const JNINativeMethod sStatementMethods[] = int register_android_database_SQLiteRawStatement(JNIEnv *env) { - return RegisterMethodsOrDie(env, "android/database/sqlite/SQLiteRawStatement", - sStatementMethods, NELEM(sStatementMethods)); + RegisterMethodsOrDie(env, "android/database/sqlite/SQLiteRawStatement", + sStatementMethods, NELEM(sStatementMethods)); + emptyArray = MakeGlobalRefOrDie(env, env->NewByteArray(0)); + return 0; } } // namespace android diff --git a/core/tests/coretests/src/android/database/sqlite/SQLiteRawStatementTest.java b/core/tests/coretests/src/android/database/sqlite/SQLiteRawStatementTest.java index 548b8ec3f6de..8071d3dff619 100644 --- a/core/tests/coretests/src/android/database/sqlite/SQLiteRawStatementTest.java +++ b/core/tests/coretests/src/android/database/sqlite/SQLiteRawStatementTest.java @@ -19,6 +19,7 @@ package android.database.sqlite; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; @@ -507,6 +508,12 @@ public class SQLiteRawStatementTest { s.bindInt(1, 3); s.step(); s.reset(); + // Bind a zero-length blob + s.clearBindings(); + s.bindInt(1, 4); + s.bindBlob(2, new byte[0]); + s.step(); + s.reset(); } mDatabase.setTransactionSuccessful(); } finally { @@ -545,6 +552,17 @@ public class SQLiteRawStatementTest { for (int i = 0; i < c.length; i++) c[i] = 0; s.bindInt(1, 3); assertTrue(s.step()); + assertNull(s.getColumnBlob(0)); + assertEquals(0, s.readColumnBlob(0, c, 0, c.length, 0)); + for (int i = 0; i < c.length; i++) assertEquals(0, c[i]); + s.reset(); + + // Fetch the zero-length blob + s.bindInt(1, 4); + assertTrue(s.step()); + byte[] r = s.getColumnBlob(0); + assertNotNull(r); + assertEquals(0, r.length); assertEquals(0, s.readColumnBlob(0, c, 0, c.length, 0)); for (int i = 0; i < c.length; i++) assertEquals(0, c[i]); s.reset(); @@ -572,6 +590,83 @@ public class SQLiteRawStatementTest { } @Test + public void testText() { + mDatabase.beginTransaction(); + try { + final String query = "CREATE TABLE t1 (i int, b text)"; + try (SQLiteRawStatement s = mDatabase.createRawStatement(query)) { + assertFalse(s.step()); + } + mDatabase.setTransactionSuccessful(); + } finally { + mDatabase.endTransaction(); + } + + // Insert data into the table. + mDatabase.beginTransaction(); + try { + final String query = "INSERT INTO t1 (i, b) VALUES (?1, ?2)"; + try (SQLiteRawStatement s = mDatabase.createRawStatement(query)) { + // Bind a string + s.bindInt(1, 1); + s.bindText(2, "text"); + s.step(); + s.reset(); + s.clearBindings(); + + // Bind a zero-length string + s.bindInt(1, 2); + s.bindText(2, ""); + s.step(); + s.reset(); + s.clearBindings(); + + // Bind a null string + s.clearBindings(); + s.bindInt(1, 3); + s.step(); + s.reset(); + s.clearBindings(); + } + mDatabase.setTransactionSuccessful(); + } finally { + mDatabase.endTransaction(); + } + + // Read back data and verify it against the reference copy. + mDatabase.beginTransactionReadOnly(); + try { + final String query = "SELECT (b) FROM t1 WHERE i = ?1"; + try (SQLiteRawStatement s = mDatabase.createRawStatement(query)) { + // Fetch the entire reference array. + s.bindInt(1, 1); + assertTrue(s.step()); + assertEquals(SQLiteRawStatement.SQLITE_DATA_TYPE_TEXT, s.getColumnType(0)); + + String a = s.getColumnText(0); + assertNotNull(a); + assertEquals(a, "text"); + s.reset(); + + s.bindInt(1, 2); + assertTrue(s.step()); + String b = s.getColumnText(0); + assertNotNull(b); + assertEquals(b, ""); + s.reset(); + + s.bindInt(1, 3); + assertTrue(s.step()); + String c = s.getColumnText(0); + assertNull(c); + s.reset(); + } + } finally { + mDatabase.endTransaction(); + } + } + + @Test public void testParameterMetadata() { createComplexDatabase(); |