close ResultSets in JdbcUtils (#6206)

* close ResultSets in JdbcUtils

* Credit for #6206

* Apply spotless

---------

Co-authored-by: James Agnew <jamesagnew@gmail.com>
This commit is contained in:
Jonas Beyer 2024-08-28 12:11:22 +00:00 committed by GitHub
parent 5e48e38b1d
commit 049c28d3e6
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 142 additions and 124 deletions

View File

@ -0,0 +1,4 @@
---
type: fix
issue: 6206
title: "A resource leak during database migration on Oracle could cause a failure `ORA-01000 maximum open cursors for session`. This has been corrected. Thanks to Jonas Beyer for the contribution!"

View File

@ -85,19 +85,16 @@ public class JdbcUtils {
try { try {
metadata = connection.getMetaData(); metadata = connection.getMetaData();
ResultSet indexes = getIndexInfo(theTableName, connection, metadata, false);
Set<String> indexNames = new HashSet<>(); Set<String> indexNames = new HashSet<>();
while (indexes.next()) {
ourLog.debug("*** Next index: {}", new ColumnMapRowMapper().mapRow(indexes, 0));
String indexName = indexes.getString("INDEX_NAME");
indexNames.add(indexName);
}
indexes = getIndexInfo(theTableName, connection, metadata, true); for (boolean unique : Set.of(false, true)) {
while (indexes.next()) { try (ResultSet indexes = getIndexInfo(theTableName, connection, metadata, unique)) {
ourLog.debug("*** Next index: {}", new ColumnMapRowMapper().mapRow(indexes, 0)); while (indexes.next()) {
String indexName = indexes.getString("INDEX_NAME"); ourLog.debug("*** Next index: {}", new ColumnMapRowMapper().mapRow(indexes, 0));
indexNames.add(indexName); String indexName = indexes.getString("INDEX_NAME");
indexNames.add(indexName);
}
}
} }
indexNames = indexNames.stream() indexNames = indexNames.stream()
@ -124,13 +121,14 @@ public class JdbcUtils {
DatabaseMetaData metadata; DatabaseMetaData metadata;
try { try {
metadata = connection.getMetaData(); metadata = connection.getMetaData();
ResultSet indexes = getIndexInfo(theTableName, connection, metadata, false); try (ResultSet indexes = getIndexInfo(theTableName, connection, metadata, false)) {
while (indexes.next()) { while (indexes.next()) {
String indexName = indexes.getString("INDEX_NAME"); String indexName = indexes.getString("INDEX_NAME");
if (theIndexName.equalsIgnoreCase(indexName)) { if (theIndexName.equalsIgnoreCase(indexName)) {
boolean nonUnique = indexes.getBoolean("NON_UNIQUE"); boolean nonUnique = indexes.getBoolean("NON_UNIQUE");
return !nonUnique; return !nonUnique;
}
} }
} }
@ -171,65 +169,69 @@ public class JdbcUtils {
metadata = connection.getMetaData(); metadata = connection.getMetaData();
String catalog = connection.getCatalog(); String catalog = connection.getCatalog();
String schema = connection.getSchema(); String schema = connection.getSchema();
ResultSet indexes = try (ResultSet indexes =
metadata.getColumns(catalog, schema, massageIdentifier(metadata, theTableName), null); metadata.getColumns(catalog, schema, massageIdentifier(metadata, theTableName), null)) {
while (indexes.next()) { while (indexes.next()) {
String tableName = indexes.getString("TABLE_NAME").toUpperCase(Locale.US); String tableName = indexes.getString("TABLE_NAME").toUpperCase(Locale.US);
if (!theTableName.equalsIgnoreCase(tableName)) { if (!theTableName.equalsIgnoreCase(tableName)) {
continue; continue;
} }
String columnName = indexes.getString("COLUMN_NAME").toUpperCase(Locale.US); String columnName = indexes.getString("COLUMN_NAME").toUpperCase(Locale.US);
if (!theColumnName.equalsIgnoreCase(columnName)) { if (!theColumnName.equalsIgnoreCase(columnName)) {
continue; continue;
} }
int dataType = indexes.getInt("DATA_TYPE"); int dataType = indexes.getInt("DATA_TYPE");
Long length = indexes.getLong("COLUMN_SIZE"); Long length = indexes.getLong("COLUMN_SIZE");
switch (dataType) { switch (dataType) {
case Types.LONGVARCHAR: case Types.LONGVARCHAR:
return new ColumnType(ColumnTypeEnum.TEXT, length); return new ColumnType(ColumnTypeEnum.TEXT, length);
case Types.BIT: case Types.BIT:
case Types.BOOLEAN: case Types.BOOLEAN:
return new ColumnType(ColumnTypeEnum.BOOLEAN, length); return new ColumnType(ColumnTypeEnum.BOOLEAN, length);
case Types.VARCHAR: case Types.VARCHAR:
return new ColumnType(ColumnTypeEnum.STRING, length); return new ColumnType(ColumnTypeEnum.STRING, length);
case Types.NUMERIC: case Types.NUMERIC:
case Types.BIGINT: case Types.BIGINT:
case Types.DECIMAL: case Types.DECIMAL:
return new ColumnType(ColumnTypeEnum.LONG, length); return new ColumnType(ColumnTypeEnum.LONG, length);
case Types.INTEGER: case Types.INTEGER:
return new ColumnType(ColumnTypeEnum.INT, length); return new ColumnType(ColumnTypeEnum.INT, length);
case Types.TIMESTAMP: case Types.TIMESTAMP:
case Types.TIMESTAMP_WITH_TIMEZONE: case Types.TIMESTAMP_WITH_TIMEZONE:
return new ColumnType(ColumnTypeEnum.DATE_TIMESTAMP, length); return new ColumnType(ColumnTypeEnum.DATE_TIMESTAMP, length);
case Types.BLOB: case Types.BLOB:
return new ColumnType(ColumnTypeEnum.BLOB, length);
case Types.LONGVARBINARY:
return new ColumnType(ColumnTypeEnum.BINARY, length);
case Types.VARBINARY:
if (DriverTypeEnum.MSSQL_2012.equals(theConnectionProperties.getDriverType())) {
// MS SQLServer seems to be mapping BLOB to VARBINARY under the covers, so we need
// to reverse that mapping
return new ColumnType(ColumnTypeEnum.BLOB, length); return new ColumnType(ColumnTypeEnum.BLOB, length);
case Types.LONGVARBINARY:
return new ColumnType(ColumnTypeEnum.BINARY, length);
case Types.VARBINARY:
if (DriverTypeEnum.MSSQL_2012.equals(theConnectionProperties.getDriverType())) {
// MS SQLServer seems to be mapping BLOB to VARBINARY under the covers,
// so we need to reverse that mapping
return new ColumnType(ColumnTypeEnum.BLOB, length);
} else { } else {
throw new IllegalArgumentException(
Msg.code(33) + "Don't know how to handle datatype " + dataType
+ " for column " + theColumnName
+ " on table " + theTableName);
}
case Types.CLOB:
return new ColumnType(ColumnTypeEnum.CLOB, length);
case Types.DOUBLE:
return new ColumnType(ColumnTypeEnum.DOUBLE, length);
case Types.FLOAT:
return new ColumnType(ColumnTypeEnum.FLOAT, length);
case Types.TINYINT:
return new ColumnType(ColumnTypeEnum.TINYINT, length);
default:
throw new IllegalArgumentException( throw new IllegalArgumentException(
Msg.code(33) + "Don't know how to handle datatype " + dataType Msg.code(34) + "Don't know how to handle datatype " + dataType
+ " for column " + theColumnName + " on table " + theTableName); + " for column " + theColumnName
} + " on table " + theTableName);
case Types.CLOB: }
return new ColumnType(ColumnTypeEnum.CLOB, length);
case Types.DOUBLE:
return new ColumnType(ColumnTypeEnum.DOUBLE, length);
case Types.FLOAT:
return new ColumnType(ColumnTypeEnum.FLOAT, length);
case Types.TINYINT:
return new ColumnType(ColumnTypeEnum.TINYINT, length);
default:
throw new IllegalArgumentException(Msg.code(34) + "Don't know how to handle datatype "
+ dataType + " for column " + theColumnName + " on table " + theTableName);
} }
} }
@ -274,13 +276,13 @@ public class JdbcUtils {
Set<String> fkNames = new HashSet<>(); Set<String> fkNames = new HashSet<>();
for (String nextParentTable : parentTables) { for (String nextParentTable : parentTables) {
ResultSet indexes = metadata.getCrossReference( try (ResultSet indexes = metadata.getCrossReference(
catalog, schema, nextParentTable, catalog, schema, foreignTable); catalog, schema, nextParentTable, catalog, schema, foreignTable)) {
while (indexes.next()) {
while (indexes.next()) { String fkName = indexes.getString("FK_NAME");
String fkName = indexes.getString("FK_NAME"); fkName = fkName.toUpperCase(Locale.US);
fkName = fkName.toUpperCase(Locale.US); fkNames.add(fkName);
fkNames.add(fkName); }
} }
} }
@ -317,14 +319,14 @@ public class JdbcUtils {
Set<String> fkNames = new HashSet<>(); Set<String> fkNames = new HashSet<>();
for (String nextParentTable : parentTables) { for (String nextParentTable : parentTables) {
ResultSet indexes = metadata.getCrossReference( try (ResultSet indexes = metadata.getCrossReference(
catalog, schema, nextParentTable, catalog, schema, foreignTable); catalog, schema, nextParentTable, catalog, schema, foreignTable)) {
while (indexes.next()) {
while (indexes.next()) { if (theForeignKeyColumn.equals(indexes.getString("FKCOLUMN_NAME"))) {
if (theForeignKeyColumn.equals(indexes.getString("FKCOLUMN_NAME"))) { String fkName = indexes.getString("FK_NAME");
String fkName = indexes.getString("FK_NAME"); fkName = fkName.toUpperCase(Locale.US);
fkName = fkName.toUpperCase(Locale.US); fkNames.add(fkName);
fkNames.add(fkName); }
} }
} }
} }
@ -348,22 +350,24 @@ public class JdbcUtils {
DatabaseMetaData metadata; DatabaseMetaData metadata;
try { try {
metadata = connection.getMetaData(); metadata = connection.getMetaData();
ResultSet indexes = metadata.getColumns( LinkedCaseInsensitiveMap<String> columnNames = new LinkedCaseInsensitiveMap<>();
try (ResultSet indexes = metadata.getColumns(
connection.getCatalog(), connection.getCatalog(),
connection.getSchema(), connection.getSchema(),
massageIdentifier(metadata, theTableName), massageIdentifier(metadata, theTableName),
null); null)) {
LinkedCaseInsensitiveMap<String> columnNames = new LinkedCaseInsensitiveMap<>(); while (indexes.next()) {
while (indexes.next()) { String tableName = indexes.getString("TABLE_NAME").toUpperCase(Locale.US);
String tableName = indexes.getString("TABLE_NAME").toUpperCase(Locale.US); if (!theTableName.equalsIgnoreCase(tableName)) {
if (!theTableName.equalsIgnoreCase(tableName)) { continue;
continue; }
String columnName = indexes.getString("COLUMN_NAME");
columnName = columnName.toUpperCase(Locale.US);
columnNames.put(columnName, columnName);
} }
String columnName = indexes.getString("COLUMN_NAME");
columnName = columnName.toUpperCase(Locale.US);
columnNames.put(columnName, columnName);
} }
return columnNames.keySet(); return columnNames.keySet();
@ -391,6 +395,7 @@ public class JdbcUtils {
SequenceInformationExtractor sequenceInformationExtractor = SequenceInformationExtractor sequenceInformationExtractor =
dialect.getSequenceInformationExtractor(); dialect.getSequenceInformationExtractor();
ExtractionContext extractionContext = new ExtractionContext.EmptyExtractionContext() { ExtractionContext extractionContext = new ExtractionContext.EmptyExtractionContext() {
@Override @Override
public Connection getJdbcConnection() { public Connection getJdbcConnection() {
return connection; return connection;
@ -404,6 +409,7 @@ public class JdbcUtils {
@Override @Override
public JdbcEnvironment getJdbcEnvironment() { public JdbcEnvironment getJdbcEnvironment() {
return new JdbcEnvironment() { return new JdbcEnvironment() {
@Override @Override
public Dialect getDialect() { public Dialect getDialect() {
return dialect; return dialect;
@ -480,22 +486,25 @@ public class JdbcUtils {
DatabaseMetaData metadata; DatabaseMetaData metadata;
try { try {
metadata = connection.getMetaData(); metadata = connection.getMetaData();
ResultSet tables = metadata.getTables(connection.getCatalog(), connection.getSchema(), null, null);
Set<String> columnNames = new HashSet<>(); Set<String> columnNames = new HashSet<>();
while (tables.next()) {
String tableName = tables.getString("TABLE_NAME");
tableName = tableName.toUpperCase(Locale.US);
String tableType = tables.getString("TABLE_TYPE"); try (ResultSet tables =
if ("SYSTEM TABLE".equalsIgnoreCase(tableType)) { metadata.getTables(connection.getCatalog(), connection.getSchema(), null, null)) {
continue;
}
if (SchemaMigrator.HAPI_FHIR_MIGRATION_TABLENAME.equalsIgnoreCase(tableName)) {
continue;
}
columnNames.add(tableName); while (tables.next()) {
String tableName = tables.getString("TABLE_NAME");
tableName = tableName.toUpperCase(Locale.US);
String tableType = tables.getString("TABLE_TYPE");
if ("SYSTEM TABLE".equalsIgnoreCase(tableType)) {
continue;
}
if (SchemaMigrator.HAPI_FHIR_MIGRATION_TABLENAME.equalsIgnoreCase(tableName)) {
continue;
}
columnNames.add(tableName);
}
} }
return columnNames; return columnNames;
@ -516,26 +525,27 @@ public class JdbcUtils {
DatabaseMetaData metadata; DatabaseMetaData metadata;
try { try {
metadata = connection.getMetaData(); metadata = connection.getMetaData();
ResultSet tables = metadata.getColumns( try (ResultSet tables = metadata.getColumns(
connection.getCatalog(), connection.getCatalog(),
connection.getSchema(), connection.getSchema(),
massageIdentifier(metadata, theTableName), massageIdentifier(metadata, theTableName),
null); null)) {
while (tables.next()) { while (tables.next()) {
String tableName = tables.getString("TABLE_NAME").toUpperCase(Locale.US); String tableName = tables.getString("TABLE_NAME").toUpperCase(Locale.US);
if (!theTableName.equalsIgnoreCase(tableName)) { if (!theTableName.equalsIgnoreCase(tableName)) {
continue; continue;
} }
if (theColumnName.equalsIgnoreCase(tables.getString("COLUMN_NAME"))) { if (theColumnName.equalsIgnoreCase(tables.getString("COLUMN_NAME"))) {
String nullable = tables.getString("IS_NULLABLE"); String nullable = tables.getString("IS_NULLABLE");
if ("YES".equalsIgnoreCase(nullable)) { if ("YES".equalsIgnoreCase(nullable)) {
return true; return true;
} else if ("NO".equalsIgnoreCase(nullable)) { } else if ("NO".equalsIgnoreCase(nullable)) {
return false; return false;
} else { } else {
throw new IllegalStateException(Msg.code(41) + "Unknown nullable: " + nullable); throw new IllegalStateException(Msg.code(41) + "Unknown nullable: " + nullable);
}
} }
} }
} }

View File

@ -934,6 +934,10 @@
<name>Alex Cote</name> <name>Alex Cote</name>
<organization>athenahealth</organization> <organization>athenahealth</organization>
</developer> </developer>
<developer>
<id>plchldr</id>
<name>Jonas Beyer</name>
</developer>
</developers> </developers>
<licenses> <licenses>