Merge changes from topic "index_order" into androidx-main
* changes:
Use enums for @Index#Order API
Workaround KSP enum annotation value inconsistency.
Adding support for column index order in @Index
diff --git a/room/room-common/api/current.txt b/room/room-common/api/current.txt
index 3ad7e2a..89127f9 100644
--- a/room/room-common/api/current.txt
+++ b/room/room-common/api/current.txt
@@ -139,10 +139,16 @@
@java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.CLASS) @java.lang.annotation.Target({}) public @interface Index {
method public abstract String name() default "";
+ method public abstract androidx.room.Index.Order[] orders() default {};
method public abstract boolean unique() default false;
method public abstract String[] value();
}
+ public enum Index.Order {
+ enum_constant public static final androidx.room.Index.Order ASC;
+ enum_constant public static final androidx.room.Index.Order DESC;
+ }
+
@java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.CLASS) @java.lang.annotation.Target({java.lang.annotation.ElementType.METHOD}) public @interface Insert {
method public abstract Class<?> entity() default java.lang.Object.class;
method @androidx.room.OnConflictStrategy public abstract int onConflict() default androidx.room.OnConflictStrategy.ABORT;
diff --git a/room/room-common/api/public_plus_experimental_current.txt b/room/room-common/api/public_plus_experimental_current.txt
index 3ad7e2a..89127f9 100644
--- a/room/room-common/api/public_plus_experimental_current.txt
+++ b/room/room-common/api/public_plus_experimental_current.txt
@@ -139,10 +139,16 @@
@java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.CLASS) @java.lang.annotation.Target({}) public @interface Index {
method public abstract String name() default "";
+ method public abstract androidx.room.Index.Order[] orders() default {};
method public abstract boolean unique() default false;
method public abstract String[] value();
}
+ public enum Index.Order {
+ enum_constant public static final androidx.room.Index.Order ASC;
+ enum_constant public static final androidx.room.Index.Order DESC;
+ }
+
@java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.CLASS) @java.lang.annotation.Target({java.lang.annotation.ElementType.METHOD}) public @interface Insert {
method public abstract Class<?> entity() default java.lang.Object.class;
method @androidx.room.OnConflictStrategy public abstract int onConflict() default androidx.room.OnConflictStrategy.ABORT;
diff --git a/room/room-common/api/restricted_current.txt b/room/room-common/api/restricted_current.txt
index 722a507..dbdc6af 100644
--- a/room/room-common/api/restricted_current.txt
+++ b/room/room-common/api/restricted_current.txt
@@ -139,10 +139,16 @@
@java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.CLASS) @java.lang.annotation.Target({}) public @interface Index {
method public abstract String name() default "";
+ method public abstract androidx.room.Index.Order[] orders() default {};
method public abstract boolean unique() default false;
method public abstract String[] value();
}
+ public enum Index.Order {
+ enum_constant public static final androidx.room.Index.Order ASC;
+ enum_constant public static final androidx.room.Index.Order DESC;
+ }
+
@java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.CLASS) @java.lang.annotation.Target({java.lang.annotation.ElementType.METHOD}) public @interface Insert {
method public abstract Class<?> entity() default java.lang.Object.class;
method @androidx.room.OnConflictStrategy public abstract int onConflict() default androidx.room.OnConflictStrategy.ABORT;
diff --git a/room/room-common/src/main/java/androidx/room/Index.java b/room/room-common/src/main/java/androidx/room/Index.java
index 47242b3..dc9206a 100644
--- a/room/room-common/src/main/java/androidx/room/Index.java
+++ b/room/room-common/src/main/java/androidx/room/Index.java
@@ -55,6 +55,31 @@
String[] value();
/**
+ * List of column sort orders in the Index.
+ * <p>
+ * The number of entries in the array should be equal to size of columns in {@link #value()}.
+ * <p>
+ * The default order of all columns in the index is {@link Order#ASC}.
+ * <p>
+ * Note that there is no value in providing a sort order on a single-column index. Column sort
+ * order of an index are relevant on multi-column indices and specifically in those that are
+ * considered 'covering indices', for such indices specifying an order can have performance
+ * improvements on queries containing ORDER BY clauses.SchemaDifferTest See
+ * <a href="https://www.sqlite.org/queryplanner.html#_sorting_by_index">SQLite documentation</a>
+ * for details on sorting by index and the usage of the sort order by the query optimizer.
+ * <p>
+ * As an example, consider a table called 'Song' with two columns, 'name' and 'length'. If a
+ * covering index is created for it: <code>CREATE INDEX `song_name_length` on `Song`
+ * (`name` ASC, `length` DESC)</code>, then a query containing an ORDER BY clause with matching
+ * order of the index will be able to avoid a table scan by using the index, but a mismatch in
+ * order won't. Therefore the columns order of the index should be the same as the most
+ * frequently executed query with sort order.
+ *
+ * @return The list of column sort orders in the Index.
+ */
+ Order[] orders() default {};
+
+ /**
* Name of the index. If not set, Room will set it to the list of columns joined by '_' and
* prefixed by "index_${tableName}". So if you have a table with name "Foo" and with an index
* of {"bar", "baz"}, generated index name will be "index_Foo_bar_baz". If you need to specify
@@ -71,4 +96,20 @@
* @return True if index is unique. False by default.
*/
boolean unique() default false;
+
+ enum Order {
+ /**
+ * Ascending returning order.
+ *
+ * @see Index#orders()
+ */
+ ASC,
+
+ /**
+ * Descending returning order.
+ *
+ * @see Index#orders()
+ */
+ DESC
+ }
}
diff --git a/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/KspAnnotationBox.kt b/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/KspAnnotationBox.kt
index dc10afa..72f786e 100644
--- a/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/KspAnnotationBox.kt
+++ b/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/KspAnnotationBox.kt
@@ -169,6 +169,12 @@
}
private fun <R> Any.readAsEnum(enumClass: Class<R>): R? {
+ // TODO: https://github.com/google/ksp/issues/429
+ // If the enum value is from compiled code KSP gives us the actual value an not the KSType,
+ // so return it instead of using valueOf() to get an instance of the entry.
+ if (enumClass.isAssignableFrom(this::class.java)) {
+ return enumClass.cast(this)
+ }
val ksType = this as? KSType ?: return null
val classDeclaration = ksType.declaration as? KSClassDeclaration ?: return null
val enumValue = classDeclaration.simpleName.asString()
diff --git a/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/KspAnnotationValue.kt b/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/KspAnnotationValue.kt
index 71c0519..7fd29c2 100644
--- a/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/KspAnnotationValue.kt
+++ b/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/KspAnnotationValue.kt
@@ -17,6 +17,7 @@
package androidx.room.compiler.processing.ksp
import androidx.room.compiler.processing.XAnnotationValue
+import com.google.devtools.ksp.getClassDeclarationByName
import com.google.devtools.ksp.symbol.ClassKind
import com.google.devtools.ksp.symbol.KSAnnotation
import com.google.devtools.ksp.symbol.KSClassDeclaration
@@ -56,6 +57,20 @@
// with, and using "Any" prevents the array from being cast to the correct
// type later on.
is List<*> -> value.map { unwrap(it) }
+ // TODO: https://github.com/google/ksp/issues/429
+ // If the enum value is from compiled code KSP gives us the actual value an not
+ // the KSType, so we wrap it as KspEnumEntry for consistency.
+ is Enum<*> -> {
+ val declaration =
+ env.resolver.getClassDeclarationByName(value::class.java.canonicalName)
+ ?: error("Cannot find KSClassDeclaration for Enum '$value'.")
+ val valueDeclaration = declaration.declarations
+ .filterIsInstance<KSClassDeclaration>()
+ .filter { it.classKind == ClassKind.ENUM_ENTRY }
+ .firstOrNull() { it.simpleName.getShortName() == value.name }
+ ?: error("Cannot find ENUM_ENTRY '$value' in '$declaration'.")
+ KspEnumEntry.create(env, valueDeclaration)
+ }
else -> value
}
}
diff --git a/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/XAnnotationBoxTest.kt b/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/XAnnotationBoxTest.kt
index d7ce271..9518163 100644
--- a/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/XAnnotationBoxTest.kt
+++ b/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/XAnnotationBoxTest.kt
@@ -17,6 +17,8 @@
package androidx.room.compiler.processing
import androidx.room.compiler.processing.testcode.JavaAnnotationWithDefaults
+import androidx.room.compiler.processing.testcode.JavaAnnotationWithEnum
+import androidx.room.compiler.processing.testcode.JavaAnnotationWithEnumArray
import androidx.room.compiler.processing.testcode.JavaAnnotationWithPrimitiveArray
import androidx.room.compiler.processing.testcode.JavaAnnotationWithTypeReferences
import androidx.room.compiler.processing.testcode.JavaEnum
@@ -512,6 +514,86 @@
}
@Test
+ fun javaEnum() {
+ val javaSrc = Source.java(
+ "JavaSubject.java",
+ """
+ import androidx.room.compiler.processing.testcode.*;
+ class JavaSubject {
+ @JavaAnnotationWithEnum(JavaEnum.VAL1)
+ Object annotated1;
+ }
+ """.trimIndent()
+ )
+ val kotlinSrc = Source.kotlin(
+ "KotlinSubject.kt",
+ """
+ import androidx.room.compiler.processing.testcode.*;
+ class KotlinSubject {
+ @JavaAnnotationWithEnum(JavaEnum.VAL1)
+ val annotated1: Any = TODO()
+ }
+ """.trimIndent()
+ )
+ runTest(
+ sources = listOf(javaSrc, kotlinSrc)
+ ) { invocation ->
+ arrayOf("JavaSubject", "KotlinSubject").map {
+ invocation.processingEnv.requireTypeElement(it)
+ }.forEach { subject ->
+ val annotation = subject.getField("annotated1").getAnnotation(
+ JavaAnnotationWithEnum::class
+ )
+ assertThat(
+ annotation?.value?.value
+ ).isEqualTo(
+ JavaEnum.VAL1
+ )
+ }
+ }
+ }
+
+ @Test
+ fun javaEnumArray() {
+ val javaSrc = Source.java(
+ "JavaSubject.java",
+ """
+ import androidx.room.compiler.processing.testcode.*;
+ class JavaSubject {
+ @JavaAnnotationWithEnumArray(enumArray = {JavaEnum.VAL1, JavaEnum.VAL2})
+ Object annotated1;
+ }
+ """.trimIndent()
+ )
+ val kotlinSrc = Source.kotlin(
+ "KotlinSubject.kt",
+ """
+ import androidx.room.compiler.processing.testcode.*;
+ class KotlinSubject {
+ @JavaAnnotationWithEnumArray(enumArray = [JavaEnum.VAL1, JavaEnum.VAL2])
+ val annotated1:Any = TODO()
+ }
+ """.trimIndent()
+ )
+ runTest(
+ sources = listOf(javaSrc, kotlinSrc)
+ ) { invocation ->
+ arrayOf("JavaSubject", "KotlinSubject").map {
+ invocation.processingEnv.requireTypeElement(it)
+ }.forEach { subject ->
+ val annotation = subject.getField("annotated1").getAnnotation(
+ JavaAnnotationWithEnumArray::class
+ )
+ assertThat(
+ annotation?.value?.enumArray
+ ).isEqualTo(
+ arrayOf(JavaEnum.VAL1, JavaEnum.VAL2)
+ )
+ }
+ }
+ }
+
+ @Test
fun javaRepeatableAnnotation() {
val javaSrc = Source.java(
"JavaSubject",
diff --git a/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/XAnnotationTest.kt b/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/XAnnotationTest.kt
index 05526ea..f7b5a10 100644
--- a/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/XAnnotationTest.kt
+++ b/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/XAnnotationTest.kt
@@ -17,6 +17,8 @@
package androidx.room.compiler.processing
import androidx.room.compiler.processing.testcode.JavaAnnotationWithDefaults
+import androidx.room.compiler.processing.testcode.JavaAnnotationWithEnum
+import androidx.room.compiler.processing.testcode.JavaAnnotationWithEnumArray
import androidx.room.compiler.processing.testcode.JavaAnnotationWithPrimitiveArray
import androidx.room.compiler.processing.testcode.JavaAnnotationWithTypeReferences
import androidx.room.compiler.processing.testcode.JavaEnum
@@ -587,6 +589,84 @@
}
@Test
+ fun javaEnum() {
+ val javaSrc = Source.java(
+ "JavaSubject.java",
+ """
+ import androidx.room.compiler.processing.testcode.*;
+ class JavaSubject {
+ @JavaAnnotationWithEnum(JavaEnum.VAL1)
+ Object annotated1;
+ }
+ """.trimIndent()
+ )
+ val kotlinSrc = Source.kotlin(
+ "KotlinSubject.kt",
+ """
+ import androidx.room.compiler.processing.testcode.*;
+ class KotlinSubject {
+ @JavaAnnotationWithEnum(JavaEnum.VAL1)
+ val annotated1: Any = TODO()
+ }
+ """.trimIndent()
+ )
+ runTest(
+ sources = listOf(javaSrc, kotlinSrc)
+ ) { invocation ->
+ listOf("JavaSubject", "KotlinSubject").map {
+ invocation.processingEnv.requireTypeElement(it)
+ }.forEach { subject ->
+ val annotation = subject.getField("annotated1")
+ .requireAnnotation<JavaAnnotationWithEnum>()
+ assertThat(
+ annotation.getAsEnum("value").name
+ ).isEqualTo(
+ JavaEnum.VAL1.name
+ )
+ }
+ }
+ }
+
+ @Test
+ fun javaEnumArray() {
+ val javaSrc = Source.java(
+ "JavaSubject.java",
+ """
+ import androidx.room.compiler.processing.testcode.*;
+ class JavaSubject {
+ @JavaAnnotationWithEnumArray(enumArray = {JavaEnum.VAL1, JavaEnum.VAL2})
+ Object annotated1;
+ }
+ """.trimIndent()
+ )
+ val kotlinSrc = Source.kotlin(
+ "KotlinSubject.kt",
+ """
+ import androidx.room.compiler.processing.testcode.*;
+ class KotlinSubject {
+ @JavaAnnotationWithEnumArray(enumArray = [JavaEnum.VAL1, JavaEnum.VAL2])
+ val annotated1: Any = TODO()
+ }
+ """.trimIndent()
+ )
+ runTest(
+ sources = listOf(javaSrc, kotlinSrc)
+ ) { invocation ->
+ listOf("JavaSubject", "KotlinSubject").map {
+ invocation.processingEnv.requireTypeElement(it)
+ }.forEach { subject ->
+ val annotation = subject.getField("annotated1")
+ .requireAnnotation<JavaAnnotationWithEnumArray>()
+ assertThat(
+ annotation.getAsEnumList("enumArray").map { it.name }
+ ).isEqualTo(
+ listOf(JavaEnum.VAL1.name, JavaEnum.VAL2.name)
+ )
+ }
+ }
+ }
+
+ @Test
fun javaRepeatableAnnotation() {
val javaSrc = Source.java(
"JavaSubject",
diff --git a/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/testcode/JavaAnnotationWithEnum.java b/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/testcode/JavaAnnotationWithEnum.java
new file mode 100644
index 0000000..f074db1
--- /dev/null
+++ b/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/testcode/JavaAnnotationWithEnum.java
@@ -0,0 +1,26 @@
+/*
+ * Copyright 2021 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.compiler.processing.testcode;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Target;
+
+@Target(ElementType.FIELD)
+public @interface JavaAnnotationWithEnum {
+ JavaEnum value() default JavaEnum.DEFAULT;
+}
+
diff --git a/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/testcode/JavaAnnotationWithEnumArray.java b/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/testcode/JavaAnnotationWithEnumArray.java
new file mode 100644
index 0000000..80b70b6
--- /dev/null
+++ b/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/testcode/JavaAnnotationWithEnumArray.java
@@ -0,0 +1,26 @@
+/*
+ * Copyright 2021 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.compiler.processing.testcode;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Target;
+
+@Target(ElementType.FIELD)
+public @interface JavaAnnotationWithEnumArray {
+ JavaEnum[] enumArray() default {};
+}
+
diff --git a/room/room-compiler/src/main/kotlin/androidx/room/processor/EntityProcessor.kt b/room/room-compiler/src/main/kotlin/androidx/room/processor/EntityProcessor.kt
index 9a9998e..9ff4211 100644
--- a/room/room-compiler/src/main/kotlin/androidx/room/processor/EntityProcessor.kt
+++ b/room/room-compiler/src/main/kotlin/androidx/room/processor/EntityProcessor.kt
@@ -45,12 +45,14 @@
return annotation.getAsAnnotationBoxArray<androidx.room.Index>("indices").map {
val indexAnnotation = it.value
val nameValue = indexAnnotation.name
+ val columns = indexAnnotation.value.asList()
+ val orders = indexAnnotation.orders.asList()
val name = if (nameValue == "") {
- createIndexName(indexAnnotation.value.asList(), tableName)
+ createIndexName(columns, tableName)
} else {
nameValue
}
- IndexInput(name, indexAnnotation.unique, indexAnnotation.value.asList())
+ IndexInput(name, indexAnnotation.unique, columns, orders)
}
}
@@ -83,7 +85,12 @@
/**
* Processed Index annotation output.
*/
-data class IndexInput(val name: String, val unique: Boolean, val columnNames: List<String>)
+data class IndexInput(
+ val name: String,
+ val unique: Boolean,
+ val columnNames: List<String>,
+ val orders: List<androidx.room.Index.Order>
+)
/**
* ForeignKey, before it is processed in the context of a 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 656b9e8..9638dac 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
@@ -66,6 +66,8 @@
" autoGenerate, its type must be int, Integer, long or Long."
val AUTO_INCREMENT_EMBEDDED_HAS_MULTIPLE_FIELDS = "When @PrimaryKey annotation is used on a" +
" field annotated with @Embedded, the embedded class should have only 1 field."
+ val INVALID_INDEX_ORDERS_SIZE = "The number of entries in @Index#orders() should be " +
+ "equal to the amount of columns defined in the @Index value."
val DO_NOT_USE_GENERIC_IMMUTABLE_MULTIMAP = "Do not use ImmutableMultimap as a type (as with" +
" Multimap itself). Instead use the subtypes such as ImmutableSetMultimap or " +
diff --git a/room/room-compiler/src/main/kotlin/androidx/room/processor/TableEntityProcessor.kt b/room/room-compiler/src/main/kotlin/androidx/room/processor/TableEntityProcessor.kt
index 030e56f..73f549f 100644
--- a/room/room-compiler/src/main/kotlin/androidx/room/processor/TableEntityProcessor.kt
+++ b/room/room-compiler/src/main/kotlin/androidx/room/processor/TableEntityProcessor.kt
@@ -26,6 +26,7 @@
import androidx.room.processor.EntityProcessor.Companion.extractIndices
import androidx.room.processor.EntityProcessor.Companion.extractTableName
import androidx.room.processor.ProcessorErrors.INDEX_COLUMNS_CANNOT_BE_EMPTY
+import androidx.room.processor.ProcessorErrors.INVALID_INDEX_ORDERS_SIZE
import androidx.room.processor.ProcessorErrors.RELATION_IN_ENTITY
import androidx.room.processor.cache.Cache
import androidx.room.vo.EmbeddedField
@@ -117,7 +118,8 @@
IndexInput(
name = createIndexName(listOf(it.columnName), tableName),
unique = false,
- columnNames = listOf(it.columnName)
+ columnNames = listOf(it.columnName),
+ orders = emptyList()
)
}
}
@@ -476,10 +478,21 @@
)
field
}
+ if (input.orders.isNotEmpty()) {
+ context.checker.check(
+ input.columnNames.size == input.orders.size, element,
+ INVALID_INDEX_ORDERS_SIZE
+ )
+ }
if (fields.isEmpty()) {
null
} else {
- Index(name = input.name, unique = input.unique, fields = fields)
+ Index(
+ name = input.name,
+ unique = input.unique,
+ fields = fields,
+ orders = input.orders
+ )
}
}
@@ -538,7 +551,8 @@
IndexInput(
name = createIndexName(it.columnNames, tableName),
unique = it.unique,
- columnNames = it.columnNames
+ columnNames = it.columnNames,
+ orders = it.orders
)
}
} else {
diff --git a/room/room-compiler/src/main/kotlin/androidx/room/vo/Index.kt b/room/room-compiler/src/main/kotlin/androidx/room/vo/Index.kt
index 88a8c56..81a703f 100644
--- a/room/room-compiler/src/main/kotlin/androidx/room/vo/Index.kt
+++ b/room/room-compiler/src/main/kotlin/androidx/room/vo/Index.kt
@@ -19,20 +19,36 @@
import androidx.room.migration.bundle.BundleUtil
import androidx.room.migration.bundle.IndexBundle
+private typealias IndexOrder = androidx.room.Index.Order
+
/**
* Represents a processed index.
*/
-data class Index(val name: String, val unique: Boolean, override val fields: Fields) :
- HasSchemaIdentity, HasFields {
+data class Index(
+ val name: String,
+ val unique: Boolean,
+ override val fields: Fields,
+ val orders: List<IndexOrder>
+) : HasSchemaIdentity, HasFields {
companion object {
// should match the value in TableInfo.Index.DEFAULT_PREFIX
const val DEFAULT_PREFIX = "index_"
}
- constructor(name: String, unique: Boolean, fields: List<Field>) :
- this(name, unique, Fields(fields))
+ constructor(
+ name: String,
+ unique: Boolean,
+ fields: List<Field>,
+ orders: List<IndexOrder>
+ ) : this(name, unique, Fields(fields), orders)
- override fun getIdKey() = "$unique-$name-${columnNames.joinToString(",")}"
+ override fun getIdKey() = buildString {
+ append("$unique-$name-${columnNames.joinToString(",")}")
+ // orders was newly added; it should affect the ID only when declared.
+ if (orders.isNotEmpty()) {
+ append("-${orders.joinToString(",")}")
+ }
+ }
fun createQuery(tableName: String): String {
val indexSQL = if (unique) {
@@ -40,14 +56,21 @@
} else {
"INDEX"
}
+
+ val columns = if (orders.isNotEmpty()) {
+ columnNames.mapIndexed { index, columnName -> "`$columnName` ${orders[index]}" }
+ } else {
+ columnNames.map { "`$it`" }
+ }.joinToString(", ")
+
return """
CREATE $indexSQL IF NOT EXISTS `$name`
- ON `$tableName` (${columnNames.joinToString(", ") { "`$it`" }})
+ ON `$tableName` ($columns)
""".trimIndent().replace("\n", " ")
}
fun toBundle(): IndexBundle = IndexBundle(
- name, unique, columnNames,
+ name, unique, columnNames, orders.map { it.name },
createQuery(BundleUtil.TABLE_NAME_PLACEHOLDER)
)
}
diff --git a/room/room-compiler/src/main/kotlin/androidx/room/writer/TableInfoValidationWriter.kt b/room/room-compiler/src/main/kotlin/androidx/room/writer/TableInfoValidationWriter.kt
index 8916275..1edc663 100644
--- a/room/room-compiler/src/main/kotlin/androidx/room/writer/TableInfoValidationWriter.kt
+++ b/room/room-compiler/src/main/kotlin/androidx/room/writer/TableInfoValidationWriter.kt
@@ -108,14 +108,21 @@
)
entity.indices.forEach { index ->
val columnNames = index.columnNames.joinToString(",") { "\"$it\"" }
+ val orders = if (index.orders.isEmpty()) {
+ index.columnNames.map { "ASC" }.joinToString(",") { "\"$it\"" }
+ } else {
+ index.orders.joinToString(",") { "\"$it\"" }
+ }
addStatement(
- "$L.add(new $T($S, $L, $T.asList($L)))",
+ "$L.add(new $T($S, $L, $T.asList($L), $T.asList($L)))",
indicesSetVar,
RoomTypeNames.TABLE_INFO_INDEX,
index.name,
index.unique,
Arrays::class.typeName,
- columnNames
+ columnNames,
+ Arrays::class.typeName,
+ orders,
)
}
diff --git a/room/room-compiler/src/test/kotlin/androidx/room/processor/TableEntityProcessorTest.kt b/room/room-compiler/src/test/kotlin/androidx/room/processor/TableEntityProcessorTest.kt
index a880cb0..66b9271 100644
--- a/room/room-compiler/src/test/kotlin/androidx/room/processor/TableEntityProcessorTest.kt
+++ b/room/room-compiler/src/test/kotlin/androidx/room/processor/TableEntityProcessorTest.kt
@@ -41,6 +41,8 @@
import org.junit.runner.RunWith
import org.junit.runners.JUnit4
+private typealias IndexOrder = androidx.room.Index.Order
+
@RunWith(JUnit4::class)
class TableEntityProcessorTest : BaseEntityParserTest() {
@Test
@@ -171,6 +173,85 @@
}
@Test
+ fun index_sort_desc() {
+ val annotation = mapOf(
+ "indices" to """@Index(value = {"foo"}, orders = {Index.Order.DESC})"""
+ )
+ singleEntity(
+ """
+ @PrimaryKey
+ public int id;
+ public String foo;
+ """,
+ annotation
+ ) { entity, _ ->
+ assertThat(
+ entity.indices,
+ `is`(
+ listOf(
+ Index(
+ name = "index_MyEntity_foo",
+ unique = false,
+ fields = fieldsByName(entity, "foo"),
+ orders = listOf(IndexOrder.DESC)
+ )
+ )
+ )
+ )
+ }
+ }
+
+ @Test
+ fun index_sort_multiple() {
+ val annotation = mapOf(
+ "tableName" to "\"MyTable\"",
+ "indices" to
+ """@Index(value = {"foo", "id"}, orders = {Index.Order.DESC, Index.Order.ASC})"""
+ )
+ singleEntity(
+ """
+ @PrimaryKey
+ public int id;
+ public String foo;
+ """,
+ annotation
+ ) { entity, _ ->
+ assertThat(
+ entity.indices,
+ `is`(
+ listOf(
+ Index(
+ name = "index_MyTable_foo_id",
+ unique = false,
+ fields = fieldsByName(entity, "foo", "id"),
+ orders = listOf(IndexOrder.DESC, IndexOrder.ASC)
+ )
+ )
+ )
+ )
+ }
+ }
+
+ @Test
+ fun index_invalidOrdersSize() {
+ val annotation = mapOf(
+ "indices" to """@Index(value = {"foo", "id"}, orders = {Index.Order.DESC})"""
+ )
+ singleEntity(
+ """
+ @PrimaryKey
+ public int id;
+ public String foo;
+ """,
+ annotation
+ ) { _, invocation ->
+ invocation.assertCompilationResult {
+ hasError(ProcessorErrors.INVALID_INDEX_ORDERS_SIZE)
+ }
+ }
+ }
+
+ @Test
fun getterWithAssignableType() {
singleEntity(
"""
@@ -530,7 +611,8 @@
Index(
name = "index_MyEntity_foo",
unique = false,
- fields = fieldsByName(entity, "foo")
+ fields = fieldsByName(entity, "foo"),
+ emptyList()
)
)
)
@@ -555,7 +637,8 @@
Index(
name = "index_MyEntity_foo",
unique = false,
- fields = fieldsByName(entity, "foo")
+ fields = fieldsByName(entity, "foo"),
+ orders = emptyList()
)
)
)
@@ -583,7 +666,8 @@
Index(
name = "index_MyEntity_foo_id",
unique = false,
- fields = fieldsByName(entity, "foo", "id")
+ fields = fieldsByName(entity, "foo", "id"),
+ orders = emptyList()
)
)
)
@@ -613,12 +697,14 @@
Index(
name = "index_MyEntity_foo_id",
unique = false,
- fields = fieldsByName(entity, "foo", "id")
+ fields = fieldsByName(entity, "foo", "id"),
+ orders = emptyList()
),
Index(
name = "index_MyEntity_bar_column_foo",
unique = false,
- fields = fieldsByName(entity, "bar", "foo")
+ fields = fieldsByName(entity, "bar", "foo"),
+ orders = emptyList()
)
)
)
@@ -646,7 +732,8 @@
Index(
name = "index_MyEntity_foo_id",
unique = true,
- fields = fieldsByName(entity, "foo", "id")
+ fields = fieldsByName(entity, "foo", "id"),
+ orders = emptyList()
)
)
)
@@ -674,7 +761,8 @@
Index(
name = "myName",
unique = false,
- fields = fieldsByName(entity, "foo")
+ fields = fieldsByName(entity, "foo"),
+ orders = emptyList()
)
)
)
@@ -703,7 +791,8 @@
Index(
name = "index_MyTable_foo",
unique = false,
- fields = fieldsByName(entity, "foo")
+ fields = fieldsByName(entity, "foo"),
+ orders = emptyList()
)
)
)
@@ -851,7 +940,8 @@
Index(
name = "index_MyEntity_name_lastName",
unique = false,
- fields = fieldsByName(entity, "name", "lastName")
+ fields = fieldsByName(entity, "name", "lastName"),
+ orders = emptyList()
)
)
)
@@ -892,7 +982,8 @@
Index(
name = "index_MyEntity_name_lastName",
unique = false,
- fields = fieldsByName(entity, "name", "lastName")
+ fields = fieldsByName(entity, "name", "lastName"),
+ orders = emptyList()
)
)
)
@@ -965,7 +1056,8 @@
Index(
name = "index_MyEntity_name",
unique = false,
- fields = fieldsByName(entity, "name")
+ fields = fieldsByName(entity, "name"),
+ orders = emptyList()
)
)
)
@@ -1151,7 +1243,8 @@
Index(
name = "index_MyEntity_a",
unique = false,
- fields = fieldsByName(entity, "a")
+ fields = fieldsByName(entity, "a"),
+ orders = emptyList()
)
)
)
diff --git a/room/room-compiler/src/test/kotlin/androidx/room/util/SchemaDifferTest.kt b/room/room-compiler/src/test/kotlin/androidx/room/util/SchemaDifferTest.kt
index 55a0fda..b5be2a8 100644
--- a/room/room-compiler/src/test/kotlin/androidx/room/util/SchemaDifferTest.kt
+++ b/room/room-compiler/src/test/kotlin/androidx/room/util/SchemaDifferTest.kt
@@ -1195,7 +1195,8 @@
IndexBundle(
"index1",
true,
- listOf("title"),
+ emptyList<String>(),
+ emptyList<String>(),
"CREATE UNIQUE INDEX IF NOT EXISTS `index1` ON `Song`" +
"(`title`)"
)
diff --git a/room/room-compiler/src/test/kotlin/androidx/room/vo/DatabaseTest.kt b/room/room-compiler/src/test/kotlin/androidx/room/vo/DatabaseTest.kt
index 2c90155..f9c70b1 100644
--- a/room/room-compiler/src/test/kotlin/androidx/room/vo/DatabaseTest.kt
+++ b/room/room-compiler/src/test/kotlin/androidx/room/vo/DatabaseTest.kt
@@ -47,12 +47,14 @@
Index(
name = "leIndex",
unique = false,
- fields = Fields()
+ fields = Fields(),
+ orders = emptyList()
),
Index(
name = "leIndex2",
unique = true,
- fields = Fields()
+ fields = Fields(),
+ orders = emptyList()
)
),
foreignKeys = emptyList(),
diff --git a/room/room-compiler/src/test/kotlin/androidx/room/vo/IndexTest.kt b/room/room-compiler/src/test/kotlin/androidx/room/vo/IndexTest.kt
index 248b681..ad62980 100644
--- a/room/room-compiler/src/test/kotlin/androidx/room/vo/IndexTest.kt
+++ b/room/room-compiler/src/test/kotlin/androidx/room/vo/IndexTest.kt
@@ -24,11 +24,13 @@
import org.junit.runner.RunWith
import org.junit.runners.JUnit4
+private typealias IndexOrder = androidx.room.Index.Order
+
@RunWith(JUnit4::class)
class IndexTest {
@Test
fun createSimpleSQL() {
- val index = Index("foo", false, listOf(mockField("bar"), mockField("baz")))
+ val index = Index("foo", false, listOf(mockField("bar"), mockField("baz")), emptyList())
MatcherAssert.assertThat(
index.createQuery("my_table"),
CoreMatchers.`is`(
@@ -39,7 +41,7 @@
@Test
fun createUnique() {
- val index = Index("foo", true, listOf(mockField("bar"), mockField("baz")))
+ val index = Index("foo", true, listOf(mockField("bar"), mockField("baz")), emptyList())
MatcherAssert.assertThat(
index.createQuery("my_table"),
CoreMatchers.`is`(
@@ -48,6 +50,22 @@
)
}
+ @Test
+ fun createWithSortOrder() {
+ val index = Index(
+ name = "foo",
+ unique = false,
+ fields = listOf(mockField("bar"), mockField("baz")),
+ orders = listOf(IndexOrder.ASC, IndexOrder.DESC)
+ )
+ MatcherAssert.assertThat(
+ index.createQuery("my_table"),
+ CoreMatchers.`is`(
+ "CREATE INDEX IF NOT EXISTS `foo` ON `my_table` (`bar` ASC, `baz` DESC)"
+ )
+ )
+ }
+
private fun mockField(columnName: String): Field {
val (element, type) = mockElementAndType()
return Field(
diff --git a/room/room-migration/api/restricted_current.txt b/room/room-migration/api/restricted_current.txt
index d4f4fff..50f8227 100644
--- a/room/room-migration/api/restricted_current.txt
+++ b/room/room-migration/api/restricted_current.txt
@@ -77,11 +77,13 @@
}
@RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public class IndexBundle {
- ctor public IndexBundle(String!, boolean, java.util.List<java.lang.String!>!, String!);
+ ctor @Deprecated public IndexBundle(String!, boolean, java.util.List<java.lang.String!>!, String!);
+ ctor public IndexBundle(String!, boolean, java.util.List<java.lang.String!>!, java.util.List<java.lang.String!>!, String!);
method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public String! create(String);
method public java.util.List<java.lang.String!>! getColumnNames();
method public String! getCreateSql(String!);
method public String! getName();
+ method public java.util.List<java.lang.String!>! getOrders();
method public boolean isSchemaEqual(androidx.room.migration.bundle.IndexBundle);
method public boolean isUnique();
field public static final String DEFAULT_PREFIX = "index_";
diff --git a/room/room-migration/src/main/java/androidx/room/migration/bundle/IndexBundle.java b/room/room-migration/src/main/java/androidx/room/migration/bundle/IndexBundle.java
index ae0693e..9fdd582 100644
--- a/room/room-migration/src/main/java/androidx/room/migration/bundle/IndexBundle.java
+++ b/room/room-migration/src/main/java/androidx/room/migration/bundle/IndexBundle.java
@@ -18,9 +18,11 @@
import androidx.annotation.NonNull;
import androidx.annotation.RestrictTo;
+import androidx.room.Index;
import com.google.gson.annotations.SerializedName;
+import java.util.Collections;
import java.util.List;
/**
@@ -38,14 +40,25 @@
private boolean mUnique;
@SerializedName("columnNames")
private List<String> mColumnNames;
+ @SerializedName("orders")
+ private List<String> mOrders;
@SerializedName("createSql")
private String mCreateSql;
- public IndexBundle(String name, boolean unique, List<String> columnNames,
+ /**
+ * @deprecated Use {@link #IndexBundle(String, boolean, List, List, String)}
+ */
+ @Deprecated
+ public IndexBundle(String name, boolean unique, List<String> columnNames, String createSql) {
+ this(name, unique, columnNames, null, createSql);
+ }
+
+ public IndexBundle(String name, boolean unique, List<String> columnNames, List<String> orders,
String createSql) {
mName = name;
mUnique = unique;
mColumnNames = columnNames;
+ mOrders = orders;
mCreateSql = createSql;
}
@@ -61,6 +74,10 @@
return mColumnNames;
}
+ public List<String> getOrders() {
+ return mOrders;
+ }
+
/**
* @param tableName The table name.
* @return Create index SQL query that uses the given table name.
@@ -95,6 +112,16 @@
: other.mColumnNames != null) {
return false;
}
+
+ // order matters and null orders is considered equal to all ASC orders, to be backward
+ // compatible with schemas where orders are not present in the schema file
+ int columnsSize = mColumnNames != null ? mColumnNames.size() : 0;
+ List<String> orders = mOrders == null || mOrders.isEmpty()
+ ? Collections.nCopies(columnsSize, Index.Order.ASC.name()) : mOrders;
+ List<String> otherOrders = other.mOrders == null || other.mOrders.isEmpty()
+ ? Collections.nCopies(columnsSize, Index.Order.ASC.name()) : other.mOrders;
+ if (!orders.equals(otherOrders)) return false;
+
return true;
}
}
diff --git a/room/room-migration/src/test/java/androidx/room/migration/bundle/EntityBundleTest.java b/room/room-migration/src/test/java/androidx/room/migration/bundle/EntityBundleTest.java
index 3b9bce2..b40efb5 100644
--- a/room/room-migration/src/test/java/androidx/room/migration/bundle/EntityBundleTest.java
+++ b/room/room-migration/src/test/java/androidx/room/migration/bundle/EntityBundleTest.java
@@ -158,7 +158,7 @@
private IndexBundle createIndexBundle(String colName) {
return new IndexBundle("ind_" + colName, false,
- asList(colName), "create");
+ asList(colName), Collections.emptyList(), "create");
}
private ForeignKeyBundle createForeignKeyBundle(String targetTable, String column) {
diff --git a/room/room-migration/src/test/java/androidx/room/migration/bundle/IndexBundleTest.java b/room/room-migration/src/test/java/androidx/room/migration/bundle/IndexBundleTest.java
index 82d66e2..812f7d3 100644
--- a/room/room-migration/src/test/java/androidx/room/migration/bundle/IndexBundleTest.java
+++ b/room/room-migration/src/test/java/androidx/room/migration/bundle/IndexBundleTest.java
@@ -24,60 +24,88 @@
import org.junit.runners.JUnit4;
import java.util.Arrays;
+import java.util.Collections;
@RunWith(JUnit4.class)
public class IndexBundleTest {
@Test
public void schemaEquality_same_equal() {
IndexBundle bundle = new IndexBundle("index1", false,
- Arrays.asList("col1", "col2"), "sql");
+ Arrays.asList("col1", "col2"), Arrays.asList("ASC", "ASC"), "sql");
IndexBundle other = new IndexBundle("index1", false,
- Arrays.asList("col1", "col2"), "sql");
+ Arrays.asList("col1", "col2"), Arrays.asList("ASC", "ASC"), "sql");
assertThat(bundle.isSchemaEqual(other), is(true));
}
@Test
public void schemaEquality_diffName_notEqual() {
IndexBundle bundle = new IndexBundle("index1", false,
- Arrays.asList("col1", "col2"), "sql");
+ Arrays.asList("col1", "col2"), Arrays.asList("ASC", "ASC"), "sql");
IndexBundle other = new IndexBundle("index3", false,
- Arrays.asList("col1", "col2"), "sql");
+ Arrays.asList("col1", "col2"), Arrays.asList("ASC", "ASC"), "sql");
assertThat(bundle.isSchemaEqual(other), is(false));
}
@Test
public void schemaEquality_diffGenericName_equal() {
IndexBundle bundle = new IndexBundle(IndexBundle.DEFAULT_PREFIX + "x", false,
- Arrays.asList("col1", "col2"), "sql");
+ Arrays.asList("col1", "col2"), Arrays.asList("ASC", "ASC"), "sql");
IndexBundle other = new IndexBundle(IndexBundle.DEFAULT_PREFIX + "y", false,
- Arrays.asList("col1", "col2"), "sql");
+ Arrays.asList("col1", "col2"), Arrays.asList("ASC", "ASC"), "sql");
assertThat(bundle.isSchemaEqual(other), is(true));
}
@Test
public void schemaEquality_diffUnique_notEqual() {
IndexBundle bundle = new IndexBundle("index1", false,
- Arrays.asList("col1", "col2"), "sql");
+ Arrays.asList("col1", "col2"), Arrays.asList("ASC", "ASC"), "sql");
IndexBundle other = new IndexBundle("index1", true,
- Arrays.asList("col1", "col2"), "sql");
+ Arrays.asList("col1", "col2"), Arrays.asList("ASC", "ASC"), "sql");
assertThat(bundle.isSchemaEqual(other), is(false));
}
@Test
public void schemaEquality_diffColumns_notEqual() {
IndexBundle bundle = new IndexBundle("index1", false,
- Arrays.asList("col1", "col2"), "sql");
+ Arrays.asList("col1", "col2"), Arrays.asList("ASC", "ASC"), "sql");
IndexBundle other = new IndexBundle("index1", false,
- Arrays.asList("col2", "col1"), "sql");
+ Arrays.asList("col2", "col1"), Arrays.asList("ASC", "ASC"), "sql");
assertThat(bundle.isSchemaEqual(other), is(false));
}
@Test
public void schemaEquality_diffSql_equal() {
IndexBundle bundle = new IndexBundle("index1", false,
- Arrays.asList("col1", "col2"), "sql");
+ Arrays.asList("col1", "col2"), Arrays.asList("ASC", "ASC"), "sql");
IndexBundle other = new IndexBundle("index1", false,
- Arrays.asList("col1", "col2"), "sql22");
+ Arrays.asList("col1", "col2"), Arrays.asList("ASC", "ASC"), "sql22");
+ assertThat(bundle.isSchemaEqual(other), is(true));
+ }
+
+ @Test
+ public void schemaEquality_diffSort_notEqual() {
+ IndexBundle bundle = new IndexBundle("index1", false,
+ Arrays.asList("col1", "col2"), Arrays.asList("ASC", "DESC"), "sql");
+ IndexBundle other = new IndexBundle("index1", false,
+ Arrays.asList("col1", "col2"), Arrays.asList("DESC", "ASC"), "sql");
+ assertThat(bundle.isSchemaEqual(other), is(false));
+ }
+
+ @Test
+ public void schemaEquality_sortNullVsAllAsc_isEqual() {
+ IndexBundle bundle = new IndexBundle("index1", false,
+ Arrays.asList("col1", "col2"), Arrays.asList("ASC", "ASC"), "sql");
+ IndexBundle other = new IndexBundle("index1", false,
+ Arrays.asList("col1", "col2"), null, "sql");
+ assertThat(bundle.isSchemaEqual(other), is(true));
+ }
+
+ @Test
+ public void schemaEquality_sortEmptyVsAllAsc_isEqual() {
+ IndexBundle bundle = new IndexBundle("index1", false,
+ Arrays.asList("col1", "col2"), Arrays.asList("ASC", "ASC"), "sql");
+ IndexBundle other = new IndexBundle("index1", false,
+ Arrays.asList("col1", "col2"), Collections.emptyList(), "sql");
assertThat(bundle.isSchemaEqual(other), is(true));
}
}
diff --git a/room/room-runtime/api/restricted_current.txt b/room/room-runtime/api/restricted_current.txt
index 9eaf43a..60b720c 100644
--- a/room/room-runtime/api/restricted_current.txt
+++ b/room/room-runtime/api/restricted_current.txt
@@ -330,10 +330,12 @@
}
@RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public static final class TableInfo.Index {
- ctor public TableInfo.Index(String!, boolean, java.util.List<java.lang.String!>!);
+ ctor @Deprecated public TableInfo.Index(String!, boolean, java.util.List<java.lang.String!>!);
+ ctor public TableInfo.Index(String!, boolean, java.util.List<java.lang.String!>!, java.util.List<java.lang.String!>!);
field public static final String DEFAULT_PREFIX = "index_";
field public final java.util.List<java.lang.String!>! columns;
field public final String! name;
+ field public final java.util.List<java.lang.String!>! orders;
field public final boolean unique;
}
diff --git a/room/room-runtime/src/androidTest/java/androidx/room/migration/TableInfoTest.java b/room/room-runtime/src/androidTest/java/androidx/room/migration/TableInfoTest.java
index f7eb2f8..9a200b3 100644
--- a/room/room-runtime/src/androidTest/java/androidx/room/migration/TableInfoTest.java
+++ b/room/room-runtime/src/androidTest/java/androidx/room/migration/TableInfoTest.java
@@ -327,11 +327,11 @@
TableInfo.CREATED_FROM_ENTITY)),
Collections.<TableInfo.ForeignKey>emptySet(),
toSet(new TableInfo.Index("index_foo_blahblah", false,
- Arrays.asList("a", "b")),
+ Arrays.asList("a", "b"), Collections.emptyList()),
new TableInfo.Index("foo_unique_indexed", true,
- Arrays.asList("unique_indexed")),
+ Arrays.asList("unique_indexed"), Arrays.asList("DESC")),
new TableInfo.Index("foo_indexed", false,
- Arrays.asList("indexed"))));
+ Arrays.asList("indexed"), Arrays.asList("ASC"))));
assertThat(expectedInfo, is(dbInfo));
}
diff --git a/room/room-runtime/src/main/java/androidx/room/util/TableInfo.java b/room/room-runtime/src/main/java/androidx/room/util/TableInfo.java
index 37b41d6..2d3c8be 100644
--- a/room/room-runtime/src/main/java/androidx/room/util/TableInfo.java
+++ b/room/room-runtime/src/main/java/androidx/room/util/TableInfo.java
@@ -313,11 +313,14 @@
final int seqnoColumnIndex = cursor.getColumnIndex("seqno");
final int cidColumnIndex = cursor.getColumnIndex("cid");
final int nameColumnIndex = cursor.getColumnIndex("name");
- if (seqnoColumnIndex == -1 || cidColumnIndex == -1 || nameColumnIndex == -1) {
+ final int descColumnIndex = cursor.getColumnIndex("desc");
+ if (seqnoColumnIndex == -1 || cidColumnIndex == -1
+ || nameColumnIndex == -1 || descColumnIndex == -1) {
// we cannot read them so better not validate any index.
return null;
}
- final TreeMap<Integer, String> results = new TreeMap<>();
+ final TreeMap<Integer, String> columnsMap = new TreeMap<>();
+ final TreeMap<Integer, String> ordersMap = new TreeMap<>();
while (cursor.moveToNext()) {
int cid = cursor.getInt(cidColumnIndex);
@@ -327,11 +330,16 @@
}
int seq = cursor.getInt(seqnoColumnIndex);
String columnName = cursor.getString(nameColumnIndex);
- results.put(seq, columnName);
+ String order = cursor.getInt(descColumnIndex) > 0 ? "DESC" : "ASC";
+
+ columnsMap.put(seq, columnName);
+ ordersMap.put(seq, order);
}
- final List<String> columns = new ArrayList<>(results.size());
- columns.addAll(results.values());
- return new Index(name, unique, columns);
+ final List<String> columns = new ArrayList<>(columnsMap.size());
+ columns.addAll(columnsMap.values());
+ final List<String> orders = new ArrayList<>(ordersMap.size());
+ orders.addAll(ordersMap.values());
+ return new Index(name, unique, columns, orders);
} finally {
cursor.close();
}
@@ -668,11 +676,22 @@
public final String name;
public final boolean unique;
public final List<String> columns;
+ public final List<String> orders;
+ /**
+ * @deprecated Use {@link #Index(String, boolean, List, List)}
+ */
public Index(String name, boolean unique, List<String> columns) {
+ this(name, unique, columns, null);
+ }
+
+ public Index(String name, boolean unique, List<String> columns, List<String> orders) {
this.name = name;
this.unique = unique;
this.columns = columns;
+ this.orders = orders == null || orders.size() == 0
+ ? Collections.nCopies(columns.size(), androidx.room.Index.Order.ASC.name())
+ : orders;
}
@Override
@@ -687,6 +706,9 @@
if (!columns.equals(index.columns)) {
return false;
}
+ if (!orders.equals(index.orders)) {
+ return false;
+ }
if (name.startsWith(Index.DEFAULT_PREFIX)) {
return index.name.startsWith(Index.DEFAULT_PREFIX);
} else {
@@ -704,6 +726,7 @@
}
result = 31 * result + (unique ? 1 : 0);
result = 31 * result + columns.hashCode();
+ result = 31 * result + orders.hashCode();
return result;
}
@@ -713,6 +736,7 @@
+ "name='" + name + '\''
+ ", unique=" + unique
+ ", columns=" + columns
+ + ", orders=" + orders
+ '}';
}
}
diff --git a/room/room-testing/src/main/java/androidx/room/testing/MigrationTestHelper.java b/room/room-testing/src/main/java/androidx/room/testing/MigrationTestHelper.java
index 02c5192..1431498 100644
--- a/room/room-testing/src/main/java/androidx/room/testing/MigrationTestHelper.java
+++ b/room/room-testing/src/main/java/androidx/room/testing/MigrationTestHelper.java
@@ -515,7 +515,7 @@
Set<TableInfo.Index> result = new HashSet<>();
for (IndexBundle bundle : indices) {
result.add(new TableInfo.Index(bundle.getName(), bundle.isUnique(),
- bundle.getColumnNames()));
+ bundle.getColumnNames(), bundle.getOrders()));
}
return result;
}