Skip to content

Commit ae8a353

Browse files
committed
Reject iterable/collection value for positional parameter
Closes gh-31215
1 parent 8f6c56f commit ae8a353

File tree

2 files changed

+112
-21
lines changed

2 files changed

+112
-21
lines changed

spring-jdbc/src/main/java/org/springframework/jdbc/core/simple/DefaultJdbcClient.java

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,7 @@ public DefaultStatementSpec(String sql) {
106106

107107
@Override
108108
public StatementSpec param(@Nullable Object value) {
109+
validateIndexedParamValue(value);
109110
this.indexedParams.add(value);
110111
return this;
111112
}
@@ -115,6 +116,7 @@ public StatementSpec param(int jdbcIndex, @Nullable Object value) {
115116
if (jdbcIndex < 1) {
116117
throw new IllegalArgumentException("Invalid JDBC index: needs to start at 1");
117118
}
119+
validateIndexedParamValue(value);
118120
int index = jdbcIndex - 1;
119121
int size = this.indexedParams.size();
120122
if (index < size) {
@@ -129,6 +131,14 @@ public StatementSpec param(int jdbcIndex, @Nullable Object value) {
129131
return this;
130132
}
131133

134+
private void validateIndexedParamValue(@Nullable Object value) {
135+
if (value instanceof Iterable) {
136+
throw new IllegalArgumentException("Invalid positional parameter value of type Iterable (" +
137+
value.getClass().getSimpleName() +
138+
"): Parameter expansion is only supported with named parameters.");
139+
}
140+
}
141+
132142
@Override
133143
public StatementSpec param(int jdbcIndex, @Nullable Object value, int sqlType) {
134144
return param(jdbcIndex, new SqlParameterValue(sqlType, value));

spring-jdbc/src/test/java/org/springframework/jdbc/core/simple/JdbcClientQueryTests.java

Lines changed: 102 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -30,11 +30,11 @@
3030

3131
import javax.sql.DataSource;
3232

33-
import org.junit.jupiter.api.AfterEach;
3433
import org.junit.jupiter.api.BeforeEach;
3534
import org.junit.jupiter.api.Test;
3635

3736
import static org.assertj.core.api.Assertions.assertThat;
37+
import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
3838
import static org.mockito.ArgumentMatchers.anyString;
3939
import static org.mockito.BDDMockito.given;
4040
import static org.mockito.Mockito.mock;
@@ -46,10 +46,10 @@
4646
*/
4747
public class JdbcClientQueryTests {
4848

49-
private Connection connection = mock();
50-
5149
private DataSource dataSource = mock();
5250

51+
private Connection connection = mock();
52+
5353
private PreparedStatement preparedStatement = mock();
5454

5555
private ResultSet resultSet = mock();
@@ -62,17 +62,10 @@ public class JdbcClientQueryTests {
6262
@BeforeEach
6363
public void setup() throws Exception {
6464
given(dataSource.getConnection()).willReturn(connection);
65-
given(resultSetMetaData.getColumnCount()).willReturn(1);
66-
given(resultSetMetaData.getColumnLabel(1)).willReturn("age");
6765
given(connection.prepareStatement(anyString())).willReturn(preparedStatement);
6866
given(preparedStatement.executeQuery()).willReturn(resultSet);
69-
}
70-
71-
@AfterEach
72-
public void verifyClose() throws Exception {
73-
verify(preparedStatement).close();
74-
verify(resultSet).close();
75-
verify(connection).close();
67+
given(resultSetMetaData.getColumnCount()).willReturn(1);
68+
given(resultSetMetaData.getColumnLabel(1)).willReturn("age");
7669
}
7770

7871

@@ -93,6 +86,9 @@ public void testQueryForListWithIndexedParam() throws Exception {
9386

9487
verify(connection).prepareStatement("SELECT AGE FROM CUSTMR WHERE ID < ?");
9588
verify(preparedStatement).setObject(1, 3);
89+
verify(resultSet).close();
90+
verify(preparedStatement).close();
91+
verify(connection).close();
9692
}
9793

9894
@Test
@@ -105,6 +101,9 @@ public void testQueryForListWithIndexedParamAndEmptyResult() throws Exception {
105101
assertThat(li.size()).as("All rows returned").isEqualTo(0);
106102
verify(connection).prepareStatement("SELECT AGE FROM CUSTMR WHERE ID < ?");
107103
verify(preparedStatement).setObject(1, 3);
104+
verify(resultSet).close();
105+
verify(preparedStatement).close();
106+
verify(connection).close();
108107
}
109108

110109
@Test
@@ -120,6 +119,9 @@ public void testQueryForListWithIndexedParamAndSingleRow() throws Exception {
120119
assertThat(li.get(0).get("age")).as("First row is Integer").isEqualTo(11);
121120
verify(connection).prepareStatement("SELECT AGE FROM CUSTMR WHERE ID < ?");
122121
verify(preparedStatement).setObject(1, 3);
122+
verify(resultSet).close();
123+
verify(preparedStatement).close();
124+
verify(connection).close();
123125
}
124126

125127
@Test
@@ -136,6 +138,9 @@ public void testQueryForListWithIndexedParamAndSingleColumn() throws Exception {
136138
assertThat(li.get(0)).as("First row is Integer").isEqualTo(11);
137139
verify(connection).prepareStatement("SELECT AGE FROM CUSTMR WHERE ID < ?");
138140
verify(preparedStatement).setObject(1, 3);
141+
verify(resultSet).close();
142+
verify(preparedStatement).close();
143+
verify(connection).close();
139144
}
140145

141146
@Test
@@ -151,10 +156,13 @@ public void testQueryForMapWithIndexedParamAndSingleRow() throws Exception {
151156
assertThat(map.get("age")).as("Row is Integer").isEqualTo(11);
152157
verify(connection).prepareStatement("SELECT AGE FROM CUSTMR WHERE ID < ?");
153158
verify(preparedStatement).setObject(1, 3);
159+
verify(resultSet).close();
160+
verify(preparedStatement).close();
161+
verify(connection).close();
154162
}
155163

156164
@Test
157-
public void testQueryForObjectWithIndexedParamAndRowMapper() throws Exception {
165+
public void testQueryForIntegerWithIndexedParamAndRowMapper() throws Exception {
158166
given(resultSet.next()).willReturn(true, false);
159167
given(resultSet.getInt(1)).willReturn(22);
160168

@@ -165,6 +173,9 @@ public void testQueryForObjectWithIndexedParamAndRowMapper() throws Exception {
165173
assertThat(value).isEqualTo(22);
166174
verify(connection).prepareStatement("SELECT AGE FROM CUSTMR WHERE ID = ?");
167175
verify(preparedStatement).setObject(1, 3);
176+
verify(resultSet).close();
177+
verify(preparedStatement).close();
178+
verify(connection).close();
168179
}
169180

170181
@Test
@@ -179,10 +190,13 @@ public void testQueryForOptionalWithIndexedParamAndRowMapper() throws Exception
179190
assertThat(value.get()).isEqualTo(22);
180191
verify(connection).prepareStatement("SELECT AGE FROM CUSTMR WHERE ID = ?");
181192
verify(preparedStatement).setObject(1, 3);
193+
verify(resultSet).close();
194+
verify(preparedStatement).close();
195+
verify(connection).close();
182196
}
183197

184198
@Test
185-
public void testQueryForObjectWithIndexedParamAndInteger() throws Exception {
199+
public void testQueryForIntegerWithIndexedParam() throws Exception {
186200
given(resultSet.getMetaData()).willReturn(resultSetMetaData);
187201
given(resultSet.next()).willReturn(true, false);
188202
given(resultSet.getObject(1)).willReturn(22);
@@ -194,6 +208,9 @@ public void testQueryForObjectWithIndexedParamAndInteger() throws Exception {
194208
assertThat(value).isEqualTo(22);
195209
verify(connection).prepareStatement("SELECT AGE FROM CUSTMR WHERE ID = ?");
196210
verify(preparedStatement).setObject(1, 3);
211+
verify(resultSet).close();
212+
verify(preparedStatement).close();
213+
verify(connection).close();
197214
}
198215

199216
@Test
@@ -209,6 +226,15 @@ public void testQueryForIntWithIndexedParam() throws Exception {
209226
assertThat(i).as("Return of an int").isEqualTo(22);
210227
verify(connection).prepareStatement("SELECT AGE FROM CUSTMR WHERE ID = ?");
211228
verify(preparedStatement).setObject(1, 3);
229+
verify(resultSet).close();
230+
verify(preparedStatement).close();
231+
verify(connection).close();
232+
}
233+
234+
@Test
235+
public void testQueryForObjectWithIndexedParamAndList() {
236+
assertThatIllegalArgumentException().isThrownBy(() ->
237+
client.sql("SELECT AGE FROM CUSTMR WHERE ID IN (?)").param(Arrays.asList(3, 4)).query().singleValue());
212238
}
213239

214240

@@ -230,6 +256,9 @@ public void testQueryForListWithNamedParam() throws Exception {
230256

231257
verify(connection).prepareStatement("SELECT AGE FROM CUSTMR WHERE ID < ?");
232258
verify(preparedStatement).setObject(1, 3);
259+
verify(resultSet).close();
260+
verify(preparedStatement).close();
261+
verify(connection).close();
233262
}
234263

235264
@Test
@@ -243,6 +272,9 @@ public void testQueryForListWithNamedParamAndEmptyResult() throws Exception {
243272
assertThat(li.size()).as("All rows returned").isEqualTo(0);
244273
verify(connection).prepareStatement("SELECT AGE FROM CUSTMR WHERE ID < ?");
245274
verify(preparedStatement).setObject(1, 3);
275+
verify(resultSet).close();
276+
verify(preparedStatement).close();
277+
verify(connection).close();
246278
}
247279

248280
@Test
@@ -259,6 +291,9 @@ public void testQueryForListWithNamedParamAndSingleRow() throws Exception {
259291
assertThat(li.get(0).get("age")).as("First row is Integer").isEqualTo(11);
260292
verify(connection).prepareStatement("SELECT AGE FROM CUSTMR WHERE ID < ?");
261293
verify(preparedStatement).setObject(1, 3);
294+
verify(resultSet).close();
295+
verify(preparedStatement).close();
296+
verify(connection).close();
262297
}
263298

264299
@Test
@@ -275,6 +310,9 @@ public void testQueryForListWithNamedParamAndSingleColumn() throws Exception {
275310
assertThat(li.get(0)).as("First row is Integer").isEqualTo(11);
276311
verify(connection).prepareStatement("SELECT AGE FROM CUSTMR WHERE ID < ?");
277312
verify(preparedStatement).setObject(1, 3);
313+
verify(resultSet).close();
314+
verify(preparedStatement).close();
315+
verify(connection).close();
278316
}
279317

280318
@Test
@@ -290,10 +328,13 @@ public void testQueryForMapWithNamedParamAndSingleRow() throws Exception {
290328
assertThat(map.get("age")).as("Row is Integer").isEqualTo(11);
291329
verify(connection).prepareStatement("SELECT AGE FROM CUSTMR WHERE ID < ?");
292330
verify(preparedStatement).setObject(1, 3);
331+
verify(resultSet).close();
332+
verify(preparedStatement).close();
333+
verify(connection).close();
293334
}
294335

295336
@Test
296-
public void testQueryForObjectWithNamedParamAndRowMapper() throws Exception {
337+
public void testQueryForIntegerWithNamedParamAndRowMapper() throws Exception {
297338
given(resultSet.next()).willReturn(true, false);
298339
given(resultSet.getInt(1)).willReturn(22);
299340

@@ -305,10 +346,13 @@ public void testQueryForObjectWithNamedParamAndRowMapper() throws Exception {
305346
assertThat(value).isEqualTo(22);
306347
verify(connection).prepareStatement("SELECT AGE FROM CUSTMR WHERE ID = ?");
307348
verify(preparedStatement).setObject(1, 3);
349+
verify(resultSet).close();
350+
verify(preparedStatement).close();
351+
verify(connection).close();
308352
}
309353

310354
@Test
311-
public void testQueryForObjectWithNamedParamAndMappedSimpleValue() throws Exception {
355+
public void testQueryForIntegerWithNamedParamAndMappedSimpleValue() throws Exception {
312356
given(resultSet.getMetaData()).willReturn(resultSetMetaData);
313357
given(resultSet.next()).willReturn(true, false);
314358
given(resultSet.getInt(1)).willReturn(22);
@@ -321,10 +365,13 @@ public void testQueryForObjectWithNamedParamAndMappedSimpleValue() throws Except
321365
assertThat(value).isEqualTo(22);
322366
verify(connection).prepareStatement("SELECT AGE FROM CUSTMR WHERE ID = ?");
323367
verify(preparedStatement).setObject(1, 3);
368+
verify(resultSet).close();
369+
verify(preparedStatement).close();
370+
verify(connection).close();
324371
}
325372

326373
@Test
327-
public void testQueryForObjectWithNamedParamAndMappedRecord() throws Exception {
374+
public void testQueryForMappedRecordWithNamedParam() throws Exception {
328375
given(resultSet.getMetaData()).willReturn(resultSetMetaData);
329376
given(resultSet.findColumn("age")).willReturn(1);
330377
given(resultSet.next()).willReturn(true, false);
@@ -338,10 +385,13 @@ public void testQueryForObjectWithNamedParamAndMappedRecord() throws Exception {
338385
assertThat(value.age()).isEqualTo(22);
339386
verify(connection).prepareStatement("SELECT AGE FROM CUSTMR WHERE ID = ?");
340387
verify(preparedStatement).setObject(1, 3);
388+
verify(resultSet).close();
389+
verify(preparedStatement).close();
390+
verify(connection).close();
341391
}
342392

343393
@Test
344-
public void testQueryForObjectWithNamedParamAndMappedFieldHolder() throws Exception {
394+
public void testQueryForMappedFieldHolderWithNamedParam() throws Exception {
345395
given(resultSet.getMetaData()).willReturn(resultSetMetaData);
346396
given(resultSet.next()).willReturn(true, false);
347397
given(resultSet.getInt(1)).willReturn(22);
@@ -354,10 +404,13 @@ public void testQueryForObjectWithNamedParamAndMappedFieldHolder() throws Except
354404
assertThat(value.age).isEqualTo(22);
355405
verify(connection).prepareStatement("SELECT AGE FROM CUSTMR WHERE ID = ?");
356406
verify(preparedStatement).setObject(1, 3);
407+
verify(resultSet).close();
408+
verify(preparedStatement).close();
409+
verify(connection).close();
357410
}
358411

359412
@Test
360-
public void testQueryForObjectWithNamedParamAndInteger() throws Exception {
413+
public void testQueryForIntegerWithNamedParam() throws Exception {
361414
given(resultSet.getMetaData()).willReturn(resultSetMetaData);
362415
given(resultSet.next()).willReturn(true, false);
363416
given(resultSet.getObject(1)).willReturn(22);
@@ -369,10 +422,13 @@ public void testQueryForObjectWithNamedParamAndInteger() throws Exception {
369422
assertThat(value).isEqualTo(22);
370423
verify(connection).prepareStatement("SELECT AGE FROM CUSTMR WHERE ID = ?");
371424
verify(preparedStatement).setObject(1, 3);
425+
verify(resultSet).close();
426+
verify(preparedStatement).close();
427+
verify(connection).close();
372428
}
373429

374430
@Test
375-
public void testQueryForObjectWithNamedParamAndList() throws Exception {
431+
public void testQueryForIntegerWithNamedParamAndList() throws Exception {
376432
given(resultSet.getMetaData()).willReturn(resultSetMetaData);
377433
given(resultSet.next()).willReturn(true, false);
378434
given(resultSet.getObject(1)).willReturn(22);
@@ -384,10 +440,14 @@ public void testQueryForObjectWithNamedParamAndList() throws Exception {
384440
assertThat(value).isEqualTo(22);
385441
verify(connection).prepareStatement("SELECT AGE FROM CUSTMR WHERE ID IN (?, ?)");
386442
verify(preparedStatement).setObject(1, 3);
443+
verify(preparedStatement).setObject(2, 4);
444+
verify(resultSet).close();
445+
verify(preparedStatement).close();
446+
verify(connection).close();
387447
}
388448

389449
@Test
390-
public void testQueryForObjectWithNamedParamAndListOfExpressionLists() throws Exception {
450+
public void testQueryForIntegerWithNamedParamAndListOfExpressionLists() throws Exception {
391451
given(resultSet.getMetaData()).willReturn(resultSetMetaData);
392452
given(resultSet.next()).willReturn(true, false);
393453
given(resultSet.getObject(1)).willReturn(22);
@@ -402,6 +462,12 @@ public void testQueryForObjectWithNamedParamAndListOfExpressionLists() throws Ex
402462
assertThat(value).isEqualTo(22);
403463
verify(connection).prepareStatement("SELECT AGE FROM CUSTMR WHERE (ID, NAME) IN ((?, ?), (?, ?))");
404464
verify(preparedStatement).setObject(1, 3);
465+
verify(preparedStatement).setString(2, "Rod");
466+
verify(preparedStatement).setObject(3, 4);
467+
verify(preparedStatement).setString(4, "Juergen");
468+
verify(resultSet).close();
469+
verify(preparedStatement).close();
470+
verify(connection).close();
405471
}
406472

407473
@Test
@@ -417,6 +483,9 @@ public void testQueryForIntWithNamedParam() throws Exception {
417483
assertThat(i).as("Return of an int").isEqualTo(22);
418484
verify(connection).prepareStatement("SELECT AGE FROM CUSTMR WHERE ID = ?");
419485
verify(preparedStatement).setObject(1, 3);
486+
verify(resultSet).close();
487+
verify(preparedStatement).close();
488+
verify(connection).close();
420489
}
421490

422491
@Test
@@ -432,6 +501,9 @@ public void testQueryForLongWithParamBean() throws Exception {
432501
assertThat(l).as("Return of a long").isEqualTo(87);
433502
verify(connection).prepareStatement("SELECT AGE FROM CUSTMR WHERE ID = ?");
434503
verify(preparedStatement).setObject(1, 3, Types.INTEGER);
504+
verify(resultSet).close();
505+
verify(preparedStatement).close();
506+
verify(connection).close();
435507
}
436508

437509
@Test
@@ -448,6 +520,9 @@ public void testQueryForLongWithParamBeanWithCollection() throws Exception {
448520
verify(connection).prepareStatement("SELECT AGE FROM CUSTMR WHERE ID IN (?, ?)");
449521
verify(preparedStatement).setObject(1, 3);
450522
verify(preparedStatement).setObject(2, 5);
523+
verify(resultSet).close();
524+
verify(preparedStatement).close();
525+
verify(connection).close();
451526
}
452527

453528
@Test
@@ -463,6 +538,9 @@ public void testQueryForLongWithParamRecord() throws Exception {
463538
assertThat(l).as("Return of a long").isEqualTo(87);
464539
verify(connection).prepareStatement("SELECT AGE FROM CUSTMR WHERE ID = ?");
465540
verify(preparedStatement).setObject(1, 3, Types.INTEGER);
541+
verify(resultSet).close();
542+
verify(preparedStatement).close();
543+
verify(connection).close();
466544
}
467545

468546
@Test
@@ -478,6 +556,9 @@ public void testQueryForLongWithParamFieldHolder() throws Exception {
478556
assertThat(l).as("Return of a long").isEqualTo(87);
479557
verify(connection).prepareStatement("SELECT AGE FROM CUSTMR WHERE ID = ?");
480558
verify(preparedStatement).setObject(1, 3, Types.INTEGER);
559+
verify(resultSet).close();
560+
verify(preparedStatement).close();
561+
verify(connection).close();
481562
}
482563

483564

0 commit comments

Comments
 (0)