Browse Source

Preserve nested square brackets within parameter name

Closes gh-31596
6.0.x
Juergen Hoeller 10 months ago
parent
commit
99327b7db1
  1. 25
      spring-jdbc/src/main/java/org/springframework/jdbc/core/namedparam/NamedParameterUtils.java
  2. 14
      spring-jdbc/src/test/java/org/springframework/jdbc/core/namedparam/NamedParameterUtilsTests.java
  3. 29
      spring-r2dbc/src/main/java/org/springframework/r2dbc/core/NamedParameterUtils.java

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

@ -1,5 +1,5 @@
/* /*
* Copyright 2002-2022 the original author or authors. * Copyright 2002-2023 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -55,7 +55,7 @@ public abstract class NamedParameterUtils {
* Set of characters that qualify as parameter separators, * Set of characters that qualify as parameter separators,
* indicating that a parameter name in an SQL String has ended. * indicating that a parameter name in an SQL String has ended.
*/ */
private static final String PARAMETER_SEPARATORS = "\"':&,;()|=+-*%/\\<>^]"; private static final String PARAMETER_SEPARATORS = "\"':&,;()|=+-*%/\\<>^";
/** /**
* An index with separator flags per character code. * An index with separator flags per character code.
@ -142,16 +142,25 @@ public abstract class NamedParameterUtils {
j++; j++;
} }
else { else {
while (j < statement.length && !isParameterSeparator(statement[j])) { boolean paramWithSquareBrackets = false;
while (j < statement.length) {
c = statement[j];
if (isParameterSeparator(c)) {
break;
}
if (c == '[') {
paramWithSquareBrackets = true;
}
else if (c == ']') {
if (!paramWithSquareBrackets) {
break;
}
paramWithSquareBrackets = false;
}
j++; j++;
} }
if (j - i > 1) { if (j - i > 1) {
parameter = sql.substring(i + 1, j); parameter = sql.substring(i + 1, j);
if (j < statement.length && statement[j] == ']' && parameter.contains("[")) {
// preserve end bracket for index/key
j++;
parameter = sql.substring(i + 1, j);
}
namedParameterCount = addNewNamedParameter( namedParameterCount = addNewNamedParameter(
namedParameters, namedParameterCount, parameter); namedParameters, namedParameterCount, parameter);
totalParameterCount = addNamedParameter( totalParameterCount = addNamedParameter(

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

@ -1,5 +1,5 @@
/* /*
* Copyright 2002-2022 the original author or authors. * Copyright 2002-2023 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -330,6 +330,18 @@ public class NamedParameterUtilsTests {
assertThat(sqlToUse).isEqualTo("SELECT ARRAY[?]"); assertThat(sqlToUse).isEqualTo("SELECT ARRAY[?]");
} }
@Test // gh-31596
void paramNameWithNestedSquareBrackets() {
String sql = "insert into GeneratedAlways (id, first_name, last_name) values " +
"(:records[0].id, :records[0].firstName, :records[0].lastName), " +
"(:records[1].id, :records[1].firstName, :records[1].lastName)";
ParsedSql parsedSql = NamedParameterUtils.parseSqlStatement(sql);
assertThat(parsedSql.getParameterNames()).containsOnly(
"records[0].id", "records[0].firstName", "records[0].lastName",
"records[1].id", "records[1].firstName", "records[1].lastName");
}
@Test // gh-27925 @Test // gh-27925
void namedParamMapReference() { void namedParamMapReference() {
String sql = "insert into foos (id) values (:headers[id])"; String sql = "insert into foos (id) values (:headers[id])";

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

@ -67,7 +67,7 @@ abstract class NamedParameterUtils {
* Set of characters that qualify as parameter separators, * Set of characters that qualify as parameter separators,
* indicating that a parameter name in an SQL String has ended. * indicating that a parameter name in an SQL String has ended.
*/ */
private static final String PARAMETER_SEPARATORS = "\"':&,;()|=+-*%/\\<>^]"; private static final String PARAMETER_SEPARATORS = "\"':&,;()|=+-*%/\\<>^";
/** /**
* An index with separator flags per character code. * An index with separator flags per character code.
@ -83,12 +83,12 @@ abstract class NamedParameterUtils {
// ------------------------------------------------------------------------- // -------------------------------------------------------------------------
// Core methods used by NamedParameterSupport. // Core methods used by NamedParameterExpander
// ------------------------------------------------------------------------- // -------------------------------------------------------------------------
/** /**
* Parse the SQL statement and locate any placeholders or named parameters. * Parse the SQL statement and locate any placeholders or named parameters.
* Named parameters are substituted for a R2DBC placeholder. * Named parameters are substituted for an R2DBC placeholder.
* @param sql the SQL statement * @param sql the SQL statement
* @return the parsed statement, represented as {@link ParsedSql} instance * @return the parsed statement, represented as {@link ParsedSql} instance
*/ */
@ -154,16 +154,25 @@ abstract class NamedParameterUtils {
j++; j++;
} }
else { else {
while (j < statement.length && !isParameterSeparator(statement[j])) { boolean paramWithSquareBrackets = false;
while (j < statement.length) {
c = statement[j];
if (isParameterSeparator(c)) {
break;
}
if (c == '[') {
paramWithSquareBrackets = true;
}
else if (c == ']') {
if (!paramWithSquareBrackets) {
break;
}
paramWithSquareBrackets = false;
}
j++; j++;
} }
if (j - i > 1) { if (j - i > 1) {
parameter = sql.substring(i + 1, j); parameter = sql.substring(i + 1, j);
if (j < statement.length && statement[j] == ']' && parameter.contains("[")) {
// preserve end bracket for index/key
j++;
parameter = sql.substring(i + 1, j);
}
namedParameterCount = addNewNamedParameter( namedParameterCount = addNewNamedParameter(
namedParameters, namedParameterCount, parameter); namedParameters, namedParameterCount, parameter);
totalParameterCount = addNamedParameter( totalParameterCount = addNamedParameter(
@ -261,7 +270,7 @@ abstract class NamedParameterUtils {
/** /**
* Parse the SQL statement and locate any placeholders or named parameters. Named * Parse the SQL statement and locate any placeholders or named parameters. Named
* parameters are substituted for a R2DBC placeholder, and any select list is expanded * parameters are substituted for an R2DBC placeholder, and any select list is expanded
* to the required number of placeholders. Select lists may contain an array of objects, * to the required number of placeholders. Select lists may contain an array of objects,
* and in that case the placeholders will be grouped and enclosed with parentheses. * and in that case the placeholders will be grouped and enclosed with parentheses.
* This allows for the use of "expression lists" in the SQL statement like: * This allows for the use of "expression lists" in the SQL statement like:

Loading…
Cancel
Save