From 368898fc0221e65a5cb091a6d4a24c98219ba157 Mon Sep 17 00:00:00 2001 From: Ricki Hirner Date: Sat, 2 Jan 2021 15:02:48 +0100 Subject: BatchOperation: fix back references when the transaction has to be split multiple times --- .../at/bitfire/vcard4android/BatchOperation.kt | 47 ++++++++++++++++++---- 1 file changed, 39 insertions(+), 8 deletions(-) diff --git a/src/main/java/at/bitfire/vcard4android/BatchOperation.kt b/src/main/java/at/bitfire/vcard4android/BatchOperation.kt index ce49a11..1c8b936 100644 --- a/src/main/java/at/bitfire/vcard4android/BatchOperation.kt +++ b/src/main/java/at/bitfire/vcard4android/BatchOperation.kt @@ -102,6 +102,7 @@ class BatchOperation( Constants.log.warning("Transaction too large, splitting (losing atomicity)") val mid = start + (end - start)/2 + runBatch(start, mid) runBatch(mid, end) } @@ -110,18 +111,25 @@ class BatchOperation( private fun toCPO(start: Int, end: Int): ArrayList { val cpo = ArrayList(end - start) + /* Fix back references: + * 1. If a back reference points to a row between start and end, + * adapt the reference. + * 2. If a back reference points to a row outside of start/end, + * replace it by the actual result, which has already been calculated. */ + for ((i, cpoBuilder) in queue.subList(start, end).withIndex()) { - for ((backrefKey, backrefIdx) in cpoBuilder.valueBackrefs) { - if (backrefIdx < start) { + for ((backrefKey, backref) in cpoBuilder.valueBackrefs) { + val originalIdx = backref.originalIndex + if (originalIdx < start) { // back reference is outside of the current batch, get result from previous execution ... - val resultUri = results[backrefIdx]?.uri ?: throw ContactsStorageException("Referenced operation didn't produce a valid result") + val resultUri = results[originalIdx]?.uri ?: throw ContactsStorageException("Referenced operation didn't produce a valid result") val resultId = ContentUris.parseId(resultUri) // ... and use result directly instead of using a back reference cpoBuilder .removeValueBackReference(backrefKey) .withValue(backrefKey, resultId) } else // back reference is in current batch, shift index - cpoBuilder.withValueBackReference(backrefKey, backrefIdx - start) + backref.setIndex(originalIdx - start) } if (i % MAX_OPERATIONS_PER_YIELD_POINT == MAX_OPERATIONS_PER_YIELD_POINT - 1) @@ -133,6 +141,29 @@ class BatchOperation( } + class BackReference( + /** index of the referenced row in the original, nonsplitted transaction */ + val originalIndex: Int + ) { + /** overriden index, i.e. index within the splitted transaction */ + private var index: Int? = null + + /** + * Sets the index to use in the splitted transaction. + * @param newIndex index to be used instead of [originalIndex] + */ + fun setIndex(newIndex: Int) { + index = newIndex + } + + /** + * Gets the index to use in the splitted transaction. + * @return [index] if it has been set, [originalIndex] otherwise + */ + fun getIndex() = index ?: originalIndex + } + + /** * Wrapper for [ContentProviderOperation.Builder] that allows to reset previously-set * value back references. @@ -157,7 +188,7 @@ class BatchOperation( var selectionArguments: Array? = null val values = mutableMapOf() - val valueBackrefs = mutableMapOf() + val valueBackrefs = mutableMapOf() var yieldAllowed = false @@ -169,7 +200,7 @@ class BatchOperation( } fun withValueBackReference(key: String, index: Int): CpoBuilder { - valueBackrefs[key] = index + valueBackrefs[key] = BackReference(index) return this } @@ -197,8 +228,8 @@ class BatchOperation( for ((key, value) in values) builder.withValue(key, value) - for ((key, index) in valueBackrefs) - builder.withValueBackReference(key, index) + for ((key, backref) in valueBackrefs) + builder.withValueBackReference(key, backref.getIndex()) if (yieldAllowed) builder.withYieldAllowed(true) -- cgit v1.2.3