Migrating ArrayQueryResultAdapter to use driver APIs.

This CL refactors the ArrayQueryResultAdapter to delegate
to the ListQueryResultAdapter, since SQLStatement API's
do not allow access to the row count as Android's Cursor
did. This workaround is also beneficial in reducing
boilerplate code as well as removing the special handling
of the nullable array types, since the List adapter can
handle this scenario.

The only downside is the conversion from list to an array
is not ideal, but we can circle back to it if we have a
better solution in the future.

Bug: 299168035
Bug: 333903173
Test: DaoKotlinCodeGenTest & BaseSimpleQueryTest
Change-Id: I4fe55163496005ed33de32b61e7c4d39c7a647df
diff --git a/room/integration-tests/multiplatformtestapp/src/commonTest/kotlin/androidx/room/integration/multiplatformtestapp/test/BaseQueryTest.kt b/room/integration-tests/multiplatformtestapp/src/commonTest/kotlin/androidx/room/integration/multiplatformtestapp/test/BaseQueryTest.kt
index 43909bf..1eb28cf 100644
--- a/room/integration-tests/multiplatformtestapp/src/commonTest/kotlin/androidx/room/integration/multiplatformtestapp/test/BaseQueryTest.kt
+++ b/room/integration-tests/multiplatformtestapp/src/commonTest/kotlin/androidx/room/integration/multiplatformtestapp/test/BaseQueryTest.kt
@@ -26,6 +26,7 @@
 import kotlin.test.AfterTest
 import kotlin.test.BeforeTest
 import kotlin.test.Test
+import kotlin.test.assertContentEquals
 import kotlinx.coroutines.CancellationException
 import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.Job
@@ -377,4 +378,42 @@
         }
         assertThat(db.dao().getItemList()).containsExactly(SampleEntity(1))
     }
+
+    @Test
+    fun insertAndDeleteArray() = runTest {
+        val entityArray = arrayOf(
+            SampleEntity(1, 1),
+            SampleEntity(2, 2)
+        )
+        val dao = getRoomDatabase().dao()
+
+        dao.insertArray(entityArray)
+
+        val result = dao.getItemArray()
+        assertThat(result[0].pk).isEqualTo(1)
+        assertThat(result[1].pk).isEqualTo(2)
+
+        dao.deleteArray(entityArray)
+        assertThrows<IllegalStateException> {
+            dao.getSingleItemWithColumn()
+        }.hasMessageThat().contains("The query result was empty")
+    }
+
+    @Test
+    fun insertAndReadArrays() = runTest {
+        val expected = arrayOf(
+            SampleEntity(1, 1),
+            SampleEntity(2, 2)
+        )
+        val dao = getRoomDatabase().dao()
+        dao.insertArray(expected)
+
+        val resultArray = dao.queryOfArray()
+        val resultArrayWithLong = dao.queryOfArrayWithLong()
+        val resultLongArray = dao.queryOfLongArray()
+
+        assertContentEquals(expected, resultArray)
+        assertContentEquals(arrayOf(1, 2), resultArrayWithLong)
+        assertContentEquals(longArrayOf(1, 2), resultLongArray)
+    }
 }
diff --git a/room/integration-tests/multiplatformtestapp/src/commonTest/kotlin/androidx/room/integration/multiplatformtestapp/test/SampleDatabase.kt b/room/integration-tests/multiplatformtestapp/src/commonTest/kotlin/androidx/room/integration/multiplatformtestapp/test/SampleDatabase.kt
index 1deccba..72420e1 100644
--- a/room/integration-tests/multiplatformtestapp/src/commonTest/kotlin/androidx/room/integration/multiplatformtestapp/test/SampleDatabase.kt
+++ b/room/integration-tests/multiplatformtestapp/src/commonTest/kotlin/androidx/room/integration/multiplatformtestapp/test/SampleDatabase.kt
@@ -80,6 +80,9 @@
     suspend fun getItemList(): List<SampleEntity>
 
     @Query("SELECT * FROM SampleEntity")
+    suspend fun getItemArray(): Array<SampleEntity>
+
+    @Query("SELECT * FROM SampleEntity")
     fun getItemListFlow(): Flow<List<SampleEntity>>
 
     @Transaction
@@ -88,6 +91,12 @@
         pks.forEach { deleteItem(it) }
     }
 
+    @Transaction
+    suspend fun deleteArray(entities: Array<SampleEntity>, withError: Boolean = false) {
+        require(!withError)
+        entities.forEach { delete(it) }
+    }
+
     @Query("SELECT * FROM SampleEntity")
     suspend fun getSingleItemWithColumn(): SampleEntity
 
@@ -123,6 +132,9 @@
     suspend fun insert(entity: SampleEntity)
 
     @Insert
+    suspend fun insertArray(entities: Array<SampleEntity>)
+
+    @Insert
     suspend fun insert(entity: SampleEntity2)
 
     @Insert
@@ -139,6 +151,15 @@
 
     @Update
     suspend fun update(entity: SampleEntity)
+
+    @Query("SELECT * FROM SampleEntity")
+    suspend fun queryOfArray(): Array<SampleEntity>
+
+    @Query("SELECT pk FROM SampleEntity")
+    suspend fun queryOfArrayWithLong(): Array<Long>
+
+    @Query("SELECT pk FROM SampleEntity")
+    suspend fun queryOfLongArray(): LongArray
 }
 
 @Database(
diff --git a/room/room-compiler/src/main/kotlin/androidx/room/processor/ProcessorErrors.kt b/room/room-compiler/src/main/kotlin/androidx/room/processor/ProcessorErrors.kt
index 2cdf066..e879e60 100644
--- a/room/room-compiler/src/main/kotlin/androidx/room/processor/ProcessorErrors.kt
+++ b/room/room-compiler/src/main/kotlin/androidx/room/processor/ProcessorErrors.kt
@@ -782,6 +782,10 @@
 
     val INVALID_RELATION_IN_PARTIAL_ENTITY = "Partial entities cannot have relations."
 
+    fun invalidQueryForSingleColumnArray(returnType: String) = "If a DAO function has a " +
+        "primitive array or an array of String return type, a single column must be returned. " +
+        "Please check the query of the DAO function with the `$returnType` return type."
+
     val EXPAND_PROJECTION_ALONG_WITH_REMOVE_UNUSED = """
         Using @${RewriteQueriesToDropUnusedColumns::class.simpleName} annotation when
         room.expandProjection compiler flag is enabled will disable expandProjection for queries
diff --git a/room/room-compiler/src/main/kotlin/androidx/room/solver/TypeAdapterStore.kt b/room/room-compiler/src/main/kotlin/androidx/room/solver/TypeAdapterStore.kt
index f398487..20b75c2 100644
--- a/room/room-compiler/src/main/kotlin/androidx/room/solver/TypeAdapterStore.kt
+++ b/room/room-compiler/src/main/kotlin/androidx/room/solver/TypeAdapterStore.kt
@@ -44,6 +44,7 @@
 import androidx.room.processor.PojoProcessor
 import androidx.room.processor.ProcessorErrors
 import androidx.room.processor.ProcessorErrors.DO_NOT_USE_GENERIC_IMMUTABLE_MULTIMAP
+import androidx.room.processor.ProcessorErrors.invalidQueryForSingleColumnArray
 import androidx.room.solver.binderprovider.CoroutineFlowResultBinderProvider
 import androidx.room.solver.binderprovider.CursorQueryResultBinderProvider
 import androidx.room.solver.binderprovider.DataSourceFactoryQueryResultBinderProvider
@@ -507,15 +508,46 @@
 
         // TODO: (b/192068912) Refactor the following since this if-else cascade has gotten large
         if (typeMirror.isArray() && typeMirror.componentType.isNotByte()) {
+            val componentType = typeMirror.componentType
             checkTypeNullability(
                 typeMirror,
                 extras,
                 "Array",
-                arrayComponentType = typeMirror.componentType
+                arrayComponentType = componentType
             )
-            val rowAdapter =
-                findRowAdapter(typeMirror.componentType, query) ?: return null
-            return ArrayQueryResultAdapter(typeMirror, rowAdapter)
+            val isSingleColumnArray = componentType.asTypeName().isPrimitive ||
+                componentType.isTypeOf(String::class)
+            val queryResultInfo = query.resultInfo
+            if (
+                isSingleColumnArray &&
+                queryResultInfo != null &&
+                queryResultInfo.columns.size > 1
+            ) {
+                context.logger.e(
+                    invalidQueryForSingleColumnArray(
+                        typeMirror.asTypeName().toString(context.codeLanguage)
+                    )
+                )
+                return null
+            }
+
+            // Create a type mirror for a regular List in order to use ListQueryResultAdapter. This
+            // avoids code duplication as an Array can be initialized using a list.
+            val listType = context.processingEnv.getDeclaredType(
+                context.processingEnv.requireTypeElement(List::class),
+                componentType.boxed().makeNonNullable()
+            ).makeNonNullable()
+
+            val listResultAdapter = findQueryResultAdapter(
+                typeMirror = listType,
+                query = query,
+                extras = extras
+            ) ?: return null
+
+            return ArrayQueryResultAdapter(
+                typeMirror,
+                listResultAdapter as ListQueryResultAdapter
+            )
         } else if (typeMirror.typeArguments.isEmpty()) {
             val rowAdapter = findRowAdapter(typeMirror, query) ?: return null
             return SingleItemQueryResultAdapter(rowAdapter)
@@ -568,7 +600,6 @@
                 typeMirror,
                 extras
             )
-
             val typeArg = typeMirror.typeArguments.first().extendsBoundOrSelf()
             val rowAdapter = findRowAdapter(typeArg, query) ?: return null
             return ListQueryResultAdapter(
diff --git a/room/room-compiler/src/main/kotlin/androidx/room/solver/query/result/ArrayQueryResultAdapter.kt b/room/room-compiler/src/main/kotlin/androidx/room/solver/query/result/ArrayQueryResultAdapter.kt
index 35eb742..09d9d07 100644
--- a/room/room-compiler/src/main/kotlin/androidx/room/solver/query/result/ArrayQueryResultAdapter.kt
+++ b/room/room-compiler/src/main/kotlin/androidx/room/solver/query/result/ArrayQueryResultAdapter.kt
@@ -18,103 +18,106 @@
 
 import androidx.room.compiler.codegen.CodeLanguage
 import androidx.room.compiler.codegen.XCodeBlock
+import androidx.room.compiler.codegen.XCodeBlock.Builder.Companion.beginForEachControlFlow
 import androidx.room.compiler.codegen.XTypeName
-import androidx.room.compiler.codegen.box
 import androidx.room.compiler.processing.XArrayType
-import androidx.room.compiler.processing.XNullability
-import androidx.room.ext.KotlinCollectionMemberNames.ARRAY_OF_NULLS
 import androidx.room.ext.getToArrayFunction
 import androidx.room.solver.CodeGenScope
 
 class ArrayQueryResultAdapter(
     private val arrayType: XArrayType,
-    private val rowAdapter: RowAdapter
-) : QueryResultAdapter(listOf(rowAdapter)) {
+    private val listResultAdapter: ListQueryResultAdapter
+) : QueryResultAdapter(listResultAdapter.rowAdapters) {
+    private val componentTypeName: XTypeName = arrayType.componentType.asTypeName()
+    private val arrayTypeName = XTypeName.getArrayName(componentTypeName)
+
     override fun convert(outVarName: String, cursorVarName: String, scope: CodeGenScope) {
         scope.builder.apply {
-            rowAdapter.onCursorReady(cursorVarName = cursorVarName, scope = scope)
-            val componentTypeName: XTypeName = arrayType.componentType.asTypeName()
-            val arrayTypeName = XTypeName.getArrayName(componentTypeName)
+            val listVarName = scope.getTmpVar("_listResult")
+            // Delegate to the ListQueryResultAdapter to convert query result to a List.
+            listResultAdapter.convert(listVarName, cursorVarName, scope)
 
-            // For Java, instantiate a new array of a size using the bracket syntax, for Kotlin
-            // create the array using the std-lib function arrayOfNulls.
-            val tmpResultName = scope.getTmpVar("_tmpResult")
-            addLocalVariable(
-                name = tmpResultName,
-                typeName = XTypeName.getArrayName(componentTypeName.copy(nullable = true)),
-                assignExpr = when (language) {
-                    CodeLanguage.KOTLIN ->
-                        XCodeBlock.of(
-                            language = language,
-                            format = "%M<%T>(%L.getCount())",
-                            ARRAY_OF_NULLS,
-                            componentTypeName,
-                            cursorVarName
-                        )
-                    CodeLanguage.JAVA ->
-                        XCodeBlock.of(
-                            language = language,
-                            format = "new %T[%L.getCount()]",
-                            componentTypeName,
-                            cursorVarName
-                        )
-                }
-            )
+            // Initialize _result to be returned, using the list result we have.
+            val tmpArrayResult = scope.getTmpVar("_tmpArrayResult")
 
-            val tmpVarName = scope.getTmpVar("_item")
-            val indexVar = scope.getTmpVar("_index")
-            addLocalVariable(
-                name = indexVar,
-                typeName = XTypeName.PRIMITIVE_INT,
-                assignExpr = XCodeBlock.of(language, "0"),
-                isMutable = true
-            )
-            beginControlFlow("while (%L.moveToNext())", cursorVarName).apply {
-                addLocalVariable(
-                    name = tmpVarName,
-                    typeName = componentTypeName
-                )
-                rowAdapter.convert(tmpVarName, cursorVarName, scope)
-                addStatement("%L[%L] = %L", tmpResultName, indexVar, tmpVarName)
-                addStatement("%L++", indexVar)
-            }
-            endControlFlow()
-
-            // Finally initialize _result to be returned. Will avoid an unnecessary cast in
-            // Kotlin if the Entity was already nullable.
             val assignCode = XCodeBlock.of(
                 language = language,
                 format = "%L",
-                tmpResultName
+                listVarName
             ).let {
-                if (
-                    language == CodeLanguage.KOTLIN &&
-                    componentTypeName.nullability == XNullability.NONNULL
-                ) {
-                    XCodeBlock.ofCast(
-                        language = language,
-                        typeName = XTypeName.getArrayName(componentTypeName.box()),
-                        expressionBlock = it
-                    )
-                } else {
-                    it
-                }
-            }.let {
-                // If the component is a primitive type and the language is Kotlin, we need to use
-                // an additional built-in function to cast from the boxed to the primitive array
-                // type, i.e. Array<Int> to IntArray.
-                if (
-                    language == CodeLanguage.KOTLIN &&
-                    componentTypeName.isPrimitive
-                ) {
-                    XCodeBlock.of(
-                        language = language,
-                        format = "(%L).%L",
-                        it,
-                        getToArrayFunction(componentTypeName)
-                    )
-                } else {
-                    it
+                when (language) {
+                    CodeLanguage.KOTLIN -> {
+                        if (componentTypeName.isPrimitive) {
+                            // If we have a primitive array like LongArray or ShortArray,
+                            // we use conversion functions like toLongArray() or toShortArray().
+                            XCodeBlock.of(
+                                language = language,
+                                format = "%L.%L",
+                                it,
+                                getToArrayFunction(componentTypeName)
+                            )
+                        } else {
+                            XCodeBlock.of(
+                                language = language,
+                                format = "%L.%L",
+                                it,
+                                "toTypedArray()"
+                            )
+                        }
+                    }
+                    CodeLanguage.JAVA -> {
+                        if (componentTypeName.isPrimitive) {
+                            // In Java, initializing an Array using a List is not
+                            // straightforward, and requires we create an empty array that will be
+                            // initialized using the list contents.
+                            addLocalVariable(
+                                name = tmpArrayResult,
+                                typeName = arrayTypeName,
+                                assignExpr = XCodeBlock.of(
+                                    language = language,
+                                    format = "new %T[%L.size()]",
+                                    componentTypeName,
+                                    listVarName
+                                )
+                            )
+                            // If the array is primitive, we have to loop over the list to copy
+                            // contents, as we cannot use toArray() on primitive array types.
+                            val indexVarName = scope.getTmpVar("_index")
+                            addLocalVariable(
+                                name = indexVarName,
+                                typeName = componentTypeName,
+                                isMutable = true,
+                                assignExpr = XCodeBlock.of(language, "0")
+                            )
+                            val itrVar = scope.getTmpVar("_listItem")
+                            beginForEachControlFlow(
+                                iteratorVarName = listVarName,
+                                typeName = componentTypeName,
+                                itemVarName = itrVar
+                            ).apply {
+                                addStatement(
+                                    "%L[%L] = %L",
+                                    tmpArrayResult,
+                                    indexVarName,
+                                    itrVar
+                                )
+                                addStatement("%L++", indexVarName)
+                            }.endControlFlow()
+                            XCodeBlock.of(
+                                language = language,
+                                format = "%L",
+                                tmpArrayResult
+                            )
+                        } else {
+                            // If the array is not primitive, we use the List.toArray() utility.
+                            XCodeBlock.of(
+                                language = language,
+                                format = "%L.toArray(new %T[0])",
+                                listVarName,
+                                componentTypeName
+                            )
+                        }
+                    }
                 }
             }
             addLocalVariable(
@@ -124,4 +127,6 @@
             )
         }
     }
+
+    override fun isMigratedToDriver(): Boolean = true
 }
diff --git a/room/room-compiler/src/test/kotlin/androidx/room/processor/QueryMethodProcessorTest.kt b/room/room-compiler/src/test/kotlin/androidx/room/processor/QueryMethodProcessorTest.kt
index 636a827..e4e2382 100644
--- a/room/room-compiler/src/test/kotlin/androidx/room/processor/QueryMethodProcessorTest.kt
+++ b/room/room-compiler/src/test/kotlin/androidx/room/processor/QueryMethodProcessorTest.kt
@@ -121,7 +121,7 @@
     fun testReadNoParams() {
         singleQueryMethod<ReadQueryMethod>(
             """
-                @Query("SELECT * from User")
+                @Query("SELECT uid from User")
                 abstract public int[] foo();
             """
         ) { parsedQuery, _ ->
@@ -1980,4 +1980,46 @@
             }
         }
     }
+
+    @Test
+    fun testStringArraySingleColumnQuery() {
+        if (!enableVerification) {
+            return
+        }
+        singleQueryMethod<ReadQueryMethod>(
+            """
+                @Query("select * from User")
+                abstract String[] stringArray();
+            """
+        ) { _, invocation ->
+            invocation.assertCompilationResult {
+                hasErrorContaining(
+                    ProcessorErrors.invalidQueryForSingleColumnArray(
+                        "java.lang.String[]"
+                    )
+                )
+            }
+        }
+    }
+
+    @Test
+    fun testLongArraySingleColumnQuery() {
+        if (!enableVerification) {
+            return
+        }
+        singleQueryMethod<ReadQueryMethod>(
+            """
+                @Query("select * from User")
+                abstract long[] longArray();
+            """
+        ) { _, invocation ->
+            invocation.assertCompilationResult {
+                hasErrorContaining(
+                    ProcessorErrors.invalidQueryForSingleColumnArray(
+                        "long[]"
+                    )
+                )
+            }
+        }
+    }
 }
diff --git a/room/room-compiler/src/test/kotlin/androidx/room/writer/DaoKotlinCodeGenTest.kt b/room/room-compiler/src/test/kotlin/androidx/room/writer/DaoKotlinCodeGenTest.kt
index 720724a..786c995 100644
--- a/room/room-compiler/src/test/kotlin/androidx/room/writer/DaoKotlinCodeGenTest.kt
+++ b/room/room-compiler/src/test/kotlin/androidx/room/writer/DaoKotlinCodeGenTest.kt
@@ -1339,13 +1339,21 @@
               @Query("SELECT * FROM MyEntity")
               fun queryOfArray(): Array<MyEntity>
 
+              @Suppress(RoomWarnings.UNNECESSARY_NULLABILITY_IN_DAO_RETURN_TYPE)
+              @Query("SELECT * FROM MyEntity")
+              fun queryOfNullableArray(): Array<MyEntity?>
+
               @Query("SELECT pk FROM MyEntity")
               fun queryOfArrayWithLong(): Array<Long>
 
-              @Query("SELECT * FROM MyEntity")
+              @Suppress(RoomWarnings.UNNECESSARY_NULLABILITY_IN_DAO_RETURN_TYPE)
+              @Query("SELECT pk FROM MyEntity")
+              fun queryOfArrayWithNullableLong(): Array<Long?>
+
+              @Query("SELECT pk FROM MyEntity")
               fun queryOfLongArray(): LongArray
 
-              @Query("SELECT * FROM MyEntity")
+              @Query("SELECT pk FROM MyEntity")
               fun queryOfShortArray(): ShortArray
             }
 
diff --git a/room/room-compiler/src/test/test-data/daoWriter/output/javac/ComplexDao.java b/room/room-compiler/src/test/test-data/daoWriter/output/javac/ComplexDao.java
index 7c8b733..b29e9ef 100644
--- a/room/room-compiler/src/test/test-data/daoWriter/output/javac/ComplexDao.java
+++ b/room/room-compiler/src/test/test-data/daoWriter/output/javac/ComplexDao.java
@@ -264,34 +264,44 @@
         StringUtil.appendPlaceholders(_stringBuilder, _inputSize);
         _stringBuilder.append(")");
         final String _sql = _stringBuilder.toString();
-        final int _argCount = 0 + _inputSize;
-        final RoomSQLiteQuery _statement = RoomSQLiteQuery.acquire(_sql, _argCount);
-        int _argIndex = 1;
-        if (ids == null) {
-            _statement.bindNull(_argIndex);
-        } else {
-            for (int _item : ids) {
-                _statement.bindLong(_argIndex, _item);
-                _argIndex++;
+        return DBUtil.performBlocking(__db, true, false, new Function1<SQLiteConnection, int[]>() {
+            @Override
+            @NonNull
+            public int[] invoke(@NonNull final SQLiteConnection _connection) {
+                final SQLiteStatement _stmt = _connection.prepare(_sql);
+                try {
+                    int _argIndex = 1;
+                    if (ids == null) {
+                        _stmt.bindNull(_argIndex);
+                    } else {
+                        for (int _item : ids) {
+                            _stmt.bindLong(_argIndex, _item);
+                            _argIndex++;
+                        }
+                    }
+                    final List<Integer> _listResult = new ArrayList<Integer>();
+                    while (_stmt.step()) {
+                        final Integer _item_1;
+                        if (_stmt.isNull(0)) {
+                            _item_1 = null;
+                        } else {
+                            _item_1 = (int) (_stmt.getLong(0));
+                        }
+                        _listResult.add(_item_1);
+                    }
+                    final int[] _tmpArrayResult = new int[_listResult.size()];
+                    int _index = 0;
+                    for (int _listItem : _listResult) {
+                        _tmpArrayResult[_index] = _listItem;
+                        _index++;
+                    }
+                    final int[] _result = _tmpArrayResult;
+                    return _result;
+                } finally {
+                    _stmt.close();
+                }
             }
-        }
-        __db.assertNotSuspendingTransaction();
-        final Cursor _cursor = DBUtil.query(__db, _statement, false, null);
-        try {
-            final int[] _tmpResult = new int[_cursor.getCount()];
-            int _index = 0;
-            while (_cursor.moveToNext()) {
-                final int _item_1;
-                _item_1 = _cursor.getInt(0);
-                _tmpResult[_index] = _item_1;
-                _index++;
-            }
-            final int[] _result = _tmpResult;
-            return _result;
-        } finally {
-            _cursor.close();
-            _statement.release();
-        }
+        });
     }
 
     @Override
diff --git a/room/room-compiler/src/test/test-data/daoWriter/output/ksp/ComplexDao.java b/room/room-compiler/src/test/test-data/daoWriter/output/ksp/ComplexDao.java
index d8c8fde..275e55d 100644
--- a/room/room-compiler/src/test/test-data/daoWriter/output/ksp/ComplexDao.java
+++ b/room/room-compiler/src/test/test-data/daoWriter/output/ksp/ComplexDao.java
@@ -228,34 +228,40 @@
         StringUtil.appendPlaceholders(_stringBuilder, _inputSize);
         _stringBuilder.append(")");
         final String _sql = _stringBuilder.toString();
-        final int _argCount = 0 + _inputSize;
-        final RoomSQLiteQuery _statement = RoomSQLiteQuery.acquire(_sql, _argCount);
-        int _argIndex = 1;
-        if (ids == null) {
-            _statement.bindNull(_argIndex);
-        } else {
-            for (int _item : ids) {
-                _statement.bindLong(_argIndex, _item);
-                _argIndex++;
+        return DBUtil.performBlocking(__db, true, false, new Function1<SQLiteConnection, int[]>() {
+            @Override
+            @NonNull
+            public int[] invoke(@NonNull final SQLiteConnection _connection) {
+                final SQLiteStatement _stmt = _connection.prepare(_sql);
+                try {
+                    int _argIndex = 1;
+                    if (ids == null) {
+                        _stmt.bindNull(_argIndex);
+                    } else {
+                        for (int _item : ids) {
+                            _stmt.bindLong(_argIndex, _item);
+                            _argIndex++;
+                        }
+                    }
+                    final List<Integer> _listResult = new ArrayList<Integer>();
+                    while (_stmt.step()) {
+                        final Integer _item_1;
+                        _item_1 = (int) (_stmt.getLong(0));
+                        _listResult.add(_item_1);
+                    }
+                    final int[] _tmpArrayResult = new int[_listResult.size()];
+                    int _index = 0;
+                    for (int _listItem : _listResult) {
+                        _tmpArrayResult[_index] = _listItem;
+                        _index++;
+                    }
+                    final int[] _result = _tmpArrayResult;
+                    return _result;
+                } finally {
+                    _stmt.close();
+                }
             }
-        }
-        __db.assertNotSuspendingTransaction();
-        final Cursor _cursor = DBUtil.query(__db, _statement, false, null);
-        try {
-            final int[] _tmpResult = new int[_cursor.getCount()];
-            int _index = 0;
-            while (_cursor.moveToNext()) {
-                final int _item_1;
-                _item_1 = _cursor.getInt(0);
-                _tmpResult[_index] = _item_1;
-                _index++;
-            }
-            final int[] _result = _tmpResult;
-            return _result;
-        } finally {
-            _cursor.close();
-            _statement.release();
-        }
+        });
     }
 
     @Override
diff --git a/room/room-compiler/src/test/test-data/kotlinCodeGen/queryResultAdapter_array.kt b/room/room-compiler/src/test/test-data/kotlinCodeGen/queryResultAdapter_array.kt
index 34c3a55..c3cf5e4 100644
--- a/room/room-compiler/src/test/test-data/kotlinCodeGen/queryResultAdapter_array.kt
+++ b/room/room-compiler/src/test/test-data/kotlinCodeGen/queryResultAdapter_array.kt
@@ -1,9 +1,7 @@
-import android.database.Cursor
 import androidx.room.RoomDatabase
-import androidx.room.RoomSQLiteQuery
-import androidx.room.RoomSQLiteQuery.Companion.acquire
 import androidx.room.util.getColumnIndexOrThrow
-import androidx.room.util.query
+import androidx.room.util.performBlocking
+import androidx.sqlite.SQLiteStatement
 import javax.`annotation`.processing.Generated
 import kotlin.Array
 import kotlin.Int
@@ -13,8 +11,9 @@
 import kotlin.ShortArray
 import kotlin.String
 import kotlin.Suppress
-import kotlin.arrayOfNulls
 import kotlin.collections.List
+import kotlin.collections.MutableList
+import kotlin.collections.mutableListOf
 import kotlin.reflect.KClass
 
 @Generated(value = ["androidx.room.RoomProcessor"])
@@ -29,98 +28,133 @@
 
   public override fun queryOfArray(): Array<MyEntity> {
     val _sql: String = "SELECT * FROM MyEntity"
-    val _statement: RoomSQLiteQuery = acquire(_sql, 0)
-    __db.assertNotSuspendingTransaction()
-    val _cursor: Cursor = query(__db, _statement, false, null)
-    try {
-      val _cursorIndexOfPk: Int = getColumnIndexOrThrow(_cursor, "pk")
-      val _cursorIndexOfOther: Int = getColumnIndexOrThrow(_cursor, "other")
-      val _cursorIndexOfOther2: Int = getColumnIndexOrThrow(_cursor, "other2")
-      val _tmpResult: Array<MyEntity?> = arrayOfNulls<MyEntity>(_cursor.getCount())
-      var _index: Int = 0
-      while (_cursor.moveToNext()) {
-        val _item: MyEntity
-        val _tmpPk: Int
-        _tmpPk = _cursor.getInt(_cursorIndexOfPk)
-        val _tmpOther: String
-        _tmpOther = _cursor.getString(_cursorIndexOfOther)
-        val _tmpOther2: Long
-        _tmpOther2 = _cursor.getLong(_cursorIndexOfOther2)
-        _item = MyEntity(_tmpPk,_tmpOther,_tmpOther2)
-        _tmpResult[_index] = _item
-        _index++
+    return performBlocking(__db, true, false) { _connection ->
+      val _stmt: SQLiteStatement = _connection.prepare(_sql)
+      try {
+        val _cursorIndexOfPk: Int = getColumnIndexOrThrow(_stmt, "pk")
+        val _cursorIndexOfOther: Int = getColumnIndexOrThrow(_stmt, "other")
+        val _cursorIndexOfOther2: Int = getColumnIndexOrThrow(_stmt, "other2")
+        val _listResult: MutableList<MyEntity> = mutableListOf()
+        while (_stmt.step()) {
+          val _item: MyEntity
+          val _tmpPk: Int
+          _tmpPk = _stmt.getLong(_cursorIndexOfPk).toInt()
+          val _tmpOther: String
+          _tmpOther = _stmt.getText(_cursorIndexOfOther)
+          val _tmpOther2: Long
+          _tmpOther2 = _stmt.getLong(_cursorIndexOfOther2)
+          _item = MyEntity(_tmpPk,_tmpOther,_tmpOther2)
+          _listResult.add(_item)
+        }
+        val _result: Array<MyEntity> = _listResult.toTypedArray()
+        _result
+      } finally {
+        _stmt.close()
       }
-      val _result: Array<MyEntity> = (_tmpResult) as Array<MyEntity>
-      return _result
-    } finally {
-      _cursor.close()
-      _statement.release()
+    }
+  }
+
+  public override fun queryOfNullableArray(): Array<MyEntity?> {
+    val _sql: String = "SELECT * FROM MyEntity"
+    return performBlocking(__db, true, false) { _connection ->
+      val _stmt: SQLiteStatement = _connection.prepare(_sql)
+      try {
+        val _cursorIndexOfPk: Int = getColumnIndexOrThrow(_stmt, "pk")
+        val _cursorIndexOfOther: Int = getColumnIndexOrThrow(_stmt, "other")
+        val _cursorIndexOfOther2: Int = getColumnIndexOrThrow(_stmt, "other2")
+        val _listResult: MutableList<MyEntity> = mutableListOf()
+        while (_stmt.step()) {
+          val _item: MyEntity
+          val _tmpPk: Int
+          _tmpPk = _stmt.getLong(_cursorIndexOfPk).toInt()
+          val _tmpOther: String
+          _tmpOther = _stmt.getText(_cursorIndexOfOther)
+          val _tmpOther2: Long
+          _tmpOther2 = _stmt.getLong(_cursorIndexOfOther2)
+          _item = MyEntity(_tmpPk,_tmpOther,_tmpOther2)
+          _listResult.add(_item)
+        }
+        val _result: Array<MyEntity?> = _listResult.toTypedArray()
+        _result
+      } finally {
+        _stmt.close()
+      }
     }
   }
 
   public override fun queryOfArrayWithLong(): Array<Long> {
     val _sql: String = "SELECT pk FROM MyEntity"
-    val _statement: RoomSQLiteQuery = acquire(_sql, 0)
-    __db.assertNotSuspendingTransaction()
-    val _cursor: Cursor = query(__db, _statement, false, null)
-    try {
-      val _tmpResult: Array<Long?> = arrayOfNulls<Long>(_cursor.getCount())
-      var _index: Int = 0
-      while (_cursor.moveToNext()) {
-        val _item: Long
-        _item = _cursor.getLong(0)
-        _tmpResult[_index] = _item
-        _index++
+    return performBlocking(__db, true, false) { _connection ->
+      val _stmt: SQLiteStatement = _connection.prepare(_sql)
+      try {
+        val _listResult: MutableList<Long> = mutableListOf()
+        while (_stmt.step()) {
+          val _item: Long
+          _item = _stmt.getLong(0)
+          _listResult.add(_item)
+        }
+        val _result: Array<Long> = _listResult.toTypedArray()
+        _result
+      } finally {
+        _stmt.close()
       }
-      val _result: Array<Long> = (_tmpResult) as Array<Long>
-      return _result
-    } finally {
-      _cursor.close()
-      _statement.release()
+    }
+  }
+
+  public override fun queryOfArrayWithNullableLong(): Array<Long?> {
+    val _sql: String = "SELECT pk FROM MyEntity"
+    return performBlocking(__db, true, false) { _connection ->
+      val _stmt: SQLiteStatement = _connection.prepare(_sql)
+      try {
+        val _listResult: MutableList<Long> = mutableListOf()
+        while (_stmt.step()) {
+          val _item: Long
+          _item = _stmt.getLong(0)
+          _listResult.add(_item)
+        }
+        val _result: Array<Long?> = _listResult.toTypedArray()
+        _result
+      } finally {
+        _stmt.close()
+      }
     }
   }
 
   public override fun queryOfLongArray(): LongArray {
-    val _sql: String = "SELECT * FROM MyEntity"
-    val _statement: RoomSQLiteQuery = acquire(_sql, 0)
-    __db.assertNotSuspendingTransaction()
-    val _cursor: Cursor = query(__db, _statement, false, null)
-    try {
-      val _tmpResult: Array<Long?> = arrayOfNulls<Long>(_cursor.getCount())
-      var _index: Int = 0
-      while (_cursor.moveToNext()) {
-        val _item: Long
-        _item = _cursor.getLong(0)
-        _tmpResult[_index] = _item
-        _index++
+    val _sql: String = "SELECT pk FROM MyEntity"
+    return performBlocking(__db, true, false) { _connection ->
+      val _stmt: SQLiteStatement = _connection.prepare(_sql)
+      try {
+        val _listResult: MutableList<Long> = mutableListOf()
+        while (_stmt.step()) {
+          val _item: Long
+          _item = _stmt.getLong(0)
+          _listResult.add(_item)
+        }
+        val _result: LongArray = _listResult.toLongArray()
+        _result
+      } finally {
+        _stmt.close()
       }
-      val _result: LongArray = ((_tmpResult) as Array<Long>).toLongArray()
-      return _result
-    } finally {
-      _cursor.close()
-      _statement.release()
     }
   }
 
   public override fun queryOfShortArray(): ShortArray {
-    val _sql: String = "SELECT * FROM MyEntity"
-    val _statement: RoomSQLiteQuery = acquire(_sql, 0)
-    __db.assertNotSuspendingTransaction()
-    val _cursor: Cursor = query(__db, _statement, false, null)
-    try {
-      val _tmpResult: Array<Short?> = arrayOfNulls<Short>(_cursor.getCount())
-      var _index: Int = 0
-      while (_cursor.moveToNext()) {
-        val _item: Short
-        _item = _cursor.getShort(0)
-        _tmpResult[_index] = _item
-        _index++
+    val _sql: String = "SELECT pk FROM MyEntity"
+    return performBlocking(__db, true, false) { _connection ->
+      val _stmt: SQLiteStatement = _connection.prepare(_sql)
+      try {
+        val _listResult: MutableList<Short> = mutableListOf()
+        while (_stmt.step()) {
+          val _item: Short
+          _item = _stmt.getLong(0).toShort()
+          _listResult.add(_item)
+        }
+        val _result: ShortArray = _listResult.toShortArray()
+        _result
+      } finally {
+        _stmt.close()
       }
-      val _result: ShortArray = ((_tmpResult) as Array<Short>).toShortArray()
-      return _result
-    } finally {
-      _cursor.close()
-      _statement.release()
     }
   }