From cc8ce3ca99d3c372c00206ab10c094f84c0d6f70 Mon Sep 17 00:00:00 2001 From: nikinagy <61757742+nikinagy@users.noreply.github.com> Date: Tue, 19 May 2020 14:04:19 +0100 Subject: [PATCH 1/2] empty list checks (#6262) --- .../vault/HibernateQueryCriteriaParser.kt | 28 +++++++++++++------ .../node/services/vault/VaultQueryTests.kt | 21 ++++++++++++++ 2 files changed, 41 insertions(+), 8 deletions(-) diff --git a/node/src/main/kotlin/net/corda/node/services/vault/HibernateQueryCriteriaParser.kt b/node/src/main/kotlin/net/corda/node/services/vault/HibernateQueryCriteriaParser.kt index 2a52f9b948..5b13e476e0 100644 --- a/node/src/main/kotlin/net/corda/node/services/vault/HibernateQueryCriteriaParser.kt +++ b/node/src/main/kotlin/net/corda/node/services/vault/HibernateQueryCriteriaParser.kt @@ -766,8 +766,14 @@ class HibernateQueryCriteriaParser(val contractStateType: Class) .values.map { participant -> (participant as LiteralExpression<*>).literal } log.warn("Adding new participants: $participants to existing participants: $existingParticipants") - commonPredicates.replace(predicateID, criteriaBuilder.and( - getPersistentPartyRoot().get("x500Name").`in`(existingParticipants + participants))) + commonPredicates.replace( + predicateID, + checkIfListIsEmpty( + args = existingParticipants + participants, + criteriaBuilder = criteriaBuilder, + predicate = criteriaBuilder.and(getPersistentPartyRoot().get("x500Name").`in`(existingParticipants + participants)) + ) + ) } else { // Get the persistent party entity. @@ -796,12 +802,18 @@ class HibernateQueryCriteriaParser(val contractStateType: Class("stateRef"), - subRoot.get("compositeKey").get("stateRef"))), - criteriaBuilder.not(subRoot.get("x500Name").`in`(exactParticipants))) - val subQueryNotExistsPredicate = criteriaBuilder.and(criteriaBuilder.not(criteriaBuilder.exists(subQueryNotExists))) - constraintPredicates.add(subQueryNotExistsPredicate) + + //if the list of exact participants is empty, we return nothing with 1=0 + if (exactParticipants.isEmpty()) { + constraintPredicates.add(criteriaBuilder.and(criteriaBuilder.equal(criteriaBuilder.literal(1), 0))) + } else { + subQueryNotExists.where(criteriaBuilder.and( + criteriaBuilder.equal(vaultStates.get("stateRef"), + subRoot.get("compositeKey").get("stateRef"))), + criteriaBuilder.not(subRoot.get("x500Name").`in`(exactParticipants))) + val subQueryNotExistsPredicate = criteriaBuilder.and(criteriaBuilder.not(criteriaBuilder.exists(subQueryNotExists))) + constraintPredicates.add(subQueryNotExistsPredicate) + } // join with transactions for each matching participant (only required where more than one) if (exactParticipants.size > 1) diff --git a/node/src/test/kotlin/net/corda/node/services/vault/VaultQueryTests.kt b/node/src/test/kotlin/net/corda/node/services/vault/VaultQueryTests.kt index ef3b1f5a02..b4c0ec98bf 100644 --- a/node/src/test/kotlin/net/corda/node/services/vault/VaultQueryTests.kt +++ b/node/src/test/kotlin/net/corda/node/services/vault/VaultQueryTests.kt @@ -249,6 +249,27 @@ abstract class VaultQueryTestsBase : VaultQueryParties { } } + @Test(timeout=300_000) + fun `returns zero states when exact participants list is empty`() { + database.transaction { + identitySvc.verifyAndRegisterIdentity(BIG_CORP_IDENTITY) + vaultFiller.fillWithDummyState(participants = listOf(MEGA_CORP)) + vaultFiller.fillWithDummyState(participants = listOf(MEGA_CORP, BIG_CORP)) + + val criteria = VaultQueryCriteria(exactParticipants = emptyList()) + val results = vaultService.queryBy(criteria) + assertThat(results.states).hasSize(0) + + val criteriaWithOneExactParticipant = VaultQueryCriteria(exactParticipants = listOf(MEGA_CORP)) + val resultsWithOneExactParticipant = vaultService.queryBy(criteriaWithOneExactParticipant) + assertThat(resultsWithOneExactParticipant.states).hasSize(1) + + val criteriaWithMoreExactParticipants = VaultQueryCriteria(exactParticipants = listOf(MEGA_CORP, BIG_CORP)) + val resultsWithMoreExactParticipants = vaultService.queryBy(criteriaWithMoreExactParticipants) + assertThat(resultsWithMoreExactParticipants.states).hasSize(1) + } + } + @Test(timeout=300_000) fun `unconsumed base contract states for two participants`() { database.transaction { From 8e74eea6072f06b571f7bac1cc3310e166fd7057 Mon Sep 17 00:00:00 2001 From: nikinagy <61757742+nikinagy@users.noreply.github.com> Date: Thu, 21 May 2020 13:26:55 +0100 Subject: [PATCH 2/2] CORDA-3587 - adding kdocs for current behaviour of VaultQueryCriteria (#6242) * adding kdocs for current behaviour of VaultQueryCriteria * improving the kdocs * address PR comments --- .../core/node/services/vault/QueryCriteria.kt | 92 +++++++++++++++++-- 1 file changed, 86 insertions(+), 6 deletions(-) diff --git a/core/src/main/kotlin/net/corda/core/node/services/vault/QueryCriteria.kt b/core/src/main/kotlin/net/corda/core/node/services/vault/QueryCriteria.kt index 91bfe9bc0d..059e801730 100644 --- a/core/src/main/kotlin/net/corda/core/node/services/vault/QueryCriteria.kt +++ b/core/src/main/kotlin/net/corda/core/node/services/vault/QueryCriteria.kt @@ -169,19 +169,93 @@ sealed class QueryCriteria : GenericQueryCriteria>): VaultQueryCriteria = copy(contractStateTypes = contractStateTypes) - fun withStateRefs(stateRefs: List): VaultQueryCriteria = copy(stateRefs = stateRefs) - fun withNotary(notary: List): VaultQueryCriteria = copy(notary = notary) - fun withSoftLockingCondition(softLockingCondition: SoftLockingCondition): VaultQueryCriteria = copy(softLockingCondition = softLockingCondition) - fun withTimeCondition(timeCondition: TimeCondition): VaultQueryCriteria = copy(timeCondition = timeCondition) + /** + * This function creates a new [VaultQueryCriteria] object with default values, and sets the value of [relevancyStatus]. + * Please use only one function in this group at a time to make sure they are not overwriting each other. + */ fun withRelevancyStatus(relevancyStatus: Vault.RelevancyStatus): VaultQueryCriteria = copy(relevancyStatus = relevancyStatus) + + /** + * This function creates a new [VaultQueryCriteria] object with default values, and sets the value of [constraintTypes]. + * Please use only one function in this group at a time to make sure they are not overwriting each other. + */ fun withConstraintTypes(constraintTypes: Set): VaultQueryCriteria = copy(constraintTypes = constraintTypes) + + /** + * This function creates a new [VaultQueryCriteria] object with default values, and sets the value of [constraints]. + * Please use only one function in this group at a time to make sure they are not overwriting each other. + */ fun withConstraints(constraints: Set): VaultQueryCriteria = copy(constraints = constraints) + + /** + * This function creates a new [VaultQueryCriteria] object with default values, and sets the value of [participants]. + * Please use only one function in this group at a time to make sure they are not overwriting each other. + */ fun withParticipants(participants: List): VaultQueryCriteria = copy(participants = participants) + + /** + * This function creates a new [VaultQueryCriteria] object with default values, and sets the value of [externalIds]. + * Please use only one function in this group at a time to make sure they are not overwriting each other. + */ fun withExternalIds(externalIds: List): VaultQueryCriteria = copy(externalIds = externalIds) + + /** + * This function creates a new [VaultQueryCriteria] object with default values, and sets the value of [exactParticipants]. + * Please use only one function in this group at a time to make sure they are not overwriting each other. + */ fun withExactParticipants(exactParticipants: List): VaultQueryCriteria = copy(exactParticipants = exactParticipants) + /** + * This function copies the existing [VaultQueryCriteria] object and sets the given value for [status]. + * You can use more than one of the functions in this group together. + * In case you are also using a function that creates a new [VaultQueryCriteria] object, make sure that you are + * calling that method first. + */ + fun withStatus(status: Vault.StateStatus): VaultQueryCriteria = copy(status = status) + + /** + * This function copies the existing [VaultQueryCriteria] object and sets the given value for [contractStateTypes]. + * You can use more than one of the functions in this group together. + * In case you are also using a function that creates a new [VaultQueryCriteria] object, make sure that you are + * calling that method first. + */ + fun withContractStateTypes(contractStateTypes: Set>): VaultQueryCriteria = copy(contractStateTypes = contractStateTypes) + + /** + * This function copies the existing [VaultQueryCriteria] object and sets the given value for [stateRefs]. + * You can use more than one of the functions in this group together. + * In case you are also using a function that creates a new [VaultQueryCriteria] object, make sure that you are + * calling that method first. + */ + fun withStateRefs(stateRefs: List): VaultQueryCriteria = copy(stateRefs = stateRefs) + + /** + * This function copies the existing [VaultQueryCriteria] object and sets the given value for [notary]. + * You can use more than one of the functions in this group together. + * In case you are also using a function that creates a new [VaultQueryCriteria] object, make sure that you are + * calling that method first. + */ + fun withNotary(notary: List): VaultQueryCriteria = copy(notary = notary) + + /** + * This function copies the existing [VaultQueryCriteria] object and sets the given value for [softLockingCondition]. + * You can use more than one of the functions in this group together. + * In case you are also using a function that creates a new [VaultQueryCriteria] object, make sure that you are + * calling that method first. + */ + fun withSoftLockingCondition(softLockingCondition: SoftLockingCondition): VaultQueryCriteria = copy(softLockingCondition = softLockingCondition) + + /** + * This function copies the existing [VaultQueryCriteria] object and sets the given value for [timeCondition]. + * You can use more than one of the functions in this group together. + * In case you are also using a function that creates a new [VaultQueryCriteria] object, make sure that you are + * calling that method first. + */ + fun withTimeCondition(timeCondition: TimeCondition): VaultQueryCriteria = copy(timeCondition = timeCondition) + + /** + * This function creates a [VaultQueryCriteria] object with the given values. All other fields have the default values set. + */ fun copy( status: Vault.StateStatus = Vault.StateStatus.UNCONSUMED, contractStateTypes: Set>? = null, @@ -211,6 +285,9 @@ sealed class QueryCriteria : GenericQueryCriteria>? = null, @@ -238,6 +315,9 @@ sealed class QueryCriteria : GenericQueryCriteria>? = this.contractStateTypes,