Skip to content

Commit

Permalink
Preserve nested square brackets within parameter name
Browse files Browse the repository at this point in the history
Closes gh-31596
  • Loading branch information
jhoeller committed Nov 14, 2023
1 parent 3e06441 commit 99327b7
Show file tree
Hide file tree
Showing 3 changed files with 49 additions and 19 deletions.
Original file line number Diff line number Diff line change
@@ -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");
* you may not use this file except in compliance with the License.
Expand Down Expand Up @@ -55,7 +55,7 @@ public abstract class NamedParameterUtils {
* Set of characters that qualify as parameter separators,
* 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.
Expand Down Expand Up @@ -142,16 +142,25 @@ public static ParsedSql parseSqlStatement(String sql) {
j++;
}
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++;
}
if (j - i > 1) {
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(
namedParameters, namedParameterCount, parameter);
totalParameterCount = addNamedParameter(
Expand Down
Original file line number Diff line number Diff line change
@@ -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");
* you may not use this file except in compliance with the License.
Expand Down Expand Up @@ -330,6 +330,18 @@ public void parseSqlStatementWithSquareBracket() {
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
void namedParamMapReference() {
String sql = "insert into foos (id) values (:headers[id])";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ abstract class NamedParameterUtils {
* Set of characters that qualify as parameter separators,
* 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.
Expand All @@ -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.
* Named parameters are substituted for a R2DBC placeholder.
* Named parameters are substituted for an R2DBC placeholder.
* @param sql the SQL statement
* @return the parsed statement, represented as {@link ParsedSql} instance
*/
Expand Down Expand Up @@ -154,16 +154,25 @@ public static ParsedSql parseSqlStatement(String sql) {
j++;
}
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++;
}
if (j - i > 1) {
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(
namedParameters, namedParameterCount, parameter);
totalParameterCount = addNamedParameter(
Expand Down Expand Up @@ -261,7 +270,7 @@ private static int skipCommentsAndQuotes(char[] statement, int position) {

/**
* 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,
* 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:
Expand Down

0 comments on commit 99327b7

Please sign in to comment.