Merge branch 'release/os/4.9' into shams-4.10-merge-c8135331

This commit is contained in:
Shams Asari 2023-08-22 11:24:38 +01:00
commit 46914a2b35
4 changed files with 79 additions and 33 deletions

View File

@ -272,7 +272,7 @@ class HibernateAttachmentQueryCriteriaParser<T,R>(override val criteriaBuilder:
class HibernateQueryCriteriaParser(val contractStateType: Class<out ContractState>, class HibernateQueryCriteriaParser(val contractStateType: Class<out ContractState>,
val contractStateTypeMappings: Map<String, Set<String>>, val contractStateTypeMappings: Map<String, Set<String>>,
override val criteriaBuilder: CriteriaBuilder, override val criteriaBuilder: CriteriaBuilder,
val criteriaQuery: CriteriaQuery<Tuple>, val criteriaQuery: CriteriaQuery<*>,
val vaultStates: Root<VaultSchemaV1.VaultStates>) : AbstractQueryCriteriaParser<QueryCriteria, IQueryCriteriaParser, Sort>(), IQueryCriteriaParser { val vaultStates: Root<VaultSchemaV1.VaultStates>) : AbstractQueryCriteriaParser<QueryCriteria, IQueryCriteriaParser, Sort>(), IQueryCriteriaParser {
private companion object { private companion object {
private val log = contextLogger() private val log = contextLogger()

View File

@ -35,7 +35,6 @@ import net.corda.core.node.services.vault.PageSpecification
import net.corda.core.node.services.vault.QueryCriteria import net.corda.core.node.services.vault.QueryCriteria
import net.corda.core.node.services.vault.Sort import net.corda.core.node.services.vault.Sort
import net.corda.core.node.services.vault.SortAttribute import net.corda.core.node.services.vault.SortAttribute
import net.corda.core.node.services.vault.builder
import net.corda.core.observable.internal.OnResilientSubscribe import net.corda.core.observable.internal.OnResilientSubscribe
import net.corda.core.schemas.PersistentStateRef import net.corda.core.schemas.PersistentStateRef
import net.corda.core.serialization.SingletonSerializeAsToken import net.corda.core.serialization.SingletonSerializeAsToken
@ -69,17 +68,21 @@ import java.security.PublicKey
import java.sql.SQLException import java.sql.SQLException
import java.time.Clock import java.time.Clock
import java.time.Instant import java.time.Instant
import java.util.Arrays import java.util.*
import java.util.UUID
import java.util.concurrent.ConcurrentHashMap import java.util.concurrent.ConcurrentHashMap
import java.util.concurrent.CopyOnWriteArraySet import java.util.concurrent.CopyOnWriteArraySet
import java.util.stream.Stream import java.util.stream.Stream
import javax.persistence.PersistenceException import javax.persistence.PersistenceException
import javax.persistence.Tuple import javax.persistence.Tuple
import javax.persistence.criteria.CriteriaBuilder import javax.persistence.criteria.CriteriaBuilder
import javax.persistence.criteria.CriteriaQuery
import javax.persistence.criteria.CriteriaUpdate import javax.persistence.criteria.CriteriaUpdate
import javax.persistence.criteria.Predicate import javax.persistence.criteria.Predicate
import javax.persistence.criteria.Root import javax.persistence.criteria.Root
import kotlin.collections.ArrayList
import kotlin.collections.LinkedHashSet
import kotlin.collections.component1
import kotlin.collections.component2
/** /**
* The vault service handles storage, retrieval and querying of states. * The vault service handles storage, retrieval and querying of states.
@ -706,10 +709,8 @@ class NodeVaultService(
paging: PageSpecification, paging: PageSpecification,
sorting: Sort, sorting: Sort,
contractStateType: Class<out T>): Vault.Page<T> { contractStateType: Class<out T>): Vault.Page<T> {
// calculate total results where a page specification has been defined val (criteriaQuery, criteriaParser) = buildCriteriaQuery<Tuple>(criteria, contractStateType, sorting)
val totalStatesAvailable = if (paging.isDefault) -1 else queryTotalStateCount(criteria, contractStateType) val query = getSession().createQuery(criteriaQuery)
val (query, stateTypes) = createQuery(criteria, contractStateType, sorting)
query.setResultWindow(paging) query.setResultWindow(paging)
val statesMetadata: MutableList<Vault.StateMetadata> = mutableListOf() val statesMetadata: MutableList<Vault.StateMetadata> = mutableListOf()
@ -732,7 +733,14 @@ class NodeVaultService(
ArrayList() ArrayList()
) )
return Vault.Page(states, statesMetadata, totalStatesAvailable, stateTypes, otherResults) val totalStatesAvailable = when {
paging.isDefault -> -1L
// If the first page isn't full then we know that's all the states that are available
paging.pageNumber == DEFAULT_PAGE_NUM && states.size < paging.pageSize -> states.size.toLong()
else -> queryTotalStateCount(criteria, contractStateType)
}
return Vault.Page(states, statesMetadata, totalStatesAvailable, criteriaParser.stateTypes, otherResults)
} }
private fun <R> Query<R>.resultStream(paging: PageSpecification): Stream<R> { private fun <R> Query<R>.resultStream(paging: PageSpecification): Stream<R> {
@ -761,19 +769,17 @@ class NodeVaultService(
} }
} }
private fun <T : ContractState> queryTotalStateCount(baseCriteria: QueryCriteria, contractStateType: Class<out T>): Long { private fun <T : ContractState> queryTotalStateCount(criteria: QueryCriteria, contractStateType: Class<out T>): Long {
val count = builder { VaultSchemaV1.VaultStates::recordedTime.count() } val (criteriaQuery, criteriaParser) = buildCriteriaQuery<Long>(criteria, contractStateType, null)
val countCriteria = QueryCriteria.VaultCustomQueryCriteria(count, Vault.StateStatus.ALL) criteriaQuery.select(criteriaBuilder.countDistinct(criteriaParser.vaultStates))
val criteria = baseCriteria.and(countCriteria) val query = getSession().createQuery(criteriaQuery)
val (query) = createQuery(criteria, contractStateType, null) return query.singleResult
val results = query.resultList
return results.last().toArray().last() as Long
} }
private fun <T : ContractState> createQuery(criteria: QueryCriteria, private inline fun <reified T> buildCriteriaQuery(criteria: QueryCriteria,
contractStateType: Class<out T>, contractStateType: Class<out ContractState>,
sorting: Sort?): Pair<Query<Tuple>, Vault.StateStatus> { sorting: Sort?): Pair<CriteriaQuery<T>, HibernateQueryCriteriaParser> {
val criteriaQuery = criteriaBuilder.createQuery(Tuple::class.java) val criteriaQuery = criteriaBuilder.createQuery(T::class.java)
val criteriaParser = HibernateQueryCriteriaParser( val criteriaParser = HibernateQueryCriteriaParser(
contractStateType, contractStateType,
contractStateTypeMappings, contractStateTypeMappings,
@ -782,8 +788,7 @@ class NodeVaultService(
criteriaQuery.from(VaultSchemaV1.VaultStates::class.java) criteriaQuery.from(VaultSchemaV1.VaultStates::class.java)
) )
criteriaParser.parse(criteria, sorting) criteriaParser.parse(criteria, sorting)
val query = getSession().createQuery(criteriaQuery) return Pair(criteriaQuery, criteriaParser)
return Pair(query, criteriaParser.stateTypes)
} }
/** /**

View File

@ -22,7 +22,7 @@ class VaultQueryExceptionsTests : VaultQueryParties by rule {
@ClassRule @ClassRule
@JvmField @JvmField
val rule = object : VaultQueryTestRule() { val rule = object : VaultQueryTestRule(persistentServices = false) {
override val cordappPackages = listOf( override val cordappPackages = listOf(
"net.corda.testing.contracts", "net.corda.testing.contracts",
"net.corda.finance.contracts", "net.corda.finance.contracts",

View File

@ -4,6 +4,7 @@ import com.nhaarman.mockito_kotlin.mock
import net.corda.core.contracts.* import net.corda.core.contracts.*
import net.corda.core.crypto.* import net.corda.core.crypto.*
import net.corda.core.identity.AbstractParty import net.corda.core.identity.AbstractParty
import net.corda.core.identity.AnonymousParty
import net.corda.core.identity.CordaX500Name import net.corda.core.identity.CordaX500Name
import net.corda.core.identity.Party import net.corda.core.identity.Party
import net.corda.core.internal.packageName import net.corda.core.internal.packageName
@ -37,6 +38,7 @@ import net.corda.testing.internal.configureDatabase
import net.corda.testing.internal.vault.* import net.corda.testing.internal.vault.*
import net.corda.testing.node.MockServices import net.corda.testing.node.MockServices
import net.corda.testing.node.MockServices.Companion.makeTestDatabaseAndMockServices import net.corda.testing.node.MockServices.Companion.makeTestDatabaseAndMockServices
import net.corda.testing.node.MockServices.Companion.makeTestDatabaseAndPersistentServices
import net.corda.testing.node.makeTestIdentityService import net.corda.testing.node.makeTestIdentityService
import org.assertj.core.api.Assertions.assertThat import org.assertj.core.api.Assertions.assertThat
import org.assertj.core.api.Assertions.assertThatCode import org.assertj.core.api.Assertions.assertThatCode
@ -102,7 +104,7 @@ interface VaultQueryParties {
val cordappPackages: List<String> val cordappPackages: List<String>
} }
open class VaultQueryTestRule : ExternalResource(), VaultQueryParties { open class VaultQueryTestRule(private val persistentServices: Boolean) : ExternalResource(), VaultQueryParties {
override val alice = TestIdentity(ALICE_NAME, 70) override val alice = TestIdentity(ALICE_NAME, 70)
override val bankOfCorda = TestIdentity(BOC_NAME) override val bankOfCorda = TestIdentity(BOC_NAME)
override val bigCorp = TestIdentity(CordaX500Name("BigCorporation", "New York", "US")) override val bigCorp = TestIdentity(CordaX500Name("BigCorporation", "New York", "US"))
@ -135,12 +137,22 @@ open class VaultQueryTestRule : ExternalResource(), VaultQueryParties {
override fun before() { override fun before() {
// register additional identities val databaseAndServices = if (persistentServices) {
val databaseAndServices = makeTestDatabaseAndMockServices( makeTestDatabaseAndPersistentServices(
cordappPackages, cordappPackages,
makeTestIdentityService(MEGA_CORP_IDENTITY, MINI_CORP_IDENTITY, dummyCashIssuer.identity, dummyNotary.identity), megaCorp,
megaCorp, moreKeys = setOf(DUMMY_NOTARY_KEY),
moreKeys = *arrayOf(DUMMY_NOTARY_KEY)) moreIdentities = setOf(MEGA_CORP_IDENTITY, MINI_CORP_IDENTITY, dummyCashIssuer.identity, dummyNotary.identity)
)
} else {
@Suppress("SpreadOperator")
makeTestDatabaseAndMockServices(
cordappPackages,
makeTestIdentityService(MEGA_CORP_IDENTITY, MINI_CORP_IDENTITY, dummyCashIssuer.identity, dummyNotary.identity),
megaCorp,
moreKeys = *arrayOf(DUMMY_NOTARY_KEY)
)
}
database = databaseAndServices.first database = databaseAndServices.first
services = databaseAndServices.second services = databaseAndServices.second
vaultFiller = VaultFiller(services, dummyNotary) vaultFiller = VaultFiller(services, dummyNotary)
@ -2832,9 +2844,8 @@ abstract class VaultQueryTestsBase : VaultQueryParties {
} }
class VaultQueryTests : VaultQueryTestsBase(), VaultQueryParties by delegate { class VaultQueryTests : VaultQueryTestsBase(), VaultQueryParties by delegate {
companion object { companion object {
val delegate = VaultQueryTestRule() val delegate = VaultQueryTestRule(persistentServices = false)
} }
@Rule @Rule
@ -3137,4 +3148,34 @@ class VaultQueryTests : VaultQueryTestsBase(), VaultQueryParties by delegate {
) )
} }
} }
} }
class PersistentServicesVaultQueryTests : VaultQueryParties by delegate {
companion object {
val delegate = VaultQueryTestRule(persistentServices = true)
@ClassRule
@JvmField
val testSerialization = SerializationEnvironmentRule()
}
@Rule
@JvmField
val vaultQueryTestRule = delegate
@Test(timeout = 300_000)
fun `query on externalId which maps to multiple keys`() {
val externalId = UUID.randomUUID()
val page = database.transaction {
val keys = Array(2) { services.keyManagementService.freshKey(externalId) }
vaultFiller.fillWithDummyState(participants = keys.map(::AnonymousParty))
services.vaultService.queryBy<ContractState>(
VaultQueryCriteria(externalIds = listOf(externalId)),
paging = PageSpecification(DEFAULT_PAGE_NUM, 10)
)
}
assertThat(page.states).hasSize(1)
assertThat(page.totalStatesAvailable).isEqualTo(1)
}
}