diff --git a/platform/workspace/storage/src/com/intellij/platform/workspace/storage/impl/AddDiffOperation.kt b/platform/workspace/storage/src/com/intellij/platform/workspace/storage/impl/AddDiffOperation.kt index bff290ab248d..b33b185bcd3b 100644 --- a/platform/workspace/storage/src/com/intellij/platform/workspace/storage/impl/AddDiffOperation.kt +++ b/platform/workspace/storage/src/com/intellij/platform/workspace/storage/impl/AddDiffOperation.kt @@ -127,7 +127,7 @@ internal class AddDiffOperation(val target: MutableEntityStorageImpl, val diff: if (targetParentId != null) { // For one-to-one connections it's necessary to remove the obsolete children to avoid "entity leaks" and the state of broken store - if (connectionId.connectionType == ConnectionId.ConnectionType.ONE_TO_ONE || connectionId.connectionType == ConnectionId.ConnectionType.ABSTRACT_ONE_TO_ONE) { + if (connectionId.isOneToOne && !connectionId.isParentNullable) { val obsoleteChild = target.extractOneToOneChild(connectionId, targetParentId.id) if (obsoleteChild != null && obsoleteChild.id != targetEntityId.id) { target.removeEntity(obsoleteChild) diff --git a/platform/workspace/storage/tests/testSrc/com/intellij/platform/workspace/storage/tests/AddDiffTest.kt b/platform/workspace/storage/tests/testSrc/com/intellij/platform/workspace/storage/tests/AddDiffTest.kt index 999f96ecfb48..651a97ea6a3b 100644 --- a/platform/workspace/storage/tests/testSrc/com/intellij/platform/workspace/storage/tests/AddDiffTest.kt +++ b/platform/workspace/storage/tests/testSrc/com/intellij/platform/workspace/storage/tests/AddDiffTest.kt @@ -731,6 +731,50 @@ class AddDiffTest { assertTrue(target.entities(XChildWithOptionalParentEntity::class.java).none()) } + @RepeatedTest(10) + fun `replace child by modification`() { + val parentEntity = target addEntity OoParentEntity("Parent2", MySource) { + this.anotherChild = OoChildWithNullableParentEntity(MySource) + } + + val source = createBuilderFrom(target) + source.modifyEntity(parentEntity.from(source)) { + this.anotherChild = OoChildWithNullableParentEntity(AnotherSource) + } + + // The previous child was not removed because it has optional parent + assertEquals(2, source.entities(OoChildWithNullableParentEntity::class.java).toList().size) + + target.addDiff(source) + + target.assertConsistency() + + assertEquals(AnotherSource, target.entities(OoParentEntity::class.java).single().anotherChild!!.entitySource) + assertEquals(2, target.entities(OoChildWithNullableParentEntity::class.java).toList().size) + } + + @RepeatedTest(10) + fun `add child with parallel update`() { + val parentEntity = target addEntity OoParentEntity("Parent2", MySource) + + val source = createBuilderFrom(target) + source.modifyEntity(parentEntity.from(source)) { + this.anotherChild = OoChildWithNullableParentEntity(AnotherSource) + } + + // Update target builder in parallel with the source builder + target.modifyEntity(parentEntity.from(source)) { + this.anotherChild = OoChildWithNullableParentEntity(MySource) + } + + target.addDiff(source) + + target.assertConsistency() + + assertEquals(AnotherSource, target.entities(OoParentEntity::class.java).single().anotherChild!!.entitySource) + assertEquals(2, target.entities(OoChildWithNullableParentEntity::class.java).toList().size) + } + @RepeatedTest(10) fun `remove entity with soft link`() { val entity = target addEntity WithSoftLinkEntity(NameId("id"), MySource)