From 4c28b1b552d9d614e518778fe2663b63f4451016 Mon Sep 17 00:00:00 2001 From: Jason Roberts Date: Fri, 24 Sep 2021 09:46:48 -0400 Subject: [PATCH 1/2] fix npe --- .../fhir/changelog/5_6_0/3031-index-migration-npe.yaml | 5 +++++ .../main/java/ca/uhn/fhir/jpa/migrate/JdbcUtils.java | 10 +++++++--- 2 files changed, 12 insertions(+), 3 deletions(-) create mode 100644 hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/5_6_0/3031-index-migration-npe.yaml diff --git a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/5_6_0/3031-index-migration-npe.yaml b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/5_6_0/3031-index-migration-npe.yaml new file mode 100644 index 00000000000..579d3ebe95c --- /dev/null +++ b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/5_6_0/3031-index-migration-npe.yaml @@ -0,0 +1,5 @@ +--- +type: fix +issue: 3031 +jira: SMILE-3178 +title: "Fixes a bug that was causing a null pointer exception to be thrown during database migrations that add or drop indexes." diff --git a/hapi-fhir-sql-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/JdbcUtils.java b/hapi-fhir-sql-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/JdbcUtils.java index 110417e4a19..b2edc322230 100644 --- a/hapi-fhir-sql-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/JdbcUtils.java +++ b/hapi-fhir-sql-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/JdbcUtils.java @@ -61,6 +61,7 @@ import java.util.List; import java.util.Locale; import java.util.Objects; import java.util.Set; +import java.util.stream.Collectors; public class JdbcUtils { private static final Logger ourLog = LoggerFactory.getLogger(JdbcUtils.class); @@ -86,7 +87,6 @@ public class JdbcUtils { while (indexes.next()) { ourLog.debug("*** Next index: {}", new ColumnMapRowMapper().mapRow(indexes, 0)); String indexName = indexes.getString("INDEX_NAME"); - indexName = indexName.toUpperCase(Locale.US); indexNames.add(indexName); } @@ -94,11 +94,15 @@ public class JdbcUtils { while (indexes.next()) { ourLog.debug("*** Next index: {}", new ColumnMapRowMapper().mapRow(indexes, 0)); String indexName = indexes.getString("INDEX_NAME"); - indexName = indexName.toUpperCase(Locale.US); indexNames.add(indexName); } - indexNames.removeIf(i -> i == null); + indexNames = indexNames + .stream() + .filter(Objects::nonNull) // filter out the nulls first + .map(s -> s.toUpperCase(Locale.US)) // then convert the non-null entries to upper case + .collect(Collectors.toSet()); + return indexNames; } catch (SQLException e) { From 81288d952600ba986b79575d17ed2611fe0e567c Mon Sep 17 00:00:00 2001 From: Jason Roberts Date: Fri, 24 Sep 2021 11:08:55 -0400 Subject: [PATCH 2/2] improve test coverage --- .../uhn/fhir/jpa/migrate/JdbcUtilsTest.java | 45 +++++++++++++++++++ 1 file changed, 45 insertions(+) diff --git a/hapi-fhir-sql-migrate/src/test/java/ca/uhn/fhir/jpa/migrate/JdbcUtilsTest.java b/hapi-fhir-sql-migrate/src/test/java/ca/uhn/fhir/jpa/migrate/JdbcUtilsTest.java index cdc9fb01a1f..3ffc8e6aed7 100644 --- a/hapi-fhir-sql-migrate/src/test/java/ca/uhn/fhir/jpa/migrate/JdbcUtilsTest.java +++ b/hapi-fhir-sql-migrate/src/test/java/ca/uhn/fhir/jpa/migrate/JdbcUtilsTest.java @@ -10,10 +10,14 @@ import javax.sql.DataSource; import java.sql.Connection; import java.sql.DatabaseMetaData; import java.sql.ResultSet; +import java.sql.ResultSetMetaData; import java.sql.SQLException; import java.sql.Types; +import java.util.Set; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; @ExtendWith(MockitoExtension.class) @@ -64,4 +68,45 @@ public class JdbcUtilsTest { assertEquals(theExpectedColumnType, testColumnType.getColumnTypeEnum()); } + + @Test + public void testGetIndexNames_verifyNullHandling() throws SQLException { + + // setup + ResultSet mockTableResultSet = mock(ResultSet.class); + when(mockTableResultSet.next()).thenReturn(true, false); + when(mockTableResultSet.getString("TABLE_NAME")).thenReturn("TEST_TABLE"); + when(mockTableResultSet.getString("TABLE_TYPE")).thenReturn("USER TABLE"); + + ResultSetMetaData mockResultSetMetaData = mock(ResultSetMetaData.class); + when(mockResultSetMetaData.getColumnCount()).thenReturn(0); + + ResultSet mockIndicesResultSet = mock(ResultSet.class); + when(mockIndicesResultSet.next()).thenReturn(true, true, true, false); + when(mockIndicesResultSet.getString("INDEX_NAME")).thenReturn("IDX_1", null, "idx_2"); + when(mockIndicesResultSet.getMetaData()).thenReturn(mockResultSetMetaData); + + ResultSet mockUniqueIndicesResultSet = mock(ResultSet.class); + when(mockUniqueIndicesResultSet.next()).thenReturn(true, true, false); + when(mockUniqueIndicesResultSet.getString("INDEX_NAME")).thenReturn(null, "Idx_3"); + when(mockUniqueIndicesResultSet.getMetaData()).thenReturn(mockResultSetMetaData); + + when(myDatabaseMetaData.getTables("Catalog", "Schema", null, null)).thenReturn(mockTableResultSet); + when(myDatabaseMetaData.getIndexInfo("Catalog", "Schema", "TEST_TABLE", false, true)).thenReturn(mockIndicesResultSet); + when(myDatabaseMetaData.getIndexInfo("Catalog", "Schema", "TEST_TABLE", true, true)).thenReturn(mockUniqueIndicesResultSet); + when(myConnection.getMetaData()).thenReturn(myDatabaseMetaData); + when(myConnection.getCatalog()).thenReturn("Catalog"); + when(myConnection.getSchema()).thenReturn("Schema"); + when(myDataSource.getConnection()).thenReturn(myConnection); + DriverTypeEnum.ConnectionProperties myConnectionProperties = DriverTypeEnum.H2_EMBEDDED.newConnectionProperties(myDataSource); + + //execute + Set indexNames = JdbcUtils.getIndexNames(myConnectionProperties, "TEST_TABLE"); + + // verify + assertEquals(3, indexNames.size()); + assertTrue(indexNames.contains("IDX_1")); + assertTrue(indexNames.contains("IDX_2")); + assertTrue(indexNames.contains("IDX_3")); + } }