diff --git a/entity-store/build.gradle.kts b/entity-store/build.gradle.kts index 2c4cab556..29c6178ba 100644 --- a/entity-store/build.gradle.kts +++ b/entity-store/build.gradle.kts @@ -1,6 +1,6 @@ dependencies { api(project(":xodus-openAPI")) - api("com.orientechnologies:orientdb-core:4.0.0-20241101.163455-177") + api("com.orientechnologies:orientdb-core:4.0.0-20241118.113248-197") implementation(project(":xodus-utils")) implementation(project(":xodus-environment")) diff --git a/entity-store/src/main/kotlin/jetbrains/exodus/entitystore/orientdb/ODatabaseCompacter.kt b/entity-store/src/main/kotlin/jetbrains/exodus/entitystore/orientdb/ODatabaseCompacter.kt index 6d5d60300..c88250246 100644 --- a/entity-store/src/main/kotlin/jetbrains/exodus/entitystore/orientdb/ODatabaseCompacter.kt +++ b/entity-store/src/main/kotlin/jetbrains/exodus/entitystore/orientdb/ODatabaseCompacter.kt @@ -16,7 +16,8 @@ package jetbrains.exodus.entitystore.orientdb import com.orientechnologies.orient.core.command.OCommandOutputListener -import com.orientechnologies.orient.core.db.ODatabaseDocumentInternal +import com.orientechnologies.orient.core.db.ODatabaseSession +import com.orientechnologies.orient.core.db.ODatabaseSessionInternal import com.orientechnologies.orient.core.db.ODatabaseType import com.orientechnologies.orient.core.db.tool.ODatabaseExport import com.orientechnologies.orient.core.db.tool.ODatabaseImport @@ -39,7 +40,7 @@ class ODatabaseCompacter( dbProvider.withSession { session -> val exporter = ODatabaseExport( - session as ODatabaseDocumentInternal, + session as ODatabaseSessionInternal, backupFile.outputStream(), listener ) @@ -55,7 +56,7 @@ class ODatabaseCompacter( dbProvider.withSession { session -> logger.info("Importing database from dump") val importer = ODatabaseImport( - session as ODatabaseDocumentInternal, + session as ODatabaseSessionInternal, backupFile.inputStream(), listener ) diff --git a/entity-store/src/main/kotlin/jetbrains/exodus/entitystore/orientdb/ODatabaseProvider.kt b/entity-store/src/main/kotlin/jetbrains/exodus/entitystore/orientdb/ODatabaseProvider.kt index 640a2698e..9a4f6e81a 100644 --- a/entity-store/src/main/kotlin/jetbrains/exodus/entitystore/orientdb/ODatabaseProvider.kt +++ b/entity-store/src/main/kotlin/jetbrains/exodus/entitystore/orientdb/ODatabaseProvider.kt @@ -18,8 +18,6 @@ package jetbrains.exodus.entitystore.orientdb import com.orientechnologies.orient.core.db.ODatabaseRecordThreadLocal import com.orientechnologies.orient.core.db.ODatabaseSession import com.orientechnologies.orient.core.db.OrientDB -import com.orientechnologies.orient.core.tx.OTransaction -import com.orientechnologies.orient.core.tx.OTransactionNoTx interface ODatabaseProvider { val databaseLocation: String @@ -33,7 +31,10 @@ interface ODatabaseProvider { * Never use this method. If you use this method, make sure you 100% understand what happens, * and do not hesitate to invite people to review your code. */ - fun executeInASeparateSession(currentSession: ODatabaseSession, action: (ODatabaseSession) -> T): T + fun executeInASeparateSession( + currentSession: ODatabaseSession, + action: (ODatabaseSession) -> T + ): T /** * Database-wise read-only mode. @@ -68,16 +69,15 @@ fun ODatabaseProvider.withCurrentOrNewSession( } internal fun ODatabaseSession.hasActiveTransaction(): Boolean { - return isActiveOnCurrentThread && transaction !is OTransactionNoTx + return isActiveOnCurrentThread && activeTxCount() > 0 } -internal fun ODatabaseSession.requireActiveTransaction(): OTransaction { +internal fun ODatabaseSession.requireActiveTransaction() { require(hasActiveTransaction()) { "No active transaction is found. Happy debugging, pal!" } - return transaction } internal fun ODatabaseSession.requireNoActiveTransaction() { - assert(isActiveOnCurrentThread && transaction is OTransactionNoTx) { "Active transaction is detected. Changes in the schema must not happen in a transaction." } + assert(isActiveOnCurrentThread && activeTxCount() == 0) { "Active transaction is detected. Changes in the schema must not happen in a transaction." } } internal fun requireNoActiveSession() { diff --git a/entity-store/src/main/kotlin/jetbrains/exodus/entitystore/orientdb/OSchemaBuddy.kt b/entity-store/src/main/kotlin/jetbrains/exodus/entitystore/orientdb/OSchemaBuddy.kt index a96155a45..5ee9ddf60 100644 --- a/entity-store/src/main/kotlin/jetbrains/exodus/entitystore/orientdb/OSchemaBuddy.kt +++ b/entity-store/src/main/kotlin/jetbrains/exodus/entitystore/orientdb/OSchemaBuddy.kt @@ -118,7 +118,9 @@ class OSchemaBuddyImpl( override fun updateSequence(session: ODatabaseSession, sequenceName: String, currentValue: Long) { session.executeInASeparateSessionIfCurrentHasTransaction(dbProvider) { sessionToWork -> + sessionToWork.begin() getSequence(sessionToWork, sequenceName).updateParams(CreateParams().setCurrentValue(currentValue)) + sessionToWork.commit() } } diff --git a/entity-store/src/main/kotlin/jetbrains/exodus/entitystore/orientdb/OStoreTransaction.kt b/entity-store/src/main/kotlin/jetbrains/exodus/entitystore/orientdb/OStoreTransaction.kt index d84fee192..5643e4daa 100644 --- a/entity-store/src/main/kotlin/jetbrains/exodus/entitystore/orientdb/OStoreTransaction.kt +++ b/entity-store/src/main/kotlin/jetbrains/exodus/entitystore/orientdb/OStoreTransaction.kt @@ -39,7 +39,7 @@ interface OStoreTransaction : StoreTransaction { fun activateOnCurrentThread() - fun getRecord(id: OEntityId): T? + fun getRecord(id: OEntityId): T where T: ORecord fun newEntity(entityType: String, localEntityId: Long): OVertexEntity diff --git a/entity-store/src/main/kotlin/jetbrains/exodus/entitystore/orientdb/OStoreTransactionImpl.kt b/entity-store/src/main/kotlin/jetbrains/exodus/entitystore/orientdb/OStoreTransactionImpl.kt index 950dddde5..825186d67 100644 --- a/entity-store/src/main/kotlin/jetbrains/exodus/entitystore/orientdb/OStoreTransactionImpl.kt +++ b/entity-store/src/main/kotlin/jetbrains/exodus/entitystore/orientdb/OStoreTransactionImpl.kt @@ -15,15 +15,15 @@ */ package jetbrains.exodus.entitystore.orientdb -import com.orientechnologies.orient.core.db.ODatabase import com.orientechnologies.orient.core.db.ODatabaseSession +import com.orientechnologies.orient.core.db.ODatabaseSessionInternal +import com.orientechnologies.orient.core.exception.ORecordNotFoundException import com.orientechnologies.orient.core.metadata.schema.OClass import com.orientechnologies.orient.core.metadata.sequence.OSequence import com.orientechnologies.orient.core.record.ORecord import com.orientechnologies.orient.core.record.OVertex import com.orientechnologies.orient.core.sql.executor.OResultSet import com.orientechnologies.orient.core.tx.OTransaction.TXSTATUS -import com.orientechnologies.orient.core.tx.OTransactionNoTx import jetbrains.exodus.entitystore.* import jetbrains.exodus.entitystore.orientdb.OVertexEntity.Companion.LOCAL_ENTITY_ID_PROPERTY_NAME import jetbrains.exodus.entitystore.orientdb.iterate.OEntityOfTypeIterable @@ -53,17 +53,22 @@ class OStoreTransactionImpl( */ private val transactionIdImpl by lazy { requireActiveTransaction() - session.transaction.id.toLong() + (session as ODatabaseSessionInternal).transaction.id.toLong() } override fun getTransactionId(): Long { return transactionIdImpl } - override fun getRecord(id: OEntityId): T? + override fun getRecord(id: OEntityId): T where T : ORecord { requireActiveTransaction() - return session.getRecord(id.asOId()) + try { + return session.load(id.asOId()) + } catch (e: ORecordNotFoundException) { + throw EntityRemovedInDatabaseException(id.getTypeName(), id) + } + } override fun query(sql: String, params: Map): OResultSet { @@ -76,7 +81,7 @@ class OStoreTransactionImpl( } override fun isIdempotent(): Boolean { - return readOnly || session.transaction.recordOperations.none() + return readOnly || (session as ODatabaseSessionInternal).transaction.recordOperations.none() } override fun isReadonly(): Boolean { @@ -84,7 +89,7 @@ class OStoreTransactionImpl( } override fun isFinished(): Boolean { - return session.status == ODatabase.STATUS.CLOSED + return session.status == ODatabaseSession.STATUS.CLOSED } override fun isCurrent(): Boolean { @@ -96,9 +101,9 @@ class OStoreTransactionImpl( } override fun requireActiveTransaction() { - check(session.status == ODatabase.STATUS.OPEN) { "The transaction is finished, the internal session state: ${session.status}" } + check(session.status == ODatabaseSession.STATUS.OPEN) { "The transaction is finished, the internal session state: ${session.status}" } check(session.isActiveOnCurrentThread) { "The active session is no the session the transaction was started in" } - check(session.transaction.status == TXSTATUS.BEGUN) { "The current OTransaction status is ${session.transaction.status}, but the status ${TXSTATUS.BEGUN} was expected." } + check((session as ODatabaseSessionInternal).transaction.status == TXSTATUS.BEGUN) { "The current OTransaction status is ${session.transaction.status}, but the status ${TXSTATUS.BEGUN} was expected." } } override fun requireActiveWritableTransaction() { @@ -112,15 +117,15 @@ class OStoreTransactionImpl( } override fun activateOnCurrentThread() { - check(session.status == ODatabase.STATUS.OPEN) { "The transaction is finished, the internal session state: ${session.status}" } + check(session.status == ODatabaseSession.STATUS.OPEN) { "The transaction is finished, the internal session state: ${session.status}" } check(!session.isActiveOnCurrentThread) { "The transaction is already active on the current thread" } onActivated(session, this) } fun begin() { - check(session.status == ODatabase.STATUS.OPEN) { "The session status is ${session.status}. But ${ODatabase.STATUS.OPEN} is required." } + check(session.status == ODatabaseSession.STATUS.OPEN) { "The session status is ${session.status}. But ${ODatabaseSession.STATUS.OPEN} is required." } check(session.isActiveOnCurrentThread) { "The session is not active on the current thread" } - check(session.transaction is OTransactionNoTx) { "The session must not have a transaction" } + check(session.activeTxCount() == 0) { "The session must not have a transaction" } try { session.begin() // initialize transaction id @@ -189,7 +194,7 @@ class OStoreTransactionImpl( } private fun cleanUpTxIfNeeded() { - if (session.status == ODatabase.STATUS.OPEN && session.transaction.status == TXSTATUS.INVALID) { + if (session.status == ODatabaseSession.STATUS.OPEN && session.activeTxCount() == 0) { onFinished(session, this) } } @@ -233,8 +238,12 @@ class OStoreTransactionImpl( if (oId == ORIDEntityId.EMPTY_ID) { throw EntityRemovedInDatabaseException(oId.getTypeName(), id) } - val vertex: OVertex = session.load(oId.asOId()) ?: throw EntityRemovedInDatabaseException(oId.getTypeName(), id) - return OVertexEntity(vertex, store) + try { + val vertex: OVertex = session.load(oId.asOId()) + return OVertexEntity(vertex, store) + } catch (e: ORecordNotFoundException) { + throw EntityRemovedInDatabaseException(oId.getTypeName(), id) + } } override fun getEntityTypes(): MutableList { @@ -252,7 +261,11 @@ class OStoreTransactionImpl( return OSingleEntityIterable(this, entity) } - override fun find(entityType: String, propertyName: String, value: Comparable): EntityIterable { + override fun find( + entityType: String, + propertyName: String, + value: Comparable + ): EntityIterable { requireActiveTransaction() return OPropertyEqualIterable(this, entityType, propertyName, value) } @@ -277,7 +290,11 @@ class OStoreTransactionImpl( return OPropertyContainsIterable(this, entityType, propertyName, value) } - override fun findStartingWith(entityType: String, propertyName: String, value: String): EntityIterable { + override fun findStartingWith( + entityType: String, + propertyName: String, + value: String + ): EntityIterable { requireActiveTransaction() return OPropertyStartsWithIterable(this, entityType, propertyName, value) } @@ -298,7 +315,10 @@ class OStoreTransactionImpl( return OPropertyExistsIterable(this, entityType, propertyName) } - override fun findWithPropSortedByValue(entityType: String, propertyName: String): EntityIterable { + override fun findWithPropSortedByValue( + entityType: String, + propertyName: String + ): EntityIterable { requireActiveTransaction() return OPropertyExistsSortedIterable(this, entityType, propertyName) } @@ -313,7 +333,11 @@ class OStoreTransactionImpl( return OLinkOfTypeToEntityIterable(this, linkName, entity.id as OEntityId, entityType) } - override fun findLinks(entityType: String, entities: EntityIterable, linkName: String): EntityIterable { + override fun findLinks( + entityType: String, + entities: EntityIterable, + linkName: String + ): EntityIterable { requireActiveTransaction() return OLinkIterableToEntityIterable(this, entities.asOQueryIterable(), linkName) } @@ -333,7 +357,11 @@ class OStoreTransactionImpl( return OLinkExistsEntityIterable(this, entityType, linkName) } - override fun sort(entityType: String, propertyName: String, ascending: Boolean): EntityIterable { + override fun sort( + entityType: String, + propertyName: String, + ascending: Boolean + ): EntityIterable { requireActiveTransaction() return OPropertySortedIterable(this, entityType, propertyName, null, ascending) } @@ -345,7 +373,13 @@ class OStoreTransactionImpl( ascending: Boolean ): EntityIterable { requireActiveTransaction() - return OPropertySortedIterable(this, entityType, propertyName, rightOrder.asOQueryIterable(), ascending) + return OPropertySortedIterable( + this, + entityType, + propertyName, + rightOrder.asOQueryIterable(), + ascending + ) } override fun sortLinks( @@ -356,7 +390,12 @@ class OStoreTransactionImpl( rightOrder: EntityIterable ): EntityIterable { requireActiveTransaction() - return OLinkSortEntityIterable(this, sortedLinks.asOQueryIterable(), linkName, rightOrder.asOQueryIterable()) + return OLinkSortEntityIterable( + this, + sortedLinks.asOQueryIterable(), + linkName, + rightOrder.asOQueryIterable() + ) } override fun sortLinks( @@ -370,11 +409,19 @@ class OStoreTransactionImpl( ): EntityIterable { requireActiveTransaction() // Not sure about skipping oppositeEntityType and oppositeLinkName values - return OLinkSortEntityIterable(this, sortedLinks.asOQueryIterable(), linkName, rightOrder.asOQueryIterable()) + return OLinkSortEntityIterable( + this, + sortedLinks.asOQueryIterable(), + linkName, + rightOrder.asOQueryIterable() + ) } @Deprecated("Deprecated in Java") - override fun mergeSorted(sorted: MutableList, comparator: Comparator): EntityIterable { + override fun mergeSorted( + sorted: MutableList, + comparator: Comparator + ): EntityIterable { throw UnsupportedOperationException("Not implemented") } @@ -418,7 +465,11 @@ class OStoreTransactionImpl( schemaBuddy.renameOClass(session, oldName, newName) } - override fun getOrCreateEdgeClass(linkName: String, outClassName: String, inClassName: String): OClass { + override fun getOrCreateEdgeClass( + linkName: String, + outClassName: String, + inClassName: String + ): OClass { requireActiveTransaction() return schemaBuddy.getOrCreateEdgeClass(session, linkName, outClassName, inClassName) } diff --git a/entity-store/src/main/kotlin/jetbrains/exodus/entitystore/orientdb/OVertexEntity.kt b/entity-store/src/main/kotlin/jetbrains/exodus/entitystore/orientdb/OVertexEntity.kt index a128ceaf7..27cc7db12 100644 --- a/entity-store/src/main/kotlin/jetbrains/exodus/entitystore/orientdb/OVertexEntity.kt +++ b/entity-store/src/main/kotlin/jetbrains/exodus/entitystore/orientdb/OVertexEntity.kt @@ -22,12 +22,14 @@ import com.orientechnologies.orient.core.id.ORecordId import com.orientechnologies.orient.core.metadata.schema.OClass import com.orientechnologies.orient.core.record.ODirection import com.orientechnologies.orient.core.record.OEdge +import com.orientechnologies.orient.core.record.ORecordAbstract import com.orientechnologies.orient.core.record.OVertex import com.orientechnologies.orient.core.record.impl.ORecordBytes import jetbrains.exodus.ByteIterable import jetbrains.exodus.entitystore.Entity import jetbrains.exodus.entitystore.EntityId import jetbrains.exodus.entitystore.EntityIterable +import jetbrains.exodus.entitystore.EntityRemovedInDatabaseException import jetbrains.exodus.entitystore.orientdb.OVertexEntity.Companion.CLASS_ID_CUSTOM_PROPERTY_NAME import jetbrains.exodus.entitystore.orientdb.OVertexEntity.Companion.LOCAL_ENTITY_ID_PROPERTY_NAME import jetbrains.exodus.entitystore.orientdb.OVertexEntity.Companion.linkTargetEntityIdPropertyName @@ -48,8 +50,11 @@ open class OVertexEntity(vertex: OVertex, private val store: OEntityStore) : OEn private const val LINK_TARGET_ENTITY_ID_PROPERTY_NAME_SUFFIX = "_targetEntityId" private const val BLOB_SIZE_PROPERTY_NAME_SUFFIX = "_blob_size" private const val STRING_BLOB_HASH_PROPERTY_NAME_SUFFIX = "_string_blob_hash" - fun blobSizeProperty(propertyName: String) = "\$$propertyName$BLOB_SIZE_PROPERTY_NAME_SUFFIX" - fun blobHashProperty(propertyName: String) = "\$$propertyName$STRING_BLOB_HASH_PROPERTY_NAME_SUFFIX" + fun blobSizeProperty(propertyName: String) = + "\$$propertyName$BLOB_SIZE_PROPERTY_NAME_SUFFIX" + + fun blobHashProperty(propertyName: String) = + "\$$propertyName$STRING_BLOB_HASH_PROPERTY_NAME_SUFFIX" // Backward compatible EntityId @@ -64,7 +69,8 @@ open class OVertexEntity(vertex: OVertex, private val store: OEntityStore) : OEn STRING_BLOB_HASH_PROPERTY_NAME_SUFFIX ) - fun localEntityIdSequenceName(className: String): String = "${className}_sequence_localEntityId" + fun localEntityIdSequenceName(className: String): String = + "${className}_sequence_localEntityId" fun edgeClassName(className: String): String { // YouTrack has fancy link names like '__CUSTOM_FIELD__Country/Region_227'. OrientDB does not like symbols @@ -125,9 +131,11 @@ open class OVertexEntity(vertex: OVertex, private val store: OEntityStore) : OEn val clusterId = vertexRecord.identity.clusterId vertexRecord.identity.reset() - vertexRecord.resetToNew() - (vertexRecord.identity as ORecordId).clusterId = clusterId + (vertexRecord as ORecordAbstract).also { + resetToNew() + (it.identity as ORecordId).clusterId = clusterId + } } override fun generateId() { @@ -301,8 +309,12 @@ open class OVertexEntity(vertex: OVertex, private val store: OEntityStore) : OEn if (targetOId == ORIDEntityId.EMPTY_ID) { return false } - val target = currentTx.getRecord(targetOId) ?: return false - return currentTx.addLinkImpl(linkName, target) + try { + val target = currentTx.getRecord(targetOId) + return currentTx.addLinkImpl(linkName, target) + } catch (e: EntityRemovedInDatabaseException) { + return false + } } private fun OStoreTransaction.addLinkImpl(linkName: String, target: OVertex): Boolean { @@ -323,9 +335,10 @@ open class OVertexEntity(vertex: OVertex, private val store: OEntityStore) : OEn Well, during the data migration process, there are no any indices and skipping this findEdge(...) call is exactly what we need. */ - val currentEdge: OEdge? = if (edgeClass.areIndexed(OEdge.DIRECTION_IN, OEdge.DIRECTION_OUT)) { - findEdge(edgeClassName, target.identity) - } else null + val currentEdge: OEdge? = + if (edgeClass.areIndexed(OEdge.DIRECTION_IN, OEdge.DIRECTION_OUT)) { + findEdge(edgeClassName, target.identity) + } else null if (currentEdge == null) { vertex.addEdge(target, edgeClassName) @@ -419,8 +432,12 @@ open class OVertexEntity(vertex: OVertex, private val store: OEntityStore) : OEn if (targetOId == ORIDEntityId.EMPTY_ID) { return false } - val target = currentTx.getRecord(targetOId) ?: return false - return currentTx.setLinkImpl(linkName, target) + try { + val target = currentTx.getRecord(targetOId) + return currentTx.setLinkImpl(linkName, target) + } catch (e: EntityRemovedInDatabaseException) { + return false + } } private fun OStoreTransaction.setLinkImpl(linkName: String, target: OVertex?): Boolean { @@ -479,13 +496,15 @@ open class OVertexEntity(vertex: OVertex, private val store: OEntityStore) : OEn override fun getLinkNames(): List { requireActiveTx() - return ArrayList(vertex.getEdgeNames(ODirection.OUT) - .filter { it.endsWith(EDGE_CLASS_SUFFIX) } - .map { it.substringBefore(EDGE_CLASS_SUFFIX) }) + return ArrayList( + vertex.getEdgeNames(ODirection.OUT) + .filter { it.endsWith(EDGE_CLASS_SUFFIX) } + .map { it.substringBefore(EDGE_CLASS_SUFFIX) }) } private fun OStoreTransaction.findEdge(edgeClassName: String, targetId: ORID): OEdge? { - val query = "SELECT FROM $edgeClassName WHERE ${OEdge.DIRECTION_OUT} = :outId AND ${OEdge.DIRECTION_IN} = :inId" + val query = + "SELECT FROM $edgeClassName WHERE ${OEdge.DIRECTION_OUT} = :outId AND ${OEdge.DIRECTION_IN} = :inId" val result = query(query, mapOf("outId" to vertex.identity, "inId" to targetId)) val foundEdge = result.edgeStream().findFirst() return foundEdge.getOrNull() diff --git a/entity-store/src/test/kotlin/jetbrains/exodus/entitystore/orientdb/OSchemaBuddyTest.kt b/entity-store/src/test/kotlin/jetbrains/exodus/entitystore/orientdb/OSchemaBuddyTest.kt index 43ceaf7b8..74f21d260 100644 --- a/entity-store/src/test/kotlin/jetbrains/exodus/entitystore/orientdb/OSchemaBuddyTest.kt +++ b/entity-store/src/test/kotlin/jetbrains/exodus/entitystore/orientdb/OSchemaBuddyTest.kt @@ -143,7 +143,7 @@ class OSchemaBuddyTest: OTestMixin { // the changes made in the transaction are still there withSession { session -> - assertNotNull(session.getRecord(issId)) + assertNotNull(session.loadVertex(issId)) } } diff --git a/entity-store/src/test/kotlin/jetbrains/exodus/entitystore/orientdb/OStoreTransactionTest.kt b/entity-store/src/test/kotlin/jetbrains/exodus/entitystore/orientdb/OStoreTransactionTest.kt index 838fec325..3ab0e77b2 100644 --- a/entity-store/src/test/kotlin/jetbrains/exodus/entitystore/orientdb/OStoreTransactionTest.kt +++ b/entity-store/src/test/kotlin/jetbrains/exodus/entitystore/orientdb/OStoreTransactionTest.kt @@ -17,8 +17,10 @@ package jetbrains.exodus.entitystore.orientdb import com.google.common.truth.Truth.assertThat import com.orientechnologies.orient.core.db.ODatabaseSession +import com.orientechnologies.orient.core.db.ODatabaseSessionInternal import com.orientechnologies.orient.core.exception.ODatabaseException import com.orientechnologies.orient.core.record.ODirection +import com.orientechnologies.orient.core.record.ORecord import com.orientechnologies.orient.core.record.OVertex import jetbrains.exodus.entitystore.EntityRemovedInDatabaseException import jetbrains.exodus.entitystore.PersistentEntityId @@ -34,7 +36,6 @@ import org.junit.Rule import org.junit.Test import kotlin.test.assertEquals import kotlin.test.assertFailsWith -import kotlin.test.assertNull class OStoreTransactionTest : OTestMixin { @@ -82,10 +83,20 @@ class OStoreTransactionTest : OTestMixin { } withStoreTx { - Assert.assertEquals(1, it.findLinks(Boards.CLASS, test.issue1, Boards.Links.HAS_ISSUE).size()) - Assert.assertEquals(1, it.findLinks(Projects.CLASS, test.issue1, Boards.Links.HAS_ISSUE).size()) - Assert.assertEquals(2, - test.issue1.vertex.getEdges(ODirection.IN, OVertexEntity.edgeClassName(Boards.Links.HAS_ISSUE)) + Assert.assertEquals( + 1, + it.findLinks(Boards.CLASS, test.issue1, Boards.Links.HAS_ISSUE).size() + ) + Assert.assertEquals( + 1, + it.findLinks(Projects.CLASS, test.issue1, Boards.Links.HAS_ISSUE).size() + ) + Assert.assertEquals( + 2, + test.issue1.vertex.getEdges( + ODirection.IN, + OVertexEntity.edgeClassName(Boards.Links.HAS_ISSUE) + ) .toList().size ) } @@ -355,8 +366,10 @@ class OStoreTransactionTest : OTestMixin { // When withStoreTx { tx -> // Find all issues that in project1 or project2 - val issuesInProject1 = tx.findLinks(Issues.CLASS, testCase.project1, Issues.Links.IN_PROJECT) - val issuesInProject2 = tx.findLinks(Issues.CLASS, testCase.project2, Issues.Links.IN_PROJECT) + val issuesInProject1 = + tx.findLinks(Issues.CLASS, testCase.project1, Issues.Links.IN_PROJECT) + val issuesInProject2 = + tx.findLinks(Issues.CLASS, testCase.project2, Issues.Links.IN_PROJECT) val issues = issuesInProject1.union(issuesInProject2) // Then @@ -694,10 +707,16 @@ class OStoreTransactionTest : OTestMixin { } withStoreTx { tx -> - val boards = OEntityOfTypeIterable(tx, Issues.CLASS).selectManyDistinct(Issues.Links.ON_BOARD).toList() + val boards = + OEntityOfTypeIterable(tx, Issues.CLASS).selectManyDistinct(Issues.Links.ON_BOARD) + .toList() //selectManyDistinct Assert.assertEquals(2, boards.size) - Assert.assertEquals("Should not contain nulls", 0, boards.filter { board -> board == null }.size) + Assert.assertEquals( + "Should not contain nulls", + 0, + boards.filter { board -> board == null }.size + ) } } @@ -732,7 +751,8 @@ class OStoreTransactionTest : OTestMixin { @Test fun `transactionId does not get changed on flush()`() { withStoreTx { tx -> - val oTransactionId = ODatabaseSession.getActiveSession().transaction.id.toLong() + val oTransactionId = + (ODatabaseSession.getActiveSession() as ODatabaseSessionInternal).transaction.id.toLong() assertEquals(oTransactionId, tx.getTransactionId()) tx.flush() assertEquals(oTransactionId, tx.getTransactionId()) @@ -802,7 +822,7 @@ class OStoreTransactionTest : OTestMixin { } @Test - fun `getRecord()` () { + fun `getRecord()`() { val id = withStoreTx { tx -> val e1 = tx.createIssue("opca trista") e1.setProperty("mamba", "caramba") @@ -817,7 +837,12 @@ class OStoreTransactionTest : OTestMixin { } withStoreTx { tx -> - assertNull(tx.getRecord(id)) + try { + tx.getRecord(id) + Assert.fail() + } catch (e: EntityRemovedInDatabaseException) { + // expected + } } } } \ No newline at end of file diff --git a/entity-store/src/test/kotlin/jetbrains/exodus/entitystore/orientdb/OTransactionLifecycleTest.kt b/entity-store/src/test/kotlin/jetbrains/exodus/entitystore/orientdb/OTransactionLifecycleTest.kt index d4e67e656..d8042a0b4 100644 --- a/entity-store/src/test/kotlin/jetbrains/exodus/entitystore/orientdb/OTransactionLifecycleTest.kt +++ b/entity-store/src/test/kotlin/jetbrains/exodus/entitystore/orientdb/OTransactionLifecycleTest.kt @@ -16,11 +16,13 @@ package jetbrains.exodus.entitystore.orientdb import com.orientechnologies.common.concur.lock.OModificationOperationProhibitedException -import com.orientechnologies.orient.core.db.ODatabase.STATUS -import com.orientechnologies.orient.core.db.ODatabaseDocumentInternal +import com.orientechnologies.orient.core.db.ODatabaseSession.STATUS +import com.orientechnologies.orient.core.db.ODatabaseSessionInternal import com.orientechnologies.orient.core.exception.ODatabaseException +import com.orientechnologies.orient.core.exception.ORecordNotFoundException import com.orientechnologies.orient.core.metadata.schema.OClass import com.orientechnologies.orient.core.metadata.schema.OType +import com.orientechnologies.orient.core.record.ORecord import com.orientechnologies.orient.core.record.OVertex import com.orientechnologies.orient.core.storage.ORecordDuplicatedException import com.orientechnologies.orient.core.tx.OTransaction.TXSTATUS @@ -28,6 +30,7 @@ import com.orientechnologies.orient.core.tx.OTransactionNoTx import jetbrains.exodus.entitystore.orientdb.testutil.InMemoryOrientDB import jetbrains.exodus.entitystore.orientdb.testutil.OTestMixin import junit.framework.TestCase.assertFalse +import org.junit.Assert import org.junit.Rule import org.junit.Test import kotlin.test.* @@ -63,7 +66,7 @@ class OTransactionLifecycleTest : OTestMixin { @Test fun `open, begin, commit, close - no changes`() { - val session = orientDb.openSession() + val session = orientDb.openSession() as ODatabaseSessionInternal assertFalse(session.transaction == null) assertEquals(STATUS.OPEN, session.status) @@ -86,7 +89,7 @@ class OTransactionLifecycleTest : OTestMixin { @Test fun `open, begin, commit, close - changes`() { - val session = orientDb.openSession() + val session = orientDb.openSession() as ODatabaseSessionInternal val oClass = session.getOrCreateVertexClass("trista") assertFalse(session.transaction == null) @@ -105,7 +108,7 @@ class OTransactionLifecycleTest : OTestMixin { assertTrue(session.isActiveOnCurrentThread) session.begin() - assertNotNull(session.getRecord(trista.identity)) + session.load(trista.identity) session.commit() session.close() @@ -115,7 +118,7 @@ class OTransactionLifecycleTest : OTestMixin { @Test fun `open, begin, rollback, close - no changes`() { - val session = orientDb.openSession() + val session = orientDb.openSession() as ODatabaseSessionInternal assertFalse(session.transaction == null) @@ -136,7 +139,7 @@ class OTransactionLifecycleTest : OTestMixin { @Test fun `open, begin, rollback, close - changes`() { - val session = orientDb.openSession() + val session = orientDb.openSession() as ODatabaseSessionInternal val oClass = session.getOrCreateVertexClass("trista") assertFalse(session.transaction == null) @@ -155,7 +158,13 @@ class OTransactionLifecycleTest : OTestMixin { assertTrue(session.isActiveOnCurrentThread) session.begin() - assertNull(session.getRecord(trista.identity)) + try { + session.load(trista.identity) + Assert.fail() + } catch (e: ORecordNotFoundException) { + // expected + } + session.commit() session.close() @@ -195,7 +204,7 @@ class OTransactionLifecycleTest : OTestMixin { @Test fun `rollback() does NOT throw an exception if there is no active transaction`() { - val session = orientDb.openSession() + val session = orientDb.openSession() as ODatabaseSessionInternal assertFalse(session.hasActiveTransaction()) @@ -220,7 +229,7 @@ class OTransactionLifecycleTest : OTestMixin { @Test fun `if commit() fails, changes get rolled back`() { - val session = orientDb.openSession() + val session = orientDb.openSession() as ODatabaseSessionInternal val oClass = session.getOrCreateVertexClass("trista") oClass.createProperty("name", OType.STRING) oClass.createIndex("idx_name", OClass.INDEX_TYPE.UNIQUE, "name") @@ -243,15 +252,26 @@ class OTransactionLifecycleTest : OTestMixin { assertTrue(session.isActiveOnCurrentThread) session.begin() - assertNull(session.getRecord(trista1.identity)) - assertNull(session.getRecord(trista2.identity)) + try { + session.load(trista1.identity) + Assert.fail() + } catch (_: ORecordNotFoundException) { + + } + + try { + session.load(trista2.identity) + Assert.fail() + } catch (_: ORecordNotFoundException) { + + } session.commit() session.close() } @Test fun `embedded transactions successful case`() { - val session = orientDb.openSession() + val session = orientDb.openSession() as ODatabaseSessionInternal session.getOrCreateVertexClass("trista") assertEquals(TXSTATUS.INVALID, session.transaction.status) assertEquals(0, session.transaction.amountOfNestedTxs()) @@ -296,15 +316,15 @@ class OTransactionLifecycleTest : OTestMixin { session.begin() assertNotEquals(tx1, session.transaction) - assertNotNull(session.getRecord(trista1.identity)) - assertNotNull(session.getRecord(trista2.identity)) + session.load(trista1.identity) + session.load(trista2.identity) session.commit() session.close() } @Test fun `rollback(force = true) on an embedded transaction rolls back all the transactions`() { - val session = orientDb.openSession() + val session = orientDb.openSession() as ODatabaseSessionInternal session.getOrCreateVertexClass("trista") session.begin() // tx1 @@ -325,21 +345,31 @@ class OTransactionLifecycleTest : OTestMixin { trista2.setProperty("name", "sth") trista2.save() - (session as ODatabaseDocumentInternal).rollback(true) + session.rollback(true) assertEquals(TXSTATUS.INVALID, session.transaction.status) assertEquals(0, session.transaction.amountOfNestedTxs()) session.begin() - assertNull(session.getRecord(trista1.identity)) - assertNull(session.getRecord(trista2.identity)) + try { + session.loadVertex(trista1.identity) + Assert.fail() + } catch (e: ORecordNotFoundException) { + // expected + } + try { + session.loadVertex(trista2.identity) + Assert.fail() + } catch (e: ORecordNotFoundException) { + // expected + } session.commit() session.close() } @Test fun `rollback() on an embedded transaction decreases amountOfNestedTxs by 1`() { - val session = orientDb.openSession() + val session = orientDb.openSession() as ODatabaseSessionInternal session.getOrCreateVertexClass("trista") session.begin() // tx1 @@ -371,8 +401,18 @@ class OTransactionLifecycleTest : OTestMixin { assertEquals(0, session.transaction.amountOfNestedTxs()) session.begin() - assertNull(session.getRecord(trista1.identity)) - assertNull(session.getRecord(trista2.identity)) + try { + session.loadVertex(trista1.identity) + Assert.fail() + } catch (e: ORecordNotFoundException) { + // expected + } + try { + session.loadVertex(trista2.identity) + Assert.fail() + } catch (e: ORecordNotFoundException) { + // expected + } session.commit() session.close() } @@ -403,8 +443,19 @@ class OTransactionLifecycleTest : OTestMixin { assertFalse(session.hasActiveTransaction()) session.begin() - assertNull(session.getRecord(trista1.identity)) - assertNull(session.getRecord(trista2.identity)) + try { + session.load(trista1.identity) + Assert.fail() + } catch (e: ORecordNotFoundException) { + // expected + } + + try { + session.load(trista2.identity) + Assert.fail() + } catch (e: ORecordNotFoundException) { + // expected + } session.commit() session.close() } diff --git a/query/src/test/kotlin/jetbrains/exodus/query/metadata/MigrateDataTest.kt b/query/src/test/kotlin/jetbrains/exodus/query/metadata/MigrateDataTest.kt index 3d7983dd2..f27894d91 100644 --- a/query/src/test/kotlin/jetbrains/exodus/query/metadata/MigrateDataTest.kt +++ b/query/src/test/kotlin/jetbrains/exodus/query/metadata/MigrateDataTest.kt @@ -157,9 +157,9 @@ class MigrateDataTest { } orientDb.withTxSession { session -> - val e1 = session.getRecord(id) - val blob = e1.getProperty("blob1") - val gotBytes = blob.toStream() + val e1 = session.loadVertex(id) + val blob = e1.getBlobProperty("blob1") + val gotBytes = blob!!.toStream() message.append(", got ${gotBytes.size} from the database") if (!gotBytes.contentEquals(bytes)) { brokenSizes.add(message.toString()) @@ -528,7 +528,7 @@ internal fun OVertexEntity.assertEquals(expected: Entity) { internal fun OVertexEntity.getTestId(): Int = getProperty("id") as Int -internal fun OVertexDocument.getTestId(): Int = getProperty("id") as Int +internal fun OVertexDocument.getTestId(): Int = getProperty("id") as Int internal fun StoreTransaction.createEntities(pile: PileOfEntities) { for (type in pile.types) { diff --git a/query/src/test/kotlin/jetbrains/exodus/query/metadata/OModelMetaDataTest.kt b/query/src/test/kotlin/jetbrains/exodus/query/metadata/OModelMetaDataTest.kt index a4ce078b6..977d84e47 100644 --- a/query/src/test/kotlin/jetbrains/exodus/query/metadata/OModelMetaDataTest.kt +++ b/query/src/test/kotlin/jetbrains/exodus/query/metadata/OModelMetaDataTest.kt @@ -29,7 +29,7 @@ import kotlin.test.assertFailsWith import kotlin.test.assertFalse import kotlin.test.assertTrue -class OModelMetaDataTest: OTestMixin { +class OModelMetaDataTest : OTestMixin { @Rule @JvmField val orientDbRule = InMemoryOrientDB(initializeIssueSchema = false) @@ -123,9 +123,21 @@ class OModelMetaDataTest: OTestMixin { } assertFailsWith { model.addAssociation( - "type2", "type1", AssociationType.Directed, "ass1", AssociationEndCardinality._1, - false, false, false, false, null, - null, false, false, false, false + "type2", + "type1", + AssociationType.Directed, + "ass1", + AssociationEndCardinality._1, + false, + false, + false, + false, + null, + null, + false, + false, + false, + false ) } assertFailsWith { @@ -168,9 +180,10 @@ class OModelMetaDataTest: OTestMixin { @Test fun `prepare() initializes the classId map`() { - val model = oModel(orientDb.provider, OSchemaBuddyImpl(orientDb.provider, autoInitialize = false)) { - entity("type1") - } + val model = + oModel(orientDb.provider, OSchemaBuddyImpl(orientDb.provider, autoInitialize = false)) { + entity("type1") + } // We have not yet called prepare() for the model, autoInitialize is disabled orientDb.provider.acquireSession().use { @@ -242,9 +255,9 @@ class OModelMetaDataTest: OTestMixin { // prepare() must have called initializeComplementaryPropertiesForNewIndexedLinks orientDb.withTxSession { session -> - val v11 = session.getRecord(id11) - val v12 = session.getRecord(id12) - val v21 = session.getRecord(id21) + val v11 = session.loadVertex(id11) + val v12 = session.loadVertex(id12) + val v21 = session.loadVertex(id21) val bag11 = v11.getTargetLocalEntityIds("ass1") val bag21 = v21.getTargetLocalEntityIds("ass2") diff --git a/query/src/test/kotlin/jetbrains/exodus/query/metadata/OrientDbSchemaInitializerLinkIndicesTest.kt b/query/src/test/kotlin/jetbrains/exodus/query/metadata/OrientDbSchemaInitializerLinkIndicesTest.kt index df4c0641d..b20433827 100644 --- a/query/src/test/kotlin/jetbrains/exodus/query/metadata/OrientDbSchemaInitializerLinkIndicesTest.kt +++ b/query/src/test/kotlin/jetbrains/exodus/query/metadata/OrientDbSchemaInitializerLinkIndicesTest.kt @@ -25,6 +25,7 @@ import jetbrains.exodus.entitystore.orientdb.testutil.InMemoryOrientDB import org.junit.Assert.assertEquals import org.junit.Rule import org.junit.Test +import kotlin.streams.toList import kotlin.test.assertFailsWith import kotlin.test.assertFalse import kotlin.test.assertTrue @@ -50,7 +51,10 @@ class OrientDbSchemaInitializerLinkIndicesTest { entity("type1") { property("prop1", "int") index(IndexedField("indexedAss1", isProperty = false)) - index(IndexedField("prop1", isProperty = true), IndexedField("indexedAss2", isProperty = false)) + index( + IndexedField("prop1", isProperty = true), + IndexedField("indexedAss2", isProperty = false) + ) } association("type1", "indexedAss1", "type2", AssociationEndCardinality._0_n) association("type1", "indexedAss2", "type2", AssociationEndCardinality._0_n) @@ -128,9 +132,9 @@ class OrientDbSchemaInitializerLinkIndicesTest { } orientDb.withTxSession { session -> - val v11 = session.getRecord(id11) - val v12 = session.getRecord(id12) - val v21 = session.getRecord(id21) + val v11 = session.loadVertex(id11) + val v12 = session.loadVertex(id12) + val v21 = session.loadVertex(id21) val bag11 = v11.getTargetLocalEntityIds("ass1") val bag21 = v21.getTargetLocalEntityIds("ass2") @@ -180,8 +184,8 @@ class OrientDbSchemaInitializerLinkIndicesTest { // ({ v3 }) == ({ v3 }) assertFailsWith { orientDb.withTxSession { oSession -> - val v1 = oSession.getRecord(id1) - val v3 = oSession.getRecord(id3) + val v1 = oSession.loadVertex(id1) + val v3 = oSession.loadVertex(id3) v1.addIndexedEdge("ass1", v3) } @@ -190,9 +194,9 @@ class OrientDbSchemaInitializerLinkIndicesTest { // ({ v2, v3 }) == ({ v3 }) assertFailsWith { orientDb.withTxSession { oSession -> - val v1 = oSession.getRecord(id1) - val v2 = oSession.getRecord(id2) - val v3 = oSession.getRecord(id3) + val v1 = oSession.loadVertex(id1) + val v2 = oSession.loadVertex(id2) + val v3 = oSession.loadVertex(id3) v1.addIndexedEdge("ass1", v2) v2.addIndexedEdge("ass1", v3) @@ -201,9 +205,9 @@ class OrientDbSchemaInitializerLinkIndicesTest { // ({ v2 }) != ({ v3 }) orientDb.withTxSession { oSession -> - val v1 = oSession.getRecord(id1) - val v2 = oSession.getRecord(id2) - val v3 = oSession.getRecord(id3) + val v1 = oSession.loadVertex(id1) + val v2 = oSession.loadVertex(id2) + val v3 = oSession.loadVertex(id3) v1.deleteIndexedEdge("ass1", v3) v2.addIndexedEdge("ass1", v3) @@ -216,7 +220,10 @@ class OrientDbSchemaInitializerLinkIndicesTest { entity("type2") entity("type1") { property("prop1", "int") - index(IndexedField("prop1", isProperty = true), IndexedField("ass1", isProperty = false)) + index( + IndexedField("prop1", isProperty = true), + IndexedField("ass1", isProperty = false) + ) } association("type1", "ass1", "type2", AssociationEndCardinality._0_n) } @@ -269,8 +276,8 @@ class OrientDbSchemaInitializerLinkIndicesTest { // (1, { v3 } ) == (1, { v3 } ) assertFailsWith { orientDb.withTxSession { oSession -> - val v2 = oSession.getRecord(id2) - val v3 = oSession.getRecord(id3) + val v2 = oSession.loadVertex(id2) + val v3 = oSession.loadVertex(id3) v2.addIndexedEdge("ass1", v3) } @@ -278,8 +285,8 @@ class OrientDbSchemaInitializerLinkIndicesTest { // (1, { v2, v3 } ) != (1, no links) orientDb.withTxSession { oSession -> - val v1 = oSession.getRecord(id1) - val v2 = oSession.getRecord(id2) + val v1 = oSession.loadVertex(id1) + val v2 = oSession.loadVertex(id2) v1.addIndexedEdge("ass1", v2) } @@ -287,8 +294,8 @@ class OrientDbSchemaInitializerLinkIndicesTest { // (1, { v2, v3 } ) == (1, { v3 } ), who could think... assertFailsWith { orientDb.withTxSession { oSession -> - val v2 = oSession.getRecord(id2) - val v3 = oSession.getRecord(id3) + val v2 = oSession.loadVertex(id2) + val v3 = oSession.loadVertex(id3) v2.addIndexedEdge("ass1", v3) } @@ -301,7 +308,10 @@ class OrientDbSchemaInitializerLinkIndicesTest { entity("type2") entity("type1") { property("prop1", "int") - index(IndexedField("prop1", isProperty = true), IndexedField("ass1", isProperty = false)) + index( + IndexedField("prop1", isProperty = true), + IndexedField("ass1", isProperty = false) + ) } association("type1", "ass1", "type2", AssociationEndCardinality._0_n) } @@ -326,9 +336,9 @@ class OrientDbSchemaInitializerLinkIndicesTest { // (1, no links) != (1, { v3 }) orientDb.withTxSession { oSession -> - val v1 = oSession.getRecord(id1) - val v2 = oSession.getRecord(id2) - val v3 = oSession.getRecord(id3) + val v1 = oSession.loadVertex(id1) + val v2 = oSession.loadVertex(id2) + val v3 = oSession.loadVertex(id3) v1.deleteIndexedEdge("ass1", v3) v2.addIndexedEdge("ass1", v3) @@ -341,7 +351,10 @@ class OrientDbSchemaInitializerLinkIndicesTest { entity("type2") entity("type1") { property("prop1", "int") - index(IndexedField("prop1", isProperty = true), IndexedField("ass1", isProperty = false)) + index( + IndexedField("prop1", isProperty = true), + IndexedField("ass1", isProperty = false) + ) } association("type1", "ass1", "type2", AssociationEndCardinality._0_n) } @@ -453,7 +466,8 @@ class OrientDbSchemaInitializerLinkIndicesTest { orientDb.withTxSession { oSession -> val v1 = - oSession.query("select from type1").vertexStream().toList().first { it.getProperty("prop1") == 1 } + oSession.query("select from type1").vertexStream().toList() + .first { it.getProperty("prop1") == 1 } val links: MutableIterable = v1.getVertices(ODirection.OUT, edgeClassName) assertEquals(1, links.count()) }