diff --git a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/5_2_0/2097-avoid-crash-on-double-chained-date.yaml b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/5_2_0/2097-avoid-crash-on-double-chained-date.yaml new file mode 100644 index 00000000000..e4f44a4b5e0 --- /dev/null +++ b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/5_2_0/2097-avoid-crash-on-double-chained-date.yaml @@ -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." diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/predicate/PredicateBuilderDate.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/predicate/PredicateBuilderDate.java index 100927c2300..4d021a15785 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/predicate/PredicateBuilderDate.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/predicate/PredicateBuilderDate.java @@ -49,7 +49,6 @@ import java.util.Map; public class PredicateBuilderDate extends BasePredicateBuilder implements IPredicateBuilder { private static final Logger ourLog = LoggerFactory.getLogger(PredicateBuilderDate.class); - private Map> myJoinMap; PredicateBuilderDate(SearchBuilder theSearchBuilder) { super(theSearchBuilder); @@ -64,15 +63,15 @@ public class PredicateBuilderDate extends BasePredicateBuilder implements IPredi String paramName = theSearchParam.getName(); boolean newJoin = false; - if (myJoinMap == null) { - myJoinMap = new HashMap<>(); - } + + Map> joinMap = myQueryStack.getJoinMap(); String key = theResourceName + " " + paramName; - From join = myJoinMap.get(key); + From join = joinMap.get(key); + if (join == null) { join = myQueryStack.createJoin(SearchBuilderJoinEnum.DATE, paramName); - myJoinMap.put(key, join); + joinMap.put(key, join); newJoin = true; } diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/predicate/querystack/QueryRootEntry.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/predicate/querystack/QueryRootEntry.java index c70f9be97b4..fab4da10c6f 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/predicate/querystack/QueryRootEntry.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/predicate/querystack/QueryRootEntry.java @@ -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.SearchBuilderJoinEnum; 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.CriteriaBuilder; @@ -37,7 +38,9 @@ import javax.persistence.criteria.Subquery; import java.util.ArrayList; import java.util.Collections; import java.util.Date; +import java.util.HashMap; import java.util.List; +import java.util.Map; import java.util.Optional; abstract class QueryRootEntry { @@ -45,6 +48,7 @@ abstract class QueryRootEntry { private final IndexJoins myIndexJoins = new IndexJoins(); private final CriteriaBuilder myCriteriaBuilder; private boolean myHasImplicitTypeSelection; + private Map> myJoinMap; QueryRootEntry(CriteriaBuilder theCriteriaBuilder) { myCriteriaBuilder = theCriteriaBuilder; @@ -108,6 +112,15 @@ abstract class QueryRootEntry { return getQueryRoot(); } + public Map> getJoinMap() { + Map> retVal = myJoinMap; + if (retVal==null) { + retVal = new HashMap<>(); + myJoinMap = retVal; + } + return retVal; + } + abstract void orderBy(List theOrders); abstract Expression getLastUpdatedColumn(); diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/predicate/querystack/QueryStack.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/predicate/querystack/QueryStack.java index 9adad4a92de..635d1966acb 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/predicate/querystack/QueryStack.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/predicate/querystack/QueryStack.java @@ -23,6 +23,7 @@ package ca.uhn.fhir.jpa.dao.predicate.querystack; import ca.uhn.fhir.interceptor.model.RequestPartitionId; import ca.uhn.fhir.jpa.dao.predicate.SearchBuilderJoinEnum; import ca.uhn.fhir.jpa.dao.predicate.SearchBuilderJoinKey; +import ca.uhn.fhir.jpa.model.entity.ResourceIndexedSearchParamDate; import ca.uhn.fhir.jpa.searchparam.SearchParameterMap; import org.apache.commons.lang3.Validate; @@ -38,6 +39,7 @@ import javax.persistence.criteria.Root; import javax.persistence.criteria.Subquery; import java.util.Date; import java.util.List; +import java.util.Map; import java.util.Optional; import java.util.Stack; @@ -281,4 +283,9 @@ public class QueryStack { public Predicate addNeverMatchingPredicate() { return top().addNeverMatchingPredicate(); } + + public Map> getJoinMap() { + return top().getJoinMap(); + } + } diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4SearchNoFtTest.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4SearchNoFtTest.java index 4f62e20710c..51cd7e0a579 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4SearchNoFtTest.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4SearchNoFtTest.java @@ -72,6 +72,7 @@ import org.hl7.fhir.r4.model.CodeSystem; import org.hl7.fhir.r4.model.CodeType; import org.hl7.fhir.r4.model.CodeableConcept; 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.Condition; import org.hl7.fhir.r4.model.ContactPoint.ContactPointSystem; @@ -5080,6 +5081,34 @@ public class FhirResourceDaoR4SearchNoFtTest extends BaseJpaR4Test { 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 public void testCircularReferencesDontBreakRevIncludes() { diff --git a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/messaging/BaseResourceMessage.java b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/messaging/BaseResourceMessage.java index 3d555b34236..8b9241b1ba6 100644 --- a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/messaging/BaseResourceMessage.java +++ b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/messaging/BaseResourceMessage.java @@ -1,5 +1,25 @@ 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; diff --git a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/messaging/BaseResourceModifiedMessage.java b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/messaging/BaseResourceModifiedMessage.java index a3b9303cebe..d98402938d8 100644 --- a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/messaging/BaseResourceModifiedMessage.java +++ b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/messaging/BaseResourceModifiedMessage.java @@ -1,5 +1,25 @@ 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.model.api.IModelJson; import ca.uhn.fhir.parser.IParser; diff --git a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/messaging/IResourceMessage.java b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/messaging/IResourceMessage.java index d2432451091..35cc86b266f 100644 --- a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/messaging/IResourceMessage.java +++ b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/messaging/IResourceMessage.java @@ -1,5 +1,25 @@ 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 { diff --git a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/messaging/ResourceOperationMessage.java b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/messaging/ResourceOperationMessage.java index 529e20b41ce..3134df128eb 100644 --- a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/messaging/ResourceOperationMessage.java +++ b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/messaging/ResourceOperationMessage.java @@ -1,5 +1,25 @@ 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.rest.api.server.RequestDetails; import org.hl7.fhir.instance.model.api.IBaseResource; diff --git a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/messaging/json/BaseJsonMessage.java b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/messaging/json/BaseJsonMessage.java index e3eff4e118c..90c8f8743b2 100644 --- a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/messaging/json/BaseJsonMessage.java +++ b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/messaging/json/BaseJsonMessage.java @@ -1,5 +1,25 @@ 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 com.fasterxml.jackson.annotation.JsonProperty; diff --git a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/messaging/json/HapiMessageHeaders.java b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/messaging/json/HapiMessageHeaders.java index a3b81e74f61..cc54ed2aebd 100644 --- a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/messaging/json/HapiMessageHeaders.java +++ b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/messaging/json/HapiMessageHeaders.java @@ -1,5 +1,25 @@ 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 com.fasterxml.jackson.annotation.JsonProperty; import org.springframework.messaging.MessageHeaders; diff --git a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/messaging/json/ResourceOperationJsonMessage.java b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/messaging/json/ResourceOperationJsonMessage.java index dd843293153..8d46d9ee44b 100644 --- a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/messaging/json/ResourceOperationJsonMessage.java +++ b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/messaging/json/ResourceOperationJsonMessage.java @@ -1,5 +1,25 @@ 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 com.fasterxml.jackson.annotation.JsonProperty; import org.apache.commons.lang3.builder.ToStringBuilder;