diff --git a/org.springframework.jdbc/src/main/java/org/springframework/jdbc/core/namedparam/NamedParameterUtils.java b/org.springframework.jdbc/src/main/java/org/springframework/jdbc/core/namedparam/NamedParameterUtils.java index 799f007192..aeed165922 100644 --- a/org.springframework.jdbc/src/main/java/org/springframework/jdbc/core/namedparam/NamedParameterUtils.java +++ b/org.springframework.jdbc/src/main/java/org/springframework/jdbc/core/namedparam/NamedParameterUtils.java @@ -73,13 +73,18 @@ public abstract class NamedParameterUtils { Assert.notNull(sql, "SQL must not be null"); Set namedParameters = new HashSet(); - ParsedSql parsedSql = new ParsedSql(sql); + String sqlToUse = sql; + if (sql.contains("\\:")) { + sqlToUse = sql.replace("\\:", ":"); + } + ParsedSql parsedSql = new ParsedSql(sqlToUse); char[] statement = sql.toCharArray(); int namedParameterCount = 0; int unnamedParameterCount = 0; int totalParameterCount = 0; + int escapes = 0; int i = 0; while (i < statement.length) { int skipToPosition = skipCommentsAndQuotes(statement, i); @@ -97,21 +102,41 @@ public abstract class NamedParameterUtils { i = i + 2; continue; } - while (j < statement.length && !isParameterSeparator(statement[j])) { + String parameter = null; + if (j < statement.length && c == ':' && statement[j] == '{') { + // :{x} style parameter + while (j < statement.length && !('}' == statement[j])) { + j++; + } + if (j - i > 3) { + parameter = sql.substring(i + 2, j); + namedParameterCount = addNewNamedParameter(namedParameters, namedParameterCount, parameter); + totalParameterCount = addNamedParameter(parsedSql, totalParameterCount, escapes, i, j + 1, parameter); + } j++; } - if (j - i > 1) { - String parameter = sql.substring(i + 1, j); - if (!namedParameters.contains(parameter)) { - namedParameters.add(parameter); - namedParameterCount++; + else { + while (j < statement.length && !isParameterSeparator(statement[j])) { + j++; + } + if (j - i > 1) { + parameter = sql.substring(i + 1, j); + namedParameterCount = addNewNamedParameter(namedParameters, namedParameterCount, parameter); + totalParameterCount = addNamedParameter(parsedSql, totalParameterCount, escapes, i, j, parameter); } - parsedSql.addNamedParameter(parameter, i, j); - totalParameterCount++; } i = j - 1; } else { + if (c == '\\') { + int j = i + 1; + if (j < statement.length && statement[j] == ':') { + // this is an escaped : and should be skipped + escapes++; + i = i + 2; + continue; + } + } if (c == '?') { unnamedParameterCount++; totalParameterCount++; @@ -125,6 +150,21 @@ public abstract class NamedParameterUtils { return parsedSql; } + protected static int addNamedParameter(ParsedSql parsedSql, int totalParameterCount, int escapes, int i, int j, + String parameter) { + parsedSql.addNamedParameter(parameter, i - escapes, j - escapes); + totalParameterCount++; + return totalParameterCount; + } + + protected static int addNewNamedParameter(Set namedParameters, int namedParameterCount, String parameter) { + if (!namedParameters.contains(parameter)) { + namedParameters.add(parameter); + namedParameterCount++; + } + return namedParameterCount; + } + /** * Skip over comments and quoted names present in an SQL statement * @param statement character array containing SQL statement diff --git a/org.springframework.jdbc/src/test/java/org/springframework/jdbc/core/namedparam/NamedParameterUtilsTests.java b/org.springframework.jdbc/src/test/java/org/springframework/jdbc/core/namedparam/NamedParameterUtilsTests.java index 2f498fb8c5..b7f1b7fd6e 100644 --- a/org.springframework.jdbc/src/test/java/org/springframework/jdbc/core/namedparam/NamedParameterUtilsTests.java +++ b/org.springframework.jdbc/src/test/java/org/springframework/jdbc/core/namedparam/NamedParameterUtilsTests.java @@ -55,7 +55,6 @@ public class NamedParameterUtilsTests { assertEquals("a", psql3.getParameterNames().get(0)); assertEquals("b", psql3.getParameterNames().get(1)); assertEquals("c", psql3.getParameterNames().get(2)); - } @Test @@ -185,6 +184,56 @@ public class NamedParameterUtilsTests { assertEquals(expectedSql, NamedParameterUtils.substituteNamedParameters(parsedSql, null)); } + /* + * SPR-7476 + */ + @Test + public void parseSqlStatementWithEscapedColon() throws Exception { + String expectedSql = "select foo from bar where baz < DATE(? 23:59:59) and baz = ?"; + String sql = "select foo from bar where baz < DATE(:p1 23\\:59\\:59) and baz = :p2"; + + ParsedSql parsedSql = NamedParameterUtils.parseSqlStatement(sql); + assertEquals(2, parsedSql.getParameterNames().size()); + String finalSql = NamedParameterUtils.substituteNamedParameters(parsedSql, null); + assertEquals(expectedSql, finalSql); + } + + /* + * SPR-7476 + */ + @Test + public void parseSqlStatementWithBracketDelimitedParameterNames() throws Exception { + String expectedSql = "select foo from bar where baz = b??z"; + String sql = "select foo from bar where baz = b:{p1}:{p2}z"; + + ParsedSql parsedSql = NamedParameterUtils.parseSqlStatement(sql); + assertEquals(2, parsedSql.getParameterNames().size()); + String finalSql = NamedParameterUtils.substituteNamedParameters(parsedSql, null); + assertEquals(expectedSql, finalSql); + } + + /* + * SPR-7476 + */ + @Test + public void parseSqlStatementWithEmptyBracketsOrBracketsInQuotes() throws Exception { + String expectedSql = "select foo from bar where baz = b:{}z"; + String sql = "select foo from bar where baz = b:{}z"; + ParsedSql parsedSql = NamedParameterUtils.parseSqlStatement(sql); + assertEquals(0, parsedSql.getParameterNames().size()); + String finalSql = NamedParameterUtils.substituteNamedParameters(parsedSql, null); + assertEquals(expectedSql, finalSql); + + String expectedSql2 = "select foo from bar where baz = 'b:{p1}z'"; + String sql2 = "select foo from bar where baz = 'b:{p1}z'"; + + ParsedSql parsedSql2 = NamedParameterUtils.parseSqlStatement(sql2); + assertEquals(0, parsedSql2.getParameterNames().size()); + String finalSql2 = NamedParameterUtils.substituteNamedParameters(parsedSql2, null); + assertEquals(expectedSql2, finalSql2); + + } + /* * SPR-2544 */