Avoid a crash on double date chains (#2097)

* Avoid a crash on double date chains

* Add changelog

* Address coverage warning
This commit is contained in:
James Agnew 2020-09-21 15:43:07 -04:00 committed by GitHub
parent 631556041c
commit 072e63be5a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 199 additions and 6 deletions

View File

@ -0,0 +1,5 @@
---
type: fix
issue: 2097
title: "A crash in the JPA server was fixed when performing a search containined two chained search parameters to
date target types."

View File

@ -49,7 +49,6 @@ import java.util.Map;
public class PredicateBuilderDate extends BasePredicateBuilder implements IPredicateBuilder { public class PredicateBuilderDate extends BasePredicateBuilder implements IPredicateBuilder {
private static final Logger ourLog = LoggerFactory.getLogger(PredicateBuilderDate.class); private static final Logger ourLog = LoggerFactory.getLogger(PredicateBuilderDate.class);
private Map<String, From<?, ResourceIndexedSearchParamDate>> myJoinMap;
PredicateBuilderDate(SearchBuilder theSearchBuilder) { PredicateBuilderDate(SearchBuilder theSearchBuilder) {
super(theSearchBuilder); super(theSearchBuilder);
@ -64,15 +63,15 @@ public class PredicateBuilderDate extends BasePredicateBuilder implements IPredi
String paramName = theSearchParam.getName(); String paramName = theSearchParam.getName();
boolean newJoin = false; boolean newJoin = false;
if (myJoinMap == null) {
myJoinMap = new HashMap<>(); Map<String, From<?, ResourceIndexedSearchParamDate>> joinMap = myQueryStack.getJoinMap();
}
String key = theResourceName + " " + paramName; String key = theResourceName + " " + paramName;
From<?, ResourceIndexedSearchParamDate> join = myJoinMap.get(key); From<?, ResourceIndexedSearchParamDate> join = joinMap.get(key);
if (join == null) { if (join == null) {
join = myQueryStack.createJoin(SearchBuilderJoinEnum.DATE, paramName); join = myQueryStack.createJoin(SearchBuilderJoinEnum.DATE, paramName);
myJoinMap.put(key, join); joinMap.put(key, join);
newJoin = true; newJoin = true;
} }

View File

@ -23,6 +23,7 @@ package ca.uhn.fhir.jpa.dao.predicate.querystack;
import ca.uhn.fhir.jpa.dao.predicate.IndexJoins; import ca.uhn.fhir.jpa.dao.predicate.IndexJoins;
import ca.uhn.fhir.jpa.dao.predicate.SearchBuilderJoinEnum; import ca.uhn.fhir.jpa.dao.predicate.SearchBuilderJoinEnum;
import ca.uhn.fhir.jpa.dao.predicate.SearchBuilderJoinKey; import ca.uhn.fhir.jpa.dao.predicate.SearchBuilderJoinKey;
import ca.uhn.fhir.jpa.model.entity.ResourceIndexedSearchParamDate;
import javax.persistence.criteria.AbstractQuery; import javax.persistence.criteria.AbstractQuery;
import javax.persistence.criteria.CriteriaBuilder; import javax.persistence.criteria.CriteriaBuilder;
@ -37,7 +38,9 @@ import javax.persistence.criteria.Subquery;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
import java.util.Date; import java.util.Date;
import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map;
import java.util.Optional; import java.util.Optional;
abstract class QueryRootEntry { abstract class QueryRootEntry {
@ -45,6 +48,7 @@ abstract class QueryRootEntry {
private final IndexJoins myIndexJoins = new IndexJoins(); private final IndexJoins myIndexJoins = new IndexJoins();
private final CriteriaBuilder myCriteriaBuilder; private final CriteriaBuilder myCriteriaBuilder;
private boolean myHasImplicitTypeSelection; private boolean myHasImplicitTypeSelection;
private Map<String, From<?, ResourceIndexedSearchParamDate>> myJoinMap;
QueryRootEntry(CriteriaBuilder theCriteriaBuilder) { QueryRootEntry(CriteriaBuilder theCriteriaBuilder) {
myCriteriaBuilder = theCriteriaBuilder; myCriteriaBuilder = theCriteriaBuilder;
@ -108,6 +112,15 @@ abstract class QueryRootEntry {
return getQueryRoot(); return getQueryRoot();
} }
public Map<String, From<?, ResourceIndexedSearchParamDate>> getJoinMap() {
Map<String, From<?, ResourceIndexedSearchParamDate>> retVal = myJoinMap;
if (retVal==null) {
retVal = new HashMap<>();
myJoinMap = retVal;
}
return retVal;
}
abstract void orderBy(List<Order> theOrders); abstract void orderBy(List<Order> theOrders);
abstract Expression<Date> getLastUpdatedColumn(); abstract Expression<Date> getLastUpdatedColumn();

View File

@ -23,6 +23,7 @@ package ca.uhn.fhir.jpa.dao.predicate.querystack;
import ca.uhn.fhir.interceptor.model.RequestPartitionId; import ca.uhn.fhir.interceptor.model.RequestPartitionId;
import ca.uhn.fhir.jpa.dao.predicate.SearchBuilderJoinEnum; import ca.uhn.fhir.jpa.dao.predicate.SearchBuilderJoinEnum;
import ca.uhn.fhir.jpa.dao.predicate.SearchBuilderJoinKey; import ca.uhn.fhir.jpa.dao.predicate.SearchBuilderJoinKey;
import ca.uhn.fhir.jpa.model.entity.ResourceIndexedSearchParamDate;
import ca.uhn.fhir.jpa.searchparam.SearchParameterMap; import ca.uhn.fhir.jpa.searchparam.SearchParameterMap;
import org.apache.commons.lang3.Validate; import org.apache.commons.lang3.Validate;
@ -38,6 +39,7 @@ import javax.persistence.criteria.Root;
import javax.persistence.criteria.Subquery; import javax.persistence.criteria.Subquery;
import java.util.Date; import java.util.Date;
import java.util.List; import java.util.List;
import java.util.Map;
import java.util.Optional; import java.util.Optional;
import java.util.Stack; import java.util.Stack;
@ -281,4 +283,9 @@ public class QueryStack {
public Predicate addNeverMatchingPredicate() { public Predicate addNeverMatchingPredicate() {
return top().addNeverMatchingPredicate(); return top().addNeverMatchingPredicate();
} }
public Map<String, From<?, ResourceIndexedSearchParamDate>> getJoinMap() {
return top().getJoinMap();
}
} }

View File

@ -72,6 +72,7 @@ import org.hl7.fhir.r4.model.CodeSystem;
import org.hl7.fhir.r4.model.CodeType; import org.hl7.fhir.r4.model.CodeType;
import org.hl7.fhir.r4.model.CodeableConcept; import org.hl7.fhir.r4.model.CodeableConcept;
import org.hl7.fhir.r4.model.Coding; import org.hl7.fhir.r4.model.Coding;
import org.hl7.fhir.r4.model.Communication;
import org.hl7.fhir.r4.model.CommunicationRequest; import org.hl7.fhir.r4.model.CommunicationRequest;
import org.hl7.fhir.r4.model.Condition; import org.hl7.fhir.r4.model.Condition;
import org.hl7.fhir.r4.model.ContactPoint.ContactPointSystem; import org.hl7.fhir.r4.model.ContactPoint.ContactPointSystem;
@ -5080,6 +5081,34 @@ public class FhirResourceDaoR4SearchNoFtTest extends BaseJpaR4Test {
assertThat(toUnqualifiedVersionlessIdValues(outcome), contains(crId)); assertThat(toUnqualifiedVersionlessIdValues(outcome), contains(crId));
} }
@Test
public void testSearchWithTwoChainedDates() {
// Matches
Encounter e1 = new Encounter();
e1.setPeriod(new Period().setStartElement(new DateTimeType("2020-09-14T12:00:00Z")).setEndElement(new DateTimeType("2020-09-14T12:00:00Z")));
String e1Id = myEncounterDao.create(e1).getId().toUnqualifiedVersionless().getValue();
Communication c1 = new Communication();
c1.getEncounter().setReference(e1Id);
myCommunicationDao.create(c1);
// Doesn't match
Encounter e2 = new Encounter();
e2.setPeriod(new Period().setStartElement(new DateTimeType("2020-02-14T12:00:00Z")).setEndElement(new DateTimeType("2020-02-14T12:00:00Z")));
String e2Id = myEncounterDao.create(e2).getId().toUnqualifiedVersionless().getValue();
Communication c2 = new Communication();
c2.getEncounter().setReference(e2Id);
myCommunicationDao.create(c2);
SearchParameterMap map = SearchParameterMap.newSynchronous();
map.add(Communication.SP_ENCOUNTER, new ReferenceParam("ge2020-09-14").setChain("date"));
map.add(Communication.SP_ENCOUNTER, new ReferenceParam("le2020-09-15").setChain("date"));
IBundleProvider outcome = myCommunicationDao.search(map);
assertEquals(1, outcome.sizeOrThrowNpe());
}
@Test @Test
public void testCircularReferencesDontBreakRevIncludes() { public void testCircularReferencesDontBreakRevIncludes() {

View File

@ -1,5 +1,25 @@
package ca.uhn.fhir.rest.server.messaging; package ca.uhn.fhir.rest.server.messaging;
/*-
* #%L
* HAPI FHIR - Server Framework
* %%
* Copyright (C) 2014 - 2020 University Health Network
* %%
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* #L%
*/
import ca.uhn.fhir.model.api.IModelJson; import ca.uhn.fhir.model.api.IModelJson;

View File

@ -1,5 +1,25 @@
package ca.uhn.fhir.rest.server.messaging; package ca.uhn.fhir.rest.server.messaging;
/*-
* #%L
* HAPI FHIR - Server Framework
* %%
* Copyright (C) 2014 - 2020 University Health Network
* %%
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* #L%
*/
import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.model.api.IModelJson; import ca.uhn.fhir.model.api.IModelJson;
import ca.uhn.fhir.parser.IParser; import ca.uhn.fhir.parser.IParser;

View File

@ -1,5 +1,25 @@
package ca.uhn.fhir.rest.server.messaging; package ca.uhn.fhir.rest.server.messaging;
/*-
* #%L
* HAPI FHIR - Server Framework
* %%
* Copyright (C) 2014 - 2020 University Health Network
* %%
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* #L%
*/
public interface IResourceMessage { public interface IResourceMessage {

View File

@ -1,5 +1,25 @@
package ca.uhn.fhir.rest.server.messaging; package ca.uhn.fhir.rest.server.messaging;
/*-
* #%L
* HAPI FHIR - Server Framework
* %%
* Copyright (C) 2014 - 2020 University Health Network
* %%
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* #L%
*/
import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.rest.api.server.RequestDetails; import ca.uhn.fhir.rest.api.server.RequestDetails;
import org.hl7.fhir.instance.model.api.IBaseResource; import org.hl7.fhir.instance.model.api.IBaseResource;

View File

@ -1,5 +1,25 @@
package ca.uhn.fhir.rest.server.messaging.json; package ca.uhn.fhir.rest.server.messaging.json;
/*-
* #%L
* HAPI FHIR - Server Framework
* %%
* Copyright (C) 2014 - 2020 University Health Network
* %%
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* #L%
*/
import ca.uhn.fhir.model.api.IModelJson; import ca.uhn.fhir.model.api.IModelJson;
import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.annotation.JsonProperty;

View File

@ -1,5 +1,25 @@
package ca.uhn.fhir.rest.server.messaging.json; package ca.uhn.fhir.rest.server.messaging.json;
/*-
* #%L
* HAPI FHIR - Server Framework
* %%
* Copyright (C) 2014 - 2020 University Health Network
* %%
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* #L%
*/
import ca.uhn.fhir.model.api.IModelJson; import ca.uhn.fhir.model.api.IModelJson;
import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.annotation.JsonProperty;
import org.springframework.messaging.MessageHeaders; import org.springframework.messaging.MessageHeaders;

View File

@ -1,5 +1,25 @@
package ca.uhn.fhir.rest.server.messaging.json; package ca.uhn.fhir.rest.server.messaging.json;
/*-
* #%L
* HAPI FHIR - Server Framework
* %%
* Copyright (C) 2014 - 2020 University Health Network
* %%
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* #L%
*/
import ca.uhn.fhir.rest.server.messaging.ResourceOperationMessage; import ca.uhn.fhir.rest.server.messaging.ResourceOperationMessage;
import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.annotation.JsonProperty;
import org.apache.commons.lang3.builder.ToStringBuilder; import org.apache.commons.lang3.builder.ToStringBuilder;