Skip to content

Commit d4fac82

Browse files
committed
Preserve square brackets for index/key expressions
Closes gh-27925
1 parent 327eec0 commit d4fac82

File tree

4 files changed

+58
-15
lines changed

4 files changed

+58
-15
lines changed

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

Lines changed: 20 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2021 the original author or authors.
2+
* Copyright 2002-2022 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.
@@ -55,7 +55,7 @@ public abstract class NamedParameterUtils {
5555
* Set of characters that qualify as parameter separators,
5656
* indicating that a parameter name in an SQL String has ended.
5757
*/
58-
private static final String PARAMETER_SEPARATORS = "\"':&,;()|=+-*%/\\<>^[]";
58+
private static final String PARAMETER_SEPARATORS = "\"':&,;()|=+-*%/\\<>^]";
5959

6060
/**
6161
* An index with separator flags per character code.
@@ -78,9 +78,9 @@ public abstract class NamedParameterUtils {
7878
* Parse the SQL statement and locate any placeholders or named parameters.
7979
* Named parameters are substituted for a JDBC placeholder.
8080
* @param sql the SQL statement
81-
* @return the parsed statement, represented as ParsedSql instance
81+
* @return the parsed statement, represented as {@link ParsedSql} instance
8282
*/
83-
public static ParsedSql parseSqlStatement(final String sql) {
83+
public static ParsedSql parseSqlStatement(String sql) {
8484
Assert.notNull(sql, "SQL must not be null");
8585

8686
Set<String> namedParameters = new HashSet<>();
@@ -122,17 +122,20 @@ public static ParsedSql parseSqlStatement(final String sql) {
122122
while (statement[j] != '}') {
123123
j++;
124124
if (j >= statement.length) {
125-
throw new InvalidDataAccessApiUsageException("Non-terminated named parameter declaration " +
126-
"at position " + i + " in statement: " + sql);
125+
throw new InvalidDataAccessApiUsageException(
126+
"Non-terminated named parameter declaration at position " + i +
127+
" in statement: " + sql);
127128
}
128129
if (statement[j] == ':' || statement[j] == '{') {
129-
throw new InvalidDataAccessApiUsageException("Parameter name contains invalid character '" +
130-
statement[j] + "' at position " + i + " in statement: " + sql);
130+
throw new InvalidDataAccessApiUsageException(
131+
"Parameter name contains invalid character '" + statement[j] +
132+
"' at position " + i + " in statement: " + sql);
131133
}
132134
}
133135
if (j - i > 2) {
134136
parameter = sql.substring(i + 2, j);
135-
namedParameterCount = addNewNamedParameter(namedParameters, namedParameterCount, parameter);
137+
namedParameterCount = addNewNamedParameter(
138+
namedParameters, namedParameterCount, parameter);
136139
totalParameterCount = addNamedParameter(
137140
parameterList, totalParameterCount, escapes, i, j + 1, parameter);
138141
}
@@ -144,7 +147,11 @@ public static ParsedSql parseSqlStatement(final String sql) {
144147
}
145148
if (j - i > 1) {
146149
parameter = sql.substring(i + 1, j);
147-
namedParameterCount = addNewNamedParameter(namedParameters, namedParameterCount, parameter);
150+
if (parameter.contains("[")) {
151+
parameter += "]"; // preserve end bracket for index/key
152+
}
153+
namedParameterCount = addNewNamedParameter(
154+
namedParameters, namedParameterCount, parameter);
148155
totalParameterCount = addNamedParameter(
149156
parameterList, totalParameterCount, escapes, i, j, parameter);
150157
}
@@ -185,8 +192,8 @@ public static ParsedSql parseSqlStatement(final String sql) {
185192
return parsedSql;
186193
}
187194

188-
private static int addNamedParameter(
189-
List<ParameterHolder> parameterList, int totalParameterCount, int escapes, int i, int j, String parameter) {
195+
private static int addNamedParameter(List<ParameterHolder> parameterList,
196+
int totalParameterCount, int escapes, int i, int j, String parameter) {
190197

191198
parameterList.add(new ParameterHolder(parameter, i - escapes, j - escapes));
192199
totalParameterCount++;
@@ -271,6 +278,7 @@ public static String substituteNamedParameters(ParsedSql parsedSql, @Nullable Sq
271278
if (paramNames.isEmpty()) {
272279
return originalSql;
273280
}
281+
274282
StringBuilder actualSql = new StringBuilder(originalSql.length());
275283
int lastIndex = 0;
276284
for (int i = 0; i < paramNames.size(); i++) {

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

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2021 the original author or authors.
2+
* Copyright 2002-2022 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.
@@ -327,4 +327,29 @@ public void parseSqlStatementWithSquareBracket() {
327327
assertThat(psql.getParameterNames()).containsExactly("ext");
328328
}
329329

330+
@Test // gh-27925
331+
void namedParamMapReference() {
332+
String sql = "insert into foos (id) values (:headers[id])";
333+
ParsedSql psql = NamedParameterUtils.parseSqlStatement(sql);
334+
assertThat(psql.getNamedParameterCount()).isEqualTo(1);
335+
assertThat(psql.getParameterNames()).containsExactly("headers[id]");
336+
337+
class Foo {
338+
final Map<String, Object> headers = new HashMap<>();
339+
public Foo() {
340+
this.headers.put("id", 1);
341+
}
342+
public Map<String, Object> getHeaders() {
343+
return this.headers;
344+
}
345+
}
346+
347+
Foo foo = new Foo();
348+
Object[] params = NamedParameterUtils.buildValueArray(psql,
349+
new BeanPropertySqlParameterSource(foo), null);
350+
351+
assertThat(params[0]).isInstanceOf(SqlParameterValue.class);
352+
assertThat(((SqlParameterValue) params[0]).getValue()).isEqualTo(foo.getHeaders().get("id"));
353+
}
354+
330355
}

spring-r2dbc/src/main/java/org/springframework/r2dbc/core/NamedParameterUtils.java

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,7 @@ abstract class NamedParameterUtils {
6868
* Set of characters that qualify as parameter separators,
6969
* indicating that a parameter name in an SQL String has ended.
7070
*/
71-
private static final String PARAMETER_SEPARATORS = "\"':&,;()|=+-*%/\\<>^[]";
71+
private static final String PARAMETER_SEPARATORS = "\"':&,;()|=+-*%/\\<>^]";
7272

7373
/**
7474
* An index with separator flags per character code.
@@ -160,6 +160,9 @@ public static ParsedSql parseSqlStatement(String sql) {
160160
}
161161
if (j - i > 1) {
162162
parameter = sql.substring(i + 1, j);
163+
if (parameter.contains("[")) {
164+
parameter += "]"; // preserve end bracket for index/key
165+
}
163166
namedParameterCount = addNewNamedParameter(
164167
namedParameters, namedParameterCount, parameter);
165168
totalParameterCount = addNamedParameter(
@@ -295,7 +298,6 @@ public static PreparedOperation<String> substituteNamedParameters(ParsedSql pars
295298
if (paramSource.hasValue(paramName)) {
296299
Parameter parameter = paramSource.getValue(paramName);
297300
if (parameter.getValue() instanceof Collection<?> c) {
298-
299301
Iterator<?> entryIter = c.iterator();
300302
int k = 0;
301303
int counter = 0;

spring-r2dbc/src/test/java/org/springframework/r2dbc/core/NamedParameterUtilsUnitTests.java

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -277,6 +277,14 @@ public void parseSqlStatementWithSquareBracket() {
277277
assertThat(psql.getParameterNames()).containsExactly("ext");
278278
}
279279

280+
@Test // gh-27925
281+
void namedParamMapReference() {
282+
String sql = "insert into foos (id) values (:headers[id])";
283+
ParsedSql psql = NamedParameterUtils.parseSqlStatement(sql);
284+
assertThat(psql.getNamedParameterCount()).isEqualTo(1);
285+
assertThat(psql.getParameterNames()).containsExactly("headers[id]");
286+
}
287+
280288
@Test
281289
public void shouldAllowParsingMultipleUseOfParameter() {
282290
String sql = "SELECT * FROM person where name = :id or lastname = :id";

0 commit comments

Comments
 (0)