Cleanup sql test

This commit is contained in:
Michael Buckley 2024-09-29 18:25:23 -04:00
parent d05079da22
commit 4cf3af3f39
1 changed files with 60 additions and 71 deletions

View File

@ -5,9 +5,7 @@ import ca.uhn.fhir.jpa.model.config.PartitionSettings;
import ca.uhn.fhir.jpa.searchparam.SearchParameterMap; import ca.uhn.fhir.jpa.searchparam.SearchParameterMap;
import ca.uhn.fhir.jpa.test.BaseJpaR4Test; import ca.uhn.fhir.jpa.test.BaseJpaR4Test;
import ca.uhn.fhir.rest.api.Constants; import ca.uhn.fhir.rest.api.Constants;
import ca.uhn.fhir.rest.api.SortSpec;
import ca.uhn.fhir.rest.api.server.IBundleProvider; import ca.uhn.fhir.rest.api.server.IBundleProvider;
import ca.uhn.fhir.rest.param.StringParam;
import ca.uhn.fhir.rest.param.TokenParam; import ca.uhn.fhir.rest.param.TokenParam;
import ca.uhn.fhir.rest.param.UriParam; import ca.uhn.fhir.rest.param.UriParam;
import org.hl7.fhir.instance.model.api.IIdType; import org.hl7.fhir.instance.model.api.IIdType;
@ -17,7 +15,7 @@ import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.CsvSource; import org.junit.jupiter.params.provider.MethodSource;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
@ -26,7 +24,6 @@ import java.util.UUID;
import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
public class FhirResourceDaoR4SearchSqlTest extends BaseJpaR4Test { public class FhirResourceDaoR4SearchSqlTest extends BaseJpaR4Test {
@ -47,80 +44,73 @@ public class FhirResourceDaoR4SearchSqlTest extends BaseJpaR4Test {
} }
/** record SqlGenerationTestCase(String comment, String restQuery, String expectedSql, String expectedPartitionedSql) {}
* One regular search params - Doesn't need HFJ_RESOURCE as root
*/
@Test
public void testSingleRegularSearchParam() {
myCaptureQueriesListener.clear(); static List<SqlGenerationTestCase> sqlGenerationTestCases() {
SearchParameterMap map = SearchParameterMap.newSynchronous(Patient.SP_NAME, new StringParam("FOO")); return List.of(
myPatientDao.search(map); new SqlGenerationTestCase(
assertEquals(1, myCaptureQueriesListener.countSelectQueries()); "single string - no hfj_resource root",
String sql = myCaptureQueriesListener.getSelectQueriesForCurrentThread().get(0).getSql(false, false); "Patient?name=FOO",
assertEquals("SELECT t0.RES_ID FROM HFJ_SPIDX_STRING t0 WHERE ((t0.HASH_NORM_PREFIX = ?) AND (t0.SP_VALUE_NORMALIZED LIKE ?))", sql); "SELECT t0.RES_ID FROM HFJ_SPIDX_STRING t0 WHERE ((t0.HASH_NORM_PREFIX = ?) AND (t0.SP_VALUE_NORMALIZED LIKE ?))",
"SELECT t0.PARTITION_ID,t0.RES_ID FROM HFJ_SPIDX_STRING t0 WHERE ((t0.HASH_NORM_PREFIX = ?) AND (t0.SP_VALUE_NORMALIZED LIKE ?))"
)
, new SqlGenerationTestCase(
"two regular params - should use hfj_resource as root",
"Patient?name=smith&active=true",
"SELECT t1.RES_ID FROM HFJ_RESOURCE t1 INNER JOIN HFJ_SPIDX_STRING t0 ON (t1.RES_ID = t0.RES_ID) INNER JOIN HFJ_SPIDX_TOKEN t2 ON (t1.RES_ID = t2.RES_ID) WHERE (((t0.HASH_NORM_PREFIX = ?) AND (t0.SP_VALUE_NORMALIZED LIKE ?)) AND (t2.HASH_VALUE = ?))",
"SELECT t1.PARTITION_ID,t1.RES_ID FROM HFJ_RESOURCE t1 INNER JOIN HFJ_SPIDX_STRING t0 ON ((t1.PARTITION_ID = t0.PARTITION_ID) AND (t1.RES_ID = t0.RES_ID)) INNER JOIN HFJ_SPIDX_TOKEN t2 ON ((t1.PARTITION_ID = t2.PARTITION_ID) AND (t1.RES_ID = t2.RES_ID)) WHERE (((t0.HASH_NORM_PREFIX = ?) AND (t0.SP_VALUE_NORMALIZED LIKE ?)) AND (t2.HASH_VALUE = ?))"
)
, new SqlGenerationTestCase(
"token not as a NOT IN subselect",
"Encounter?class:not=not-there",
"SELECT t0.RES_ID FROM HFJ_RESOURCE t0 WHERE (((t0.RES_TYPE = ?) AND (t0.RES_DELETED_AT IS NULL)) AND ((t0.RES_ID) NOT IN (SELECT t0.RES_ID FROM HFJ_SPIDX_TOKEN t0 WHERE (t0.HASH_VALUE = ?)) ))",
"SELECT t0.PARTITION_ID,t0.RES_ID FROM HFJ_RESOURCE t0 WHERE (((t0.RES_TYPE = ?) AND (t0.RES_DELETED_AT IS NULL)) AND ((t0.PARTITION_ID,t0.RES_ID) NOT IN (SELECT t0.PARTITION_ID,t0.RES_ID FROM HFJ_SPIDX_TOKEN t0 WHERE (t0.HASH_VALUE = ?)) ))"
)
, new SqlGenerationTestCase(
"token not on chain join - NOT IN from hfj_res_link target columns",
"Observation?encounter.class:not=not-there",
"SELECT t0.SRC_RESOURCE_ID FROM HFJ_RES_LINK t0 WHERE ((t0.SRC_PATH = ?) AND ((t0.TARGET_RESOURCE_ID) NOT IN (SELECT t0.RES_ID FROM HFJ_SPIDX_TOKEN t0 WHERE (t0.HASH_VALUE = ?)) ))",
"SELECT t0.PARTITION_ID,t0.SRC_RESOURCE_ID FROM HFJ_RES_LINK t0 WHERE ((t0.SRC_PATH = ?) AND ((t0.TARGET_RES_PARTITION_ID,t0.TARGET_RESOURCE_ID) NOT IN (SELECT t0.PARTITION_ID,t0.RES_ID FROM HFJ_SPIDX_TOKEN t0 WHERE (t0.HASH_VALUE = ?)) ))"
)
, new SqlGenerationTestCase(
"bare sort",
"Patient?_sort=name",
"SELECT t0.RES_ID FROM HFJ_RESOURCE t0 LEFT OUTER JOIN HFJ_SPIDX_STRING t1 ON ((t0.RES_ID = t1.RES_ID) AND (t1.HASH_IDENTITY = ?)) WHERE ((t0.RES_TYPE = ?) AND (t0.RES_DELETED_AT IS NULL)) ORDER BY t1.SP_VALUE_NORMALIZED ASC NULLS LAST",
"SELECT t0.PARTITION_ID,t0.RES_ID FROM HFJ_RESOURCE t0 LEFT OUTER JOIN HFJ_SPIDX_STRING t1 ON ((t0.PARTITION_ID = t1.PARTITION_ID) AND (t0.RES_ID = t1.RES_ID) AND (t1.HASH_IDENTITY = ?)) WHERE ((t0.RES_TYPE = ?) AND (t0.RES_DELETED_AT IS NULL)) ORDER BY t1.SP_VALUE_NORMALIZED ASC NULLS LAST"
)
);
}
@ParameterizedTest
@MethodSource("sqlGenerationTestCases")
void testSqlGeneration_DefaultNoPartitionJoin(SqlGenerationTestCase theTestCase) {
// default config
String sql = getSqlForRestQuery(theTestCase.restQuery);
assertEquals(theTestCase.expectedSql, sql, theTestCase.comment);
} }
@ParameterizedTest @ParameterizedTest
@CsvSource(textBlock = """ @MethodSource("sqlGenerationTestCases")
single param - no hfj_resource, Patient?name=smith , 'SELECT t0.RES_ID FROM HFJ_SPIDX_STRING t0 WHERE ((t0.HASH_NORM_PREFIX = ?) AND (t0.SP_VALUE_NORMALIZED LIKE ?))' void testSqlGeneration_WithPartitionJoins(SqlGenerationTestCase theTestCase) {
single join, Patient?name=smith&active=true ,'SELECT t1.RES_ID FROM HFJ_RESOURCE t1 INNER JOIN HFJ_SPIDX_STRING t0 ON (t1.RES_ID = t0.RES_ID) INNER JOIN HFJ_SPIDX_TOKEN t2 ON (t1.RES_ID = t2.RES_ID) WHERE (((t0.HASH_NORM_PREFIX = ?) AND (t0.SP_VALUE_NORMALIZED LIKE ?)) AND (t2.HASH_VALUE = ?))' // include partition_id in joins
not, Encounter?class:not=not-there ,'SELECT t0.RES_ID FROM HFJ_RESOURCE t0 WHERE (((t0.RES_TYPE = ?) AND (t0.RES_DELETED_AT IS NULL)) AND ((t0.RES_ID) NOT IN (SELECT t0.RES_ID FROM HFJ_SPIDX_TOKEN t0 WHERE (t0.HASH_VALUE = ?)) ))'
not in chain, Observation?encounter.class:not=not-there ,'SELECT t0.SRC_RESOURCE_ID FROM HFJ_RES_LINK t0 WHERE ((t0.SRC_PATH = ?) AND ((t0.TARGET_RESOURCE_ID) NOT IN (SELECT t0.RES_ID FROM HFJ_SPIDX_TOKEN t0 WHERE (t0.HASH_VALUE = ?)) ))'
bare sort, Patient?_sort=name ,'SELECT t0.RES_ID FROM HFJ_RESOURCE t0 LEFT OUTER JOIN HFJ_SPIDX_STRING t1 ON ((t0.RES_ID = t1.RES_ID) AND (t1.HASH_IDENTITY = ?)) WHERE ((t0.RES_TYPE = ?) AND (t0.RES_DELETED_AT IS NULL)) ORDER BY t1.SP_VALUE_NORMALIZED ASC NULLS LAST'
""")
public void testSqlGeneration(String theComment, String theFhirRestQuery, String theExpectedSql) {
// setup
myCaptureQueriesListener.clear();
// execute
myTestDaoSearch.searchForIds(theFhirRestQuery);
// verify
assertEquals(1, myCaptureQueriesListener.countSelectQueries());
String sql = myCaptureQueriesListener.getSelectQueriesForCurrentThread().get(0).getSql(false, false);
assertEquals(theExpectedSql, sql, theComment);
}
@ParameterizedTest
@CsvSource(textBlock = """
single- no hfj_resource,Patient?name=smith ,'SELECT t0.PARTITION_ID,t0.RES_ID FROM HFJ_SPIDX_STRING t0 WHERE ((t0.HASH_NORM_PREFIX = ?) AND (t0.SP_VALUE_NORMALIZED LIKE ?))'
single join, Patient?name=smith&active=true ,'SELECT t1.PARTITION_ID,t1.RES_ID FROM HFJ_RESOURCE t1 INNER JOIN HFJ_SPIDX_STRING t0 ON ((t1.PARTITION_ID = t0.PARTITION_ID) AND (t1.RES_ID = t0.RES_ID)) INNER JOIN HFJ_SPIDX_TOKEN t2 ON ((t1.PARTITION_ID = t2.PARTITION_ID) AND (t1.RES_ID = t2.RES_ID)) WHERE (((t0.HASH_NORM_PREFIX = ?) AND (t0.SP_VALUE_NORMALIZED LIKE ?)) AND (t2.HASH_VALUE = ?))'
not, Encounter?class:not=not-there ,'SELECT t0.PARTITION_ID,t0.RES_ID FROM HFJ_RESOURCE t0 WHERE (((t0.RES_TYPE = ?) AND (t0.RES_DELETED_AT IS NULL)) AND ((t0.PARTITION_ID,t0.RES_ID) NOT IN (SELECT t0.PARTITION_ID,t0.RES_ID FROM HFJ_SPIDX_TOKEN t0 WHERE (t0.HASH_VALUE = ?)) ))'
not in chain, Observation?encounter.class:not=not-there ,'SELECT t0.PARTITION_ID,t0.SRC_RESOURCE_ID FROM HFJ_RES_LINK t0 WHERE ((t0.SRC_PATH = ?) AND ((t0.TARGET_RES_PARTITION_ID,t0.TARGET_RESOURCE_ID) NOT IN (SELECT t0.PARTITION_ID,t0.RES_ID FROM HFJ_SPIDX_TOKEN t0 WHERE (t0.HASH_VALUE = ?)) ))'
bare sort, Patient?_sort=name ,'SELECT t0.PARTITION_ID,t0.RES_ID FROM HFJ_RESOURCE t0 LEFT OUTER JOIN HFJ_SPIDX_STRING t1 ON ((t0.PARTITION_ID = t1.PARTITION_ID) AND (t0.RES_ID = t1.RES_ID) AND (t1.HASH_IDENTITY = ?)) WHERE ((t0.RES_TYPE = ?) AND (t0.RES_DELETED_AT IS NULL)) ORDER BY t1.SP_VALUE_NORMALIZED ASC NULLS LAST'
""")
public void testSqlGenerationWithPartitionJoins(String theComment, String theFhirRestQuery, String theExpectedSql) {
// setup
myPartitionSettings.setDefaultPartitionId(0); myPartitionSettings.setDefaultPartitionId(0);
myPartitionSettings.setPartitionIdsInPrimaryKeys(true); myPartitionSettings.setPartitionIdsInPrimaryKeys(true);
myCaptureQueriesListener.clear();
// execute String sql = getSqlForRestQuery(theTestCase.restQuery);
assertEquals(theTestCase.expectedPartitionedSql, sql, theTestCase.comment);
}
private String getSqlForRestQuery(String theFhirRestQuery) {
myCaptureQueriesListener.clear();
myTestDaoSearch.searchForIds(theFhirRestQuery); myTestDaoSearch.searchForIds(theFhirRestQuery);
// verify
assertEquals(1, myCaptureQueriesListener.countSelectQueries()); assertEquals(1, myCaptureQueriesListener.countSelectQueries());
String sql = myCaptureQueriesListener.getSelectQueriesForCurrentThread().get(0).getSql(false, false); return myCaptureQueriesListener.getSelectQueriesForCurrentThread().get(0).getSql(false, false);
assertEquals(theExpectedSql, sql, theComment);
} }
/**
* Two regular search params - Should use HFJ_RESOURCE as root
*/
@Test
public void testTwoRegularSearchParams() {
myCaptureQueriesListener.clear();
SearchParameterMap map = SearchParameterMap.newSynchronous()
.add(Patient.SP_NAME, new StringParam("FOO"))
.add(Patient.SP_GENDER, new TokenParam("a", "b"));
myPatientDao.search(map);
assertEquals(1, myCaptureQueriesListener.countSelectQueries());
String sql = myCaptureQueriesListener.getSelectQueriesForCurrentThread().get(0).getSql(false, false);
assertEquals("SELECT t1.RES_ID FROM HFJ_RESOURCE t1 INNER JOIN HFJ_SPIDX_STRING t0 ON (t1.RES_ID = t0.RES_ID) INNER JOIN HFJ_SPIDX_TOKEN t2 ON (t1.RES_ID = t2.RES_ID) WHERE (((t0.HASH_NORM_PREFIX = ?) AND (t0.SP_VALUE_NORMALIZED LIKE ?)) AND (t2.HASH_SYS_AND_VALUE = ?))", sql);
}
@Test @Test
public void testSearchByProfile_VersionedMode() { public void testSearchByProfile_VersionedMode() {
@ -129,14 +119,14 @@ public class FhirResourceDaoR4SearchSqlTest extends BaseJpaR4Test {
String code = "http://" + UUID.randomUUID(); String code = "http://" + UUID.randomUUID();
Patient p = new Patient(); Patient p = new Patient();
p.getMeta().addProfile(code); p.getMeta().addProfile(code);
IIdType id = myPatientDao.create(p).getId().toUnqualifiedVersionless(); IIdType id = myPatientDao.create(p, mySrd).getId().toUnqualifiedVersionless();
myMemoryCacheService.invalidateAllCaches(); myMemoryCacheService.invalidateAllCaches();
// Search // Search
myCaptureQueriesListener.clear(); myCaptureQueriesListener.clear();
SearchParameterMap map = SearchParameterMap.newSynchronous() SearchParameterMap map = SearchParameterMap.newSynchronous()
.add(Constants.PARAM_PROFILE, new TokenParam(code)); .add(Constants.PARAM_PROFILE, new TokenParam(code));
IBundleProvider outcome = myPatientDao.search(map); IBundleProvider outcome = myPatientDao.search(map, mySrd);
assertEquals(3, myCaptureQueriesListener.countSelectQueries()); assertEquals(3, myCaptureQueriesListener.countSelectQueries());
// Query 1 - Find resources: Make sure we search for tag type+system+code always // Query 1 - Find resources: Make sure we search for tag type+system+code always
String sql = myCaptureQueriesListener.getSelectQueriesForCurrentThread().get(0).getSql(false, false); String sql = myCaptureQueriesListener.getSelectQueriesForCurrentThread().get(0).getSql(false, false);
@ -158,7 +148,6 @@ public class FhirResourceDaoR4SearchSqlTest extends BaseJpaR4Test {
boolean reindexParamCache = myStorageSettings.isMarkResourcesForReindexingUponSearchParameterChange(); boolean reindexParamCache = myStorageSettings.isMarkResourcesForReindexingUponSearchParameterChange();
myStorageSettings.setMarkResourcesForReindexingUponSearchParameterChange(false); myStorageSettings.setMarkResourcesForReindexingUponSearchParameterChange(false);
// SearchParameter searchParameter = FhirResourceDaoR4TagsTest.createSearchParamForInlineResourceProfile();
SearchParameter searchParameter = FhirResourceDaoR4TagsInlineTest.createSearchParameterForInlineProfile(); SearchParameter searchParameter = FhirResourceDaoR4TagsInlineTest.createSearchParameterForInlineProfile();
ourLog.debug("SearchParam:\n{}", myFhirContext.newJsonParser().setPrettyPrint(true).encodeResourceToString(searchParameter)); ourLog.debug("SearchParam:\n{}", myFhirContext.newJsonParser().setPrettyPrint(true).encodeResourceToString(searchParameter));
mySearchParameterDao.update(searchParameter, mySrd); mySearchParameterDao.update(searchParameter, mySrd);
@ -168,14 +157,14 @@ public class FhirResourceDaoR4SearchSqlTest extends BaseJpaR4Test {
String code = "http://" + UUID.randomUUID(); String code = "http://" + UUID.randomUUID();
Patient p = new Patient(); Patient p = new Patient();
p.getMeta().addProfile(code); p.getMeta().addProfile(code);
IIdType id = myPatientDao.create(p).getId().toUnqualifiedVersionless(); IIdType id = myPatientDao.create(p, mySrd).getId().toUnqualifiedVersionless();
myMemoryCacheService.invalidateAllCaches(); myMemoryCacheService.invalidateAllCaches();
// Search // Search
myCaptureQueriesListener.clear(); myCaptureQueriesListener.clear();
SearchParameterMap map = SearchParameterMap.newSynchronous() SearchParameterMap map = SearchParameterMap.newSynchronous()
.add(Constants.PARAM_PROFILE, new UriParam(code)); .add(Constants.PARAM_PROFILE, new UriParam(code));
IBundleProvider outcome = myPatientDao.search(map); IBundleProvider outcome = myPatientDao.search(map, mySrd);
assertEquals(2, myCaptureQueriesListener.countSelectQueries()); assertEquals(2, myCaptureQueriesListener.countSelectQueries());
// Query 1 - Find resources: Just a standard token search in this mode // Query 1 - Find resources: Just a standard token search in this mode
String sql = myCaptureQueriesListener.getSelectQueriesForCurrentThread().get(0).getSql(false, false); String sql = myCaptureQueriesListener.getSelectQueriesForCurrentThread().get(0).getSql(false, false);