From ae8a3530419b6bd4f983ba6141d1f5bab4c66b46 Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Wed, 13 Sep 2023 16:50:01 +0200 Subject: [PATCH] Reject iterable/collection value for positional parameter Closes gh-31215 --- .../jdbc/core/simple/DefaultJdbcClient.java | 10 ++ .../core/simple/JdbcClientQueryTests.java | 123 +++++++++++++++--- 2 files changed, 112 insertions(+), 21 deletions(-) diff --git a/spring-jdbc/src/main/java/org/springframework/jdbc/core/simple/DefaultJdbcClient.java b/spring-jdbc/src/main/java/org/springframework/jdbc/core/simple/DefaultJdbcClient.java index b9b44bd73a..c15365fec8 100644 --- a/spring-jdbc/src/main/java/org/springframework/jdbc/core/simple/DefaultJdbcClient.java +++ b/spring-jdbc/src/main/java/org/springframework/jdbc/core/simple/DefaultJdbcClient.java @@ -106,6 +106,7 @@ final class DefaultJdbcClient implements JdbcClient { @Override public StatementSpec param(@Nullable Object value) { + validateIndexedParamValue(value); this.indexedParams.add(value); return this; } @@ -115,6 +116,7 @@ final class DefaultJdbcClient implements JdbcClient { if (jdbcIndex < 1) { throw new IllegalArgumentException("Invalid JDBC index: needs to start at 1"); } + validateIndexedParamValue(value); int index = jdbcIndex - 1; int size = this.indexedParams.size(); if (index < size) { @@ -129,6 +131,14 @@ final class DefaultJdbcClient implements JdbcClient { return this; } + private void validateIndexedParamValue(@Nullable Object value) { + if (value instanceof Iterable) { + throw new IllegalArgumentException("Invalid positional parameter value of type Iterable (" + + value.getClass().getSimpleName() + + "): Parameter expansion is only supported with named parameters."); + } + } + @Override public StatementSpec param(int jdbcIndex, @Nullable Object value, int sqlType) { return param(jdbcIndex, new SqlParameterValue(sqlType, value)); diff --git a/spring-jdbc/src/test/java/org/springframework/jdbc/core/simple/JdbcClientQueryTests.java b/spring-jdbc/src/test/java/org/springframework/jdbc/core/simple/JdbcClientQueryTests.java index 4d006aa34e..24085826b8 100644 --- a/spring-jdbc/src/test/java/org/springframework/jdbc/core/simple/JdbcClientQueryTests.java +++ b/spring-jdbc/src/test/java/org/springframework/jdbc/core/simple/JdbcClientQueryTests.java @@ -30,11 +30,11 @@ import java.util.Optional; import javax.sql.DataSource; -import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException; import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.BDDMockito.given; import static org.mockito.Mockito.mock; @@ -46,10 +46,10 @@ import static org.mockito.Mockito.verify; */ public class JdbcClientQueryTests { - private Connection connection = mock(); - private DataSource dataSource = mock(); + private Connection connection = mock(); + private PreparedStatement preparedStatement = mock(); private ResultSet resultSet = mock(); @@ -62,17 +62,10 @@ public class JdbcClientQueryTests { @BeforeEach public void setup() throws Exception { given(dataSource.getConnection()).willReturn(connection); - given(resultSetMetaData.getColumnCount()).willReturn(1); - given(resultSetMetaData.getColumnLabel(1)).willReturn("age"); given(connection.prepareStatement(anyString())).willReturn(preparedStatement); given(preparedStatement.executeQuery()).willReturn(resultSet); - } - - @AfterEach - public void verifyClose() throws Exception { - verify(preparedStatement).close(); - verify(resultSet).close(); - verify(connection).close(); + given(resultSetMetaData.getColumnCount()).willReturn(1); + given(resultSetMetaData.getColumnLabel(1)).willReturn("age"); } @@ -93,6 +86,9 @@ public class JdbcClientQueryTests { verify(connection).prepareStatement("SELECT AGE FROM CUSTMR WHERE ID < ?"); verify(preparedStatement).setObject(1, 3); + verify(resultSet).close(); + verify(preparedStatement).close(); + verify(connection).close(); } @Test @@ -105,6 +101,9 @@ public class JdbcClientQueryTests { assertThat(li.size()).as("All rows returned").isEqualTo(0); verify(connection).prepareStatement("SELECT AGE FROM CUSTMR WHERE ID < ?"); verify(preparedStatement).setObject(1, 3); + verify(resultSet).close(); + verify(preparedStatement).close(); + verify(connection).close(); } @Test @@ -120,6 +119,9 @@ public class JdbcClientQueryTests { assertThat(li.get(0).get("age")).as("First row is Integer").isEqualTo(11); verify(connection).prepareStatement("SELECT AGE FROM CUSTMR WHERE ID < ?"); verify(preparedStatement).setObject(1, 3); + verify(resultSet).close(); + verify(preparedStatement).close(); + verify(connection).close(); } @Test @@ -136,6 +138,9 @@ public class JdbcClientQueryTests { assertThat(li.get(0)).as("First row is Integer").isEqualTo(11); verify(connection).prepareStatement("SELECT AGE FROM CUSTMR WHERE ID < ?"); verify(preparedStatement).setObject(1, 3); + verify(resultSet).close(); + verify(preparedStatement).close(); + verify(connection).close(); } @Test @@ -151,10 +156,13 @@ public class JdbcClientQueryTests { assertThat(map.get("age")).as("Row is Integer").isEqualTo(11); verify(connection).prepareStatement("SELECT AGE FROM CUSTMR WHERE ID < ?"); verify(preparedStatement).setObject(1, 3); + verify(resultSet).close(); + verify(preparedStatement).close(); + verify(connection).close(); } @Test - public void testQueryForObjectWithIndexedParamAndRowMapper() throws Exception { + public void testQueryForIntegerWithIndexedParamAndRowMapper() throws Exception { given(resultSet.next()).willReturn(true, false); given(resultSet.getInt(1)).willReturn(22); @@ -165,6 +173,9 @@ public class JdbcClientQueryTests { assertThat(value).isEqualTo(22); verify(connection).prepareStatement("SELECT AGE FROM CUSTMR WHERE ID = ?"); verify(preparedStatement).setObject(1, 3); + verify(resultSet).close(); + verify(preparedStatement).close(); + verify(connection).close(); } @Test @@ -179,10 +190,13 @@ public class JdbcClientQueryTests { assertThat(value.get()).isEqualTo(22); verify(connection).prepareStatement("SELECT AGE FROM CUSTMR WHERE ID = ?"); verify(preparedStatement).setObject(1, 3); + verify(resultSet).close(); + verify(preparedStatement).close(); + verify(connection).close(); } @Test - public void testQueryForObjectWithIndexedParamAndInteger() throws Exception { + public void testQueryForIntegerWithIndexedParam() throws Exception { given(resultSet.getMetaData()).willReturn(resultSetMetaData); given(resultSet.next()).willReturn(true, false); given(resultSet.getObject(1)).willReturn(22); @@ -194,6 +208,9 @@ public class JdbcClientQueryTests { assertThat(value).isEqualTo(22); verify(connection).prepareStatement("SELECT AGE FROM CUSTMR WHERE ID = ?"); verify(preparedStatement).setObject(1, 3); + verify(resultSet).close(); + verify(preparedStatement).close(); + verify(connection).close(); } @Test @@ -209,6 +226,15 @@ public class JdbcClientQueryTests { assertThat(i).as("Return of an int").isEqualTo(22); verify(connection).prepareStatement("SELECT AGE FROM CUSTMR WHERE ID = ?"); verify(preparedStatement).setObject(1, 3); + verify(resultSet).close(); + verify(preparedStatement).close(); + verify(connection).close(); + } + + @Test + public void testQueryForObjectWithIndexedParamAndList() { + assertThatIllegalArgumentException().isThrownBy(() -> + client.sql("SELECT AGE FROM CUSTMR WHERE ID IN (?)").param(Arrays.asList(3, 4)).query().singleValue()); } @@ -230,6 +256,9 @@ public class JdbcClientQueryTests { verify(connection).prepareStatement("SELECT AGE FROM CUSTMR WHERE ID < ?"); verify(preparedStatement).setObject(1, 3); + verify(resultSet).close(); + verify(preparedStatement).close(); + verify(connection).close(); } @Test @@ -243,6 +272,9 @@ public class JdbcClientQueryTests { assertThat(li.size()).as("All rows returned").isEqualTo(0); verify(connection).prepareStatement("SELECT AGE FROM CUSTMR WHERE ID < ?"); verify(preparedStatement).setObject(1, 3); + verify(resultSet).close(); + verify(preparedStatement).close(); + verify(connection).close(); } @Test @@ -259,6 +291,9 @@ public class JdbcClientQueryTests { assertThat(li.get(0).get("age")).as("First row is Integer").isEqualTo(11); verify(connection).prepareStatement("SELECT AGE FROM CUSTMR WHERE ID < ?"); verify(preparedStatement).setObject(1, 3); + verify(resultSet).close(); + verify(preparedStatement).close(); + verify(connection).close(); } @Test @@ -275,6 +310,9 @@ public class JdbcClientQueryTests { assertThat(li.get(0)).as("First row is Integer").isEqualTo(11); verify(connection).prepareStatement("SELECT AGE FROM CUSTMR WHERE ID < ?"); verify(preparedStatement).setObject(1, 3); + verify(resultSet).close(); + verify(preparedStatement).close(); + verify(connection).close(); } @Test @@ -290,10 +328,13 @@ public class JdbcClientQueryTests { assertThat(map.get("age")).as("Row is Integer").isEqualTo(11); verify(connection).prepareStatement("SELECT AGE FROM CUSTMR WHERE ID < ?"); verify(preparedStatement).setObject(1, 3); + verify(resultSet).close(); + verify(preparedStatement).close(); + verify(connection).close(); } @Test - public void testQueryForObjectWithNamedParamAndRowMapper() throws Exception { + public void testQueryForIntegerWithNamedParamAndRowMapper() throws Exception { given(resultSet.next()).willReturn(true, false); given(resultSet.getInt(1)).willReturn(22); @@ -305,10 +346,13 @@ public class JdbcClientQueryTests { assertThat(value).isEqualTo(22); verify(connection).prepareStatement("SELECT AGE FROM CUSTMR WHERE ID = ?"); verify(preparedStatement).setObject(1, 3); + verify(resultSet).close(); + verify(preparedStatement).close(); + verify(connection).close(); } @Test - public void testQueryForObjectWithNamedParamAndMappedSimpleValue() throws Exception { + public void testQueryForIntegerWithNamedParamAndMappedSimpleValue() throws Exception { given(resultSet.getMetaData()).willReturn(resultSetMetaData); given(resultSet.next()).willReturn(true, false); given(resultSet.getInt(1)).willReturn(22); @@ -321,10 +365,13 @@ public class JdbcClientQueryTests { assertThat(value).isEqualTo(22); verify(connection).prepareStatement("SELECT AGE FROM CUSTMR WHERE ID = ?"); verify(preparedStatement).setObject(1, 3); + verify(resultSet).close(); + verify(preparedStatement).close(); + verify(connection).close(); } @Test - public void testQueryForObjectWithNamedParamAndMappedRecord() throws Exception { + public void testQueryForMappedRecordWithNamedParam() throws Exception { given(resultSet.getMetaData()).willReturn(resultSetMetaData); given(resultSet.findColumn("age")).willReturn(1); given(resultSet.next()).willReturn(true, false); @@ -338,10 +385,13 @@ public class JdbcClientQueryTests { assertThat(value.age()).isEqualTo(22); verify(connection).prepareStatement("SELECT AGE FROM CUSTMR WHERE ID = ?"); verify(preparedStatement).setObject(1, 3); + verify(resultSet).close(); + verify(preparedStatement).close(); + verify(connection).close(); } @Test - public void testQueryForObjectWithNamedParamAndMappedFieldHolder() throws Exception { + public void testQueryForMappedFieldHolderWithNamedParam() throws Exception { given(resultSet.getMetaData()).willReturn(resultSetMetaData); given(resultSet.next()).willReturn(true, false); given(resultSet.getInt(1)).willReturn(22); @@ -354,10 +404,13 @@ public class JdbcClientQueryTests { assertThat(value.age).isEqualTo(22); verify(connection).prepareStatement("SELECT AGE FROM CUSTMR WHERE ID = ?"); verify(preparedStatement).setObject(1, 3); + verify(resultSet).close(); + verify(preparedStatement).close(); + verify(connection).close(); } @Test - public void testQueryForObjectWithNamedParamAndInteger() throws Exception { + public void testQueryForIntegerWithNamedParam() throws Exception { given(resultSet.getMetaData()).willReturn(resultSetMetaData); given(resultSet.next()).willReturn(true, false); given(resultSet.getObject(1)).willReturn(22); @@ -369,10 +422,13 @@ public class JdbcClientQueryTests { assertThat(value).isEqualTo(22); verify(connection).prepareStatement("SELECT AGE FROM CUSTMR WHERE ID = ?"); verify(preparedStatement).setObject(1, 3); + verify(resultSet).close(); + verify(preparedStatement).close(); + verify(connection).close(); } @Test - public void testQueryForObjectWithNamedParamAndList() throws Exception { + public void testQueryForIntegerWithNamedParamAndList() throws Exception { given(resultSet.getMetaData()).willReturn(resultSetMetaData); given(resultSet.next()).willReturn(true, false); given(resultSet.getObject(1)).willReturn(22); @@ -384,10 +440,14 @@ public class JdbcClientQueryTests { assertThat(value).isEqualTo(22); verify(connection).prepareStatement("SELECT AGE FROM CUSTMR WHERE ID IN (?, ?)"); verify(preparedStatement).setObject(1, 3); + verify(preparedStatement).setObject(2, 4); + verify(resultSet).close(); + verify(preparedStatement).close(); + verify(connection).close(); } @Test - public void testQueryForObjectWithNamedParamAndListOfExpressionLists() throws Exception { + public void testQueryForIntegerWithNamedParamAndListOfExpressionLists() throws Exception { given(resultSet.getMetaData()).willReturn(resultSetMetaData); given(resultSet.next()).willReturn(true, false); given(resultSet.getObject(1)).willReturn(22); @@ -402,6 +462,12 @@ public class JdbcClientQueryTests { assertThat(value).isEqualTo(22); verify(connection).prepareStatement("SELECT AGE FROM CUSTMR WHERE (ID, NAME) IN ((?, ?), (?, ?))"); verify(preparedStatement).setObject(1, 3); + verify(preparedStatement).setString(2, "Rod"); + verify(preparedStatement).setObject(3, 4); + verify(preparedStatement).setString(4, "Juergen"); + verify(resultSet).close(); + verify(preparedStatement).close(); + verify(connection).close(); } @Test @@ -417,6 +483,9 @@ public class JdbcClientQueryTests { assertThat(i).as("Return of an int").isEqualTo(22); verify(connection).prepareStatement("SELECT AGE FROM CUSTMR WHERE ID = ?"); verify(preparedStatement).setObject(1, 3); + verify(resultSet).close(); + verify(preparedStatement).close(); + verify(connection).close(); } @Test @@ -432,6 +501,9 @@ public class JdbcClientQueryTests { assertThat(l).as("Return of a long").isEqualTo(87); verify(connection).prepareStatement("SELECT AGE FROM CUSTMR WHERE ID = ?"); verify(preparedStatement).setObject(1, 3, Types.INTEGER); + verify(resultSet).close(); + verify(preparedStatement).close(); + verify(connection).close(); } @Test @@ -448,6 +520,9 @@ public class JdbcClientQueryTests { verify(connection).prepareStatement("SELECT AGE FROM CUSTMR WHERE ID IN (?, ?)"); verify(preparedStatement).setObject(1, 3); verify(preparedStatement).setObject(2, 5); + verify(resultSet).close(); + verify(preparedStatement).close(); + verify(connection).close(); } @Test @@ -463,6 +538,9 @@ public class JdbcClientQueryTests { assertThat(l).as("Return of a long").isEqualTo(87); verify(connection).prepareStatement("SELECT AGE FROM CUSTMR WHERE ID = ?"); verify(preparedStatement).setObject(1, 3, Types.INTEGER); + verify(resultSet).close(); + verify(preparedStatement).close(); + verify(connection).close(); } @Test @@ -478,6 +556,9 @@ public class JdbcClientQueryTests { assertThat(l).as("Return of a long").isEqualTo(87); verify(connection).prepareStatement("SELECT AGE FROM CUSTMR WHERE ID = ?"); verify(preparedStatement).setObject(1, 3, Types.INTEGER); + verify(resultSet).close(); + verify(preparedStatement).close(); + verify(connection).close(); }