diff --git a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_8_0/6460-dont-store-reslink-target-partition-date.yaml b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_8_0/6460-dont-store-reslink-target-partition-date.yaml new file mode 100644 index 00000000000..db25abdd722 --- /dev/null +++ b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_8_0/6460-dont-store-reslink-target-partition-date.yaml @@ -0,0 +1,5 @@ +--- +type: change +issue: 6460 +title: "The HFJ_RES_LINK table with no longer store the `PARTITION_DATE` value for the indexed link target + resource, as this was an unused feature which has been removed as a part of a larger performance optimization." diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/BaseHapiFhirDao.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/BaseHapiFhirDao.java index 72e69cb6f16..6e37e4eeb62 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/BaseHapiFhirDao.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/BaseHapiFhirDao.java @@ -1319,7 +1319,7 @@ public abstract class BaseHapiFhirDao extends BaseStora * the previous version entity. */ if (historyEntry == null) { - historyEntry = theEntity.toHistory(versionedTags); + historyEntry = theEntity.toHistory(versionedTags && theEntity.getDeleted() == null); } historyEntry.setEncoding(theChanged.getEncoding()); diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/BaseHapiFhirResourceDao.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/BaseHapiFhirResourceDao.java index a98f5cde90a..bf1a0df0329 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/BaseHapiFhirResourceDao.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/BaseHapiFhirResourceDao.java @@ -1788,10 +1788,6 @@ public abstract class BaseHapiFhirResourceDao extends B } } - if (entity == null) { - throw new ResourceNotFoundException(Msg.code(1996) + "Resource " + theId + " is not known"); - } - if (theId.hasVersionIdPart()) { if (!theId.isVersionIdPartValidLong()) { throw new ResourceNotFoundException(Msg.code(978) diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/BaseHapiFhirSystemDao.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/BaseHapiFhirSystemDao.java index 0aedafefed7..fde57c39836 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/BaseHapiFhirSystemDao.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/BaseHapiFhirSystemDao.java @@ -192,7 +192,7 @@ public abstract class BaseHapiFhirSystemDao extends B HapiTransactionService.requireTransaction(); List pids = theResolvedIds.stream().map(t -> ((JpaPid) t).getId()).collect(Collectors.toList()); - new QueryChunker().chunk(pids, idChunk -> { + QueryChunker.chunk(pids, idChunk -> { /* * Pre-fetch the resources we're touching in this transaction in mass - this reduced the diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/index/IdHelperService.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/index/IdHelperService.java index 032c2f82185..c4542ea8aac 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/index/IdHelperService.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/index/IdHelperService.java @@ -599,18 +599,28 @@ public class IdHelperService implements IIdHelperService { return Optional.empty(); } else if (theRequestPartitionId.isAllPartitions()) { return Optional.empty(); - } else if (theRequestPartitionId.isDefaultPartition() && myPartitionSettings.getDefaultPartitionId() == null) { - Predicate partitionIdCriteria = cb.isNull(from.get("myPartitionIdValue")); - return Optional.of(partitionIdCriteria); - } else if (!theRequestPartitionId.isAllPartitions()) { + } else { List partitionIds = theRequestPartitionId.getPartitionIds(); partitionIds = replaceDefaultPartitionIdIfNonNull(myPartitionSettings, partitionIds); - if (partitionIds.size() > 1) { - Predicate partitionIdCriteria = from.get("myPartitionIdValue").in(partitionIds); - return Optional.of(partitionIdCriteria); - } else if (partitionIds.size() == 1) { - Predicate partitionIdCriteria = cb.equal(from.get("myPartitionIdValue"), partitionIds.get(0)); - return Optional.of(partitionIdCriteria); + if (partitionIds.contains(null)) { + Predicate partitionIdNullCriteria = + from.get("myPartitionIdValue").isNull(); + if (partitionIds.size() == 1) { + return Optional.of(partitionIdNullCriteria); + } else { + Predicate partitionIdCriteria = from.get("myPartitionIdValue") + .in(partitionIds.stream().filter(t -> t != null).collect(Collectors.toList())); + return Optional.of(cb.or(partitionIdCriteria, partitionIdNullCriteria)); + } + } else { + if (partitionIds.size() > 1) { + Predicate partitionIdCriteria = + from.get("myPartitionIdValue").in(partitionIds); + return Optional.of(partitionIdCriteria); + } else if (partitionIds.size() == 1) { + Predicate partitionIdCriteria = cb.equal(from.get("myPartitionIdValue"), partitionIds.get(0)); + return Optional.of(partitionIdCriteria); + } } } return Optional.empty(); diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/builder/predicate/ResourceIdPredicateBuilder.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/builder/predicate/ResourceIdPredicateBuilder.java index 7b67aab243c..b58b0acf2ab 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/builder/predicate/ResourceIdPredicateBuilder.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/builder/predicate/ResourceIdPredicateBuilder.java @@ -23,23 +23,25 @@ import ca.uhn.fhir.interceptor.model.RequestPartitionId; import ca.uhn.fhir.jpa.api.svc.IIdHelperService; import ca.uhn.fhir.jpa.api.svc.ResolveIdentityMode; import ca.uhn.fhir.jpa.dao.predicate.SearchFilterParser; +import ca.uhn.fhir.jpa.model.cross.IResourceLookup; import ca.uhn.fhir.jpa.model.dao.JpaPid; import ca.uhn.fhir.jpa.search.builder.sql.SearchQueryBuilder; import ca.uhn.fhir.jpa.util.QueryParameterUtils; import ca.uhn.fhir.model.api.IQueryParameterType; import ca.uhn.fhir.rest.param.TokenParam; import ca.uhn.fhir.rest.param.TokenParamModifier; -import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException; import com.healthmarketscience.sqlbuilder.Condition; import com.healthmarketscience.sqlbuilder.dbspec.basic.DbColumn; import jakarta.annotation.Nullable; -import org.hl7.fhir.r4.model.IdType; +import org.hl7.fhir.instance.model.api.IIdType; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import java.util.HashSet; +import java.util.LinkedHashSet; import java.util.List; +import java.util.Map; import java.util.Set; import static org.apache.commons.lang3.ObjectUtils.defaultIfNull; @@ -69,9 +71,8 @@ public class ResourceIdPredicateBuilder extends BasePredicateBuilder { Set allOrPids = null; SearchFilterParser.CompareOperation defaultOperation = SearchFilterParser.CompareOperation.eq; - boolean allIdsAreForcedIds = true; for (List nextValue : theValues) { - Set orPids = new HashSet<>(); + Set ids = new LinkedHashSet<>(); boolean haveValue = false; for (IQueryParameterType next : nextValue) { String value = next.getValueAsQueryToken(getFhirContext()); @@ -79,25 +80,14 @@ public class ResourceIdPredicateBuilder extends BasePredicateBuilder { value = value.substring(1); } - IdType valueAsId = new IdType(value); if (isNotBlank(value)) { - if (!myIdHelperService.idRequiresForcedId(valueAsId.getIdPart()) && allIdsAreForcedIds) { - allIdsAreForcedIds = false; - } haveValue = true; - try { - JpaPid pid = myIdHelperService - .resolveResourceIdentity( - theRequestPartitionId, - theResourceName, - valueAsId.getIdPart(), - ResolveIdentityMode.excludeDeleted().noCacheUnlessDeletesDisabled()) - .getPersistentId(); - orPids.add(pid); - } catch (ResourceNotFoundException e) { - // This is not an error in a search, it just results in no matches - ourLog.debug("Resource ID {} was requested but does not exist", valueAsId.getIdPart()); + if (!value.contains("/")) { + value = theResourceName + "/" + value; } + IIdType id = getFhirContext().getVersion().newIdType(); + id.setValue(value); + ids.add(id); } if (next instanceof TokenParam) { @@ -106,6 +96,20 @@ public class ResourceIdPredicateBuilder extends BasePredicateBuilder { } } } + + Set orPids = new HashSet<>(); + + // We're joining this to a query that will explicitly ask for non-deleted, + // so we really only want the PID and can safely cache (even if a previously + // deleted status was cached, since it might now be undeleted) + Map> resolvedPids = myIdHelperService.resolveResourceIdentities( + theRequestPartitionId, + ids, + ResolveIdentityMode.includeDeleted().cacheOk()); + for (IResourceLookup lookup : resolvedPids.values()) { + orPids.add(lookup.getPersistentId()); + } + if (haveValue) { if (allOrPids == null) { allOrPids = orPids; @@ -127,7 +131,7 @@ public class ResourceIdPredicateBuilder extends BasePredicateBuilder { List resourceIds = JpaPid.toLongList(allOrPids); if (theSourceJoinColumn == null) { - BaseJoiningPredicateBuilder queryRootTable = super.getOrCreateQueryRootTable(!allIdsAreForcedIds); + BaseJoiningPredicateBuilder queryRootTable = super.getOrCreateQueryRootTable(true); Condition predicate; switch (operation) { default: diff --git a/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/ResourceTable.java b/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/ResourceTable.java index 7c54390af20..e03bdee71ec 100644 --- a/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/ResourceTable.java +++ b/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/ResourceTable.java @@ -850,8 +850,22 @@ public class ResourceTable extends BaseHasResource implements Serializable, IBas } } + // If we've deleted and updated the same resource in the same transaction, + // we need to actually create 2 distinct versions + if (getCurrentVersionEntity() != null + && getCurrentVersionEntity().getId() != null + && getVersion() == getCurrentVersionEntity().getVersion()) { + myVersion++; + } + populateHistoryEntityVersionAndDates(retVal); + // FIXME: delete? + // if (getCurrentVersionEntity() != null && getCurrentVersionEntity().getId() != null && + // isVersionUpdatedInCurrentTransaction()) { + // retVal.setVersion(getCurrentVersionEntity().getVersion() + 1); + // } + return retVal; } diff --git a/hapi-fhir-jpaserver-test-dstu2/src/test/java/ca/uhn/fhir/jpa/dao/dstu2/FhirResourceDaoDstu2Test.java b/hapi-fhir-jpaserver-test-dstu2/src/test/java/ca/uhn/fhir/jpa/dao/dstu2/FhirResourceDaoDstu2Test.java index 0677a9722b0..0cd3edb41a2 100644 --- a/hapi-fhir-jpaserver-test-dstu2/src/test/java/ca/uhn/fhir/jpa/dao/dstu2/FhirResourceDaoDstu2Test.java +++ b/hapi-fhir-jpaserver-test-dstu2/src/test/java/ca/uhn/fhir/jpa/dao/dstu2/FhirResourceDaoDstu2Test.java @@ -1661,46 +1661,6 @@ public class FhirResourceDaoDstu2Test extends BaseJpaDstu2Test { } - @Test - public void testReadInvalidVersion() throws Exception { - String methodName = "testReadInvalidVersion"; - - Patient pat = new Patient(); - pat.addIdentifier().setSystem("urn:system").setValue(methodName); - IIdType id = myPatientDao.create(pat, mySrd).getId(); - - assertEquals(methodName, myPatientDao.read(id, mySrd).getIdentifier().get(0).getValue()); - - try { - myPatientDao.read(id.withVersion("0"), mySrd); - fail(""); - } catch (ResourceNotFoundException e) { - assertEquals(Msg.code(979) + "Version \"0\" is not valid for resource Patient/" + id.getIdPart(), e.getMessage()); - } - - try { - myPatientDao.read(id.withVersion("2"), mySrd); - fail(""); - } catch (ResourceNotFoundException e) { - assertEquals(Msg.code(979) + "Version \"2\" is not valid for resource Patient/" + id.getIdPart(), e.getMessage()); - } - - try { - myPatientDao.read(id.withVersion("H"), mySrd); - fail(""); - } catch (ResourceNotFoundException e) { - assertEquals(Msg.code(978) + "Version \"H\" is not valid for resource Patient/" + id.getIdPart(), e.getMessage()); - } - - try { - myPatientDao.read(new IdDt("Patient/9999999999999/_history/1"), mySrd); - fail(""); - } catch (ResourceNotFoundException e) { - assertEquals(Msg.code(1996) + "Resource Patient/9999999999999/_history/1 is not known", e.getMessage()); - } - - } - @Test public void testReadWithDeletedResource() { String methodName = "testReadWithDeletedResource"; diff --git a/hapi-fhir-jpaserver-test-dstu3/src/test/java/ca/uhn/fhir/jpa/dao/dstu3/FhirResourceDaoDstu3Test.java b/hapi-fhir-jpaserver-test-dstu3/src/test/java/ca/uhn/fhir/jpa/dao/dstu3/FhirResourceDaoDstu3Test.java index e56646f8a95..065afeeb639 100644 --- a/hapi-fhir-jpaserver-test-dstu3/src/test/java/ca/uhn/fhir/jpa/dao/dstu3/FhirResourceDaoDstu3Test.java +++ b/hapi-fhir-jpaserver-test-dstu3/src/test/java/ca/uhn/fhir/jpa/dao/dstu3/FhirResourceDaoDstu3Test.java @@ -2294,7 +2294,7 @@ public class FhirResourceDaoDstu3Test extends BaseJpaDstu3Test { myPatientDao.read(new IdType("Patient/9999999999999/_history/1"), mySrd); fail(""); } catch (ResourceNotFoundException e) { - assertEquals(Msg.code(1996) + "Resource Patient/9999999999999/_history/1 is not known", e.getMessage()); + assertEquals(Msg.code(2001) + "Resource Patient/9999999999999 is not known", e.getMessage()); } } diff --git a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4QueryCountTest.java b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4QueryCountTest.java index e4b5e6c0837..635e847d5dc 100644 --- a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4QueryCountTest.java +++ b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4QueryCountTest.java @@ -40,6 +40,7 @@ import ca.uhn.fhir.rest.api.SortSpec; import ca.uhn.fhir.rest.api.server.IBundleProvider; import ca.uhn.fhir.rest.api.server.SystemRequestDetails; import ca.uhn.fhir.rest.param.ReferenceParam; +import ca.uhn.fhir.rest.param.TokenOrListParam; import ca.uhn.fhir.rest.param.TokenParam; import ca.uhn.fhir.rest.server.SimpleBundleProvider; import ca.uhn.fhir.rest.server.exceptions.PreconditionFailedException; @@ -51,6 +52,7 @@ import ca.uhn.fhir.test.utilities.server.HashMapResourceProviderExtension; import ca.uhn.fhir.test.utilities.server.RestfulServerExtension; import ca.uhn.fhir.util.BundleBuilder; import jakarta.annotation.Nonnull; +import org.hl7.fhir.instance.model.api.IAnyResource; import org.hl7.fhir.instance.model.api.IBaseResource; import org.hl7.fhir.instance.model.api.IIdType; import org.hl7.fhir.r4.model.BooleanType; @@ -1407,6 +1409,57 @@ public class FhirResourceDaoR4QueryCountTest extends BaseResourceProviderR4Test return ids; } + @Test + public void testSearchByMultipleIds() { + // Setup + List idValues = new ArrayList<>(); + for (int i = 0; i < 5; i++) { + // Client assigned and server assigned IDs + idValues.add(createPatient(withId(UUID.randomUUID().toString()), withActiveTrue()).toUnqualifiedVersionless().getValue()); + idValues.add(createPatient(withActiveTrue()).toUnqualifiedVersionless().getValue()); + } + String[] idValueArray = idValues.toArray(new String[0]); + + // Test + SearchParameterMap map = SearchParameterMap.newSynchronous(); + map.add(IAnyResource.SP_RES_ID, new TokenOrListParam(null, idValueArray)); + myCaptureQueriesListener.clear(); + IBundleProvider outcome = myPatientDao.search(map, mySrd); + List values = toUnqualifiedVersionlessIdValues(outcome); + + // Verify + myCaptureQueriesListener.logSelectQueries(); + assertEquals(2, myCaptureQueriesListener.countSelectQueries()); + assertThat(values).asList().containsExactlyInAnyOrder(idValueArray); + + // Now invalidate the caches, should add one more query + myMemoryCacheService.invalidateAllCaches(); + map = SearchParameterMap.newSynchronous(); + map.add(IAnyResource.SP_RES_ID, new TokenOrListParam(null, idValueArray)); + myCaptureQueriesListener.clear(); + outcome = myPatientDao.search(map, mySrd); + values = toUnqualifiedVersionlessIdValues(outcome); + + // Verify + myCaptureQueriesListener.logSelectQueries(); + assertEquals(3, myCaptureQueriesListener.countSelectQueries()); + assertThat(values).asList().containsExactlyInAnyOrder(idValueArray); + + // And again, should be cached once more + map = SearchParameterMap.newSynchronous(); + map.add(IAnyResource.SP_RES_ID, new TokenOrListParam(null, idValueArray)); + myCaptureQueriesListener.clear(); + outcome = myPatientDao.search(map, mySrd); + values = toUnqualifiedVersionlessIdValues(outcome); + + // Verify + myCaptureQueriesListener.logSelectQueries(); + assertEquals(2, myCaptureQueriesListener.countSelectQueries()); + assertThat(values).asList().containsExactlyInAnyOrder(idValueArray); + + } + + /** * See the class javadoc before changing the counts in this test! @@ -1647,12 +1700,12 @@ public class FhirResourceDaoR4QueryCountTest extends BaseResourceProviderR4Test IBundleProvider outcome = dao.search(map, mySrd); toUnqualifiedVersionlessIdValues(outcome); myCaptureQueriesListener.logSelectQueries(); - assertEquals(4, myCaptureQueriesListener.countSelectQueries()); + assertEquals(3, myCaptureQueriesListener.countSelectQueries()); myCaptureQueriesListener.clear(); outcome = dao.search(map, mySrd); toUnqualifiedVersionlessIdValues(outcome); - assertEquals(4, myCaptureQueriesListener.countSelectQueries()); + assertEquals(3, myCaptureQueriesListener.countSelectQueries()); } diff --git a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4SearchNoFtTest.java b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4SearchNoFtTest.java index 2331b0f8a41..9cec742dca9 100644 --- a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4SearchNoFtTest.java +++ b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4SearchNoFtTest.java @@ -1731,65 +1731,6 @@ public class FhirResourceDaoR4SearchNoFtTest extends BaseJpaR4Test { } - @Test - public void testSearchByIdParam_QueryIsMinimal() { - // With only an _id parameter - { - SearchParameterMap params = new SearchParameterMap(); - params.setLoadSynchronous(true); - params.add(PARAM_ID, new StringParam("DiagnosticReport/123")); - myCaptureQueriesListener.clear(); - myDiagnosticReportDao.search(params).size(); - List selectQueries = myCaptureQueriesListener.getSelectQueriesForCurrentThread(); - assertThat(selectQueries).hasSize(1); - - String sqlQuery = selectQueries.get(0).getSql(true, true).toLowerCase(); - ourLog.info("SQL Query:\n{}", sqlQuery); - assertThat(countMatches(sqlQuery, "res_id = '123'")).as(sqlQuery).isEqualTo(1); - assertThat(countMatches(sqlQuery, "join")).as(sqlQuery).isEqualTo(0); - assertThat(countMatches(sqlQuery, "res_type = 'diagnosticreport'")).as(sqlQuery).isEqualTo(1); - assertThat(countMatches(sqlQuery, "res_deleted_at is null")).as(sqlQuery).isEqualTo(1); - } - // With an _id parameter and a standard search param - { - SearchParameterMap params = new SearchParameterMap(); - params.setLoadSynchronous(true); - params.add(PARAM_ID, new StringParam("DiagnosticReport/123")); - params.add("code", new TokenParam("foo", "bar")); - myCaptureQueriesListener.clear(); - myDiagnosticReportDao.search(params).size(); - List selectQueries = myCaptureQueriesListener.getSelectQueriesForCurrentThread(); - assertThat(selectQueries).hasSize(1); - - String sqlQuery = selectQueries.get(0).getSql(true, true).toLowerCase(); - ourLog.info("SQL Query:\n{}", sqlQuery); - assertThat(countMatches(sqlQuery, "res_id = '123'")).as(sqlQuery).isEqualTo(1); - assertThat(countMatches(sqlQuery, "join")).as(sqlQuery).isEqualTo(1); - assertThat(countMatches(sqlQuery, "hash_sys_and_value")).as(sqlQuery).isEqualTo(1); - assertThat(countMatches(sqlQuery, "res_type = 'diagnosticreport")).as(sqlQuery).isEqualTo(0); // could be 0 - assertThat(countMatches(sqlQuery, "res_deleted_at")).as(sqlQuery).isEqualTo(0); // could be 0 - } - } - - @Test - public void testSearchByIdParamAndOtherSearchParam_QueryIsMinimal() { - SearchParameterMap params = new SearchParameterMap(); - params.setLoadSynchronous(true); - params.add(PARAM_ID, new StringParam("DiagnosticReport/123")); - params.add(PARAM_ID, new StringParam("DiagnosticReport/123")); - myCaptureQueriesListener.clear(); - myDiagnosticReportDao.search(params).size(); - List selectQueries = myCaptureQueriesListener.getSelectQueriesForCurrentThread(); - assertThat(selectQueries).hasSize(1); - - String sqlQuery = selectQueries.get(0).getSql(true, true).toLowerCase(); - ourLog.info("SQL Query:\n{}", sqlQuery); - assertThat(countMatches(sqlQuery, "res_id = '123'")).as(sqlQuery).isEqualTo(1); - assertThat(countMatches(sqlQuery, "join")).as(sqlQuery).isEqualTo(0); - assertThat(countMatches(sqlQuery, "res_type = 'diagnosticreport'")).as(sqlQuery).isEqualTo(1); - assertThat(countMatches(sqlQuery, "res_deleted_at is null")).as(sqlQuery).isEqualTo(1); - } - @Test public void testSearchByIdParamAnd() { IIdType id1; diff --git a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4SearchOptimizedTest.java b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4SearchOptimizedTest.java index 05b5877c6cf..23c5855e33a 100644 --- a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4SearchOptimizedTest.java +++ b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4SearchOptimizedTest.java @@ -95,7 +95,7 @@ public class FhirResourceDaoR4SearchOptimizedTest extends BaseJpaR4Test { mySearchCoordinatorSvcImpl = ProxyUtil.getSingletonTarget(mySearchCoordinatorSvc, SearchCoordinatorSvcImpl.class); mySearchCoordinatorSvcImpl.setLoadingThrottleForUnitTests(null); mySearchCoordinatorSvcImpl.setSyncSizeForUnitTests(QueryParameterUtils.DEFAULT_SYNC_SIZE); - myCaptureQueriesListener.setCaptureQueryStackTrace(true); +// myCaptureQueriesListener.setCaptureQueryStackTrace(true); myStorageSettings.setAdvancedHSearchIndexing(false); } @@ -876,8 +876,11 @@ public class FhirResourceDaoR4SearchOptimizedTest extends BaseJpaR4Test { String obs2id = myObservationDao.create(obs2).getId().getIdPart(); assertThat(obs2id).matches("^[0-9]+$"); - // Search by ID where all IDs are forced IDs + + // Search by ID where all IDs are forced IDs, and in two separate params { + myMemoryCacheService.invalidateAllCaches(); + SearchParameterMap map = SearchParameterMap.newSynchronous(); map.add("_id", new TokenParam("A")); map.add("subject", new ReferenceParam("Patient/B")); @@ -885,20 +888,20 @@ public class FhirResourceDaoR4SearchOptimizedTest extends BaseJpaR4Test { myCaptureQueriesListener.clear(); IBundleProvider outcome = myObservationDao.search(map, new SystemRequestDetails()); assertThat(outcome.getResources(0, 999)).hasSize(1); + myCaptureQueriesListener.logSelectQueriesForCurrentThread(); - String selectQuery = myCaptureQueriesListener.getSelectQueriesForCurrentThread().get(0).getSql(true, false); - assertThat(StringUtils.countMatches(selectQuery.toLowerCase(), "rt1_0.res_type='observation'")).as(selectQuery).isEqualTo(1); - assertThat(StringUtils.countMatches(selectQuery.toLowerCase(), "rt1_0.fhir_id='a'")).as(selectQuery).isEqualTo(1); - + assertThat(selectQuery).contains("where rt1_0.RES_TYPE='Patient' and rt1_0.FHIR_ID='B'"); selectQuery = myCaptureQueriesListener.getSelectQueriesForCurrentThread().get(1).getSql(true, false); - assertThat(StringUtils.countMatches(selectQuery.toLowerCase(), "select t1.res_id from hfj_resource t1")).as(selectQuery).isEqualTo(1); - assertThat(StringUtils.countMatches(selectQuery.toLowerCase(), "t1.res_type='observation'")).as(selectQuery).isEqualTo(0); - assertThat(StringUtils.countMatches(selectQuery.toLowerCase(), "t1.res_deleted_at is null")).as(selectQuery).isEqualTo(0); + assertThat(selectQuery).contains("where (rt1_0.RES_TYPE='Observation' and rt1_0.FHIR_ID='A')"); + assertEquals(4, myCaptureQueriesListener.countSelectQueriesForCurrentThread()); + } // Search by ID where at least one ID is a numeric ID { + myMemoryCacheService.invalidateAllCaches(); + SearchParameterMap map = SearchParameterMap.newSynchronous(); map.add("_id", new TokenOrListParam(null, "A", obs2id)); myCaptureQueriesListener.clear(); @@ -906,20 +909,19 @@ public class FhirResourceDaoR4SearchOptimizedTest extends BaseJpaR4Test { assertEquals(2, outcome.size()); assertThat(outcome.getResources(0, 999)).hasSize(2); myCaptureQueriesListener.logSelectQueriesForCurrentThread(); - String selectQuery = myCaptureQueriesListener.getSelectQueriesForCurrentThread().get(1).getSql(true, false); - assertThat(StringUtils.countMatches(selectQuery.toLowerCase(), "select t0.res_id from hfj_resource t0")).as(selectQuery).isEqualTo(1); - // Because we included a non-forced ID, we need to verify the type - assertThat(StringUtils.countMatches(selectQuery.toLowerCase(), "t0.res_type = 'observation'")).as(selectQuery).isEqualTo(1); - assertThat(StringUtils.countMatches(selectQuery.toLowerCase(), "t0.res_deleted_at is null")).as(selectQuery).isEqualTo(1); + String selectQuery = myCaptureQueriesListener.getSelectQueriesForCurrentThread().get(0).getSql(true, false); + assertThat(selectQuery).contains("where (rt1_0.RES_TYPE='Observation' and rt1_0.FHIR_ID='A' or rt1_0.RES_ID='3')"); + assertEquals(3, myCaptureQueriesListener.countSelectQueriesForCurrentThread()); } - // Delete the resource - There should only be one search performed because deleted resources will - // be filtered out in the query that resolves forced ids to persistent ids + // Delete the resource myObservationDao.delete(new IdType("Observation/A")); myObservationDao.delete(new IdType("Observation/" + obs2id)); // Search by ID where all IDs are forced IDs { + myMemoryCacheService.invalidateAllCaches(); + SearchParameterMap map = SearchParameterMap.newSynchronous(); map.add("_id", new TokenParam("A")); myCaptureQueriesListener.clear(); @@ -928,26 +930,9 @@ public class FhirResourceDaoR4SearchOptimizedTest extends BaseJpaR4Test { assertThat(outcome.getResources(0, 999)).isEmpty(); myCaptureQueriesListener.logSelectQueriesForCurrentThread(); - assertThat(myCaptureQueriesListener.getSelectQueriesForCurrentThread()).hasSize(1); String selectQuery = myCaptureQueriesListener.getSelectQueriesForCurrentThread().get(0).getSql(true, false); - assertThat(StringUtils.countMatches(selectQuery.toLowerCase(), "rt1_0.res_type='observation'")).as(selectQuery).isEqualTo(1); - assertThat(StringUtils.countMatches(selectQuery.toLowerCase(), "rt1_0.fhir_id='a'")).as(selectQuery).isEqualTo(1); - } - - // Search by ID where at least one ID is a numeric ID - { - SearchParameterMap map = SearchParameterMap.newSynchronous(); - map.add("_id", new TokenOrListParam(null, "A", obs2id)); - myCaptureQueriesListener.clear(); - IBundleProvider outcome = myObservationDao.search(map, new SystemRequestDetails()); - assertEquals(0, outcome.size()); - assertThat(outcome.getResources(0, 999)).isEmpty(); - myCaptureQueriesListener.logSelectQueriesForCurrentThread(); - String selectQuery = myCaptureQueriesListener.getSelectQueriesForCurrentThread().get(1).getSql(true, false); - assertThat(StringUtils.countMatches(selectQuery.toLowerCase(), "select t0.res_id from hfj_resource t0")).as(selectQuery).isEqualTo(1); - // Because we included a non-forced ID, we need to verify the type - assertThat(StringUtils.countMatches(selectQuery.toLowerCase(), "t0.res_type = 'observation'")).as(selectQuery).isEqualTo(1); - assertThat(StringUtils.countMatches(selectQuery.toLowerCase(), "t0.res_deleted_at is null")).as(selectQuery).isEqualTo(1); + assertThat(selectQuery).contains("where (rt1_0.RES_TYPE='Observation' and rt1_0.FHIR_ID='A')"); + assertEquals(2, myCaptureQueriesListener.countSelectQueriesForCurrentThread()); } } diff --git a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4Test.java b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4Test.java index 36a5a67c0bf..82c8ae17cb7 100644 --- a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4Test.java +++ b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4Test.java @@ -5,6 +5,7 @@ import ca.uhn.fhir.interceptor.model.RequestPartitionId; import ca.uhn.fhir.jpa.api.config.JpaStorageSettings; import ca.uhn.fhir.jpa.api.dao.IFhirResourceDao; import ca.uhn.fhir.jpa.api.dao.IFhirSystemDao; +import ca.uhn.fhir.jpa.api.model.DaoMethodOutcome; import ca.uhn.fhir.jpa.api.model.HistoryCountModeEnum; import ca.uhn.fhir.jpa.api.pid.StreamTemplate; import ca.uhn.fhir.jpa.dao.BaseHapiFhirDao; @@ -1090,16 +1091,28 @@ public class FhirResourceDaoR4Test extends BaseJpaR4Test { runInTransaction(() -> { Patient p = new Patient(); - p.setId(id); + p.setId(id.getIdPart()); p.setActive(true); p.addName().setFamily("FAMILY"); - myPatientDao.update(p); + IIdType update1Id = myPatientDao.update(p).getId(); + assertEquals("2", update1Id.getVersionIdPart()); p = new Patient(); - p.setId(id); + p.setId(id.getIdPart()); p.setActive(false); p.addName().setFamily("FAMILY2"); - myPatientDao.update(p); + IIdType update2Id = myPatientDao.update(p).getId(); + assertEquals("3", update2Id.getVersionIdPart()); + assertEquals(update1Id.getIdPart(), update2Id.getIdPart()); + }); + + runInTransaction(()->{ + List resourceTables = myResourceTableDao.findAll(); + assertEquals(1, resourceTables.size()); + assertEquals(2, resourceTables.get(0).getVersion()); + + List versions = myResourceHistoryTableDao.findAllVersionsForResourceIdInOrder(resourceTables.get(0).getResourceId()); + assertThat(versions.stream().map(ResourceHistoryTable::getVersion).toList()).asList().containsExactly(1L, 2L, 3L); }); assertEquals(1, myPatientDao.search(SearchParameterMap.newSynchronous("name", new StringParam("family2"))).size()); @@ -2770,7 +2783,7 @@ public class FhirResourceDaoR4Test extends BaseJpaR4Test { myPatientDao.read(new IdType("Patient/9999999999999/_history/1"), mySrd); fail(); } catch (ResourceNotFoundException e) { - assertEquals(Msg.code(1996) + "Resource Patient/9999999999999/_history/1 is not known", e.getMessage()); + assertEquals(Msg.code(2001) + "Resource Patient/9999999999999 is not known", e.getMessage()); } } diff --git a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/dao/r4/PartitioningSqlR4Test.java b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/dao/r4/PartitioningSqlR4Test.java index 7a7859b8894..737c764724b 100644 --- a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/dao/r4/PartitioningSqlR4Test.java +++ b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/dao/r4/PartitioningSqlR4Test.java @@ -169,9 +169,10 @@ public class PartitioningSqlR4Test extends BasePartitioningR4Test { List selectQueries = myCaptureQueriesListener.getSelectQueriesForCurrentThread(); assertEquals(1, selectQueries.size()); // Look up the referenced subject/patient - String sql = selectQueries.get(0).getSql(true, false).toLowerCase(); - assertThat(sql).contains(" from hfj_resource "); - assertEquals(2, StringUtils.countMatches(selectQueries.get(0).getSql(true, false).toLowerCase(), "partition")); + String searchSql = selectQueries.get(0).getSql(true, false); + assertThat(searchSql).contains(" from HFJ_RESOURCE "); + assertEquals(1, StringUtils.countMatches(searchSql, "PARTITION_ID from"), searchSql); + assertEquals(1, StringUtils.countMatches(searchSql, "PARTITION_ID"), searchSql); runInTransaction(() -> { List resLinks = myResourceLinkDao.findAll(); @@ -180,7 +181,6 @@ public class PartitioningSqlR4Test extends BasePartitioningR4Test { assertEquals(obsId.getIdPartAsLong(), resLinks.get(0).getSourceResourcePid()); assertEquals(patientId.getIdPartAsLong(), resLinks.get(0).getTargetResourcePid()); assertEquals(myPartitionId, resLinks.get(0).getTargetResourcePartitionId().getPartitionId()); - assertLocalDateFromDbMatches(myPartitionDate, resLinks.get(0).getTargetResourcePartitionId().getPartitionDate()); }); } @@ -466,7 +466,6 @@ public class PartitioningSqlR4Test extends BasePartitioningR4Test { assertEquals(myPartitionId, resourceLinks.get(0).getPartitionId().getPartitionId().intValue()); assertLocalDateFromDbMatches(myPartitionDate, resourceLinks.get(0).getPartitionId().getPartitionDate()); assertEquals(myPartitionId, resourceLinks.get(0).getTargetResourcePartitionId().getPartitionId().intValue()); - assertLocalDateFromDbMatches(myPartitionDate, resourceLinks.get(0).getTargetResourcePartitionId().getPartitionDate()); // HFJ_RES_PARAM_PRESENT List presents = mySearchParamPresentDao.findAllForResource(resourceTable); @@ -876,7 +875,7 @@ public class PartitioningSqlR4Test extends BasePartitioningR4Test { IdType gotId1 = myPatientDao.read(patientId1, mySrd).getIdElement().toUnqualifiedVersionless(); assertEquals(patientId1, gotId1); - String searchSql = myCaptureQueriesListener.getSelectQueriesForCurrentThread().get(0).getSql(true, true); + String searchSql = myCaptureQueriesListener.getSelectQueriesForCurrentThread().get(1).getSql(true, true); ourLog.info("Search SQL:\n{}", searchSql); // Only the read columns should be used, no criteria use partition @@ -888,7 +887,7 @@ public class PartitioningSqlR4Test extends BasePartitioningR4Test { IdType gotId2 = myPatientDao.read(patientId2, mySrd).getIdElement().toUnqualifiedVersionless(); assertEquals(patientId2, gotId2); - String searchSql = myCaptureQueriesListener.getSelectQueriesForCurrentThread().get(0).getSql(true, true); + String searchSql = myCaptureQueriesListener.getSelectQueriesForCurrentThread().get(1).getSql(true, true); ourLog.info("Search SQL:\n{}", searchSql); // Only the read columns should be used, no criteria use partition @@ -925,7 +924,7 @@ public class PartitioningSqlR4Test extends BasePartitioningR4Test { myPatientDao.read(patientIdNull, mySrd).getIdElement().toUnqualifiedVersionless(); fail(); } catch (ResourceNotFoundException e) { - assertThat(e.getMessage()).matches(Msg.code(1996) + "Resource Patient/[0-9]+ is not known"); + assertThat(e.getMessage()).matches(Msg.code(2001) + "Resource Patient/[0-9]+ is not known"); } } @@ -936,7 +935,7 @@ public class PartitioningSqlR4Test extends BasePartitioningR4Test { myPatientDao.read(patientId2, mySrd).getIdElement().toUnqualifiedVersionless(); fail(); } catch (ResourceNotFoundException e) { - assertThat(e.getMessage()).matches(Msg.code(1996) + "Resource Patient/[0-9]+ is not known"); + assertThat(e.getMessage()).matches(Msg.code(2001) + "Resource Patient/[0-9]+ is not known"); } } } @@ -948,6 +947,8 @@ public class PartitioningSqlR4Test extends BasePartitioningR4Test { createPatient(withPartition(2), withActiveTrue()); IIdType patientId3 = createPatient(withPartition(3), withActiveTrue()); + logAllResources(); + // Two partitions - Found { myCaptureQueriesListener.clear(); @@ -957,22 +958,30 @@ public class PartitioningSqlR4Test extends BasePartitioningR4Test { assertEquals(patientId1, gotId1); // Only the read columns should be used, but no selectors on partition ID - String searchSql = myCaptureQueriesListener.getSelectQueriesForCurrentThread().get(0).getSql(true, true); - assertEquals(1, StringUtils.countMatches(searchSql, "PARTITION_ID,"), searchSql); + String searchSql = myCaptureQueriesListener.getSelectQueriesForCurrentThread().get(0).getSql(true, false); + assertEquals(1, StringUtils.countMatches(searchSql, "PARTITION_ID from"), searchSql); assertEquals(1, StringUtils.countMatches(searchSql, "PARTITION_ID in ("), searchSql); + assertEquals(2, StringUtils.countMatches(searchSql, "PARTITION_ID"), searchSql); } // Two partitions including default - Found { myCaptureQueriesListener.clear(); myPartitionInterceptor.addReadPartition(RequestPartitionId.fromPartitionNames(PARTITION_1, JpaConstants.DEFAULT_PARTITION_NAME)); - IdType gotId1 = myPatientDao.read(patientIdNull, mySrd).getIdElement().toUnqualifiedVersionless(); + IdType gotId1; + try { + gotId1 = myPatientDao.read(patientIdNull, mySrd).getIdElement().toUnqualifiedVersionless(); + } finally { + myCaptureQueriesListener.logSelectQueries(); + } assertEquals(patientIdNull, gotId1); // Only the read columns should be used, but no selectors on partition ID - String searchSql = myCaptureQueriesListener.getSelectQueriesForCurrentThread().get(0).getSql(true, true); - assertEquals(1, StringUtils.countMatches(searchSql, "PARTITION_ID,"), searchSql); + String searchSql = myCaptureQueriesListener.getSelectQueriesForCurrentThread().get(0).getSql(true, false); + assertEquals(1, StringUtils.countMatches(searchSql, "PARTITION_ID from"), searchSql); + assertEquals(1, StringUtils.countMatches(searchSql, "PARTITION_ID in ("), searchSql); assertEquals(1, StringUtils.countMatches(searchSql, "PARTITION_ID is null"), searchSql); + assertEquals(3, StringUtils.countMatches(searchSql, "PARTITION_ID"), searchSql); } // Two partitions - Not Found @@ -1010,10 +1019,15 @@ public class PartitioningSqlR4Test extends BasePartitioningR4Test { IdType gotId1 = myPatientDao.read(patientId1, mySrd).getIdElement().toUnqualifiedVersionless(); assertEquals(patientId1, gotId1); - // Only the read columns should be used, but no selectors on partition ID - String searchSql = myCaptureQueriesListener.getSelectQueriesForCurrentThread().get(0).getSql(true, true); + String resolveIdentitySql = myCaptureQueriesListener.getSelectQueriesForCurrentThread().get(0).getSql(true, false); + assertEquals(1, StringUtils.countMatches(resolveIdentitySql, "PARTITION_ID from"), resolveIdentitySql); + assertEquals(1, StringUtils.countMatches(resolveIdentitySql, "PARTITION_ID in ("), resolveIdentitySql); + assertEquals(2, StringUtils.countMatches(resolveIdentitySql, "PARTITION_ID"), resolveIdentitySql); + + String searchSql = myCaptureQueriesListener.getSelectQueriesForCurrentThread().get(1).getSql(true, false); assertEquals(1, StringUtils.countMatches(searchSql, "PARTITION_ID,"), searchSql); assertEquals(1, StringUtils.countMatches(searchSql, "PARTITION_ID in ("), searchSql); + assertEquals(2, StringUtils.countMatches(searchSql, "PARTITION_ID"), searchSql); } // Two partitions including default - Found @@ -1025,7 +1039,7 @@ public class PartitioningSqlR4Test extends BasePartitioningR4Test { assertEquals(patientIdNull, gotId1); // Only the read columns should be used, but no selectors on partition ID - String searchSql = myCaptureQueriesListener.getSelectQueriesForCurrentThread().get(0).getSql(true, true); + String searchSql = myCaptureQueriesListener.getSelectQueriesForCurrentThread().get(1).getSql(true, true); assertEquals(1, StringUtils.countMatches(searchSql, "PARTITION_ID,"), searchSql); assertEquals(1, StringUtils.countMatches(searchSql, "PARTITION_ID is null"), searchSql); } @@ -1079,7 +1093,7 @@ public class PartitioningSqlR4Test extends BasePartitioningR4Test { myPatientDao.read(patientId1, mySrd).getIdElement().toUnqualifiedVersionless(); fail(); } catch (ResourceNotFoundException e) { - assertThat(e.getMessage()).matches(Msg.code(1996) + "Resource Patient/[0-9]+ is not known"); + assertThat(e.getMessage()).matches(Msg.code(2001) + "Resource Patient/[0-9]+ is not known"); } } } @@ -1269,6 +1283,7 @@ public class PartitioningSqlR4Test extends BasePartitioningR4Test { IIdType gotId1 = searchOutcome.getResources(0, 1).get(0).getIdElement().toUnqualifiedVersionless(); assertEquals(patientId1, gotId1); + myCaptureQueriesListener.logSelectQueries(); String searchSql = myCaptureQueriesListener.getSelectQueriesForCurrentThread().get(0).getSql(true, false); ourLog.info("Search SQL:\n{}", searchSql); @@ -1321,6 +1336,7 @@ public class PartitioningSqlR4Test extends BasePartitioningR4Test { IIdType gotId1 = searchOutcome.getResources(0, 1).get(0).getIdElement().toUnqualifiedVersionless(); assertEquals(patientId1, gotId1); + myCaptureQueriesListener.logSelectQueries(); String searchSql = myCaptureQueriesListener.getSelectQueriesForCurrentThread().get(0).getSql(true, false); ourLog.info("Search SQL:\n{}", searchSql); @@ -1380,7 +1396,7 @@ public class PartitioningSqlR4Test extends BasePartitioningR4Test { ourLog.info("Search SQL:\n{}", searchSql); // Only the read columns should be used, no criteria use partition - assertThat(searchSql).as(searchSql).contains("PARTITION_ID='1'"); + assertThat(searchSql).as(searchSql).contains("PARTITION_ID = '1'"); assertThat(StringUtils.countMatches(searchSql, "PARTITION_ID")).as(searchSql).isEqualTo(2); } @@ -1429,13 +1445,13 @@ public class PartitioningSqlR4Test extends BasePartitioningR4Test { assertEquals(1, searchOutcome.size()); IIdType gotId1 = searchOutcome.getResources(0, 1).get(0).getIdElement().toUnqualifiedVersionless(); assertEquals(patientId1, gotId1); + myCaptureQueriesListener.logSelectQueries(); // Performs the search String searchSql = myCaptureQueriesListener.getSelectQueriesForCurrentThread().get(0).getSql(true, false).toUpperCase(); ourLog.info("Search SQL:\n{}", searchSql); - assertThat(searchSql).as(searchSql).contains("FROM HFJ_RESOURCE RT1_0 WHERE"); - assertThat(searchSql).as(searchSql).contains("PARTITION_ID='1'"); - assertThat(StringUtils.countMatches(searchSql, "PARTITION_ID")).as(searchSql).isEqualTo(2); + assertThat(searchSql).as(searchSql).contains("FROM HFJ_RESOURCE "); + assertThat(searchSql).as(searchSql).contains("PARTITION_ID = '1'"); } // Read in null Partition @@ -2088,7 +2104,7 @@ public class PartitioningSqlR4Test extends BasePartitioningR4Test { String searchSql = myCaptureQueriesListener.getSelectQueriesForCurrentThread().get(0).getSql(true, false); ourLog.info("Search SQL:\n{}", searchSql); - assertThat(searchSql).as(searchSql).contains("PARTITION_ID='1'"); + assertThat(searchSql).as(searchSql).contains("PARTITION_ID = '1'"); assertThat(StringUtils.countMatches(searchSql, "PARTITION_ID")).as(searchSql).isEqualTo(2); } diff --git a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/interceptor/PatientCompartmentEnforcingInterceptorTest.java b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/interceptor/PatientCompartmentEnforcingInterceptorTest.java index 4c5f49942a7..9f65a4f9993 100644 --- a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/interceptor/PatientCompartmentEnforcingInterceptorTest.java +++ b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/interceptor/PatientCompartmentEnforcingInterceptorTest.java @@ -43,20 +43,25 @@ public class PatientCompartmentEnforcingInterceptorTest extends BaseResourceProv myPartitionSettings.setDefaultPartitionId(ALTERNATE_DEFAULT_ID); } + @Override @AfterEach - public void after() { + public void after() throws Exception { + super.after(); myInterceptorRegistry.unregisterInterceptor(myPatientIdPartitionInterceptor); myInterceptorRegistry.unregisterInterceptor(myForceOffsetSearchModeInterceptor); myInterceptorRegistry.unregisterInterceptor(mySvc); myPartitionSettings.setPartitioningEnabled(false); - myPartitionSettings.setUnnamedPartitionMode(new PartitionSettings().isUnnamedPartitionMode()); - myPartitionSettings.setDefaultPartitionId(new PartitionSettings().getDefaultPartitionId()); + PartitionSettings defaultPartitionSettings = new PartitionSettings(); + myPartitionSettings.setUnnamedPartitionMode(defaultPartitionSettings.isUnnamedPartitionMode()); + myPartitionSettings.setDefaultPartitionId(defaultPartitionSettings.getDefaultPartitionId()); + myPartitionSettings.setAllowReferencesAcrossPartitions(defaultPartitionSettings.getAllowReferencesAcrossPartitions()); } @Test public void testUpdateResource_whenCrossingPatientCompartment_throws() { + myPartitionSettings.setAllowReferencesAcrossPartitions(PartitionSettings.CrossPartitionReferenceMode.ALLOWED_UNQUALIFIED); createPatientA(); createPatientB(); diff --git a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/provider/r4/MultitenantServerR4Test.java b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/provider/r4/MultitenantServerR4Test.java index f9b18495041..c01fa52f1b7 100644 --- a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/provider/r4/MultitenantServerR4Test.java +++ b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/provider/r4/MultitenantServerR4Test.java @@ -531,7 +531,8 @@ public class MultitenantServerR4Test extends BaseMultitenantResourceProviderR4Te myPatientDao.update((Patient) patientA, requestDetails); fail(); } catch (InvalidRequestException e) { - assertEquals(Msg.code(2079) + "Resource " + ((Patient) patientA).getResourceType() + "/" + ((Patient) patientA).getIdElement().getIdPart() + " is not known", e.getMessage()); + String idPart = ((Patient) patientA).getIdElement().getIdPart(); + assertThat(e.getMessage()).contains("HAPI-0960: Can not create resource with ID[" + idPart + "]"); } } diff --git a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderRevIncludeTest.java b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderRevIncludeTest.java index d531251f661..3fc95980017 100644 --- a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderRevIncludeTest.java +++ b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderRevIncludeTest.java @@ -145,7 +145,7 @@ public class ResourceProviderRevIncludeTest extends BaseResourceProviderR4Test { //Ensure that the revincludes are included in the query list of the sql trace. //TODO GGG/KHS reduce this to something less than 6 by smarter iterating and getting the resource types earlier when needed. - assertThat(sqlCapturingInterceptor.getQueryList()).hasSize(5); + assertThat(sqlCapturingInterceptor.getQueryList()).hasSize(6); myInterceptorRegistry.unregisterInterceptor(sqlCapturingInterceptor); } diff --git a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/stresstest/GiantTransactionPerfTest.java b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/stresstest/GiantTransactionPerfTest.java index 9138ed0095d..081492fb569 100644 --- a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/stresstest/GiantTransactionPerfTest.java +++ b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/stresstest/GiantTransactionPerfTest.java @@ -292,11 +292,11 @@ public class GiantTransactionPerfTest { ourLog.info("Merges:\n * " + myEntityManager.myMergeCount.stream().map(t->t.toString()).collect(Collectors.joining("\n * "))); - assertThat(myEntityManager.myPersistCount.stream().map(t -> t.getClass().getSimpleName()).collect(Collectors.toList())).containsExactly("ResourceTable"); + assertThat(myEntityManager.myPersistCount.stream().map(t -> t.getClass().getSimpleName()).collect(Collectors.toList())).containsExactly("ResourceTable", "ResourceHistoryTable"); assertThat(myEntityManager.myMergeCount.stream().map(t -> t.getClass().getSimpleName()).collect(Collectors.toList())).containsExactlyInAnyOrder("ResourceTable", "ResourceIndexedSearchParamToken", "ResourceIndexedSearchParamToken"); assertEquals(1, myEntityManager.myFlushCount); assertEquals(1, myResourceVersionSvc.myGetVersionMap); - assertEquals(1, myResourceHistoryTableDao.mySaveCount); + assertEquals(0, myResourceHistoryTableDao.mySaveCount); } @Test diff --git a/hapi-fhir-jpaserver-test-r5/src/test/java/ca/uhn/fhir/jpa/dao/r5/FhirSystemDaoTransactionR5Test.java b/hapi-fhir-jpaserver-test-r5/src/test/java/ca/uhn/fhir/jpa/dao/r5/FhirSystemDaoTransactionR5Test.java index e9580c7aaff..63e6ab0b518 100644 --- a/hapi-fhir-jpaserver-test-r5/src/test/java/ca/uhn/fhir/jpa/dao/r5/FhirSystemDaoTransactionR5Test.java +++ b/hapi-fhir-jpaserver-test-r5/src/test/java/ca/uhn/fhir/jpa/dao/r5/FhirSystemDaoTransactionR5Test.java @@ -647,10 +647,17 @@ public class FhirSystemDaoTransactionR5Test extends BaseJpaR5Test { assertEquals("http://foo", actual.getIdentifierFirstRep().getSystem()); assertEquals("http://tag", actual.getMeta().getTagFirstRep().getSystem()); + logAllResources(); + logAllResourceVersions(); + // Second pass (resource already exists) myCaptureQueriesListener.clear(); - outcome = mySystemDao.transaction(mySrd, createBundleWithDeleteAndUpdateOnSameResource(myFhirContext)); + try { + outcome = mySystemDao.transaction(mySrd, createBundleWithDeleteAndUpdateOnSameResource(myFhirContext)); + } finally { + myCaptureQueriesListener.logInsertQueries(); + } myCaptureQueriesListener.logUpdateQueries(); ourLog.info(myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(outcome)); assertEquals("Patient/P/_history/2", outcome.getEntry().get(0).getResponse().getLocation()); @@ -661,9 +668,11 @@ public class FhirSystemDaoTransactionR5Test extends BaseJpaR5Test { logAllResources(); logAllResourceVersions(); - assertThrows(ResourceGoneException.class, () -> myPatientDao.read(resourceId.withVersion("2"), mySrd)); + assertThrows(ResourceGoneException.class, ()->myPatientDao.read(resourceId.withVersion("2"), mySrd)); actual = myPatientDao.read(resourceId.withVersion("3"), mySrd); assertEquals("3", actual.getIdElement().getVersionIdPart()); + actual = myPatientDao.read(resourceId.toVersionless(), mySrd); + assertEquals("3", actual.getIdElement().getVersionIdPart()); assertEquals("http://foo", actual.getIdentifierFirstRep().getSystem()); assertEquals("http://tag", actual.getMeta().getTagFirstRep().getSystem()); @@ -676,7 +685,6 @@ public class FhirSystemDaoTransactionR5Test extends BaseJpaR5Test { assertEquals("Patient/P/_history/5", outcome.getEntry().get(1).getResponse().getLocation()); assertEquals("201 Created", outcome.getEntry().get(1).getResponse().getStatus()); - assertThrows(ResourceGoneException.class, () -> myPatientDao.read(resourceId.withVersion("4"), mySrd)); actual = myPatientDao.read(resourceId.withVersion("5"), mySrd); assertEquals("5", actual.getIdElement().getVersionIdPart()); assertEquals("http://foo", actual.getIdentifierFirstRep().getSystem()); @@ -695,12 +703,12 @@ public class FhirSystemDaoTransactionR5Test extends BaseJpaR5Test { bb.addTransactionDeleteEntry(new IdType("Patient/P")); bb.addTransactionUpdateEntry(createPatientWithIdentifierAndTag().setActive(false)).conditional("Patient?identifier=http://foo|bar"); Bundle outcome = mySystemDao.transaction(mySrd, bb.getBundleTyped()); + ourLog.info(myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(outcome)); assertThat(outcome.getEntry().get(0).getResponse().getLocation()).endsWith("_history/2"); assertThat(outcome.getEntry().get(1).getResponse().getLocation()).endsWith("_history/3"); // Verify - assertThrows(ResourceGoneException.class, ()->myPatientDao.read(new IdType("Patient/P/_history/2"), mySrd)); Patient patient = myPatientDao.read(new IdType("Patient/P"), mySrd); assertFalse(patient.getActive()); assertEquals("3", patient.getIdElement().getVersionIdPart());