From ddd8bc8a2672a13ad35a09136fb9423594f728f9 Mon Sep 17 00:00:00 2001 From: Valentyn Sobol Date: Fri, 18 Oct 2024 13:24:37 +0300 Subject: [PATCH] Extract jacodb-storage module [jacodb-storage] Extract jacodb-api-storage module The jacodb-api-storage module depends on jacodb-api-common. The jacodb-api-jvm module depends on jacodb-api-common & jacodb-api-storage. [jacodb-storage] Fix review points: remove EntityRelationshipStorage.asReadWrite [jacodb-storage] Fix review points: setImmutable() waits for background jobs [jacodb-storage] Minor: remove some comments [jacodb-storage] Minor: rename [jacodb-api, jacodb-core] Add ability to set persistence and database to immutable state Got rid of the JcDatabase.classpathOf() method. [jacodb-storage] Add navigation over AttributesImmutable In implementation of AttributesImmutable, AttributesCursor added for navigation in attribute values. It is used in PropertiesImmutable for searching by property value and for searching in a range of values. AttributesCursorEntityIterable added as a lazy implementation of EntityIterable atop of AttributesCursor. More test cases added. [jacodb-storage] Generate and add SparseBitSetTest [jacodb-core] Extract ERS API implementations to module jacodb-storage All EntityRelationshipStorage implementations, except SqlEntityRelationshipStorageSPI, moved to the jacodb-storage module. Extra minor refactoring are done in order to de-couple dependencies. [jacodb-core] Get rid of unnecessary extra check if instanceIds are sorted [jacodb-core] Add first version of RAMDataContainerImmutable RAMDataContainerImmutable is intended as a data container for RAMEntityRelationshipStorage saving data in a compact format with high data locality using large arrays. Auxiliary refactorings: - EntityIdSet is obsolete; - EntityIdCollectionEntityIterable is obsolete, as it is replaced with InstanceIdCollectionEntityIterable everywhere; - SparseBitSet added for compact bitsets; - ByteArray.asComparable() added; - ERS API: Transaction.getEntityUnsafe(id) added. TODO: provide PropertiesImmutable with property value indices and use then in find*() methods. Currently, find*() methods use brute force search. [build] Add facilities to deal with read-only state of storages For EntityRelationshipStorage, added ability to get read-only & read/write snapshots of the storage. By default, they do nothing. In PluggableKeyValueStorage, added property allowing to switch to read-only mode and back to read/write mode. It has non-trivial implementation only in XodusKeyValueStorage. [build] For tests, set JVM target to that the build is being run under This change allows to run tests against examples compiled for different JVM targets whereas main artifacts are still being compiled for Java 1.8. Both Java and Kotlin configured. We have to compile test fixtures for 1.8 as well since Gradle considers them as dependencies of main artifacts. [jacodb-core] Update Kotlin to version 2.0.20 As of this commit, Kotlin metadata version is equal to Kotlin version. Refactored Kotlin metadata classes' usages as they live in the new package. Fixed nullability issues with Kotlin class types appeared due to lack of corresponding JB annotations since Kotlin 2.0.0. Extension functions declared in the Nullables.kt moved from the jacodb-api-jvm module to the jacodb-core one. [jacodb-core] Fix running benchmarks with ERS persistence using pluggable K/V storages [jacodb-core] Process Class-Path attribute of jar manifest As of this commit, classpath is not only set of jar files and build directories, but also a set of references to jar files calculated using "Class-Path" attribute in manifest of any jar file. --- build.gradle.kts | 39 +++- buildSrc/src/main/kotlin/Dependencies.kt | 12 +- buildSrc/src/main/kotlin/Tests.kt | 4 +- jacodb-analysis/build.gradle.kts | 1 + .../analysis/impl/custom/FlowAnalysisImpl.kt | 2 +- .../jacodb/analysis/npe/NpeFlowFunctions.kt | 2 +- jacodb-api-jvm/build.gradle.kts | 1 + .../src/main/kotlin/org/jacodb/api/jvm/Api.kt | 25 ++- jacodb-api-storage/build.gradle.kts | 4 + .../kotlin/org/jacodb/api}/spi/CommonSPI.kt | 2 +- .../kotlin/org/jacodb/api}/spi/SPILoader.kt | 2 +- .../org/jacodb/api}/storage/ByteArrayKey.kt | 6 +- .../org/jacodb/api}/storage/ers/Binding.kt | 2 +- .../api}/storage/ers/BindingProvider.kt | 2 +- .../org/jacodb/api}/storage/ers/Entity.kt | 2 +- .../org/jacodb/api}/storage/ers/EntityEx.kt | 2 +- .../jacodb/api}/storage/ers/EntityIterable.kt | 79 ++++++- .../api}/storage/ers/EntityIterableEx.kt | 8 +- .../storage/ers/EntityRelationshipStorage.kt | 13 +- .../jacodb/api}/storage/ers/Transaction.kt | 12 +- .../org/jacodb/api}/storage/ers/readme.md | 0 .../jacodb/api}/storage/ers/typed/ErsType.kt | 0 .../api}/storage/ers/typed/TypedErsApi.kt | 10 +- .../api}/storage/ers/typed/TypedErsImpl.kt | 10 +- .../jacodb/api}/storage/ers/typed/readme.md | 0 .../org/jacodb/api}/storage/kv/Cursor.kt | 2 +- .../storage/kv/PluggableKeyValueStorage.kt | 17 +- .../org/jacodb/api}/storage/kv/Transaction.kt | 2 +- .../org/jacodb/api}/storage/kv/readme.md | 0 jacodb-approximations/build.gradle.kts | 3 +- .../jacodb/approximation/Approximations.kt | 2 +- jacodb-benchmarks/build.gradle.kts | 4 +- jacodb-core/build.gradle.kts | 14 +- .../jacodb/impl/JCDBSymbolsInternerImpl.kt | 6 +- .../kotlin/org/jacodb/impl/JcDatabaseImpl.kt | 13 +- .../jacodb/impl/JcDatabasePersistenceSPI.kt | 4 +- .../main/kotlin/org/jacodb/impl/JcSettings.kt | 13 +- .../org/jacodb/impl/bytecode/KMetadata.kt | 12 +- .../org/jacodb/impl/bytecode}/Nullables.kt | 9 +- .../impl/caches/PluggableCacheProvider.kt | 4 +- .../org/jacodb/impl/features/Builders.kt | 2 +- .../impl/features/HierarchyExtension.kt | 10 +- .../jacodb/impl/features/InMemoryHierarchy.kt | 4 +- .../kotlin/org/jacodb/impl/features/Usages.kt | 6 +- .../features/classpaths/KotlinMetadata.kt | 12 +- .../org/jacodb/impl/fs/ByteCodeLocations.kt | 35 ++- .../kotlin/org/jacodb/impl/fs/JavaRuntime.kt | 4 +- .../impl/storage/AbstractJcDbPersistence.kt | 7 +- .../org/jacodb/impl/storage/JCDBContexts.kt | 2 +- .../storage/PersistentByteCodeLocation.kt | 8 +- .../storage/PersistentLocationsRegistry.kt | 2 +- .../impl/storage/SQLitePersistenceImpl.kt | 42 ++-- .../storage/ers/ErsDatabasePersistenceSPI.kt | 2 +- .../org/jacodb/impl/storage/ers/ErsExt.kt | 2 +- .../impl/storage/ers/ErsPersistenceImpl.kt | 23 +- .../impl/storage/ers/ram/EntityIdSet.kt | 53 ----- .../org/jacodb/impl/storage/ers/ram/Links.kt | 54 ----- .../ers/ram/XodusPersistentCollectionsEx.kt | 147 ------------ .../ers/sql/SqlEntityRelationshipStorage.kt | 8 +- .../sql/SqlEntityRelationshipStorageSPI.kt | 6 +- .../impl/storage/ers/sql/SqlErsContext.kt | 2 +- .../sql/SqlErsCustomExceptionTransformer.kt | 2 +- .../impl/storage/ers/sql/SqlErsEntity.kt | 6 +- .../storage/ers/sql/SqlErsEntityIterable.kt | 6 +- .../impl/storage/ers/sql/SqlErsTransaction.kt | 12 +- .../org/jacodb/impl/types/JcTypedFieldImpl.kt | 26 ++- .../jacodb/impl/types/JcTypedMethodImpl.kt | 2 +- .../impl/types/JcTypedMethodParameterImpl.kt | 2 +- .../JvmTypeAnnotationUpdateVisitor.kt | 4 +- .../JvmTypeKMetadataUpdateVisitor.kt | 6 +- .../jacodb/impl/types/signature/Signature.kt | 2 +- .../types/substition/JcSubstitutorImpl.kt | 4 +- ...i.storage.ers.EntityRelationshipStorageSPI | 1 + .../test/java/org/jacodb/testing/JavaApi.java | 4 - .../testing/persistence/RestoredDBTest.kt | 1 - .../ers/SqlEntityRelationshipStorageTest.kt | 2 +- .../jacodb/testing/tests/DatabaseEnvTest.kt | 2 +- jacodb-storage/build.gradle.kts | 14 ++ .../kotlin/org/jacodb/impl/JcErsSettings.kt | 29 +++ .../impl/storage/ers/BuiltInBindings.kt | 6 +- .../ers/decorators/AbstractDecorators.kt | 11 +- .../storage/ers/decorators/AllDecorators.kt | 2 +- .../impl/storage/ers/decorators/Checked.kt | 12 +- .../storage/ers/decorators/DeepDecorators.kt | 11 +- .../decorators/RecomputingEntityIterable.kt | 4 +- .../jacodb/impl/storage/ers/kv/KVEntity.kt | 12 +- .../ers/kv/KVEntityRelationshipStorage.kt | 37 +-- .../ers/kv/KVEntityRelationshipStorageSPI.kt | 8 +- .../impl/storage/ers/kv/KVErsTransaction.kt | 28 +-- .../jacodb/impl/storage/ers/kv/KVErsUtil.kt | 4 +- .../storage/ers/ram/AttributesImmutable.kt | 210 ++++++++++++++++++ .../ers/ram/CompactPersistentLongSet.kt | 4 +- .../org/jacodb/impl/storage/ers/ram/Links.kt | 162 ++++++++++++++ .../impl/storage/ers/ram/MutableContainer.kt | 3 + .../ers/ram/PackedPersistentLongSet.kt | 0 .../jacodb/impl/storage/ers/ram/Properties.kt | 115 ++++++++-- .../impl/storage/ers/ram/RAMDataContainer.kt | 92 ++++++++ .../ers/ram/RAMDataContainerImmutable.kt | 179 +++++++++++++++ .../ers/ram/RAMDataContainerMutable.kt | 172 ++++++++------ .../jacodb/impl/storage/ers/ram/RAMEntity.kt | 6 +- .../ers/ram/RAMEntityRelationshipStorage.kt | 22 +- .../ram/RAMEntityRelationshipStorageSPI.kt | 6 +- .../impl/storage/ers/ram/RAMTransaction.kt | 22 +- .../impl/storage/ers/ram/SparseBitSet.kt | 83 +++++++ .../impl/storage/ers/ram/TinyBlobsCache.kt | 2 +- .../ers/ram/TransactionalPersistentLongMap.kt | 13 +- .../ers/ram/TransactionalPersistentMap.kt | 7 +- .../ers/ram/XodusPersistentCollectionsEx.kt | 106 +++++++++ .../jacodb/impl/storage/kv/lmdb/LmdbCursor.kt | 4 +- .../org/jacodb/impl/storage/kv/lmdb/LmdbEx.kt | 0 .../storage/kv/lmdb/LmdbKeyValueStorage.kt | 6 +- .../storage/kv/lmdb/LmdbKeyValueStorageSPI.kt | 9 +- .../impl/storage/kv/lmdb/LmdbNamedMap.kt | 6 +- .../impl/storage/kv/lmdb/LmdbTransaction.kt | 11 +- .../storage/kv/rocks/ByteArrayPairUtils.kt | 0 .../impl/storage/kv/rocks/RocksCursor.kt | 8 +- .../storage/kv/rocks/RocksKeyValueStorage.kt | 4 +- .../kv/rocks/RocksKeyValueStorageSPI.kt | 6 +- .../impl/storage/kv/rocks/RocksNamedMap.kt | 10 +- .../impl/storage/kv/rocks/RocksTransaction.kt | 8 +- .../impl/storage/kv/xodus/XodusCursor.kt | 4 +- .../storage/kv/xodus/XodusEnvironmentsEx.kt | 0 .../storage/kv/xodus/XodusKeyValueStorage.kt | 10 +- .../kv/xodus/XodusKeyValueStorageSPI.kt | 6 +- .../impl/storage/kv/xodus/XodusNamedMap.kt | 4 +- .../impl/storage/kv/xodus/XodusTransaction.kt | 10 +- ...i.storage.ers.EntityRelationshipStorageSPI | 1 - ...api.storage.kv.PluggableKeyValueStorageSPI | 0 .../impl/storage/ers/ram/SparseBitSetTest.kt | 106 +++++++++ .../testing/storage/ers/BindingsTest.kt | 0 .../ers/CompactPersistentLongSetTest.kt | 0 .../LmdbKVEntityRelationshipStorageTest.kt | 0 ...MEntityRelationshipStorageImmutableTest.kt | 150 +++++++++++++ .../ers/RAMEntityRelationshipStorageTest.kt | 0 .../RocksKVEntityRelationshipStorageTest.kt | 0 .../XodusKVEntityRelationshipStorageTest.kt | 14 ++ .../storage/kv/LmdbKeyValueStorageTest.kt | 0 .../storage/kv/RocksKeyValueStorageTest.kt | 0 .../storage/kv/XodusKeyValueStorageTest.kt | 17 ++ .../ers/EntityRelationshipStorageTest.kt | 38 ++-- .../kv/PluggableKeyValueStorageTest.kt | 20 +- jacodb-taint-configuration/build.gradle.kts | 2 +- settings.gradle.kts | 2 + 143 files changed, 1939 insertions(+), 757 deletions(-) create mode 100644 jacodb-api-storage/build.gradle.kts rename {jacodb-api-jvm/src/main/kotlin/org/jacodb/api/jvm => jacodb-api-storage/src/main/kotlin/org/jacodb/api}/spi/CommonSPI.kt (95%) rename {jacodb-api-jvm/src/main/kotlin/org/jacodb/api/jvm => jacodb-api-storage/src/main/kotlin/org/jacodb/api}/spi/SPILoader.kt (97%) rename {jacodb-api-jvm/src/main/kotlin/org/jacodb/api/jvm => jacodb-api-storage/src/main/kotlin/org/jacodb/api}/storage/ByteArrayKey.kt (94%) rename {jacodb-api-jvm/src/main/kotlin/org/jacodb/api/jvm => jacodb-api-storage/src/main/kotlin/org/jacodb/api}/storage/ers/Binding.kt (96%) rename {jacodb-api-jvm/src/main/kotlin/org/jacodb/api/jvm => jacodb-api-storage/src/main/kotlin/org/jacodb/api}/storage/ers/BindingProvider.kt (94%) rename {jacodb-api-jvm/src/main/kotlin/org/jacodb/api/jvm => jacodb-api-storage/src/main/kotlin/org/jacodb/api}/storage/ers/Entity.kt (98%) rename {jacodb-api-jvm/src/main/kotlin/org/jacodb/api/jvm => jacodb-api-storage/src/main/kotlin/org/jacodb/api}/storage/ers/EntityEx.kt (98%) rename {jacodb-api-jvm/src/main/kotlin/org/jacodb/api/jvm => jacodb-api-storage/src/main/kotlin/org/jacodb/api}/storage/ers/EntityIterable.kt (52%) rename {jacodb-api-jvm/src/main/kotlin/org/jacodb/api/jvm => jacodb-api-storage/src/main/kotlin/org/jacodb/api}/storage/ers/EntityIterableEx.kt (94%) rename {jacodb-api-jvm/src/main/kotlin/org/jacodb/api/jvm => jacodb-api-storage/src/main/kotlin/org/jacodb/api}/storage/ers/EntityRelationshipStorage.kt (90%) rename {jacodb-api-jvm/src/main/kotlin/org/jacodb/api/jvm => jacodb-api-storage/src/main/kotlin/org/jacodb/api}/storage/ers/Transaction.kt (93%) rename {jacodb-api-jvm/src/main/kotlin/org/jacodb/api/jvm => jacodb-api-storage/src/main/kotlin/org/jacodb/api}/storage/ers/readme.md (100%) rename {jacodb-api-jvm/src/main/kotlin/org/jacodb/api/jvm => jacodb-api-storage/src/main/kotlin/org/jacodb/api}/storage/ers/typed/ErsType.kt (100%) rename {jacodb-api-jvm/src/main/kotlin/org/jacodb/api/jvm => jacodb-api-storage/src/main/kotlin/org/jacodb/api}/storage/ers/typed/TypedErsApi.kt (95%) rename {jacodb-api-jvm/src/main/kotlin/org/jacodb/api/jvm => jacodb-api-storage/src/main/kotlin/org/jacodb/api}/storage/ers/typed/TypedErsImpl.kt (94%) rename {jacodb-api-jvm/src/main/kotlin/org/jacodb/api/jvm => jacodb-api-storage/src/main/kotlin/org/jacodb/api}/storage/ers/typed/readme.md (100%) rename {jacodb-api-jvm/src/main/kotlin/org/jacodb/api/jvm => jacodb-api-storage/src/main/kotlin/org/jacodb/api}/storage/kv/Cursor.kt (99%) rename {jacodb-api-jvm/src/main/kotlin/org/jacodb/api/jvm => jacodb-api-storage/src/main/kotlin/org/jacodb/api}/storage/kv/PluggableKeyValueStorage.kt (87%) rename {jacodb-api-jvm/src/main/kotlin/org/jacodb/api/jvm => jacodb-api-storage/src/main/kotlin/org/jacodb/api}/storage/kv/Transaction.kt (98%) rename {jacodb-api-jvm/src/main/kotlin/org/jacodb/api/jvm => jacodb-api-storage/src/main/kotlin/org/jacodb/api}/storage/kv/readme.md (100%) rename {jacodb-api-jvm/src/main/kotlin/org/jacodb/api/jvm/ext => jacodb-core/src/main/kotlin/org/jacodb/impl/bytecode}/Nullables.kt (89%) delete mode 100644 jacodb-core/src/main/kotlin/org/jacodb/impl/storage/ers/ram/EntityIdSet.kt delete mode 100644 jacodb-core/src/main/kotlin/org/jacodb/impl/storage/ers/ram/Links.kt delete mode 100644 jacodb-core/src/main/kotlin/org/jacodb/impl/storage/ers/ram/XodusPersistentCollectionsEx.kt create mode 100644 jacodb-core/src/main/resources/META-INF/services/org.jacodb.api.storage.ers.EntityRelationshipStorageSPI create mode 100644 jacodb-storage/build.gradle.kts create mode 100644 jacodb-storage/src/main/kotlin/org/jacodb/impl/JcErsSettings.kt rename {jacodb-core => jacodb-storage}/src/main/kotlin/org/jacodb/impl/storage/ers/BuiltInBindings.kt (98%) rename {jacodb-core => jacodb-storage}/src/main/kotlin/org/jacodb/impl/storage/ers/decorators/AbstractDecorators.kt (94%) rename {jacodb-core => jacodb-storage}/src/main/kotlin/org/jacodb/impl/storage/ers/decorators/AllDecorators.kt (94%) rename {jacodb-core => jacodb-storage}/src/main/kotlin/org/jacodb/impl/storage/ers/decorators/Checked.kt (90%) rename {jacodb-core => jacodb-storage}/src/main/kotlin/org/jacodb/impl/storage/ers/decorators/DeepDecorators.kt (93%) rename {jacodb-core => jacodb-storage}/src/main/kotlin/org/jacodb/impl/storage/ers/decorators/RecomputingEntityIterable.kt (91%) rename {jacodb-core => jacodb-storage}/src/main/kotlin/org/jacodb/impl/storage/ers/kv/KVEntity.kt (94%) rename {jacodb-core => jacodb-storage}/src/main/kotlin/org/jacodb/impl/storage/ers/kv/KVEntityRelationshipStorage.kt (89%) rename {jacodb-core => jacodb-storage}/src/main/kotlin/org/jacodb/impl/storage/ers/kv/KVEntityRelationshipStorageSPI.kt (86%) rename {jacodb-core => jacodb-storage}/src/main/kotlin/org/jacodb/impl/storage/ers/kv/KVErsTransaction.kt (93%) rename {jacodb-core => jacodb-storage}/src/main/kotlin/org/jacodb/impl/storage/ers/kv/KVErsUtil.kt (94%) create mode 100644 jacodb-storage/src/main/kotlin/org/jacodb/impl/storage/ers/ram/AttributesImmutable.kt rename {jacodb-core => jacodb-storage}/src/main/kotlin/org/jacodb/impl/storage/ers/ram/CompactPersistentLongSet.kt (98%) create mode 100644 jacodb-storage/src/main/kotlin/org/jacodb/impl/storage/ers/ram/Links.kt rename {jacodb-core => jacodb-storage}/src/main/kotlin/org/jacodb/impl/storage/ers/ram/MutableContainer.kt (87%) rename {jacodb-core => jacodb-storage}/src/main/kotlin/org/jacodb/impl/storage/ers/ram/PackedPersistentLongSet.kt (100%) rename {jacodb-core => jacodb-storage}/src/main/kotlin/org/jacodb/impl/storage/ers/ram/Properties.kt (63%) create mode 100644 jacodb-storage/src/main/kotlin/org/jacodb/impl/storage/ers/ram/RAMDataContainer.kt create mode 100644 jacodb-storage/src/main/kotlin/org/jacodb/impl/storage/ers/ram/RAMDataContainerImmutable.kt rename jacodb-core/src/main/kotlin/org/jacodb/impl/storage/ers/ram/RAMPersistentDataContainer.kt => jacodb-storage/src/main/kotlin/org/jacodb/impl/storage/ers/ram/RAMDataContainerMutable.kt (64%) rename {jacodb-core => jacodb-storage}/src/main/kotlin/org/jacodb/impl/storage/ers/ram/RAMEntity.kt (91%) rename {jacodb-core => jacodb-storage}/src/main/kotlin/org/jacodb/impl/storage/ers/ram/RAMEntityRelationshipStorage.kt (63%) rename {jacodb-core => jacodb-storage}/src/main/kotlin/org/jacodb/impl/storage/ers/ram/RAMEntityRelationshipStorageSPI.kt (86%) rename {jacodb-core => jacodb-storage}/src/main/kotlin/org/jacodb/impl/storage/ers/ram/RAMTransaction.kt (91%) create mode 100644 jacodb-storage/src/main/kotlin/org/jacodb/impl/storage/ers/ram/SparseBitSet.kt rename jacodb-core/src/main/kotlin/org/jacodb/impl/storage/ers/ram/BlobsCache.kt => jacodb-storage/src/main/kotlin/org/jacodb/impl/storage/ers/ram/TinyBlobsCache.kt (94%) rename {jacodb-core => jacodb-storage}/src/main/kotlin/org/jacodb/impl/storage/ers/ram/TransactionalPersistentLongMap.kt (82%) rename {jacodb-core => jacodb-storage}/src/main/kotlin/org/jacodb/impl/storage/ers/ram/TransactionalPersistentMap.kt (90%) create mode 100644 jacodb-storage/src/main/kotlin/org/jacodb/impl/storage/ers/ram/XodusPersistentCollectionsEx.kt rename {jacodb-core => jacodb-storage}/src/main/kotlin/org/jacodb/impl/storage/kv/lmdb/LmdbCursor.kt (92%) rename {jacodb-core => jacodb-storage}/src/main/kotlin/org/jacodb/impl/storage/kv/lmdb/LmdbEx.kt (100%) rename {jacodb-core => jacodb-storage}/src/main/kotlin/org/jacodb/impl/storage/kv/lmdb/LmdbKeyValueStorage.kt (94%) rename {jacodb-core => jacodb-storage}/src/main/kotlin/org/jacodb/impl/storage/kv/lmdb/LmdbKeyValueStorageSPI.kt (79%) rename {jacodb-core => jacodb-storage}/src/main/kotlin/org/jacodb/impl/storage/kv/lmdb/LmdbNamedMap.kt (87%) rename {jacodb-core => jacodb-storage}/src/main/kotlin/org/jacodb/impl/storage/kv/lmdb/LmdbTransaction.kt (93%) rename {jacodb-core => jacodb-storage}/src/main/kotlin/org/jacodb/impl/storage/kv/rocks/ByteArrayPairUtils.kt (100%) rename {jacodb-core => jacodb-storage}/src/main/kotlin/org/jacodb/impl/storage/kv/rocks/RocksCursor.kt (93%) rename {jacodb-core => jacodb-storage}/src/main/kotlin/org/jacodb/impl/storage/kv/rocks/RocksKeyValueStorage.kt (98%) rename {jacodb-core => jacodb-storage}/src/main/kotlin/org/jacodb/impl/storage/kv/rocks/RocksKeyValueStorageSPI.kt (86%) rename {jacodb-core => jacodb-storage}/src/main/kotlin/org/jacodb/impl/storage/kv/rocks/RocksNamedMap.kt (96%) rename {jacodb-core => jacodb-storage}/src/main/kotlin/org/jacodb/impl/storage/kv/rocks/RocksTransaction.kt (95%) rename {jacodb-core => jacodb-storage}/src/main/kotlin/org/jacodb/impl/storage/kv/xodus/XodusCursor.kt (94%) rename {jacodb-core => jacodb-storage}/src/main/kotlin/org/jacodb/impl/storage/kv/xodus/XodusEnvironmentsEx.kt (100%) rename {jacodb-core => jacodb-storage}/src/main/kotlin/org/jacodb/impl/storage/kv/xodus/XodusKeyValueStorage.kt (90%) rename {jacodb-core => jacodb-storage}/src/main/kotlin/org/jacodb/impl/storage/kv/xodus/XodusKeyValueStorageSPI.kt (86%) rename {jacodb-core => jacodb-storage}/src/main/kotlin/org/jacodb/impl/storage/kv/xodus/XodusNamedMap.kt (90%) rename {jacodb-core => jacodb-storage}/src/main/kotlin/org/jacodb/impl/storage/kv/xodus/XodusTransaction.kt (91%) rename jacodb-core/src/main/resources/META-INF/services/org.jacodb.api.jvm.storage.ers.EntityRelationshipStorageSPI => jacodb-storage/src/main/resources/META-INF/services/org.jacodb.api.storage.ers.EntityRelationshipStorageSPI (66%) rename jacodb-core/src/main/resources/META-INF/services/org.jacodb.api.jvm.storage.kv.PluggableKeyValueStorageSPI => jacodb-storage/src/main/resources/META-INF/services/org.jacodb.api.storage.kv.PluggableKeyValueStorageSPI (100%) create mode 100644 jacodb-storage/src/test/kotlin/org/jacodb/impl/storage/ers/ram/SparseBitSetTest.kt rename {jacodb-core => jacodb-storage}/src/test/kotlin/org/jacodb/testing/storage/ers/BindingsTest.kt (100%) rename {jacodb-core => jacodb-storage}/src/test/kotlin/org/jacodb/testing/storage/ers/CompactPersistentLongSetTest.kt (100%) rename {jacodb-core => jacodb-storage}/src/test/kotlin/org/jacodb/testing/storage/ers/LmdbKVEntityRelationshipStorageTest.kt (100%) create mode 100644 jacodb-storage/src/test/kotlin/org/jacodb/testing/storage/ers/RAMEntityRelationshipStorageImmutableTest.kt rename {jacodb-core => jacodb-storage}/src/test/kotlin/org/jacodb/testing/storage/ers/RAMEntityRelationshipStorageTest.kt (100%) rename {jacodb-core => jacodb-storage}/src/test/kotlin/org/jacodb/testing/storage/ers/RocksKVEntityRelationshipStorageTest.kt (100%) rename {jacodb-core => jacodb-storage}/src/test/kotlin/org/jacodb/testing/storage/ers/XodusKVEntityRelationshipStorageTest.kt (69%) rename {jacodb-core => jacodb-storage}/src/test/kotlin/org/jacodb/testing/storage/kv/LmdbKeyValueStorageTest.kt (100%) rename {jacodb-core => jacodb-storage}/src/test/kotlin/org/jacodb/testing/storage/kv/RocksKeyValueStorageTest.kt (100%) rename {jacodb-core => jacodb-storage}/src/test/kotlin/org/jacodb/testing/storage/kv/XodusKeyValueStorageTest.kt (59%) rename {jacodb-core => jacodb-storage}/src/testFixtures/kotlin/org/jacodb/testing/storage/ers/EntityRelationshipStorageTest.kt (94%) rename {jacodb-core => jacodb-storage}/src/testFixtures/kotlin/org/jacodb/testing/storage/kv/PluggableKeyValueStorageTest.kt (92%) diff --git a/build.gradle.kts b/build.gradle.kts index d302e9ed9..a098d089b 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -1,5 +1,4 @@ import org.jetbrains.dokka.gradle.DokkaTaskPartial -import org.jetbrains.kotlin.gradle.tasks.KotlinCompile val semVer: String? by project val includeDokka: String? by project @@ -26,6 +25,9 @@ allprojects { apply { plugin("kotlin") + plugin("java") + plugin("java-library") + plugin("java-test-fixtures") plugin("org.jetbrains.kotlin.plugin.allopen") plugin(Plugins.Dokka.id) plugin(Plugins.Licenser.id) @@ -56,32 +58,45 @@ allprojects { testRuntimeOnly(Libs.guava) } + kotlin { + compilerOptions { + freeCompilerArgs.add("-Xsam-conversions=class") + freeCompilerArgs.add("-Xcontext-receivers") + freeCompilerArgs.add("-Xjvm-default=all") + allWarningsAsErrors = false + } + } + tasks { withType { sourceCompatibility = "1.8" - targetCompatibility = "1.8" options.encoding = "UTF-8" options.compilerArgs.add("-Xlint:all") } - withType { + compileJava { + targetCompatibility = "1.8" + } + compileKotlin { kotlinOptions { jvmTarget = "1.8" - freeCompilerArgs += "-Xallow-result-return-type" - freeCompilerArgs += "-Xsam-conversions=class" - freeCompilerArgs += "-Xcontext-receivers" - freeCompilerArgs += "-Xjvm-default=all" - allWarningsAsErrors = false } } + compileTestJava { + targetCompatibility = runtimeJavaVersion() + } + compileTestFixturesJava { + targetCompatibility = "1.8" + } compileTestKotlin { + kotlinOptions { + jvmTarget = runtimeJavaVersion() + } + } + compileTestFixturesKotlin { kotlinOptions { jvmTarget = "1.8" - freeCompilerArgs += "-Xallow-result-return-type" - freeCompilerArgs += "-Xsam-conversions=class" - freeCompilerArgs += "-Xcontext-receivers" - allWarningsAsErrors = false } } diff --git a/buildSrc/src/main/kotlin/Dependencies.kt b/buildSrc/src/main/kotlin/Dependencies.kt index 82136e319..0d7bc888f 100644 --- a/buildSrc/src/main/kotlin/Dependencies.kt +++ b/buildSrc/src/main/kotlin/Dependencies.kt @@ -22,13 +22,13 @@ object Versions { const val jooq = "3.14.16" const val juliet = "1.3.2" const val junit = "5.9.2" - const val kotlin = "1.9.25" + const val kotlin = "2.0.20" const val kotlin_logging = "1.8.3" const val kotlinx_benchmark = "0.4.6" const val kotlinx_cli = "0.3.5" const val kotlinx_collections_immutable = "0.3.5" const val kotlinx_coroutines = "1.6.4" - const val kotlinx_metadata = "0.9.0" + const val kotlin_metadata = kotlin const val kotlinx_serialization = "1.4.1" const val licenser = "0.6.1" const val mockk = "1.13.3" @@ -114,10 +114,10 @@ object Libs { version = Versions.kotlinx_collections_immutable ) - val kotlinx_metadata_jvm = dep( - group = "org.jetbrains.kotlinx", - name = "kotlinx-metadata-jvm", - version = Versions.kotlinx_metadata + val kotlin_metadata_jvm = dep( + group = "org.jetbrains.kotlin", + name = "kotlin-metadata-jvm", + version = Versions.kotlin_metadata ) val javax_activation = dep( diff --git a/buildSrc/src/main/kotlin/Tests.kt b/buildSrc/src/main/kotlin/Tests.kt index aff1eb7ac..5fa50b775 100644 --- a/buildSrc/src/main/kotlin/Tests.kt +++ b/buildSrc/src/main/kotlin/Tests.kt @@ -1,7 +1,7 @@ import org.gradle.api.tasks.TaskProvider import org.gradle.api.tasks.testing.Test import org.gradle.api.tasks.testing.logging.TestExceptionFormat -import java.util.StringTokenizer +import java.util.* object Tests { val lifecycleTag = "lifecycle" @@ -26,3 +26,5 @@ fun Test.setup(jacocoTestReport: TaskProvider<*>) { ) } } + +fun Any.runtimeJavaVersion(): String = System.getProperty("java.specification.version") \ No newline at end of file diff --git a/jacodb-analysis/build.gradle.kts b/jacodb-analysis/build.gradle.kts index a4e22e750..dd121df72 100644 --- a/jacodb-analysis/build.gradle.kts +++ b/jacodb-analysis/build.gradle.kts @@ -18,6 +18,7 @@ dependencies { testImplementation(project(":jacodb-api-jvm")) testImplementation(testFixtures(project(":jacodb-core"))) + testImplementation(testFixtures(project(":jacodb-storage"))) testImplementation(kotlin("test")) testImplementation(Libs.mockk) diff --git a/jacodb-analysis/src/main/kotlin/org/jacodb/analysis/impl/custom/FlowAnalysisImpl.kt b/jacodb-analysis/src/main/kotlin/org/jacodb/analysis/impl/custom/FlowAnalysisImpl.kt index 874cd5721..454436c19 100644 --- a/jacodb-analysis/src/main/kotlin/org/jacodb/analysis/impl/custom/FlowAnalysisImpl.kt +++ b/jacodb-analysis/src/main/kotlin/org/jacodb/analysis/impl/custom/FlowAnalysisImpl.kt @@ -152,7 +152,7 @@ private fun FlowEntry.visitEntry( } private fun NODE.toEntry( - pred: FlowEntry?, + pred: FlowEntry, visited: MutableMap>, ): FlowEntry { // either we reach a new node or a merge node, the latter one is rare diff --git a/jacodb-analysis/src/main/kotlin/org/jacodb/analysis/npe/NpeFlowFunctions.kt b/jacodb-analysis/src/main/kotlin/org/jacodb/analysis/npe/NpeFlowFunctions.kt index 84251e6ba..c2144b76b 100644 --- a/jacodb-analysis/src/main/kotlin/org/jacodb/analysis/npe/NpeFlowFunctions.kt +++ b/jacodb-analysis/src/main/kotlin/org/jacodb/analysis/npe/NpeFlowFunctions.kt @@ -59,7 +59,7 @@ import org.jacodb.api.jvm.cfg.JcNewArrayExpr import org.jacodb.api.jvm.cfg.JcNullConstant import org.jacodb.api.jvm.cfg.JcReturnInst import org.jacodb.api.jvm.ext.findType -import org.jacodb.api.jvm.ext.isNullable +import org.jacodb.impl.bytecode.isNullable import org.jacodb.taint.configuration.AssignMark import org.jacodb.taint.configuration.CopyAllMarks import org.jacodb.taint.configuration.CopyMark diff --git a/jacodb-api-jvm/build.gradle.kts b/jacodb-api-jvm/build.gradle.kts index fbc4ef990..1eb2a4c4c 100644 --- a/jacodb-api-jvm/build.gradle.kts +++ b/jacodb-api-jvm/build.gradle.kts @@ -1,5 +1,6 @@ dependencies { api(project(":jacodb-api-common")) + api(project(":jacodb-api-storage")) api(Libs.asm) api(Libs.asm_tree) diff --git a/jacodb-api-jvm/src/main/kotlin/org/jacodb/api/jvm/Api.kt b/jacodb-api-jvm/src/main/kotlin/org/jacodb/api/jvm/Api.kt index d20f37f35..74ca005fc 100644 --- a/jacodb-api-jvm/src/main/kotlin/org/jacodb/api/jvm/Api.kt +++ b/jacodb-api-jvm/src/main/kotlin/org/jacodb/api/jvm/Api.kt @@ -18,8 +18,8 @@ package org.jacodb.api.jvm import kotlinx.coroutines.GlobalScope import kotlinx.coroutines.future.future -import org.jacodb.api.jvm.storage.ers.EntityRelationshipStorage -import org.jacodb.api.jvm.storage.ers.Transaction +import org.jacodb.api.storage.ers.EntityRelationshipStorage +import org.jacodb.api.storage.ers.Transaction import org.jooq.DSLContext import java.io.Closeable import java.io.File @@ -79,8 +79,6 @@ interface JcDatabase : Closeable { fun asyncClasspath(dirOrJars: List, features: List?) = GlobalScope.future { classpath(dirOrJars, features) } - fun classpathOf(locations: List, features: List?): JcClasspath - /** * process and index single byte-code resource * @param dirOrJar build folder or jar file @@ -135,6 +133,17 @@ interface JcDatabase : Closeable { suspend fun awaitBackgroundJobs() fun asyncAwaitBackgroundJobs() = GlobalScope.future { awaitBackgroundJobs() } + /** + * Sets this database's internal state to immutable if corresponding backend supports this operation. + * If it does, any write operation is no longer possible. + * The method can be used in order to "fix" current snapshot of the model. + * Generally, there is no way to switch the database back to mutable. + */ + suspend fun setImmutable() { + awaitBackgroundJobs() + persistence.setImmutable() + } + fun isInstalled(feature: JcFeature<*, *>): Boolean = features.contains(feature) val features: List> @@ -165,6 +174,14 @@ interface JcDatabasePersistence : Closeable { fun findClassSources(cp: JcClasspath, fullName: String): List fun createIndexes() {} + + /** + * Sets this persistence's internal state to immutable if corresponding backend supports this operation. + * If it does, any write operation is no longer possible. + * The method can be used in order to "fix" current snapshot of the model. + * Generally, there is no way to switch the persistence back to mutable. + */ + fun setImmutable() {} } /** diff --git a/jacodb-api-storage/build.gradle.kts b/jacodb-api-storage/build.gradle.kts new file mode 100644 index 000000000..c88009326 --- /dev/null +++ b/jacodb-api-storage/build.gradle.kts @@ -0,0 +1,4 @@ +dependencies { + api(project(":jacodb-api-common")) + api(Libs.kotlinx_collections_immutable) +} diff --git a/jacodb-api-jvm/src/main/kotlin/org/jacodb/api/jvm/spi/CommonSPI.kt b/jacodb-api-storage/src/main/kotlin/org/jacodb/api/spi/CommonSPI.kt similarity index 95% rename from jacodb-api-jvm/src/main/kotlin/org/jacodb/api/jvm/spi/CommonSPI.kt rename to jacodb-api-storage/src/main/kotlin/org/jacodb/api/spi/CommonSPI.kt index 78c87922c..6466c9726 100644 --- a/jacodb-api-jvm/src/main/kotlin/org/jacodb/api/jvm/spi/CommonSPI.kt +++ b/jacodb-api-storage/src/main/kotlin/org/jacodb/api/spi/CommonSPI.kt @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.jacodb.api.jvm.spi +package org.jacodb.api.spi interface CommonSPI { diff --git a/jacodb-api-jvm/src/main/kotlin/org/jacodb/api/jvm/spi/SPILoader.kt b/jacodb-api-storage/src/main/kotlin/org/jacodb/api/spi/SPILoader.kt similarity index 97% rename from jacodb-api-jvm/src/main/kotlin/org/jacodb/api/jvm/spi/SPILoader.kt rename to jacodb-api-storage/src/main/kotlin/org/jacodb/api/spi/SPILoader.kt index 55d40b0bd..b62e6c537 100644 --- a/jacodb-api-jvm/src/main/kotlin/org/jacodb/api/jvm/spi/SPILoader.kt +++ b/jacodb-api-storage/src/main/kotlin/org/jacodb/api/spi/SPILoader.kt @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.jacodb.api.jvm.spi +package org.jacodb.api.spi import java.lang.ref.SoftReference import java.util.* diff --git a/jacodb-api-jvm/src/main/kotlin/org/jacodb/api/jvm/storage/ByteArrayKey.kt b/jacodb-api-storage/src/main/kotlin/org/jacodb/api/storage/ByteArrayKey.kt similarity index 94% rename from jacodb-api-jvm/src/main/kotlin/org/jacodb/api/jvm/storage/ByteArrayKey.kt rename to jacodb-api-storage/src/main/kotlin/org/jacodb/api/storage/ByteArrayKey.kt index 2fabb77f2..4404d3f38 100644 --- a/jacodb-api-jvm/src/main/kotlin/org/jacodb/api/jvm/storage/ByteArrayKey.kt +++ b/jacodb-api-storage/src/main/kotlin/org/jacodb/api/storage/ByteArrayKey.kt @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.jacodb.api.jvm.storage +package org.jacodb.api.storage import kotlin.math.min @@ -36,4 +36,6 @@ class ByteArrayKey(val bytes: ByteArray) : Comparable { override fun hashCode(): Int = bytes.contentHashCode() override fun toString(): String = bytes.contentToString() -} \ No newline at end of file +} + +fun ByteArray.asComparable() = ByteArrayKey(this) \ No newline at end of file diff --git a/jacodb-api-jvm/src/main/kotlin/org/jacodb/api/jvm/storage/ers/Binding.kt b/jacodb-api-storage/src/main/kotlin/org/jacodb/api/storage/ers/Binding.kt similarity index 96% rename from jacodb-api-jvm/src/main/kotlin/org/jacodb/api/jvm/storage/ers/Binding.kt rename to jacodb-api-storage/src/main/kotlin/org/jacodb/api/storage/ers/Binding.kt index 532fd8183..f0f887aaa 100644 --- a/jacodb-api-jvm/src/main/kotlin/org/jacodb/api/jvm/storage/ers/Binding.kt +++ b/jacodb-api-storage/src/main/kotlin/org/jacodb/api/storage/ers/Binding.kt @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.jacodb.api.jvm.storage.ers +package org.jacodb.api.storage.ers interface Binding { diff --git a/jacodb-api-jvm/src/main/kotlin/org/jacodb/api/jvm/storage/ers/BindingProvider.kt b/jacodb-api-storage/src/main/kotlin/org/jacodb/api/storage/ers/BindingProvider.kt similarity index 94% rename from jacodb-api-jvm/src/main/kotlin/org/jacodb/api/jvm/storage/ers/BindingProvider.kt rename to jacodb-api-storage/src/main/kotlin/org/jacodb/api/storage/ers/BindingProvider.kt index e8c179cac..ddd0c5901 100644 --- a/jacodb-api-jvm/src/main/kotlin/org/jacodb/api/jvm/storage/ers/BindingProvider.kt +++ b/jacodb-api-storage/src/main/kotlin/org/jacodb/api/storage/ers/BindingProvider.kt @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.jacodb.api.jvm.storage.ers +package org.jacodb.api.storage.ers interface BindingProvider { fun getBinding(clazz: Class): Binding diff --git a/jacodb-api-jvm/src/main/kotlin/org/jacodb/api/jvm/storage/ers/Entity.kt b/jacodb-api-storage/src/main/kotlin/org/jacodb/api/storage/ers/Entity.kt similarity index 98% rename from jacodb-api-jvm/src/main/kotlin/org/jacodb/api/jvm/storage/ers/Entity.kt rename to jacodb-api-storage/src/main/kotlin/org/jacodb/api/storage/ers/Entity.kt index c92870873..d2a336a84 100644 --- a/jacodb-api-jvm/src/main/kotlin/org/jacodb/api/jvm/storage/ers/Entity.kt +++ b/jacodb-api-storage/src/main/kotlin/org/jacodb/api/storage/ers/Entity.kt @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.jacodb.api.jvm.storage.ers +package org.jacodb.api.storage.ers abstract class Entity : Comparable { diff --git a/jacodb-api-jvm/src/main/kotlin/org/jacodb/api/jvm/storage/ers/EntityEx.kt b/jacodb-api-storage/src/main/kotlin/org/jacodb/api/storage/ers/EntityEx.kt similarity index 98% rename from jacodb-api-jvm/src/main/kotlin/org/jacodb/api/jvm/storage/ers/EntityEx.kt rename to jacodb-api-storage/src/main/kotlin/org/jacodb/api/storage/ers/EntityEx.kt index 5fd633adf..42d637024 100644 --- a/jacodb-api-jvm/src/main/kotlin/org/jacodb/api/jvm/storage/ers/EntityEx.kt +++ b/jacodb-api-storage/src/main/kotlin/org/jacodb/api/storage/ers/EntityEx.kt @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.jacodb.api.jvm.storage.ers +package org.jacodb.api.storage.ers import kotlin.reflect.KProperty diff --git a/jacodb-api-jvm/src/main/kotlin/org/jacodb/api/jvm/storage/ers/EntityIterable.kt b/jacodb-api-storage/src/main/kotlin/org/jacodb/api/storage/ers/EntityIterable.kt similarity index 52% rename from jacodb-api-jvm/src/main/kotlin/org/jacodb/api/jvm/storage/ers/EntityIterable.kt rename to jacodb-api-storage/src/main/kotlin/org/jacodb/api/storage/ers/EntityIterable.kt index cf25de739..da517b657 100644 --- a/jacodb-api-jvm/src/main/kotlin/org/jacodb/api/jvm/storage/ers/EntityIterable.kt +++ b/jacodb-api-storage/src/main/kotlin/org/jacodb/api/storage/ers/EntityIterable.kt @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.jacodb.api.jvm.storage.ers +package org.jacodb.api.storage.ers interface EntityIterable : Sequence { @@ -84,9 +84,7 @@ class EntityIdCollectionEntityIterable( override fun contains(e: Entity) = e.id in set - override fun iterator() = buildList { - set.forEach { id -> txn.getEntityOrNull(id)?.let { e -> add(e) } } - }.iterator() + override fun iterator() = set.mapNotNullTo(ArrayList(set.size)) { id -> txn.getEntityOrNull(id) }.iterator() } class InstanceIdCollectionEntityIterable( @@ -101,7 +99,74 @@ class InstanceIdCollectionEntityIterable( override fun contains(e: Entity) = e.id.typeId == typeId && e.id.instanceId in set - override fun iterator() = buildList { - set.forEach { instanceId -> txn.getEntityOrNull(EntityId(typeId, instanceId))?.let { e -> add(e) } } - }.iterator() + override fun iterator() = + set.mapNotNullTo(ArrayList(set.size)) { instanceId -> txn.getEntityOrNull(EntityId(typeId, instanceId)) } + .iterator() +} + +typealias EntityIdPredicate = (EntityId) -> Boolean +typealias InstanceIdPredicate = (Long) -> Boolean + +private class FilterEntityIdEntityIterable( + private val decorated: EntityIterable, + private val predicate: EntityIdPredicate +) : EntityIterable { + + override fun iterator(): Iterator = Sequence { + decorated.iterator() + }.filter { predicate(it.id) }.iterator() +} + +private class FilterInstanceIdEntityIterable( + private val decorated: EntityIterable, + private val predicate: InstanceIdPredicate +) : EntityIterable { + + override fun iterator(): Iterator = Sequence { + decorated.iterator() + }.filter { predicate(it.id.instanceId) }.iterator() +} + +fun EntityIterable.filterEntityIds(predicate: EntityIdPredicate): EntityIterable { + return FilterEntityIdEntityIterable(this, predicate) +} + +fun EntityIterable.filterInstanceIds(predicate: InstanceIdPredicate): EntityIterable { + return FilterInstanceIdEntityIterable(this, predicate) +} + +/** + * Iterates entities with instance ids from `LongRange`. + * The range is expected to have step 1. + */ +private class LongRangeEntityIterable( + private val txn: Transaction, + private val typeId: Int, + private val range: LongRange, + private val filterIdPredicate: InstanceIdPredicate? = null +) : EntityIterable { + + override val size = if (filterIdPredicate == null) range.last - range.start + 1 else super.size + + override fun contains(e: Entity) = + if (filterIdPredicate == null) e.id.typeId == typeId && e.id.instanceId in range else super.contains(e) + + override fun iterator(): Iterator { + var result = range.asSequence() + filterIdPredicate?.let { result = result.filter(it) } + return result.map { txn.getEntityUnsafe(EntityId(typeId, it)) }.iterator() + } +} + +fun Any.longRangeIterable( + txn: Transaction, + typeId: Int, + range: LongRange, + filterIdPredicate: InstanceIdPredicate? = null +): EntityIterable { + return LongRangeEntityIterable(txn, typeId, range, filterIdPredicate) +} + +inline fun EntityIterable(crossinline iterator: () -> Iterator): EntityIterable = object : EntityIterable { + override fun iterator(): Iterator = iterator() } \ No newline at end of file diff --git a/jacodb-api-jvm/src/main/kotlin/org/jacodb/api/jvm/storage/ers/EntityIterableEx.kt b/jacodb-api-storage/src/main/kotlin/org/jacodb/api/storage/ers/EntityIterableEx.kt similarity index 94% rename from jacodb-api-jvm/src/main/kotlin/org/jacodb/api/jvm/storage/ers/EntityIterableEx.kt rename to jacodb-api-storage/src/main/kotlin/org/jacodb/api/storage/ers/EntityIterableEx.kt index a9dd6e3dc..a67ebcc66 100644 --- a/jacodb-api-jvm/src/main/kotlin/org/jacodb/api/jvm/storage/ers/EntityIterableEx.kt +++ b/jacodb-api-storage/src/main/kotlin/org/jacodb/api/storage/ers/EntityIterableEx.kt @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.jacodb.api.jvm.storage.ers +package org.jacodb.api.storage.ers /** * Default lazy implementations of binary operations over instances of `EntityIterable` @@ -72,9 +72,8 @@ fun EntityIterable.intersect(other: EntityIterable): EntityIterable { return object : EntityIterable { override fun iterator(): Iterator { val otherSet = other.toEntityIdSet() - return self.filter { it.id in otherSet }.iterator() + return self.filterEntityIds { id -> id in otherSet }.iterator() } - } } @@ -83,9 +82,8 @@ fun EntityIterable.subtract(other: EntityIterable): EntityIterable { return object : EntityIterable { override fun iterator(): Iterator { val otherSet = other.toEntityIdSet() - return self.filter { it.id !in otherSet }.iterator() + return self.filterEntityIds { id -> id !in otherSet }.iterator() } - } } diff --git a/jacodb-api-jvm/src/main/kotlin/org/jacodb/api/jvm/storage/ers/EntityRelationshipStorage.kt b/jacodb-api-storage/src/main/kotlin/org/jacodb/api/storage/ers/EntityRelationshipStorage.kt similarity index 90% rename from jacodb-api-jvm/src/main/kotlin/org/jacodb/api/jvm/storage/ers/EntityRelationshipStorage.kt rename to jacodb-api-storage/src/main/kotlin/org/jacodb/api/storage/ers/EntityRelationshipStorage.kt index 75e6c86e5..9a8b33598 100644 --- a/jacodb-api-jvm/src/main/kotlin/org/jacodb/api/jvm/storage/ers/EntityRelationshipStorage.kt +++ b/jacodb-api-storage/src/main/kotlin/org/jacodb/api/storage/ers/EntityRelationshipStorage.kt @@ -14,15 +14,17 @@ * limitations under the License. */ -package org.jacodb.api.jvm.storage.ers +package org.jacodb.api.storage.ers -import org.jacodb.api.jvm.spi.CommonSPI -import org.jacodb.api.jvm.spi.SPILoader +import org.jacodb.api.spi.CommonSPI +import org.jacodb.api.spi.SPILoader import java.io.Closeable import java.util.* interface EntityRelationshipStorage : Closeable, BindingProvider { + val isInRam: Boolean get() = false + @Throws(ERSConflictingTransactionException::class) fun beginTransaction(readonly: Boolean = false): Transaction @@ -45,6 +47,11 @@ interface EntityRelationshipStorage : Closeable, BindingProvider { } throw ERSConflictingTransactionException("Failed to commit transaction after $attempts optimistic attempts") } + + /** + * Returns a read-only storage holding the latest available snapshot of data. + */ + val asReadonly: EntityRelationshipStorage get() = this } interface EntityRelationshipStorageSPI : CommonSPI { diff --git a/jacodb-api-jvm/src/main/kotlin/org/jacodb/api/jvm/storage/ers/Transaction.kt b/jacodb-api-storage/src/main/kotlin/org/jacodb/api/storage/ers/Transaction.kt similarity index 93% rename from jacodb-api-jvm/src/main/kotlin/org/jacodb/api/jvm/storage/ers/Transaction.kt rename to jacodb-api-storage/src/main/kotlin/org/jacodb/api/storage/ers/Transaction.kt index 4313a4d19..2d47bba29 100644 --- a/jacodb-api-jvm/src/main/kotlin/org/jacodb/api/jvm/storage/ers/Transaction.kt +++ b/jacodb-api-storage/src/main/kotlin/org/jacodb/api/storage/ers/Transaction.kt @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.jacodb.api.jvm.storage.ers +package org.jacodb.api.storage.ers import java.io.Closeable @@ -34,8 +34,18 @@ interface Transaction : Closeable { fun newEntity(type: String): Entity + /** + * Returns entity by entity id, or `null` if the entity doesn't exist. + */ fun getEntityOrNull(id: EntityId): Entity? + /** + * Returns entity by entity id. The entity is not checked for existence. + * + * @see isEntityDeleted + */ + fun getEntityUnsafe(id: EntityId): Entity + fun deleteEntity(id: EntityId) fun isEntityDeleted(id: EntityId) = getEntityOrNull(id) == null diff --git a/jacodb-api-jvm/src/main/kotlin/org/jacodb/api/jvm/storage/ers/readme.md b/jacodb-api-storage/src/main/kotlin/org/jacodb/api/storage/ers/readme.md similarity index 100% rename from jacodb-api-jvm/src/main/kotlin/org/jacodb/api/jvm/storage/ers/readme.md rename to jacodb-api-storage/src/main/kotlin/org/jacodb/api/storage/ers/readme.md diff --git a/jacodb-api-jvm/src/main/kotlin/org/jacodb/api/jvm/storage/ers/typed/ErsType.kt b/jacodb-api-storage/src/main/kotlin/org/jacodb/api/storage/ers/typed/ErsType.kt similarity index 100% rename from jacodb-api-jvm/src/main/kotlin/org/jacodb/api/jvm/storage/ers/typed/ErsType.kt rename to jacodb-api-storage/src/main/kotlin/org/jacodb/api/storage/ers/typed/ErsType.kt diff --git a/jacodb-api-jvm/src/main/kotlin/org/jacodb/api/jvm/storage/ers/typed/TypedErsApi.kt b/jacodb-api-storage/src/main/kotlin/org/jacodb/api/storage/ers/typed/TypedErsApi.kt similarity index 95% rename from jacodb-api-jvm/src/main/kotlin/org/jacodb/api/jvm/storage/ers/typed/TypedErsApi.kt rename to jacodb-api-storage/src/main/kotlin/org/jacodb/api/storage/ers/typed/TypedErsApi.kt index fcaf0f650..b9374abe5 100644 --- a/jacodb-api-jvm/src/main/kotlin/org/jacodb/api/jvm/storage/ers/typed/TypedErsApi.kt +++ b/jacodb-api-storage/src/main/kotlin/org/jacodb/api/storage/ers/typed/TypedErsApi.kt @@ -16,11 +16,11 @@ package org.jacodb.api.jvm.storage.ers.typed -import org.jacodb.api.jvm.storage.ers.Entity -import org.jacodb.api.jvm.storage.ers.EntityId -import org.jacodb.api.jvm.storage.ers.EntityIterable -import org.jacodb.api.jvm.storage.ers.FindOption -import org.jacodb.api.jvm.storage.ers.Transaction +import org.jacodb.api.storage.ers.Entity +import org.jacodb.api.storage.ers.EntityId +import org.jacodb.api.storage.ers.EntityIterable +import org.jacodb.api.storage.ers.FindOption +import org.jacodb.api.storage.ers.Transaction fun Transaction.newEntity(type: ENTITY_TYPE): TypedEntity = TypedEntityImpl(newEntity(type.typeName)) diff --git a/jacodb-api-jvm/src/main/kotlin/org/jacodb/api/jvm/storage/ers/typed/TypedErsImpl.kt b/jacodb-api-storage/src/main/kotlin/org/jacodb/api/storage/ers/typed/TypedErsImpl.kt similarity index 94% rename from jacodb-api-jvm/src/main/kotlin/org/jacodb/api/jvm/storage/ers/typed/TypedErsImpl.kt rename to jacodb-api-storage/src/main/kotlin/org/jacodb/api/storage/ers/typed/TypedErsImpl.kt index bdd9900e0..fbb526a95 100644 --- a/jacodb-api-jvm/src/main/kotlin/org/jacodb/api/jvm/storage/ers/typed/TypedErsImpl.kt +++ b/jacodb-api-storage/src/main/kotlin/org/jacodb/api/storage/ers/typed/TypedErsImpl.kt @@ -16,11 +16,11 @@ package org.jacodb.api.jvm.storage.ers.typed -import org.jacodb.api.jvm.storage.ers.Entity -import org.jacodb.api.jvm.storage.ers.EntityId -import org.jacodb.api.jvm.storage.ers.EntityIterable -import org.jacodb.api.jvm.storage.ers.Transaction -import org.jacodb.api.jvm.storage.ers.getBinding +import org.jacodb.api.storage.ers.Entity +import org.jacodb.api.storage.ers.EntityId +import org.jacodb.api.storage.ers.EntityIterable +import org.jacodb.api.storage.ers.Transaction +import org.jacodb.api.storage.ers.getBinding internal class TypedEntityIterableImpl( override val untypedIterable: EntityIterable diff --git a/jacodb-api-jvm/src/main/kotlin/org/jacodb/api/jvm/storage/ers/typed/readme.md b/jacodb-api-storage/src/main/kotlin/org/jacodb/api/storage/ers/typed/readme.md similarity index 100% rename from jacodb-api-jvm/src/main/kotlin/org/jacodb/api/jvm/storage/ers/typed/readme.md rename to jacodb-api-storage/src/main/kotlin/org/jacodb/api/storage/ers/typed/readme.md diff --git a/jacodb-api-jvm/src/main/kotlin/org/jacodb/api/jvm/storage/kv/Cursor.kt b/jacodb-api-storage/src/main/kotlin/org/jacodb/api/storage/kv/Cursor.kt similarity index 99% rename from jacodb-api-jvm/src/main/kotlin/org/jacodb/api/jvm/storage/kv/Cursor.kt rename to jacodb-api-storage/src/main/kotlin/org/jacodb/api/storage/kv/Cursor.kt index 31d4d90e9..6cf88619c 100644 --- a/jacodb-api-jvm/src/main/kotlin/org/jacodb/api/jvm/storage/kv/Cursor.kt +++ b/jacodb-api-storage/src/main/kotlin/org/jacodb/api/storage/kv/Cursor.kt @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.jacodb.api.jvm.storage.kv +package org.jacodb.api.storage.kv import java.io.Closeable diff --git a/jacodb-api-jvm/src/main/kotlin/org/jacodb/api/jvm/storage/kv/PluggableKeyValueStorage.kt b/jacodb-api-storage/src/main/kotlin/org/jacodb/api/storage/kv/PluggableKeyValueStorage.kt similarity index 87% rename from jacodb-api-jvm/src/main/kotlin/org/jacodb/api/jvm/storage/kv/PluggableKeyValueStorage.kt rename to jacodb-api-storage/src/main/kotlin/org/jacodb/api/storage/kv/PluggableKeyValueStorage.kt index 9949b2704..c1b261359 100644 --- a/jacodb-api-jvm/src/main/kotlin/org/jacodb/api/jvm/storage/kv/PluggableKeyValueStorage.kt +++ b/jacodb-api-storage/src/main/kotlin/org/jacodb/api/storage/kv/PluggableKeyValueStorage.kt @@ -14,12 +14,12 @@ * limitations under the License. */ -package org.jacodb.api.jvm.storage.kv +package org.jacodb.api.storage.kv -import org.jacodb.api.jvm.spi.CommonSPI -import org.jacodb.api.jvm.spi.SPILoader -import org.jacodb.api.jvm.storage.ers.EmptyErsSettings -import org.jacodb.api.jvm.storage.ers.ErsSettings +import org.jacodb.api.spi.CommonSPI +import org.jacodb.api.spi.SPILoader +import org.jacodb.api.storage.ers.EmptyErsSettings +import org.jacodb.api.storage.ers.ErsSettings import java.io.Closeable abstract class PluggableKeyValueStorage : Closeable { @@ -32,6 +32,13 @@ abstract class PluggableKeyValueStorage : Closeable { fun readonlyTransactional(action: (Transaction) -> T) = beginReadonlyTransaction().use(action) + /** + * Switches the storage to read-only mode and back. By default, does nothing. + */ + open var readonly: Boolean + get() = false + set(_) {} + fun get(map: String, key: ByteArray) = readonlyTransactional { txn -> txn.get(map, key) } fun put(map: String, key: ByteArray, value: ByteArray) = transactional { txn -> txn.put(map, key, value) } diff --git a/jacodb-api-jvm/src/main/kotlin/org/jacodb/api/jvm/storage/kv/Transaction.kt b/jacodb-api-storage/src/main/kotlin/org/jacodb/api/storage/kv/Transaction.kt similarity index 98% rename from jacodb-api-jvm/src/main/kotlin/org/jacodb/api/jvm/storage/kv/Transaction.kt rename to jacodb-api-storage/src/main/kotlin/org/jacodb/api/storage/kv/Transaction.kt index 67f472cfd..8f175a6f1 100644 --- a/jacodb-api-jvm/src/main/kotlin/org/jacodb/api/jvm/storage/kv/Transaction.kt +++ b/jacodb-api-storage/src/main/kotlin/org/jacodb/api/storage/kv/Transaction.kt @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.jacodb.api.jvm.storage.kv +package org.jacodb.api.storage.kv import java.io.Closeable diff --git a/jacodb-api-jvm/src/main/kotlin/org/jacodb/api/jvm/storage/kv/readme.md b/jacodb-api-storage/src/main/kotlin/org/jacodb/api/storage/kv/readme.md similarity index 100% rename from jacodb-api-jvm/src/main/kotlin/org/jacodb/api/jvm/storage/kv/readme.md rename to jacodb-api-storage/src/main/kotlin/org/jacodb/api/storage/kv/readme.md diff --git a/jacodb-approximations/build.gradle.kts b/jacodb-approximations/build.gradle.kts index 5e4d58b7b..c935b7b62 100644 --- a/jacodb-approximations/build.gradle.kts +++ b/jacodb-approximations/build.gradle.kts @@ -1,8 +1,9 @@ dependencies { implementation(project(":jacodb-api-jvm")) implementation(project(":jacodb-core")) - implementation(testFixtures(project(":jacodb-core"))) + testImplementation(testFixtures(project(":jacodb-core"))) + testImplementation(testFixtures(project(":jacodb-storage"))) testImplementation(Libs.kotlin_logging) testRuntimeOnly(Libs.guava) } diff --git a/jacodb-approximations/src/main/kotlin/org/jacodb/approximation/Approximations.kt b/jacodb-approximations/src/main/kotlin/org/jacodb/approximation/Approximations.kt index ff0c80bf0..8dbebb5a4 100644 --- a/jacodb-approximations/src/main/kotlin/org/jacodb/approximation/Approximations.kt +++ b/jacodb-approximations/src/main/kotlin/org/jacodb/approximation/Approximations.kt @@ -30,7 +30,7 @@ import org.jacodb.api.jvm.JcSignal import org.jacodb.api.jvm.RegisteredLocation import org.jacodb.api.jvm.cfg.JcInstList import org.jacodb.api.jvm.cfg.JcRawInst -import org.jacodb.api.jvm.storage.ers.compressed +import org.jacodb.api.storage.ers.compressed import org.jacodb.approximation.TransformerIntoVirtual.transformMethodIntoVirtual import org.jacodb.approximation.annotation.Approximate import org.jacodb.impl.cfg.JcInstListImpl diff --git a/jacodb-benchmarks/build.gradle.kts b/jacodb-benchmarks/build.gradle.kts index c0cb90042..39f7aa962 100644 --- a/jacodb-benchmarks/build.gradle.kts +++ b/jacodb-benchmarks/build.gradle.kts @@ -8,7 +8,6 @@ plugins { } dependencies { - implementation(project(":jacodb-api-jvm")) implementation(project(":jacodb-core")) implementation(testFixtures(project(":jacodb-core"))) @@ -20,6 +19,9 @@ dependencies { implementation(Libs.sootup_java_bytecode) testImplementation(Libs.kotlinx_benchmark_runtime) + testImplementation(Libs.xodusEnvironment) + testImplementation(Libs.lmdb_java) + testImplementation(Libs.rocks_db) } benchmark { diff --git a/jacodb-core/build.gradle.kts b/jacodb-core/build.gradle.kts index 29f0a064f..93ba05833 100644 --- a/jacodb-core/build.gradle.kts +++ b/jacodb-core/build.gradle.kts @@ -38,30 +38,25 @@ buildscript { } } -plugins { - `java-test-fixtures` -} - kotlin.sourceSets["main"].kotlin { srcDir("src/main/jooq") srcDir("src/main/ers/jooq") } dependencies { - implementation(project(":jacodb-api-jvm")) + api(project(":jacodb-api-jvm")) + compileOnly(project(":jacodb-storage")) implementation(Libs.kotlin_logging) - implementation(Libs.kotlinx_metadata_jvm) + implementation(Libs.kotlin_metadata_jvm) implementation(Libs.kotlinx_serialization_cbor) implementation(Libs.jdot) implementation(Libs.guava) implementation(Libs.sqlite) implementation(Libs.hikaricp) implementation(Libs.xodusUtils) - compileOnly(Libs.xodusEnvironment) - compileOnly(Libs.lmdb_java) - compileOnly(Libs.rocks_db) + testImplementation(testFixtures(project(":jacodb-storage"))) testImplementation(Libs.javax_activation) testImplementation(Libs.javax_mail) testImplementation(Libs.joda_time) @@ -77,6 +72,7 @@ dependencies { testFixturesImplementation(Libs.junit_jupiter) testFixturesImplementation(Libs.guava) testFixturesImplementation(Libs.jetbrains_annotations) + testFixturesImplementation(Libs.kotlin_logging) testFixturesImplementation(Libs.kotlinx_coroutines_core) testFixturesImplementation(Libs.jgit_test_only_lib) testFixturesImplementation(Libs.commons_compress_test_only_lib) diff --git a/jacodb-core/src/main/kotlin/org/jacodb/impl/JCDBSymbolsInternerImpl.kt b/jacodb-core/src/main/kotlin/org/jacodb/impl/JCDBSymbolsInternerImpl.kt index 48810a36f..3be17df5e 100644 --- a/jacodb-core/src/main/kotlin/org/jacodb/impl/JCDBSymbolsInternerImpl.kt +++ b/jacodb-core/src/main/kotlin/org/jacodb/impl/JCDBSymbolsInternerImpl.kt @@ -19,9 +19,9 @@ package org.jacodb.impl import org.jacodb.api.jvm.JCDBContext import org.jacodb.api.jvm.JCDBSymbolsInterner import org.jacodb.api.jvm.JcDatabasePersistence -import org.jacodb.api.jvm.storage.ers.compressed -import org.jacodb.api.jvm.storage.ers.nonSearchable -import org.jacodb.api.jvm.storage.kv.forEach +import org.jacodb.api.storage.ers.compressed +import org.jacodb.api.storage.ers.nonSearchable +import org.jacodb.api.storage.kv.forEach import org.jacodb.impl.storage.connection import org.jacodb.impl.storage.ers.BuiltInBindingProvider import org.jacodb.impl.storage.ers.decorators.unwrap diff --git a/jacodb-core/src/main/kotlin/org/jacodb/impl/JcDatabaseImpl.kt b/jacodb-core/src/main/kotlin/org/jacodb/impl/JcDatabaseImpl.kt index ae1b512cb..98b419aab 100644 --- a/jacodb-core/src/main/kotlin/org/jacodb/impl/JcDatabaseImpl.kt +++ b/jacodb-core/src/main/kotlin/org/jacodb/impl/JcDatabaseImpl.kt @@ -91,7 +91,7 @@ class JcDatabaseImpl( locationsRegistry.setup(runtime).new.process(false) locationsRegistry.registerIfNeeded( settings.predefinedDirOrJars.filter { it.exists() } - .map { it.asByteCodeLocation(javaRuntime.version, isRuntime = false) } + .flatMap { it.asByteCodeLocation(javaRuntime.version, isRuntime = false) }.distinct() ).new.process(true) } @@ -111,15 +111,12 @@ class JcDatabaseImpl( override suspend fun classpath(dirOrJars: List, features: List?): JcClasspath { assertNotClosed() - val existingLocations = dirOrJars.filterExisting().map { it.asByteCodeLocation(javaRuntime.version) } + val existingLocations = + dirOrJars.filterExisting().flatMap { it.asByteCodeLocation(javaRuntime.version) }.distinct() val processed = locationsRegistry.registerIfNeeded(existingLocations) .also { it.new.process(true) }.registered + locationsRegistry.runtimeLocations - return classpathOf(processed, features) - } - - override fun classpathOf(locations: List, features: List?): JcClasspath { return JcClasspathImpl( - locationsRegistry.newSnapshot(locations), + locationsRegistry.newSnapshot(processed), this, features.appendBuiltInFeatures(), classesVfs @@ -146,7 +143,7 @@ class JcDatabaseImpl( override suspend fun load(dirOrJars: List) = apply { assertNotClosed() - loadLocations(dirOrJars.filterExisting().map { it.asByteCodeLocation(javaRuntime.version) }) + loadLocations(dirOrJars.filterExisting().flatMap { it.asByteCodeLocation(javaRuntime.version) }.distinct()) } override suspend fun loadLocations(locations: List) = apply { diff --git a/jacodb-core/src/main/kotlin/org/jacodb/impl/JcDatabasePersistenceSPI.kt b/jacodb-core/src/main/kotlin/org/jacodb/impl/JcDatabasePersistenceSPI.kt index 8b3f56a7f..f1599c19a 100644 --- a/jacodb-core/src/main/kotlin/org/jacodb/impl/JcDatabasePersistenceSPI.kt +++ b/jacodb-core/src/main/kotlin/org/jacodb/impl/JcDatabasePersistenceSPI.kt @@ -18,8 +18,8 @@ package org.jacodb.impl import org.jacodb.api.jvm.JcDatabase import org.jacodb.api.jvm.JcDatabasePersistence -import org.jacodb.api.jvm.spi.CommonSPI -import org.jacodb.api.jvm.spi.SPILoader +import org.jacodb.api.spi.CommonSPI +import org.jacodb.api.spi.SPILoader import org.jacodb.impl.fs.JavaRuntime class JcDatabaseException(message: String) : RuntimeException(message) diff --git a/jacodb-core/src/main/kotlin/org/jacodb/impl/JcSettings.kt b/jacodb-core/src/main/kotlin/org/jacodb/impl/JcSettings.kt index 3ca6fac6f..6378987c2 100644 --- a/jacodb-core/src/main/kotlin/org/jacodb/impl/JcSettings.kt +++ b/jacodb-core/src/main/kotlin/org/jacodb/impl/JcSettings.kt @@ -23,15 +23,14 @@ import org.jacodb.api.jvm.JcDatabase import org.jacodb.api.jvm.JcFeature import org.jacodb.api.jvm.JcPersistenceImplSettings import org.jacodb.api.jvm.JcPersistenceSettings -import org.jacodb.api.jvm.storage.ers.EmptyErsSettings -import org.jacodb.api.jvm.storage.ers.ErsSettings +import org.jacodb.api.storage.ers.EmptyErsSettings +import org.jacodb.api.storage.ers.ErsSettings import org.jacodb.impl.caches.guava.GUAVA_CACHE_PROVIDER_ID import org.jacodb.impl.storage.SQLITE_DATABASE_PERSISTENCE_SPI import org.jacodb.impl.storage.ers.ERS_DATABASE_PERSISTENCE_SPI import org.jacodb.impl.storage.ers.kv.KV_ERS_SPI import org.jacodb.impl.storage.ers.ram.RAM_ERS_SPI import org.jacodb.impl.storage.ers.sql.SQL_ERS_SPI -import org.jacodb.impl.storage.kv.lmdb.LMDB_KEY_VALUE_STORAGE_SPI import org.jacodb.impl.storage.kv.rocks.ROCKS_KEY_VALUE_STORAGE_SPI import org.jacodb.impl.storage.kv.xodus.XODUS_KEY_VALUE_STORAGE_SPI import java.io.File @@ -244,18 +243,10 @@ object JcRamErsSettings : JcErsSettings(RAM_ERS_SPI) object JcSqlErsSettings : JcErsSettings(SQL_ERS_SPI) -/** - * Id of pluggable K/V storage being passed for [org.jacodb.impl.storage.ers.kv.KVEntityRelationshipStorageSPI]. - */ -open class JcKvErsSettings(val kvId: String) : ErsSettings - object JcXodusKvErsSettings : JcErsSettings(KV_ERS_SPI, JcKvErsSettings(XODUS_KEY_VALUE_STORAGE_SPI)) object JcRocksKvErsSettings : JcErsSettings(KV_ERS_SPI, JcKvErsSettings(ROCKS_KEY_VALUE_STORAGE_SPI)) -// by default, mapSize is 1Gb -class JcLmdbErsSettings(val mapSize: Long = 0x40_00_00_00) : JcKvErsSettings(LMDB_KEY_VALUE_STORAGE_SPI) - object JcLmdbKvErsSettings : JcErsSettings(KV_ERS_SPI, JcLmdbErsSettings()) { fun withMapSize(mapSize: Long): JcErsSettings { diff --git a/jacodb-core/src/main/kotlin/org/jacodb/impl/bytecode/KMetadata.kt b/jacodb-core/src/main/kotlin/org/jacodb/impl/bytecode/KMetadata.kt index 050e4ec23..316f927bf 100644 --- a/jacodb-core/src/main/kotlin/org/jacodb/impl/bytecode/KMetadata.kt +++ b/jacodb-core/src/main/kotlin/org/jacodb/impl/bytecode/KMetadata.kt @@ -16,12 +16,12 @@ package org.jacodb.impl.bytecode -import kotlinx.metadata.KmConstructor -import kotlinx.metadata.KmFunction -import kotlinx.metadata.KmType -import kotlinx.metadata.KmValueParameter -import kotlinx.metadata.jvm.fieldSignature -import kotlinx.metadata.jvm.signature +import kotlin.metadata.KmConstructor +import kotlin.metadata.KmFunction +import kotlin.metadata.KmType +import kotlin.metadata.KmValueParameter +import kotlin.metadata.jvm.fieldSignature +import kotlin.metadata.jvm.signature import mu.KLogging import org.jacodb.api.jvm.JcClassOrInterface import org.jacodb.api.jvm.JcField diff --git a/jacodb-api-jvm/src/main/kotlin/org/jacodb/api/jvm/ext/Nullables.kt b/jacodb-core/src/main/kotlin/org/jacodb/impl/bytecode/Nullables.kt similarity index 89% rename from jacodb-api-jvm/src/main/kotlin/org/jacodb/api/jvm/ext/Nullables.kt rename to jacodb-core/src/main/kotlin/org/jacodb/impl/bytecode/Nullables.kt index 4a690abc2..750703478 100644 --- a/jacodb-api-jvm/src/main/kotlin/org/jacodb/api/jvm/ext/Nullables.kt +++ b/jacodb-core/src/main/kotlin/org/jacodb/impl/bytecode/Nullables.kt @@ -16,7 +16,7 @@ @file:JvmName("Nullables") -package org.jacodb.api.jvm.ext +package org.jacodb.impl.bytecode import org.jacodb.api.jvm.JcAnnotated import org.jacodb.api.jvm.JcAnnotation @@ -25,6 +25,7 @@ import org.jacodb.api.jvm.JcMethod import org.jacodb.api.jvm.JcParameter import org.jacodb.api.jvm.PredefinedPrimitives import org.jacodb.api.jvm.TypeName +import kotlin.metadata.isNullable val JcAnnotation.isNotNullAnnotation: Boolean get() = NullabilityAnnotations.notNullAnnotations.any { matches(it) } @@ -54,10 +55,10 @@ private fun JcAnnotated.isNullable(type: TypeName): Boolean? = // TODO: maybe move these methods from ext into class definitions? // We already have many nullability-related methods there, furthermore this way we can use jacodb-core in implementation val JcField.isNullable: Boolean? - get() = isNullable(type) + get() = isNullable(type) ?: kmType?.isNullable val JcParameter.isNullable: Boolean? - get() = isNullable(type) + get() = isNullable(type) ?: kmType?.isNullable val JcMethod.isNullable: Boolean? - get() = isNullable(returnType) + get() = isNullable(returnType) ?: kmReturnType?.isNullable diff --git a/jacodb-core/src/main/kotlin/org/jacodb/impl/caches/PluggableCacheProvider.kt b/jacodb-core/src/main/kotlin/org/jacodb/impl/caches/PluggableCacheProvider.kt index 017142a2a..4020dc887 100644 --- a/jacodb-core/src/main/kotlin/org/jacodb/impl/caches/PluggableCacheProvider.kt +++ b/jacodb-core/src/main/kotlin/org/jacodb/impl/caches/PluggableCacheProvider.kt @@ -16,8 +16,8 @@ package org.jacodb.impl.caches -import org.jacodb.api.jvm.spi.CommonSPI -import org.jacodb.api.jvm.spi.SPILoader +import org.jacodb.api.spi.CommonSPI +import org.jacodb.api.spi.SPILoader /** * Service Provider Interface to load pluggable implementation of [PluggableCacheBuilder] and [PluggableCache] diff --git a/jacodb-core/src/main/kotlin/org/jacodb/impl/features/Builders.kt b/jacodb-core/src/main/kotlin/org/jacodb/impl/features/Builders.kt index e58672c75..6c8ad590d 100644 --- a/jacodb-core/src/main/kotlin/org/jacodb/impl/features/Builders.kt +++ b/jacodb-core/src/main/kotlin/org/jacodb/impl/features/Builders.kt @@ -26,7 +26,7 @@ import org.jacodb.api.jvm.JcFeature import org.jacodb.api.jvm.JcSignal import org.jacodb.api.jvm.RegisteredLocation import org.jacodb.api.jvm.ext.jvmPrimitiveNames -import org.jacodb.api.jvm.storage.ers.compressed +import org.jacodb.api.storage.ers.compressed import org.jacodb.impl.fs.PersistenceClassSource import org.jacodb.impl.fs.className import org.jacodb.impl.storage.execute diff --git a/jacodb-core/src/main/kotlin/org/jacodb/impl/features/HierarchyExtension.kt b/jacodb-core/src/main/kotlin/org/jacodb/impl/features/HierarchyExtension.kt index 020fa4c05..f67141f07 100644 --- a/jacodb-core/src/main/kotlin/org/jacodb/impl/features/HierarchyExtension.kt +++ b/jacodb-core/src/main/kotlin/org/jacodb/impl/features/HierarchyExtension.kt @@ -29,11 +29,11 @@ import org.jacodb.api.jvm.JcMethod import org.jacodb.api.jvm.ext.HierarchyExtension import org.jacodb.api.jvm.ext.JAVA_OBJECT import org.jacodb.api.jvm.ext.findDeclaredMethodOrNull -import org.jacodb.api.jvm.storage.ers.CollectionEntityIterable -import org.jacodb.api.jvm.storage.ers.Entity -import org.jacodb.api.jvm.storage.ers.EntityIterable -import org.jacodb.api.jvm.storage.ers.Transaction -import org.jacodb.api.jvm.storage.ers.compressed +import org.jacodb.api.storage.ers.CollectionEntityIterable +import org.jacodb.api.storage.ers.Entity +import org.jacodb.api.storage.ers.EntityIterable +import org.jacodb.api.storage.ers.Transaction +import org.jacodb.api.storage.ers.compressed import org.jacodb.impl.asSymbolId import org.jacodb.impl.fs.PersistenceClassSource import org.jacodb.impl.storage.BatchedSequence diff --git a/jacodb-core/src/main/kotlin/org/jacodb/impl/features/InMemoryHierarchy.kt b/jacodb-core/src/main/kotlin/org/jacodb/impl/features/InMemoryHierarchy.kt index aad98739a..3fa9630e2 100644 --- a/jacodb-core/src/main/kotlin/org/jacodb/impl/features/InMemoryHierarchy.kt +++ b/jacodb-core/src/main/kotlin/org/jacodb/impl/features/InMemoryHierarchy.kt @@ -27,8 +27,8 @@ import org.jacodb.api.jvm.JcFeature import org.jacodb.api.jvm.JcSignal import org.jacodb.api.jvm.RegisteredLocation import org.jacodb.api.jvm.ext.JAVA_OBJECT -import org.jacodb.api.jvm.storage.ers.compressed -import org.jacodb.api.jvm.storage.ers.links +import org.jacodb.api.storage.ers.compressed +import org.jacodb.api.storage.ers.links import org.jacodb.impl.asSymbolId import org.jacodb.impl.fs.PersistenceClassSource import org.jacodb.impl.fs.className diff --git a/jacodb-core/src/main/kotlin/org/jacodb/impl/features/Usages.kt b/jacodb-core/src/main/kotlin/org/jacodb/impl/features/Usages.kt index 7ca126561..3ee854efc 100644 --- a/jacodb-core/src/main/kotlin/org/jacodb/impl/features/Usages.kt +++ b/jacodb-core/src/main/kotlin/org/jacodb/impl/features/Usages.kt @@ -17,9 +17,9 @@ package org.jacodb.impl.features import org.jacodb.api.jvm.* -import org.jacodb.api.jvm.storage.ers.compressed -import org.jacodb.api.jvm.storage.ers.links -import org.jacodb.api.jvm.storage.ers.nonSearchable +import org.jacodb.api.storage.ers.compressed +import org.jacodb.api.storage.ers.links +import org.jacodb.api.storage.ers.nonSearchable import org.jacodb.impl.asSymbolId import org.jacodb.impl.fs.PersistenceClassSource import org.jacodb.impl.fs.className diff --git a/jacodb-core/src/main/kotlin/org/jacodb/impl/features/classpaths/KotlinMetadata.kt b/jacodb-core/src/main/kotlin/org/jacodb/impl/features/classpaths/KotlinMetadata.kt index a8a35ad5b..a74af15f3 100644 --- a/jacodb-core/src/main/kotlin/org/jacodb/impl/features/classpaths/KotlinMetadata.kt +++ b/jacodb-core/src/main/kotlin/org/jacodb/impl/features/classpaths/KotlinMetadata.kt @@ -16,11 +16,11 @@ package org.jacodb.impl.features.classpaths -import kotlinx.metadata.KmConstructor -import kotlinx.metadata.KmFunction -import kotlinx.metadata.KmProperty -import kotlinx.metadata.KmTypeParameter -import kotlinx.metadata.jvm.KotlinClassMetadata +import kotlin.metadata.KmConstructor +import kotlin.metadata.KmFunction +import kotlin.metadata.KmProperty +import kotlin.metadata.KmTypeParameter +import kotlin.metadata.jvm.KotlinClassMetadata import org.jacodb.api.jvm.JcClassExtFeature import org.jacodb.api.jvm.JcClassOrInterface import org.jacodb.api.jvm.ext.annotation @@ -46,7 +46,7 @@ object KotlinMetadata : JcClassExtFeature { private val JcClassOrInterface.kMetadata: KotlinClassMetadata? get() { val kmParameters = annotation("kotlin.Metadata")?.values ?: return null - val metadata = kotlinx.metadata.jvm.Metadata( + val metadata = kotlin.metadata.jvm.Metadata( kmParameters["k"] as? Int, (kmParameters["mv"] as? List)?.toIntArray(), (kmParameters["d1"] as? List)?.toTypedArray(), diff --git a/jacodb-core/src/main/kotlin/org/jacodb/impl/fs/ByteCodeLocations.kt b/jacodb-core/src/main/kotlin/org/jacodb/impl/fs/ByteCodeLocations.kt index 78480c6ed..a7364e5b9 100644 --- a/jacodb-core/src/main/kotlin/org/jacodb/impl/fs/ByteCodeLocations.kt +++ b/jacodb-core/src/main/kotlin/org/jacodb/impl/fs/ByteCodeLocations.kt @@ -20,22 +20,30 @@ import mu.KLogging import org.jacodb.api.jvm.JavaVersion import org.jacodb.api.jvm.JcByteCodeLocation import java.io.File +import java.nio.file.Paths +import java.util.jar.JarFile val logger = object : KLogging() {}.logger -fun File.asByteCodeLocation(runtimeVersion: JavaVersion, isRuntime: Boolean = false): JcByteCodeLocation { +/** + * Returns collection of `JcByteCodeLocation` of a file or directory. + * Any jar file can have its own classpath defined in the manifest, that's why the method returns collection. + * The method called of different files can have same locations in the result, so use `distinct()` to + * filter duplicates out. + */ +fun File.asByteCodeLocation(runtimeVersion: JavaVersion, isRuntime: Boolean = false): Collection { if (!exists()) { throw IllegalArgumentException("file $absolutePath doesn't exist") } if (isFile && name.endsWith(".jar") || name.endsWith(".jmod")) { - return JarLocation(this, isRuntime, runtimeVersion) - } else if (!isFile) { - return BuildFolderLocation(this) + return mutableSetOf().also { classPath(it) }.map { JarLocation(it, isRuntime, runtimeVersion) } + } else if (isDirectory) { + return listOf(BuildFolderLocation(this)) } - throw IllegalArgumentException("file $absolutePath is not jar-file nor build dir folder") + error("$absolutePath is nether a jar file nor a build directory") } -fun List.filterExisting(): List = filter { file -> +fun Collection.filterExisting(): List = filter { file -> file.exists().also { if (!it) { logger.warn("${file.absolutePath} doesn't exists. make sure there is no mistake") @@ -43,9 +51,14 @@ fun List.filterExisting(): List = filter { file -> } } -fun String.matchesOneOf(loadClassesOnlyFrom: List?): Boolean { - loadClassesOnlyFrom ?: return true - return loadClassesOnlyFrom.any { - startsWith(it) +private fun File.classPath(classpath: MutableCollection) { + if (exists() && classpath.add(this)) { + JarFile(this).use { jarFile -> + jarFile.manifest?.mainAttributes?.getValue("Class-Path")?.split(' ')?.forEach { ref -> + Paths.get( + if (ref.startsWith("file:")) ref.substring("file:".length) else ref + ).toFile().classPath(classpath) + } + } } -} +} \ No newline at end of file diff --git a/jacodb-core/src/main/kotlin/org/jacodb/impl/fs/JavaRuntime.kt b/jacodb-core/src/main/kotlin/org/jacodb/impl/fs/JavaRuntime.kt index e5dacd880..bc24ac97b 100644 --- a/jacodb-core/src/main/kotlin/org/jacodb/impl/fs/JavaRuntime.kt +++ b/jacodb-core/src/main/kotlin/org/jacodb/impl/fs/JavaRuntime.kt @@ -64,7 +64,7 @@ class JavaRuntime(private val javaHome: File) { .listFiles { file -> file.name.endsWith(".jar") || file.name.endsWith(".jmod") } .orEmpty() .toList() - .map { it.asByteCodeLocation(version, true) } + .flatMap { it.asByteCodeLocation(version, true) } + .distinct() } - } diff --git a/jacodb-core/src/main/kotlin/org/jacodb/impl/storage/AbstractJcDbPersistence.kt b/jacodb-core/src/main/kotlin/org/jacodb/impl/storage/AbstractJcDbPersistence.kt index e568a1439..b23288634 100644 --- a/jacodb-core/src/main/kotlin/org/jacodb/impl/storage/AbstractJcDbPersistence.kt +++ b/jacodb-core/src/main/kotlin/org/jacodb/impl/storage/AbstractJcDbPersistence.kt @@ -19,7 +19,7 @@ package org.jacodb.impl.storage import org.jacodb.api.jvm.JcByteCodeLocation import org.jacodb.api.jvm.JcDatabasePersistence import org.jacodb.api.jvm.RegisteredLocation -import org.jacodb.api.jvm.storage.ers.getEntityOrNull +import org.jacodb.api.storage.ers.getEntityOrNull import org.jacodb.impl.JCDBSymbolsInternerImpl import org.jacodb.impl.asSymbolId import org.jacodb.impl.caches.PluggableCache @@ -78,7 +78,7 @@ abstract class AbstractJcDbPersistence( } catch (e: Exception) { null } - } + }.flatten().distinct() } } @@ -145,7 +145,8 @@ abstract class AbstractJcDbPersistence( return read { context -> context.execute( sqlAction = { jooq -> - val hasBytecodeLocations = jooq.meta().tables.any { it.name.equals(BYTECODELOCATIONS.name, true) } + val hasBytecodeLocations = + jooq.meta().tables.any { it.name.equals(BYTECODELOCATIONS.name, true) } if (!hasBytecodeLocations) { return@execute false } diff --git a/jacodb-core/src/main/kotlin/org/jacodb/impl/storage/JCDBContexts.kt b/jacodb-core/src/main/kotlin/org/jacodb/impl/storage/JCDBContexts.kt index be37d8350..c5b48fe25 100644 --- a/jacodb-core/src/main/kotlin/org/jacodb/impl/storage/JCDBContexts.kt +++ b/jacodb-core/src/main/kotlin/org/jacodb/impl/storage/JCDBContexts.kt @@ -19,7 +19,7 @@ package org.jacodb.impl.storage import org.jacodb.api.jvm.ContextProperty import org.jacodb.api.jvm.JCDBContext import org.jacodb.api.jvm.invoke -import org.jacodb.api.jvm.storage.ers.Transaction +import org.jacodb.api.storage.ers.Transaction import org.jooq.DSLContext import java.sql.Connection diff --git a/jacodb-core/src/main/kotlin/org/jacodb/impl/storage/PersistentByteCodeLocation.kt b/jacodb-core/src/main/kotlin/org/jacodb/impl/storage/PersistentByteCodeLocation.kt index ed7c8395c..693e2e3af 100644 --- a/jacodb-core/src/main/kotlin/org/jacodb/impl/storage/PersistentByteCodeLocation.kt +++ b/jacodb-core/src/main/kotlin/org/jacodb/impl/storage/PersistentByteCodeLocation.kt @@ -21,8 +21,8 @@ import org.jacodb.api.jvm.JcByteCodeLocation import org.jacodb.api.jvm.JcDatabase import org.jacodb.api.jvm.JcDatabasePersistence import org.jacodb.api.jvm.RegisteredLocation -import org.jacodb.api.jvm.storage.ers.Entity -import org.jacodb.api.jvm.storage.ers.getEntityOrNull +import org.jacodb.api.storage.ers.Entity +import org.jacodb.api.storage.ers.getEntityOrNull import org.jacodb.impl.fs.asByteCodeLocation import org.jacodb.impl.storage.jooq.tables.records.BytecodelocationsRecord import org.jacodb.impl.storage.jooq.tables.references.BYTECODELOCATIONS @@ -117,8 +117,8 @@ class PersistentByteCodeLocation( private fun PersistentByteCodeLocationData.toJcLocation(): JcByteCodeLocation? { try { - val newOne = File(path).asByteCodeLocation(runtimeVersion, isRuntime = runtime) - if (newOne.fileSystemId != fileSystemId) { + val newOne = File(path).asByteCodeLocation(runtimeVersion, isRuntime = runtime).singleOrNull() + if (newOne?.fileSystemId != fileSystemId) { return null } return newOne diff --git a/jacodb-core/src/main/kotlin/org/jacodb/impl/storage/PersistentLocationsRegistry.kt b/jacodb-core/src/main/kotlin/org/jacodb/impl/storage/PersistentLocationsRegistry.kt index e3f61fa6a..b565ffbc3 100644 --- a/jacodb-core/src/main/kotlin/org/jacodb/impl/storage/PersistentLocationsRegistry.kt +++ b/jacodb-core/src/main/kotlin/org/jacodb/impl/storage/PersistentLocationsRegistry.kt @@ -20,7 +20,7 @@ import org.jacodb.api.jvm.JCDBContext import org.jacodb.api.jvm.JcByteCodeLocation import org.jacodb.api.jvm.LocationType import org.jacodb.api.jvm.RegisteredLocation -import org.jacodb.api.jvm.storage.ers.getEntityOrNull +import org.jacodb.api.storage.ers.getEntityOrNull import org.jacodb.impl.CleanupResult import org.jacodb.impl.JcDatabaseImpl import org.jacodb.impl.JcInternalSignal diff --git a/jacodb-core/src/main/kotlin/org/jacodb/impl/storage/SQLitePersistenceImpl.kt b/jacodb-core/src/main/kotlin/org/jacodb/impl/storage/SQLitePersistenceImpl.kt index ae52fa284..16c6a7164 100644 --- a/jacodb-core/src/main/kotlin/org/jacodb/impl/storage/SQLitePersistenceImpl.kt +++ b/jacodb-core/src/main/kotlin/org/jacodb/impl/storage/SQLitePersistenceImpl.kt @@ -22,7 +22,7 @@ import org.jacodb.api.jvm.JCDBContext import org.jacodb.api.jvm.JcClasspath import org.jacodb.api.jvm.JcDatabase import org.jacodb.api.jvm.RegisteredLocation -import org.jacodb.api.jvm.storage.ers.EntityRelationshipStorage +import org.jacodb.api.storage.ers.EntityRelationshipStorage import org.jacodb.impl.JCDBSymbolsInternerImpl import org.jacodb.impl.fs.JavaRuntime import org.jacodb.impl.fs.PersistenceClassSource @@ -51,7 +51,12 @@ class SQLitePersistenceImpl( internal val jooq = DSL.using(connection, SQLDialect.SQLITE, Settings().withExecuteLogging(false)) private val lock = ReentrantLock() private val persistenceService = SQLitePersistenceService(this) - override val ers: EntityRelationshipStorage = SqlEntityRelationshipStorage(dataSource, BuiltInBindingProvider) + override val ers: EntityRelationshipStorage by lazy { + SqlEntityRelationshipStorage( + dataSource, + BuiltInBindingProvider + ) + } companion object { private val logger = KotlinLogging.logger {} @@ -116,21 +121,26 @@ class SQLitePersistenceImpl( return CLASSES.LOCATION_ID.`in`(registeredLocationIds) } - private fun JcDatabase.classSources(clause: Condition, single: Boolean = false): List = read { context -> - val jooq = context.dslContext - val classesQuery = - jooq.select(CLASSES.LOCATION_ID, CLASSES.ID, CLASSES.BYTECODE, SYMBOLS.NAME).from(CLASSES).join(SYMBOLS) - .on(CLASSES.NAME.eq(SYMBOLS.ID)).where(clause) - val classes = when { - single -> listOfNotNull(classesQuery.fetchAny()) - else -> classesQuery.fetch() - } - classes.map { (locationId, classId, bytecode, name) -> - PersistenceClassSource( - db = this, className = name!!, classId = classId!!, locationId = locationId!!, cachedByteCode = bytecode - ) + private fun JcDatabase.classSources(clause: Condition, single: Boolean = false): List = + read { context -> + val jooq = context.dslContext + val classesQuery = + jooq.select(CLASSES.LOCATION_ID, CLASSES.ID, CLASSES.BYTECODE, SYMBOLS.NAME).from(CLASSES).join(SYMBOLS) + .on(CLASSES.NAME.eq(SYMBOLS.ID)).where(clause) + val classes = when { + single -> listOfNotNull(classesQuery.fetchAny()) + else -> classesQuery.fetch() + } + classes.map { (locationId, classId, bytecode, name) -> + PersistenceClassSource( + db = this, + className = name!!, + classId = classId!!, + locationId = locationId!!, + cachedByteCode = bytecode + ) + } } - } } fun String.sqlScript(): String { diff --git a/jacodb-core/src/main/kotlin/org/jacodb/impl/storage/ers/ErsDatabasePersistenceSPI.kt b/jacodb-core/src/main/kotlin/org/jacodb/impl/storage/ers/ErsDatabasePersistenceSPI.kt index 76ae209df..aec03ac90 100644 --- a/jacodb-core/src/main/kotlin/org/jacodb/impl/storage/ers/ErsDatabasePersistenceSPI.kt +++ b/jacodb-core/src/main/kotlin/org/jacodb/impl/storage/ers/ErsDatabasePersistenceSPI.kt @@ -18,7 +18,7 @@ package org.jacodb.impl.storage.ers import org.jacodb.api.jvm.JcDatabase import org.jacodb.api.jvm.JcDatabasePersistence -import org.jacodb.api.jvm.storage.ers.EntityRelationshipStorageSPI +import org.jacodb.api.storage.ers.EntityRelationshipStorageSPI import org.jacodb.impl.JcDatabaseImpl import org.jacodb.impl.JcDatabasePersistenceSPI import org.jacodb.impl.JcErsSettings diff --git a/jacodb-core/src/main/kotlin/org/jacodb/impl/storage/ers/ErsExt.kt b/jacodb-core/src/main/kotlin/org/jacodb/impl/storage/ers/ErsExt.kt index 2ae670765..f9c4e91c5 100644 --- a/jacodb-core/src/main/kotlin/org/jacodb/impl/storage/ers/ErsExt.kt +++ b/jacodb-core/src/main/kotlin/org/jacodb/impl/storage/ers/ErsExt.kt @@ -18,7 +18,7 @@ package org.jacodb.impl.storage.ers import org.jacodb.api.jvm.ClassSource import org.jacodb.api.jvm.JcDatabase -import org.jacodb.api.jvm.storage.ers.Entity +import org.jacodb.api.storage.ers.Entity import org.jacodb.impl.fs.PersistenceClassSource fun Sequence.toClassSourceSequence(db: JcDatabase): Sequence { diff --git a/jacodb-core/src/main/kotlin/org/jacodb/impl/storage/ers/ErsPersistenceImpl.kt b/jacodb-core/src/main/kotlin/org/jacodb/impl/storage/ers/ErsPersistenceImpl.kt index b9a891a3c..7ed22b057 100644 --- a/jacodb-core/src/main/kotlin/org/jacodb/impl/storage/ers/ErsPersistenceImpl.kt +++ b/jacodb-core/src/main/kotlin/org/jacodb/impl/storage/ers/ErsPersistenceImpl.kt @@ -23,13 +23,13 @@ import org.jacodb.api.jvm.JcClasspath import org.jacodb.api.jvm.JcDatabase import org.jacodb.api.jvm.RegisteredLocation import org.jacodb.api.jvm.ext.JAVA_OBJECT -import org.jacodb.api.jvm.storage.ers.Entity -import org.jacodb.api.jvm.storage.ers.EntityRelationshipStorage -import org.jacodb.api.jvm.storage.ers.Transaction -import org.jacodb.api.jvm.storage.ers.compressed -import org.jacodb.api.jvm.storage.ers.findOrNew -import org.jacodb.api.jvm.storage.ers.links -import org.jacodb.api.jvm.storage.ers.nonSearchable +import org.jacodb.api.storage.ers.Entity +import org.jacodb.api.storage.ers.EntityRelationshipStorage +import org.jacodb.api.storage.ers.Transaction +import org.jacodb.api.storage.ers.compressed +import org.jacodb.api.storage.ers.findOrNew +import org.jacodb.api.storage.ers.links +import org.jacodb.api.storage.ers.nonSearchable import org.jacodb.impl.JCDBSymbolsInternerImpl import org.jacodb.impl.asSymbolId import org.jacodb.impl.fs.JavaRuntime @@ -37,7 +37,6 @@ import org.jacodb.impl.fs.PersistenceClassSource import org.jacodb.impl.fs.info import org.jacodb.impl.storage.AbstractJcDbPersistence import org.jacodb.impl.storage.AnnotationValueKind -import org.jacodb.impl.storage.ers.ram.RAMEntityRelationshipStorage import org.jacodb.impl.storage.toJCDBContext import org.jacodb.impl.storage.txn import org.jacodb.impl.types.AnnotationInfo @@ -53,7 +52,7 @@ import kotlin.concurrent.withLock class ErsPersistenceImpl( javaRuntime: JavaRuntime, clearOnStart: Boolean, - override val ers: EntityRelationshipStorage, + override var ers: EntityRelationshipStorage, ) : AbstractJcDbPersistence(javaRuntime) { companion object { private val logger = KotlinLogging.logger {} @@ -76,7 +75,7 @@ class ErsPersistenceImpl( } override fun read(action: (JCDBContext) -> T): T { - return if (ers is RAMEntityRelationshipStorage) { // RAMEntityRelationshipStorage doesn't support readonly transactions + return if (ers.isInRam) { // RAM storage doesn't support explicit readonly transactions ers.transactionalOptimistic(attempts = 10) { txn -> action(toJCDBContext(txn)) } @@ -234,6 +233,10 @@ class ErsPersistenceImpl( } } + override fun setImmutable() { + ers = ers.asReadonly + } + override fun close() { try { ers.close() diff --git a/jacodb-core/src/main/kotlin/org/jacodb/impl/storage/ers/ram/EntityIdSet.kt b/jacodb-core/src/main/kotlin/org/jacodb/impl/storage/ers/ram/EntityIdSet.kt deleted file mode 100644 index 49ae02428..000000000 --- a/jacodb-core/src/main/kotlin/org/jacodb/impl/storage/ers/ram/EntityIdSet.kt +++ /dev/null @@ -1,53 +0,0 @@ -/* - * Copyright 2022 UnitTestBot contributors (utbot.org) - *

- * 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 org.jacodb.impl.storage.ers.ram - -import org.jacodb.api.jvm.storage.ers.EntityId - -internal class EntityIdSet( - private var typeId: Int = -1, - private val instances: CompactPersistentLongSet = CompactPersistentLongSet() -) { - - val size: Int get() = instances.size - - val isEmpty: Boolean get() = instances.isEmpty() - - fun toList(): List { - return instances.map { EntityId(typeId, it) } - } - - fun add(id: EntityId): EntityIdSet { - val typeId = checkTypeId(id) - val newInstances = instances.add(id.instanceId) - return if (newInstances === instances) this else EntityIdSet(typeId, newInstances) - } - - fun remove(id: EntityId): EntityIdSet { - val typeId = checkTypeId(id) - val newInstances = instances.remove(id.instanceId) - return if (newInstances === instances) this else EntityIdSet(typeId, newInstances) - } - - private fun checkTypeId(id: EntityId): Int { - val typeId = id.typeId - if (this.typeId != -1 && this.typeId != typeId) { - throw IllegalStateException("EntityIdSet can only store ids of the same typeId") - } - return typeId - } -} \ No newline at end of file diff --git a/jacodb-core/src/main/kotlin/org/jacodb/impl/storage/ers/ram/Links.kt b/jacodb-core/src/main/kotlin/org/jacodb/impl/storage/ers/ram/Links.kt deleted file mode 100644 index 12ce0d40e..000000000 --- a/jacodb-core/src/main/kotlin/org/jacodb/impl/storage/ers/ram/Links.kt +++ /dev/null @@ -1,54 +0,0 @@ -/* - * Copyright 2022 UnitTestBot contributors (utbot.org) - *

- * 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 org.jacodb.impl.storage.ers.ram - -import jetbrains.exodus.core.dataStructures.persistent.PersistentLong23TreeMap -import jetbrains.exodus.core.dataStructures.persistent.PersistentLongMap -import org.jacodb.api.jvm.storage.ers.EntityId -import org.jacodb.api.jvm.storage.ers.EntityIdCollectionEntityIterable -import org.jacodb.api.jvm.storage.ers.EntityIterable - -internal class Links(private val links: PersistentLongMap = PersistentLong23TreeMap()) { - - internal fun getLinks(txn: RAMTransaction, instanceId: Long): EntityIterable { - return EntityIdCollectionEntityIterable(txn, (links[instanceId]?.toList() ?: return EntityIterable.EMPTY)) - } - - internal fun addLink(instanceId: Long, targetId: EntityId): Links { - val idSet = links[instanceId] ?: EntityIdSet() - val newIdSet = idSet.add(targetId) - return if (idSet === newIdSet) this else Links(links.write { put(instanceId, newIdSet) }.second) - } - - fun deleteLink(instanceId: Long, targetId: EntityId): Links { - val idSet = links[instanceId] ?: return this - val newIdSet = idSet.remove(targetId) - return if (idSet === newIdSet) { - this - } else { - Links( - links.write { - if (newIdSet.isEmpty) { - remove(instanceId) - } else { - put(instanceId, newIdSet) - } - }.second - ) - } - } -} \ No newline at end of file diff --git a/jacodb-core/src/main/kotlin/org/jacodb/impl/storage/ers/ram/XodusPersistentCollectionsEx.kt b/jacodb-core/src/main/kotlin/org/jacodb/impl/storage/ers/ram/XodusPersistentCollectionsEx.kt deleted file mode 100644 index 4e5cbdc59..000000000 --- a/jacodb-core/src/main/kotlin/org/jacodb/impl/storage/ers/ram/XodusPersistentCollectionsEx.kt +++ /dev/null @@ -1,147 +0,0 @@ -/* - * Copyright 2022 UnitTestBot contributors (utbot.org) - *

- * 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 org.jacodb.impl.storage.ers.ram - -import jetbrains.exodus.core.dataStructures.persistent.Persistent23Tree -import jetbrains.exodus.core.dataStructures.persistent.Persistent23TreeMap -import jetbrains.exodus.core.dataStructures.persistent.PersistentHashMap -import jetbrains.exodus.core.dataStructures.persistent.PersistentHashSet -import jetbrains.exodus.core.dataStructures.persistent.PersistentHashSet.ImmutablePersistentHashSet -import jetbrains.exodus.core.dataStructures.persistent.PersistentLongMap -import jetbrains.exodus.core.dataStructures.persistent.PersistentLongSet - -typealias PersistentMutableHashSet = PersistentHashSet.MutablePersistentHashSet -typealias PersistentLongImmutableSet = PersistentLongSet.ImmutableSet -typealias PersistentLongMutableSet = PersistentLongSet.MutableSet -typealias PersistentLongImmutableMap = PersistentLongMap.ImmutableMap -typealias PersistentLongMutableMap = PersistentLongMap.MutableMap - -inline fun PersistentHashSet.write(writer: PersistentMutableHashSet.() -> Unit): PersistentHashSet = - clone.also { clone -> - clone.beginWrite().apply { - writer() - endWrite() - } - } - -inline fun PersistentHashSet.writeCheckSize(writer: PersistentMutableHashSet.() -> Unit): PersistentHashSet { - clone.also { clone -> - clone.beginWrite().apply { - val initialSize = size() - writer() - if (initialSize != size() && endWrite()) { - return clone - } - } - } - return this -} - -inline fun > Persistent23Tree.write(writer: Persistent23Tree.MutableTree.() -> Unit): Persistent23Tree = - clone.also { clone -> - clone.beginWrite().apply { - writer() - endWrite() - } - } - -inline fun > Persistent23Tree.writeCheckSize(writer: Persistent23Tree.MutableTree.() -> Unit): Persistent23Tree { - clone.also { clone -> - clone.beginWrite().apply { - val initialSize = size() - writer() - if (initialSize != size() && endWrite()) { - return clone - } - } - } - return this -} - -inline fun PersistentHashSet<*>.read(reader: ImmutablePersistentHashSet<*>.() -> T): T = beginRead().reader() - -inline val PersistentHashSet<*>.size: Int get() = beginRead().size() - -operator fun PersistentHashSet<*>.contains(value: Long): Boolean = - value in (beginRead() as ImmutablePersistentHashSet) - -operator fun PersistentHashMap.get(key: K): V? = current[key] - -inline fun PersistentHashMap.write(writer: PersistentHashMap.MutablePersistentHashMap.() -> T?): Pair> = - clone.run { - beginWrite().run { - val result = writer() - endWrite() - result - } to this - } - -operator fun , V> Persistent23TreeMap.get(key: K): V? = beginRead()[key] - -inline fun , V, T> Persistent23TreeMap.write(writer: Persistent23TreeMap.MutableMap.() -> T?): Pair> = - clone.run { - beginWrite().run { - val result = writer() - endWrite() - result - } to this - } - -inline fun PersistentLongSet.write(writer: PersistentLongMutableSet.() -> Unit): PersistentLongSet = clone.apply { - beginWrite().apply { - writer() - endWrite() - } -} - -inline fun PersistentLongSet.writeCheckSize(writer: PersistentLongMutableSet.() -> Unit): PersistentLongSet { - clone.apply { - beginWrite().apply { - val initialSize = size() - writer() - if (initialSize != size() && endWrite()) { - return clone - } - } - } - return this -} - -fun PersistentLongSet.read(reader: PersistentLongImmutableSet.() -> T): T = beginRead().reader() - -inline val PersistentLongSet.size: Int get() = beginRead().size() - -fun PersistentLongSet.asIterable(): Iterable = beginRead().longIterator().asSequence().asIterable() - -fun PersistentLongSet.iterator(): Iterator = beginRead().longIterator() - -operator fun PersistentLongSet.contains(value: Long): Boolean = value in beginRead() - -inline fun PersistentLongMap.write(writer: PersistentLongMutableMap.() -> T?): Pair> = - clone.run { - beginWrite().run { - val result = writer() - endWrite() - result - } to this - } - -inline fun PersistentLongMap<*>.read(reader: PersistentLongImmutableMap<*>.() -> T): T = beginRead().reader() - -val PersistentLongMap<*>.size: Int get() = beginRead().size() - -operator fun PersistentLongMap.get(key: Long): V? = beginRead()[key] \ No newline at end of file diff --git a/jacodb-core/src/main/kotlin/org/jacodb/impl/storage/ers/sql/SqlEntityRelationshipStorage.kt b/jacodb-core/src/main/kotlin/org/jacodb/impl/storage/ers/sql/SqlEntityRelationshipStorage.kt index a639c3a7b..8785f53cb 100644 --- a/jacodb-core/src/main/kotlin/org/jacodb/impl/storage/ers/sql/SqlEntityRelationshipStorage.kt +++ b/jacodb-core/src/main/kotlin/org/jacodb/impl/storage/ers/sql/SqlEntityRelationshipStorage.kt @@ -18,10 +18,10 @@ package org.jacodb.impl.storage.ers.sql import com.zaxxer.hikari.HikariConfig import com.zaxxer.hikari.HikariDataSource -import org.jacodb.api.jvm.storage.ers.BindingProvider -import org.jacodb.api.jvm.storage.ers.ERSConflictingTransactionException -import org.jacodb.api.jvm.storage.ers.EntityRelationshipStorage -import org.jacodb.api.jvm.storage.ers.Transaction +import org.jacodb.api.storage.ers.BindingProvider +import org.jacodb.api.storage.ers.ERSConflictingTransactionException +import org.jacodb.api.storage.ers.EntityRelationshipStorage +import org.jacodb.api.storage.ers.Transaction import org.jacodb.impl.storage.ers.decorators.withAllDecorators import org.jacodb.impl.storage.ers.jooq.tables.references.TYPES import org.jacodb.impl.storage.ers.sql.SqlErsNames.ENTITY_ID_FIELD diff --git a/jacodb-core/src/main/kotlin/org/jacodb/impl/storage/ers/sql/SqlEntityRelationshipStorageSPI.kt b/jacodb-core/src/main/kotlin/org/jacodb/impl/storage/ers/sql/SqlEntityRelationshipStorageSPI.kt index abf19319c..e61675d11 100644 --- a/jacodb-core/src/main/kotlin/org/jacodb/impl/storage/ers/sql/SqlEntityRelationshipStorageSPI.kt +++ b/jacodb-core/src/main/kotlin/org/jacodb/impl/storage/ers/sql/SqlEntityRelationshipStorageSPI.kt @@ -16,9 +16,9 @@ package org.jacodb.impl.storage.ers.sql -import org.jacodb.api.jvm.storage.ers.ErsSettings -import org.jacodb.api.jvm.storage.ers.EntityRelationshipStorage -import org.jacodb.api.jvm.storage.ers.EntityRelationshipStorageSPI +import org.jacodb.api.storage.ers.ErsSettings +import org.jacodb.api.storage.ers.EntityRelationshipStorage +import org.jacodb.api.storage.ers.EntityRelationshipStorageSPI import org.jacodb.impl.storage.configuredSQLiteDataSource import org.jacodb.impl.storage.ers.BuiltInBindingProvider diff --git a/jacodb-core/src/main/kotlin/org/jacodb/impl/storage/ers/sql/SqlErsContext.kt b/jacodb-core/src/main/kotlin/org/jacodb/impl/storage/ers/sql/SqlErsContext.kt index 88d523e8e..a78303d72 100644 --- a/jacodb-core/src/main/kotlin/org/jacodb/impl/storage/ers/sql/SqlErsContext.kt +++ b/jacodb-core/src/main/kotlin/org/jacodb/impl/storage/ers/sql/SqlErsContext.kt @@ -16,7 +16,7 @@ package org.jacodb.impl.storage.ers.sql -import org.jacodb.api.jvm.storage.ers.EntityRelationshipStorage +import org.jacodb.api.storage.ers.EntityRelationshipStorage import org.jacodb.impl.storage.ers.jooq.tables.references.TYPES import org.jacodb.impl.storage.ers.sql.SqlErsNames.BLOB_VALUE_FIELD import org.jacodb.impl.storage.ers.sql.SqlErsNames.ENTITY_ID_FIELD diff --git a/jacodb-core/src/main/kotlin/org/jacodb/impl/storage/ers/sql/SqlErsCustomExceptionTransformer.kt b/jacodb-core/src/main/kotlin/org/jacodb/impl/storage/ers/sql/SqlErsCustomExceptionTransformer.kt index 2ec810c08..f69726400 100644 --- a/jacodb-core/src/main/kotlin/org/jacodb/impl/storage/ers/sql/SqlErsCustomExceptionTransformer.kt +++ b/jacodb-core/src/main/kotlin/org/jacodb/impl/storage/ers/sql/SqlErsCustomExceptionTransformer.kt @@ -16,7 +16,7 @@ package org.jacodb.impl.storage.ers.sql -import org.jacodb.api.jvm.storage.ers.ERSConflictingTransactionException +import org.jacodb.api.storage.ers.ERSConflictingTransactionException import org.jooq.ExecuteContext import org.jooq.exception.DataAccessException import org.jooq.impl.DefaultExecuteListener diff --git a/jacodb-core/src/main/kotlin/org/jacodb/impl/storage/ers/sql/SqlErsEntity.kt b/jacodb-core/src/main/kotlin/org/jacodb/impl/storage/ers/sql/SqlErsEntity.kt index 7e4340e05..869eda7f5 100644 --- a/jacodb-core/src/main/kotlin/org/jacodb/impl/storage/ers/sql/SqlErsEntity.kt +++ b/jacodb-core/src/main/kotlin/org/jacodb/impl/storage/ers/sql/SqlErsEntity.kt @@ -16,9 +16,9 @@ package org.jacodb.impl.storage.ers.sql -import org.jacodb.api.jvm.storage.ers.Entity -import org.jacodb.api.jvm.storage.ers.EntityId -import org.jacodb.api.jvm.storage.ers.EntityIterable +import org.jacodb.api.storage.ers.Entity +import org.jacodb.api.storage.ers.EntityId +import org.jacodb.api.storage.ers.EntityIterable import org.jacodb.impl.storage.ers.sql.SqlErsNames.BLOB_VALUE_FIELD import org.jacodb.impl.storage.ers.sql.SqlErsNames.ENTITY_ID_FIELD import org.jacodb.impl.storage.ers.sql.SqlErsNames.LINK_SOURCE_ENTITY_ID_FIELD diff --git a/jacodb-core/src/main/kotlin/org/jacodb/impl/storage/ers/sql/SqlErsEntityIterable.kt b/jacodb-core/src/main/kotlin/org/jacodb/impl/storage/ers/sql/SqlErsEntityIterable.kt index 21aff11c2..30a42705f 100644 --- a/jacodb-core/src/main/kotlin/org/jacodb/impl/storage/ers/sql/SqlErsEntityIterable.kt +++ b/jacodb-core/src/main/kotlin/org/jacodb/impl/storage/ers/sql/SqlErsEntityIterable.kt @@ -16,9 +16,9 @@ package org.jacodb.impl.storage.ers.sql -import org.jacodb.api.jvm.storage.ers.Entity -import org.jacodb.api.jvm.storage.ers.EntityId -import org.jacodb.api.jvm.storage.ers.EntityIterable +import org.jacodb.api.storage.ers.Entity +import org.jacodb.api.storage.ers.EntityId +import org.jacodb.api.storage.ers.EntityIterable import org.jooq.Condition import org.jooq.Field import org.jooq.Table diff --git a/jacodb-core/src/main/kotlin/org/jacodb/impl/storage/ers/sql/SqlErsTransaction.kt b/jacodb-core/src/main/kotlin/org/jacodb/impl/storage/ers/sql/SqlErsTransaction.kt index 6d56f737e..cf19c7c31 100644 --- a/jacodb-core/src/main/kotlin/org/jacodb/impl/storage/ers/sql/SqlErsTransaction.kt +++ b/jacodb-core/src/main/kotlin/org/jacodb/impl/storage/ers/sql/SqlErsTransaction.kt @@ -16,11 +16,11 @@ package org.jacodb.impl.storage.ers.sql -import org.jacodb.api.jvm.storage.ers.Entity -import org.jacodb.api.jvm.storage.ers.EntityId -import org.jacodb.api.jvm.storage.ers.EntityIterable -import org.jacodb.api.jvm.storage.ers.Transaction -import org.jacodb.api.jvm.storage.ers.probablyCompressed +import org.jacodb.api.storage.ers.Entity +import org.jacodb.api.storage.ers.EntityId +import org.jacodb.api.storage.ers.EntityIterable +import org.jacodb.api.storage.ers.Transaction +import org.jacodb.api.storage.ers.probablyCompressed import org.jacodb.impl.storage.ers.sql.SqlErsNames.ENTITY_ID_FIELD import org.jacodb.impl.storage.ers.sql.SqlErsNames.PROPERTY_VALUE_FIELD import org.jacodb.impl.storage.executeQueriesFrom @@ -64,6 +64,8 @@ class SqlErsTransactionImpl( ?.let { SqlErsEntity(id, txn = this) } } + override fun getEntityUnsafe(id: EntityId): Entity = SqlErsEntity(id, txn = this) + override fun deleteEntity(id: EntityId) { val entityTable = getEntityTableByTypeIdOrNull(id.typeId) ?: return jooq.deleteFrom(entityTable) diff --git a/jacodb-core/src/main/kotlin/org/jacodb/impl/types/JcTypedFieldImpl.kt b/jacodb-core/src/main/kotlin/org/jacodb/impl/types/JcTypedFieldImpl.kt index f58ad029c..496c6f74b 100644 --- a/jacodb-core/src/main/kotlin/org/jacodb/impl/types/JcTypedFieldImpl.kt +++ b/jacodb-core/src/main/kotlin/org/jacodb/impl/types/JcTypedFieldImpl.kt @@ -21,7 +21,7 @@ import org.jacodb.api.jvm.JcRefType import org.jacodb.api.jvm.JcSubstitutor import org.jacodb.api.jvm.JcType import org.jacodb.api.jvm.JcTypedField -import org.jacodb.api.jvm.ext.isNullable +import org.jacodb.impl.bytecode.isNullable import org.jacodb.api.jvm.throwClassNotFound import org.jacodb.impl.bytecode.JcAnnotationImpl import org.jacodb.impl.bytecode.JcFieldImpl @@ -49,17 +49,19 @@ class JcTypedFieldImpl( override val type: JcType by lazy { val typeName = field.type.typeName - val type = resolvedType?.let { - classpath.typeOf(substitutor.substitute(it)) - } ?: classpath.findTypeOrNull(field.type.typeName)?.copyWithAnnotations( - (field as? JcFieldImpl)?.typeAnnotationInfos?.map { - JcAnnotationImpl(it, field.enclosingClass.classpath) - } ?: listOf() - ) ?: typeName.throwClassNotFound() - - field.isNullable?.let { - (type as? JcRefType)?.copyWithNullability(it) - } ?: type + val rt = resolvedType + if (rt != null) { + classpath.typeOf(substitutor.substitute(rt)) + } else { + val type = classpath.findTypeOrNull(field.type.typeName)?.copyWithAnnotations( + (field as? JcFieldImpl)?.typeAnnotationInfos?.map { + JcAnnotationImpl(it, field.enclosingClass.classpath) + } ?: listOf() + ) ?: typeName.throwClassNotFound() + field.isNullable?.let { + (type as? JcRefType)?.copyWithNullability(it) + } ?: type + } } // delegate identity to JcField diff --git a/jacodb-core/src/main/kotlin/org/jacodb/impl/types/JcTypedMethodImpl.kt b/jacodb-core/src/main/kotlin/org/jacodb/impl/types/JcTypedMethodImpl.kt index c3cae8526..d97a772b4 100644 --- a/jacodb-core/src/main/kotlin/org/jacodb/impl/types/JcTypedMethodImpl.kt +++ b/jacodb-core/src/main/kotlin/org/jacodb/impl/types/JcTypedMethodImpl.kt @@ -19,7 +19,7 @@ package org.jacodb.impl.types import org.jacodb.api.jvm.* import org.jacodb.api.jvm.ext.findTypeOrNull import org.jacodb.api.jvm.ext.isEnum -import org.jacodb.api.jvm.ext.isNullable +import org.jacodb.impl.bytecode.isNullable import org.jacodb.impl.bytecode.JcAnnotationImpl import org.jacodb.impl.bytecode.JcMethodImpl import org.jacodb.impl.types.signature.FieldResolutionImpl diff --git a/jacodb-core/src/main/kotlin/org/jacodb/impl/types/JcTypedMethodParameterImpl.kt b/jacodb-core/src/main/kotlin/org/jacodb/impl/types/JcTypedMethodParameterImpl.kt index d89a7573d..e3d4ec65a 100644 --- a/jacodb-core/src/main/kotlin/org/jacodb/impl/types/JcTypedMethodParameterImpl.kt +++ b/jacodb-core/src/main/kotlin/org/jacodb/impl/types/JcTypedMethodParameterImpl.kt @@ -17,7 +17,7 @@ package org.jacodb.impl.types import org.jacodb.api.jvm.* -import org.jacodb.api.jvm.ext.isNullable +import org.jacodb.impl.bytecode.isNullable import org.jacodb.impl.bytecode.JcAnnotationImpl import org.jacodb.impl.bytecode.JcMethodImpl diff --git a/jacodb-core/src/main/kotlin/org/jacodb/impl/types/signature/JvmTypeAnnotationUpdateVisitor.kt b/jacodb-core/src/main/kotlin/org/jacodb/impl/types/signature/JvmTypeAnnotationUpdateVisitor.kt index 75e282c1a..5105e3a0d 100644 --- a/jacodb-core/src/main/kotlin/org/jacodb/impl/types/signature/JvmTypeAnnotationUpdateVisitor.kt +++ b/jacodb-core/src/main/kotlin/org/jacodb/impl/types/signature/JvmTypeAnnotationUpdateVisitor.kt @@ -18,8 +18,8 @@ package org.jacodb.impl.types.signature import org.jacodb.api.jvm.JcClasspath import org.jacodb.api.jvm.JvmType -import org.jacodb.api.jvm.ext.isNotNullAnnotation -import org.jacodb.api.jvm.ext.isNullableAnnotation +import org.jacodb.impl.bytecode.isNotNullAnnotation +import org.jacodb.impl.bytecode.isNullableAnnotation import org.jacodb.impl.bytecode.JcAnnotationImpl import org.jacodb.impl.types.AnnotationInfo import org.objectweb.asm.TypePath diff --git a/jacodb-core/src/main/kotlin/org/jacodb/impl/types/signature/JvmTypeKMetadataUpdateVisitor.kt b/jacodb-core/src/main/kotlin/org/jacodb/impl/types/signature/JvmTypeKMetadataUpdateVisitor.kt index e34de343f..abe54e8ca 100644 --- a/jacodb-core/src/main/kotlin/org/jacodb/impl/types/signature/JvmTypeKMetadataUpdateVisitor.kt +++ b/jacodb-core/src/main/kotlin/org/jacodb/impl/types/signature/JvmTypeKMetadataUpdateVisitor.kt @@ -16,9 +16,9 @@ package org.jacodb.impl.types.signature -import kotlinx.metadata.KmType -import kotlinx.metadata.KmTypeParameter -import kotlinx.metadata.isNullable +import kotlin.metadata.KmType +import kotlin.metadata.KmTypeParameter +import kotlin.metadata.isNullable import org.jacodb.api.jvm.JvmType import org.jacodb.api.jvm.JvmTypeParameterDeclaration diff --git a/jacodb-core/src/main/kotlin/org/jacodb/impl/types/signature/Signature.kt b/jacodb-core/src/main/kotlin/org/jacodb/impl/types/signature/Signature.kt index 47d0f6045..1861a218d 100644 --- a/jacodb-core/src/main/kotlin/org/jacodb/impl/types/signature/Signature.kt +++ b/jacodb-core/src/main/kotlin/org/jacodb/impl/types/signature/Signature.kt @@ -16,7 +16,7 @@ package org.jacodb.impl.types.signature -import kotlinx.metadata.KmTypeParameter +import kotlin.metadata.KmTypeParameter import org.jacodb.api.jvm.JcAccessible import org.jacodb.api.jvm.JvmType import org.jacodb.api.jvm.JvmTypeParameterDeclaration diff --git a/jacodb-core/src/main/kotlin/org/jacodb/impl/types/substition/JcSubstitutorImpl.kt b/jacodb-core/src/main/kotlin/org/jacodb/impl/types/substition/JcSubstitutorImpl.kt index 2f949e84d..9dae3a550 100644 --- a/jacodb-core/src/main/kotlin/org/jacodb/impl/types/substition/JcSubstitutorImpl.kt +++ b/jacodb-core/src/main/kotlin/org/jacodb/impl/types/substition/JcSubstitutorImpl.kt @@ -22,7 +22,7 @@ import kotlinx.collections.immutable.toPersistentMap import org.jacodb.api.jvm.JcSubstitutor import org.jacodb.api.jvm.JvmType import org.jacodb.api.jvm.JvmTypeParameterDeclaration -import org.jacodb.api.jvm.ext.isNotNullAnnotation +import org.jacodb.impl.bytecode.isNotNullAnnotation import org.jacodb.impl.types.signature.JvmTypeParameterDeclarationImpl import org.jacodb.impl.types.signature.JvmTypeVariable import org.jacodb.impl.types.signature.copyWith @@ -165,6 +165,4 @@ class JcSubstitutorImpl( override fun hashCode(): Int { return substitutions.hashCode() } - - } diff --git a/jacodb-core/src/main/resources/META-INF/services/org.jacodb.api.storage.ers.EntityRelationshipStorageSPI b/jacodb-core/src/main/resources/META-INF/services/org.jacodb.api.storage.ers.EntityRelationshipStorageSPI new file mode 100644 index 000000000..e408c2c8c --- /dev/null +++ b/jacodb-core/src/main/resources/META-INF/services/org.jacodb.api.storage.ers.EntityRelationshipStorageSPI @@ -0,0 +1 @@ +org.jacodb.impl.storage.ers.sql.SqlEntityRelationshipStorageSPI \ No newline at end of file diff --git a/jacodb-core/src/test/java/org/jacodb/testing/JavaApi.java b/jacodb-core/src/test/java/org/jacodb/testing/JavaApi.java index 55df79695..9d1057226 100644 --- a/jacodb-core/src/test/java/org/jacodb/testing/JavaApi.java +++ b/jacodb-core/src/test/java/org/jacodb/testing/JavaApi.java @@ -17,14 +17,10 @@ package org.jacodb.testing; import org.jacodb.api.jvm.JcDatabase; -import org.jacodb.api.jvm.cfg.JcArgument; -import org.jacodb.api.jvm.cfg.JcExpr; -import org.jacodb.api.jvm.cfg.TypedExprResolver; import org.jacodb.impl.JacoDB; import org.jacodb.impl.JcCacheSettings; import org.jacodb.impl.JcSettings; import org.jacodb.impl.features.Usages; -import org.jetbrains.annotations.NotNull; import java.time.Duration; import java.time.temporal.ChronoUnit; diff --git a/jacodb-core/src/test/kotlin/org/jacodb/testing/persistence/RestoredDBTest.kt b/jacodb-core/src/test/kotlin/org/jacodb/testing/persistence/RestoredDBTest.kt index 5cd5a8b94..524ba4c89 100644 --- a/jacodb-core/src/test/kotlin/org/jacodb/testing/persistence/RestoredDBTest.kt +++ b/jacodb-core/src/test/kotlin/org/jacodb/testing/persistence/RestoredDBTest.kt @@ -20,7 +20,6 @@ import kotlinx.coroutines.runBlocking import org.jacodb.api.jvm.JcClasspath import org.jacodb.api.jvm.JcPersistenceImplSettings import org.jacodb.api.jvm.ext.HierarchyExtension -import org.jacodb.impl.JcRamErsSettings import org.jacodb.impl.JcXodusKvErsSettings import org.jacodb.impl.features.hierarchyExt import org.jacodb.testing.LifecycleTest diff --git a/jacodb-core/src/test/kotlin/org/jacodb/testing/storage/ers/SqlEntityRelationshipStorageTest.kt b/jacodb-core/src/test/kotlin/org/jacodb/testing/storage/ers/SqlEntityRelationshipStorageTest.kt index 1e245a2d9..46833a177 100644 --- a/jacodb-core/src/test/kotlin/org/jacodb/testing/storage/ers/SqlEntityRelationshipStorageTest.kt +++ b/jacodb-core/src/test/kotlin/org/jacodb/testing/storage/ers/SqlEntityRelationshipStorageTest.kt @@ -21,4 +21,4 @@ import org.jacodb.impl.storage.ers.sql.SQL_ERS_SPI class SqlEntityRelationshipStorageTest : EntityRelationshipStorageTest() { override val ersId: String get() = SQL_ERS_SPI -} +} \ No newline at end of file diff --git a/jacodb-core/src/testFixtures/kotlin/org/jacodb/testing/tests/DatabaseEnvTest.kt b/jacodb-core/src/testFixtures/kotlin/org/jacodb/testing/tests/DatabaseEnvTest.kt index b433509a6..4d0fe5b43 100644 --- a/jacodb-core/src/testFixtures/kotlin/org/jacodb/testing/tests/DatabaseEnvTest.kt +++ b/jacodb-core/src/testFixtures/kotlin/org/jacodb/testing/tests/DatabaseEnvTest.kt @@ -35,7 +35,7 @@ import org.jacodb.api.jvm.ext.humanReadableSignature import org.jacodb.api.jvm.ext.isEnum import org.jacodb.api.jvm.ext.isLocal import org.jacodb.api.jvm.ext.isMemberClass -import org.jacodb.api.jvm.ext.isNullable +import org.jacodb.impl.bytecode.isNullable import org.jacodb.api.jvm.ext.jcdbSignature import org.jacodb.api.jvm.ext.jvmSignature import org.jacodb.api.jvm.ext.methods diff --git a/jacodb-storage/build.gradle.kts b/jacodb-storage/build.gradle.kts new file mode 100644 index 000000000..c09c59312 --- /dev/null +++ b/jacodb-storage/build.gradle.kts @@ -0,0 +1,14 @@ +dependencies { + api(project(":jacodb-api-storage")) + + compileOnly(Libs.xodusEnvironment) + compileOnly(Libs.lmdb_java) + compileOnly(Libs.rocks_db) + + testImplementation(Libs.xodusEnvironment) + testImplementation(Libs.lmdb_java) + testImplementation(Libs.rocks_db) + + testFixturesImplementation(platform(Libs.junit_bom)) + testFixturesImplementation(Libs.junit_jupiter) +} \ No newline at end of file diff --git a/jacodb-storage/src/main/kotlin/org/jacodb/impl/JcErsSettings.kt b/jacodb-storage/src/main/kotlin/org/jacodb/impl/JcErsSettings.kt new file mode 100644 index 000000000..06afa72f6 --- /dev/null +++ b/jacodb-storage/src/main/kotlin/org/jacodb/impl/JcErsSettings.kt @@ -0,0 +1,29 @@ +/* + * Copyright 2022 UnitTestBot contributors (utbot.org) + *

+ * 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 org.jacodb.impl + +import org.jacodb.api.storage.ers.ErsSettings +import org.jacodb.impl.storage.kv.lmdb.LMDB_KEY_VALUE_STORAGE_SPI + +/** + * Id of pluggable K/V storage being passed for [org.jacodb.impl.storage.ers.kv.KVEntityRelationshipStorageSPI]. + */ +open class JcKvErsSettings(val kvId: String) : ErsSettings + +// by default, mapSize is 1Gb +class JcLmdbErsSettings(val mapSize: Long = 0x40_00_00_00) : JcKvErsSettings(LMDB_KEY_VALUE_STORAGE_SPI) + diff --git a/jacodb-core/src/main/kotlin/org/jacodb/impl/storage/ers/BuiltInBindings.kt b/jacodb-storage/src/main/kotlin/org/jacodb/impl/storage/ers/BuiltInBindings.kt similarity index 98% rename from jacodb-core/src/main/kotlin/org/jacodb/impl/storage/ers/BuiltInBindings.kt rename to jacodb-storage/src/main/kotlin/org/jacodb/impl/storage/ers/BuiltInBindings.kt index 5357e7243..ce6b509b1 100644 --- a/jacodb-core/src/main/kotlin/org/jacodb/impl/storage/ers/BuiltInBindings.kt +++ b/jacodb-storage/src/main/kotlin/org/jacodb/impl/storage/ers/BuiltInBindings.kt @@ -16,9 +16,9 @@ package org.jacodb.impl.storage.ers -import org.jacodb.api.jvm.storage.ers.Binding -import org.jacodb.api.jvm.storage.ers.BindingProvider -import org.jacodb.api.jvm.storage.ers.ERSException +import org.jacodb.api.storage.ers.Binding +import org.jacodb.api.storage.ers.BindingProvider +import org.jacodb.api.storage.ers.ERSException import kotlin.experimental.and object BuiltInBindingProvider : BindingProvider { diff --git a/jacodb-core/src/main/kotlin/org/jacodb/impl/storage/ers/decorators/AbstractDecorators.kt b/jacodb-storage/src/main/kotlin/org/jacodb/impl/storage/ers/decorators/AbstractDecorators.kt similarity index 94% rename from jacodb-core/src/main/kotlin/org/jacodb/impl/storage/ers/decorators/AbstractDecorators.kt rename to jacodb-storage/src/main/kotlin/org/jacodb/impl/storage/ers/decorators/AbstractDecorators.kt index e7799552f..a31fd94dc 100644 --- a/jacodb-core/src/main/kotlin/org/jacodb/impl/storage/ers/decorators/AbstractDecorators.kt +++ b/jacodb-storage/src/main/kotlin/org/jacodb/impl/storage/ers/decorators/AbstractDecorators.kt @@ -16,11 +16,11 @@ package org.jacodb.impl.storage.ers.decorators -import org.jacodb.api.jvm.storage.ers.Entity -import org.jacodb.api.jvm.storage.ers.EntityId -import org.jacodb.api.jvm.storage.ers.EntityIterable -import org.jacodb.api.jvm.storage.ers.EntityRelationshipStorage -import org.jacodb.api.jvm.storage.ers.Transaction +import org.jacodb.api.storage.ers.Entity +import org.jacodb.api.storage.ers.EntityId +import org.jacodb.api.storage.ers.EntityIterable +import org.jacodb.api.storage.ers.EntityRelationshipStorage +import org.jacodb.api.storage.ers.Transaction import org.jacodb.impl.storage.ers.decorators.CombineOperation.MINUS import org.jacodb.impl.storage.ers.decorators.CombineOperation.PLUS import org.jacodb.impl.storage.ers.decorators.CombineOperation.TIMES @@ -33,6 +33,7 @@ abstract class AbstractTransactionDecorator : Transaction { override val isFinished: Boolean get() = delegate.isFinished override fun newEntity(type: String): Entity = delegate.newEntity(type) override fun getEntityOrNull(id: EntityId): Entity? = delegate.getEntityOrNull(id) + override fun getEntityUnsafe(id: EntityId): Entity = delegate.getEntityUnsafe(id) override fun deleteEntity(id: EntityId) = delegate.deleteEntity(id) override fun getTypeId(type: String): Int = delegate.getTypeId(type) override fun getPropertyNames(type: String): Set = delegate.getPropertyNames(type) diff --git a/jacodb-core/src/main/kotlin/org/jacodb/impl/storage/ers/decorators/AllDecorators.kt b/jacodb-storage/src/main/kotlin/org/jacodb/impl/storage/ers/decorators/AllDecorators.kt similarity index 94% rename from jacodb-core/src/main/kotlin/org/jacodb/impl/storage/ers/decorators/AllDecorators.kt rename to jacodb-storage/src/main/kotlin/org/jacodb/impl/storage/ers/decorators/AllDecorators.kt index 820b7cca5..69b3b98bf 100644 --- a/jacodb-core/src/main/kotlin/org/jacodb/impl/storage/ers/decorators/AllDecorators.kt +++ b/jacodb-storage/src/main/kotlin/org/jacodb/impl/storage/ers/decorators/AllDecorators.kt @@ -16,7 +16,7 @@ package org.jacodb.impl.storage.ers.decorators -import org.jacodb.api.jvm.storage.ers.Transaction +import org.jacodb.api.storage.ers.Transaction fun Transaction.withAllDecorators(): Transaction = recomputeEntityIterableOnEachUse().withChecks() diff --git a/jacodb-core/src/main/kotlin/org/jacodb/impl/storage/ers/decorators/Checked.kt b/jacodb-storage/src/main/kotlin/org/jacodb/impl/storage/ers/decorators/Checked.kt similarity index 90% rename from jacodb-core/src/main/kotlin/org/jacodb/impl/storage/ers/decorators/Checked.kt rename to jacodb-storage/src/main/kotlin/org/jacodb/impl/storage/ers/decorators/Checked.kt index 186de4d18..0c00decd6 100644 --- a/jacodb-core/src/main/kotlin/org/jacodb/impl/storage/ers/decorators/Checked.kt +++ b/jacodb-storage/src/main/kotlin/org/jacodb/impl/storage/ers/decorators/Checked.kt @@ -16,12 +16,12 @@ package org.jacodb.impl.storage.ers.decorators -import org.jacodb.api.jvm.storage.ers.ERSNonExistingEntityException -import org.jacodb.api.jvm.storage.ers.Entity -import org.jacodb.api.jvm.storage.ers.EntityId -import org.jacodb.api.jvm.storage.ers.EntityIterable -import org.jacodb.api.jvm.storage.ers.EntityRelationshipStorage -import org.jacodb.api.jvm.storage.ers.Transaction +import org.jacodb.api.storage.ers.ERSNonExistingEntityException +import org.jacodb.api.storage.ers.Entity +import org.jacodb.api.storage.ers.EntityId +import org.jacodb.api.storage.ers.EntityIterable +import org.jacodb.api.storage.ers.EntityRelationshipStorage +import org.jacodb.api.storage.ers.Transaction fun Transaction.withChecks(): Transaction = CheckedTransaction(uncheckedDelegate = this).decorateDeeply( diff --git a/jacodb-core/src/main/kotlin/org/jacodb/impl/storage/ers/decorators/DeepDecorators.kt b/jacodb-storage/src/main/kotlin/org/jacodb/impl/storage/ers/decorators/DeepDecorators.kt similarity index 93% rename from jacodb-core/src/main/kotlin/org/jacodb/impl/storage/ers/decorators/DeepDecorators.kt rename to jacodb-storage/src/main/kotlin/org/jacodb/impl/storage/ers/decorators/DeepDecorators.kt index 36ddf2370..e046c89e3 100644 --- a/jacodb-core/src/main/kotlin/org/jacodb/impl/storage/ers/decorators/DeepDecorators.kt +++ b/jacodb-storage/src/main/kotlin/org/jacodb/impl/storage/ers/decorators/DeepDecorators.kt @@ -16,10 +16,10 @@ package org.jacodb.impl.storage.ers.decorators -import org.jacodb.api.jvm.storage.ers.Entity -import org.jacodb.api.jvm.storage.ers.EntityId -import org.jacodb.api.jvm.storage.ers.EntityIterable -import org.jacodb.api.jvm.storage.ers.Transaction +import org.jacodb.api.storage.ers.Entity +import org.jacodb.api.storage.ers.EntityId +import org.jacodb.api.storage.ers.EntityIterable +import org.jacodb.api.storage.ers.Transaction inline fun Transaction.decorateDeeply( crossinline entityIterableWrapper: (EntityIterable) -> EntityIterable = { it }, @@ -70,6 +70,9 @@ private class WrapperTransaction( override fun getEntityOrNull(id: EntityId): Entity? = super.getEntityOrNull(id)?.let { wrapper.wrapEntity(it) } + override fun getEntityUnsafe(id: EntityId): Entity = + wrapper.wrapEntity(super.getEntityUnsafe(id)) + override fun all(type: String): EntityIterable = wrapper.wrapEntityIterable { super.all(type) } diff --git a/jacodb-core/src/main/kotlin/org/jacodb/impl/storage/ers/decorators/RecomputingEntityIterable.kt b/jacodb-storage/src/main/kotlin/org/jacodb/impl/storage/ers/decorators/RecomputingEntityIterable.kt similarity index 91% rename from jacodb-core/src/main/kotlin/org/jacodb/impl/storage/ers/decorators/RecomputingEntityIterable.kt rename to jacodb-storage/src/main/kotlin/org/jacodb/impl/storage/ers/decorators/RecomputingEntityIterable.kt index 72c14a339..b89187ee5 100644 --- a/jacodb-core/src/main/kotlin/org/jacodb/impl/storage/ers/decorators/RecomputingEntityIterable.kt +++ b/jacodb-storage/src/main/kotlin/org/jacodb/impl/storage/ers/decorators/RecomputingEntityIterable.kt @@ -16,8 +16,8 @@ package org.jacodb.impl.storage.ers.decorators -import org.jacodb.api.jvm.storage.ers.EntityIterable -import org.jacodb.api.jvm.storage.ers.Transaction +import org.jacodb.api.storage.ers.EntityIterable +import org.jacodb.api.storage.ers.Transaction fun Transaction.recomputeEntityIterableOnEachUse() = decorateDeeplyWithLazyIterable( entityIterableWrapper = { entityIterableCreator -> RecomputingEntityIterable(entityIterableCreator) } diff --git a/jacodb-core/src/main/kotlin/org/jacodb/impl/storage/ers/kv/KVEntity.kt b/jacodb-storage/src/main/kotlin/org/jacodb/impl/storage/ers/kv/KVEntity.kt similarity index 94% rename from jacodb-core/src/main/kotlin/org/jacodb/impl/storage/ers/kv/KVEntity.kt rename to jacodb-storage/src/main/kotlin/org/jacodb/impl/storage/ers/kv/KVEntity.kt index 9b87ce31e..eaf3b893d 100644 --- a/jacodb-core/src/main/kotlin/org/jacodb/impl/storage/ers/kv/KVEntity.kt +++ b/jacodb-storage/src/main/kotlin/org/jacodb/impl/storage/ers/kv/KVEntity.kt @@ -16,12 +16,12 @@ package org.jacodb.impl.storage.ers.kv -import org.jacodb.api.jvm.storage.ers.ERSException -import org.jacodb.api.jvm.storage.ers.Entity -import org.jacodb.api.jvm.storage.ers.EntityId -import org.jacodb.api.jvm.storage.ers.EntityIterable -import org.jacodb.api.jvm.storage.ers.InstanceIdCollectionEntityIterable -import org.jacodb.api.jvm.storage.kv.forEachWithKey +import org.jacodb.api.storage.ers.ERSException +import org.jacodb.api.storage.ers.Entity +import org.jacodb.api.storage.ers.EntityId +import org.jacodb.api.storage.ers.EntityIterable +import org.jacodb.api.storage.ers.InstanceIdCollectionEntityIterable +import org.jacodb.api.storage.kv.forEachWithKey class KVEntity(override val id: EntityId, override val txn: KVErsTransaction) : Entity() { diff --git a/jacodb-core/src/main/kotlin/org/jacodb/impl/storage/ers/kv/KVEntityRelationshipStorage.kt b/jacodb-storage/src/main/kotlin/org/jacodb/impl/storage/ers/kv/KVEntityRelationshipStorage.kt similarity index 89% rename from jacodb-core/src/main/kotlin/org/jacodb/impl/storage/ers/kv/KVEntityRelationshipStorage.kt rename to jacodb-storage/src/main/kotlin/org/jacodb/impl/storage/ers/kv/KVEntityRelationshipStorage.kt index 2a36e8264..a5c34d4dc 100644 --- a/jacodb-core/src/main/kotlin/org/jacodb/impl/storage/ers/kv/KVEntityRelationshipStorage.kt +++ b/jacodb-storage/src/main/kotlin/org/jacodb/impl/storage/ers/kv/KVEntityRelationshipStorage.kt @@ -16,12 +16,12 @@ package org.jacodb.impl.storage.ers.kv -import org.jacodb.api.jvm.storage.ers.Binding -import org.jacodb.api.jvm.storage.ers.ERSConflictingTransactionException -import org.jacodb.api.jvm.storage.ers.EntityRelationshipStorage -import org.jacodb.api.jvm.storage.ers.Transaction -import org.jacodb.api.jvm.storage.kv.NamedMap -import org.jacodb.api.jvm.storage.kv.PluggableKeyValueStorage +import org.jacodb.api.storage.ers.Binding +import org.jacodb.api.storage.ers.ERSConflictingTransactionException +import org.jacodb.api.storage.ers.EntityRelationshipStorage +import org.jacodb.api.storage.ers.Transaction +import org.jacodb.api.storage.kv.NamedMap +import org.jacodb.api.storage.kv.PluggableKeyValueStorage import org.jacodb.impl.storage.ers.decorators.withAllDecorators import org.jacodb.impl.storage.ers.getBinding import java.util.concurrent.ConcurrentHashMap @@ -53,12 +53,15 @@ class KVEntityRelationshipStorage(private val kvStorage: PluggableKeyValueStorag this, if (readonly) kvStorage.beginReadonlyTransaction() else kvStorage.beginTransaction() ).also { txn -> - if (!readonly) { + if (!txn.isReadonly) { currentThreadRWTxn.set(txn) } }.withAllDecorators() } + override val asReadonly: EntityRelationshipStorage + get() = this.apply { kvStorage.readonly = true } + override fun close() = kvStorage.close() override fun getBinding(clazz: Class): Binding = clazz.getBinding() @@ -81,7 +84,7 @@ class KVEntityRelationshipStorage(private val kvStorage: PluggableKeyValueStorag internal fun getEntityTypeId( type: String, - kvTxn: org.jacodb.api.jvm.storage.kv.Transaction + kvTxn: org.jacodb.api.storage.kv.Transaction ): Int? { entityTypes[type]?.let { return it } val entityTypesMap = entityTypesMap(kvTxn, create = false) ?: return null @@ -92,7 +95,7 @@ class KVEntityRelationshipStorage(private val kvStorage: PluggableKeyValueStorag internal fun getOrAllocateEntityTypeId( type: String, - kvTxn: org.jacodb.api.jvm.storage.kv.Transaction + kvTxn: org.jacodb.api.storage.kv.Transaction ): Int { return entityTypes.computeIfAbsent(type) { val typeEntry = stringBinding.getBytes(type) @@ -107,13 +110,13 @@ class KVEntityRelationshipStorage(private val kvStorage: PluggableKeyValueStorag } } - private fun entityTypesMap(txn: org.jacodb.api.jvm.storage.kv.Transaction, create: Boolean): NamedMap? { + private fun entityTypesMap(txn: org.jacodb.api.storage.kv.Transaction, create: Boolean): NamedMap? { return entityTypesMap ?: txn.getNamedMap(entityTypesMapName, create)?.also { entityTypesMap = it } } - internal fun entityCountersMap(txn: org.jacodb.api.jvm.storage.kv.Transaction, create: Boolean): NamedMap? { + internal fun entityCountersMap(txn: org.jacodb.api.storage.kv.Transaction, create: Boolean): NamedMap? { return entityCountersMap ?: txn.getNamedMap(entityCountersMapName, create)?.also { entityCountersMap = it } @@ -121,7 +124,7 @@ class KVEntityRelationshipStorage(private val kvStorage: PluggableKeyValueStorag internal fun deletedEntitiesMap( typeId: Int, - txn: org.jacodb.api.jvm.storage.kv.Transaction, + txn: org.jacodb.api.storage.kv.Transaction, create: Boolean ): NamedMap? { return deletedEntitiesMaps.getOrElse(typeId) { @@ -134,7 +137,7 @@ class KVEntityRelationshipStorage(private val kvStorage: PluggableKeyValueStorag internal fun propertiesMap( typeId: Int, propName: String, - txn: org.jacodb.api.jvm.storage.kv.Transaction, + txn: org.jacodb.api.storage.kv.Transaction, create: Boolean ): NamedMap? { return propertiesMaps.getOrElse(typeId with propName) { @@ -165,7 +168,7 @@ class KVEntityRelationshipStorage(private val kvStorage: PluggableKeyValueStorag internal fun propertiesIndex( typeId: Int, propName: String, - txn: org.jacodb.api.jvm.storage.kv.Transaction, + txn: org.jacodb.api.storage.kv.Transaction, create: Boolean ): NamedMap? { return propertiesIndices.getOrElse(typeId with propName) { @@ -178,7 +181,7 @@ class KVEntityRelationshipStorage(private val kvStorage: PluggableKeyValueStorag internal fun blobsMap( typeId: Int, blobName: String, - txn: org.jacodb.api.jvm.storage.kv.Transaction, + txn: org.jacodb.api.storage.kv.Transaction, create: Boolean ): NamedMap? { return blobsMaps.getOrElse(typeId with blobName) { @@ -190,7 +193,7 @@ class KVEntityRelationshipStorage(private val kvStorage: PluggableKeyValueStorag internal fun linkTargetTypesMap( typeId: Int, - txn: org.jacodb.api.jvm.storage.kv.Transaction, + txn: org.jacodb.api.storage.kv.Transaction, create: Boolean ): NamedMap? { return linkTargetTypesMaps.getOrElse(typeId) { @@ -203,7 +206,7 @@ class KVEntityRelationshipStorage(private val kvStorage: PluggableKeyValueStorag internal fun linkTargetsMap( typeId: Int, linkName: String, - txn: org.jacodb.api.jvm.storage.kv.Transaction, + txn: org.jacodb.api.storage.kv.Transaction, create: Boolean ): NamedMap? { return linkTargetsMaps.getOrElse(typeId with linkName) { diff --git a/jacodb-core/src/main/kotlin/org/jacodb/impl/storage/ers/kv/KVEntityRelationshipStorageSPI.kt b/jacodb-storage/src/main/kotlin/org/jacodb/impl/storage/ers/kv/KVEntityRelationshipStorageSPI.kt similarity index 86% rename from jacodb-core/src/main/kotlin/org/jacodb/impl/storage/ers/kv/KVEntityRelationshipStorageSPI.kt rename to jacodb-storage/src/main/kotlin/org/jacodb/impl/storage/ers/kv/KVEntityRelationshipStorageSPI.kt index d28c79c97..f9137fcbc 100644 --- a/jacodb-core/src/main/kotlin/org/jacodb/impl/storage/ers/kv/KVEntityRelationshipStorageSPI.kt +++ b/jacodb-storage/src/main/kotlin/org/jacodb/impl/storage/ers/kv/KVEntityRelationshipStorageSPI.kt @@ -16,10 +16,10 @@ package org.jacodb.impl.storage.ers.kv -import org.jacodb.api.jvm.storage.ers.EntityRelationshipStorage -import org.jacodb.api.jvm.storage.ers.EntityRelationshipStorageSPI -import org.jacodb.api.jvm.storage.ers.ErsSettings -import org.jacodb.api.jvm.storage.kv.PluggableKeyValueStorageSPI +import org.jacodb.api.storage.ers.EntityRelationshipStorage +import org.jacodb.api.storage.ers.EntityRelationshipStorageSPI +import org.jacodb.api.storage.ers.ErsSettings +import org.jacodb.api.storage.kv.PluggableKeyValueStorageSPI import org.jacodb.impl.JcKvErsSettings const val KV_ERS_SPI = "org.jacodb.impl.storage.ers.kv.KVEntityRelationshipStorageSPI" diff --git a/jacodb-core/src/main/kotlin/org/jacodb/impl/storage/ers/kv/KVErsTransaction.kt b/jacodb-storage/src/main/kotlin/org/jacodb/impl/storage/ers/kv/KVErsTransaction.kt similarity index 93% rename from jacodb-core/src/main/kotlin/org/jacodb/impl/storage/ers/kv/KVErsTransaction.kt rename to jacodb-storage/src/main/kotlin/org/jacodb/impl/storage/ers/kv/KVErsTransaction.kt index ef63cd069..d5f0c65a1 100644 --- a/jacodb-core/src/main/kotlin/org/jacodb/impl/storage/ers/kv/KVErsTransaction.kt +++ b/jacodb-storage/src/main/kotlin/org/jacodb/impl/storage/ers/kv/KVErsTransaction.kt @@ -19,23 +19,23 @@ package org.jacodb.impl.storage.ers.kv import jetbrains.exodus.core.dataStructures.ConcurrentObjectCache import jetbrains.exodus.core.dataStructures.ObjectCacheBase import jetbrains.exodus.core.dataStructures.hash.IntHashMap -import org.jacodb.api.jvm.storage.ers.Binding -import org.jacodb.api.jvm.storage.ers.ERSConflictingTransactionException -import org.jacodb.api.jvm.storage.ers.Entity -import org.jacodb.api.jvm.storage.ers.EntityId -import org.jacodb.api.jvm.storage.ers.EntityIterable -import org.jacodb.api.jvm.storage.ers.InstanceIdCollectionEntityIterable -import org.jacodb.api.jvm.storage.ers.Transaction -import org.jacodb.api.jvm.storage.ers.probablyCompressed -import org.jacodb.api.jvm.storage.kv.Cursor -import org.jacodb.api.jvm.storage.kv.NamedMap -import org.jacodb.api.jvm.storage.kv.asIterable -import org.jacodb.api.jvm.storage.kv.asIterableWithKey +import org.jacodb.api.storage.ers.Binding +import org.jacodb.api.storage.ers.ERSConflictingTransactionException +import org.jacodb.api.storage.ers.Entity +import org.jacodb.api.storage.ers.EntityId +import org.jacodb.api.storage.ers.EntityIterable +import org.jacodb.api.storage.ers.InstanceIdCollectionEntityIterable +import org.jacodb.api.storage.ers.Transaction +import org.jacodb.api.storage.ers.probablyCompressed +import org.jacodb.api.storage.kv.Cursor +import org.jacodb.api.storage.kv.NamedMap +import org.jacodb.api.storage.kv.asIterable +import org.jacodb.api.storage.kv.asIterableWithKey import org.jacodb.impl.storage.kv.xodus.getOrPut class KVErsTransaction( override val ers: KVEntityRelationshipStorage, - internal val kvTxn: org.jacodb.api.jvm.storage.kv.Transaction + val kvTxn: org.jacodb.api.storage.kv.Transaction ) : Transaction { private val isDeletedCache: ObjectCacheBase = ConcurrentObjectCache(1_000, 2) @@ -61,6 +61,8 @@ class KVErsTransaction( return if (id.instanceId in 0 until entityCounter && !isEntityDeleted(id)) KVEntity(id, this) else null } + override fun getEntityUnsafe(id: EntityId): Entity = KVEntity(id, txn = this) + override fun deleteEntity(id: EntityId) { // invalidate isDeleted cache isDeletedCache.cacheObject(id, true) diff --git a/jacodb-core/src/main/kotlin/org/jacodb/impl/storage/ers/kv/KVErsUtil.kt b/jacodb-storage/src/main/kotlin/org/jacodb/impl/storage/ers/kv/KVErsUtil.kt similarity index 94% rename from jacodb-core/src/main/kotlin/org/jacodb/impl/storage/ers/kv/KVErsUtil.kt rename to jacodb-storage/src/main/kotlin/org/jacodb/impl/storage/ers/kv/KVErsUtil.kt index f4851342c..63decf716 100644 --- a/jacodb-core/src/main/kotlin/org/jacodb/impl/storage/ers/kv/KVErsUtil.kt +++ b/jacodb-storage/src/main/kotlin/org/jacodb/impl/storage/ers/kv/KVErsUtil.kt @@ -16,8 +16,8 @@ package org.jacodb.impl.storage.ers.kv -import org.jacodb.api.jvm.storage.ByteArrayKey -import org.jacodb.api.jvm.storage.kv.Cursor +import org.jacodb.api.storage.ByteArrayKey +import org.jacodb.api.storage.kv.Cursor internal class TypeIdWithName(val typeId: Int, val name: String) { diff --git a/jacodb-storage/src/main/kotlin/org/jacodb/impl/storage/ers/ram/AttributesImmutable.kt b/jacodb-storage/src/main/kotlin/org/jacodb/impl/storage/ers/ram/AttributesImmutable.kt new file mode 100644 index 000000000..4b8219350 --- /dev/null +++ b/jacodb-storage/src/main/kotlin/org/jacodb/impl/storage/ers/ram/AttributesImmutable.kt @@ -0,0 +1,210 @@ +/* + * Copyright 2022 UnitTestBot contributors (utbot.org) + *

+ * 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 org.jacodb.impl.storage.ers.ram + +import org.jacodb.api.storage.ByteArrayKey +import org.jacodb.api.storage.asComparable +import org.jacodb.api.storage.ers.Entity +import org.jacodb.api.storage.ers.EntityId +import org.jacodb.api.storage.ers.EntityIterable + +internal fun Any.toAttributesImmutable(instanceValues: List>): AttributesImmutable { + if (instanceValues.isEmpty()) { + return EmptyAttributesImmutable + } + + val totalSize = instanceValues.fold(0) { sum, instanceValue -> sum + instanceValue.second.size } + val values = ByteArray(totalSize) + val instanceIds = LongArray(instanceValues.size) + val offsetAndLens = LongArray(instanceValues.size) + var offset = 0 + + instanceValues.forEachIndexed { i, (instanceId, value) -> + val len = value.size + value.copyInto(destination = values, destinationOffset = offset) + val indexValue = (len.toLong() shl 32) + offset + instanceIds[i] = instanceId + offsetAndLens[i] = indexValue + offset += len + } + + return AttributesImmutable(values, instanceIds, offsetAndLens) +} + +internal open class AttributesImmutable( + private val values: ByteArray, + private val instanceIds: LongArray, + private val offsetAndLens: LongArray +) { + + private val sameOrder: Boolean // `true` if order of instance ids is the same as the one sorted by value + private val sortedByValueInstanceIds by lazy { + // NB! + // We need stable sorting here, and java.util.Collections.sort() guarantees the sort is stable + instanceIds.sortedBy { get(it)!!.asComparable() }.toLongArray() + } + + init { + var sameOrder = true + var prevId = Long.MIN_VALUE + var prevValue: ByteArrayKey? = null + for (i in instanceIds.indices) { + // check if instanceIds are sorted in ascending order and there are no duplicates + val currentId = instanceIds[i] + if (prevId >= currentId) { + error("AttributesImmutable: instanceIds should be sorted and have no duplicates") + } + prevId = currentId + // check if order of values is the same as order of ids + if (sameOrder) { + val currentValue = ByteArrayKey(get(currentId)!!) + prevValue?.let { + if (it > currentValue) { + sameOrder = false + } + } + prevValue = currentValue + } + } + this.sameOrder = sameOrder + } + + operator fun get(instanceId: Long): ByteArray? { + val index = instanceIds.binarySearch(instanceId) + if (index < 0) { + return null + } + val offsetAndLen = offsetAndLens[index] + val offset = offsetAndLen.toInt() + val len = (offsetAndLen shr 32).toInt() + return values.sliceArray(offset until offset + len) + } + + fun navigate(value: ByteArray, leftBound: Boolean): AttributesCursor { + if (instanceIds.isEmpty()) { + return EmptyAttributesCursor + } + val ids = if (sameOrder) instanceIds else sortedByValueInstanceIds + val valueComparable = value.asComparable() + // in order to find exact left or right bound, we have to use binary search without early break on equality + var low = 0 + var high = ids.size - 1 + var found = -1 + while (low <= high) { + val mid = (low + high).ushr(1) + val midValue = get(ids[mid])!!.asComparable() + val cmp = valueComparable.compareTo(midValue) + if (cmp == 0) { + found = mid + } + if (leftBound) { + if (cmp > 0) { + low = mid + 1 + } else { + high = mid - 1 + } + } else { + if (cmp < 0) { + high = mid - 1 + } else { + low = mid + 1 + } + } + } + val index = if (found in ids.indices) found else -(low + 1) + return object : AttributesCursor { + + private var idx: Int = if (index < 0) -index - 1 else index + + override val hasMatch: Boolean = index >= 0 + + override val current: Pair + get() { + val instanceId = ids[idx] + return instanceId to get(instanceId)!! + } + + override fun moveNext(): Boolean = ++idx < ids.size + + override fun movePrev(): Boolean = --idx >= 0 + } + } +} + +private object EmptyAttributesImmutable : AttributesImmutable(byteArrayOf(), longArrayOf(), longArrayOf()) + +internal interface AttributesCursor { + + val hasMatch: Boolean + + val current: Pair + + fun moveNext(): Boolean + + fun movePrev(): Boolean +} + +private object EmptyAttributesCursor : AttributesCursor { + override val hasMatch: Boolean = false + override val current: Pair = error("EmptyAttributesCursor doesn't navigate") + override fun moveNext(): Boolean = false + override fun movePrev(): Boolean = false +} + +internal class AttributesCursorEntityIterable( + private val txn: RAMTransaction, + private val typeId: Int, + private val cursor: AttributesCursor, + private val forwardDirection: Boolean, + private val filter: ((Long, ByteArray) -> Boolean)? = null +) : EntityIterable { + + override fun iterator(): Iterator = object : Iterator { + + private var next: Entity? = null + + override fun hasNext(): Boolean { + if (next == null) { + next = advance() + } + return next != null + } + + override fun next(): Entity { + if (next == null) { + next = advance() + } + return next.also { next = null } ?: throw NoSuchElementException() + } + + private fun advance(): Entity? { + if (next == null) { + val moved = if (forwardDirection) cursor.moveNext() else cursor.movePrev() + if (moved) { + val (instanceId, value) = cursor.current + filter?.let { func -> + if (!func(instanceId, value)) { + return null + } + } + next = txn.getEntityOrNull(EntityId(typeId, instanceId)) + } + } + return next + } + } +} \ No newline at end of file diff --git a/jacodb-core/src/main/kotlin/org/jacodb/impl/storage/ers/ram/CompactPersistentLongSet.kt b/jacodb-storage/src/main/kotlin/org/jacodb/impl/storage/ers/ram/CompactPersistentLongSet.kt similarity index 98% rename from jacodb-core/src/main/kotlin/org/jacodb/impl/storage/ers/ram/CompactPersistentLongSet.kt rename to jacodb-storage/src/main/kotlin/org/jacodb/impl/storage/ers/ram/CompactPersistentLongSet.kt index c9d6fd617..1093bcafc 100644 --- a/jacodb-core/src/main/kotlin/org/jacodb/impl/storage/ers/ram/CompactPersistentLongSet.kt +++ b/jacodb-storage/src/main/kotlin/org/jacodb/impl/storage/ers/ram/CompactPersistentLongSet.kt @@ -124,13 +124,13 @@ internal class CompactPersistentLongSet(private val value: Any? = null) : Collec IllegalStateException("CompactPersistentLongSet.value can only be Long or PersistentLongSet") } -private val Long.interned: Long +internal val Long.interned: Long get() = if (this in 0 until LongInterner.boxedLongs.size) LongInterner.boxedLongs[this.toInt()] else this // TODO: remove this interner if specialized persistent collections would be used -object LongInterner { +private object LongInterner { val boxedLongs = Array(200000) { it.toLong() } } diff --git a/jacodb-storage/src/main/kotlin/org/jacodb/impl/storage/ers/ram/Links.kt b/jacodb-storage/src/main/kotlin/org/jacodb/impl/storage/ers/ram/Links.kt new file mode 100644 index 000000000..6424818b2 --- /dev/null +++ b/jacodb-storage/src/main/kotlin/org/jacodb/impl/storage/ers/ram/Links.kt @@ -0,0 +1,162 @@ +/* + * Copyright 2022 UnitTestBot contributors (utbot.org) + *

+ * 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 org.jacodb.impl.storage.ers.ram + +import jetbrains.exodus.core.dataStructures.persistent.PersistentLong23TreeMap +import jetbrains.exodus.core.dataStructures.persistent.PersistentLongMap +import org.jacodb.api.storage.ers.Entity +import org.jacodb.api.storage.ers.EntityId +import org.jacodb.api.storage.ers.EntityIterable +import org.jacodb.api.storage.ers.InstanceIdCollectionEntityIterable + +internal class LinksMutable( + internal val targetTypeId: Int = -1, + internal val links: PersistentLongMap = PersistentLong23TreeMap() +) { + + internal fun getLinks(txn: RAMTransaction, instanceId: Long): EntityIterable { + return InstanceIdCollectionEntityIterable( + txn, + targetTypeId, + links[instanceId]?.toList() ?: return EntityIterable.EMPTY + ) + } + + internal fun addLink(instanceId: Long, targetId: EntityId): LinksMutable { + val targetTypeId = checkTypeId(targetId) + val idSet = links[instanceId] ?: CompactPersistentLongSet() + val newIdSet = idSet.add(targetId.instanceId) + return if (idSet === newIdSet) { + this + } else { + LinksMutable(targetTypeId, links.write { put(instanceId, newIdSet) }.second) + } + } + + fun deleteLink(instanceId: Long, targetId: EntityId): LinksMutable { + val targetTypeId = checkTypeId(targetId) + val idSet = links[instanceId] ?: return this + val newIdSet = idSet.remove(targetId.instanceId) + return if (idSet === newIdSet) { + this + } else { + LinksMutable( + targetTypeId, + links.write { + if (newIdSet.isEmpty()) { + remove(instanceId) + } else { + put(instanceId, newIdSet) + } + }.second + ) + } + } + + private fun checkTypeId(id: EntityId): Int { + val typeId = id.typeId + if (targetTypeId != -1 && targetTypeId != typeId) { + error("LinksMutable can only store ids of the same typeId") + } + return typeId + } +} + +internal class LinksImmutable( + private val targetTypeId: Int, + private val attributes: AttributesImmutable +) { + + internal fun getLinks(txn: RAMTransaction, instanceId: Long): EntityIterable { + val bytes = attributes[instanceId] ?: return EntityIterable.EMPTY + return EntityIterable { + object : Iterator { + + var offset = 0 + var nextLink: Entity? = null + + override fun hasNext(): Boolean { + if (nextLink == null) { + while (offset < bytes.size) { + val (targetInstanceId, len) = readCompressedUnsignedLong(bytes, offset) + offset += len + val e = txn.getEntityOrNull(EntityId(targetTypeId, targetInstanceId)) + if (e != null) { + nextLink = e + return true + } + } + return false + } + return true + } + + override fun next(): Entity = nextLink?.also { nextLink = null } ?: throw NoSuchElementException() + } + } + } +} + +internal fun LinksMutable.toImmutable(): LinksImmutable { + val linkList = mutableListOf>() + links.beginRead().forEach { link -> + val instanceId = link.key + val linkSet = link.value + var valueArray = byteArrayOf() + linkSet.forEach { targetId -> + valueArray += writeCompressedUnsignedLong(targetId) + } + linkList += instanceId to valueArray + } + return LinksImmutable(targetTypeId, toAttributesImmutable(linkList)) +} + +/** + * Returns read unsigned long value and the length of the byte array used for the value. + */ +private fun readCompressedUnsignedLong(bytes: ByteArray, offset: Int): Pair { + var len = 0 + var result = 0L + while (true) { + val b = bytes[offset + len].toInt() + result += (b and 0x7F) shl (len * 7) + len++ + if ((b and 0x80) != 0) break + } + return result to len +} + +/** + * Writes compressed unsigned long value to a new byte array. + */ +private fun writeCompressedUnsignedLong(l: Long): ByteArray { + check(l >= 0) + var t = l + var len = 1 + while (t > 127) { + t = t shr 7 + len++ + } + t = l + val result = ByteArray(len) + for (i in 0 until len) { + result[i] = (t and 0x7F).toByte() + t = t shr 7 + } + result[len - 1] = (result[len - 1].toInt() or 0x80).toByte() + return result +} \ No newline at end of file diff --git a/jacodb-core/src/main/kotlin/org/jacodb/impl/storage/ers/ram/MutableContainer.kt b/jacodb-storage/src/main/kotlin/org/jacodb/impl/storage/ers/ram/MutableContainer.kt similarity index 87% rename from jacodb-core/src/main/kotlin/org/jacodb/impl/storage/ers/ram/MutableContainer.kt rename to jacodb-storage/src/main/kotlin/org/jacodb/impl/storage/ers/ram/MutableContainer.kt index ddb2a9c70..54ba47965 100644 --- a/jacodb-core/src/main/kotlin/org/jacodb/impl/storage/ers/ram/MutableContainer.kt +++ b/jacodb-storage/src/main/kotlin/org/jacodb/impl/storage/ers/ram/MutableContainer.kt @@ -16,6 +16,9 @@ package org.jacodb.impl.storage.ers.ram +/** + * Any persistent data structure that can have an intermediate "mutable" state prior to its new version. + */ internal interface MutableContainer { val isMutable: Boolean diff --git a/jacodb-core/src/main/kotlin/org/jacodb/impl/storage/ers/ram/PackedPersistentLongSet.kt b/jacodb-storage/src/main/kotlin/org/jacodb/impl/storage/ers/ram/PackedPersistentLongSet.kt similarity index 100% rename from jacodb-core/src/main/kotlin/org/jacodb/impl/storage/ers/ram/PackedPersistentLongSet.kt rename to jacodb-storage/src/main/kotlin/org/jacodb/impl/storage/ers/ram/PackedPersistentLongSet.kt diff --git a/jacodb-core/src/main/kotlin/org/jacodb/impl/storage/ers/ram/Properties.kt b/jacodb-storage/src/main/kotlin/org/jacodb/impl/storage/ers/ram/Properties.kt similarity index 63% rename from jacodb-core/src/main/kotlin/org/jacodb/impl/storage/ers/ram/Properties.kt rename to jacodb-storage/src/main/kotlin/org/jacodb/impl/storage/ers/ram/Properties.kt index 2d6b12b56..ddf99f37d 100644 --- a/jacodb-core/src/main/kotlin/org/jacodb/impl/storage/ers/ram/Properties.kt +++ b/jacodb-storage/src/main/kotlin/org/jacodb/impl/storage/ers/ram/Properties.kt @@ -19,18 +19,19 @@ package org.jacodb.impl.storage.ers.ram import jetbrains.exodus.core.dataStructures.persistent.Persistent23TreeMap import jetbrains.exodus.core.dataStructures.persistent.PersistentLong23TreeMap import jetbrains.exodus.core.dataStructures.persistent.PersistentLongMap -import org.jacodb.api.jvm.storage.ByteArrayKey -import org.jacodb.api.jvm.storage.ers.EntityIterable -import org.jacodb.api.jvm.storage.ers.InstanceIdCollectionEntityIterable +import org.jacodb.api.storage.ByteArrayKey +import org.jacodb.api.storage.asComparable +import org.jacodb.api.storage.ers.EntityIterable +import org.jacodb.api.storage.ers.InstanceIdCollectionEntityIterable private typealias ValueIndex = Persistent23TreeMap -internal class Properties( - val props: PersistentLongMap = PersistentLong23TreeMap(), // instanceId -> prop value +internal class PropertiesMutable( + internal val props: PersistentLongMap = PersistentLong23TreeMap(), // instanceId -> prop value private val valueIndex: ValueIndex? = null // prop value -> Set ) { - internal fun deleteProperty(instanceId: Long): Properties? { + internal fun deleteProperty(instanceId: Long): PropertiesMutable? { val (value, newProps) = props.write { remove(instanceId) } @@ -48,12 +49,12 @@ internal class Properties( put(byteArrayKey, newInstanceIdSet) } }.second - return Properties(newProps, newValueIndex) + return PropertiesMutable(newProps, newValueIndex) } - return Properties(newProps) + return PropertiesMutable(newProps) } - internal fun setProperty(instanceId: Long, value: ByteArray): Properties? { + internal fun setProperty(instanceId: Long, value: ByteArray): PropertiesMutable? { var unchanged = false val (oldValue, newProps) = props.write { if (valueIndex == null) { @@ -88,18 +89,18 @@ internal class Properties( } } }.second - return Properties(newProps, newValueIndex) + return PropertiesMutable(newProps, newValueIndex) } - return Properties(newProps) + return PropertiesMutable(newProps) } internal fun getEntitiesWithValue( txn: RAMTransaction, typeId: Int, value: ByteArray - ): Pair { + ): Pair { val (actualIndex, newIndex) = ensureValueIndex() - val newProperties = newIndex?.let { Properties(props, newIndex) } + val newProperties = newIndex?.let { PropertiesMutable(props, newIndex) } return newProperties to InstanceIdCollectionEntityIterable( txn, typeId, actualIndex[ByteArrayKey(value)] ?: return newProperties to EntityIterable.EMPTY ) @@ -109,9 +110,9 @@ internal class Properties( txn: RAMTransaction, typeId: Int, value: ByteArray - ): Pair { + ): Pair { val (actualIndex, newIndex) = ensureValueIndex() - val newProperties = newIndex?.let { Properties(props, newIndex) } + val newProperties = newIndex?.let { PropertiesMutable(props, newIndex) } val bound = ByteArrayKey(value) val result = mutableListOf() actualIndex.beginRead().iterator().forEach { @@ -131,9 +132,9 @@ internal class Properties( txn: RAMTransaction, typeId: Int, value: ByteArray - ): Pair { + ): Pair { val (actualIndex, newIndex) = ensureValueIndex() - val newProperties = newIndex?.let { Properties(props, newIndex) } + val newProperties = newIndex?.let { PropertiesMutable(props, newIndex) } val bound = ByteArrayKey(value) val result = mutableListOf() actualIndex.beginRead().iterator().forEach { @@ -153,9 +154,9 @@ internal class Properties( txn: RAMTransaction, typeId: Int, value: ByteArray - ): Pair { + ): Pair { val (actualIndex, newIndex) = ensureValueIndex() - val newProperties = newIndex?.let { Properties(props, newIndex) } + val newProperties = newIndex?.let { PropertiesMutable(props, newIndex) } val bound = ByteArrayKey(value) val result = mutableListOf() actualIndex.beginRead().tailIterator(actualIndex.createEntry(bound)).forEach { @@ -175,9 +176,9 @@ internal class Properties( txn: RAMTransaction, typeId: Int, value: ByteArray - ): Pair { + ): Pair { val (actualIndex, newIndex) = ensureValueIndex() - val newProperties = newIndex?.let { Properties(props, newIndex) } + val newProperties = newIndex?.let { PropertiesMutable(props, newIndex) } val bound = ByteArrayKey(value) val result = mutableListOf() actualIndex.beginRead().tailIterator(actualIndex.createEntry(bound)).forEach { @@ -208,4 +209,74 @@ internal class Properties( }.second return newIndex to newIndex } -} \ No newline at end of file +} + +internal class PropertiesImmutable(private val attributes: AttributesImmutable) { + + operator fun get(instanceId: Long): ByteArray? { + return attributes[instanceId] + } + + internal fun getEntitiesWithValue(txn: RAMTransaction, typeId: Int, value: ByteArray): EntityIterable { + return AttributesCursorEntityIterable( + txn = txn, + typeId = typeId, + forwardDirection = true, + cursor = attributes.navigate(value, leftBound = true).apply { movePrev() } + ) { _, v -> + v contentEquals value + } + } + + fun getEntitiesLtValue(txn: RAMTransaction, typeId: Int, value: ByteArray): EntityIterable { + val valueComparable = value.asComparable() + return AttributesCursorEntityIterable( + txn = txn, + typeId = typeId, + forwardDirection = false, + cursor = attributes.navigate(value, leftBound = true) + ) { _, v -> + v.asComparable() < valueComparable + } + } + + fun getEntitiesEqOrLtValue(txn: RAMTransaction, typeId: Int, value: ByteArray): EntityIterable { + val valueComparable = value.asComparable() + return AttributesCursorEntityIterable( + txn = txn, + typeId = typeId, + forwardDirection = false, + cursor = attributes.navigate(value, leftBound = false).apply { if (hasMatch) moveNext() } + ) { _, v -> + v.asComparable() <= valueComparable + } + } + + fun getEntitiesGtValue(txn: RAMTransaction, typeId: Int, value: ByteArray): EntityIterable { + val valueComparable = value.asComparable() + return AttributesCursorEntityIterable( + txn = txn, + typeId = typeId, + forwardDirection = true, + cursor = attributes.navigate(value, leftBound = false).apply { if (!hasMatch) movePrev() } + ) { _, v -> + v.asComparable() > valueComparable + } + } + + fun getEntitiesEqOrGtValue(txn: RAMTransaction, typeId: Int, value: ByteArray): EntityIterable { + val valueComparable = value.asComparable() + return AttributesCursorEntityIterable( + txn = txn, + typeId = typeId, + forwardDirection = true, + cursor = attributes.navigate(value, leftBound = true).apply { movePrev() } + ) { _, v -> + v.asComparable() >= valueComparable + } + } +} + +internal fun PropertiesMutable.toImmutable(): PropertiesImmutable { + return PropertiesImmutable(toAttributesImmutable(props.beginRead().map { it.key to it.value })) +} diff --git a/jacodb-storage/src/main/kotlin/org/jacodb/impl/storage/ers/ram/RAMDataContainer.kt b/jacodb-storage/src/main/kotlin/org/jacodb/impl/storage/ers/ram/RAMDataContainer.kt new file mode 100644 index 000000000..c87031bba --- /dev/null +++ b/jacodb-storage/src/main/kotlin/org/jacodb/impl/storage/ers/ram/RAMDataContainer.kt @@ -0,0 +1,92 @@ +/* + * Copyright 2022 UnitTestBot contributors (utbot.org) + *

+ * 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 org.jacodb.impl.storage.ers.ram + +import org.jacodb.api.storage.ers.EntityId +import org.jacodb.api.storage.ers.EntityIterable + +internal interface RAMDataContainer : MutableContainer { + + fun toImmutable(): RAMDataContainer + + fun entityExists(id: EntityId): Boolean + + fun getTypeId(type: String): Int + + fun getOrAllocateTypeId(type: String): Pair + + fun allocateInstanceId(typeId: Int): Pair + + fun getPropertyNames(type: String): Set + + fun getBlobNames(type: String): Set + + fun getLinkNames(type: String): Set + + fun all(txn: RAMTransaction, type: String): EntityIterable + + fun deleteEntity(id: EntityId): RAMDataContainer + + fun getRawProperty(id: EntityId, propertyName: String): ByteArray? + + fun setRawProperty(id: EntityId, propertyName: String, value: ByteArray?): RAMDataContainer + + fun getEntitiesWithPropertyValue( + txn: RAMTransaction, + type: String, + propertyName: String, + value: ByteArray + ): Pair + + fun getEntitiesLtPropertyValue( + txn: RAMTransaction, + type: String, + propertyName: String, + value: ByteArray + ): Pair + + fun getEntitiesEqOrLtPropertyValue( + txn: RAMTransaction, + type: String, + propertyName: String, + value: ByteArray + ): Pair + + fun getEntitiesGtPropertyValue( + txn: RAMTransaction, + type: String, + propertyName: String, + value: ByteArray + ): Pair + + fun getEntitiesEqOrGtPropertyValue( + txn: RAMTransaction, + type: String, + propertyName: String, + value: ByteArray + ): Pair + + fun getBlob(id: EntityId, blobName: String): ByteArray? + + fun setBlob(id: EntityId, blobName: String, value: ByteArray?): RAMDataContainer + + fun getLinks(txn: RAMTransaction, id: EntityId, linkName: String): EntityIterable + + fun addLink(id: EntityId, linkName: String, targetId: EntityId): RAMDataContainer + + fun deleteLink(id: EntityId, linkName: String, targetId: EntityId): RAMDataContainer +} \ No newline at end of file diff --git a/jacodb-storage/src/main/kotlin/org/jacodb/impl/storage/ers/ram/RAMDataContainerImmutable.kt b/jacodb-storage/src/main/kotlin/org/jacodb/impl/storage/ers/ram/RAMDataContainerImmutable.kt new file mode 100644 index 000000000..db37810b4 --- /dev/null +++ b/jacodb-storage/src/main/kotlin/org/jacodb/impl/storage/ers/ram/RAMDataContainerImmutable.kt @@ -0,0 +1,179 @@ +/* + * Copyright 2022 UnitTestBot contributors (utbot.org) + *

+ * 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 org.jacodb.impl.storage.ers.ram + +import org.jacodb.api.storage.ers.EntityId +import org.jacodb.api.storage.ers.EntityIterable +import org.jacodb.api.storage.ers.longRangeIterable + +internal class RAMDataContainerImmutable( + // map of entity types to their type ids + private val types: Map, + // arrays of instance info by typeId + // typeId is an index in the array, pair contains next free instance id and bit set of ids of deleted entities + private val instances: Array?>, + // (typeId, propName) -> PropertiesImmutable + private var properties: Map, + // (typeId, linkName) -> // LinksImmutable + private var links: Map, + // (typeId, linkName) -> blobs + private var blobs: Map +) : RAMDataContainer { + + override val isMutable = false + + override fun mutate() = throwError("cannot mutate") + + override fun commit(): RAMDataContainer = this + + override fun toImmutable(): RAMDataContainer = this + + override fun entityExists(id: EntityId): Boolean { + val typeId = id.typeId + return typeId in instances.indices && true == instances[typeId]?.let { (nextFreeInstance, deleted) -> + val instanceId = id.instanceId + instanceId < nextFreeInstance && !deleted.contains(instanceId) + } + } + + override fun getTypeId(type: String): Int = types[type] ?: -1 + + override fun getOrAllocateTypeId(type: String): Pair { + return getTypeId(type).let { + if (it >= 0L) this to it else cantModify() + } + } + + override fun allocateInstanceId(typeId: Int): Pair = cantModify() + + override fun getPropertyNames(type: String): Set = getAttributeNames(type, properties.keys) + + override fun getBlobNames(type: String): Set = getAttributeNames(type, blobs.keys) + + override fun getLinkNames(type: String): Set = getAttributeNames(type, links.keys) + + override fun all(txn: RAMTransaction, type: String): EntityIterable { + val typeId = types[type] ?: return EntityIterable.EMPTY + val (nextFreeInstanceId, deleted) = instances[typeId] ?: return EntityIterable.EMPTY + return if (deleted.isEmpty) { + longRangeIterable(txn, typeId, 0 until nextFreeInstanceId) + } else { + longRangeIterable(txn, typeId, 0 until nextFreeInstanceId) { + !deleted.contains(it) + } + } + } + + override fun deleteEntity(id: EntityId): RAMDataContainer = cantModify() + + override fun getRawProperty(id: EntityId, propertyName: String): ByteArray? { + val typeId = id.typeId + return properties[typeId withField propertyName]?.let { it[id.instanceId] } + } + + override fun setRawProperty(id: EntityId, propertyName: String, value: ByteArray?): RAMDataContainer = cantModify() + + override fun getEntitiesWithPropertyValue( + txn: RAMTransaction, + type: String, + propertyName: String, + value: ByteArray + ): Pair { + return null to getEntitiesWithPropertyFunction(type, propertyName) { typeId -> + getEntitiesWithValue(txn, typeId, value) + } + } + + override fun getEntitiesLtPropertyValue( + txn: RAMTransaction, + type: String, + propertyName: String, + value: ByteArray + ): Pair { + return null to getEntitiesWithPropertyFunction(type, propertyName) { typeId -> + getEntitiesLtValue(txn, typeId, value) + } + } + + override fun getEntitiesEqOrLtPropertyValue( + txn: RAMTransaction, + type: String, + propertyName: String, + value: ByteArray + ): Pair { + return null to getEntitiesWithPropertyFunction(type, propertyName) { typeId -> + getEntitiesEqOrLtValue(txn, typeId, value) + } + } + + override fun getEntitiesGtPropertyValue( + txn: RAMTransaction, + type: String, + propertyName: String, + value: ByteArray + ): Pair { + return null to getEntitiesWithPropertyFunction(type, propertyName) { typeId -> + getEntitiesGtValue(txn, typeId, value) + } + } + + override fun getEntitiesEqOrGtPropertyValue( + txn: RAMTransaction, + type: String, + propertyName: String, + value: ByteArray + ): Pair { + return null to getEntitiesWithPropertyFunction(type, propertyName) { typeId -> + getEntitiesEqOrGtValue(txn, typeId, value) + } + } + + override fun getBlob(id: EntityId, blobName: String): ByteArray? { + val typeId = id.typeId + return blobs[typeId withField blobName]?.let { it[id.instanceId] } + } + + override fun setBlob(id: EntityId, blobName: String, value: ByteArray?): RAMDataContainer = cantModify() + + override fun getLinks( + txn: RAMTransaction, + id: EntityId, + linkName: String + ): EntityIterable { + val typeId = id.typeId + val links = links[typeId withField linkName] ?: return EntityIterable.EMPTY + return links.getLinks(txn, id.instanceId) + } + + override fun addLink(id: EntityId, linkName: String, targetId: EntityId): RAMDataContainer = cantModify() + + override fun deleteLink(id: EntityId, linkName: String, targetId: EntityId): RAMDataContainer = cantModify() + + private fun getEntitiesWithPropertyFunction( + type: String, + propertyName: String, + f: PropertiesImmutable.(Int) -> EntityIterable + ): EntityIterable { + val typeId = types[type] ?: return EntityIterable.EMPTY + val properties = properties[typeId withField propertyName] ?: return EntityIterable.EMPTY + return properties.f(typeId) + } + + private fun cantModify(): Nothing = throwError("cannot modify") + + private fun throwError(msg: String): Nothing = error("RAMDataContainerImmutable: $msg") +} \ No newline at end of file diff --git a/jacodb-core/src/main/kotlin/org/jacodb/impl/storage/ers/ram/RAMPersistentDataContainer.kt b/jacodb-storage/src/main/kotlin/org/jacodb/impl/storage/ers/ram/RAMDataContainerMutable.kt similarity index 64% rename from jacodb-core/src/main/kotlin/org/jacodb/impl/storage/ers/ram/RAMPersistentDataContainer.kt rename to jacodb-storage/src/main/kotlin/org/jacodb/impl/storage/ers/ram/RAMDataContainerMutable.kt index cc7622940..c26545722 100644 --- a/jacodb-core/src/main/kotlin/org/jacodb/impl/storage/ers/ram/RAMPersistentDataContainer.kt +++ b/jacodb-storage/src/main/kotlin/org/jacodb/impl/storage/ers/ram/RAMDataContainerMutable.kt @@ -16,20 +16,20 @@ package org.jacodb.impl.storage.ers.ram -import org.jacodb.api.jvm.storage.ers.CollectionEntityIterable -import org.jacodb.api.jvm.storage.ers.Entity -import org.jacodb.api.jvm.storage.ers.EntityId -import org.jacodb.api.jvm.storage.ers.EntityIterable +import org.jacodb.api.storage.ers.EntityId +import org.jacodb.api.storage.ers.EntityIterable +import org.jacodb.api.storage.ers.filterInstanceIds +import org.jacodb.api.storage.ers.longRangeIterable -internal class RAMPersistentDataContainer( +internal class RAMDataContainerMutable( private var typeIdCounter: Int, // next free type id private var types: TransactionalPersistentMap, // types private var instances: TransactionalPersistentMap, // type id -> Entities - private var properties: TransactionalPersistentMap, // (typeId, propName) -> Properties - private var links: TransactionalPersistentMap, // (typeId, linkName) -> // Links + private var properties: TransactionalPersistentMap, // (typeId, propName) -> Properties + private var links: TransactionalPersistentMap, // (typeId, linkName) -> // Links private var blobs: TransactionalPersistentMap>, // (typeId, linkName) -> blobs override val isMutable: Boolean = false -) : MutableContainer { +) : RAMDataContainer { private val mutableEntities = hashMapOf() private val mutableBlobs = hashMapOf>() @@ -43,9 +43,9 @@ internal class RAMPersistentDataContainer( TransactionalPersistentMap(), ) - override fun mutate(): RAMPersistentDataContainer { + override fun mutate(): RAMDataContainer { return if (isMutable) this - else RAMPersistentDataContainer( + else RAMDataContainerMutable( typeIdCounter, types.getClone(), instances.getClone(), @@ -56,7 +56,7 @@ internal class RAMPersistentDataContainer( ) } - override fun commit(): RAMPersistentDataContainer { + override fun commit(): RAMDataContainer { return if (!isMutable) { this } else { @@ -68,7 +68,7 @@ internal class RAMPersistentDataContainer( this.blobs.put(attributeKey, blobs.commit()) } mutableBlobs.clear() - RAMPersistentDataContainer( + RAMDataContainerMutable( typeIdCounter, types.commit(), instances.commit(), @@ -79,14 +79,52 @@ internal class RAMPersistentDataContainer( } } - fun entityExists(id: EntityId): Boolean { + override fun toImmutable(): RAMDataContainerImmutable { + val types = HashMap().also { map -> + this.types.entries().forEach { entry -> map[entry.key] = entry.value } + } + val instances = arrayOfNulls>(this.instances.keys().max() + 1) + this.instances.entries().forEach { entry -> + val entities = entry.value + instances[entry.key] = entities.instanceIdCounter to + if (entities.deleted.isEmpty()) EmptySparseBitSet else SparseBitSet().apply { + entities.deleted.forEach { deletedId -> + set(deletedId) + } + } + } + val properties = HashMap().also { map -> + this.properties.entries().forEach { entry -> + map[entry.key] = entry.value.toImmutable() + } + } + val links = HashMap().also { map -> + this.links.entries().forEach { entry -> + map[entry.key] = entry.value.toImmutable() + } + } + val blobs = HashMap().also { map -> + this.blobs.entries().forEach { entry -> + map[entry.key] = toAttributesImmutable(entry.value.entries().map { it.key to it.value }) + } + } + return RAMDataContainerImmutable( + types = types, + instances = instances, + properties = properties, + links = links, + blobs = blobs + ) + } + + override fun entityExists(id: EntityId): Boolean { val entities = instances[id.typeId] ?: return false return !entities.deleted.contains(id.instanceId) } - fun getTypeId(type: String): Int = types[type] ?: -1 + override fun getTypeId(type: String): Int = types[type] ?: -1 - fun getOrAllocateTypeId(type: String): Pair { + override fun getOrAllocateTypeId(type: String): Pair { types[type]?.let { id -> return this to id } @@ -97,7 +135,7 @@ internal class RAMPersistentDataContainer( } to id } - fun allocateInstanceId(typeId: Int): Pair { + override fun allocateInstanceId(typeId: Int): Pair { val entities = instances[typeId] ?: Entities() val id = entities.instanceIdCounter return entities.withMutableEntities(typeId) { @@ -105,38 +143,29 @@ internal class RAMPersistentDataContainer( } to id } - fun getPropertyNames(type: String): Set = getAttributeNames(type, properties) + override fun getPropertyNames(type: String): Set = getAttributeNames(type, properties.getClone().keys()) - fun getBlobNames(type: String): Set = getAttributeNames(type, blobs) + override fun getBlobNames(type: String): Set = getAttributeNames(type, blobs.getClone().keys()) - fun getLinkNames(type: String): Set = getAttributeNames(type, links) + override fun getLinkNames(type: String): Set = getAttributeNames(type, links.getClone().keys()) - fun all(txn: RAMTransaction, type: String): EntityIterable { + override fun all(txn: RAMTransaction, type: String): EntityIterable { val typeId = types[type] ?: return EntityIterable.EMPTY val entities = instances[typeId] ?: return EntityIterable.EMPTY val entitiesCount = entities.instanceIdCounter if (entitiesCount == 0L) { return EntityIterable.EMPTY } - val result = if (entities.deleted.isEmpty()) { - ArrayList(entitiesCount.toInt()).apply { - (0 until entitiesCount).forEach { instanceId -> - add(RAMEntity(txn, EntityId(typeId, instanceId))) - } - } + return if (entities.deleted.isEmpty()) { + longRangeIterable(txn, typeId, 0 until entitiesCount) } else { - (0 until entitiesCount).mapNotNullTo(ArrayList(entitiesCount.toInt())) { instanceId -> - if (instanceId in entities.deleted) { - null - } else { - RAMEntity(txn, EntityId(typeId, instanceId)) - } + longRangeIterable(txn, typeId, 0 until entitiesCount) { + it !in entities.deleted } } - return CollectionEntityIterable(result) } - fun deleteEntity(id: EntityId): RAMPersistentDataContainer { + override fun deleteEntity(id: EntityId): RAMDataContainer { val typeId = id.typeId val entities = instances[typeId] ?: return this return entities.withMutableEntities(typeId) { @@ -144,19 +173,19 @@ internal class RAMPersistentDataContainer( } } - fun getRawProperty(id: EntityId, propertyName: String): ByteArray? { + override fun getRawProperty(id: EntityId, propertyName: String): ByteArray? { val typeId = id.typeId return properties[typeId withField propertyName]?.let { it.props[id.instanceId] } } - fun setRawProperty(id: EntityId, propertyName: String, value: ByteArray?): RAMPersistentDataContainer { + override fun setRawProperty(id: EntityId, propertyName: String, value: ByteArray?): RAMDataContainer { val propertiesKey = id.typeId withField propertyName // if value == null we are to delete the property val newProperties = if (value == null) { val properties = properties[propertiesKey] ?: return this properties.deleteProperty(id.instanceId) ?: return this } else { - val properties = properties[propertiesKey] ?: Properties() + val properties = properties[propertiesKey] ?: PropertiesMutable() properties.setProperty(id.instanceId, value) ?: return this } return withMutableCopy { @@ -164,67 +193,67 @@ internal class RAMPersistentDataContainer( } } - fun getEntitiesWithPropertyValue( + override fun getEntitiesWithPropertyValue( txn: RAMTransaction, type: String, propertyName: String, value: ByteArray - ): Pair { + ): Pair { return getEntitiesWithPropertyFunction(type, propertyName) { typeId -> getEntitiesWithValue(txn, typeId, value) } } - fun getEntitiesLtPropertyValue( + override fun getEntitiesLtPropertyValue( txn: RAMTransaction, type: String, propertyName: String, value: ByteArray - ): Pair { + ): Pair { return getEntitiesWithPropertyFunction(type, propertyName) { typeId -> getEntitiesLtValue(txn, typeId, value) } } - fun getEntitiesEqOrLtPropertyValue( + override fun getEntitiesEqOrLtPropertyValue( txn: RAMTransaction, type: String, propertyName: String, value: ByteArray - ): Pair { + ): Pair { return getEntitiesWithPropertyFunction(type, propertyName) { typeId -> getEntitiesEqOrLtValue(txn, typeId, value) } } - fun getEntitiesGtPropertyValue( + override fun getEntitiesGtPropertyValue( txn: RAMTransaction, type: String, propertyName: String, value: ByteArray - ): Pair { + ): Pair { return getEntitiesWithPropertyFunction(type, propertyName) { typeId -> getEntitiesGtValue(txn, typeId, value) } } - fun getEntitiesEqOrGtPropertyValue( + override fun getEntitiesEqOrGtPropertyValue( txn: RAMTransaction, type: String, propertyName: String, value: ByteArray - ): Pair { + ): Pair { return getEntitiesWithPropertyFunction(type, propertyName) { typeId -> getEntitiesEqOrGtValue(txn, typeId, value) } } - fun getBlob(id: EntityId, blobName: String): ByteArray? { + override fun getBlob(id: EntityId, blobName: String): ByteArray? { val typeId = id.typeId return blobs[typeId withField blobName]?.let { it[id.instanceId] } } - fun setBlob(id: EntityId, blobName: String, value: ByteArray?): RAMPersistentDataContainer { + override fun setBlob(id: EntityId, blobName: String, value: ByteArray?): RAMDataContainer { val blobsKey = id.typeId withField blobName // if value == null we are to delete the blob return if (value == null) { @@ -240,16 +269,16 @@ internal class RAMPersistentDataContainer( } } - fun getLinks(txn: RAMTransaction, id: EntityId, linkName: String): EntityIterable { + override fun getLinks(txn: RAMTransaction, id: EntityId, linkName: String): EntityIterable { val typeId = id.typeId val links = links[typeId withField linkName] ?: return EntityIterable.EMPTY return links.getLinks(txn, id.instanceId).filterDeleted(typeId) } - fun addLink(id: EntityId, linkName: String, targetId: EntityId): RAMPersistentDataContainer { + override fun addLink(id: EntityId, linkName: String, targetId: EntityId): RAMDataContainer { val typeId = id.typeId val linksKey = typeId withField linkName - val links = links[linksKey] ?: Links() + val links = links[linksKey] ?: LinksMutable() val newLinks = links.addLink(id.instanceId, targetId) return if (newLinks === links) { this @@ -260,7 +289,7 @@ internal class RAMPersistentDataContainer( } } - fun deleteLink(id: EntityId, linkName: String, targetId: EntityId): RAMPersistentDataContainer { + override fun deleteLink(id: EntityId, linkName: String, targetId: EntityId): RAMDataContainer { val typeId = id.typeId val linksKey = typeId withField linkName val links = links[linksKey] ?: return this @@ -277,8 +306,8 @@ internal class RAMPersistentDataContainer( private fun getEntitiesWithPropertyFunction( type: String, propertyName: String, - f: Properties.(Int) -> Pair - ): Pair { + f: PropertiesMutable.(Int) -> Pair + ): Pair { val typeId = types[type] ?: return null to EntityIterable.EMPTY val propertiesKey = typeId withField propertyName val properties = properties[propertiesKey] ?: return null to EntityIterable.EMPTY @@ -295,26 +324,26 @@ internal class RAMPersistentDataContainer( if (this === EntityIterable.EMPTY) { return this } - val instances = this@RAMPersistentDataContainer.instances[typeId] ?: return EntityIterable.EMPTY + val instances = this@RAMDataContainerMutable.instances[typeId] ?: return EntityIterable.EMPTY return if (instances.deleted.isEmpty()) { this } else { - CollectionEntityIterable(filterTo(toMutableSet()) { e -> e.id.instanceId !in instances.deleted }) + filterInstanceIds { instanceId -> instanceId !in instances.deleted } } } - private fun withMutableCopy(action: RAMPersistentDataContainer.() -> Unit): RAMPersistentDataContainer { + private fun withMutableCopy(action: RAMDataContainerMutable.() -> Unit): RAMDataContainer { return if (isMutable) { this } else { - mutate() + mutate() as RAMDataContainerMutable }.apply(action) } - private fun Entities.withMutableEntities(typeId: Int, action: Entities.() -> Unit): RAMPersistentDataContainer { + private fun Entities.withMutableEntities(typeId: Int, action: Entities.() -> Unit): RAMDataContainer { return if (isMutable) { action() - this@RAMPersistentDataContainer + this@RAMDataContainerMutable } else mutate().let { it.action() withMutableCopy { @@ -327,10 +356,10 @@ internal class RAMPersistentDataContainer( private fun TransactionalPersistentLongMap.withMutableBlobs( key: AttributeKey, action: TransactionalPersistentLongMap.() -> Unit - ): RAMPersistentDataContainer { + ): RAMDataContainer { return if (isMutable) { action() - this@RAMPersistentDataContainer + this@RAMDataContainerMutable } else mutate().let { it.action() withMutableCopy { @@ -339,12 +368,6 @@ internal class RAMPersistentDataContainer( } } } - - private fun getAttributeNames(type: String, map: TransactionalPersistentMap): Set = - types[type]?.let { typeId -> - map.getClone().entries() - .mapNotNullTo(sortedSetOf()) { if (it.key.typeId == typeId) it.key.name else null } - } ?: emptySet() } internal data class AttributeKey(val typeId: Int, val name: String) : Comparable { @@ -356,7 +379,16 @@ internal data class AttributeKey(val typeId: Int, val name: String) : Comparable } } -private infix fun Int.withField(name: String): AttributeKey = AttributeKey(this, name) +internal fun RAMDataContainer.getAttributeNames(type: String, keys: Iterable): Set = + getTypeId(type).let { typeId -> + if (typeId >= 0L) { + keys.mapNotNullTo(sortedSetOf()) { if (it.typeId == typeId) it.name else null } + } else { + emptySet() + } + } + +internal infix fun Int.withField(name: String): AttributeKey = AttributeKey(this, name) internal class Entities( var instanceIdCounter: Long = 0L, diff --git a/jacodb-core/src/main/kotlin/org/jacodb/impl/storage/ers/ram/RAMEntity.kt b/jacodb-storage/src/main/kotlin/org/jacodb/impl/storage/ers/ram/RAMEntity.kt similarity index 91% rename from jacodb-core/src/main/kotlin/org/jacodb/impl/storage/ers/ram/RAMEntity.kt rename to jacodb-storage/src/main/kotlin/org/jacodb/impl/storage/ers/ram/RAMEntity.kt index 69d63a9fc..3a8022d4c 100644 --- a/jacodb-core/src/main/kotlin/org/jacodb/impl/storage/ers/ram/RAMEntity.kt +++ b/jacodb-storage/src/main/kotlin/org/jacodb/impl/storage/ers/ram/RAMEntity.kt @@ -16,9 +16,9 @@ package org.jacodb.impl.storage.ers.ram -import org.jacodb.api.jvm.storage.ers.Entity -import org.jacodb.api.jvm.storage.ers.EntityId -import org.jacodb.api.jvm.storage.ers.EntityIterable +import org.jacodb.api.storage.ers.Entity +import org.jacodb.api.storage.ers.EntityId +import org.jacodb.api.storage.ers.EntityIterable internal class RAMEntity(override val txn: RAMTransaction, override val id: EntityId) : Entity() { diff --git a/jacodb-core/src/main/kotlin/org/jacodb/impl/storage/ers/ram/RAMEntityRelationshipStorage.kt b/jacodb-storage/src/main/kotlin/org/jacodb/impl/storage/ers/ram/RAMEntityRelationshipStorage.kt similarity index 63% rename from jacodb-core/src/main/kotlin/org/jacodb/impl/storage/ers/ram/RAMEntityRelationshipStorage.kt rename to jacodb-storage/src/main/kotlin/org/jacodb/impl/storage/ers/ram/RAMEntityRelationshipStorage.kt index 749362f8e..df93e82c1 100644 --- a/jacodb-core/src/main/kotlin/org/jacodb/impl/storage/ers/ram/RAMEntityRelationshipStorage.kt +++ b/jacodb-storage/src/main/kotlin/org/jacodb/impl/storage/ers/ram/RAMEntityRelationshipStorage.kt @@ -16,32 +16,38 @@ package org.jacodb.impl.storage.ers.ram -import org.jacodb.api.jvm.storage.ers.Binding -import org.jacodb.api.jvm.storage.ers.EntityRelationshipStorage +import org.jacodb.api.storage.ers.Binding +import org.jacodb.api.storage.ers.EntityRelationshipStorage import org.jacodb.impl.storage.ers.decorators.withAllDecorators import org.jacodb.impl.storage.ers.getBinding import java.util.concurrent.atomic.AtomicReference -internal class RAMEntityRelationshipStorage : EntityRelationshipStorage { +internal class RAMEntityRelationshipStorage(dataContainer: RAMDataContainer = RAMDataContainerMutable()) : + EntityRelationshipStorage { - private val data: AtomicReference = AtomicReference(RAMPersistentDataContainer()) + private val data: AtomicReference = AtomicReference(dataContainer) + + override val isInRam: Boolean get() = true override fun beginTransaction(readonly: Boolean) = RAMTransaction(this).withAllDecorators() + override val asReadonly: EntityRelationshipStorage + get() = if (dataContainer is RAMDataContainerImmutable) this else RAMEntityRelationshipStorage(dataContainer.toImmutable()) + override fun getBinding(clazz: Class): Binding = clazz.getBinding() override fun close() { - data.set(RAMPersistentDataContainer()) + data.set(RAMDataContainerMutable()) } - internal var dataContainer: RAMPersistentDataContainer + internal var dataContainer: RAMDataContainer get() = data.get() set(value) { data.set(value) } internal fun compareAndSetDataContainer( - expected: RAMPersistentDataContainer, - newOne: RAMPersistentDataContainer + expected: RAMDataContainer, + newOne: RAMDataContainer ): Boolean = data.compareAndSet(expected, newOne) } \ No newline at end of file diff --git a/jacodb-core/src/main/kotlin/org/jacodb/impl/storage/ers/ram/RAMEntityRelationshipStorageSPI.kt b/jacodb-storage/src/main/kotlin/org/jacodb/impl/storage/ers/ram/RAMEntityRelationshipStorageSPI.kt similarity index 86% rename from jacodb-core/src/main/kotlin/org/jacodb/impl/storage/ers/ram/RAMEntityRelationshipStorageSPI.kt rename to jacodb-storage/src/main/kotlin/org/jacodb/impl/storage/ers/ram/RAMEntityRelationshipStorageSPI.kt index f05fd6396..e75b3b76b 100644 --- a/jacodb-core/src/main/kotlin/org/jacodb/impl/storage/ers/ram/RAMEntityRelationshipStorageSPI.kt +++ b/jacodb-storage/src/main/kotlin/org/jacodb/impl/storage/ers/ram/RAMEntityRelationshipStorageSPI.kt @@ -16,9 +16,9 @@ package org.jacodb.impl.storage.ers.ram -import org.jacodb.api.jvm.storage.ers.ErsSettings -import org.jacodb.api.jvm.storage.ers.EntityRelationshipStorage -import org.jacodb.api.jvm.storage.ers.EntityRelationshipStorageSPI +import org.jacodb.api.storage.ers.ErsSettings +import org.jacodb.api.storage.ers.EntityRelationshipStorage +import org.jacodb.api.storage.ers.EntityRelationshipStorageSPI const val RAM_ERS_SPI = "org.jacodb.impl.storage.ers.ram.RAMEntityRelationshipStorageSPI" diff --git a/jacodb-core/src/main/kotlin/org/jacodb/impl/storage/ers/ram/RAMTransaction.kt b/jacodb-storage/src/main/kotlin/org/jacodb/impl/storage/ers/ram/RAMTransaction.kt similarity index 91% rename from jacodb-core/src/main/kotlin/org/jacodb/impl/storage/ers/ram/RAMTransaction.kt rename to jacodb-storage/src/main/kotlin/org/jacodb/impl/storage/ers/ram/RAMTransaction.kt index f7ae9c930..70d93f800 100644 --- a/jacodb-core/src/main/kotlin/org/jacodb/impl/storage/ers/ram/RAMTransaction.kt +++ b/jacodb-storage/src/main/kotlin/org/jacodb/impl/storage/ers/ram/RAMTransaction.kt @@ -16,18 +16,18 @@ package org.jacodb.impl.storage.ers.ram -import org.jacodb.api.jvm.storage.ers.ERSConflictingTransactionException -import org.jacodb.api.jvm.storage.ers.ERSException -import org.jacodb.api.jvm.storage.ers.Entity -import org.jacodb.api.jvm.storage.ers.EntityId -import org.jacodb.api.jvm.storage.ers.EntityIterable -import org.jacodb.api.jvm.storage.ers.Transaction -import org.jacodb.api.jvm.storage.ers.probablyCompressed +import org.jacodb.api.storage.ers.ERSConflictingTransactionException +import org.jacodb.api.storage.ers.ERSException +import org.jacodb.api.storage.ers.Entity +import org.jacodb.api.storage.ers.EntityId +import org.jacodb.api.storage.ers.EntityIterable +import org.jacodb.api.storage.ers.Transaction +import org.jacodb.api.storage.ers.probablyCompressed internal class RAMTransaction(override val ers: RAMEntityRelationshipStorage) : Transaction { private var originContainer = ers.dataContainer - private var dataContainer: RAMPersistentDataContainer? = originContainer + private var dataContainer: RAMDataContainer? = originContainer override val isReadonly = false @@ -43,6 +43,8 @@ internal class RAMTransaction(override val ers: RAMEntityRelationshipStorage) : override fun getEntityOrNull(id: EntityId): Entity? = if (dataContainerChecked.entityExists(id)) RAMEntity(this, id) else null + override fun getEntityUnsafe(id: EntityId): Entity = RAMEntity(txn = this, id) + override fun deleteEntity(id: EntityId) { dataContainer = dataContainerChecked.deleteEntity(id) } @@ -98,7 +100,7 @@ internal class RAMTransaction(override val ers: RAMEntityRelationshipStorage) : } override fun dropAll() { - dataContainer = RAMPersistentDataContainer() + dataContainer = RAMDataContainerMutable() } override fun commit() { @@ -151,7 +153,7 @@ internal class RAMTransaction(override val ers: RAMEntityRelationshipStorage) : } } - private val dataContainerChecked: RAMPersistentDataContainer + private val dataContainerChecked: RAMDataContainer get() = dataContainer ?: throw ERSException("Transaction has been already finished") private fun getOrAllocateTypeId(type: String): Int { diff --git a/jacodb-storage/src/main/kotlin/org/jacodb/impl/storage/ers/ram/SparseBitSet.kt b/jacodb-storage/src/main/kotlin/org/jacodb/impl/storage/ers/ram/SparseBitSet.kt new file mode 100644 index 000000000..4f12325e6 --- /dev/null +++ b/jacodb-storage/src/main/kotlin/org/jacodb/impl/storage/ers/ram/SparseBitSet.kt @@ -0,0 +1,83 @@ +/* + * Copyright 2022 UnitTestBot contributors (utbot.org) + *

+ * 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 org.jacodb.impl.storage.ers.ram + +import java.util.Collections +import java.util.NavigableMap +import java.util.TreeMap + +object EmptySparseBitSet: SparseBitSet(immutable = true) + +open class SparseBitSet(bits: Collection = emptyList(), immutable: Boolean = false) { + + private val map: NavigableMap = + if (immutable) Collections.unmodifiableNavigableMap(TreeMap()) else TreeMap() + + init { + bits.forEach { set(it) } + } + + val isEmpty: Boolean get() = map.isEmpty() + + /** + * Sets bit and returns `true` if the bit was actually set, i.e. if bitmap was mutated. + * + * @param bit bit value + * @return `true` if the bit was actually set, i.e. if bitmap was mutated. + */ + fun set(bit: Long) = setBit(bit, true) + + /** + * Clears bit and returns `true if the bit was actually cleared, i.e. if bitmap was mutated. + * + * @param bit bit value + * @return `true` if the bit was actually cleared, i.e. if bitmap was mutated. + */ + fun clear(bit: Long) = setBit(bit, false) + + fun test(bit: Long): Boolean { + val bucket = bit shr 6 + val pos = bit.toInt() and 0x3f; + val mask = 1L shl pos + return (map.getOrDefault(bucket, 0L) and mask) != 0L + } + + fun contains(bit: Long) = test(bit) + + private fun setBit(bit: Long, bitValue: Boolean): Boolean { + val bucket = bit shr 6 + val pos = bit.toInt() and 0x3f + val mask = 1L shl pos + var bits = map.getOrDefault(bucket, 0L) + val mutated = bitValue == ((bits and mask) == 0L) + if (!mutated) { + return false + } + bits = bits xor mask + if (bits == 0L) { + map.remove(bucket) + } else { + map[bucket] = if (bits == -1L) ALL_BITS_SET else bits.interned + } + return true + } + + companion object { + + private const val ALL_BITS_SET = -1L + } +} diff --git a/jacodb-core/src/main/kotlin/org/jacodb/impl/storage/ers/ram/BlobsCache.kt b/jacodb-storage/src/main/kotlin/org/jacodb/impl/storage/ers/ram/TinyBlobsCache.kt similarity index 94% rename from jacodb-core/src/main/kotlin/org/jacodb/impl/storage/ers/ram/BlobsCache.kt rename to jacodb-storage/src/main/kotlin/org/jacodb/impl/storage/ers/ram/TinyBlobsCache.kt index f23b1e7ac..fa471ee4f 100644 --- a/jacodb-core/src/main/kotlin/org/jacodb/impl/storage/ers/ram/BlobsCache.kt +++ b/jacodb-storage/src/main/kotlin/org/jacodb/impl/storage/ers/ram/TinyBlobsCache.kt @@ -17,7 +17,7 @@ package org.jacodb.impl.storage.ers.ram -fun ByteArray.probablyCached(): ByteArray { +internal fun ByteArray.probablyCached(): ByteArray { return if (size == 1) { theCache[this[0].toInt() and 0xff] } else { diff --git a/jacodb-core/src/main/kotlin/org/jacodb/impl/storage/ers/ram/TransactionalPersistentLongMap.kt b/jacodb-storage/src/main/kotlin/org/jacodb/impl/storage/ers/ram/TransactionalPersistentLongMap.kt similarity index 82% rename from jacodb-core/src/main/kotlin/org/jacodb/impl/storage/ers/ram/TransactionalPersistentLongMap.kt rename to jacodb-storage/src/main/kotlin/org/jacodb/impl/storage/ers/ram/TransactionalPersistentLongMap.kt index 2678563c9..d28440fad 100644 --- a/jacodb-core/src/main/kotlin/org/jacodb/impl/storage/ers/ram/TransactionalPersistentLongMap.kt +++ b/jacodb-storage/src/main/kotlin/org/jacodb/impl/storage/ers/ram/TransactionalPersistentLongMap.kt @@ -18,13 +18,18 @@ package org.jacodb.impl.storage.ers.ram import jetbrains.exodus.core.dataStructures.persistent.PersistentLong23TreeMap import jetbrains.exodus.core.dataStructures.persistent.PersistentLongMap -import org.jacodb.api.jvm.storage.ers.ERSException +import org.jacodb.api.storage.ers.ERSException -class TransactionalPersistentLongMap(private val committed: PersistentLongMap = PersistentLong23TreeMap()) : - MutableContainer> { +class TransactionalPersistentLongMap( + private val committed: PersistentLongMap = PersistentLong23TreeMap() +) : MutableContainer> { private var mutated: PersistentLongMutableMap? = null + fun keys(): Iterable = (mutated ?: committed.beginRead()).map { it.key } + + fun entries(): Iterable> = (mutated ?: committed.beginRead()) + operator fun get(key: Long): V? { mutated?.let { return it[key] @@ -44,7 +49,7 @@ class TransactionalPersistentLongMap(private val committed: PersistentL // looks effective according to benchmarks mutated = null if (!it.endWrite()) { - throw ERSException("Failed to commit TransactionalPersistentLongMap.") + throw ERSException("Failed to commit TransactionalPersistentLongMap") } TransactionalPersistentLongMap(committed) } ?: this diff --git a/jacodb-core/src/main/kotlin/org/jacodb/impl/storage/ers/ram/TransactionalPersistentMap.kt b/jacodb-storage/src/main/kotlin/org/jacodb/impl/storage/ers/ram/TransactionalPersistentMap.kt similarity index 90% rename from jacodb-core/src/main/kotlin/org/jacodb/impl/storage/ers/ram/TransactionalPersistentMap.kt rename to jacodb-storage/src/main/kotlin/org/jacodb/impl/storage/ers/ram/TransactionalPersistentMap.kt index 0bc6e4cf5..4fc711fd0 100644 --- a/jacodb-core/src/main/kotlin/org/jacodb/impl/storage/ers/ram/TransactionalPersistentMap.kt +++ b/jacodb-storage/src/main/kotlin/org/jacodb/impl/storage/ers/ram/TransactionalPersistentMap.kt @@ -18,6 +18,7 @@ package org.jacodb.impl.storage.ers.ram import kotlinx.collections.immutable.PersistentMap import kotlinx.collections.immutable.toPersistentHashMap +import kotlin.collections.Map.Entry internal class TransactionalPersistentMap(private val committed: PersistentMap = mapOf().toPersistentHashMap()) { @@ -30,9 +31,9 @@ internal class TransactionalPersistentMap(private val committed: Persisten return committed[key] } - fun entries(): Iterable> { - return (mutated ?: committed).entries - } + fun keys(): Iterable = (mutated ?: committed).entries.map { it.key } + + fun entries(): Iterable> = (mutated ?: committed).entries fun put(key: K, value: V) { mutated()[key] = value diff --git a/jacodb-storage/src/main/kotlin/org/jacodb/impl/storage/ers/ram/XodusPersistentCollectionsEx.kt b/jacodb-storage/src/main/kotlin/org/jacodb/impl/storage/ers/ram/XodusPersistentCollectionsEx.kt new file mode 100644 index 000000000..399344b29 --- /dev/null +++ b/jacodb-storage/src/main/kotlin/org/jacodb/impl/storage/ers/ram/XodusPersistentCollectionsEx.kt @@ -0,0 +1,106 @@ +/* + * Copyright 2022 UnitTestBot contributors (utbot.org) + *

+ * 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 org.jacodb.impl.storage.ers.ram + +import jetbrains.exodus.core.dataStructures.persistent.Persistent23Tree +import jetbrains.exodus.core.dataStructures.persistent.Persistent23TreeMap +import jetbrains.exodus.core.dataStructures.persistent.PersistentHashMap +import jetbrains.exodus.core.dataStructures.persistent.PersistentHashSet +import jetbrains.exodus.core.dataStructures.persistent.PersistentHashSet.ImmutablePersistentHashSet +import jetbrains.exodus.core.dataStructures.persistent.PersistentLongMap +import jetbrains.exodus.core.dataStructures.persistent.PersistentLongSet + +typealias PersistentMutableHashSet = PersistentHashSet.MutablePersistentHashSet +typealias PersistentLongImmutableSet = PersistentLongSet.ImmutableSet +typealias PersistentLongMutableSet = PersistentLongSet.MutableSet +typealias PersistentLongImmutableMap = PersistentLongMap.ImmutableMap +typealias PersistentLongMutableMap = PersistentLongMap.MutableMap + +internal inline fun PersistentHashSet.write(writer: PersistentMutableHashSet.() -> Unit): PersistentHashSet = + clone.also { clone -> + clone.beginWrite().apply { + writer() + endWrite() + } + } + +internal inline fun > Persistent23Tree.write(writer: Persistent23Tree.MutableTree.() -> Unit): Persistent23Tree = + clone.also { clone -> + clone.beginWrite().apply { + writer() + endWrite() + } + } + +internal inline fun PersistentHashSet<*>.read(reader: ImmutablePersistentHashSet<*>.() -> T): T = beginRead().reader() + +internal inline val PersistentHashSet<*>.size: Int get() = beginRead().size() + +internal operator fun PersistentHashSet<*>.contains(value: Long): Boolean = + value in (beginRead() as ImmutablePersistentHashSet) + +internal operator fun PersistentHashMap.get(key: K): V? = current[key] + +internal inline fun PersistentHashMap.write(writer: PersistentHashMap.MutablePersistentHashMap.() -> T?): Pair> = + clone.run { + beginWrite().run { + val result = writer() + endWrite() + result + } to this + } + +internal operator fun , V> Persistent23TreeMap.get(key: K): V? = beginRead()[key] + +internal inline fun , V, T> Persistent23TreeMap.write(writer: Persistent23TreeMap.MutableMap.() -> T?): Pair> = + clone.run { + beginWrite().run { + val result = writer() + endWrite() + result + } to this + } + +internal inline fun PersistentLongSet.write(writer: PersistentLongMutableSet.() -> Unit): PersistentLongSet = clone.apply { + beginWrite().apply { + writer() + endWrite() + } +} + +internal fun PersistentLongSet.read(reader: PersistentLongImmutableSet.() -> T): T = beginRead().reader() + +internal inline val PersistentLongSet.size: Int get() = beginRead().size() + +internal fun PersistentLongSet.iterator(): Iterator = beginRead().longIterator() + +internal operator fun PersistentLongSet.contains(value: Long): Boolean = value in beginRead() + +internal inline fun PersistentLongMap.write(writer: PersistentLongMutableMap.() -> T?): Pair> = + clone.run { + beginWrite().run { + val result = writer() + endWrite() + result + } to this + } + +internal inline fun PersistentLongMap<*>.read(reader: PersistentLongImmutableMap<*>.() -> T): T = beginRead().reader() + +internal val PersistentLongMap<*>.size: Int get() = beginRead().size() + +internal operator fun PersistentLongMap.get(key: Long): V? = beginRead()[key] \ No newline at end of file diff --git a/jacodb-core/src/main/kotlin/org/jacodb/impl/storage/kv/lmdb/LmdbCursor.kt b/jacodb-storage/src/main/kotlin/org/jacodb/impl/storage/kv/lmdb/LmdbCursor.kt similarity index 92% rename from jacodb-core/src/main/kotlin/org/jacodb/impl/storage/kv/lmdb/LmdbCursor.kt rename to jacodb-storage/src/main/kotlin/org/jacodb/impl/storage/kv/lmdb/LmdbCursor.kt index 771c7b211..38e556246 100644 --- a/jacodb-core/src/main/kotlin/org/jacodb/impl/storage/kv/lmdb/LmdbCursor.kt +++ b/jacodb-storage/src/main/kotlin/org/jacodb/impl/storage/kv/lmdb/LmdbCursor.kt @@ -16,8 +16,8 @@ package org.jacodb.impl.storage.kv.lmdb -import org.jacodb.api.jvm.storage.kv.Cursor -import org.jacodb.api.jvm.storage.kv.Transaction +import org.jacodb.api.storage.kv.Cursor +import org.jacodb.api.storage.kv.Transaction import java.nio.ByteBuffer class LmdbCursor( diff --git a/jacodb-core/src/main/kotlin/org/jacodb/impl/storage/kv/lmdb/LmdbEx.kt b/jacodb-storage/src/main/kotlin/org/jacodb/impl/storage/kv/lmdb/LmdbEx.kt similarity index 100% rename from jacodb-core/src/main/kotlin/org/jacodb/impl/storage/kv/lmdb/LmdbEx.kt rename to jacodb-storage/src/main/kotlin/org/jacodb/impl/storage/kv/lmdb/LmdbEx.kt diff --git a/jacodb-core/src/main/kotlin/org/jacodb/impl/storage/kv/lmdb/LmdbKeyValueStorage.kt b/jacodb-storage/src/main/kotlin/org/jacodb/impl/storage/kv/lmdb/LmdbKeyValueStorage.kt similarity index 94% rename from jacodb-core/src/main/kotlin/org/jacodb/impl/storage/kv/lmdb/LmdbKeyValueStorage.kt rename to jacodb-storage/src/main/kotlin/org/jacodb/impl/storage/kv/lmdb/LmdbKeyValueStorage.kt index aac48ba24..2686ce85a 100644 --- a/jacodb-core/src/main/kotlin/org/jacodb/impl/storage/kv/lmdb/LmdbKeyValueStorage.kt +++ b/jacodb-storage/src/main/kotlin/org/jacodb/impl/storage/kv/lmdb/LmdbKeyValueStorage.kt @@ -16,9 +16,9 @@ package org.jacodb.impl.storage.kv.lmdb -import org.jacodb.api.jvm.storage.kv.PluggableKeyValueStorage -import org.jacodb.api.jvm.storage.kv.Transaction -import org.jacodb.api.jvm.storage.kv.withFinishedState +import org.jacodb.api.storage.kv.PluggableKeyValueStorage +import org.jacodb.api.storage.kv.Transaction +import org.jacodb.api.storage.kv.withFinishedState import org.jacodb.impl.JcLmdbErsSettings import org.lmdbjava.Dbi import org.lmdbjava.Dbi.KeyNotFoundException diff --git a/jacodb-core/src/main/kotlin/org/jacodb/impl/storage/kv/lmdb/LmdbKeyValueStorageSPI.kt b/jacodb-storage/src/main/kotlin/org/jacodb/impl/storage/kv/lmdb/LmdbKeyValueStorageSPI.kt similarity index 79% rename from jacodb-core/src/main/kotlin/org/jacodb/impl/storage/kv/lmdb/LmdbKeyValueStorageSPI.kt rename to jacodb-storage/src/main/kotlin/org/jacodb/impl/storage/kv/lmdb/LmdbKeyValueStorageSPI.kt index 1d24f9811..81e3b2506 100644 --- a/jacodb-core/src/main/kotlin/org/jacodb/impl/storage/kv/lmdb/LmdbKeyValueStorageSPI.kt +++ b/jacodb-storage/src/main/kotlin/org/jacodb/impl/storage/kv/lmdb/LmdbKeyValueStorageSPI.kt @@ -16,10 +16,9 @@ package org.jacodb.impl.storage.kv.lmdb -import org.jacodb.api.jvm.storage.ers.ErsSettings -import org.jacodb.api.jvm.storage.kv.PluggableKeyValueStorage -import org.jacodb.api.jvm.storage.kv.PluggableKeyValueStorageSPI -import org.jacodb.impl.JcLmdbErsSettings +import org.jacodb.api.storage.ers.ErsSettings +import org.jacodb.api.storage.kv.PluggableKeyValueStorage +import org.jacodb.api.storage.kv.PluggableKeyValueStorageSPI import kotlin.io.path.createTempDirectory const val LMDB_KEY_VALUE_STORAGE_SPI = "org.jacodb.impl.storage.kv.lmdb.LmdbKeyValueStorageSPI" @@ -31,7 +30,7 @@ class LmdbKeyValueStorageSPI : PluggableKeyValueStorageSPI { override fun newStorage(location: String?, settings: ErsSettings): PluggableKeyValueStorage { return LmdbKeyValueStorage( location ?: createTempDirectory(prefix = "lmdbKeyValueStorage").toString(), - if (settings is JcLmdbErsSettings) settings else JcLmdbErsSettings() + if (settings is org.jacodb.impl.JcLmdbErsSettings) settings else org.jacodb.impl.JcLmdbErsSettings() ) } } \ No newline at end of file diff --git a/jacodb-core/src/main/kotlin/org/jacodb/impl/storage/kv/lmdb/LmdbNamedMap.kt b/jacodb-storage/src/main/kotlin/org/jacodb/impl/storage/kv/lmdb/LmdbNamedMap.kt similarity index 87% rename from jacodb-core/src/main/kotlin/org/jacodb/impl/storage/kv/lmdb/LmdbNamedMap.kt rename to jacodb-storage/src/main/kotlin/org/jacodb/impl/storage/kv/lmdb/LmdbNamedMap.kt index fb08f3389..e7a14731d 100644 --- a/jacodb-core/src/main/kotlin/org/jacodb/impl/storage/kv/lmdb/LmdbNamedMap.kt +++ b/jacodb-storage/src/main/kotlin/org/jacodb/impl/storage/kv/lmdb/LmdbNamedMap.kt @@ -16,9 +16,9 @@ package org.jacodb.impl.storage.kv.lmdb -import org.jacodb.api.jvm.storage.kv.NamedMap -import org.jacodb.api.jvm.storage.kv.Transaction -import org.jacodb.api.jvm.storage.kv.TransactionDecorator +import org.jacodb.api.storage.kv.NamedMap +import org.jacodb.api.storage.kv.Transaction +import org.jacodb.api.storage.kv.TransactionDecorator import org.lmdbjava.Dbi import java.nio.ByteBuffer diff --git a/jacodb-core/src/main/kotlin/org/jacodb/impl/storage/kv/lmdb/LmdbTransaction.kt b/jacodb-storage/src/main/kotlin/org/jacodb/impl/storage/kv/lmdb/LmdbTransaction.kt similarity index 93% rename from jacodb-core/src/main/kotlin/org/jacodb/impl/storage/kv/lmdb/LmdbTransaction.kt rename to jacodb-storage/src/main/kotlin/org/jacodb/impl/storage/kv/lmdb/LmdbTransaction.kt index fdac8b21b..01554a3ef 100644 --- a/jacodb-core/src/main/kotlin/org/jacodb/impl/storage/kv/lmdb/LmdbTransaction.kt +++ b/jacodb-storage/src/main/kotlin/org/jacodb/impl/storage/kv/lmdb/LmdbTransaction.kt @@ -16,17 +16,18 @@ package org.jacodb.impl.storage.kv.lmdb -import org.jacodb.api.jvm.storage.kv.Cursor -import org.jacodb.api.jvm.storage.kv.NamedMap -import org.jacodb.api.jvm.storage.kv.Transaction -import org.jacodb.api.jvm.storage.kv.withFirstMoveSkipped +import org.jacodb.api.storage.kv.Cursor +import org.jacodb.api.storage.kv.NamedMap +import org.jacodb.api.storage.kv.Transaction +import org.jacodb.api.storage.kv.withFirstMoveSkipped import org.lmdbjava.GetOp import org.lmdbjava.SeekOp +import org.lmdbjava.Txn import java.nio.ByteBuffer internal class LmdbTransaction( override val storage: LmdbKeyValueStorage, - internal val lmdbTxn: org.lmdbjava.Txn + internal val lmdbTxn: Txn ) : Transaction { override val isReadonly: Boolean get() = lmdbTxn.isReadOnly diff --git a/jacodb-core/src/main/kotlin/org/jacodb/impl/storage/kv/rocks/ByteArrayPairUtils.kt b/jacodb-storage/src/main/kotlin/org/jacodb/impl/storage/kv/rocks/ByteArrayPairUtils.kt similarity index 100% rename from jacodb-core/src/main/kotlin/org/jacodb/impl/storage/kv/rocks/ByteArrayPairUtils.kt rename to jacodb-storage/src/main/kotlin/org/jacodb/impl/storage/kv/rocks/ByteArrayPairUtils.kt diff --git a/jacodb-core/src/main/kotlin/org/jacodb/impl/storage/kv/rocks/RocksCursor.kt b/jacodb-storage/src/main/kotlin/org/jacodb/impl/storage/kv/rocks/RocksCursor.kt similarity index 93% rename from jacodb-core/src/main/kotlin/org/jacodb/impl/storage/kv/rocks/RocksCursor.kt rename to jacodb-storage/src/main/kotlin/org/jacodb/impl/storage/kv/rocks/RocksCursor.kt index c19d04252..c277497bb 100644 --- a/jacodb-core/src/main/kotlin/org/jacodb/impl/storage/kv/rocks/RocksCursor.kt +++ b/jacodb-storage/src/main/kotlin/org/jacodb/impl/storage/kv/rocks/RocksCursor.kt @@ -16,10 +16,10 @@ package org.jacodb.impl.storage.kv.rocks -import org.jacodb.api.jvm.storage.kv.Cursor -import org.jacodb.api.jvm.storage.kv.EmptyCursor -import org.jacodb.api.jvm.storage.kv.withFirstMovePrevSkipped -import org.jacodb.api.jvm.storage.kv.withFirstMoveSkipped +import org.jacodb.api.storage.kv.Cursor +import org.jacodb.api.storage.kv.EmptyCursor +import org.jacodb.api.storage.kv.withFirstMovePrevSkipped +import org.jacodb.api.storage.kv.withFirstMoveSkipped import org.rocksdb.RocksIterator internal fun seekNoDuplicateKeyCursor(txn: RocksTransaction, iterator: RocksIterator, key: ByteArray): Cursor { diff --git a/jacodb-core/src/main/kotlin/org/jacodb/impl/storage/kv/rocks/RocksKeyValueStorage.kt b/jacodb-storage/src/main/kotlin/org/jacodb/impl/storage/kv/rocks/RocksKeyValueStorage.kt similarity index 98% rename from jacodb-core/src/main/kotlin/org/jacodb/impl/storage/kv/rocks/RocksKeyValueStorage.kt rename to jacodb-storage/src/main/kotlin/org/jacodb/impl/storage/kv/rocks/RocksKeyValueStorage.kt index 7723903e1..4b0d9d24e 100644 --- a/jacodb-core/src/main/kotlin/org/jacodb/impl/storage/kv/rocks/RocksKeyValueStorage.kt +++ b/jacodb-storage/src/main/kotlin/org/jacodb/impl/storage/kv/rocks/RocksKeyValueStorage.kt @@ -16,8 +16,8 @@ package org.jacodb.impl.storage.kv.rocks -import org.jacodb.api.jvm.storage.kv.PluggableKeyValueStorage -import org.jacodb.api.jvm.storage.kv.Transaction +import org.jacodb.api.storage.kv.PluggableKeyValueStorage +import org.jacodb.api.storage.kv.Transaction import org.jacodb.impl.storage.ers.BuiltInBindingProvider import org.rocksdb.ColumnFamilyDescriptor import org.rocksdb.ColumnFamilyHandle diff --git a/jacodb-core/src/main/kotlin/org/jacodb/impl/storage/kv/rocks/RocksKeyValueStorageSPI.kt b/jacodb-storage/src/main/kotlin/org/jacodb/impl/storage/kv/rocks/RocksKeyValueStorageSPI.kt similarity index 86% rename from jacodb-core/src/main/kotlin/org/jacodb/impl/storage/kv/rocks/RocksKeyValueStorageSPI.kt rename to jacodb-storage/src/main/kotlin/org/jacodb/impl/storage/kv/rocks/RocksKeyValueStorageSPI.kt index 02e5de897..77c0f4ad6 100644 --- a/jacodb-core/src/main/kotlin/org/jacodb/impl/storage/kv/rocks/RocksKeyValueStorageSPI.kt +++ b/jacodb-storage/src/main/kotlin/org/jacodb/impl/storage/kv/rocks/RocksKeyValueStorageSPI.kt @@ -16,9 +16,9 @@ package org.jacodb.impl.storage.kv.rocks -import org.jacodb.api.jvm.storage.ers.ErsSettings -import org.jacodb.api.jvm.storage.kv.PluggableKeyValueStorage -import org.jacodb.api.jvm.storage.kv.PluggableKeyValueStorageSPI +import org.jacodb.api.storage.ers.ErsSettings +import org.jacodb.api.storage.kv.PluggableKeyValueStorage +import org.jacodb.api.storage.kv.PluggableKeyValueStorageSPI import kotlin.io.path.createTempDirectory const val ROCKS_KEY_VALUE_STORAGE_SPI = "org.jacodb.impl.storage.kv.rocks.RocksKeyValueStorageSPI" diff --git a/jacodb-core/src/main/kotlin/org/jacodb/impl/storage/kv/rocks/RocksNamedMap.kt b/jacodb-storage/src/main/kotlin/org/jacodb/impl/storage/kv/rocks/RocksNamedMap.kt similarity index 96% rename from jacodb-core/src/main/kotlin/org/jacodb/impl/storage/kv/rocks/RocksNamedMap.kt rename to jacodb-storage/src/main/kotlin/org/jacodb/impl/storage/kv/rocks/RocksNamedMap.kt index 54147fba6..56582857c 100644 --- a/jacodb-core/src/main/kotlin/org/jacodb/impl/storage/kv/rocks/RocksNamedMap.kt +++ b/jacodb-storage/src/main/kotlin/org/jacodb/impl/storage/kv/rocks/RocksNamedMap.kt @@ -16,11 +16,11 @@ package org.jacodb.impl.storage.kv.rocks -import org.jacodb.api.jvm.storage.kv.Cursor -import org.jacodb.api.jvm.storage.kv.EmptyCursor -import org.jacodb.api.jvm.storage.kv.NamedMap -import org.jacodb.api.jvm.storage.kv.Transaction -import org.jacodb.api.jvm.storage.kv.TransactionDecorator +import org.jacodb.api.storage.kv.Cursor +import org.jacodb.api.storage.kv.EmptyCursor +import org.jacodb.api.storage.kv.NamedMap +import org.jacodb.api.storage.kv.Transaction +import org.jacodb.api.storage.kv.TransactionDecorator import org.jacodb.impl.storage.ers.BuiltInBindingProvider import org.rocksdb.ColumnFamilyHandle import org.rocksdb.RocksIterator diff --git a/jacodb-core/src/main/kotlin/org/jacodb/impl/storage/kv/rocks/RocksTransaction.kt b/jacodb-storage/src/main/kotlin/org/jacodb/impl/storage/kv/rocks/RocksTransaction.kt similarity index 95% rename from jacodb-core/src/main/kotlin/org/jacodb/impl/storage/kv/rocks/RocksTransaction.kt rename to jacodb-storage/src/main/kotlin/org/jacodb/impl/storage/kv/rocks/RocksTransaction.kt index a25beb0ba..b4e7c03e8 100644 --- a/jacodb-core/src/main/kotlin/org/jacodb/impl/storage/kv/rocks/RocksTransaction.kt +++ b/jacodb-storage/src/main/kotlin/org/jacodb/impl/storage/kv/rocks/RocksTransaction.kt @@ -16,10 +16,10 @@ package org.jacodb.impl.storage.kv.rocks -import org.jacodb.api.jvm.storage.kv.Cursor -import org.jacodb.api.jvm.storage.kv.NamedMap -import org.jacodb.api.jvm.storage.kv.Transaction -import org.jacodb.api.jvm.storage.kv.withFinishedState +import org.jacodb.api.storage.kv.Cursor +import org.jacodb.api.storage.kv.NamedMap +import org.jacodb.api.storage.kv.Transaction +import org.jacodb.api.storage.kv.withFinishedState import org.rocksdb.ColumnFamilyHandle import org.rocksdb.ReadOptions import org.rocksdb.RocksDBException diff --git a/jacodb-core/src/main/kotlin/org/jacodb/impl/storage/kv/xodus/XodusCursor.kt b/jacodb-storage/src/main/kotlin/org/jacodb/impl/storage/kv/xodus/XodusCursor.kt similarity index 94% rename from jacodb-core/src/main/kotlin/org/jacodb/impl/storage/kv/xodus/XodusCursor.kt rename to jacodb-storage/src/main/kotlin/org/jacodb/impl/storage/kv/xodus/XodusCursor.kt index 0a35f4391..7a05ba604 100644 --- a/jacodb-core/src/main/kotlin/org/jacodb/impl/storage/kv/xodus/XodusCursor.kt +++ b/jacodb-storage/src/main/kotlin/org/jacodb/impl/storage/kv/xodus/XodusCursor.kt @@ -16,8 +16,8 @@ package org.jacodb.impl.storage.kv.xodus -import org.jacodb.api.jvm.storage.kv.Cursor -import org.jacodb.api.jvm.storage.kv.Transaction +import org.jacodb.api.storage.kv.Cursor +import org.jacodb.api.storage.kv.Transaction internal class XodusCursor( override val txn: Transaction, diff --git a/jacodb-core/src/main/kotlin/org/jacodb/impl/storage/kv/xodus/XodusEnvironmentsEx.kt b/jacodb-storage/src/main/kotlin/org/jacodb/impl/storage/kv/xodus/XodusEnvironmentsEx.kt similarity index 100% rename from jacodb-core/src/main/kotlin/org/jacodb/impl/storage/kv/xodus/XodusEnvironmentsEx.kt rename to jacodb-storage/src/main/kotlin/org/jacodb/impl/storage/kv/xodus/XodusEnvironmentsEx.kt diff --git a/jacodb-core/src/main/kotlin/org/jacodb/impl/storage/kv/xodus/XodusKeyValueStorage.kt b/jacodb-storage/src/main/kotlin/org/jacodb/impl/storage/kv/xodus/XodusKeyValueStorage.kt similarity index 90% rename from jacodb-core/src/main/kotlin/org/jacodb/impl/storage/kv/xodus/XodusKeyValueStorage.kt rename to jacodb-storage/src/main/kotlin/org/jacodb/impl/storage/kv/xodus/XodusKeyValueStorage.kt index dfef8c755..ecb5e1d89 100644 --- a/jacodb-core/src/main/kotlin/org/jacodb/impl/storage/kv/xodus/XodusKeyValueStorage.kt +++ b/jacodb-storage/src/main/kotlin/org/jacodb/impl/storage/kv/xodus/XodusKeyValueStorage.kt @@ -22,8 +22,8 @@ import jetbrains.exodus.env.Store import jetbrains.exodus.env.StoreConfig.WITHOUT_DUPLICATES_WITH_PREFIXING import jetbrains.exodus.env.StoreConfig.WITH_DUPLICATES_WITH_PREFIXING import jetbrains.exodus.env.TransactionBase -import org.jacodb.api.jvm.storage.kv.PluggableKeyValueStorage -import org.jacodb.api.jvm.storage.kv.Transaction +import org.jacodb.api.storage.kv.PluggableKeyValueStorage +import org.jacodb.api.storage.kv.Transaction internal class XodusKeyValueStorage(location: String) : PluggableKeyValueStorage() { @@ -36,6 +36,12 @@ internal class XodusKeyValueStorage(location: String) : PluggableKeyValueStorage } ) + override var readonly: Boolean + get() = env.environmentConfig.envIsReadonly + set(value) { + env.environmentConfig.envIsReadonly = value + } + override fun beginTransaction(): Transaction = XodusTransaction(this, env.beginTransaction().withNoStoreGetCache()) diff --git a/jacodb-core/src/main/kotlin/org/jacodb/impl/storage/kv/xodus/XodusKeyValueStorageSPI.kt b/jacodb-storage/src/main/kotlin/org/jacodb/impl/storage/kv/xodus/XodusKeyValueStorageSPI.kt similarity index 86% rename from jacodb-core/src/main/kotlin/org/jacodb/impl/storage/kv/xodus/XodusKeyValueStorageSPI.kt rename to jacodb-storage/src/main/kotlin/org/jacodb/impl/storage/kv/xodus/XodusKeyValueStorageSPI.kt index 6fbc7e3fb..8a9f82491 100644 --- a/jacodb-core/src/main/kotlin/org/jacodb/impl/storage/kv/xodus/XodusKeyValueStorageSPI.kt +++ b/jacodb-storage/src/main/kotlin/org/jacodb/impl/storage/kv/xodus/XodusKeyValueStorageSPI.kt @@ -16,9 +16,9 @@ package org.jacodb.impl.storage.kv.xodus -import org.jacodb.api.jvm.storage.ers.ErsSettings -import org.jacodb.api.jvm.storage.kv.PluggableKeyValueStorage -import org.jacodb.api.jvm.storage.kv.PluggableKeyValueStorageSPI +import org.jacodb.api.storage.ers.ErsSettings +import org.jacodb.api.storage.kv.PluggableKeyValueStorage +import org.jacodb.api.storage.kv.PluggableKeyValueStorageSPI import kotlin.io.path.createTempDirectory const val XODUS_KEY_VALUE_STORAGE_SPI = "org.jacodb.impl.storage.kv.xodus.XodusKeyValueStorageSPI" diff --git a/jacodb-core/src/main/kotlin/org/jacodb/impl/storage/kv/xodus/XodusNamedMap.kt b/jacodb-storage/src/main/kotlin/org/jacodb/impl/storage/kv/xodus/XodusNamedMap.kt similarity index 90% rename from jacodb-core/src/main/kotlin/org/jacodb/impl/storage/kv/xodus/XodusNamedMap.kt rename to jacodb-storage/src/main/kotlin/org/jacodb/impl/storage/kv/xodus/XodusNamedMap.kt index e4c89a0be..ded6210d3 100644 --- a/jacodb-core/src/main/kotlin/org/jacodb/impl/storage/kv/xodus/XodusNamedMap.kt +++ b/jacodb-storage/src/main/kotlin/org/jacodb/impl/storage/kv/xodus/XodusNamedMap.kt @@ -17,8 +17,8 @@ package org.jacodb.impl.storage.kv.xodus import jetbrains.exodus.env.Store -import org.jacodb.api.jvm.storage.kv.NamedMap -import org.jacodb.api.jvm.storage.kv.Transaction +import org.jacodb.api.storage.kv.NamedMap +import org.jacodb.api.storage.kv.Transaction internal class XodusNamedMap(val store: Store) : NamedMap { diff --git a/jacodb-core/src/main/kotlin/org/jacodb/impl/storage/kv/xodus/XodusTransaction.kt b/jacodb-storage/src/main/kotlin/org/jacodb/impl/storage/kv/xodus/XodusTransaction.kt similarity index 91% rename from jacodb-core/src/main/kotlin/org/jacodb/impl/storage/kv/xodus/XodusTransaction.kt rename to jacodb-storage/src/main/kotlin/org/jacodb/impl/storage/kv/xodus/XodusTransaction.kt index ecff5002e..f685a8917 100644 --- a/jacodb-core/src/main/kotlin/org/jacodb/impl/storage/kv/xodus/XodusTransaction.kt +++ b/jacodb-storage/src/main/kotlin/org/jacodb/impl/storage/kv/xodus/XodusTransaction.kt @@ -16,11 +16,11 @@ package org.jacodb.impl.storage.kv.xodus -import org.jacodb.api.jvm.storage.kv.Cursor -import org.jacodb.api.jvm.storage.kv.NamedMap -import org.jacodb.api.jvm.storage.kv.Transaction -import org.jacodb.api.jvm.storage.kv.withFirstMovePrevSkipped -import org.jacodb.api.jvm.storage.kv.withFirstMoveSkipped +import org.jacodb.api.storage.kv.Cursor +import org.jacodb.api.storage.kv.NamedMap +import org.jacodb.api.storage.kv.Transaction +import org.jacodb.api.storage.kv.withFirstMovePrevSkipped +import org.jacodb.api.storage.kv.withFirstMoveSkipped internal class XodusTransaction( override val storage: XodusKeyValueStorage, diff --git a/jacodb-core/src/main/resources/META-INF/services/org.jacodb.api.jvm.storage.ers.EntityRelationshipStorageSPI b/jacodb-storage/src/main/resources/META-INF/services/org.jacodb.api.storage.ers.EntityRelationshipStorageSPI similarity index 66% rename from jacodb-core/src/main/resources/META-INF/services/org.jacodb.api.jvm.storage.ers.EntityRelationshipStorageSPI rename to jacodb-storage/src/main/resources/META-INF/services/org.jacodb.api.storage.ers.EntityRelationshipStorageSPI index 0fa91bd9c..26973df0e 100644 --- a/jacodb-core/src/main/resources/META-INF/services/org.jacodb.api.jvm.storage.ers.EntityRelationshipStorageSPI +++ b/jacodb-storage/src/main/resources/META-INF/services/org.jacodb.api.storage.ers.EntityRelationshipStorageSPI @@ -1,3 +1,2 @@ org.jacodb.impl.storage.ers.ram.RAMEntityRelationshipStorageSPI -org.jacodb.impl.storage.ers.sql.SqlEntityRelationshipStorageSPI org.jacodb.impl.storage.ers.kv.KVEntityRelationshipStorageSPI \ No newline at end of file diff --git a/jacodb-core/src/main/resources/META-INF/services/org.jacodb.api.jvm.storage.kv.PluggableKeyValueStorageSPI b/jacodb-storage/src/main/resources/META-INF/services/org.jacodb.api.storage.kv.PluggableKeyValueStorageSPI similarity index 100% rename from jacodb-core/src/main/resources/META-INF/services/org.jacodb.api.jvm.storage.kv.PluggableKeyValueStorageSPI rename to jacodb-storage/src/main/resources/META-INF/services/org.jacodb.api.storage.kv.PluggableKeyValueStorageSPI diff --git a/jacodb-storage/src/test/kotlin/org/jacodb/impl/storage/ers/ram/SparseBitSetTest.kt b/jacodb-storage/src/test/kotlin/org/jacodb/impl/storage/ers/ram/SparseBitSetTest.kt new file mode 100644 index 000000000..f5f56bc97 --- /dev/null +++ b/jacodb-storage/src/test/kotlin/org/jacodb/impl/storage/ers/ram/SparseBitSetTest.kt @@ -0,0 +1,106 @@ +/* + * Copyright 2022 UnitTestBot contributors (utbot.org) + *

+ * 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 org.jacodb.impl.storage.ers.ram + +import org.junit.jupiter.api.Assertions.assertFalse +import org.junit.jupiter.api.Assertions.assertTrue +import org.junit.jupiter.api.BeforeEach +import org.junit.jupiter.api.Test + +class SparseBitSetTest { + + private lateinit var sparseBitSet: SparseBitSet + + @BeforeEach + fun setUp() { + sparseBitSet = SparseBitSet() + } + + /** + * - Given an empty SparseBitSet instance + * - When a bit is set to a specific value + * - Then the SparseBitSet should indicate that the bit is contained within it + * - And the SparseBitSet should not be empty + */ + @Test + fun `Verify that a bit can be set in the SparseBitSet`() { + val bitToSet = 5L + val wasSet = sparseBitSet.set(bitToSet) + assertTrue(wasSet) + assertTrue(sparseBitSet.contains(bitToSet)) + assertFalse(sparseBitSet.isEmpty) + } + + /** + * - Given a SparseBitSet with a bit set to a specific value + * - When the same bit is set again + * - Then the SparseBitSet should indicate that the bit is still contained within it + * - And the SparseBitSet should remain unchanged + */ + @Test + fun `Verify that setting the same bit twice does not mutate the SparseBitSet`() { + val bitToSet = 5L + sparseBitSet.set(bitToSet) + val wasSetAgain = sparseBitSet.set(bitToSet) + assertFalse(wasSetAgain) + assertTrue(sparseBitSet.contains(bitToSet)) + } + + /** + * - Given a SparseBitSet with a bit set to a specific value + * - When the bit is cleared + * - Then the SparseBitSet should indicate that the bit is no longer contained within it + * - And the SparseBitSet should reflect that it is not empty if other bits are present + */ + @Test + fun `Verify that a bit can be cleared from the SparseBitSet`() { + val bitToSet = 5L + sparseBitSet.set(bitToSet) + val wasCleared = sparseBitSet.clear(bitToSet) + assertTrue(wasCleared) + assertFalse(sparseBitSet.contains(bitToSet)) + } + + /** + * - Given a SparseBitSet with a specific bit set + * - When a different bit is cleared + * - Then the SparseBitSet should indicate that the original bit is still contained within it + * - And the SparseBitSet should remain unchanged + */ + @Test + fun `Verify that clearing a bit that is not set does not mutate the SparseBitSet`() { + val bitToSet = 5L + sparseBitSet.set(bitToSet) + val wasCleared = sparseBitSet.clear(10L) + assertFalse(wasCleared) + assertTrue(sparseBitSet.contains(bitToSet)) + } + + /** + * - Given a SparseBitSet with multiple bits set + * - When a test is performed on a specific bit that is set + * - Then the result should indicate that the bit is contained within the SparseBitSet + * - And when a test is performed on a bit that is not set + * - Then the result should indicate that the bit is not contained within the SparseBitSet + */ + @Test + fun `Verify that testing a bit returns the correct result`() { + sparseBitSet.set(5L) + assertTrue(sparseBitSet.test(5L)) + assertFalse(sparseBitSet.test(10L)) + } +} \ No newline at end of file diff --git a/jacodb-core/src/test/kotlin/org/jacodb/testing/storage/ers/BindingsTest.kt b/jacodb-storage/src/test/kotlin/org/jacodb/testing/storage/ers/BindingsTest.kt similarity index 100% rename from jacodb-core/src/test/kotlin/org/jacodb/testing/storage/ers/BindingsTest.kt rename to jacodb-storage/src/test/kotlin/org/jacodb/testing/storage/ers/BindingsTest.kt diff --git a/jacodb-core/src/test/kotlin/org/jacodb/testing/storage/ers/CompactPersistentLongSetTest.kt b/jacodb-storage/src/test/kotlin/org/jacodb/testing/storage/ers/CompactPersistentLongSetTest.kt similarity index 100% rename from jacodb-core/src/test/kotlin/org/jacodb/testing/storage/ers/CompactPersistentLongSetTest.kt rename to jacodb-storage/src/test/kotlin/org/jacodb/testing/storage/ers/CompactPersistentLongSetTest.kt diff --git a/jacodb-core/src/test/kotlin/org/jacodb/testing/storage/ers/LmdbKVEntityRelationshipStorageTest.kt b/jacodb-storage/src/test/kotlin/org/jacodb/testing/storage/ers/LmdbKVEntityRelationshipStorageTest.kt similarity index 100% rename from jacodb-core/src/test/kotlin/org/jacodb/testing/storage/ers/LmdbKVEntityRelationshipStorageTest.kt rename to jacodb-storage/src/test/kotlin/org/jacodb/testing/storage/ers/LmdbKVEntityRelationshipStorageTest.kt diff --git a/jacodb-storage/src/test/kotlin/org/jacodb/testing/storage/ers/RAMEntityRelationshipStorageImmutableTest.kt b/jacodb-storage/src/test/kotlin/org/jacodb/testing/storage/ers/RAMEntityRelationshipStorageImmutableTest.kt new file mode 100644 index 000000000..76cb7a28d --- /dev/null +++ b/jacodb-storage/src/test/kotlin/org/jacodb/testing/storage/ers/RAMEntityRelationshipStorageImmutableTest.kt @@ -0,0 +1,150 @@ +/* + * Copyright 2022 UnitTestBot contributors (utbot.org) + *

+ * 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 org.jacodb.testing.storage.ers + +import org.jacodb.api.storage.ers.EmptyErsSettings +import org.jacodb.api.storage.ers.EntityRelationshipStorage +import org.jacodb.api.storage.ers.EntityRelationshipStorageSPI +import org.jacodb.api.storage.ers.compressed +import org.jacodb.api.storage.ers.nonSearchable +import org.jacodb.impl.storage.ers.ram.RAM_ERS_SPI +import org.junit.jupiter.api.Assertions.assertEquals +import org.junit.jupiter.api.BeforeEach +import org.junit.jupiter.api.Test + +class RAMEntityRelationshipStorageImmutableTest { + + private val ersSpi by lazy(LazyThreadSafetyMode.NONE) { + EntityRelationshipStorageSPI.getProvider(RAM_ERS_SPI) + } + + private lateinit var readonlyStorage: EntityRelationshipStorage + + @Test + fun `get all users`() { + readonlyStorage.transactional { txn -> + assertEquals(100L, txn.all("User").size) + txn.all("User").forEachIndexed { i, user -> + assertEquals("login$i", user.get("login")) + assertEquals("password$i", user.get("password")) + assertEquals("!@#$%^&$i", user.getBlob("avatar")) + } + } + } + + @Test + fun `get all users in a group`() { + readonlyStorage.transactional { txn -> + val groupUsers = txn.all("UserGroup").first().getLinks("user").toList() + assertEquals(100, groupUsers.size) + assertEquals(100L, txn.all("User").size) + txn.all("User").forEachIndexed { i, user -> + assertEquals(groupUsers[i], user) + } + } + } + + @Test + fun `find users by property`() { + readonlyStorage.transactional { txn -> + repeat(100) { i -> + (txn.find("User", "login", "login$i") * txn.find("User", "password", "password$i")).first() + .let { user -> + assertEquals("login$i", user.get("login")) + assertEquals("password$i", user.get("password")) + assertEquals("!@#$%^&$i", user.getBlob("avatar")) + } + } + } + } + + @Test + fun `find users by property in range`() { + readonlyStorage.transactional { txn -> + assertEquals(100L, txn.findEqOrGt("User", "age", 20.compressed).size) + assertEquals(90L, txn.findGt("User", "age", 20.compressed).size) + assertEquals(40L, txn.findGt("User", "age", 25.compressed).size) + assertEquals(0L, txn.findEqOrGt("User", "age", 30.compressed).size) + assertEquals(0L, txn.findGt("User", "age", 29.compressed).size) + assertEquals(0L, txn.findLt("User", "age", 20.compressed).size) + assertEquals(10L, txn.findEqOrLt("User", "age", 20.compressed).size) + assertEquals(20L, txn.findEqOrLt("User", "age", 21.compressed).size) + assertEquals(10L, txn.findLt("User", "age", 21.compressed).size) + assertEquals(20L, txn.findLt("User", "age", 22.compressed).size) + + assertEquals(100L, txn.findEqOrGt("User", "height", 100.compressed).size) + assertEquals(100L, txn.findEqOrLt("User", "height", 180.compressed).size) + assertEquals(90L, txn.findLt("User", "height", 180.compressed).size) + + repeat(100) { i -> + assertEquals(100L - i, txn.findEqOrGt("User", "dozen", i * 12).size) + assertEquals(99L - i, txn.findGt("User", "dozen", i * 12).size) + assertEquals(99L - i, txn.findEqOrGt("User", "dozen", i * 12 + 1).size) + assertEquals(99L - i, txn.findGt("User", "dozen", i * 12 + 1).size) + assertEquals(i.toLong(), txn.findLt("User", "dozen", i * 12).size) + assertEquals(i.toLong() + 1, txn.findEqOrLt("User", "dozen", i * 12).size) + assertEquals(i.toLong() + 1, txn.findLt("User", "dozen", i * 12 + 1).size) + assertEquals(i.toLong() + 1, txn.findEqOrLt("User", "dozen", i * 12 + 1).size) + } + } + } + + @Test + fun `entity metadata`() { + readonlyStorage.transactional { txn -> + txn.getPropertyNames("User").toList().let { props -> + assertEquals(5, props.size) + assertEquals("age", props[0]) + assertEquals("dozen", props[1]) + assertEquals("height", props[2]) + assertEquals("login", props[3]) + assertEquals("password", props[4]) + } + txn.getBlobNames("User").toList().let { blobs -> + assertEquals(1, blobs.size) + assertEquals("avatar", blobs[0]) + } + txn.getLinkNames("UserGroup").toList().let { links -> + assertEquals(1, links.size) + assertEquals("user", links[0]) + } + } + } + + @BeforeEach + fun setUp() { + readonlyStorage = ersSpi.newStorage(persistenceLocation = null, settings = EmptyErsSettings).let { rwStorage -> + // populate some data + rwStorage.transactional { txn -> + repeat(100) { i -> + val user = txn.newEntity("User") + user["login"] = "login$i" + user["password"] = "password$i" + user["avatar"] = "!@#$%^&$i".nonSearchable + user["age"] = (20 + (i / 10)).compressed + user["height"] = (180 - (i / 10)).compressed + user["dozen"] = i * 12 + } + val userGroup = txn.newEntity("UserGroup") + txn.all("User").forEach { user -> + userGroup.addLink("user", user) + } + } + rwStorage.asReadonly + } + } +} \ No newline at end of file diff --git a/jacodb-core/src/test/kotlin/org/jacodb/testing/storage/ers/RAMEntityRelationshipStorageTest.kt b/jacodb-storage/src/test/kotlin/org/jacodb/testing/storage/ers/RAMEntityRelationshipStorageTest.kt similarity index 100% rename from jacodb-core/src/test/kotlin/org/jacodb/testing/storage/ers/RAMEntityRelationshipStorageTest.kt rename to jacodb-storage/src/test/kotlin/org/jacodb/testing/storage/ers/RAMEntityRelationshipStorageTest.kt diff --git a/jacodb-core/src/test/kotlin/org/jacodb/testing/storage/ers/RocksKVEntityRelationshipStorageTest.kt b/jacodb-storage/src/test/kotlin/org/jacodb/testing/storage/ers/RocksKVEntityRelationshipStorageTest.kt similarity index 100% rename from jacodb-core/src/test/kotlin/org/jacodb/testing/storage/ers/RocksKVEntityRelationshipStorageTest.kt rename to jacodb-storage/src/test/kotlin/org/jacodb/testing/storage/ers/RocksKVEntityRelationshipStorageTest.kt diff --git a/jacodb-core/src/test/kotlin/org/jacodb/testing/storage/ers/XodusKVEntityRelationshipStorageTest.kt b/jacodb-storage/src/test/kotlin/org/jacodb/testing/storage/ers/XodusKVEntityRelationshipStorageTest.kt similarity index 69% rename from jacodb-core/src/test/kotlin/org/jacodb/testing/storage/ers/XodusKVEntityRelationshipStorageTest.kt rename to jacodb-storage/src/test/kotlin/org/jacodb/testing/storage/ers/XodusKVEntityRelationshipStorageTest.kt index 883a9b8db..c0a3f0236 100644 --- a/jacodb-core/src/test/kotlin/org/jacodb/testing/storage/ers/XodusKVEntityRelationshipStorageTest.kt +++ b/jacodb-storage/src/test/kotlin/org/jacodb/testing/storage/ers/XodusKVEntityRelationshipStorageTest.kt @@ -16,13 +16,27 @@ package org.jacodb.testing.storage.ers +import jetbrains.exodus.env.ReadonlyTransactionException import org.jacodb.impl.JcKvErsSettings import org.jacodb.impl.storage.ers.kv.KV_ERS_SPI import org.jacodb.impl.storage.kv.xodus.XODUS_KEY_VALUE_STORAGE_SPI +import org.junit.jupiter.api.Assertions.assertThrows +import org.junit.jupiter.api.Test class XodusKVEntityRelationshipStorageTest : EntityRelationshipStorageTest() { override val ersSettings = JcKvErsSettings(XODUS_KEY_VALUE_STORAGE_SPI) override val ersId = KV_ERS_SPI + + @Test + fun `read-only storage throws ReadonlyTransactionException`() { + val storage = txn.ers + txn.close() + assertThrows(ReadonlyTransactionException::class.java) { + storage.asReadonly.transactional(false) { + it.newEntity("User") + } + } + } } \ No newline at end of file diff --git a/jacodb-core/src/test/kotlin/org/jacodb/testing/storage/kv/LmdbKeyValueStorageTest.kt b/jacodb-storage/src/test/kotlin/org/jacodb/testing/storage/kv/LmdbKeyValueStorageTest.kt similarity index 100% rename from jacodb-core/src/test/kotlin/org/jacodb/testing/storage/kv/LmdbKeyValueStorageTest.kt rename to jacodb-storage/src/test/kotlin/org/jacodb/testing/storage/kv/LmdbKeyValueStorageTest.kt diff --git a/jacodb-core/src/test/kotlin/org/jacodb/testing/storage/kv/RocksKeyValueStorageTest.kt b/jacodb-storage/src/test/kotlin/org/jacodb/testing/storage/kv/RocksKeyValueStorageTest.kt similarity index 100% rename from jacodb-core/src/test/kotlin/org/jacodb/testing/storage/kv/RocksKeyValueStorageTest.kt rename to jacodb-storage/src/test/kotlin/org/jacodb/testing/storage/kv/RocksKeyValueStorageTest.kt diff --git a/jacodb-core/src/test/kotlin/org/jacodb/testing/storage/kv/XodusKeyValueStorageTest.kt b/jacodb-storage/src/test/kotlin/org/jacodb/testing/storage/kv/XodusKeyValueStorageTest.kt similarity index 59% rename from jacodb-core/src/test/kotlin/org/jacodb/testing/storage/kv/XodusKeyValueStorageTest.kt rename to jacodb-storage/src/test/kotlin/org/jacodb/testing/storage/kv/XodusKeyValueStorageTest.kt index e79b78ec3..c47b26fb8 100644 --- a/jacodb-core/src/test/kotlin/org/jacodb/testing/storage/kv/XodusKeyValueStorageTest.kt +++ b/jacodb-storage/src/test/kotlin/org/jacodb/testing/storage/kv/XodusKeyValueStorageTest.kt @@ -16,9 +16,26 @@ package org.jacodb.testing.storage.kv +import jetbrains.exodus.env.ReadonlyTransactionException import org.jacodb.impl.storage.kv.xodus.XODUS_KEY_VALUE_STORAGE_SPI +import org.junit.jupiter.api.Assertions.assertThrows +import org.junit.jupiter.api.Test class XodusKeyValueStorageTest : PluggableKeyValueStorageTest() { override val kvStorageId = XODUS_KEY_VALUE_STORAGE_SPI + + @Test + fun `read-only storage throws ReadonlyTransactionException`() { + storage.readonly = true + assertThrows(ReadonlyTransactionException::class.java) { + storage.transactional { + it.put("a map", "key".asByteArray, "value".asByteArray) + } + } + storage.readonly = false + storage.transactional { + it.put("a map", "key".asByteArray, "value".asByteArray) + } + } } \ No newline at end of file diff --git a/jacodb-core/src/testFixtures/kotlin/org/jacodb/testing/storage/ers/EntityRelationshipStorageTest.kt b/jacodb-storage/src/testFixtures/kotlin/org/jacodb/testing/storage/ers/EntityRelationshipStorageTest.kt similarity index 94% rename from jacodb-core/src/testFixtures/kotlin/org/jacodb/testing/storage/ers/EntityRelationshipStorageTest.kt rename to jacodb-storage/src/testFixtures/kotlin/org/jacodb/testing/storage/ers/EntityRelationshipStorageTest.kt index c3f41e76c..7353c162b 100644 --- a/jacodb-core/src/testFixtures/kotlin/org/jacodb/testing/storage/ers/EntityRelationshipStorageTest.kt +++ b/jacodb-storage/src/testFixtures/kotlin/org/jacodb/testing/storage/ers/EntityRelationshipStorageTest.kt @@ -16,20 +16,20 @@ package org.jacodb.testing.storage.ers -import org.jacodb.api.jvm.storage.ers.ERSConflictingTransactionException -import org.jacodb.api.jvm.storage.ers.ERSNonExistingEntityException -import org.jacodb.api.jvm.storage.ers.ERSTransactionFinishedException -import org.jacodb.api.jvm.storage.ers.EmptyErsSettings -import org.jacodb.api.jvm.storage.ers.EntityIterable -import org.jacodb.api.jvm.storage.ers.EntityRelationshipStorageSPI -import org.jacodb.api.jvm.storage.ers.ErsSettings -import org.jacodb.api.jvm.storage.ers.FindOption -import org.jacodb.api.jvm.storage.ers.Transaction -import org.jacodb.api.jvm.storage.ers.blobOf -import org.jacodb.api.jvm.storage.ers.compressed -import org.jacodb.api.jvm.storage.ers.links -import org.jacodb.api.jvm.storage.ers.nonSearchable -import org.jacodb.api.jvm.storage.ers.propertyOf +import org.jacodb.api.storage.ers.ERSConflictingTransactionException +import org.jacodb.api.storage.ers.ERSNonExistingEntityException +import org.jacodb.api.storage.ers.ERSTransactionFinishedException +import org.jacodb.api.storage.ers.EmptyErsSettings +import org.jacodb.api.storage.ers.EntityIterable +import org.jacodb.api.storage.ers.EntityRelationshipStorageSPI +import org.jacodb.api.storage.ers.ErsSettings +import org.jacodb.api.storage.ers.FindOption +import org.jacodb.api.storage.ers.Transaction +import org.jacodb.api.storage.ers.blobOf +import org.jacodb.api.storage.ers.compressed +import org.jacodb.api.storage.ers.links +import org.jacodb.api.storage.ers.nonSearchable +import org.jacodb.api.storage.ers.propertyOf import org.jacodb.api.jvm.storage.ers.typed.ErsSearchability import org.jacodb.api.jvm.storage.ers.typed.ErsType import org.jacodb.api.jvm.storage.ers.typed.all @@ -40,7 +40,6 @@ import org.jacodb.api.jvm.storage.ers.typed.newEntity import org.jacodb.api.jvm.storage.ers.typed.property import org.jacodb.impl.storage.ers.kv.KV_ERS_SPI import org.jacodb.impl.storage.ers.ram.RAM_ERS_SPI -import org.jacodb.impl.storage.ers.sql.SQL_ERS_SPI import org.junit.jupiter.api.AfterEach import org.junit.jupiter.api.Assertions.assertArrayEquals import org.junit.jupiter.api.Assertions.assertEquals @@ -58,13 +57,12 @@ import org.junit.jupiter.api.assertThrows abstract class EntityRelationshipStorageTest { open val ersSettings: ErsSettings get() = EmptyErsSettings - open val persistenceLocation: String? get() = null abstract val ersId: String private val ersSpi by lazy(LazyThreadSafetyMode.NONE) { EntityRelationshipStorageSPI.getProvider(ersId) } - private lateinit var txn: Transaction + protected lateinit var txn: Transaction @Test fun createEntityLoadEntity() { @@ -90,7 +88,7 @@ abstract class EntityRelationshipStorageTest { assertNull(noSuchProperty) // TODO: resolve this - if (ersSpi.id != SQL_ERS_SPI) { + if (ersSpi.id == KV_ERS_SPI || ersSpi.id == RAM_ERS_SPI) { txn.getPropertyNames("User").toList().let { props -> assertEquals(2, props.size) assertEquals("login", props[0]) @@ -122,7 +120,7 @@ abstract class EntityRelationshipStorageTest { } // TODO: resolve this - if (ersSpi.id != SQL_ERS_SPI) { + if (ersSpi.id == KV_ERS_SPI || ersSpi.id == RAM_ERS_SPI) { txn.getPropertyNames("User").toList().let { props -> assertEquals(2, props.size) assertEquals("login", props[0]) @@ -596,7 +594,7 @@ abstract class EntityRelationshipStorageTest { @BeforeEach fun setUp() { txn = ersSpi.newStorage( - persistenceLocation = persistenceLocation, + persistenceLocation = null, settings = ersSettings ).beginTransaction() } diff --git a/jacodb-core/src/testFixtures/kotlin/org/jacodb/testing/storage/kv/PluggableKeyValueStorageTest.kt b/jacodb-storage/src/testFixtures/kotlin/org/jacodb/testing/storage/kv/PluggableKeyValueStorageTest.kt similarity index 92% rename from jacodb-core/src/testFixtures/kotlin/org/jacodb/testing/storage/kv/PluggableKeyValueStorageTest.kt rename to jacodb-storage/src/testFixtures/kotlin/org/jacodb/testing/storage/kv/PluggableKeyValueStorageTest.kt index cbd379007..cbca8c97b 100644 --- a/jacodb-core/src/testFixtures/kotlin/org/jacodb/testing/storage/kv/PluggableKeyValueStorageTest.kt +++ b/jacodb-storage/src/testFixtures/kotlin/org/jacodb/testing/storage/kv/PluggableKeyValueStorageTest.kt @@ -16,9 +16,9 @@ package org.jacodb.testing.storage.kv -import org.jacodb.api.jvm.storage.kv.PluggableKeyValueStorage -import org.jacodb.api.jvm.storage.kv.PluggableKeyValueStorageSPI -import org.jacodb.api.jvm.storage.kv.asIterable +import org.jacodb.api.storage.kv.PluggableKeyValueStorage +import org.jacodb.api.storage.kv.PluggableKeyValueStorageSPI +import org.jacodb.api.storage.kv.asIterable import org.jacodb.impl.storage.ers.BuiltInBindingProvider import org.junit.jupiter.api.AfterEach import org.junit.jupiter.api.Assertions.assertEquals @@ -36,7 +36,7 @@ abstract class PluggableKeyValueStorageTest { private val kvStorageSpi by lazy(LazyThreadSafetyMode.NONE) { PluggableKeyValueStorageSPI.getProvider(kvStorageId) } - private lateinit var storage: PluggableKeyValueStorage + protected lateinit var storage: PluggableKeyValueStorage @Test fun putGet() { @@ -154,12 +154,10 @@ abstract class PluggableKeyValueStorageTest { fun tearDown() { storage.close() } -} -private val String.asByteArray: ByteArray - get() = - BuiltInBindingProvider.getBinding(String::class.java).getBytes(this) + protected val String.asByteArray: ByteArray + get() = BuiltInBindingProvider.getBinding(String::class.java).getBytes(this) -private val ByteArray.asString: String - get() = - BuiltInBindingProvider.getBinding(String::class.java).getObject(this) \ No newline at end of file + protected val ByteArray.asString: String + get() = BuiltInBindingProvider.getBinding(String::class.java).getObject(this) +} \ No newline at end of file diff --git a/jacodb-taint-configuration/build.gradle.kts b/jacodb-taint-configuration/build.gradle.kts index c8fd9e074..d46a5bd28 100644 --- a/jacodb-taint-configuration/build.gradle.kts +++ b/jacodb-taint-configuration/build.gradle.kts @@ -1,5 +1,4 @@ plugins { - id("java") kotlin("plugin.serialization") } @@ -15,6 +14,7 @@ dependencies { implementation(Libs.kotlinx_serialization_core) implementation(Libs.kotlinx_serialization_json) // for local tests only + testImplementation(testFixtures(project(":jacodb-storage"))) testImplementation(Libs.kotlin_logging) } diff --git a/settings.gradle.kts b/settings.gradle.kts index 743dac2bd..4d681dd07 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -21,7 +21,9 @@ gitHooks { include("jacodb-api-common") include("jacodb-api-jvm") +include("jacodb-api-storage") include("jacodb-core") +include("jacodb-storage") include("jacodb-analysis") include("jacodb-examples") include("jacodb-benchmarks")