Browse Source

Merge branch '6.0.x'

pull/31608/head
Juergen Hoeller 10 months ago
parent
commit
2b7a9209b3
  1. 22
      spring-expression/src/main/java/org/springframework/expression/spel/support/StandardTypeLocator.java
  2. 25
      spring-jdbc/src/main/java/org/springframework/jdbc/core/namedparam/NamedParameterUtils.java
  3. 14
      spring-jdbc/src/test/java/org/springframework/jdbc/core/namedparam/NamedParameterUtilsTests.java
  4. 29
      spring-r2dbc/src/main/java/org/springframework/r2dbc/core/NamedParameterUtils.java

22
spring-expression/src/main/java/org/springframework/expression/spel/support/StandardTypeLocator.java

@ -19,7 +19,10 @@ package org.springframework.expression.spel.support;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import org.springframework.core.SmartClassLoader;
import org.springframework.expression.EvaluationException; import org.springframework.expression.EvaluationException;
import org.springframework.expression.TypeLocator; import org.springframework.expression.TypeLocator;
import org.springframework.expression.spel.SpelEvaluationException; import org.springframework.expression.spel.SpelEvaluationException;
@ -48,6 +51,8 @@ public class StandardTypeLocator implements TypeLocator {
private final List<String> importPrefixes = new ArrayList<>(1); private final List<String> importPrefixes = new ArrayList<>(1);
private final Map<String, Class<?>> typeCache = new ConcurrentHashMap<>();
/** /**
* Create a {@code StandardTypeLocator} for the default {@link ClassLoader} * Create a {@code StandardTypeLocator} for the default {@link ClassLoader}
@ -110,6 +115,21 @@ public class StandardTypeLocator implements TypeLocator {
*/ */
@Override @Override
public Class<?> findType(String typeName) throws EvaluationException { public Class<?> findType(String typeName) throws EvaluationException {
Class<?> cachedType = this.typeCache.get(typeName);
if (cachedType != null) {
return cachedType;
}
Class<?> loadedType = loadType(typeName);
if (loadedType != null &&
!(this.classLoader instanceof SmartClassLoader scl && scl.isClassReloadable(loadedType))) {
this.typeCache.put(typeName, loadedType);
return loadedType;
}
throw new SpelEvaluationException(SpelMessage.TYPE_NOT_FOUND, typeName);
}
@Nullable
private Class<?> loadType(String typeName) {
try { try {
return ClassUtils.forName(typeName, this.classLoader); return ClassUtils.forName(typeName, this.classLoader);
} }
@ -125,7 +145,7 @@ public class StandardTypeLocator implements TypeLocator {
// might be a different prefix // might be a different prefix
} }
} }
throw new SpelEvaluationException(SpelMessage.TYPE_NOT_FOUND, typeName); return null;
} }
} }

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