Handle special case SQLite exception during upsert.
When 2067 SQLITE_CONSTRAINT_UNIQUE is thrown during an upsert, upsert should perform an update.
Bug: 243039555
Test: EntityUpsertionAdapterTest.java
Change-Id: If28499c59443f590ac456924eff03b18f1a87e4f
diff --git a/room/integration-tests/testapp/src/androidTest/java/androidx/room/integration/testapp/test/EntityUpsertionAdapterTest.java b/room/integration-tests/testapp/src/androidTest/java/androidx/room/integration/testapp/test/EntityUpsertionAdapterTest.java
index 071d890..8e7343a 100644
--- a/room/integration-tests/testapp/src/androidTest/java/androidx/room/integration/testapp/test/EntityUpsertionAdapterTest.java
+++ b/room/integration-tests/testapp/src/androidTest/java/androidx/room/integration/testapp/test/EntityUpsertionAdapterTest.java
@@ -45,6 +45,7 @@
import java.util.Date;
import java.util.List;
+import java.util.UUID;
@RunWith(AndroidJUnit4.class)
@MediumTest
@@ -235,6 +236,33 @@
}
@Test
+ public void upsertFKUnique2067Error() {
+ Pet pet = new Pet();
+ pet.setPetId(232);
+ pet.setName(UUID.randomUUID().toString());
+ pet.setAdoptionDate(new Date());
+ mInsertionAdapter.insert(pet);
+
+ Toy testToy = new Toy();
+ testToy.setId(2);
+ testToy.setName("toy name");
+ testToy.setPetId(232);
+
+ Toy testToy2 = new Toy();
+ testToy2.setId(3);
+ testToy2.setName("toy name");
+ testToy2.setPetId(232);
+
+ mUpsertionAdapter.upsertAndReturnId(pet);
+ mUpsertionAdapterToy.upsertAndReturnId(testToy);
+ try {
+ mUpsertionAdapterToy.upsertAndReturnId(testToy2);
+ } catch (SQLiteConstraintException ex) {
+ assertThat(ex.toString().contains("2067"));
+ }
+ }
+
+ @Test
public void testUpsertWithoutTryCatch() {
Pet testPet = TestUtil.createPet(232);
Pet testPet2 = TestUtil.createPet(232);
diff --git a/room/room-runtime/src/main/java/androidx/room/EntityUpsertionAdapter.kt b/room/room-runtime/src/main/java/androidx/room/EntityUpsertionAdapter.kt
index b1c65a8..db31e11 100644
--- a/room/room-runtime/src/main/java/androidx/room/EntityUpsertionAdapter.kt
+++ b/room/room-runtime/src/main/java/androidx/room/EntityUpsertionAdapter.kt
@@ -17,14 +17,18 @@
package androidx.room
import android.database.sqlite.SQLiteConstraintException
-import android.os.Build
import androidx.annotation.RestrictTo
/**
- * The ErrorCode defined by SQLite Library for SQLITE_CONSTRAINT_PRIMARYKEY error
- * Only used by android of version newer than 19
+ * The error code defined by SQLite Library for SQLITE_CONSTRAINT_PRIMARYKEY error
+ * Only used by android of version newer than 19.
*/
-private const val ErrorCode = "1555"
+private const val SQLITE_CONSTRAINT_PRIMARYKEY = "1555"
+
+/**
+ * The error code defined by SQLite Library for SQLITE_CONSTRAINT_UNIQUE error.
+ */
+private const val SQLITE_CONSTRAINT_UNIQUE = "2067"
/**
* For android of version below and including 19, use error message instead of
@@ -207,18 +211,13 @@
*/
private fun checkUniquenessException(ex: SQLiteConstraintException) {
val message = ex.message ?: throw ex
- if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.KITKAT) {
- if (!message.contains(ErrorMsg, ignoreCase = true) && !message.contains(
- ErrorCode,
- ignoreCase = true
- )
- ) {
- throw ex
- }
- } else {
- if (!message.contains(ErrorCode, ignoreCase = true)) {
- throw ex
- }
+ val hasUniqueConstraintEx =
+ message.contains(ErrorMsg, ignoreCase = true) ||
+ message.contains(SQLITE_CONSTRAINT_UNIQUE) ||
+ message.contains(SQLITE_CONSTRAINT_PRIMARYKEY)
+
+ if (!hasUniqueConstraintEx) {
+ throw ex
}
}
}