Add new `convertRows()` methods that wrap an SQLiteStatement with a Cursor.
This CL refactors LimitOffsetDataSource and LimitOffsetPagingSource for backwards compatibility by adding a new `convertRows` method in each API that allows generating code using the new SQLite driver API for Paging2 sources.
Bug: 319660042
Test: DaoKotlinCodeGenTest.kt
Change-Id: I526504dd4760f8ada561d6ef38b99e830f666ae6
diff --git a/room/integration-tests/testapp/src/androidTest/java/androidx/room/integration/testapp/paging/LimitOffsetDataSourceTest.java b/room/integration-tests/testapp/src/androidTest/java/androidx/room/integration/testapp/paging/LimitOffsetDataSourceTest.java
index cf95bce..94984a6 100644
--- a/room/integration-tests/testapp/src/androidTest/java/androidx/room/integration/testapp/paging/LimitOffsetDataSourceTest.java
+++ b/room/integration-tests/testapp/src/androidTest/java/androidx/room/integration/testapp/paging/LimitOffsetDataSourceTest.java
@@ -16,10 +16,9 @@
package androidx.room.integration.testapp.paging;
-import static junit.framework.Assert.assertFalse;
-
import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.Assert.assertFalse;
import androidx.annotation.NonNull;
import androidx.room.integration.testapp.test.TestDatabaseTest;
diff --git a/room/room-compiler/src/main/kotlin/androidx/room/ext/xpoet_ext.kt b/room/room-compiler/src/main/kotlin/androidx/room/ext/xpoet_ext.kt
index 07a1f63..0ce5c3f 100644
--- a/room/room-compiler/src/main/kotlin/androidx/room/ext/xpoet_ext.kt
+++ b/room/room-compiler/src/main/kotlin/androidx/room/ext/xpoet_ext.kt
@@ -303,6 +303,7 @@
val DB_UTIL_DROP_FTS_SYNC_TRIGGERS = RoomTypeNames.DB_UTIL.packageMember("dropFtsSyncTriggers")
val DB_UTIL_PERFORM_SUSPENDING = RoomTypeNames.DB_UTIL.packageMember("performSuspending")
val DB_UTIL_PERFORM_BLOCKING = RoomTypeNames.DB_UTIL.packageMember("performBlocking")
+ val DB_UTIL_SUPPORT_DB_TO_CONNECTION = RoomTypeNames.DB_UTIL.packageMember("toSQLiteConnection")
val DB_UTIL_PERFORM_IN_TRANSACTION_SUSPENDING =
RoomTypeNames.DB_UTIL.packageMember("performInTransactionSuspending")
val CURSOR_UTIL_GET_COLUMN_INDEX = RoomTypeNames.CURSOR_UTIL.packageMember("getColumnIndex")
diff --git a/room/room-compiler/src/main/kotlin/androidx/room/solver/binderprovider/MultiTypedPagingSourceQueryResultBinderProvider.kt b/room/room-compiler/src/main/kotlin/androidx/room/solver/binderprovider/MultiTypedPagingSourceQueryResultBinderProvider.kt
index ebc088f..de31dcf 100644
--- a/room/room-compiler/src/main/kotlin/androidx/room/solver/binderprovider/MultiTypedPagingSourceQueryResultBinderProvider.kt
+++ b/room/room-compiler/src/main/kotlin/androidx/room/solver/binderprovider/MultiTypedPagingSourceQueryResultBinderProvider.kt
@@ -31,6 +31,7 @@
import androidx.room.solver.TypeAdapterExtras
import androidx.room.solver.query.result.ListQueryResultAdapter
import androidx.room.solver.query.result.MultiTypedPagingSourceQueryResultBinder
+import androidx.room.solver.query.result.Paging3PagingSourceQueryResultBinder
import androidx.room.solver.query.result.QueryResultBinder
class MultiTypedPagingSourceQueryResultBinderProvider(
@@ -70,8 +71,8 @@
((listAdapter?.accessedTableNames() ?: emptyList()) + query.tables.map { it.name })
.toSet()
- val convertRowsOverrideInfo =
- if (pagingSourceTypeName == PagingTypeNames.PAGING_SOURCE) {
+ return if (pagingSourceTypeName == PagingTypeNames.PAGING_SOURCE) {
+ val convertRowsOverrideInfo =
ConvertRowsOverrideInfo(
method = convertExecutableElement,
continuationParamName = convertExecutableElement.parameters.last().name,
@@ -79,16 +80,19 @@
context.processingEnv.getDeclaredType(roomPagingSourceTypeElement, typeArg),
returnTypeName = LIST.parametrizedBy(typeArg.asTypeName())
)
- } else {
- null
- }
-
- return MultiTypedPagingSourceQueryResultBinder(
- listAdapter = listAdapter,
- tableNames = tableNames,
- className = roomPagingClassName,
- convertRowsOverrideInfo = convertRowsOverrideInfo
- )
+ Paging3PagingSourceQueryResultBinder(
+ listAdapter = listAdapter,
+ tableNames = tableNames,
+ className = roomPagingClassName,
+ convertRowsOverrideInfo = convertRowsOverrideInfo
+ )
+ } else {
+ MultiTypedPagingSourceQueryResultBinder(
+ listAdapter = listAdapter,
+ tableNames = tableNames,
+ className = roomPagingClassName
+ )
+ }
}
override fun matches(declared: XType): Boolean {
diff --git a/room/room-compiler/src/main/kotlin/androidx/room/solver/query/result/CursorQueryResultBinder.kt b/room/room-compiler/src/main/kotlin/androidx/room/solver/query/result/CursorQueryResultBinder.kt
index 1d2de07..258d8f6 100644
--- a/room/room-compiler/src/main/kotlin/androidx/room/solver/query/result/CursorQueryResultBinder.kt
+++ b/room/room-compiler/src/main/kotlin/androidx/room/solver/query/result/CursorQueryResultBinder.kt
@@ -17,14 +17,19 @@
package androidx.room.solver.query.result
import androidx.room.compiler.codegen.XPropertySpec
+import androidx.room.compiler.codegen.XTypeName
import androidx.room.ext.AndroidTypeNames
import androidx.room.solver.CodeGenScope
class CursorQueryResultBinder : QueryResultBinder(NO_OP_RESULT_ADAPTER) {
+
+ override val usesCompatQueryWriter = true
+
override fun convertAndReturn(
- roomSQLiteQueryVar: String,
- canReleaseQuery: Boolean,
+ sqlQueryVar: String,
dbProperty: XPropertySpec,
+ bindStatement: (CodeGenScope.(String) -> Unit)?,
+ returnTypeName: XTypeName,
inTransaction: Boolean,
scope: CodeGenScope
) {
@@ -42,7 +47,7 @@
AndroidTypeNames.CURSOR,
"%N.query(%L)",
dbProperty,
- roomSQLiteQueryVar
+ sqlQueryVar
)
transactionWrapper?.commitTransaction()
addStatement("return %L", resultName)
@@ -50,6 +55,19 @@
}
}
+ override fun convertAndReturn(
+ roomSQLiteQueryVar: String,
+ canReleaseQuery: Boolean,
+ dbProperty: XPropertySpec,
+ inTransaction: Boolean,
+ scope: CodeGenScope
+ ) {
+ error(
+ "This convertAndReturn() should never be invoked, it will be removed once " +
+ "migration to drivers is completed."
+ )
+ }
+
companion object {
private val NO_OP_RESULT_ADAPTER =
object : QueryResultAdapter(emptyList()) {
diff --git a/room/room-compiler/src/main/kotlin/androidx/room/solver/query/result/DataSourceFactoryQueryResultBinder.kt b/room/room-compiler/src/main/kotlin/androidx/room/solver/query/result/DataSourceFactoryQueryResultBinder.kt
index 2204dac..89ab607 100644
--- a/room/room-compiler/src/main/kotlin/androidx/room/solver/query/result/DataSourceFactoryQueryResultBinder.kt
+++ b/room/room-compiler/src/main/kotlin/androidx/room/solver/query/result/DataSourceFactoryQueryResultBinder.kt
@@ -30,10 +30,15 @@
val typeName: XTypeName = positionalDataSourceQueryResultBinder.itemTypeName
+ override val usesCompatQueryWriter = true
+
+ override fun isMigratedToDriver(): Boolean = true
+
override fun convertAndReturn(
- roomSQLiteQueryVar: String,
- canReleaseQuery: Boolean,
+ sqlQueryVar: String,
dbProperty: XPropertySpec,
+ bindStatement: (CodeGenScope.(String) -> Unit)?,
+ returnTypeName: XTypeName,
inTransaction: Boolean,
scope: CodeGenScope
) {
@@ -48,8 +53,10 @@
)
)
addCreateMethod(
- roomSQLiteQueryVar = roomSQLiteQueryVar,
+ roomSQLiteQueryVar = sqlQueryVar,
dbProperty = dbProperty,
+ bindStatement = bindStatement,
+ returnTypeName = returnTypeName,
inTransaction = inTransaction,
scope = scope
)
@@ -59,11 +66,26 @@
}
}
+ override fun convertAndReturn(
+ roomSQLiteQueryVar: String,
+ canReleaseQuery: Boolean,
+ dbProperty: XPropertySpec,
+ inTransaction: Boolean,
+ scope: CodeGenScope
+ ) {
+ error(
+ "This convertAndReturn() should never be invoked, it will be removed once " +
+ "migration to drivers is completed."
+ )
+ }
+
private fun XTypeSpec.Builder.addCreateMethod(
roomSQLiteQueryVar: String,
dbProperty: XPropertySpec,
inTransaction: Boolean,
- scope: CodeGenScope
+ scope: CodeGenScope,
+ bindStatement: (CodeGenScope.(String) -> Unit)?,
+ returnTypeName: XTypeName
) {
addFunction(
XFunSpec.builder(
@@ -75,9 +97,10 @@
returns(positionalDataSourceQueryResultBinder.typeName)
val countedBinderScope = scope.fork()
positionalDataSourceQueryResultBinder.convertAndReturn(
- roomSQLiteQueryVar = roomSQLiteQueryVar,
- canReleaseQuery = true,
+ sqlQueryVar = roomSQLiteQueryVar,
dbProperty = dbProperty,
+ bindStatement = bindStatement,
+ returnTypeName = returnTypeName,
inTransaction = inTransaction,
scope = countedBinderScope
)
diff --git a/room/room-compiler/src/main/kotlin/androidx/room/solver/query/result/MultiTypedPagingSourceQueryResultBinder.kt b/room/room-compiler/src/main/kotlin/androidx/room/solver/query/result/MultiTypedPagingSourceQueryResultBinder.kt
index 33f05e1..b0ac1de4 100644
--- a/room/room-compiler/src/main/kotlin/androidx/room/solver/query/result/MultiTypedPagingSourceQueryResultBinder.kt
+++ b/room/room-compiler/src/main/kotlin/androidx/room/solver/query/result/MultiTypedPagingSourceQueryResultBinder.kt
@@ -16,44 +16,33 @@
package androidx.room.solver.query.result
-import androidx.room.compiler.codegen.CodeLanguage
import androidx.room.compiler.codegen.VisibilityModifier
import androidx.room.compiler.codegen.XClassName
-import androidx.room.compiler.codegen.XCodeBlock
-import androidx.room.compiler.codegen.XCodeBlock.Builder.Companion.applyTo
import androidx.room.compiler.codegen.XFunSpec
import androidx.room.compiler.codegen.XPropertySpec
import androidx.room.compiler.codegen.XTypeName
import androidx.room.compiler.codegen.XTypeSpec
-import androidx.room.ext.AndroidTypeNames
import androidx.room.ext.CommonTypeNames
-import androidx.room.ext.Function1TypeSpec
-import androidx.room.ext.InvokeWithLambdaParameter
-import androidx.room.ext.KotlinTypeNames
-import androidx.room.ext.LambdaSpec
-import androidx.room.ext.RoomMemberNames.DB_UTIL_PERFORM_SUSPENDING
-import androidx.room.ext.RoomTypeNames.RAW_QUERY
import androidx.room.ext.SQLiteDriverTypeNames
import androidx.room.solver.CodeGenScope
-import androidx.room.solver.binderprovider.ConvertRowsOverrideInfo
/**
- * This Binder binds queries directly to native Paging3 PagingSource (i.e.
- * [androidx.room.paging.LimitOffsetPagingSource]) or its subclasses such as
- * [androidx.room.paging.guava.LimitOffsetListenableFuturePagingSource]. Used solely by Paging3.
+ * This Binder binds queries directly to Non-KMP compatible Paging3 PagingSource Binders. Used
+ * solely by Non-KMP Paging3.
*/
class MultiTypedPagingSourceQueryResultBinder(
private val listAdapter: ListQueryResultAdapter?,
private val tableNames: Set<String>,
- className: XClassName,
- val convertRowsOverrideInfo: ConvertRowsOverrideInfo?
+ className: XClassName
) : QueryResultBinder(listAdapter) {
private val itemTypeName: XTypeName =
listAdapter?.rowAdapters?.firstOrNull()?.out?.asTypeName() ?: XTypeName.ANY_OBJECT
private val pagingSourceTypeName: XTypeName = className.parametrizedBy(itemTypeName)
- override fun isMigratedToDriver(): Boolean = convertRowsOverrideInfo != null
+ override val usesCompatQueryWriter = true
+
+ override fun isMigratedToDriver(): Boolean = true
override fun convertAndReturn(
roomSQLiteQueryVar: String,
@@ -62,12 +51,26 @@
inTransaction: Boolean,
scope: CodeGenScope
) {
+ error(
+ "This convertAndReturn() should never be invoked, it will be removed once " +
+ "migration to drivers is completed."
+ )
+ }
+
+ override fun convertAndReturn(
+ sqlQueryVar: String,
+ dbProperty: XPropertySpec,
+ bindStatement: (CodeGenScope.(String) -> Unit)?,
+ returnTypeName: XTypeName,
+ inTransaction: Boolean,
+ scope: CodeGenScope
+ ) {
scope.builder.apply {
val tableNamesList = tableNames.joinToString(", ") { "\"$it\"" }
val pagingSourceSpec =
XTypeSpec.anonymousClassBuilder(
argsFormat = "%L, %N, %L",
- roomSQLiteQueryVar,
+ sqlQueryVar,
dbProperty,
tableNamesList
)
@@ -81,11 +84,11 @@
)
.apply {
val rowsScope = scope.fork()
- val cursorParamName = "cursor"
+ val cursorParamName = "statement"
val resultVar = scope.getTmpVar("_result")
returns(CommonTypeNames.LIST.parametrizedBy(itemTypeName))
addParameter(
- typeName = AndroidTypeNames.CURSOR,
+ typeName = SQLiteDriverTypeNames.STATEMENT,
name = cursorParamName
)
listAdapter?.convert(resultVar, cursorParamName, rowsScope)
@@ -99,203 +102,4 @@
addStatement("return %L", pagingSourceSpec)
}
}
-
- override fun convertAndReturn(
- sqlQueryVar: String,
- dbProperty: XPropertySpec,
- bindStatement: (CodeGenScope.(String) -> Unit)?,
- returnTypeName: XTypeName,
- inTransaction: Boolean,
- scope: CodeGenScope
- ) {
- checkNotNull(convertRowsOverrideInfo) {
- "This version of `convertAndReturn` should only be called when the binder is for the " +
- "base PagingSource. "
- }
- val rawQueryVarName = scope.getTmpVar("_rawQuery")
- val stmtVarName = scope.getTmpVar("_stmt")
-
- when (scope.language) {
- CodeLanguage.JAVA -> {
- val assignExpr =
- if (bindStatement != null) {
- XCodeBlock.ofNewInstance(
- typeName = RAW_QUERY,
- "%L, %L",
- sqlQueryVar,
- Function1TypeSpec(
- parameterTypeName = SQLiteDriverTypeNames.STATEMENT,
- parameterName = stmtVarName,
- returnTypeName = KotlinTypeNames.UNIT
- ) {
- val functionScope = scope.fork()
- functionScope.builder
- .apply { bindStatement.invoke(functionScope, stmtVarName) }
- .build()
- addCode(functionScope.generate())
- addStatement("return %T.INSTANCE", KotlinTypeNames.UNIT)
- }
- )
- } else {
- XCodeBlock.ofNewInstance(typeName = RAW_QUERY, "%L", sqlQueryVar)
- }
- scope.builder.apply {
- addLocalVariable(
- name = rawQueryVarName,
- typeName = RAW_QUERY,
- assignExpr = assignExpr
- )
- addStatement(
- "return %L",
- createPagingSourceSpec(
- scope = scope,
- rawQueryVarName = rawQueryVarName,
- dbProperty = dbProperty,
- stmtVarName = stmtVarName,
- inTransaction = inTransaction
- )
- )
- }
- }
- CodeLanguage.KOTLIN -> {
- scope.builder.apply {
- if (bindStatement != null) {
- beginControlFlow(
- "val %L: %T = %T(%N) { %L ->",
- rawQueryVarName,
- RAW_QUERY,
- RAW_QUERY,
- sqlQueryVar,
- stmtVarName
- )
- bindStatement.invoke(scope, stmtVarName)
- endControlFlow()
- } else {
- addLocalVariable(
- name = rawQueryVarName,
- typeName = RAW_QUERY,
- assignExpr =
- XCodeBlock.ofNewInstance(
- typeName = RAW_QUERY,
- argsFormat = "%N",
- sqlQueryVar
- )
- )
- }
- addStatement(
- "return %L",
- createPagingSourceSpec(
- scope = scope,
- rawQueryVarName = rawQueryVarName,
- stmtVarName = stmtVarName,
- dbProperty = dbProperty,
- inTransaction = false
- )
- )
- }
- }
- }
- }
-
- private fun XCodeBlock.Builder.createPagingSourceSpec(
- scope: CodeGenScope,
- rawQueryVarName: String,
- stmtVarName: String,
- dbProperty: XPropertySpec,
- inTransaction: Boolean
- ): XTypeSpec {
- val tableNamesList = tableNames.joinToString(", ") { "\"$it\"" }
- return XTypeSpec.anonymousClassBuilder(
- argsFormat = "%L, %N, %L",
- rawQueryVarName,
- dbProperty,
- tableNamesList
- )
- .apply {
- superclass(pagingSourceTypeName)
- addFunction(
- createConvertRowsMethod(
- scope = scope,
- dbProperty = dbProperty,
- stmtVarName = stmtVarName,
- inTransaction = inTransaction
- )
- )
- }
- .build()
- }
-
- private fun createConvertRowsMethod(
- scope: CodeGenScope,
- dbProperty: XPropertySpec,
- stmtVarName: String,
- inTransaction: Boolean
- ): XFunSpec {
- val resultVar = scope.getTmpVar("_result")
- checkNotNull(convertRowsOverrideInfo)
- return XFunSpec.overridingBuilder(
- element = convertRowsOverrideInfo.method,
- owner = convertRowsOverrideInfo.owner
- )
- .apply {
- val limitRawQueryParamName = "limitOffsetQuery"
- val rowsScope = scope.fork()
- val connectionVar = scope.getTmpVar("_connection")
- val performBlock =
- InvokeWithLambdaParameter(
- scope = scope,
- functionName = DB_UTIL_PERFORM_SUSPENDING,
- argFormat = listOf("%N", "%L", "%L"),
- args = listOf(dbProperty, /* isReadOnly= */ true, inTransaction),
- continuationParamName = convertRowsOverrideInfo.continuationParamName,
- lambdaSpec =
- object :
- LambdaSpec(
- parameterTypeName = SQLiteDriverTypeNames.CONNECTION,
- parameterName = connectionVar,
- returnTypeName = convertRowsOverrideInfo.returnTypeName,
- javaLambdaSyntaxAvailable = scope.javaLambdaSyntaxAvailable
- ) {
- override fun XCodeBlock.Builder.body(scope: CodeGenScope) {
- applyTo { language ->
- val getSql =
- when (language) {
- CodeLanguage.JAVA -> "getSql()"
- CodeLanguage.KOTLIN -> "sql"
- }
- addLocalVal(
- stmtVarName,
- SQLiteDriverTypeNames.STATEMENT,
- "%L.prepare(%L.$getSql)",
- connectionVar,
- limitRawQueryParamName
- )
- }
- addStatement(
- "%L.getBindingFunction().invoke(%L)",
- limitRawQueryParamName,
- stmtVarName
- )
- beginControlFlow("try").apply {
- listAdapter?.convert(resultVar, stmtVarName, scope)
- applyTo { language ->
- val returnPrefix =
- when (language) {
- CodeLanguage.JAVA -> "return "
- CodeLanguage.KOTLIN -> ""
- }
- addStatement("$returnPrefix%L", resultVar)
- }
- }
- nextControlFlow("finally")
- addStatement("%L.close()", stmtVarName)
- endControlFlow()
- }
- }
- )
- rowsScope.builder.add("return %L", performBlock)
- addCode(rowsScope.generate())
- }
- .build()
- }
}
diff --git a/room/room-compiler/src/main/kotlin/androidx/room/solver/query/result/Paging3SourceQueryResultBinder.kt b/room/room-compiler/src/main/kotlin/androidx/room/solver/query/result/Paging3SourceQueryResultBinder.kt
new file mode 100644
index 0000000..d05aa69
--- /dev/null
+++ b/room/room-compiler/src/main/kotlin/androidx/room/solver/query/result/Paging3SourceQueryResultBinder.kt
@@ -0,0 +1,260 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.room.solver.query.result
+
+import androidx.room.compiler.codegen.CodeLanguage
+import androidx.room.compiler.codegen.XClassName
+import androidx.room.compiler.codegen.XCodeBlock
+import androidx.room.compiler.codegen.XCodeBlock.Builder.Companion.applyTo
+import androidx.room.compiler.codegen.XFunSpec
+import androidx.room.compiler.codegen.XPropertySpec
+import androidx.room.compiler.codegen.XTypeName
+import androidx.room.compiler.codegen.XTypeSpec
+import androidx.room.ext.Function1TypeSpec
+import androidx.room.ext.InvokeWithLambdaParameter
+import androidx.room.ext.KotlinTypeNames
+import androidx.room.ext.LambdaSpec
+import androidx.room.ext.RoomMemberNames.DB_UTIL_PERFORM_SUSPENDING
+import androidx.room.ext.RoomTypeNames.RAW_QUERY
+import androidx.room.ext.SQLiteDriverTypeNames
+import androidx.room.solver.CodeGenScope
+import androidx.room.solver.binderprovider.ConvertRowsOverrideInfo
+
+/**
+ * This Binder binds queries directly to KMP Compatible Paging3 PagingSource (i.e.
+ * [androidx.room.paging.LimitOffsetPagingSource]). Used solely by KMP Paging3.
+ */
+class Paging3PagingSourceQueryResultBinder(
+ private val listAdapter: ListQueryResultAdapter?,
+ private val tableNames: Set<String>,
+ private val convertRowsOverrideInfo: ConvertRowsOverrideInfo,
+ className: XClassName
+) : QueryResultBinder(listAdapter) {
+
+ private val itemTypeName: XTypeName =
+ listAdapter?.rowAdapters?.firstOrNull()?.out?.asTypeName() ?: XTypeName.ANY_OBJECT
+ private val pagingSourceTypeName: XTypeName = className.parametrizedBy(itemTypeName)
+
+ override fun isMigratedToDriver(): Boolean = true
+
+ override fun convertAndReturn(
+ roomSQLiteQueryVar: String,
+ canReleaseQuery: Boolean,
+ dbProperty: XPropertySpec,
+ inTransaction: Boolean,
+ scope: CodeGenScope
+ ) {
+ error(
+ "This convertAndReturn() should never be invoked, it will be removed once " +
+ "migration to drivers is completed."
+ )
+ }
+
+ override fun convertAndReturn(
+ sqlQueryVar: String,
+ dbProperty: XPropertySpec,
+ bindStatement: (CodeGenScope.(String) -> Unit)?,
+ returnTypeName: XTypeName,
+ inTransaction: Boolean,
+ scope: CodeGenScope
+ ) {
+ val rawQueryVarName = scope.getTmpVar("_rawQuery")
+ val stmtVarName = scope.getTmpVar("_stmt")
+
+ when (scope.language) {
+ CodeLanguage.JAVA -> {
+ val assignExpr =
+ if (bindStatement != null) {
+ XCodeBlock.ofNewInstance(
+ typeName = RAW_QUERY,
+ "%L, %L",
+ sqlQueryVar,
+ Function1TypeSpec(
+ parameterTypeName = SQLiteDriverTypeNames.STATEMENT,
+ parameterName = stmtVarName,
+ returnTypeName = KotlinTypeNames.UNIT
+ ) {
+ val functionScope = scope.fork()
+ functionScope.builder
+ .apply { bindStatement.invoke(functionScope, stmtVarName) }
+ .build()
+ addCode(functionScope.generate())
+ addStatement("return %T.INSTANCE", KotlinTypeNames.UNIT)
+ }
+ )
+ } else {
+ XCodeBlock.ofNewInstance(typeName = RAW_QUERY, "%L", sqlQueryVar)
+ }
+ scope.builder.apply {
+ addLocalVariable(
+ name = rawQueryVarName,
+ typeName = RAW_QUERY,
+ assignExpr = assignExpr
+ )
+ addStatement(
+ "return %L",
+ createPagingSourceSpec(
+ scope = scope,
+ rawQueryVarName = rawQueryVarName,
+ stmtVarName = stmtVarName,
+ dbProperty = dbProperty,
+ inTransaction = inTransaction
+ )
+ )
+ }
+ }
+ CodeLanguage.KOTLIN -> {
+ scope.builder.apply {
+ if (bindStatement != null) {
+ beginControlFlow(
+ "val %L: %T = %T(%N) { %L ->",
+ rawQueryVarName,
+ RAW_QUERY,
+ RAW_QUERY,
+ sqlQueryVar,
+ stmtVarName
+ )
+ bindStatement.invoke(scope, stmtVarName)
+ endControlFlow()
+ } else {
+ addLocalVariable(
+ name = rawQueryVarName,
+ typeName = RAW_QUERY,
+ assignExpr =
+ XCodeBlock.ofNewInstance(
+ typeName = RAW_QUERY,
+ argsFormat = "%N",
+ sqlQueryVar
+ )
+ )
+ }
+ addStatement(
+ "return %L",
+ createPagingSourceSpec(
+ scope = scope,
+ rawQueryVarName = rawQueryVarName,
+ stmtVarName = stmtVarName,
+ dbProperty = dbProperty,
+ inTransaction = false
+ )
+ )
+ }
+ }
+ }
+ }
+
+ private fun createPagingSourceSpec(
+ scope: CodeGenScope,
+ rawQueryVarName: String,
+ stmtVarName: String,
+ dbProperty: XPropertySpec,
+ inTransaction: Boolean
+ ): XTypeSpec {
+ val tableNamesList = tableNames.joinToString(", ") { "\"$it\"" }
+ return XTypeSpec.anonymousClassBuilder(
+ argsFormat = "%L, %N, %L",
+ rawQueryVarName,
+ dbProperty,
+ tableNamesList
+ )
+ .apply {
+ superclass(pagingSourceTypeName)
+ addFunction(
+ createConvertRowsMethod(
+ scope = scope,
+ dbProperty = dbProperty,
+ stmtVarName = stmtVarName,
+ inTransaction = inTransaction
+ )
+ )
+ }
+ .build()
+ }
+
+ private fun createConvertRowsMethod(
+ scope: CodeGenScope,
+ dbProperty: XPropertySpec,
+ stmtVarName: String,
+ inTransaction: Boolean
+ ): XFunSpec {
+ val resultVar = scope.getTmpVar("_result")
+ return XFunSpec.overridingBuilder(
+ element = convertRowsOverrideInfo.method,
+ owner = convertRowsOverrideInfo.owner
+ )
+ .apply {
+ val limitRawQueryParamName = "limitOffsetQuery"
+ val rowsScope = scope.fork()
+ val connectionVar = scope.getTmpVar("_connection")
+ val performBlock =
+ InvokeWithLambdaParameter(
+ scope = scope,
+ functionName = DB_UTIL_PERFORM_SUSPENDING,
+ argFormat = listOf("%N", "%L", "%L"),
+ args = listOf(dbProperty, /* isReadOnly= */ true, inTransaction),
+ continuationParamName = convertRowsOverrideInfo.continuationParamName,
+ lambdaSpec =
+ object :
+ LambdaSpec(
+ parameterTypeName = SQLiteDriverTypeNames.CONNECTION,
+ parameterName = connectionVar,
+ returnTypeName = convertRowsOverrideInfo.returnTypeName,
+ javaLambdaSyntaxAvailable = scope.javaLambdaSyntaxAvailable
+ ) {
+ override fun XCodeBlock.Builder.body(scope: CodeGenScope) {
+ applyTo { language ->
+ val getSql =
+ when (language) {
+ CodeLanguage.JAVA -> "getSql()"
+ CodeLanguage.KOTLIN -> "sql"
+ }
+ addLocalVal(
+ stmtVarName,
+ SQLiteDriverTypeNames.STATEMENT,
+ "%L.prepare(%L.$getSql)",
+ connectionVar,
+ limitRawQueryParamName
+ )
+ }
+ addStatement(
+ "%L.getBindingFunction().invoke(%L)",
+ limitRawQueryParamName,
+ stmtVarName
+ )
+ beginControlFlow("try").apply {
+ listAdapter?.convert(resultVar, stmtVarName, scope)
+ applyTo { language ->
+ val returnPrefix =
+ when (language) {
+ CodeLanguage.JAVA -> "return "
+ CodeLanguage.KOTLIN -> ""
+ }
+ addStatement("$returnPrefix%L", resultVar)
+ }
+ }
+ nextControlFlow("finally")
+ addStatement("%L.close()", stmtVarName)
+ endControlFlow()
+ }
+ }
+ )
+ rowsScope.builder.add("return %L", performBlock)
+ addCode(rowsScope.generate())
+ }
+ .build()
+ }
+}
diff --git a/room/room-compiler/src/main/kotlin/androidx/room/solver/query/result/PositionalDataSourceQueryResultBinder.kt b/room/room-compiler/src/main/kotlin/androidx/room/solver/query/result/PositionalDataSourceQueryResultBinder.kt
index 360af30..ea76a1c 100644
--- a/room/room-compiler/src/main/kotlin/androidx/room/solver/query/result/PositionalDataSourceQueryResultBinder.kt
+++ b/room/room-compiler/src/main/kotlin/androidx/room/solver/query/result/PositionalDataSourceQueryResultBinder.kt
@@ -16,14 +16,18 @@
package androidx.room.solver.query.result
+import androidx.room.compiler.codegen.CodeLanguage
import androidx.room.compiler.codegen.VisibilityModifier
+import androidx.room.compiler.codegen.XCodeBlock.Builder.Companion.applyTo
import androidx.room.compiler.codegen.XFunSpec
import androidx.room.compiler.codegen.XPropertySpec
import androidx.room.compiler.codegen.XTypeName
import androidx.room.compiler.codegen.XTypeSpec
-import androidx.room.ext.AndroidTypeNames.CURSOR
import androidx.room.ext.CommonTypeNames.LIST
+import androidx.room.ext.RoomMemberNames.DB_UTIL_SUPPORT_DB_TO_CONNECTION
import androidx.room.ext.RoomTypeNames
+import androidx.room.ext.SQLiteDriverTypeNames
+import androidx.room.ext.SQLiteDriverTypeNames.CONNECTION
import androidx.room.solver.CodeGenScope
/** Used by Paging2 pipeline */
@@ -35,10 +39,15 @@
listAdapter?.rowAdapters?.firstOrNull()?.out?.asTypeName() ?: XTypeName.ANY_OBJECT
val typeName: XTypeName = RoomTypeNames.LIMIT_OFFSET_DATA_SOURCE.parametrizedBy(itemTypeName)
+ override val usesCompatQueryWriter = true
+
+ override fun isMigratedToDriver(): Boolean = true
+
override fun convertAndReturn(
- roomSQLiteQueryVar: String,
- canReleaseQuery: Boolean,
+ sqlQueryVar: String,
dbProperty: XPropertySpec,
+ bindStatement: (CodeGenScope.(String) -> Unit)?,
+ returnTypeName: XTypeName,
inTransaction: Boolean,
scope: CodeGenScope
) {
@@ -50,7 +59,7 @@
XTypeSpec.anonymousClassBuilder(
"%N, %L, %L, %L%L",
dbProperty,
- roomSQLiteQueryVar,
+ sqlQueryVar,
inTransaction,
true,
tableNamesList
@@ -60,9 +69,41 @@
addConvertRowsMethod(scope)
}
.build()
+ val rowAdapter = listAdapter?.rowAdapters?.first()
+ if (rowAdapter is PojoRowAdapter && rowAdapter.relationCollectors.isNotEmpty()) {
+ // @Relation use found, initialize a connection.
+ val connectionVar = scope.getTmpVar("_connection")
+ scope.builder.applyTo { language ->
+ val assignExprFormat =
+ when (language) {
+ CodeLanguage.JAVA -> "%M(%L.getOpenHelper().getWritableDatabase())"
+ CodeLanguage.KOTLIN -> "%M(%L.openHelper.writableDatabase)"
+ }
+ addLocalVal(
+ name = connectionVar,
+ typeName = CONNECTION,
+ assignExprFormat = assignExprFormat,
+ DB_UTIL_SUPPORT_DB_TO_CONNECTION,
+ dbProperty.name
+ )
+ }
+ }
scope.builder.addStatement("return %L", spec)
}
+ override fun convertAndReturn(
+ roomSQLiteQueryVar: String,
+ canReleaseQuery: Boolean,
+ dbProperty: XPropertySpec,
+ inTransaction: Boolean,
+ scope: CodeGenScope
+ ) {
+ error(
+ "This convertAndReturn() should never be invoked, it will be removed once " +
+ "migration to drivers is completed."
+ )
+ }
+
private fun XTypeSpec.Builder.addConvertRowsMethod(scope: CodeGenScope) {
addFunction(
XFunSpec.builder(
@@ -72,8 +113,8 @@
)
.apply {
returns(LIST.parametrizedBy(itemTypeName))
- val cursorParamName = "cursor"
- addParameter(cursorParamName, CURSOR)
+ val cursorParamName = "statement"
+ addParameter(cursorParamName, SQLiteDriverTypeNames.STATEMENT)
val resultVar = scope.getTmpVar("_res")
val rowsScope = scope.fork()
listAdapter?.convert(resultVar, cursorParamName, rowsScope)
diff --git a/room/room-compiler/src/main/kotlin/androidx/room/solver/query/result/QueryResultBinder.kt b/room/room-compiler/src/main/kotlin/androidx/room/solver/query/result/QueryResultBinder.kt
index 286394d..a1d11a6 100644
--- a/room/room-compiler/src/main/kotlin/androidx/room/solver/query/result/QueryResultBinder.kt
+++ b/room/room-compiler/src/main/kotlin/androidx/room/solver/query/result/QueryResultBinder.kt
@@ -44,6 +44,8 @@
// TODO(b/319660042): Remove once migration to driver API is done.
open fun isMigratedToDriver(): Boolean = false
+ open val usesCompatQueryWriter: Boolean = false
+
/**
* Receives the SQL and a function to bind args into a statement, it must then generate the code
* that steps on the query, reads its columns and returns the result.
@@ -56,6 +58,6 @@
inTransaction: Boolean,
scope: CodeGenScope
) {
- error("Result binder has not been migrated to use driver API.")
+ error("Result binder has not been migrated to use driver API. ")
}
}
diff --git a/room/room-compiler/src/main/kotlin/androidx/room/writer/DaoWriter.kt b/room/room-compiler/src/main/kotlin/androidx/room/writer/DaoWriter.kt
index 5fd0551..ed4c8f76 100644
--- a/room/room-compiler/src/main/kotlin/androidx/room/writer/DaoWriter.kt
+++ b/room/room-compiler/src/main/kotlin/androidx/room/writer/DaoWriter.kt
@@ -316,19 +316,24 @@
private fun createRawQueryMethod(method: RawQueryMethod): XFunSpec {
return overrideWithoutAnnotations(method.element, declaredDao)
- .addCode(createRawQueryMethodBody(method))
+ .addCode(
+ if (
+ method.runtimeQueryParam == null ||
+ method.queryResultBinder.usesCompatQueryWriter
+ ) {
+ compatCreateRawQueryMethodBody(method)
+ } else {
+ createRawQueryMethodBody(method)
+ }
+ )
.build()
}
private fun createRawQueryMethodBody(method: RawQueryMethod): XCodeBlock {
- if (method.runtimeQueryParam == null || !method.queryResultBinder.isMigratedToDriver()) {
- return compatCreateRawQueryMethodBody(method)
- }
-
val scope = CodeGenScope(this@DaoWriter, useDriverApi = true)
val sqlQueryVar = scope.getTmpVar("_sql")
val rawQueryParamName =
- if (method.runtimeQueryParam.isSupportQuery()) {
+ if (method.runtimeQueryParam!!.isSupportQuery()) {
val rawQueryVar = scope.getTmpVar("_rawQuery")
scope.builder.addLocalVariable(
name = rawQueryVar,
@@ -352,6 +357,7 @@
rawQueryParamName,
XCodeBlock.ofString(java = "getSql()", kotlin = "sql")
)
+
if (method.returnsValue) {
method.queryResultBinder.convertAndReturn(
sqlQueryVar = sqlQueryVar,
@@ -371,19 +377,17 @@
return scope.generate()
}
+ /** Used by the Non-KMP Paging3 binders and the Paging2 binders. */
private fun compatCreateRawQueryMethodBody(method: RawQueryMethod): XCodeBlock =
XCodeBlock.builder()
.apply {
- val scope = CodeGenScope(this@DaoWriter)
+ val scope = CodeGenScope(this@DaoWriter, useDriverApi = true)
val roomSQLiteQueryVar: String
val queryParam = method.runtimeQueryParam
- val shouldReleaseQuery: Boolean
if (queryParam?.isSupportQuery() == true) {
- roomSQLiteQueryVar = queryParam.paramName
- shouldReleaseQuery = false
+ queryParam.paramName
} else if (queryParam?.isString() == true) {
roomSQLiteQueryVar = scope.getTmpVar("_statement")
- shouldReleaseQuery = true
addLocalVariable(
name = roomSQLiteQueryVar,
typeName = ROOM_SQL_QUERY,
@@ -397,7 +401,6 @@
} else {
// try to generate compiling code. we would've already reported this error
roomSQLiteQueryVar = scope.getTmpVar("_statement")
- shouldReleaseQuery = false
addLocalVariable(
name = roomSQLiteQueryVar,
typeName = ROOM_SQL_QUERY,
@@ -409,16 +412,24 @@
),
)
}
- if (method.returnsValue) {
- // don't generate code because it will create 1 more error. The original
- // error is already reported by the processor.
- method.queryResultBinder.convertAndReturn(
- roomSQLiteQueryVar = roomSQLiteQueryVar,
- canReleaseQuery = shouldReleaseQuery,
- dbProperty = dbProperty,
- inTransaction = method.inTransaction,
- scope = scope
- )
+ val rawQueryParamName = method.runtimeQueryParam?.paramName
+ if (rawQueryParamName != null) {
+ if (method.returnsValue) {
+ method.queryResultBinder.convertAndReturn(
+ sqlQueryVar = rawQueryParamName,
+ dbProperty = dbProperty,
+ bindStatement = { stmtVar ->
+ this.builder.addStatement(
+ "%L.getBindingFunction().invoke(%L)",
+ rawQueryParamName,
+ stmtVar
+ )
+ },
+ returnTypeName = method.returnType.asTypeName(),
+ inTransaction = method.inTransaction,
+ scope = scope
+ )
+ }
}
add(scope.generate())
}
@@ -630,7 +641,6 @@
if (!method.preparedQueryResultBinder.isMigratedToDriver()) {
return compatCreatePreparedQueryMethodBody(method)
}
-
val scope = CodeGenScope(this, useDriverApi = true)
val queryWriter = QueryWriter(method)
val sqlVar = scope.getTmpVar("_sql")
@@ -671,27 +681,35 @@
}
private fun createQueryMethodBody(method: ReadQueryMethod): XCodeBlock {
- if (!method.queryResultBinder.isMigratedToDriver()) {
- return compatCreateQueryMethodBody(method)
- }
-
val scope = CodeGenScope(this, useDriverApi = true)
val queryWriter = QueryWriter(method)
- val sqlVar = scope.getTmpVar("_sql")
- val listSizeArgs = queryWriter.prepareQuery(sqlVar, scope)
+ val sqlStringVar = scope.getTmpVar("_sql")
+
+ val (sqlVar, listSizeArgs) =
+ if (method.queryResultBinder.usesCompatQueryWriter) {
+ val roomSQLiteQueryVar = scope.getTmpVar("_statement")
+ queryWriter.prepareReadAndBind(sqlStringVar, roomSQLiteQueryVar, scope)
+ roomSQLiteQueryVar to emptyList()
+ } else {
+ sqlStringVar to queryWriter.prepareQuery(sqlStringVar, scope)
+ }
+
+ val bindStatement: (CodeGenScope.(String) -> Unit)? =
+ if (queryWriter.parameters.isNotEmpty()) {
+ { stmtVar -> queryWriter.bindArgs(stmtVar, listSizeArgs, this) }
+ } else {
+ null
+ }
+
method.queryResultBinder.convertAndReturn(
sqlQueryVar = sqlVar,
dbProperty = dbProperty,
- bindStatement =
- if (queryWriter.parameters.isNotEmpty()) {
- { stmtVar -> queryWriter.bindArgs(stmtVar, listSizeArgs, this) }
- } else {
- null
- },
+ bindStatement = bindStatement,
returnTypeName = method.returnType.asTypeName(),
inTransaction = method.inTransaction,
scope = scope
)
+
return scope.generate()
}
diff --git a/room/room-compiler/src/test/kotlin/androidx/room/solver/TypeAdapterStoreTest.kt b/room/room-compiler/src/test/kotlin/androidx/room/solver/TypeAdapterStoreTest.kt
index d95c391..4ecbbf4 100644
--- a/room/room-compiler/src/test/kotlin/androidx/room/solver/TypeAdapterStoreTest.kt
+++ b/room/room-compiler/src/test/kotlin/androidx/room/solver/TypeAdapterStoreTest.kt
@@ -61,6 +61,7 @@
import androidx.room.solver.binderprovider.RxQueryResultBinderProvider
import androidx.room.solver.query.parameter.CollectionQueryParameterAdapter
import androidx.room.solver.query.result.MultiTypedPagingSourceQueryResultBinder
+import androidx.room.solver.query.result.Paging3PagingSourceQueryResultBinder
import androidx.room.solver.shortcut.binderprovider.GuavaListenableFutureDeleteOrUpdateMethodBinderProvider
import androidx.room.solver.shortcut.binderprovider.GuavaListenableFutureInsertOrUpsertMethodBinderProvider
import androidx.room.solver.shortcut.binderprovider.RxCallableDeleteOrUpdateMethodBinderProvider
@@ -1428,7 +1429,7 @@
val parsedDao = parser.process()
val binder =
parsedDao.queryMethods.filterIsInstance<ReadQueryMethod>().first().queryResultBinder
- assertThat(binder is MultiTypedPagingSourceQueryResultBinder).isTrue()
+ assertThat(binder is Paging3PagingSourceQueryResultBinder).isTrue()
val pagingSourceXRawType: XRawType? =
invocation.context.processingEnv
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 71326b5..6c29224 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
@@ -800,7 +800,7 @@
@Entity
data class MyEntity(
@PrimaryKey
- val pk: Int,
+ val pk: String,
)
"""
.trimIndent()
@@ -1938,6 +1938,9 @@
abstract class MyDao {
@Query("SELECT * from MyEntity")
abstract fun getDataSourceFactory(): DataSource.Factory<Int, MyEntity>
+
+ @Query("SELECT * FROM MyEntity WHERE pk > :gt ORDER BY pk ASC")
+ abstract fun getDataSourceFactoryWithArgs(gt: Long): DataSource.Factory<Int, MyEntity>
}
@Entity
diff --git a/room/room-compiler/src/test/kotlin/androidx/room/writer/DaoRelationshipKotlinCodeGenTest.kt b/room/room-compiler/src/test/kotlin/androidx/room/writer/DaoRelationshipKotlinCodeGenTest.kt
index 27b0618..26ec990 100644
--- a/room/room-compiler/src/test/kotlin/androidx/room/writer/DaoRelationshipKotlinCodeGenTest.kt
+++ b/room/room-compiler/src/test/kotlin/androidx/room/writer/DaoRelationshipKotlinCodeGenTest.kt
@@ -465,4 +465,95 @@
)
runTest(sources = listOf(src), expectedFilePath = getTestGoldenPath(testName.methodName))
}
+
+ @Test
+ fun relations_dataSource() {
+ val src =
+ Source.kotlin(
+ "MyDao.kt",
+ """
+ import androidx.room.*
+ import androidx.paging.DataSource
+
+ @Dao
+ @Suppress(
+ RoomWarnings.RELATION_QUERY_WITHOUT_TRANSACTION,
+ RoomWarnings.MISSING_INDEX_ON_JUNCTION
+ )
+ interface MyDao {
+ // 1 to 1
+ @Query("SELECT * FROM Song")
+ fun getSongsWithArtist(): DataSource.Factory<Int, SongWithArtist>
+
+ // 1 to many
+ @Query("SELECT * FROM Artist")
+ fun getArtistAndSongs(): DataSource.Factory<Int, ArtistAndSongs>
+
+ // many to many
+ @Query("SELECT * FROM Playlist")
+ fun getPlaylistAndSongs(): DataSource.Factory<Int, PlaylistAndSongs>
+ }
+
+ data class SongWithArtist(
+ @Embedded
+ val song: Song,
+ @Relation(parentColumn = "artistKey", entityColumn = "artistId")
+ val artist: Artist
+ )
+
+ data class ArtistAndSongs(
+ @Embedded
+ val artist: Artist,
+ @Relation(parentColumn = "artistId", entityColumn = "artistKey")
+ val songs: List<Song>
+ )
+
+ data class PlaylistAndSongs(
+ @Embedded
+ val playlist: Playlist,
+ @Relation(
+ parentColumn = "playlistId",
+ entityColumn = "songId",
+ associateBy = Junction(
+ value = PlaylistSongXRef::class,
+ parentColumn = "playlistKey",
+ entityColumn = "songKey",
+ )
+ )
+ val songs: List<Song>
+ )
+
+ @Entity
+ data class Artist(
+ @PrimaryKey
+ val artistId: Long
+ )
+
+ @Entity
+ data class Song(
+ @PrimaryKey
+ val songId: Long,
+ val artistKey: Long
+ )
+
+ @Entity
+ data class Playlist(
+ @PrimaryKey
+ val playlistId: Long,
+ )
+
+ @Entity(primaryKeys = ["playlistKey", "songKey"])
+ data class PlaylistSongXRef(
+ val playlistKey: Long,
+ val songKey: Long,
+ )
+ """
+ .trimIndent()
+ )
+ runTest(
+ sources =
+ listOf(src, databaseSrc, COMMON.DATA_SOURCE_FACTORY, COMMON.POSITIONAL_DATA_SOURCE),
+ expectedFilePath = getTestGoldenPath(testName.methodName)
+ )
+ }
}
diff --git a/room/room-compiler/src/test/test-data/common/input/LimitOffsetListenableFuturePagingSource.kt b/room/room-compiler/src/test/test-data/common/input/LimitOffsetListenableFuturePagingSource.kt
index 4a8eaed..cecbc5c 100644
--- a/room/room-compiler/src/test/test-data/common/input/LimitOffsetListenableFuturePagingSource.kt
+++ b/room/room-compiler/src/test/test-data/common/input/LimitOffsetListenableFuturePagingSource.kt
@@ -15,9 +15,9 @@
*/
package androidx.room.paging.guava
-import android.database.Cursor
import androidx.room.RoomDatabase
import androidx.room.RoomSQLiteQuery
+import androidx.sqlite.SQLiteStatement
abstract class LimitOffsetListenableFuturePagingSource<T : Any>(
private val sourceQuery: RoomSQLiteQuery,
@@ -28,5 +28,5 @@
db,
*tables
) {
- protected abstract override fun convertRows(cursor: Cursor): List<T>
+ protected override abstract fun convertRows(statement: SQLiteStatement): List<T>
}
\ No newline at end of file
diff --git a/room/room-compiler/src/test/test-data/common/input/LimitOffsetRx2PagingSource.kt b/room/room-compiler/src/test/test-data/common/input/LimitOffsetRx2PagingSource.kt
index 1eef542..a3184f0 100644
--- a/room/room-compiler/src/test/test-data/common/input/LimitOffsetRx2PagingSource.kt
+++ b/room/room-compiler/src/test/test-data/common/input/LimitOffsetRx2PagingSource.kt
@@ -15,9 +15,9 @@
*/
package androidx.room.paging.rxjava2
-import android.database.Cursor
import androidx.room.RoomDatabase
import androidx.room.RoomSQLiteQuery
+import androidx.sqlite.SQLiteStatement
abstract class LimitOffsetRxPagingSource<T : Any>(
private val sourceQuery: RoomSQLiteQuery,
@@ -28,5 +28,5 @@
db,
*tables
) {
- protected abstract override fun convertRows(cursor: Cursor): List<T>
+ protected override abstract fun convertRows(statement: SQLiteStatement): List<T>
}
\ No newline at end of file
diff --git a/room/room-compiler/src/test/test-data/common/input/LimitOffsetRx3PagingSource.kt b/room/room-compiler/src/test/test-data/common/input/LimitOffsetRx3PagingSource.kt
index bfb1294..7b2b8b6 100644
--- a/room/room-compiler/src/test/test-data/common/input/LimitOffsetRx3PagingSource.kt
+++ b/room/room-compiler/src/test/test-data/common/input/LimitOffsetRx3PagingSource.kt
@@ -15,9 +15,9 @@
*/
package androidx.room.paging.rxjava3
-import android.database.Cursor
import androidx.room.RoomDatabase
import androidx.room.RoomSQLiteQuery
+import androidx.sqlite.SQLiteStatement
abstract class LimitOffsetRxPagingSource<T : Any>(
private val sourceQuery: RoomSQLiteQuery,
@@ -28,5 +28,5 @@
db,
*tables
) {
- protected abstract override fun convertRows(cursor: Cursor): List<T>
+ protected override abstract fun convertRows(statement: SQLiteStatement): List<T>
}
\ No newline at end of file
diff --git a/room/room-compiler/src/test/test-data/common/input/ListenableFuturePagingSource.kt b/room/room-compiler/src/test/test-data/common/input/ListenableFuturePagingSource.kt
index 1e84ae6..042e6f1 100644
--- a/room/room-compiler/src/test/test-data/common/input/ListenableFuturePagingSource.kt
+++ b/room/room-compiler/src/test/test-data/common/input/ListenableFuturePagingSource.kt
@@ -15,10 +15,10 @@
*/
package androidx.paging
-import android.database.Cursor
import androidx.paging.PagingState
import androidx.room.RoomDatabase
import androidx.room.RoomSQLiteQuery
+import androidx.sqlite.SQLiteStatement
@Suppress("UNUSED_PARAMETER")
abstract class ListenableFuturePagingSource<K : Any, T : Any>(
@@ -33,5 +33,6 @@
override public suspend fun load(params: LoadParams<K>): LoadResult<K, T> {
return LoadResult.Invalid()
}
- protected abstract fun convertRows(cursor: Cursor): List<T>
+
+ protected abstract fun convertRows(statement: SQLiteStatement): List<T>
}
\ No newline at end of file
diff --git a/room/room-compiler/src/test/test-data/common/input/Rx2PagingSource.kt b/room/room-compiler/src/test/test-data/common/input/Rx2PagingSource.kt
index a98e980..e673d19 100644
--- a/room/room-compiler/src/test/test-data/common/input/Rx2PagingSource.kt
+++ b/room/room-compiler/src/test/test-data/common/input/Rx2PagingSource.kt
@@ -19,6 +19,8 @@
import androidx.paging.PagingState
import androidx.room.RoomDatabase
import androidx.room.RoomSQLiteQuery
+import androidx.room.paging.CursorSQLiteStatement
+import androidx.sqlite.SQLiteStatement
@Suppress("UNUSED_PARAMETER")
abstract class RxPagingSource<K : Any, T : Any>(
@@ -34,5 +36,14 @@
return LoadResult.Invalid()
}
- protected abstract fun convertRows(cursor: Cursor): List<T>
+ protected open fun convertRows(cursor: Cursor): List<T> {
+ return convertRows(CursorSQLiteStatement(cursor))
+ }
+
+ protected open fun convertRows(statement: SQLiteStatement): List<T> {
+ throw NotImplementedError(
+ "Unexpected call to a function with no implementation that Room is suppose to " +
+ "generate."
+ )
+ }
}
\ No newline at end of file
diff --git a/room/room-compiler/src/test/test-data/common/input/Rx3PagingSource.kt b/room/room-compiler/src/test/test-data/common/input/Rx3PagingSource.kt
index 212cd77..2b16445 100644
--- a/room/room-compiler/src/test/test-data/common/input/Rx3PagingSource.kt
+++ b/room/room-compiler/src/test/test-data/common/input/Rx3PagingSource.kt
@@ -19,6 +19,8 @@
import androidx.paging.PagingState
import androidx.room.RoomDatabase
import androidx.room.RoomSQLiteQuery
+import androidx.room.paging.CursorSQLiteStatement
+import androidx.sqlite.SQLiteStatement
@Suppress("UNUSED_PARAMETER")
abstract class RxPagingSource<K : Any, T : Any>(
@@ -34,5 +36,14 @@
return LoadResult.Invalid()
}
- protected abstract fun convertRows(cursor: Cursor): List<T>
+ protected open fun convertRows(cursor: Cursor): List<T> {
+ return convertRows(CursorSQLiteStatement(cursor))
+ }
+
+ protected open fun convertRows(statement: SQLiteStatement): List<T> {
+ throw NotImplementedError(
+ "Unexpected call to a function with no implementation that Room is suppose to " +
+ "generate."
+ )
+ }
}
\ No newline at end of file
diff --git a/room/room-compiler/src/test/test-data/kotlinCodeGen/multiTypedPagingSourceResultBinder.kt b/room/room-compiler/src/test/test-data/kotlinCodeGen/multiTypedPagingSourceResultBinder.kt
index fb9b14c..4615252 100644
--- a/room/room-compiler/src/test/test-data/kotlinCodeGen/multiTypedPagingSourceResultBinder.kt
+++ b/room/room-compiler/src/test/test-data/kotlinCodeGen/multiTypedPagingSourceResultBinder.kt
@@ -1,4 +1,3 @@
-import android.database.Cursor
import androidx.paging.ListenableFuturePagingSource
import androidx.paging.PagingSource
import androidx.room.RoomDatabase
@@ -47,8 +46,8 @@
val _result: MutableList<MyEntity> = mutableListOf()
while (_stmt.step()) {
val _item: MyEntity
- val _tmpPk: Int
- _tmpPk = _stmt.getLong(_cursorIndexOfPk).toInt()
+ val _tmpPk: String
+ _tmpPk = _stmt.getText(_cursorIndexOfPk)
_item = MyEntity(_tmpPk)
_result.add(_item)
}
@@ -76,8 +75,8 @@
val _result: MutableList<MyEntity> = mutableListOf()
while (_stmt.step()) {
val _item: MyEntity
- val _tmpPk: Int
- _tmpPk = _stmt.getLong(_cursorIndexOfPk).toInt()
+ val _tmpPk: String
+ _tmpPk = _stmt.getText(_cursorIndexOfPk)
_item = MyEntity(_tmpPk)
_result.add(_item)
}
@@ -93,13 +92,13 @@
val _sql: String = "SELECT pk FROM MyEntity"
val _statement: RoomSQLiteQuery = acquire(_sql, 0)
return object : Rxjava2LimitOffsetRxPagingSource<MyEntity>(_statement, __db, "MyEntity") {
- protected override fun convertRows(cursor: Cursor): List<MyEntity> {
+ protected override fun convertRows(statement: SQLiteStatement): List<MyEntity> {
val _cursorIndexOfPk: Int = 0
val _result: MutableList<MyEntity> = mutableListOf()
- while (cursor.moveToNext()) {
+ while (statement.step()) {
val _item: MyEntity
- val _tmpPk: Int
- _tmpPk = cursor.getInt(_cursorIndexOfPk)
+ val _tmpPk: String
+ _tmpPk = statement.getText(_cursorIndexOfPk)
_item = MyEntity(_tmpPk)
_result.add(_item)
}
@@ -112,13 +111,13 @@
val _sql: String = "SELECT pk FROM MyEntity"
val _statement: RoomSQLiteQuery = acquire(_sql, 0)
return object : Rxjava3LimitOffsetRxPagingSource<MyEntity>(_statement, __db, "MyEntity") {
- protected override fun convertRows(cursor: Cursor): List<MyEntity> {
+ protected override fun convertRows(statement: SQLiteStatement): List<MyEntity> {
val _cursorIndexOfPk: Int = 0
val _result: MutableList<MyEntity> = mutableListOf()
- while (cursor.moveToNext()) {
+ while (statement.step()) {
val _item: MyEntity
- val _tmpPk: Int
- _tmpPk = cursor.getInt(_cursorIndexOfPk)
+ val _tmpPk: String
+ _tmpPk = statement.getText(_cursorIndexOfPk)
_item = MyEntity(_tmpPk)
_result.add(_item)
}
@@ -132,13 +131,13 @@
val _statement: RoomSQLiteQuery = acquire(_sql, 0)
return object : LimitOffsetListenableFuturePagingSource<MyEntity>(_statement, __db, "MyEntity")
{
- protected override fun convertRows(cursor: Cursor): List<MyEntity> {
+ protected override fun convertRows(statement: SQLiteStatement): List<MyEntity> {
val _cursorIndexOfPk: Int = 0
val _result: MutableList<MyEntity> = mutableListOf()
- while (cursor.moveToNext()) {
+ while (statement.step()) {
val _item: MyEntity
- val _tmpPk: Int
- _tmpPk = cursor.getInt(_cursorIndexOfPk)
+ val _tmpPk: String
+ _tmpPk = statement.getText(_cursorIndexOfPk)
_item = MyEntity(_tmpPk)
_result.add(_item)
}
diff --git a/room/room-compiler/src/test/test-data/kotlinCodeGen/paging_dataSource.kt b/room/room-compiler/src/test/test-data/kotlinCodeGen/paging_dataSource.kt
index 2730d8f..f8e49bc 100644
--- a/room/room-compiler/src/test/test-data/kotlinCodeGen/paging_dataSource.kt
+++ b/room/room-compiler/src/test/test-data/kotlinCodeGen/paging_dataSource.kt
@@ -1,12 +1,13 @@
-import android.database.Cursor
import androidx.paging.DataSource
import androidx.room.RoomDatabase
import androidx.room.RoomSQLiteQuery
import androidx.room.RoomSQLiteQuery.Companion.acquire
import androidx.room.paging.LimitOffsetDataSource
import androidx.room.util.getColumnIndexOrThrow
+import androidx.sqlite.SQLiteStatement
import javax.`annotation`.processing.Generated
import kotlin.Int
+import kotlin.Long
import kotlin.String
import kotlin.Suppress
import kotlin.collections.List
@@ -30,16 +31,43 @@
return object : DataSource.Factory<Int, MyEntity>() {
public override fun create(): LimitOffsetDataSource<MyEntity> = object :
LimitOffsetDataSource<MyEntity>(__db, _statement, false, true, "MyEntity") {
- protected override fun convertRows(cursor: Cursor): List<MyEntity> {
- val _cursorIndexOfPk: Int = getColumnIndexOrThrow(cursor, "pk")
- val _cursorIndexOfOther: Int = getColumnIndexOrThrow(cursor, "other")
+ protected override fun convertRows(statement: SQLiteStatement): List<MyEntity> {
+ val _cursorIndexOfPk: Int = getColumnIndexOrThrow(statement, "pk")
+ val _cursorIndexOfOther: Int = getColumnIndexOrThrow(statement, "other")
val _res: MutableList<MyEntity> = mutableListOf()
- while (cursor.moveToNext()) {
+ while (statement.step()) {
val _item: MyEntity
val _tmpPk: Int
- _tmpPk = cursor.getInt(_cursorIndexOfPk)
+ _tmpPk = statement.getLong(_cursorIndexOfPk).toInt()
val _tmpOther: String
- _tmpOther = cursor.getString(_cursorIndexOfOther)
+ _tmpOther = statement.getText(_cursorIndexOfOther)
+ _item = MyEntity(_tmpPk,_tmpOther)
+ _res.add(_item)
+ }
+ return _res
+ }
+ }
+ }
+ }
+
+ public override fun getDataSourceFactoryWithArgs(gt: Long): DataSource.Factory<Int, MyEntity> {
+ val _sql: String = "SELECT * FROM MyEntity WHERE pk > ? ORDER BY pk ASC"
+ val _statement: RoomSQLiteQuery = acquire(_sql, 1)
+ var _argIndex: Int = 1
+ _statement.bindLong(_argIndex, gt)
+ return object : DataSource.Factory<Int, MyEntity>() {
+ public override fun create(): LimitOffsetDataSource<MyEntity> = object :
+ LimitOffsetDataSource<MyEntity>(__db, _statement, false, true, "MyEntity") {
+ protected override fun convertRows(statement: SQLiteStatement): List<MyEntity> {
+ val _cursorIndexOfPk: Int = getColumnIndexOrThrow(statement, "pk")
+ val _cursorIndexOfOther: Int = getColumnIndexOrThrow(statement, "other")
+ val _res: MutableList<MyEntity> = mutableListOf()
+ while (statement.step()) {
+ val _item: MyEntity
+ val _tmpPk: Int
+ _tmpPk = statement.getLong(_cursorIndexOfPk).toInt()
+ val _tmpOther: String
+ _tmpOther = statement.getText(_cursorIndexOfOther)
_item = MyEntity(_tmpPk,_tmpOther)
_res.add(_item)
}
diff --git a/room/room-compiler/src/test/test-data/kotlinCodeGen/relations_dataSource.kt b/room/room-compiler/src/test/test-data/kotlinCodeGen/relations_dataSource.kt
new file mode 100644
index 0000000..68783b0
--- /dev/null
+++ b/room/room-compiler/src/test/test-data/kotlinCodeGen/relations_dataSource.kt
@@ -0,0 +1,314 @@
+import androidx.paging.DataSource
+import androidx.room.RoomDatabase
+import androidx.room.RoomSQLiteQuery
+import androidx.room.RoomSQLiteQuery.Companion.acquire
+import androidx.room.paging.LimitOffsetDataSource
+import androidx.room.util.appendPlaceholders
+import androidx.room.util.getColumnIndex
+import androidx.room.util.getColumnIndexOrThrow
+import androidx.room.util.recursiveFetchMap
+import androidx.room.util.toSQLiteConnection
+import androidx.sqlite.SQLiteConnection
+import androidx.sqlite.SQLiteStatement
+import javax.`annotation`.processing.Generated
+import kotlin.Int
+import kotlin.Long
+import kotlin.String
+import kotlin.Suppress
+import kotlin.collections.List
+import kotlin.collections.MutableList
+import kotlin.collections.MutableMap
+import kotlin.collections.Set
+import kotlin.collections.mutableListOf
+import kotlin.collections.mutableMapOf
+import kotlin.reflect.KClass
+import kotlin.text.StringBuilder
+
+@Generated(value = ["androidx.room.RoomProcessor"])
+@Suppress(names = ["UNCHECKED_CAST", "DEPRECATION", "REDUNDANT_PROJECTION", "REMOVAL"])
+public class MyDao_Impl(
+ __db: RoomDatabase,
+) : MyDao {
+ private val __db: RoomDatabase
+ init {
+ this.__db = __db
+ }
+
+ public override fun getSongsWithArtist(): DataSource.Factory<Int, SongWithArtist> {
+ val _sql: String = "SELECT * FROM Song"
+ val _statement: RoomSQLiteQuery = acquire(_sql, 0)
+ return object : DataSource.Factory<Int, SongWithArtist>() {
+ public override fun create(): LimitOffsetDataSource<SongWithArtist> {
+ val _connection: SQLiteConnection = toSQLiteConnection(__db.openHelper.writableDatabase)
+ return object : LimitOffsetDataSource<SongWithArtist>(__db, _statement, false, true,
+ "Artist", "Song") {
+ protected override fun convertRows(statement: SQLiteStatement): List<SongWithArtist> {
+ val _cursorIndexOfSongId: Int = getColumnIndexOrThrow(statement, "songId")
+ val _cursorIndexOfArtistKey: Int = getColumnIndexOrThrow(statement, "artistKey")
+ val _collectionArtist: MutableMap<Long, Artist?> = mutableMapOf()
+ while (statement.step()) {
+ val _tmpKey: Long
+ _tmpKey = statement.getLong(_cursorIndexOfArtistKey)
+ _collectionArtist.put(_tmpKey, null)
+ }
+ statement.reset()
+ __fetchRelationshipArtistAsArtist(_connection, _collectionArtist)
+ val _res: MutableList<SongWithArtist> = mutableListOf()
+ while (statement.step()) {
+ val _item: SongWithArtist
+ val _tmpSong: Song
+ val _tmpSongId: Long
+ _tmpSongId = statement.getLong(_cursorIndexOfSongId)
+ val _tmpArtistKey: Long
+ _tmpArtistKey = statement.getLong(_cursorIndexOfArtistKey)
+ _tmpSong = Song(_tmpSongId,_tmpArtistKey)
+ val _tmpArtist: Artist?
+ val _tmpKey_1: Long
+ _tmpKey_1 = statement.getLong(_cursorIndexOfArtistKey)
+ _tmpArtist = _collectionArtist.get(_tmpKey_1)
+ if (_tmpArtist == null) {
+ error("Relationship item 'artist' was expected to be NON-NULL but is NULL in @Relation involving a parent column named 'artistKey' and entityColumn named 'artistId'.")
+ }
+ _item = SongWithArtist(_tmpSong,_tmpArtist)
+ _res.add(_item)
+ }
+ return _res
+ }
+ }
+ }
+ }
+ }
+
+ public override fun getArtistAndSongs(): DataSource.Factory<Int, ArtistAndSongs> {
+ val _sql: String = "SELECT * FROM Artist"
+ val _statement: RoomSQLiteQuery = acquire(_sql, 0)
+ return object : DataSource.Factory<Int, ArtistAndSongs>() {
+ public override fun create(): LimitOffsetDataSource<ArtistAndSongs> {
+ val _connection: SQLiteConnection = toSQLiteConnection(__db.openHelper.writableDatabase)
+ return object : LimitOffsetDataSource<ArtistAndSongs>(__db, _statement, false, true, "Song",
+ "Artist") {
+ protected override fun convertRows(statement: SQLiteStatement): List<ArtistAndSongs> {
+ val _cursorIndexOfArtistId: Int = getColumnIndexOrThrow(statement, "artistId")
+ val _collectionSongs: MutableMap<Long, MutableList<Song>> = mutableMapOf()
+ while (statement.step()) {
+ val _tmpKey: Long
+ _tmpKey = statement.getLong(_cursorIndexOfArtistId)
+ if (!_collectionSongs.containsKey(_tmpKey)) {
+ _collectionSongs.put(_tmpKey, mutableListOf())
+ }
+ }
+ statement.reset()
+ __fetchRelationshipSongAsSong(_connection, _collectionSongs)
+ val _res: MutableList<ArtistAndSongs> = mutableListOf()
+ while (statement.step()) {
+ val _item: ArtistAndSongs
+ val _tmpArtist: Artist
+ val _tmpArtistId: Long
+ _tmpArtistId = statement.getLong(_cursorIndexOfArtistId)
+ _tmpArtist = Artist(_tmpArtistId)
+ val _tmpSongsCollection: MutableList<Song>
+ val _tmpKey_1: Long
+ _tmpKey_1 = statement.getLong(_cursorIndexOfArtistId)
+ _tmpSongsCollection = _collectionSongs.getValue(_tmpKey_1)
+ _item = ArtistAndSongs(_tmpArtist,_tmpSongsCollection)
+ _res.add(_item)
+ }
+ return _res
+ }
+ }
+ }
+ }
+ }
+
+ public override fun getPlaylistAndSongs(): DataSource.Factory<Int, PlaylistAndSongs> {
+ val _sql: String = "SELECT * FROM Playlist"
+ val _statement: RoomSQLiteQuery = acquire(_sql, 0)
+ return object : DataSource.Factory<Int, PlaylistAndSongs>() {
+ public override fun create(): LimitOffsetDataSource<PlaylistAndSongs> {
+ val _connection: SQLiteConnection = toSQLiteConnection(__db.openHelper.writableDatabase)
+ return object : LimitOffsetDataSource<PlaylistAndSongs>(__db, _statement, false, true,
+ "PlaylistSongXRef", "Song", "Playlist") {
+ protected override fun convertRows(statement: SQLiteStatement): List<PlaylistAndSongs> {
+ val _cursorIndexOfPlaylistId: Int = getColumnIndexOrThrow(statement, "playlistId")
+ val _collectionSongs: MutableMap<Long, MutableList<Song>> = mutableMapOf()
+ while (statement.step()) {
+ val _tmpKey: Long
+ _tmpKey = statement.getLong(_cursorIndexOfPlaylistId)
+ if (!_collectionSongs.containsKey(_tmpKey)) {
+ _collectionSongs.put(_tmpKey, mutableListOf())
+ }
+ }
+ statement.reset()
+ __fetchRelationshipSongAsSong_1(_connection, _collectionSongs)
+ val _res: MutableList<PlaylistAndSongs> = mutableListOf()
+ while (statement.step()) {
+ val _item: PlaylistAndSongs
+ val _tmpPlaylist: Playlist
+ val _tmpPlaylistId: Long
+ _tmpPlaylistId = statement.getLong(_cursorIndexOfPlaylistId)
+ _tmpPlaylist = Playlist(_tmpPlaylistId)
+ val _tmpSongsCollection: MutableList<Song>
+ val _tmpKey_1: Long
+ _tmpKey_1 = statement.getLong(_cursorIndexOfPlaylistId)
+ _tmpSongsCollection = _collectionSongs.getValue(_tmpKey_1)
+ _item = PlaylistAndSongs(_tmpPlaylist,_tmpSongsCollection)
+ _res.add(_item)
+ }
+ return _res
+ }
+ }
+ }
+ }
+ }
+
+ private fun __fetchRelationshipArtistAsArtist(_connection: SQLiteConnection,
+ _map: MutableMap<Long, Artist?>) {
+ val __mapKeySet: Set<Long> = _map.keys
+ if (__mapKeySet.isEmpty()) {
+ return
+ }
+ if (_map.size > 999) {
+ recursiveFetchMap(_map, false) { _tmpMap ->
+ __fetchRelationshipArtistAsArtist(_connection, _tmpMap)
+ }
+ return
+ }
+ val _stringBuilder: StringBuilder = StringBuilder()
+ _stringBuilder.append("SELECT `artistId` FROM `Artist` WHERE `artistId` IN (")
+ val _inputSize: Int = __mapKeySet.size
+ appendPlaceholders(_stringBuilder, _inputSize)
+ _stringBuilder.append(")")
+ val _sql: String = _stringBuilder.toString()
+ val _stmt: SQLiteStatement = _connection.prepare(_sql)
+ var _argIndex: Int = 1
+ for (_item: Long in __mapKeySet) {
+ _stmt.bindLong(_argIndex, _item)
+ _argIndex++
+ }
+ try {
+ val _itemKeyIndex: Int = getColumnIndex(_stmt, "artistId")
+ if (_itemKeyIndex == -1) {
+ return
+ }
+ val _cursorIndexOfArtistId: Int = 0
+ while (_stmt.step()) {
+ val _tmpKey: Long
+ _tmpKey = _stmt.getLong(_itemKeyIndex)
+ if (_map.containsKey(_tmpKey)) {
+ val _item_1: Artist
+ val _tmpArtistId: Long
+ _tmpArtistId = _stmt.getLong(_cursorIndexOfArtistId)
+ _item_1 = Artist(_tmpArtistId)
+ _map.put(_tmpKey, _item_1)
+ }
+ }
+ } finally {
+ _stmt.close()
+ }
+ }
+
+ private fun __fetchRelationshipSongAsSong(_connection: SQLiteConnection,
+ _map: MutableMap<Long, MutableList<Song>>) {
+ val __mapKeySet: Set<Long> = _map.keys
+ if (__mapKeySet.isEmpty()) {
+ return
+ }
+ if (_map.size > 999) {
+ recursiveFetchMap(_map, true) { _tmpMap ->
+ __fetchRelationshipSongAsSong(_connection, _tmpMap)
+ }
+ return
+ }
+ val _stringBuilder: StringBuilder = StringBuilder()
+ _stringBuilder.append("SELECT `songId`,`artistKey` FROM `Song` WHERE `artistKey` IN (")
+ val _inputSize: Int = __mapKeySet.size
+ appendPlaceholders(_stringBuilder, _inputSize)
+ _stringBuilder.append(")")
+ val _sql: String = _stringBuilder.toString()
+ val _stmt: SQLiteStatement = _connection.prepare(_sql)
+ var _argIndex: Int = 1
+ for (_item: Long in __mapKeySet) {
+ _stmt.bindLong(_argIndex, _item)
+ _argIndex++
+ }
+ try {
+ val _itemKeyIndex: Int = getColumnIndex(_stmt, "artistKey")
+ if (_itemKeyIndex == -1) {
+ return
+ }
+ val _cursorIndexOfSongId: Int = 0
+ val _cursorIndexOfArtistKey: Int = 1
+ while (_stmt.step()) {
+ val _tmpKey: Long
+ _tmpKey = _stmt.getLong(_itemKeyIndex)
+ val _tmpRelation: MutableList<Song>? = _map.get(_tmpKey)
+ if (_tmpRelation != null) {
+ val _item_1: Song
+ val _tmpSongId: Long
+ _tmpSongId = _stmt.getLong(_cursorIndexOfSongId)
+ val _tmpArtistKey: Long
+ _tmpArtistKey = _stmt.getLong(_cursorIndexOfArtistKey)
+ _item_1 = Song(_tmpSongId,_tmpArtistKey)
+ _tmpRelation.add(_item_1)
+ }
+ }
+ } finally {
+ _stmt.close()
+ }
+ }
+
+ private fun __fetchRelationshipSongAsSong_1(_connection: SQLiteConnection,
+ _map: MutableMap<Long, MutableList<Song>>) {
+ val __mapKeySet: Set<Long> = _map.keys
+ if (__mapKeySet.isEmpty()) {
+ return
+ }
+ if (_map.size > 999) {
+ recursiveFetchMap(_map, true) { _tmpMap ->
+ __fetchRelationshipSongAsSong_1(_connection, _tmpMap)
+ }
+ return
+ }
+ val _stringBuilder: StringBuilder = StringBuilder()
+ _stringBuilder.append("SELECT `Song`.`songId` AS `songId`,`Song`.`artistKey` AS `artistKey`,_junction.`playlistKey` FROM `PlaylistSongXRef` AS _junction INNER JOIN `Song` ON (_junction.`songKey` = `Song`.`songId`) WHERE _junction.`playlistKey` IN (")
+ val _inputSize: Int = __mapKeySet.size
+ appendPlaceholders(_stringBuilder, _inputSize)
+ _stringBuilder.append(")")
+ val _sql: String = _stringBuilder.toString()
+ val _stmt: SQLiteStatement = _connection.prepare(_sql)
+ var _argIndex: Int = 1
+ for (_item: Long in __mapKeySet) {
+ _stmt.bindLong(_argIndex, _item)
+ _argIndex++
+ }
+ try {
+ // _junction.playlistKey
+ val _itemKeyIndex: Int = 2
+ if (_itemKeyIndex == -1) {
+ return
+ }
+ val _cursorIndexOfSongId: Int = 0
+ val _cursorIndexOfArtistKey: Int = 1
+ while (_stmt.step()) {
+ val _tmpKey: Long
+ _tmpKey = _stmt.getLong(_itemKeyIndex)
+ val _tmpRelation: MutableList<Song>? = _map.get(_tmpKey)
+ if (_tmpRelation != null) {
+ val _item_1: Song
+ val _tmpSongId: Long
+ _tmpSongId = _stmt.getLong(_cursorIndexOfSongId)
+ val _tmpArtistKey: Long
+ _tmpArtistKey = _stmt.getLong(_cursorIndexOfArtistKey)
+ _item_1 = Song(_tmpSongId,_tmpArtistKey)
+ _tmpRelation.add(_item_1)
+ }
+ }
+ } finally {
+ _stmt.close()
+ }
+ }
+
+ public companion object {
+ public fun getRequiredConverters(): List<KClass<*>> = emptyList()
+ }
+}
diff --git a/room/room-paging-guava/src/main/java/androidx/room/paging/guava/LimitOffsetListenableFuturePagingSource.kt b/room/room-paging-guava/src/main/java/androidx/room/paging/guava/LimitOffsetListenableFuturePagingSource.kt
index 0e5309c..2022ec2 100644
--- a/room/room-paging-guava/src/main/java/androidx/room/paging/guava/LimitOffsetListenableFuturePagingSource.kt
+++ b/room/room-paging-guava/src/main/java/androidx/room/paging/guava/LimitOffsetListenableFuturePagingSource.kt
@@ -18,7 +18,6 @@
import android.database.Cursor
import android.os.CancellationSignal
-import androidx.annotation.NonNull
import androidx.annotation.RestrictTo
import androidx.annotation.VisibleForTesting
import androidx.paging.ListenableFuturePagingSource
@@ -26,12 +25,14 @@
import androidx.room.RoomDatabase
import androidx.room.RoomSQLiteQuery
import androidx.room.guava.createListenableFuture
+import androidx.room.paging.CursorSQLiteStatement
import androidx.room.paging.util.INITIAL_ITEM_COUNT
import androidx.room.paging.util.INVALID
import androidx.room.paging.util.ThreadSafeInvalidationObserver
import androidx.room.paging.util.getClippedRefreshKey
import androidx.room.paging.util.queryDatabase
import androidx.room.paging.util.queryItemCount
+import androidx.sqlite.SQLiteStatement
import androidx.sqlite.db.SupportSQLiteQuery
import com.google.common.util.concurrent.Futures
import com.google.common.util.concurrent.ListenableFuture
@@ -160,7 +161,16 @@
)
}
- @NonNull protected abstract fun convertRows(cursor: Cursor): List<Value>
+ protected open fun convertRows(cursor: Cursor): List<Value> {
+ return convertRows(CursorSQLiteStatement(cursor))
+ }
+
+ protected open fun convertRows(statement: SQLiteStatement): List<Value> {
+ throw NotImplementedError(
+ "Unexpected call to a function with no implementation that Room is suppose to " +
+ "generate. Please file a bug at: $BUG_LINK."
+ )
+ }
override val jumpingSupported: Boolean
get() = true
@@ -168,4 +178,9 @@
override fun getRefreshKey(state: PagingState<Int, Value>): Int? {
return state.getClippedRefreshKey()
}
+
+ companion object {
+ const val BUG_LINK =
+ "https://issuetracker.google.com/issues/new?component=413107&template=1096568"
+ }
}
diff --git a/room/room-paging-rxjava2/src/main/java/androidx/room/paging/rxjava2/LimitOffsetRxPagingSource.kt b/room/room-paging-rxjava2/src/main/java/androidx/room/paging/rxjava2/LimitOffsetRxPagingSource.kt
index 7bd8f30..0f4f9e0 100644
--- a/room/room-paging-rxjava2/src/main/java/androidx/room/paging/rxjava2/LimitOffsetRxPagingSource.kt
+++ b/room/room-paging-rxjava2/src/main/java/androidx/room/paging/rxjava2/LimitOffsetRxPagingSource.kt
@@ -25,12 +25,14 @@
import androidx.room.RoomDatabase
import androidx.room.RoomSQLiteQuery
import androidx.room.RxRoom
+import androidx.room.paging.CursorSQLiteStatement
import androidx.room.paging.util.INITIAL_ITEM_COUNT
import androidx.room.paging.util.INVALID
import androidx.room.paging.util.ThreadSafeInvalidationObserver
import androidx.room.paging.util.getClippedRefreshKey
import androidx.room.paging.util.queryDatabase
import androidx.room.paging.util.queryItemCount
+import androidx.sqlite.SQLiteStatement
import androidx.sqlite.db.SupportSQLiteQuery
import io.reactivex.Single
import io.reactivex.schedulers.Schedulers
@@ -100,7 +102,17 @@
return if (invalid) INVALID as LoadResult.Invalid<Int, Value> else result
}
- @NonNull protected abstract fun convertRows(cursor: Cursor): List<Value>
+ @NonNull
+ protected open fun convertRows(cursor: Cursor): List<Value> {
+ return convertRows(CursorSQLiteStatement(cursor))
+ }
+
+ protected open fun convertRows(statement: SQLiteStatement): List<Value> {
+ throw NotImplementedError(
+ "Unexpected call to a function with no implementation that Room is suppose to " +
+ "generate. Please file a bug at: $BUG_LINK."
+ )
+ }
override fun getRefreshKey(state: PagingState<Int, Value>): Int? {
return state.getClippedRefreshKey()
@@ -108,4 +120,9 @@
override val jumpingSupported: Boolean
get() = true
+
+ companion object {
+ const val BUG_LINK =
+ "https://issuetracker.google.com/issues/new?component=413107&template=1096568"
+ }
}
diff --git a/room/room-paging-rxjava3/src/main/java/androidx/room/paging/rxjava3/LimitOffsetRxPagingSource.kt b/room/room-paging-rxjava3/src/main/java/androidx/room/paging/rxjava3/LimitOffsetRxPagingSource.kt
index a2792a0..b602e0f 100644
--- a/room/room-paging-rxjava3/src/main/java/androidx/room/paging/rxjava3/LimitOffsetRxPagingSource.kt
+++ b/room/room-paging-rxjava3/src/main/java/androidx/room/paging/rxjava3/LimitOffsetRxPagingSource.kt
@@ -17,13 +17,13 @@
package androidx.room.paging.rxjava3
import android.database.Cursor
-import androidx.annotation.NonNull
import androidx.annotation.RestrictTo
import androidx.annotation.VisibleForTesting
import androidx.paging.PagingState
import androidx.paging.rxjava3.RxPagingSource
import androidx.room.RoomDatabase
import androidx.room.RoomSQLiteQuery
+import androidx.room.paging.CursorSQLiteStatement
import androidx.room.paging.util.INITIAL_ITEM_COUNT
import androidx.room.paging.util.INVALID
import androidx.room.paging.util.ThreadSafeInvalidationObserver
@@ -31,6 +31,7 @@
import androidx.room.paging.util.queryDatabase
import androidx.room.paging.util.queryItemCount
import androidx.room.rxjava3.createSingle
+import androidx.sqlite.SQLiteStatement
import androidx.sqlite.db.SupportSQLiteQuery
import io.reactivex.rxjava3.core.Single
import io.reactivex.rxjava3.schedulers.Schedulers
@@ -100,7 +101,16 @@
return if (invalid) INVALID as LoadResult.Invalid<Int, Value> else result
}
- @NonNull protected abstract fun convertRows(cursor: Cursor): List<Value>
+ protected open fun convertRows(cursor: Cursor): List<Value> {
+ return convertRows(CursorSQLiteStatement(cursor))
+ }
+
+ protected open fun convertRows(statement: SQLiteStatement): List<Value> {
+ throw NotImplementedError(
+ "Unexpected call to a function with no implementation that Room is suppose to " +
+ "generate. Please file a bug at: $BUG_LINK."
+ )
+ }
override fun getRefreshKey(state: PagingState<Int, Value>): Int? {
return state.getClippedRefreshKey()
@@ -108,4 +118,9 @@
override val jumpingSupported: Boolean
get() = true
+
+ companion object {
+ const val BUG_LINK =
+ "https://issuetracker.google.com/issues/new?component=413107&template=1096568"
+ }
}
diff --git a/room/room-paging/src/androidMain/kotlin/androidx/room/paging/CursorSQLiteStatement.android.kt b/room/room-paging/src/androidMain/kotlin/androidx/room/paging/SQLiteStatementCursor.android.kt
similarity index 94%
rename from room/room-paging/src/androidMain/kotlin/androidx/room/paging/CursorSQLiteStatement.android.kt
rename to room/room-paging/src/androidMain/kotlin/androidx/room/paging/SQLiteStatementCursor.android.kt
index d9389a0..1fe7955 100644
--- a/room/room-paging/src/androidMain/kotlin/androidx/room/paging/CursorSQLiteStatement.android.kt
+++ b/room/room-paging/src/androidMain/kotlin/androidx/room/paging/SQLiteStatementCursor.android.kt
@@ -19,7 +19,7 @@
import android.database.AbstractCursor
import androidx.sqlite.SQLiteStatement
-/** Wrapper class for backwards compatibility in room-paging. */
+/** Cursor backed by a SQLiteStatement used for backwards compatibility of Paging APIs. */
internal class SQLiteStatementCursor(
private val statement: SQLiteStatement,
private val rowCount: Int
diff --git a/room/room-runtime/api/restricted_current.txt b/room/room-runtime/api/restricted_current.txt
index a63957d..df44066 100644
--- a/room/room-runtime/api/restricted_current.txt
+++ b/room/room-runtime/api/restricted_current.txt
@@ -485,7 +485,8 @@
ctor protected LimitOffsetDataSource(androidx.room.RoomDatabase, androidx.room.RoomSQLiteQuery, boolean, java.lang.String!...);
ctor protected LimitOffsetDataSource(androidx.room.RoomDatabase, androidx.sqlite.db.SupportSQLiteQuery, boolean, boolean, java.lang.String!...);
ctor protected LimitOffsetDataSource(androidx.room.RoomDatabase, androidx.sqlite.db.SupportSQLiteQuery, boolean, java.lang.String!...);
- method protected abstract java.util.List<T!> convertRows(android.database.Cursor);
+ method protected java.util.List<T!> convertRows(android.database.Cursor);
+ method protected java.util.List<T!> convertRows(androidx.sqlite.SQLiteStatement);
method public void loadInitial(androidx.paging.PositionalDataSource.LoadInitialParams, androidx.paging.PositionalDataSource.LoadInitialCallback<T!>);
method public void loadRange(androidx.paging.PositionalDataSource.LoadRangeParams, androidx.paging.PositionalDataSource.LoadRangeCallback<T!>);
}
@@ -521,6 +522,7 @@
method @Deprecated @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public static android.database.Cursor query(androidx.room.RoomDatabase db, androidx.sqlite.db.SupportSQLiteQuery sqLiteQuery, boolean maybeCopy);
method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public static android.database.Cursor query(androidx.room.RoomDatabase db, androidx.sqlite.db.SupportSQLiteQuery sqLiteQuery, boolean maybeCopy, android.os.CancellationSignal? signal);
method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) @kotlin.jvm.Throws(exceptionClasses=IOException::class) public static int readVersion(java.io.File databaseFile) throws java.io.IOException;
+ method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public static androidx.sqlite.SQLiteConnection toSQLiteConnection(androidx.sqlite.db.SupportSQLiteDatabase db);
}
@RestrictTo({androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX}) public final class FileUtil {
diff --git a/room/room-runtime/src/androidMain/java/androidx/room/paging/CursorSQLiteStatement.android.kt b/room/room-runtime/src/androidMain/java/androidx/room/paging/CursorSQLiteStatement.android.kt
new file mode 100644
index 0000000..75b740c
--- /dev/null
+++ b/room/room-runtime/src/androidMain/java/androidx/room/paging/CursorSQLiteStatement.android.kt
@@ -0,0 +1,93 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.room.paging
+
+import android.database.Cursor
+import android.database.Cursor.FIELD_TYPE_BLOB
+import android.database.Cursor.FIELD_TYPE_FLOAT
+import android.database.Cursor.FIELD_TYPE_INTEGER
+import android.database.Cursor.FIELD_TYPE_NULL
+import android.database.Cursor.FIELD_TYPE_STRING
+import androidx.annotation.RestrictTo
+import androidx.sqlite.SQLITE_DATA_BLOB
+import androidx.sqlite.SQLITE_DATA_FLOAT
+import androidx.sqlite.SQLITE_DATA_INTEGER
+import androidx.sqlite.SQLITE_DATA_NULL
+import androidx.sqlite.SQLITE_DATA_TEXT
+import androidx.sqlite.SQLiteStatement
+
+/** SQLiteStatement backed by a Cursor used for backwards compatibility of Paging2 APIs. */
+@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+class CursorSQLiteStatement(private val cursor: Cursor) : SQLiteStatement {
+ override fun getBlob(index: Int): ByteArray = cursor.getBlob(index)
+
+ override fun getDouble(index: Int): Double = cursor.getDouble(index)
+
+ override fun getLong(index: Int): Long = cursor.getLong(index)
+
+ override fun getText(index: Int): String = cursor.getString(index)
+
+ override fun isNull(index: Int): Boolean = cursor.isNull(index)
+
+ override fun getColumnCount(): Int = cursor.columnCount
+
+ override fun getColumnName(index: Int): String = cursor.getColumnName(index)
+
+ override fun getColumnType(index: Int): Int = cursor.getDataType(index)
+
+ override fun step(): Boolean = cursor.moveToNext()
+
+ override fun reset() {
+ cursor.moveToPosition(-1)
+ }
+
+ override fun close() {
+ cursor.close()
+ }
+
+ override fun bindBlob(index: Int, value: ByteArray) =
+ error("Only get*() calls are allowed on a Cursor backed SQLiteStatement")
+
+ override fun bindDouble(index: Int, value: Double) =
+ error("Only get*() calls are allowed on a Cursor backed SQLiteStatement")
+
+ override fun bindLong(index: Int, value: Long) =
+ error("Only get*() calls are allowed on a Cursor backed SQLiteStatement")
+
+ override fun bindText(index: Int, value: String) =
+ error("Only get*() calls are allowed on a Cursor backed SQLiteStatement")
+
+ override fun bindNull(index: Int) =
+ error("Only get*() calls are allowed on a Cursor backed SQLiteStatement")
+
+ override fun clearBindings() =
+ error("Only get*() calls are allowed on a Cursor backed SQLiteStatement")
+
+ companion object {
+ private fun Cursor.getDataType(index: Int): Int {
+ val fieldType = this.getType(index)
+ return when (this.getType(index)) {
+ FIELD_TYPE_NULL -> SQLITE_DATA_NULL
+ FIELD_TYPE_INTEGER -> SQLITE_DATA_INTEGER
+ FIELD_TYPE_FLOAT -> SQLITE_DATA_FLOAT
+ FIELD_TYPE_STRING -> SQLITE_DATA_TEXT
+ FIELD_TYPE_BLOB -> SQLITE_DATA_BLOB
+ else -> error("Unknown field type: $fieldType")
+ }
+ }
+ }
+}
diff --git a/room/room-runtime/src/androidMain/java/androidx/room/paging/LimitOffsetDataSource.java b/room/room-runtime/src/androidMain/java/androidx/room/paging/LimitOffsetDataSource.java
index 2b5c391..3cdc6c0 100644
--- a/room/room-runtime/src/androidMain/java/androidx/room/paging/LimitOffsetDataSource.java
+++ b/room/room-runtime/src/androidMain/java/androidx/room/paging/LimitOffsetDataSource.java
@@ -23,6 +23,7 @@
import androidx.room.InvalidationTracker;
import androidx.room.RoomDatabase;
import androidx.room.RoomSQLiteQuery;
+import androidx.sqlite.SQLiteStatement;
import androidx.sqlite.db.SupportSQLiteQuery;
import java.util.Collections;
@@ -145,7 +146,21 @@
@NonNull
@SuppressWarnings("WeakerAccess")
- protected abstract List<T> convertRows(@NonNull Cursor cursor);
+ protected List<T> convertRows(@NonNull Cursor cursor) {
+ return convertRows(new CursorSQLiteStatement(cursor));
+ }
+
+ @NonNull
+ protected List<T> convertRows(@NonNull SQLiteStatement statement) {
+ throw new UnsupportedOperationException(
+ "Unexpected call to a function with no implementation that Room is supposed to "
+ + "generate. Please file a bug at: "
+ + BUG_LINK + "."
+ );
+ }
+
+ private static final String BUG_LINK =
+ "https://issuetracker.google.com/issues/new?component=413107&template=1096568";
@SuppressWarnings("deprecation")
@Override
diff --git a/room/room-runtime/src/androidMain/kotlin/androidx/room/util/DBUtil.android.kt b/room/room-runtime/src/androidMain/kotlin/androidx/room/util/DBUtil.android.kt
index 8cb5d70..7faeb6a 100644
--- a/room/room-runtime/src/androidMain/kotlin/androidx/room/util/DBUtil.android.kt
+++ b/room/room-runtime/src/androidMain/kotlin/androidx/room/util/DBUtil.android.kt
@@ -234,3 +234,8 @@
fun createCancellationSignal(): CancellationSignal {
return CancellationSignal()
}
+
+@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP_PREFIX)
+fun toSQLiteConnection(db: SupportSQLiteDatabase): SQLiteConnection {
+ return SupportSQLiteConnection(db)
+}