Merge branch '6.1.x'

This commit is contained in:
Stéphane Nicoll
2024-02-21 15:55:55 +01:00
3 changed files with 309 additions and 73 deletions

View File

@@ -22,7 +22,6 @@ import java.sql.SQLException;
import java.sql.Types;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
@@ -305,47 +304,42 @@ public class GenericCallMetaDataProvider implements CallMetaDataProvider {
String metaDataSchemaName = metaDataSchemaNameToUse(schemaName);
String metaDataProcedureName = procedureNameToUse(procedureName);
try {
String searchStringEscape = databaseMetaData.getSearchStringEscape();
String escapedSchemaName = escapeNamePattern(metaDataSchemaName, searchStringEscape);
String escapedProcedureName = escapeNamePattern(metaDataProcedureName, searchStringEscape);
if (logger.isDebugEnabled()) {
String schemaInfo = (Objects.equals(escapedSchemaName, metaDataSchemaName)
? metaDataSchemaName : metaDataCatalogName + "(" + escapedSchemaName + ")");
String procedureInfo = (Objects.equals(escapedProcedureName, metaDataProcedureName)
? metaDataProcedureName : metaDataProcedureName + "(" + escapedProcedureName + ")");
logger.debug("Retrieving meta-data for " + metaDataCatalogName + '/' +
schemaInfo + '/' + procedureInfo);
}
List<String> found = new ArrayList<>();
boolean function = false;
try (ResultSet procedures = databaseMetaData.getProcedures(
metaDataCatalogName, escapedSchemaName, escapedProcedureName)) {
while (procedures.next()) {
found.add(procedures.getString("PROCEDURE_CAT") + '.' + procedures.getString("PROCEDURE_SCHEM") +
'.' + procedures.getString("PROCEDURE_NAME"));
ProcedureMetadata procedureMetadata = getProcedureMetadata(databaseMetaData,
metaDataCatalogName, metaDataSchemaName, metaDataProcedureName);
if (procedureMetadata.hits() > 1) {
// Try again with exact match in case of placeholders
String searchStringEscape = databaseMetaData.getSearchStringEscape();
if (searchStringEscape != null) {
procedureMetadata = getProcedureMetadata(databaseMetaData, metaDataCatalogName,
escapeNamePattern(metaDataSchemaName, searchStringEscape),
escapeNamePattern(metaDataProcedureName, searchStringEscape));
}
}
if (found.isEmpty()) {
if (procedureMetadata.hits() == 0) {
// Functions not exposed as procedures anymore on PostgreSQL driver 42.2.11
try (ResultSet functions = databaseMetaData.getFunctions(
metaDataCatalogName, escapedSchemaName, escapedProcedureName)) {
while (functions.next()) {
found.add(functions.getString("FUNCTION_CAT") + '.' + functions.getString("FUNCTION_SCHEM") +
'.' + functions.getString("FUNCTION_NAME"));
function = true;
procedureMetadata = getProcedureMetadataAsFunction(databaseMetaData,
metaDataCatalogName, metaDataSchemaName, metaDataProcedureName);
if (procedureMetadata.hits() > 1) {
// Try again with exact match in case of placeholders
String searchStringEscape = databaseMetaData.getSearchStringEscape();
if (searchStringEscape != null) {
procedureMetadata = getProcedureMetadataAsFunction(
databaseMetaData, metaDataCatalogName,
escapeNamePattern(metaDataSchemaName, searchStringEscape),
escapeNamePattern(metaDataProcedureName, searchStringEscape));
}
}
}
// Handling matches
if (found.size() > 1) {
boolean isFunction = procedureMetadata.function();
List<String> matches = procedureMetadata.matches;
if (matches.size() > 1) {
throw new InvalidDataAccessApiUsageException(
"Unable to determine the correct call signature - multiple signatures for '" +
metaDataProcedureName + "': found " + found + " " + (function ? "functions" : "procedures"));
metaDataProcedureName + "': found " + matches + " " + (isFunction ? "functions" : "procedures"));
}
else if (found.isEmpty()) {
else if (matches.isEmpty()) {
if (metaDataProcedureName != null && metaDataProcedureName.contains(".") &&
!StringUtils.hasText(metaDataCatalogName)) {
String packageName = metaDataProcedureName.substring(0, metaDataProcedureName.indexOf('.'));
@@ -368,16 +362,16 @@ public class GenericCallMetaDataProvider implements CallMetaDataProvider {
}
if (logger.isDebugEnabled()) {
logger.debug("Retrieving column meta-data for " + (function ? "function" : "procedure") + ' ' +
metaDataCatalogName + '/' + metaDataSchemaName + '/' + metaDataProcedureName);
logger.debug("Retrieving column meta-data for " + (isFunction ? "function" : "procedure") + ' ' +
metaDataCatalogName + '/' + procedureMetadata.schemaName + '/' + procedureMetadata.procedureName);
}
try (ResultSet columns = function ?
databaseMetaData.getFunctionColumns(metaDataCatalogName, escapedSchemaName, escapedProcedureName, null) :
databaseMetaData.getProcedureColumns(metaDataCatalogName, escapedSchemaName, escapedProcedureName, null)) {
try (ResultSet columns = isFunction ?
databaseMetaData.getFunctionColumns(metaDataCatalogName, procedureMetadata.schemaName, procedureMetadata.procedureName, null) :
databaseMetaData.getProcedureColumns(metaDataCatalogName, procedureMetadata.schemaName, procedureMetadata.procedureName, null)) {
while (columns.next()) {
String columnName = columns.getString("COLUMN_NAME");
int columnType = columns.getInt("COLUMN_TYPE");
if (columnName == null && isInOrOutColumn(columnType, function)) {
if (columnName == null && isInOrOutColumn(columnType, isFunction)) {
if (logger.isDebugEnabled()) {
logger.debug("Skipping meta-data for: " + columnType + " " + columns.getInt("DATA_TYPE") +
" " + columns.getString("TYPE_NAME") + " " + columns.getInt("NULLABLE") +
@@ -385,8 +379,8 @@ public class GenericCallMetaDataProvider implements CallMetaDataProvider {
}
}
else {
int nullable = (function ? DatabaseMetaData.functionNullable : DatabaseMetaData.procedureNullable);
CallParameterMetaData meta = new CallParameterMetaData(function, columnName, columnType,
int nullable = (isFunction ? DatabaseMetaData.functionNullable : DatabaseMetaData.procedureNullable);
CallParameterMetaData meta = new CallParameterMetaData(isFunction, columnName, columnType,
columns.getInt("DATA_TYPE"), columns.getString("TYPE_NAME"),
columns.getInt("NULLABLE") == nullable);
this.callParameterMetaData.add(meta);
@@ -413,6 +407,36 @@ public class GenericCallMetaDataProvider implements CallMetaDataProvider {
}
}
private ProcedureMetadata getProcedureMetadata(DatabaseMetaData databaseMetaData,
@Nullable String catalogName, @Nullable String schemaName, @Nullable String procedureName) throws SQLException {
if (logger.isDebugEnabled()) {
logger.debug("Retrieving meta-data for " + catalogName + '/' + schemaName + '/' + procedureName);
}
List<String> matches = new ArrayList<>();
try (ResultSet procedures = databaseMetaData.getProcedures(catalogName, schemaName, procedureName)) {
while (procedures.next()) {
matches.add(procedures.getString("PROCEDURE_CAT") + '.' + procedures.getString("PROCEDURE_SCHEM") +
'.' + procedures.getString("PROCEDURE_NAME"));
}
}
return new ProcedureMetadata(schemaName, procedureName, matches, false);
}
private ProcedureMetadata getProcedureMetadataAsFunction(DatabaseMetaData databaseMetaData,
@Nullable String catalogName, @Nullable String schemaName, @Nullable String procedureName) throws SQLException {
if (logger.isDebugEnabled()) {
logger.debug("Fallback on retrieving function meta-data for " + catalogName + '/' + schemaName + '/' + procedureName);
}
List<String> matches = new ArrayList<>();
try (ResultSet functions = databaseMetaData.getFunctions(catalogName, schemaName, procedureName)) {
while (functions.next()) {
matches.add(functions.getString("FUNCTION_CAT") + '.' + functions.getString("FUNCTION_SCHEM") +
'.' + functions.getString("FUNCTION_NAME"));
}
}
return new ProcedureMetadata(schemaName, procedureName, matches, true);
}
@Nullable
private static String escapeNamePattern(@Nullable String name, @Nullable String escape) {
if (name == null || escape == null) {
@@ -436,4 +460,12 @@ public class GenericCallMetaDataProvider implements CallMetaDataProvider {
}
}
private record ProcedureMetadata(@Nullable String schemaName, @Nullable String procedureName,
List<String> matches, boolean function) {
int hits() {
return this.matches.size();
}
}
}