From 4382cd313907fe893ba63bb8d2f2d90dd65afc6e Mon Sep 17 00:00:00 2001 From: "nathaniel.doef" Date: Wed, 22 Mar 2023 19:50:00 -0400 Subject: [PATCH] handle empty values for QueryParameterUtils.toEqualToOrInPredicate and QueryParameterUtils.toNotEqualToOrInPredicate --- ...ch-with-non-existant-searchparameters.yaml | 5 ++ .../fhir/jpa/util/QueryParameterUtils.java | 12 +++ .../jpa/util/QueryParameterUtilsTest.java | 83 +++++++++++++++++++ 3 files changed, 100 insertions(+) create mode 100644 hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_6_0/4675-fhir-search-with-non-existant-searchparameters.yaml create mode 100644 hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/util/QueryParameterUtilsTest.java diff --git a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_6_0/4675-fhir-search-with-non-existant-searchparameters.yaml b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_6_0/4675-fhir-search-with-non-existant-searchparameters.yaml new file mode 100644 index 00000000000..ddeb9e7a48e --- /dev/null +++ b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_6_0/4675-fhir-search-with-non-existant-searchparameters.yaml @@ -0,0 +1,5 @@ +--- +type: fix +issue: 4675 +title: "Previously, when Postgres was configured and a `_has` query was performed with a non-existent `SearchParameter`, +a 500 Server Error was received. This has been corrected." diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/util/QueryParameterUtils.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/util/QueryParameterUtils.java index 6693f8b28df..75b82bfb95a 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/util/QueryParameterUtils.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/util/QueryParameterUtils.java @@ -37,6 +37,7 @@ import com.healthmarketscience.sqlbuilder.BinaryCondition; import com.healthmarketscience.sqlbuilder.ComboCondition; import com.healthmarketscience.sqlbuilder.Condition; import com.healthmarketscience.sqlbuilder.InCondition; +import com.healthmarketscience.sqlbuilder.CustomCondition; import com.healthmarketscience.sqlbuilder.dbspec.basic.DbColumn; import org.apache.commons.collections4.BidiMap; import org.apache.commons.collections4.bidimap.DualHashBidiMap; @@ -45,6 +46,7 @@ import org.apache.commons.lang3.ObjectUtils; import org.apache.commons.lang3.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import org.springframework.util.CollectionUtils; import javax.annotation.Nonnull; import javax.annotation.Nullable; @@ -72,6 +74,8 @@ public class QueryParameterUtils { private static final BidiMap ourCompareOperationToParamPrefix; public static final Condition[] EMPTY_CONDITION_ARRAY = new Condition[0]; + public static final String FALSE_CONDITION = "1 = 2"; + public static final String TRUE_CONDITION = "1 = 1"; static { DualHashBidiMap compareOperationToParamPrefix = new DualHashBidiMap<>(); @@ -136,6 +140,10 @@ public class QueryParameterUtils { @Nonnull public static Condition toEqualToOrInPredicate(DbColumn theColumn, List theValuePlaceholders) { + if (CollectionUtils.isEmpty(theValuePlaceholders)){ + ourLog.debug("No parameters provided for IN() on column: {}", theColumn); + return new CustomCondition(FALSE_CONDITION); + } if (theValuePlaceholders.size() == 1) { return BinaryCondition.equalTo(theColumn, theValuePlaceholders.get(0)); } @@ -144,6 +152,10 @@ public class QueryParameterUtils { @Nonnull public static Condition toNotEqualToOrNotInPredicate(DbColumn theColumn, List theValuePlaceholders) { + if (CollectionUtils.isEmpty(theValuePlaceholders)){ + ourLog.debug("No parameters provided for NOT IN() on column: {}", theColumn); + return new CustomCondition(TRUE_CONDITION); + } if (theValuePlaceholders.size() == 1) { return BinaryCondition.notEqualTo(theColumn, theValuePlaceholders.get(0)); } diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/util/QueryParameterUtilsTest.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/util/QueryParameterUtilsTest.java new file mode 100644 index 00000000000..102afd8436d --- /dev/null +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/util/QueryParameterUtilsTest.java @@ -0,0 +1,83 @@ +package ca.uhn.fhir.jpa.util; + +import com.healthmarketscience.sqlbuilder.Condition; +import com.healthmarketscience.sqlbuilder.dbspec.basic.DbColumn; +import com.healthmarketscience.sqlbuilder.dbspec.basic.DbSchema; +import com.healthmarketscience.sqlbuilder.dbspec.basic.DbSpec; +import com.healthmarketscience.sqlbuilder.dbspec.basic.DbTable; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.junit.jupiter.MockitoExtension; + +import java.util.Collections; +import java.util.List; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +@ExtendWith(MockitoExtension.class) +public class QueryParameterUtilsTest { + + private static final String VALUE_1 = "value1"; + private static final String VALUE_2 = "value2"; + public static final String SPEC_NAME = "some_spec"; + public static final String SCHEMA_NAME = "some_schema"; + public static final String TABLE_NAME = "some_table"; + public static final String COLUMN_NAME = "some_column"; + + private DbSpec myDbSpec; + private DbSchema myDbSchema; + private DbTable myDbTable; + private DbColumn myColumn; + + @BeforeEach + public void setup(){ + myDbSpec = new DbSpec(SPEC_NAME); + myDbSchema = new DbSchema(myDbSpec, SCHEMA_NAME); + myDbTable = new DbTable(myDbSchema, TABLE_NAME); + myColumn = new DbColumn(myDbTable, COLUMN_NAME, "VARCHAR", 10); + } + + @Test + public void toEqualToOrInPredicate_withNoValueParameters_returnsFalseCondition(){ + Condition result = QueryParameterUtils.toEqualToOrInPredicate(myColumn, Collections.EMPTY_LIST); + String expected = String.format("(%s)", QueryParameterUtils.FALSE_CONDITION); + assertEquals(expected, result.toString()); + } + + @Test + public void toEqualToOrInPredicate_withSingleParameter_returnBinaryEqualsCondition(){ + Condition result = QueryParameterUtils.toEqualToOrInPredicate(myColumn, List.of(VALUE_1)); + String expected = String.format("(%s0.%s = '%s')", SPEC_NAME, COLUMN_NAME, VALUE_1); + assertEquals(expected, result.toString()); + } + + @Test + public void toEqualToOrInPredicate_withMultipleParameters_returnsInCondition(){ + Condition result = QueryParameterUtils.toEqualToOrInPredicate(myColumn, List.of(VALUE_1, VALUE_2)); + String expected = String.format("(%s0.%s IN ('%s','%s') )", SPEC_NAME, COLUMN_NAME, VALUE_1, VALUE_2); + assertEquals(expected, result.toString()); + } + + @Test + public void toNotEqualToOrNotInPredicate_withNoValueParameters_returnsTrueCondition(){ + Condition result = QueryParameterUtils.toNotEqualToOrNotInPredicate(myColumn, Collections.EMPTY_LIST); + String expected = String.format("(%s)", QueryParameterUtils.TRUE_CONDITION); + assertEquals(expected, result.toString()); + } + + @Test + public void toNotEqualToOrNotInPredicate_withSingleParameter_returnBinaryNotEqualsCondition(){ + Condition result = QueryParameterUtils.toNotEqualToOrNotInPredicate(myColumn, List.of(VALUE_1)); + String expected = String.format("(%s0.%s <> '%s')", SPEC_NAME, COLUMN_NAME, VALUE_1); + assertEquals(expected, result.toString()); + } + + @Test + public void toNotEqualToOrNotInPredicate_withMultipleParameters_returnsNotInCondition(){ + Condition result = QueryParameterUtils.toNotEqualToOrNotInPredicate(myColumn, List.of(VALUE_1, VALUE_2)); + String expected = String.format("(%s0.%s NOT IN ('%s','%s') )", SPEC_NAME, COLUMN_NAME, VALUE_1, VALUE_2); + assertEquals(expected, result.toString()); + } + +}