Test fixes

This commit is contained in:
James Agnew 2024-11-20 19:39:41 -05:00
parent 8996a86fab
commit bc32b087d2
19 changed files with 224 additions and 213 deletions

View File

@ -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."

View File

@ -1319,7 +1319,7 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> extends BaseStora
* the previous version entity.
*/
if (historyEntry == null) {
historyEntry = theEntity.toHistory(versionedTags);
historyEntry = theEntity.toHistory(versionedTags && theEntity.getDeleted() == null);
}
historyEntry.setEncoding(theChanged.getEncoding());

View File

@ -1788,10 +1788,6 @@ public abstract class BaseHapiFhirResourceDao<T extends IBaseResource> 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)

View File

@ -192,7 +192,7 @@ public abstract class BaseHapiFhirSystemDao<T extends IBaseBundle, MT> extends B
HapiTransactionService.requireTransaction();
List<Long> pids = theResolvedIds.stream().map(t -> ((JpaPid) t).getId()).collect(Collectors.toList());
new QueryChunker<Long>().chunk(pids, idChunk -> {
QueryChunker.chunk(pids, idChunk -> {
/*
* Pre-fetch the resources we're touching in this transaction in mass - this reduced the

View File

@ -599,18 +599,28 @@ public class IdHelperService implements IIdHelperService<JpaPid> {
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<Integer> 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();

View File

@ -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<JpaPid> allOrPids = null;
SearchFilterParser.CompareOperation defaultOperation = SearchFilterParser.CompareOperation.eq;
boolean allIdsAreForcedIds = true;
for (List<? extends IQueryParameterType> nextValue : theValues) {
Set<JpaPid> orPids = new HashSet<>();
Set<IIdType> 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<JpaPid> 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<IIdType, IResourceLookup<JpaPid>> resolvedPids = myIdHelperService.resolveResourceIdentities(
theRequestPartitionId,
ids,
ResolveIdentityMode.includeDeleted().cacheOk());
for (IResourceLookup<JpaPid> lookup : resolvedPids.values()) {
orPids.add(lookup.getPersistentId());
}
if (haveValue) {
if (allOrPids == null) {
allOrPids = orPids;
@ -127,7 +131,7 @@ public class ResourceIdPredicateBuilder extends BasePredicateBuilder {
List<Long> resourceIds = JpaPid.toLongList(allOrPids);
if (theSourceJoinColumn == null) {
BaseJoiningPredicateBuilder queryRootTable = super.getOrCreateQueryRootTable(!allIdsAreForcedIds);
BaseJoiningPredicateBuilder queryRootTable = super.getOrCreateQueryRootTable(true);
Condition predicate;
switch (operation) {
default:

View File

@ -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;
}

View File

@ -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";

View File

@ -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());
}
}

View File

@ -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<String> 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<String> 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());
}

View File

@ -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<SqlQuery> 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<SqlQuery> 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<SqlQuery> 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;

View File

@ -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());
}
}

View File

@ -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<ResourceTable> resourceTables = myResourceTableDao.findAll();
assertEquals(1, resourceTables.size());
assertEquals(2, resourceTables.get(0).getVersion());
List<ResourceHistoryTable> 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());
}
}

View File

@ -169,9 +169,10 @@ public class PartitioningSqlR4Test extends BasePartitioningR4Test {
List<SqlQuery> 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<ResourceLink> 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<SearchParamPresentEntity> 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);
}

View File

@ -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();

View File

@ -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 + "]");
}
}

View File

@ -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);
}

View File

@ -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

View File

@ -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());