Skip to content

Commit b2322e5

Browse files
committed
Support for PostgreSQL array syntax
Includes efficient separator determination. Issue: SPR-16340
1 parent d9af4d6 commit b2322e5

File tree

2 files changed

+67
-28
lines changed

2 files changed

+67
-28
lines changed

spring-jdbc/src/main/java/org/springframework/jdbc/core/namedparam/NamedParameterUtils.java

Lines changed: 23 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2017 the original author or authors.
2+
* Copyright 2002-2018 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -43,23 +43,32 @@
4343
public abstract class NamedParameterUtils {
4444

4545
/**
46-
* Set of characters that qualify as parameter separators,
47-
* indicating that a parameter name in a SQL String has ended.
46+
* Set of characters that qualify as comment or quotes starting characters.
4847
*/
49-
private static final char[] PARAMETER_SEPARATORS =
50-
new char[] {'"', '\'', ':', '&', ',', ';', '(', ')', '|', '=', '+', '-', '*', '%', '/', '\\', '<', '>', '^'};
48+
private static final String[] START_SKIP = new String[] {"'", "\"", "--", "/*"};
5149

5250
/**
53-
* Set of characters that qualify as comment or quotes starting characters.
51+
* Set of characters that at are the corresponding comment or quotes ending characters.
5452
*/
55-
private static final String[] START_SKIP =
56-
new String[] {"'", "\"", "--", "/*"};
53+
private static final String[] STOP_SKIP = new String[] {"'", "\"", "\n", "*/"};
5754

5855
/**
59-
* Set of characters that at are the corresponding comment or quotes ending characters.
56+
* Set of characters that qualify as parameter separators,
57+
* indicating that a parameter name in a SQL String has ended.
58+
*/
59+
private static final String PARAMETER_SEPARATORS = "\"':&,;()[]|=+-*%/\\<>^";
60+
61+
/**
62+
* An index with separator flags per character code.
63+
* Technically only needed between 34 and 124 at this point.
6064
*/
61-
private static final String[] STOP_SKIP =
62-
new String[] {"'", "\"", "\n", "*/"};
65+
private static final boolean[] separatorIndex = new boolean[128];
66+
67+
static {
68+
for (char c : PARAMETER_SEPARATORS.toCharArray()) {
69+
separatorIndex[c] = true;
70+
}
71+
}
6372

6473

6574
//-------------------------------------------------------------------------
@@ -233,7 +242,6 @@ private static int skipCommentsAndQuotes(char[] statement, int position) {
233242
// character sequence ending comment or quote not found
234243
return statement.length;
235244
}
236-
237245
}
238246
}
239247
return position;
@@ -384,15 +392,7 @@ private static SqlParameter findParameter(
384392
* that is, whether the given character qualifies as a separator.
385393
*/
386394
private static boolean isParameterSeparator(char c) {
387-
if (Character.isWhitespace(c)) {
388-
return true;
389-
}
390-
for (char separator : PARAMETER_SEPARATORS) {
391-
if (c == separator) {
392-
return true;
393-
}
394-
}
395-
return false;
395+
return (separatorIndex[c] || Character.isWhitespace(c));
396396
}
397397

398398
/**
@@ -425,8 +425,8 @@ public static List<SqlParameter> buildSqlParameterList(ParsedSql parsedSql, SqlP
425425
List<String> paramNames = parsedSql.getParameterNames();
426426
List<SqlParameter> params = new LinkedList<>();
427427
for (String paramName : paramNames) {
428-
params.add(
429-
new SqlParameter(paramName, paramSource.getSqlType(paramName), paramSource.getTypeName(paramName)));
428+
params.add(new SqlParameter(
429+
paramName, paramSource.getSqlType(paramName), paramSource.getTypeName(paramName)));
430430
}
431431
return params;
432432
}

spring-jdbc/src/test/java/org/springframework/jdbc/core/namedparam/NamedParameterJdbcTemplateTests.java

Lines changed: 44 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2017 the original author or authors.
2+
* Copyright 2002-2018 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -22,6 +22,7 @@
2222
import java.sql.ResultSet;
2323
import java.sql.SQLException;
2424
import java.sql.Types;
25+
import java.util.Arrays;
2526
import java.util.Collections;
2627
import java.util.HashMap;
2728
import java.util.LinkedList;
@@ -47,32 +48,45 @@
4748
* @author Rick Evans
4849
* @author Juergen Hoeller
4950
* @author Chris Beams
51+
* @author Nikita Khateev
5052
*/
5153
public class NamedParameterJdbcTemplateTests {
5254

5355
private static final String SELECT_NAMED_PARAMETERS =
54-
"select id, forename from custmr where id = :id and country = :country";
56+
"select id, forename from custmr where id = :id and country = :country";
5557
private static final String SELECT_NAMED_PARAMETERS_PARSED =
56-
"select id, forename from custmr where id = ? and country = ?";
58+
"select id, forename from custmr where id = ? and country = ?";
5759
private static final String SELECT_NO_PARAMETERS =
5860
"select id, forename from custmr";
5961

6062
private static final String UPDATE_NAMED_PARAMETERS =
61-
"update seat_status set booking_id = null where performance_id = :perfId and price_band_id = :priceId";
63+
"update seat_status set booking_id = null where performance_id = :perfId and price_band_id = :priceId";
6264
private static final String UPDATE_NAMED_PARAMETERS_PARSED =
63-
"update seat_status set booking_id = null where performance_id = ? and price_band_id = ?";
65+
"update seat_status set booking_id = null where performance_id = ? and price_band_id = ?";
66+
67+
private static final String UPDATE_ARRAY_PARAMETERS =
68+
"update customer set type = array[:typeIds] where id = :id";
69+
private static final String UPDATE_ARRAY_PARAMETERS_PARSED =
70+
"update customer set type = array[?, ?, ?] where id = ?";
6471

6572
private static final String[] COLUMN_NAMES = new String[] {"id", "forename"};
6673

74+
6775
@Rule
6876
public ExpectedException thrown = ExpectedException.none();
6977

7078
private Connection connection;
79+
7180
private DataSource dataSource;
81+
7282
private PreparedStatement preparedStatement;
83+
7384
private ResultSet resultSet;
85+
7486
private DatabaseMetaData databaseMetaData;
87+
7588
private Map<String, Object> params = new HashMap<>();
89+
7690
private NamedParameterJdbcTemplate namedParameterTemplate;
7791

7892

@@ -131,6 +145,31 @@ public void testExecute() throws SQLException {
131145
verify(connection).close();
132146
}
133147

148+
@Test
149+
public void testExecuteArray() throws SQLException {
150+
given(preparedStatement.executeUpdate()).willReturn(1);
151+
152+
List<Integer> typeIds = Arrays.asList(1, 2, 3);
153+
154+
params.put("typeIds", typeIds);
155+
params.put("id", 1);
156+
Object result = namedParameterTemplate.execute(UPDATE_ARRAY_PARAMETERS, params,
157+
(PreparedStatementCallback<Object>) ps -> {
158+
assertEquals(preparedStatement, ps);
159+
ps.executeUpdate();
160+
return "result";
161+
});
162+
163+
assertEquals("result", result);
164+
verify(connection).prepareStatement(UPDATE_ARRAY_PARAMETERS_PARSED);
165+
verify(preparedStatement).setObject(1, 1);
166+
verify(preparedStatement).setObject(2, 2);
167+
verify(preparedStatement).setObject(3, 3);
168+
verify(preparedStatement).setObject(4, 1);
169+
verify(preparedStatement).close();
170+
verify(connection).close();
171+
}
172+
134173
@Test
135174
public void testExecuteWithTypedParameters() throws SQLException {
136175
given(preparedStatement.executeUpdate()).willReturn(1);

0 commit comments

Comments
 (0)