Merge branch '6.1.x'
This commit is contained in:
commit
6383a0d7ca
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,14 +17,26 @@
|
|||
package org.springframework.jdbc.core.metadata;
|
||||
|
||||
import java.sql.DatabaseMetaData;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.function.IntFunction;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.mockito.InOrder;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThatIllegalStateException;
|
||||
import org.springframework.dao.InvalidDataAccessApiUsageException;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.function.ThrowingBiFunction;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
|
||||
import static org.mockito.BDDMockito.given;
|
||||
import static org.mockito.Mockito.inOrder;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.verifyNoMoreInteractions;
|
||||
|
||||
/**
|
||||
* Tests for {@link GenericCallMetaDataProvider}.
|
||||
|
|
@ -36,36 +48,230 @@ class GenericCallMetaDataProviderTests {
|
|||
private final DatabaseMetaData databaseMetaData = mock(DatabaseMetaData.class);
|
||||
|
||||
@Test
|
||||
void procedureNameWithPatternIsEscape() throws SQLException {
|
||||
given(this.databaseMetaData.getSearchStringEscape()).willReturn("@");
|
||||
void procedureNameWithNoMatch() throws SQLException {
|
||||
GenericCallMetaDataProvider provider = new GenericCallMetaDataProvider(this.databaseMetaData);
|
||||
|
||||
ResultSet noProcedure = mockProcedures();
|
||||
given(this.databaseMetaData.getProcedures(null, null, "MY_PROCEDURE"))
|
||||
.willReturn(noProcedure);
|
||||
ResultSet noFunction = mockProcedures();
|
||||
given(this.databaseMetaData.getFunctions(null, null, "MY_PROCEDURE"))
|
||||
.willReturn(noFunction);
|
||||
|
||||
assertThatExceptionOfType(InvalidDataAccessApiUsageException.class)
|
||||
.isThrownBy(() -> provider.initializeWithProcedureColumnMetaData(this.databaseMetaData, null, null, "my_procedure"))
|
||||
.withMessageContaining("'MY_PROCEDURE'");
|
||||
|
||||
InOrder inOrder = inOrder(this.databaseMetaData);
|
||||
inOrder.verify(this.databaseMetaData).getUserName();
|
||||
inOrder.verify(this.databaseMetaData).getProcedures(null, null, "MY_PROCEDURE");
|
||||
inOrder.verify(this.databaseMetaData).getFunctions(null, null, "MY_PROCEDURE");
|
||||
inOrder.verify(this.databaseMetaData).getDatabaseProductName();
|
||||
verifyNoMoreInteractions(this.databaseMetaData);
|
||||
}
|
||||
|
||||
@Test
|
||||
void procedureNameWithExactMatch() throws SQLException {
|
||||
GenericCallMetaDataProvider provider = new GenericCallMetaDataProvider(this.databaseMetaData);
|
||||
|
||||
ResultSet myProcedure = mockProcedures(new Procedure(null, null, "MY_PROCEDURE"));
|
||||
given(this.databaseMetaData.getProcedures(null, null, "MY_PROCEDURE"))
|
||||
.willReturn(myProcedure);
|
||||
ResultSet myProcedureColumn = mockProcedureColumns("TEST", DatabaseMetaData.procedureColumnIn);
|
||||
given(this.databaseMetaData.getProcedureColumns(null, null, "MY_PROCEDURE", null))
|
||||
.willReturn(myProcedureColumn);
|
||||
|
||||
provider.initializeWithProcedureColumnMetaData(this.databaseMetaData, null, null, "my_procedure");
|
||||
assertThat(provider.getCallParameterMetaData()).singleElement().satisfies(callParameterMetaData -> {
|
||||
assertThat(callParameterMetaData.getParameterName()).isEqualTo("TEST");
|
||||
assertThat(callParameterMetaData.getParameterType()).isEqualTo(DatabaseMetaData.procedureColumnIn);
|
||||
});
|
||||
|
||||
InOrder inOrder = inOrder(this.databaseMetaData);
|
||||
inOrder.verify(this.databaseMetaData).getUserName();
|
||||
inOrder.verify(this.databaseMetaData).getProcedures(null, null, "MY_PROCEDURE");
|
||||
inOrder.verify(this.databaseMetaData).getProcedureColumns(null, null, "MY_PROCEDURE", null);
|
||||
verifyNoMoreInteractions(this.databaseMetaData);
|
||||
}
|
||||
|
||||
@Test
|
||||
void procedureNameWithSeveralMatchesFallBackOnEscaped() throws SQLException {
|
||||
GenericCallMetaDataProvider provider = new GenericCallMetaDataProvider(this.databaseMetaData);
|
||||
|
||||
given(this.databaseMetaData.getSearchStringEscape()).willReturn("@");
|
||||
ResultSet myProcedures = mockProcedures(new Procedure(null, null, "MY_PROCEDURE"),
|
||||
new Procedure(null, null, "MYBPROCEDURE"));
|
||||
given(this.databaseMetaData.getProcedures(null, null, "MY_PROCEDURE"))
|
||||
.willReturn(myProcedures);
|
||||
ResultSet myProcedureEscaped = mockProcedures(new Procedure(null, null, "MY@_PROCEDURE"));
|
||||
given(this.databaseMetaData.getProcedures(null, null, "MY@_PROCEDURE"))
|
||||
.willThrow(new IllegalStateException("Expected"));
|
||||
assertThatIllegalStateException().isThrownBy(() -> provider.initializeWithProcedureColumnMetaData(
|
||||
this.databaseMetaData, null, null, "my_procedure"));
|
||||
verify(this.databaseMetaData).getProcedures(null, null, "MY@_PROCEDURE");
|
||||
.willReturn(myProcedureEscaped);
|
||||
ResultSet myProcedureColumn = mockProcedureColumns("TEST", DatabaseMetaData.procedureColumnIn);
|
||||
given(this.databaseMetaData.getProcedureColumns(null, null, "MY@_PROCEDURE", null))
|
||||
.willReturn(myProcedureColumn);
|
||||
|
||||
provider.initializeWithProcedureColumnMetaData(this.databaseMetaData, null, null, "my_procedure");
|
||||
assertThat(provider.getCallParameterMetaData()).singleElement().satisfies(callParameterMetaData -> {
|
||||
assertThat(callParameterMetaData.getParameterName()).isEqualTo("TEST");
|
||||
assertThat(callParameterMetaData.getParameterType()).isEqualTo(DatabaseMetaData.procedureColumnIn);
|
||||
});
|
||||
|
||||
InOrder inOrder = inOrder(this.databaseMetaData);
|
||||
inOrder.verify(this.databaseMetaData).getUserName();
|
||||
inOrder.verify(this.databaseMetaData).getProcedures(null, null, "MY_PROCEDURE");
|
||||
inOrder.verify(this.databaseMetaData).getSearchStringEscape();
|
||||
inOrder.verify(this.databaseMetaData).getProcedures(null, null, "MY@_PROCEDURE");
|
||||
inOrder.verify(this.databaseMetaData).getProcedureColumns(null, null, "MY@_PROCEDURE", null);
|
||||
verifyNoMoreInteractions(this.databaseMetaData);
|
||||
}
|
||||
|
||||
@Test
|
||||
void schemaNameWithPatternIsEscape() throws SQLException {
|
||||
given(this.databaseMetaData.getSearchStringEscape()).willReturn("@");
|
||||
GenericCallMetaDataProvider provider = new GenericCallMetaDataProvider(this.databaseMetaData);
|
||||
given(this.databaseMetaData.getProcedures(null, "MY@_SCHEMA", "TEST"))
|
||||
.willThrow(new IllegalStateException("Expected"));
|
||||
assertThatIllegalStateException().isThrownBy(() -> provider.initializeWithProcedureColumnMetaData(
|
||||
this.databaseMetaData, null, "my_schema", "test"));
|
||||
verify(this.databaseMetaData).getProcedures(null, "MY@_SCHEMA", "TEST");
|
||||
}
|
||||
|
||||
@Test
|
||||
void nameIsNotEscapedIfEscapeCharacterIsNotAvailable() throws SQLException {
|
||||
void procedureNameWithSeveralMatchesDoesNotFallBackOnEscapedIfEscapeCharacterIsNotAvailable() throws SQLException {
|
||||
given(this.databaseMetaData.getSearchStringEscape()).willReturn(null);
|
||||
GenericCallMetaDataProvider provider = new GenericCallMetaDataProvider(this.databaseMetaData);
|
||||
given(this.databaseMetaData.getProcedures(null, "MY_SCHEMA", "MY_TEST"))
|
||||
.willThrow(new IllegalStateException("Expected"));
|
||||
assertThatIllegalStateException().isThrownBy(() -> provider.initializeWithProcedureColumnMetaData(
|
||||
this.databaseMetaData, null, "my_schema", "my_test"));
|
||||
verify(this.databaseMetaData).getProcedures(null, "MY_SCHEMA", "MY_TEST");
|
||||
|
||||
ResultSet myProcedures = mockProcedures(new Procedure(null, null, "MY_PROCEDURE"),
|
||||
new Procedure(null, null, "MYBPROCEDURE"));
|
||||
given(this.databaseMetaData.getProcedures(null, null, "MY_PROCEDURE"))
|
||||
.willReturn(myProcedures);
|
||||
|
||||
assertThatExceptionOfType(InvalidDataAccessApiUsageException.class)
|
||||
.isThrownBy(() -> provider.initializeWithProcedureColumnMetaData(this.databaseMetaData, null, null, "my_procedure"))
|
||||
.withMessageContainingAll("'MY_PROCEDURE'", "null.null.MY_PROCEDURE", "null.null.MYBPROCEDURE");
|
||||
|
||||
InOrder inOrder = inOrder(this.databaseMetaData);
|
||||
inOrder.verify(this.databaseMetaData).getUserName();
|
||||
inOrder.verify(this.databaseMetaData).getProcedures(null, null, "MY_PROCEDURE");
|
||||
inOrder.verify(this.databaseMetaData).getSearchStringEscape();
|
||||
verifyNoMoreInteractions(this.databaseMetaData);
|
||||
}
|
||||
|
||||
@Test
|
||||
void procedureNameWitNoMatchFallbackOnFunction() throws SQLException {
|
||||
GenericCallMetaDataProvider provider = new GenericCallMetaDataProvider(this.databaseMetaData);
|
||||
|
||||
given(this.databaseMetaData.getSearchStringEscape()).willReturn("@");
|
||||
ResultSet noProcedure = mockProcedures();
|
||||
given(this.databaseMetaData.getProcedures(null, null, "MY_PROCEDURE"))
|
||||
.willReturn(noProcedure);
|
||||
ResultSet noProcedureWithEscaped = mockProcedures();
|
||||
given(this.databaseMetaData.getProcedures(null, null, "MY@_PROCEDURE"))
|
||||
.willReturn(noProcedureWithEscaped);
|
||||
ResultSet function = mockFunctions(new Procedure(null, null, "MY_PROCEDURE"));
|
||||
given(this.databaseMetaData.getFunctions(null, null, "MY_PROCEDURE"))
|
||||
.willReturn(function);
|
||||
ResultSet myProcedureColumn = mockProcedureColumns("TEST", DatabaseMetaData.procedureColumnIn);
|
||||
given(this.databaseMetaData.getFunctionColumns(null, null, "MY_PROCEDURE", null))
|
||||
.willReturn(myProcedureColumn);
|
||||
|
||||
provider.initializeWithProcedureColumnMetaData(this.databaseMetaData, null, null, "my_procedure");
|
||||
assertThat(provider.getCallParameterMetaData()).singleElement().satisfies(callParameterMetaData -> {
|
||||
assertThat(callParameterMetaData.getParameterName()).isEqualTo("TEST");
|
||||
assertThat(callParameterMetaData.getParameterType()).isEqualTo(DatabaseMetaData.procedureColumnIn);
|
||||
});
|
||||
|
||||
InOrder inOrder = inOrder(this.databaseMetaData);
|
||||
inOrder.verify(this.databaseMetaData).getUserName();
|
||||
inOrder.verify(this.databaseMetaData).getProcedures(null, null, "MY_PROCEDURE");
|
||||
inOrder.verify(this.databaseMetaData).getFunctions(null, null, "MY_PROCEDURE");
|
||||
inOrder.verify(this.databaseMetaData).getFunctionColumns(null, null, "MY_PROCEDURE", null);
|
||||
verifyNoMoreInteractions(this.databaseMetaData);
|
||||
}
|
||||
|
||||
@Test
|
||||
void procedureNameWitNoMatchAndSeveralFunctionsFallbacksOnEscaped() throws SQLException {
|
||||
GenericCallMetaDataProvider provider = new GenericCallMetaDataProvider(this.databaseMetaData);
|
||||
|
||||
given(this.databaseMetaData.getSearchStringEscape()).willReturn("@");
|
||||
ResultSet noProcedure = mockProcedures();
|
||||
given(this.databaseMetaData.getProcedures(null, null, "MY_PROCEDURE"))
|
||||
.willReturn(noProcedure);
|
||||
ResultSet functions = mockFunctions(new Procedure(null, null, "MY_PROCEDURE"),
|
||||
new Procedure(null, null, "MYBPROCEDURE"));
|
||||
given(this.databaseMetaData.getFunctions(null, null, "MY_PROCEDURE"))
|
||||
.willReturn(functions);
|
||||
ResultSet functionEscaped = mockFunctions(new Procedure(null, null, "MY@_PROCEDURE"));
|
||||
given(this.databaseMetaData.getFunctions(null, null, "MY@_PROCEDURE"))
|
||||
.willReturn(functionEscaped);
|
||||
ResultSet myProcedureColumn = mockProcedureColumns("TEST", DatabaseMetaData.procedureColumnIn);
|
||||
given(this.databaseMetaData.getFunctionColumns(null, null, "MY@_PROCEDURE", null))
|
||||
.willReturn(myProcedureColumn);
|
||||
|
||||
provider.initializeWithProcedureColumnMetaData(this.databaseMetaData, null, null, "my_procedure");
|
||||
assertThat(provider.getCallParameterMetaData()).singleElement().satisfies(callParameterMetaData -> {
|
||||
assertThat(callParameterMetaData.getParameterName()).isEqualTo("TEST");
|
||||
assertThat(callParameterMetaData.getParameterType()).isEqualTo(DatabaseMetaData.procedureColumnIn);
|
||||
});
|
||||
|
||||
InOrder inOrder = inOrder(this.databaseMetaData);
|
||||
inOrder.verify(this.databaseMetaData).getUserName();
|
||||
inOrder.verify(this.databaseMetaData).getProcedures(null, null, "MY_PROCEDURE");
|
||||
inOrder.verify(this.databaseMetaData).getFunctions(null, null, "MY_PROCEDURE");
|
||||
inOrder.verify(this.databaseMetaData).getSearchStringEscape();
|
||||
inOrder.verify(this.databaseMetaData).getFunctions(null, null, "MY@_PROCEDURE");
|
||||
inOrder.verify(this.databaseMetaData).getFunctionColumns(null, null, "MY@_PROCEDURE", null);
|
||||
verifyNoMoreInteractions(this.databaseMetaData);
|
||||
}
|
||||
|
||||
private ResultSet mockProcedures(Procedure... procedures) {
|
||||
ResultSet resultSet = mock(ResultSet.class);
|
||||
List<Boolean> next = new ArrayList<>();
|
||||
Arrays.stream(procedures).forEach(p -> next.add(true));
|
||||
applyStrings(Arrays.stream(procedures).map(Procedure::catalog).toList(), (first, then) ->
|
||||
given(resultSet.getString("PROCEDURE_CAT")).willReturn(first, then));
|
||||
applyStrings(Arrays.stream(procedures).map(Procedure::schema).toList(), (first, then) ->
|
||||
given(resultSet.getString("PROCEDURE_SCHEM")).willReturn(first, then));
|
||||
applyStrings(Arrays.stream(procedures).map(Procedure::name).toList(), (first, then) ->
|
||||
given(resultSet.getString("PROCEDURE_NAME")).willReturn(first, then));
|
||||
next.add(false);
|
||||
applyBooleans(next, (first, then) -> given(resultSet.next()).willReturn(first, then));
|
||||
|
||||
return resultSet;
|
||||
}
|
||||
|
||||
private ResultSet mockFunctions(Procedure... procedures) {
|
||||
ResultSet resultSet = mock(ResultSet.class);
|
||||
List<Boolean> next = new ArrayList<>();
|
||||
Arrays.stream(procedures).forEach(p -> next.add(true));
|
||||
applyStrings(Arrays.stream(procedures).map(Procedure::catalog).toList(), (first, then) ->
|
||||
given(resultSet.getString("FUNCTION_CAT")).willReturn(first, then));
|
||||
applyStrings(Arrays.stream(procedures).map(Procedure::schema).toList(), (first, then) ->
|
||||
given(resultSet.getString("FUNCTION_SCHEM")).willReturn(first, then));
|
||||
applyStrings(Arrays.stream(procedures).map(Procedure::name).toList(), (first, then) ->
|
||||
given(resultSet.getString("FUNCTION_NAME")).willReturn(first, then));
|
||||
next.add(false);
|
||||
applyBooleans(next, (first, then) -> given(resultSet.next()).willReturn(first, then));
|
||||
|
||||
return resultSet;
|
||||
}
|
||||
|
||||
private ResultSet mockProcedureColumns(String columnName, int columnType) throws SQLException {
|
||||
ResultSet resultSet = mock(ResultSet.class);
|
||||
given(resultSet.next()).willReturn(true, false);
|
||||
given(resultSet.getString("COLUMN_NAME")).willReturn(columnName);
|
||||
given(resultSet.getInt("COLUMN_TYPE")).willReturn(columnType);
|
||||
return resultSet;
|
||||
}
|
||||
|
||||
record Procedure(@Nullable String catalog, @Nullable String schema, String name) {
|
||||
|
||||
}
|
||||
|
||||
private void applyBooleans(List<Boolean> content, ThrowingBiFunction<Boolean, Boolean[], Object> split) {
|
||||
apply(content, Boolean[]::new, split);
|
||||
}
|
||||
|
||||
private void applyStrings(List<String> content, ThrowingBiFunction<String, String[], Object> split) {
|
||||
apply(content, String[]::new, split);
|
||||
}
|
||||
|
||||
private <T> void apply(List<T> content, IntFunction<T[]> generator, ThrowingBiFunction<T, T[], Object> split) {
|
||||
if (content.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
T first = content.get(0);
|
||||
T[] array = content.subList(1, content.size()).toArray(generator);
|
||||
split.apply(first, array);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -239,9 +239,8 @@ class SimpleJdbcCallTests {
|
|||
given(databaseMetaData.getDatabaseProductName()).willReturn("Oracle");
|
||||
given(databaseMetaData.getUserName()).willReturn("ME");
|
||||
given(databaseMetaData.storesUpperCaseIdentifiers()).willReturn(true);
|
||||
given(databaseMetaData.getSearchStringEscape()).willReturn("@");
|
||||
given(databaseMetaData.getProcedures("", "ME", "ADD@_INVOICE")).willReturn(proceduresResultSet);
|
||||
given(databaseMetaData.getProcedureColumns("", "ME", "ADD@_INVOICE", null)).willReturn(procedureColumnsResultSet);
|
||||
given(databaseMetaData.getProcedures("", "ME", "ADD_INVOICE")).willReturn(proceduresResultSet);
|
||||
given(databaseMetaData.getProcedureColumns("", "ME", "ADD_INVOICE", null)).willReturn(procedureColumnsResultSet);
|
||||
|
||||
given(proceduresResultSet.next()).willReturn(true, false);
|
||||
given(proceduresResultSet.getString("PROCEDURE_NAME")).willReturn("add_invoice");
|
||||
|
|
@ -307,9 +306,8 @@ class SimpleJdbcCallTests {
|
|||
given(databaseMetaData.getDatabaseProductName()).willReturn("Oracle");
|
||||
given(databaseMetaData.getUserName()).willReturn("ME");
|
||||
given(databaseMetaData.storesUpperCaseIdentifiers()).willReturn(true);
|
||||
given(databaseMetaData.getSearchStringEscape()).willReturn("@");
|
||||
given(databaseMetaData.getProcedures("", "ME", "ADD@_INVOICE")).willReturn(proceduresResultSet);
|
||||
given(databaseMetaData.getProcedureColumns("", "ME", "ADD@_INVOICE", null)).willReturn(procedureColumnsResultSet);
|
||||
given(databaseMetaData.getProcedures("", "ME", "ADD_INVOICE")).willReturn(proceduresResultSet);
|
||||
given(databaseMetaData.getProcedureColumns("", "ME", "ADD_INVOICE", null)).willReturn(procedureColumnsResultSet);
|
||||
|
||||
given(proceduresResultSet.next()).willReturn(true, false);
|
||||
given(proceduresResultSet.getString("PROCEDURE_NAME")).willReturn("add_invoice");
|
||||
|
|
@ -332,8 +330,8 @@ class SimpleJdbcCallTests {
|
|||
}
|
||||
|
||||
private void verifyAddInvoiceWithMetaData(boolean isFunction) throws SQLException {
|
||||
ResultSet proceduresResultSet = databaseMetaData.getProcedures("", "ME", "ADD@_INVOICE");
|
||||
ResultSet procedureColumnsResultSet = databaseMetaData.getProcedureColumns("", "ME", "ADD@_INVOICE", null);
|
||||
ResultSet proceduresResultSet = databaseMetaData.getProcedures("", "ME", "ADD_INVOICE");
|
||||
ResultSet procedureColumnsResultSet = databaseMetaData.getProcedureColumns("", "ME", "ADD_INVOICE", null);
|
||||
if (isFunction) {
|
||||
verify(callableStatement).registerOutParameter(1, 4);
|
||||
verify(callableStatement).setObject(2, 1103, 4);
|
||||
|
|
|
|||
Loading…
Reference in New Issue