Check JDBC 4 getFunctions (for compatibility with PostgreSQL driver 42.2.11)

Closes gh-25399
This commit is contained in:
Juergen Hoeller 2020-07-17 17:47:53 +02:00
parent 01bab89dba
commit 30bf870810
1 changed files with 66 additions and 46 deletions

View File

@ -1,5 +1,5 @@
/*
* Copyright 2002-2018 the original author or authors.
* Copyright 2002-2020 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -46,9 +46,8 @@ public class GenericCallMetaDataProvider implements CallMetaDataProvider {
/** Logger available to subclasses. */
protected static final Log logger = LogFactory.getLog(CallMetaDataProvider.class);
private boolean procedureColumnMetaDataUsed = false;
private String userName;
private final String userName;
private boolean supportsCatalogsInProcedureCalls = true;
@ -58,7 +57,9 @@ public class GenericCallMetaDataProvider implements CallMetaDataProvider {
private boolean storesLowerCaseIdentifiers = false;
private List<CallParameterMetaData> callParameterMetaData = new ArrayList<>();
private boolean procedureColumnMetaDataUsed = false;
private final List<CallParameterMetaData> callParameterMetaData = new ArrayList<>();
/**
@ -328,20 +329,34 @@ public class GenericCallMetaDataProvider implements CallMetaDataProvider {
metaDataSchemaName + '/' + metaDataProcedureName);
}
ResultSet procs = null;
try {
procs = databaseMetaData.getProcedures(metaDataCatalogName, metaDataSchemaName, metaDataProcedureName);
List<String> found = new ArrayList<>();
while (procs.next()) {
found.add(procs.getString("PROCEDURE_CAT") + '.' + procs.getString("PROCEDURE_SCHEM") +
'.' + procs.getString("PROCEDURE_NAME"));
boolean function = false;
try (ResultSet procedures = databaseMetaData.getProcedures(
metaDataCatalogName, metaDataSchemaName, metaDataProcedureName)) {
while (procedures.next()) {
found.add(procedures.getString("PROCEDURE_CAT") + '.' + procedures.getString("PROCEDURE_SCHEM") +
'.' + procedures.getString("PROCEDURE_NAME"));
}
}
if (found.isEmpty()) {
// Functions not exposed as procedures anymore on PostgreSQL driver 42.2.11
try (ResultSet functions = databaseMetaData.getFunctions(
metaDataCatalogName, metaDataSchemaName, metaDataProcedureName)) {
while (functions.next()) {
found.add(functions.getString("FUNCTION_CAT") + '.' + functions.getString("FUNCTION_SCHEM") +
'.' + functions.getString("FUNCTION_NAME"));
function = true;
}
}
}
procs.close();
if (found.size() > 1) {
throw new InvalidDataAccessApiUsageException(
"Unable to determine the correct call signature - multiple " +
"procedures/functions/signatures for '" + metaDataProcedureName + "': found " + found);
"Unable to determine the correct call signature - multiple signatures for '" +
metaDataProcedureName + "': found " + found + " " + (function ? "functions" : "procedures"));
}
else if (found.isEmpty()) {
if (metaDataProcedureName != null && metaDataProcedureName.contains(".") &&
@ -365,30 +380,34 @@ public class GenericCallMetaDataProvider implements CallMetaDataProvider {
}
}
procs = databaseMetaData.getProcedureColumns(
metaDataCatalogName, metaDataSchemaName, metaDataProcedureName, null);
while (procs.next()) {
String columnName = procs.getString("COLUMN_NAME");
int columnType = procs.getInt("COLUMN_TYPE");
if (columnName == null && (
columnType == DatabaseMetaData.procedureColumnIn ||
columnType == DatabaseMetaData.procedureColumnInOut ||
columnType == DatabaseMetaData.procedureColumnOut)) {
if (logger.isDebugEnabled()) {
logger.debug("Skipping meta-data for: " + columnType + " " + procs.getInt("DATA_TYPE") +
" " + procs.getString("TYPE_NAME") + " " + procs.getInt("NULLABLE") +
" (probably a member of a collection)");
if (logger.isDebugEnabled()) {
logger.debug("Retrieving column meta-data for " + (function ? "function" : "procedure") + ' ' +
metaDataCatalogName + '/' + metaDataSchemaName + '/' + metaDataProcedureName);
}
try (ResultSet columns = function ?
databaseMetaData.getFunctionColumns(metaDataCatalogName, metaDataSchemaName, metaDataProcedureName, null) :
databaseMetaData.getProcedureColumns(metaDataCatalogName, metaDataSchemaName, metaDataProcedureName, null)) {
while (columns.next()) {
String columnName = columns.getString("COLUMN_NAME");
int columnType = columns.getInt("COLUMN_TYPE");
if (columnName == null && isInOrOutColumn(columnType, function)) {
if (logger.isDebugEnabled()) {
logger.debug("Skipping meta-data for: " + columnType + " " + columns.getInt("DATA_TYPE") +
" " + columns.getString("TYPE_NAME") + " " + columns.getInt("NULLABLE") +
" (probably a member of a collection)");
}
}
}
else {
CallParameterMetaData meta = new CallParameterMetaData(columnName, columnType,
procs.getInt("DATA_TYPE"), procs.getString("TYPE_NAME"),
procs.getInt("NULLABLE") == DatabaseMetaData.procedureNullable);
this.callParameterMetaData.add(meta);
if (logger.isDebugEnabled()) {
logger.debug("Retrieved meta-data: " + meta.getParameterName() + " " +
meta.getParameterType() + " " + meta.getSqlType() + " " +
meta.getTypeName() + " " + meta.isNullable());
else {
int nullable = (function ? DatabaseMetaData.functionNullable : DatabaseMetaData.procedureNullable);
CallParameterMetaData meta = new CallParameterMetaData(columnName, columnType,
columns.getInt("DATA_TYPE"), columns.getString("TYPE_NAME"),
columns.getInt("NULLABLE") == nullable);
this.callParameterMetaData.add(meta);
if (logger.isDebugEnabled()) {
logger.debug("Retrieved meta-data: " + meta.getParameterName() + " " +
meta.getParameterType() + " " + meta.getSqlType() + " " +
meta.getTypeName() + " " + meta.isNullable());
}
}
}
}
@ -398,17 +417,18 @@ public class GenericCallMetaDataProvider implements CallMetaDataProvider {
logger.warn("Error while retrieving meta-data for procedure columns: " + ex);
}
}
finally {
try {
if (procs != null) {
procs.close();
}
}
catch (SQLException ex) {
if (logger.isWarnEnabled()) {
logger.warn("Problem closing ResultSet for procedure column meta-data: " + ex);
}
}
}
private static boolean isInOrOutColumn(int columnType, boolean function) {
if (function) {
return (columnType == DatabaseMetaData.functionColumnIn ||
columnType == DatabaseMetaData.functionColumnInOut ||
columnType == DatabaseMetaData.functionColumnOut);
}
else {
return (columnType == DatabaseMetaData.procedureColumnIn ||
columnType == DatabaseMetaData.procedureColumnInOut ||
columnType == DatabaseMetaData.procedureColumnOut);
}
}