mirror of
https://github.com/spring-projects/spring-framework.git
synced 2026-09-17 08:24:13 +00:00
Merge branch '7.0.x'
This commit is contained in:
@@ -16,6 +16,6 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Create Backport Issue
|
||||
uses: spring-io/backport-bot@v0.0.2
|
||||
uses: spring-io/backport-bot@v0.0.3
|
||||
with:
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
@@ -42,7 +42,7 @@ wildcard; this means you can constrain binding more precisely:
|
||||
* `"addresses[0].city"` matches the `city` property of the element at index `0` in the `addresses` array or `List`.
|
||||
* `"map[key]"` matches the entry associated with `key` in the `map` property.
|
||||
* `"map*"` matches every entry in the `map` property, such as `"map[key1]"` and `"map[key2]"`.
|
||||
the same wildcard syntax also applies to indexed elements in an array or `List`.
|
||||
The same wildcard syntax also applies to indexed elements in an array or `List`.
|
||||
|
||||
See the {spring-framework-api}/validation/DataBinder.html#setAllowedFields(java.lang.String...)[`DataBinder#setAllowedFields`]
|
||||
javadoc for further details on the supported pattern syntax.
|
||||
|
||||
+7
-2
@@ -374,9 +374,14 @@ public class CallMetaDataContext {
|
||||
if (declaredParams.containsKey(paramNameToCheck) || (meta.isReturnParameter() && returnDeclared)) {
|
||||
SqlParameter param;
|
||||
if (meta.isReturnParameter()) {
|
||||
param = declaredParams.get(getFunctionReturnName());
|
||||
// Same normalization as the declaredParams keys above; the function
|
||||
// return name may have been adopted from a declared out parameter
|
||||
param = declaredParams.get(paramNameToCheck);
|
||||
if (param == null) {
|
||||
param = declaredParams.get(lowerCase(provider.parameterNameToUse(getFunctionReturnName())));
|
||||
}
|
||||
if (param == null && !getOutParameterNames().isEmpty()) {
|
||||
param = declaredParams.get(getOutParameterNames().get(0).toLowerCase(Locale.ROOT));
|
||||
param = declaredParams.get(lowerCase(provider.parameterNameToUse(getOutParameterNames().get(0))));
|
||||
}
|
||||
if (param == null) {
|
||||
throw new InvalidDataAccessApiUsageException(
|
||||
|
||||
+54
-1
@@ -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<SqlParameter> 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<SqlParameter> 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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+90
@@ -266,6 +266,61 @@ class SimpleJdbcCallTests {
|
||||
verify(procedureColumnsResultSet).close();
|
||||
}
|
||||
|
||||
@Test // gh-37206
|
||||
void functionWithAdditionalOutParameterDeclaredBeforeReturn() throws Exception {
|
||||
initializeGetTotalFunctionWithMetaData();
|
||||
SimpleJdbcCall function = new SimpleJdbcCall(dataSource).withFunctionName("get_total");
|
||||
function.declareParameters(
|
||||
new SqlOutParameter("out_status", Types.INTEGER),
|
||||
new SqlOutParameter("RESULT", Types.INTEGER));
|
||||
function.compile();
|
||||
assertThat(function.getCallParameters()).extracting(SqlParameter::getName)
|
||||
.containsExactly("RESULT", "AMOUNT", "out_status");
|
||||
verifyStatement(function, "{? = call GET_TOTAL(?, ?)}");
|
||||
Integer total = function.executeFunction(Integer.class, 5);
|
||||
assertThat(total).isEqualTo(42);
|
||||
}
|
||||
|
||||
@Test // gh-37206
|
||||
void functionWithAdditionalOutParameterDeclaredAfterReturn() throws Exception {
|
||||
initializeGetTotalFunctionWithMetaData();
|
||||
SimpleJdbcCall function = new SimpleJdbcCall(dataSource).withFunctionName("get_total");
|
||||
function.declareParameters(
|
||||
new SqlOutParameter("RESULT", Types.INTEGER),
|
||||
new SqlOutParameter("out_status", Types.INTEGER));
|
||||
function.compile();
|
||||
assertThat(function.getCallParameters()).extracting(SqlParameter::getName)
|
||||
.containsExactly("RESULT", "AMOUNT", "out_status");
|
||||
Integer total = function.executeFunction(Integer.class, 5);
|
||||
assertThat(total).isEqualTo(42);
|
||||
}
|
||||
|
||||
@Test // gh-37206
|
||||
void sqlServerProcedureWithReturnValueDeclaredAfterOutParameter() throws Exception {
|
||||
initializeSqlServerProcedureWithReturnValue();
|
||||
SimpleJdbcCall procedure = new SimpleJdbcCall(dataSource).withProcedureName("my_proc").withReturnValue();
|
||||
procedure.declareParameters(
|
||||
new SqlOutParameter("@out_total", Types.INTEGER),
|
||||
new SqlOutParameter("RETURN_VALUE", Types.INTEGER));
|
||||
procedure.compile();
|
||||
assertThat(procedure.getCallParameters()).extracting(SqlParameter::getName)
|
||||
.containsExactly("RETURN_VALUE", "amount", "@out_total");
|
||||
verifyStatement(procedure, "{? = call my_proc(?, ?)}");
|
||||
}
|
||||
|
||||
@Test // gh-37206
|
||||
void sqlServerProcedureWithReturnValueDeclaredFirst() throws Exception {
|
||||
initializeSqlServerProcedureWithReturnValue();
|
||||
SimpleJdbcCall procedure = new SimpleJdbcCall(dataSource).withProcedureName("my_proc").withReturnValue();
|
||||
procedure.declareParameters(
|
||||
new SqlOutParameter("RETURN_VALUE", Types.INTEGER),
|
||||
new SqlOutParameter("@out_total", Types.INTEGER));
|
||||
procedure.compile();
|
||||
assertThat(procedure.getCallParameters()).extracting(SqlParameter::getName)
|
||||
.containsExactly("RETURN_VALUE", "amount", "@out_total");
|
||||
verifyStatement(procedure, "{? = call my_proc(?, ?)}");
|
||||
}
|
||||
|
||||
|
||||
private void verifyStatement(SimpleJdbcCall adder, String expected) {
|
||||
assertThat(adder.getCallString()).as("Incorrect call statement").isEqualTo(expected);
|
||||
@@ -350,6 +405,41 @@ class SimpleJdbcCallTests {
|
||||
verify(procedureColumnsResultSet).close();
|
||||
}
|
||||
|
||||
private void initializeGetTotalFunctionWithMetaData() 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(4);
|
||||
given(procedureColumnsResultSet.getString("COLUMN_NAME")).willReturn(null, "amount", "out_status");
|
||||
given(procedureColumnsResultSet.getInt("COLUMN_TYPE")).willReturn(5, 1, 4);
|
||||
given(connection.prepareCall("{? = call GET_TOTAL(?, ?)}")).willReturn(callableStatement);
|
||||
given(callableStatement.execute()).willReturn(false);
|
||||
given(callableStatement.getUpdateCount()).willReturn(-1);
|
||||
given(callableStatement.getObject(1)).willReturn(42);
|
||||
given(callableStatement.getObject(3)).willReturn(7);
|
||||
}
|
||||
|
||||
private void initializeSqlServerProcedureWithReturnValue() throws SQLException {
|
||||
ResultSet proceduresResultSet = mock();
|
||||
ResultSet procedureColumnsResultSet = mock();
|
||||
given(databaseMetaData.getDatabaseProductName()).willReturn("Microsoft SQL Server");
|
||||
given(databaseMetaData.getProcedures(null, null, "my_proc")).willReturn(proceduresResultSet);
|
||||
given(databaseMetaData.getProcedureColumns(null, null, "my_proc", null)).willReturn(procedureColumnsResultSet);
|
||||
given(proceduresResultSet.next()).willReturn(true, false);
|
||||
given(proceduresResultSet.getString("PROCEDURE_NAME")).willReturn("my_proc");
|
||||
given(procedureColumnsResultSet.next()).willReturn(true, true, true, false);
|
||||
given(procedureColumnsResultSet.getInt("DATA_TYPE")).willReturn(4);
|
||||
given(procedureColumnsResultSet.getString("COLUMN_NAME")).willReturn("@RETURN_VALUE", "@amount", "@out_total");
|
||||
given(procedureColumnsResultSet.getInt("COLUMN_TYPE")).willReturn(5, 1, 4);
|
||||
}
|
||||
|
||||
@Test
|
||||
void correctSybaseFunctionStatementNamed() throws Exception {
|
||||
given(databaseMetaData.getDatabaseProductName()).willReturn("Sybase");
|
||||
|
||||
Reference in New Issue
Block a user