From ee7a0d48c56ac5de3294fdceef8c20971811d896 Mon Sep 17 00:00:00 2001 From: Sam Brannen <104798+sbrannen@users.noreply.github.com> Date: Tue, 1 Sep 2026 13:39:17 +0200 Subject: [PATCH] Polish contribution This commit introduces additional unit tests for CallMetaDataContext's function return parameter matching in reconcileParameters(), verifying that the declared return parameter is correctly resolved regardless of whether it is declared before or after an additional OUT parameter. See gh-37206 --- .../core/simple/CallMetaDataContextTests.java | 55 ++++++++++++++++++- .../jdbc/core/simple/SimpleJdbcCallTests.java | 9 ++- 2 files changed, 58 insertions(+), 6 deletions(-) diff --git a/spring-jdbc/src/test/java/org/springframework/jdbc/core/simple/CallMetaDataContextTests.java b/spring-jdbc/src/test/java/org/springframework/jdbc/core/simple/CallMetaDataContextTests.java index fcca55103b3..db5e6c4a731 100644 --- a/spring-jdbc/src/test/java/org/springframework/jdbc/core/simple/CallMetaDataContextTests.java +++ b/spring-jdbc/src/test/java/org/springframework/jdbc/core/simple/CallMetaDataContextTests.java @@ -18,6 +18,8 @@ package org.springframework.jdbc.core.simple; import java.sql.Connection; import java.sql.DatabaseMetaData; +import java.sql.ResultSet; +import java.sql.SQLException; import java.sql.Types; import java.util.ArrayList; import java.util.List; @@ -41,9 +43,10 @@ import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; /** - * Mock object based tests for CallMetaDataContext. + * Mock object based tests for {@link CallMetaDataContext}. * * @author Thomas Risberg + * @author Sam Brannen */ class CallMetaDataContextTests { @@ -103,4 +106,54 @@ class CallMetaDataContextTests { assertThat(callParameters).as("Wrong number of call parameters").hasSize(3); } + @Test // gh-37206 + void reconcileParametersMatchesFunctionReturnParameterDeclaredBeforeOutParameter() throws Exception { + initializeGetTotalFunctionMetaData(); + + List parameters = List.of( + new SqlOutParameter("RESULT", Types.INTEGER), + new SqlOutParameter("out_status", Types.INTEGER)); + + context.setFunction(true); + context.setProcedureName("GET_TOTAL"); + context.initializeMetaData(dataSource); + context.processParameters(parameters); + + assertThat(context.getCallParameters()).extracting(SqlParameter::getName) + .containsExactly("RESULT", "AMOUNT", "out_status"); + } + + @Test // gh-37206 + void reconcileParametersMatchesFunctionReturnParameterDeclaredAfterOutParameter() throws Exception { + initializeGetTotalFunctionMetaData(); + + List parameters = List.of( + new SqlOutParameter("out_status", Types.INTEGER), + new SqlOutParameter("RESULT", Types.INTEGER)); + + context.setFunction(true); + context.setProcedureName("GET_TOTAL"); + context.initializeMetaData(dataSource); + context.processParameters(parameters); + + assertThat(context.getCallParameters()).extracting(SqlParameter::getName) + .containsExactly("RESULT", "AMOUNT", "out_status"); + } + + private void initializeGetTotalFunctionMetaData() throws SQLException { + ResultSet proceduresResultSet = mock(); + ResultSet procedureColumnsResultSet = mock(); + given(databaseMetaData.getDatabaseProductName()).willReturn("Oracle"); + given(databaseMetaData.getUserName()).willReturn("ME"); + given(databaseMetaData.storesUpperCaseIdentifiers()).willReturn(true); + given(databaseMetaData.getProcedures("", "ME", "GET_TOTAL")).willReturn(proceduresResultSet); + given(databaseMetaData.getProcedureColumns("", "ME", "GET_TOTAL", null)).willReturn(procedureColumnsResultSet); + given(proceduresResultSet.next()).willReturn(true, false); + given(proceduresResultSet.getString("PROCEDURE_NAME")).willReturn("GET_TOTAL"); + given(procedureColumnsResultSet.next()).willReturn(true, true, true, false); + given(procedureColumnsResultSet.getInt("DATA_TYPE")).willReturn(Types.INTEGER); + given(procedureColumnsResultSet.getString("COLUMN_NAME")).willReturn(null, "amount", "out_status"); + given(procedureColumnsResultSet.getInt("COLUMN_TYPE")).willReturn(5, 1, 4); + } + } diff --git a/spring-jdbc/src/test/java/org/springframework/jdbc/core/simple/SimpleJdbcCallTests.java b/spring-jdbc/src/test/java/org/springframework/jdbc/core/simple/SimpleJdbcCallTests.java index 045cc28a68b..00910128223 100644 --- a/spring-jdbc/src/test/java/org/springframework/jdbc/core/simple/SimpleJdbcCallTests.java +++ b/spring-jdbc/src/test/java/org/springframework/jdbc/core/simple/SimpleJdbcCallTests.java @@ -266,8 +266,7 @@ class SimpleJdbcCallTests { verify(procedureColumnsResultSet).close(); } - - @Test + @Test // gh-37206 void functionWithAdditionalOutParameterDeclaredBeforeReturn() throws Exception { initializeGetTotalFunctionWithMetaData(); SimpleJdbcCall function = new SimpleJdbcCall(dataSource).withFunctionName("get_total"); @@ -282,7 +281,7 @@ class SimpleJdbcCallTests { assertThat(total).isEqualTo(42); } - @Test + @Test // gh-37206 void functionWithAdditionalOutParameterDeclaredAfterReturn() throws Exception { initializeGetTotalFunctionWithMetaData(); SimpleJdbcCall function = new SimpleJdbcCall(dataSource).withFunctionName("get_total"); @@ -296,7 +295,7 @@ class SimpleJdbcCallTests { assertThat(total).isEqualTo(42); } - @Test + @Test // gh-37206 void sqlServerProcedureWithReturnValueDeclaredAfterOutParameter() throws Exception { initializeSqlServerProcedureWithReturnValue(); SimpleJdbcCall procedure = new SimpleJdbcCall(dataSource).withProcedureName("my_proc").withReturnValue(); @@ -309,7 +308,7 @@ class SimpleJdbcCallTests { verifyStatement(procedure, "{? = call my_proc(?, ?)}"); } - @Test + @Test // gh-37206 void sqlServerProcedureWithReturnValueDeclaredFirst() throws Exception { initializeSqlServerProcedureWithReturnValue(); SimpleJdbcCall procedure = new SimpleJdbcCall(dataSource).withProcedureName("my_proc").withReturnValue();