22
22
import java .sql .Types ;
23
23
import java .util .ArrayList ;
24
24
import java .util .List ;
25
- import java .util .Objects ;
26
25
27
26
import org .apache .commons .logging .Log ;
28
27
import org .apache .commons .logging .LogFactory ;
@@ -305,47 +304,42 @@ private void processProcedureColumns(DatabaseMetaData databaseMetaData,
305
304
String metaDataSchemaName = metaDataSchemaNameToUse (schemaName );
306
305
String metaDataProcedureName = procedureNameToUse (procedureName );
307
306
try {
308
- String searchStringEscape = databaseMetaData .getSearchStringEscape ();
309
- String escapedSchemaName = escapeNamePattern (metaDataSchemaName , searchStringEscape );
310
- String escapedProcedureName = escapeNamePattern (metaDataProcedureName , searchStringEscape );
311
- if (logger .isDebugEnabled ()) {
312
- String schemaInfo = (Objects .equals (escapedSchemaName , metaDataSchemaName )
313
- ? metaDataSchemaName : metaDataCatalogName + "(" + escapedSchemaName + ")" );
314
- String procedureInfo = (Objects .equals (escapedProcedureName , metaDataProcedureName )
315
- ? metaDataProcedureName : metaDataProcedureName + "(" + escapedProcedureName + ")" );
316
- logger .debug ("Retrieving meta-data for " + metaDataCatalogName + '/' +
317
- schemaInfo + '/' + procedureInfo );
318
- }
319
-
320
- List <String > found = new ArrayList <>();
321
- boolean function = false ;
322
-
323
- try (ResultSet procedures = databaseMetaData .getProcedures (
324
- metaDataCatalogName , escapedSchemaName , escapedProcedureName )) {
325
- while (procedures .next ()) {
326
- found .add (procedures .getString ("PROCEDURE_CAT" ) + '.' + procedures .getString ("PROCEDURE_SCHEM" ) +
327
- '.' + procedures .getString ("PROCEDURE_NAME" ));
307
+ ProcedureMetadata procedureMetadata = getProcedureMetadata (databaseMetaData ,
308
+ metaDataCatalogName , metaDataSchemaName , metaDataProcedureName );
309
+ if (procedureMetadata .hits () > 1 ) {
310
+ // Try again with exact match in case of placeholders
311
+ String searchStringEscape = databaseMetaData .getSearchStringEscape ();
312
+ if (searchStringEscape != null ) {
313
+ procedureMetadata = getProcedureMetadata (databaseMetaData , metaDataCatalogName ,
314
+ escapeNamePattern (metaDataSchemaName , searchStringEscape ),
315
+ escapeNamePattern (metaDataProcedureName , searchStringEscape ));
328
316
}
329
317
}
330
-
331
- if (found .isEmpty ()) {
318
+ if (procedureMetadata .hits () == 0 ) {
332
319
// Functions not exposed as procedures anymore on PostgreSQL driver 42.2.11
333
- try (ResultSet functions = databaseMetaData .getFunctions (
334
- metaDataCatalogName , escapedSchemaName , escapedProcedureName )) {
335
- while (functions .next ()) {
336
- found .add (functions .getString ("FUNCTION_CAT" ) + '.' + functions .getString ("FUNCTION_SCHEM" ) +
337
- '.' + functions .getString ("FUNCTION_NAME" ));
338
- function = true ;
320
+ procedureMetadata = getProcedureMetadataAsFunction (databaseMetaData ,
321
+ metaDataCatalogName , metaDataSchemaName , metaDataProcedureName );
322
+ if (procedureMetadata .hits () > 1 ) {
323
+ // Try again with exact match in case of placeholders
324
+ String searchStringEscape = databaseMetaData .getSearchStringEscape ();
325
+ if (searchStringEscape != null ) {
326
+ procedureMetadata = getProcedureMetadataAsFunction (
327
+ databaseMetaData , metaDataCatalogName ,
328
+ escapeNamePattern (metaDataSchemaName , searchStringEscape ),
329
+ escapeNamePattern (metaDataProcedureName , searchStringEscape ));
339
330
}
340
331
}
341
332
}
333
+ // Handling matches
342
334
343
- if (found .size () > 1 ) {
335
+ boolean isFunction = procedureMetadata .function ();
336
+ List <String > matches = procedureMetadata .matches ;
337
+ if (matches .size () > 1 ) {
344
338
throw new InvalidDataAccessApiUsageException (
345
339
"Unable to determine the correct call signature - multiple signatures for '" +
346
- metaDataProcedureName + "': found " + found + " " + (function ? "functions" : "procedures" ));
340
+ metaDataProcedureName + "': found " + matches + " " + (isFunction ? "functions" : "procedures" ));
347
341
}
348
- else if (found .isEmpty ()) {
342
+ else if (matches .isEmpty ()) {
349
343
if (metaDataProcedureName != null && metaDataProcedureName .contains ("." ) &&
350
344
!StringUtils .hasText (metaDataCatalogName )) {
351
345
String packageName = metaDataProcedureName .substring (0 , metaDataProcedureName .indexOf ('.' ));
@@ -368,25 +362,25 @@ else if ("Oracle".equals(databaseMetaData.getDatabaseProductName())) {
368
362
}
369
363
370
364
if (logger .isDebugEnabled ()) {
371
- logger .debug ("Retrieving column meta-data for " + (function ? "function" : "procedure" ) + ' ' +
372
- metaDataCatalogName + '/' + metaDataSchemaName + '/' + metaDataProcedureName );
365
+ logger .debug ("Retrieving column meta-data for " + (isFunction ? "function" : "procedure" ) + ' ' +
366
+ metaDataCatalogName + '/' + procedureMetadata . schemaName + '/' + procedureMetadata . procedureName );
373
367
}
374
- try (ResultSet columns = function ?
375
- databaseMetaData .getFunctionColumns (metaDataCatalogName , escapedSchemaName , escapedProcedureName , null ) :
376
- databaseMetaData .getProcedureColumns (metaDataCatalogName , escapedSchemaName , escapedProcedureName , null )) {
368
+ try (ResultSet columns = isFunction ?
369
+ databaseMetaData .getFunctionColumns (metaDataCatalogName , procedureMetadata . schemaName , procedureMetadata . procedureName , null ) :
370
+ databaseMetaData .getProcedureColumns (metaDataCatalogName , procedureMetadata . schemaName , procedureMetadata . procedureName , null )) {
377
371
while (columns .next ()) {
378
372
String columnName = columns .getString ("COLUMN_NAME" );
379
373
int columnType = columns .getInt ("COLUMN_TYPE" );
380
- if (columnName == null && isInOrOutColumn (columnType , function )) {
374
+ if (columnName == null && isInOrOutColumn (columnType , isFunction )) {
381
375
if (logger .isDebugEnabled ()) {
382
376
logger .debug ("Skipping meta-data for: " + columnType + " " + columns .getInt ("DATA_TYPE" ) +
383
377
" " + columns .getString ("TYPE_NAME" ) + " " + columns .getInt ("NULLABLE" ) +
384
378
" (probably a member of a collection)" );
385
379
}
386
380
}
387
381
else {
388
- int nullable = (function ? DatabaseMetaData .functionNullable : DatabaseMetaData .procedureNullable );
389
- CallParameterMetaData meta = new CallParameterMetaData (function , columnName , columnType ,
382
+ int nullable = (isFunction ? DatabaseMetaData .functionNullable : DatabaseMetaData .procedureNullable );
383
+ CallParameterMetaData meta = new CallParameterMetaData (isFunction , columnName , columnType ,
390
384
columns .getInt ("DATA_TYPE" ), columns .getString ("TYPE_NAME" ),
391
385
columns .getInt ("NULLABLE" ) == nullable );
392
386
this .callParameterMetaData .add (meta );
@@ -413,6 +407,36 @@ else if ("Oracle".equals(databaseMetaData.getDatabaseProductName())) {
413
407
}
414
408
}
415
409
410
+ private ProcedureMetadata getProcedureMetadata (DatabaseMetaData databaseMetaData ,
411
+ @ Nullable String catalogName , @ Nullable String schemaName , @ Nullable String procedureName ) throws SQLException {
412
+ if (logger .isDebugEnabled ()) {
413
+ logger .debug ("Retrieving meta-data for " + catalogName + '/' + schemaName + '/' + procedureName );
414
+ }
415
+ List <String > matches = new ArrayList <>();
416
+ try (ResultSet procedures = databaseMetaData .getProcedures (catalogName , schemaName , procedureName )) {
417
+ while (procedures .next ()) {
418
+ matches .add (procedures .getString ("PROCEDURE_CAT" ) + '.' + procedures .getString ("PROCEDURE_SCHEM" ) +
419
+ '.' + procedures .getString ("PROCEDURE_NAME" ));
420
+ }
421
+ }
422
+ return new ProcedureMetadata (schemaName , procedureName , matches , false );
423
+ }
424
+
425
+ private ProcedureMetadata getProcedureMetadataAsFunction (DatabaseMetaData databaseMetaData ,
426
+ @ Nullable String catalogName , @ Nullable String schemaName , @ Nullable String procedureName ) throws SQLException {
427
+ if (logger .isDebugEnabled ()) {
428
+ logger .debug ("Fallback on retrieving function meta-data for " + catalogName + '/' + schemaName + '/' + procedureName );
429
+ }
430
+ List <String > matches = new ArrayList <>();
431
+ try (ResultSet functions = databaseMetaData .getFunctions (catalogName , schemaName , procedureName )) {
432
+ while (functions .next ()) {
433
+ matches .add (functions .getString ("FUNCTION_CAT" ) + '.' + functions .getString ("FUNCTION_SCHEM" ) +
434
+ '.' + functions .getString ("FUNCTION_NAME" ));
435
+ }
436
+ }
437
+ return new ProcedureMetadata (schemaName , procedureName , matches , true );
438
+ }
439
+
416
440
@ Nullable
417
441
private static String escapeNamePattern (@ Nullable String name , @ Nullable String escape ) {
418
442
if (name == null || escape == null ) {
@@ -436,4 +460,12 @@ private static boolean isInOrOutColumn(int columnType, boolean function) {
436
460
}
437
461
}
438
462
463
+ private record ProcedureMetadata (@ Nullable String schemaName , @ Nullable String procedureName ,
464
+ List <String > matches , boolean function ) {
465
+
466
+ int hits () {
467
+ return this .matches .size ();
468
+ }
469
+ }
470
+
439
471
}
0 commit comments