diff --git a/fhir-database-utils/src/main/java/com/ibm/fhir/database/utils/common/ResultSetReader.java b/fhir-database-utils/src/main/java/com/ibm/fhir/database/utils/common/ResultSetReader.java index ec847754aa3..f74901ef08a 100644 --- a/fhir-database-utils/src/main/java/com/ibm/fhir/database/utils/common/ResultSetReader.java +++ b/fhir-database-utils/src/main/java/com/ibm/fhir/database/utils/common/ResultSetReader.java @@ -6,6 +6,7 @@ package com.ibm.fhir.database.utils.common; +import java.math.BigDecimal; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Timestamp; @@ -88,6 +89,32 @@ public Long getLong() throws SQLException { return result; } + /** + * Get a BigDecimal column value and increment the column index + * @return + * @throws SQLException + */ + public BigDecimal getBigDecimal() throws SQLException { + BigDecimal result = rs.getBigDecimal(index++); + if (rs.wasNull()) { + result = null; + } + return result; + } + + /** + * Get a Double column value and increment the column index + * @return + * @throws SQLException + */ + public Double getDouble() throws SQLException { + Double result = rs.getDouble(index++); + if (rs.wasNull()) { + result = null; + } + return result; + } + /** * Get a Timestamp column value and increment the column index * @return diff --git a/fhir-remote-index/src/main/java/com/ibm/fhir/remote/index/database/PlainBatchParameterProcessor.java b/fhir-remote-index/src/main/java/com/ibm/fhir/remote/index/database/PlainBatchParameterProcessor.java index c1f65e0317a..33724b67d5e 100644 --- a/fhir-remote-index/src/main/java/com/ibm/fhir/remote/index/database/PlainBatchParameterProcessor.java +++ b/fhir-remote-index/src/main/java/com/ibm/fhir/remote/index/database/PlainBatchParameterProcessor.java @@ -152,9 +152,10 @@ public void process(String requestShard, String resourceType, String logicalId, dao.addString(logicalResourceId, parameterNameValue.getParameterNameId(), parameter.getValue(), parameter.getValue().toLowerCase(), parameter.getCompositeId()); if (parameter.isSystemParam()) { - systemDao.addString(logicalResourceId, parameterNameValue.getParameterNameId(), parameter.getValue(), parameter.getValue().toLowerCase(), parameter.getCompositeId()); + systemDao.addString(logicalResourceId, parameterNameValue.getParameterNameId(), parameter.getValue(), parameter.getValue().toLowerCase()); } } catch (SQLException x) { + logger.log(Level.SEVERE, "StringParameter", x); throw new FHIRPersistenceException("Failed inserting string params for '" + resourceType + "'"); } } diff --git a/fhir-remote-index/src/main/java/com/ibm/fhir/remote/index/database/PlainPostgresSystemParameterBatch.java b/fhir-remote-index/src/main/java/com/ibm/fhir/remote/index/database/PlainPostgresSystemParameterBatch.java index 8cbb53e8458..65e7f4b94eb 100644 --- a/fhir-remote-index/src/main/java/com/ibm/fhir/remote/index/database/PlainPostgresSystemParameterBatch.java +++ b/fhir-remote-index/src/main/java/com/ibm/fhir/remote/index/database/PlainPostgresSystemParameterBatch.java @@ -10,7 +10,6 @@ import java.sql.PreparedStatement; import java.sql.SQLException; import java.sql.Timestamp; -import java.sql.Types; import java.util.Calendar; import com.ibm.fhir.database.utils.common.CalendarHelper; @@ -131,21 +130,6 @@ public void close() { } } - /** - * Set the compositeId on the given PreparedStatement, handling a value if necessary - * @param ps - * @param index - * @param compositeId - * @throws SQLException - */ - private void setComposite(PreparedStatement ps, int index, Integer compositeId) throws SQLException { - if (compositeId != null) { - ps.setInt(index, compositeId); - } else { - ps.setNull(index, Types.INTEGER); - } - } - /** * Add a string parameter value to the whole-system batch statement * @@ -153,10 +137,9 @@ private void setComposite(PreparedStatement ps, int index, Integer compositeId) * @param parameterNameId * @param strValue * @param strValueLower - * @param compositeId * @throws SQLException */ - public void addString(long logicalResourceId, int parameterNameId, String strValue, String strValueLower, Integer compositeId) throws SQLException { + public void addString(long logicalResourceId, int parameterNameId, String strValue, String strValueLower) throws SQLException { // System level string attributes if (systemStrings == null) { final String insertSystemString = "INSERT INTO str_values (parameter_name_id, str_value, str_value_lcase, logical_resource_id) VALUES (?,?,?,?)"; @@ -166,7 +149,6 @@ public void addString(long logicalResourceId, int parameterNameId, String strVal systemStrings.setString(2, strValue); systemStrings.setString(3, strValueLower); systemStrings.setLong(4, logicalResourceId); - setComposite(systemStrings, 5, compositeId); systemStrings.addBatch(); systemStringCount++; } @@ -242,7 +224,7 @@ public void addProfile(long logicalResourceId, long canonicalId, String version, * @throws SQLException */ public void addSecurity(long logicalResourceId, long commonTokenValueId) throws SQLException { - if (systemTags == null) { + if (systemSecurity == null) { final String INS = "INSERT INTO logical_resource_security(common_token_value_id, logical_resource_id) VALUES (?,?)"; systemSecurity = connection.prepareStatement(INS); } diff --git a/fhir-remote-index/src/test/java/com/ibm/fhir/remote/index/RemoteIndexTest.java b/fhir-remote-index/src/test/java/com/ibm/fhir/remote/index/RemoteIndexTest.java index e5edafb2248..e9f5a73c788 100644 --- a/fhir-remote-index/src/test/java/com/ibm/fhir/remote/index/RemoteIndexTest.java +++ b/fhir-remote-index/src/test/java/com/ibm/fhir/remote/index/RemoteIndexTest.java @@ -6,6 +6,8 @@ package com.ibm.fhir.remote.index; +import static org.testng.Assert.assertEquals; + import java.math.BigDecimal; import java.sql.Connection; import java.sql.PreparedStatement; @@ -20,6 +22,7 @@ import java.util.Properties; import java.util.Set; import java.util.UUID; +import java.util.logging.Logger; import org.testng.annotations.BeforeClass; import org.testng.annotations.Test; @@ -28,8 +31,10 @@ import com.ibm.fhir.database.utils.api.IConnectionProvider; import com.ibm.fhir.database.utils.api.IDatabaseTranslator; import com.ibm.fhir.database.utils.common.PreparedStatementHelper; +import com.ibm.fhir.database.utils.common.ResultSetReader; import com.ibm.fhir.database.utils.derby.DerbyTranslator; import com.ibm.fhir.model.test.TestUtil; +import com.ibm.fhir.persistence.exception.FHIRPersistenceException; import com.ibm.fhir.persistence.index.RemoteIndexConstants; import com.ibm.fhir.persistence.index.RemoteIndexMessage; import com.ibm.fhir.persistence.index.SearchParametersTransportAdapter; @@ -38,18 +43,20 @@ import com.ibm.fhir.remote.index.database.PlainDerbyMessageHandler; /** - * + * Unit test for remote index message handling and database processing */ public class RemoteIndexTest { + private static final Logger logger = Logger.getLogger(RemoteIndexTest.class.getName()); private Properties testProps; private IConnectionProvider connectionProvider; private String[] TEST_RESOURCE_TYPES = {"Patient", "Observation" }; private IdentityCacheImpl identityCache; private static final String SCHEMA_NAME = "FHIRDATA"; + private static final boolean WHOLE_SYSTEM = true; private static final IDatabaseTranslator translator = new DerbyTranslator(); + private static final String OBSERVATION = "Observation"; - private final String OBSERVATION = "Observation"; private final String OBSERVATION_LOGICAL_ID = UUID.randomUUID().toString(); private final int versionId = 1; private final Instant lastUpdated = Instant.now(); @@ -65,7 +72,6 @@ public class RemoteIndexTest { private final String refResourceType = "Patient"; private final String refLogicalId = "pat1"; private final Integer refVersion = 2; - private final boolean wholeSystem = false; private final Integer compositeId = null; private final String valueString = "str1"; private final String url = "http://some.profile/location"; @@ -109,16 +115,16 @@ private List getMessages(long logicalResourceId) { // Create an Observation resource with a few parameters SearchParametersTransportAdapter adapter = new SearchParametersTransportAdapter(OBSERVATION, OBSERVATION_LOGICAL_ID, logicalResourceId, versionId, lastUpdated, requestShard, parameterHash); - adapter.dateValue("date-param", ts1, ts2, null, true); - adapter.locationValue("location-param", 0.1, 0.2, null); + adapter.stringValue("string-param", valueString, compositeId, WHOLE_SYSTEM); + adapter.dateValue("date-param", ts1, ts2, null, WHOLE_SYSTEM); adapter.numberValue("number-param", valueNumber, valueNumberLow, valueNumberHigh, null); - adapter.profileValue("profile-param", url, profileVersion, null, true); adapter.quantityValue("quantity-param", valueSystem, valueCode, valueNumber, valueNumberLow, valueNumberHigh, compositeId); - adapter.referenceValue("reference-param", refResourceType, refLogicalId, refVersion, compositeId); - adapter.securityValue("security-param", valueSystem, valueCode, wholeSystem); - adapter.stringValue("string-param", valueString, compositeId, wholeSystem); - adapter.tagValue("tag-param", valueSystem, valueCode, wholeSystem); adapter.tokenValue("token-param", valueSystem, valueCode, compositeId); + adapter.locationValue("location-param", 0.1, 0.2, null); + adapter.referenceValue("reference-param", refResourceType, refLogicalId, refVersion, compositeId); + adapter.securityValue("security-param", valueSystem, valueCode, WHOLE_SYSTEM); + adapter.profileValue("profile-param", url, profileVersion, null, WHOLE_SYSTEM); + adapter.tagValue("tag-param", valueSystem, valueCode, WHOLE_SYSTEM); sent.setData(adapter.build()); final String payload = marshallToString(sent); @@ -147,12 +153,29 @@ public void testFill() throws Exception { } try (Connection c = connectionProvider.getConnection()) { - PlainDerbyMessageHandler handler = new PlainDerbyMessageHandler(c, SCHEMA_NAME, identityCache, 1000L); - handler.process(getMessages(logicalResourceId)); - checkData(c, logicalResourceId); + try { + PlainDerbyMessageHandler handler = new PlainDerbyMessageHandler(c, SCHEMA_NAME, identityCache, 1000L); + handler.process(getMessages(logicalResourceId)); + checkData(c, logicalResourceId); + c.commit(); + } catch (Throwable t) { + safeRollback(c); + throw t; + } } } + /** + * Try and rollback the transaction, squashing any exception + * @param c + */ + private void safeRollback(Connection c) { + try { + c.rollback(); + } catch (SQLException x) { + logger.warning("rollback failed: " + x.getMessage()); + } + } /** * Inject the logical_resource_ident, logical_resources and observation_logical_resources * record as we would normally see added by the FHIR server. We're not dealing with any @@ -218,6 +241,399 @@ private long addObservationLogicalResource(Connection c, String logicalId) throw * @throws Exception */ private void checkData(Connection c, long logicalResourceId) throws Exception { + // check the resource level parameters + checkStringParam(c, OBSERVATION, logicalResourceId, valueString); + checkDateParam(c, OBSERVATION, logicalResourceId, ts1, ts2); + checkNumberParam(c, OBSERVATION, logicalResourceId, valueNumber, valueNumberLow, valueNumberHigh); + checkLocationParam(c, OBSERVATION, logicalResourceId, 0.1, 0.2); + checkProfileParam(c, OBSERVATION, logicalResourceId, url, profileVersion); + checkQuantityParam(c, OBSERVATION, logicalResourceId, valueSystem, valueCode, valueNumber, valueNumberLow, valueNumberHigh); + checkReferenceParam(c, OBSERVATION, logicalResourceId, refResourceType, refLogicalId); + checkTagParam(c, OBSERVATION, logicalResourceId, valueSystem, valueCode); + checkSecurityParam(c, OBSERVATION, logicalResourceId, valueSystem, valueCode); + checkTokenParam(c, OBSERVATION, logicalResourceId, valueSystem, valueCode); + + // check the whole-system level parameters + checkStringSystemParam(c, OBSERVATION, logicalResourceId, valueString); + checkDateSystemParam(c, OBSERVATION, logicalResourceId, ts1, ts2); + checkProfileSystemParam(c, OBSERVATION, logicalResourceId, url, profileVersion); + checkTagSystemParam(c, OBSERVATION, logicalResourceId, valueSystem, valueCode); + checkSecuritySystemParam(c, OBSERVATION, logicalResourceId, valueSystem, valueCode); + } + + /** + * @param c + * @param resourceType + * @param logicalResourceId + * @param valueSystem + * @param valueCode + * @param valueNumber + * @param valueNumberLow + * @param valueNumberHigh + */ + private void checkQuantityParam(Connection c, String resourceType, long logicalResourceId, String valueSystem, String valueCode, BigDecimal valueNumber, + BigDecimal valueNumberLow, BigDecimal valueNumberHigh) throws Exception { + final String select = "" + + "SELECT c.code_system_name, p.code, p.quantity_value, p.quantity_value_low, p.quantity_value_high " + + " FROM " + resourceType + "_quantity_values p " + + " JOIN code_systems c ON c.code_system_id = p.code_system_id " + + " WHERE p.logical_resource_id = ?"; + try (PreparedStatement ps = c.prepareStatement(select)) { + ps.setLong(1, logicalResourceId); + ResultSet rs = ps.executeQuery(); + ResultSetReader rsr = new ResultSetReader(rs); + if (rsr.next()) { + assertEquals(rsr.getString(), valueSystem); + assertEquals(rsr.getString(), valueCode); + assertEquals(rsr.getBigDecimal(), valueNumber); + assertEquals(rsr.getBigDecimal(), valueNumberLow); + assertEquals(rsr.getBigDecimal(), valueNumberHigh); + } else { + throw new FHIRPersistenceException("missing value: " + select); + } + + if (rs.next()) { + // there can be only one + throw new FHIRPersistenceException("more than one quantity parameter"); + } + } } -} + + private void checkStringParam(Connection c, String resourceType, long logicalResourceId, String valueString) throws Exception { + final String select = "SELECT str_value FROM " + resourceType + "_str_values WHERE logical_resource_id = ?"; + try (PreparedStatement ps = c.prepareStatement(select)) { + ps.setLong(1, logicalResourceId); + ResultSet rs = ps.executeQuery(); + if (rs.next()) { + assertEquals(rs.getString(1), valueString); + } else { + throw new FHIRPersistenceException("missing value: " + select); + } + + if (rs.next()) { + // there can be only one + throw new FHIRPersistenceException("more than one string parameter"); + } + } + } + + private void checkStringSystemParam(Connection c, String resourceType, long logicalResourceId, String valueString) throws Exception { + final String select = "SELECT str_value FROM str_values WHERE logical_resource_id = ?"; + try (PreparedStatement ps = c.prepareStatement(select)) { + ps.setLong(1, logicalResourceId); + ResultSet rs = ps.executeQuery(); + if (rs.next()) { + assertEquals(rs.getString(1), valueString); + } else { + throw new FHIRPersistenceException("missing value: " + select); + } + + if (rs.next()) { + // there can be only one + throw new FHIRPersistenceException("more than one string parameter"); + } + } + } + + private void checkNumberParam(Connection c, String resourceType, long logicalResourceId, BigDecimal numberValue, + BigDecimal numberValueLow, BigDecimal numberValueHigh) throws Exception { + final String select = "SELECT number_value, number_value_low, number_value_high FROM " + resourceType + "_number_values WHERE logical_resource_id = ?"; + try (PreparedStatement ps = c.prepareStatement(select)) { + ps.setLong(1, logicalResourceId); + ResultSet rs = ps.executeQuery(); + if (rs.next()) { + assertEquals(rs.getBigDecimal(1), numberValue); + assertEquals(rs.getBigDecimal(2), numberValueLow); + assertEquals(rs.getBigDecimal(3), numberValueHigh); + } else { + throw new FHIRPersistenceException("missing value: " + select); + } + + if (rs.next()) { + // there can be only one + throw new FHIRPersistenceException("more than one number parameter"); + } + } + } + + private void checkDateParam(Connection c, String resourceType, long logicalResourceId, Instant dateStart, + Instant dateEnd) throws Exception { + final String select = "SELECT date_start, date_end FROM " + resourceType + "_date_values WHERE logical_resource_id = ?"; + try (PreparedStatement ps = c.prepareStatement(select)) { + ps.setLong(1, logicalResourceId); + ResultSet rs = ps.executeQuery(); + ResultSetReader rsr = new ResultSetReader(rs); + if (rsr.next()) { + assertEquals(rsr.getTimestamp(), Timestamp.from(dateStart)); + assertEquals(rsr.getTimestamp(), Timestamp.from(dateEnd)); + } else { + throw new FHIRPersistenceException("missing value: " + select); + } + + if (rs.next()) { + // there can be only one + throw new FHIRPersistenceException("more than one date parameter"); + } + } + } + + private void checkDateSystemParam(Connection c, String resourceType, long logicalResourceId, Instant dateStart, + Instant dateEnd) throws Exception { + final String select = "SELECT date_start, date_end FROM date_values WHERE logical_resource_id = ?"; + try (PreparedStatement ps = c.prepareStatement(select)) { + ps.setLong(1, logicalResourceId); + ResultSet rs = ps.executeQuery(); + ResultSetReader rsr = new ResultSetReader(rs); + if (rsr.next()) { + assertEquals(rsr.getTimestamp(), Timestamp.from(dateStart)); + assertEquals(rsr.getTimestamp(), Timestamp.from(dateEnd)); + } else { + throw new FHIRPersistenceException("missing date system value: " + select); + } + + if (rs.next()) { + // there can be only one + throw new FHIRPersistenceException("more than one date system parameter"); + } + } + } + + private void checkLocationParam(Connection c, String resourceType, long logicalResourceId, double latitude, + double longitude) throws Exception { + final String select = "SELECT latitude_value, longitude_value FROM " + resourceType + "_latlng_values WHERE logical_resource_id = ?"; + try (PreparedStatement ps = c.prepareStatement(select)) { + ps.setLong(1, logicalResourceId); + ResultSet rs = ps.executeQuery(); + ResultSetReader rsr = new ResultSetReader(rs); + if (rsr.next()) { + assertEquals(rsr.getDouble(), latitude); + assertEquals(rsr.getDouble(), longitude); + } else { + throw new FHIRPersistenceException("missing value: " + select); + } + + if (rs.next()) { + // there can be only one + throw new FHIRPersistenceException("more than one date parameter"); + } + } + } + + private void checkProfileParam(Connection c, String resourceType, long logicalResourceId, String profile, String version) throws Exception { + final String select = "" + + "SELECT c.url, p.version FROM " + resourceType + "_profiles p" + + " JOIN common_canonical_values c ON c.canonical_id = p.canonical_id " + + " WHERE logical_resource_id = ?"; + + try (PreparedStatement ps = c.prepareStatement(select)) { + ps.setLong(1, logicalResourceId); + ResultSet rs = ps.executeQuery(); + ResultSetReader rsr = new ResultSetReader(rs); + if (rsr.next()) { + assertEquals(rsr.getString(), profile); + assertEquals(rsr.getString(), version); + } else { + throw new FHIRPersistenceException("missing value: " + select); + } + + if (rs.next()) { + // there can be only one + throw new FHIRPersistenceException("more than one profile parameter"); + } + } + } + + private void checkProfileSystemParam(Connection c, String resourceType, long logicalResourceId, String profile, String version) throws Exception { + final String select = "" + + "SELECT c.url, p.version FROM logical_resource_profiles p" + + " JOIN common_canonical_values c ON c.canonical_id = p.canonical_id " + + " WHERE logical_resource_id = ?"; + + try (PreparedStatement ps = c.prepareStatement(select)) { + ps.setLong(1, logicalResourceId); + ResultSet rs = ps.executeQuery(); + ResultSetReader rsr = new ResultSetReader(rs); + if (rsr.next()) { + assertEquals(rsr.getString(), profile); + assertEquals(rsr.getString(), version); + } else { + throw new FHIRPersistenceException("missing profile system value: " + select); + } + + if (rs.next()) { + // there can be only one + throw new FHIRPersistenceException("more than one profile system parameter"); + } + } + } + + private void checkSecurityParam(Connection c, String resourceType, long logicalResourceId, String codeSystem, String tokenValue) throws Exception { + final String select = "" + + "SELECT 1 FROM " + resourceType + "_security p" + + " JOIN common_token_values c ON c.common_token_value_id = p.common_token_value_id " + + " JOIN code_systems s ON s.code_system_id = c.code_system_id " + + " WHERE logical_resource_id = ? " + + " AND s.code_system_name = ? " + + " AND c.token_value = ? "; + + try (PreparedStatement ps = c.prepareStatement(select)) { + ps.setLong(1, logicalResourceId); + ps.setString(2, codeSystem); + ps.setString(3, tokenValue); + ResultSet rs = ps.executeQuery(); + ResultSetReader rsr = new ResultSetReader(rs); + if (rsr.next()) { + // OK + } else { + throw new FHIRPersistenceException("missing security value: " + select); + } + + if (rs.next()) { + // there can be only one + throw new FHIRPersistenceException("more than one security parameter"); + } + } + } + + private void checkSecuritySystemParam(Connection c, String resourceType, long logicalResourceId, String codeSystem, String tokenValue) throws Exception { + final String select = "" + + "SELECT 1 FROM logical_resource_security p" + + " JOIN common_token_values c ON c.common_token_value_id = p.common_token_value_id " + + " JOIN code_systems s ON s.code_system_id = c.code_system_id " + + " WHERE logical_resource_id = ? " + + " AND s.code_system_name = ? " + + " AND c.token_value = ? "; + + try (PreparedStatement ps = c.prepareStatement(select)) { + ps.setLong(1, logicalResourceId); + ps.setString(2, codeSystem); + ps.setString(3, tokenValue); + ResultSet rs = ps.executeQuery(); + ResultSetReader rsr = new ResultSetReader(rs); + if (rsr.next()) { + // OK + } else { + throw new FHIRPersistenceException("missing security value: " + select); + } + + if (rs.next()) { + // there can be only one + throw new FHIRPersistenceException("more than one security parameter"); + } + } + } + + private void checkTagParam(Connection c, String resourceType, long logicalResourceId, String codeSystem, String tokenValue) throws Exception { + final String select = "" + + "SELECT 1 FROM " + resourceType + "_tags p" + + " JOIN common_token_values c ON c.common_token_value_id = p.common_token_value_id " + + " JOIN code_systems s ON s.code_system_id = c.code_system_id " + + " WHERE logical_resource_id = ? " + + " AND s.code_system_name = ? " + + " AND c.token_value = ? "; + + try (PreparedStatement ps = c.prepareStatement(select)) { + ps.setLong(1, logicalResourceId); + ps.setString(2, codeSystem); + ps.setString(3, tokenValue); + ResultSet rs = ps.executeQuery(); + ResultSetReader rsr = new ResultSetReader(rs); + if (rsr.next()) { + // OK + } else { + throw new FHIRPersistenceException("missing tag value: " + select); + } + + if (rs.next()) { + // there can be only one + throw new FHIRPersistenceException("more than one tag parameter"); + } + } + } + + private void checkTagSystemParam(Connection c, String resourceType, long logicalResourceId, String codeSystem, String tokenValue) throws Exception { + final String select = "" + + "SELECT 1 FROM logical_resource_tags p" + + " JOIN common_token_values c ON c.common_token_value_id = p.common_token_value_id " + + " JOIN code_systems s ON s.code_system_id = c.code_system_id " + + " WHERE logical_resource_id = ? " + + " AND s.code_system_name = ? " + + " AND c.token_value = ? "; + + try (PreparedStatement ps = c.prepareStatement(select)) { + ps.setLong(1, logicalResourceId); + ps.setString(2, codeSystem); + ps.setString(3, tokenValue); + ResultSet rs = ps.executeQuery(); + ResultSetReader rsr = new ResultSetReader(rs); + if (rsr.next()) { + // OK + } else { + throw new FHIRPersistenceException("missing tag value: " + select); + } + + if (rs.next()) { + // there can be only one + throw new FHIRPersistenceException("more than one tag parameter"); + } + } + } + + private void checkTokenParam(Connection c, String resourceType, long logicalResourceId, String codeSystem, String tokenValue) throws Exception { + final String select = "" + + "SELECT 1 FROM " + resourceType + "_resource_token_refs p" + + " JOIN common_token_values c ON c.common_token_value_id = p.common_token_value_id " + + " JOIN code_systems s ON s.code_system_id = c.code_system_id " + + " WHERE logical_resource_id = ? " + + " AND s.code_system_name = ? " + + " AND c.token_value = ? "; + + try (PreparedStatement ps = c.prepareStatement(select)) { + ps.setLong(1, logicalResourceId); + ps.setString(2, codeSystem); + ps.setString(3, tokenValue); + ResultSet rs = ps.executeQuery(); + ResultSetReader rsr = new ResultSetReader(rs); + if (rsr.next()) { + // OK + } else { + throw new FHIRPersistenceException("missing token value: " + select); + } + + if (rs.next()) { + // there can be only one + throw new FHIRPersistenceException("more than one token parameter"); + } + } + } + + private void checkReferenceParam(Connection c, String resourceType, long logicalResourceId, String refResourceType, + String refLogicalId) throws Exception { + final String select = "" + + "SELECT 1 FROM " + resourceType + "_ref_values p " + + " JOIN logical_resource_ident i ON i.logical_resource_id = p.ref_logical_resource_id " + + " JOIN resource_types rrt ON rrt.resource_type_id = i.resource_type_id " + + " WHERE p.logical_resource_id = ?" + + " AND rrt.resource_type = ? " + + " AND i.logical_id = ? "; + try (PreparedStatement ps = c.prepareStatement(select)) { + ps.setLong(1, logicalResourceId); + ps.setString(2, refResourceType); + ps.setString(3, refLogicalId); + ResultSet rs = ps.executeQuery(); + ResultSetReader rsr = new ResultSetReader(rs); + if (rsr.next()) { + // ok + } else { + throw new FHIRPersistenceException("missing value: " + select); + } + + if (rs.next()) { + // there can be only one + throw new FHIRPersistenceException("more than one date parameter"); + } + } + } + +} \ No newline at end of file