Move query rendering to dedicated TokenRenderer.
This commit introduces a QueryTokenStream to reduce the number of stream and collect operations to estimate the size before iterating entries. See: #3309
This commit is contained in:
@@ -39,4 +39,9 @@ public interface PersonRepository extends ListCrudRepository<Person, Integer> {
|
||||
|
||||
@Query(value = "SELECT * FROM person WHERE firstname = ?1", nativeQuery = true)
|
||||
List<Person> findAllWithNativeQueryByFirstname(String firstname);
|
||||
|
||||
Long countByFirstname(String firstname);
|
||||
|
||||
@Query("SELECT COUNT(*) FROM org.springframework.data.jpa.model.Person p WHERE p.firstname = ?1")
|
||||
Long countWithAnnotatedQueryByFirstname(String firstname);
|
||||
}
|
||||
|
||||
@@ -58,6 +58,9 @@ import org.springframework.util.ObjectUtils;
|
||||
@Timeout(time = 2)
|
||||
public class RepositoryFinderTests {
|
||||
|
||||
private static final String PERSON_FIRSTNAME = "first";
|
||||
private static final String COLUMN_PERSON_FIRSTNAME = "firstname";
|
||||
|
||||
@State(Scope.Benchmark)
|
||||
public static class BenchmarkParameters {
|
||||
|
||||
@@ -83,7 +86,7 @@ public class RepositoryFinderTests {
|
||||
entityManager.persist(generalProfile);
|
||||
entityManager.persist(sdUserProfile);
|
||||
|
||||
Person person = new Person("first", "last");
|
||||
Person person = new Person(PERSON_FIRSTNAME, "last");
|
||||
person.setProfiles(Set.of(generalProfile, sdUserProfile));
|
||||
entityManager.persist(person);
|
||||
entityManager.getTransaction().commit();
|
||||
@@ -128,7 +131,7 @@ public class RepositoryFinderTests {
|
||||
CriteriaQuery<Person> query = criteriaBuilder.createQuery(Person.class);
|
||||
Root<Person> root = query.from(Person.class);
|
||||
TypedQuery<Person> typedQuery = parameters.entityManager
|
||||
.createQuery(query.where(criteriaBuilder.equal(root.get("firstname"), "first")));
|
||||
.createQuery(query.where(criteriaBuilder.equal(root.get(COLUMN_PERSON_FIRSTNAME), PERSON_FIRSTNAME)));
|
||||
|
||||
return typedQuery.getResultList();
|
||||
}
|
||||
@@ -138,35 +141,52 @@ public class RepositoryFinderTests {
|
||||
|
||||
Query query = parameters.entityManager
|
||||
.createQuery("SELECT p FROM org.springframework.data.jpa.model.Person p WHERE p.firstname = ?1");
|
||||
query.setParameter(1, "first");
|
||||
query.setParameter(1, PERSON_FIRSTNAME);
|
||||
|
||||
return query.getResultList();
|
||||
}
|
||||
|
||||
@Benchmark
|
||||
public Long baselineEntityManagerCount(BenchmarkParameters parameters) {
|
||||
|
||||
Query query = parameters.entityManager.createQuery("SELECT COUNT(*) FROM org.springframework.data.jpa.model.Person p WHERE p.firstname = ?1");
|
||||
query.setParameter(1, PERSON_FIRSTNAME);
|
||||
|
||||
return (Long) query.getSingleResult();
|
||||
}
|
||||
|
||||
@Benchmark
|
||||
public List<Person> derivedFinderMethod(BenchmarkParameters parameters) {
|
||||
return parameters.repositoryProxy.findAllByFirstname("first");
|
||||
return parameters.repositoryProxy.findAllByFirstname(PERSON_FIRSTNAME);
|
||||
}
|
||||
|
||||
@Benchmark
|
||||
public List<IPersonProjection> derivedFinderMethodWithInterfaceProjection(BenchmarkParameters parameters) {
|
||||
return parameters.repositoryProxy.findAllAndProjectToInterfaceByFirstname("first");
|
||||
return parameters.repositoryProxy.findAllAndProjectToInterfaceByFirstname(PERSON_FIRSTNAME);
|
||||
}
|
||||
|
||||
|
||||
@Benchmark
|
||||
public List<Person> stringBasedQuery(BenchmarkParameters parameters) {
|
||||
return parameters.repositoryProxy.findAllWithAnnotatedQueryByFirstname("first");
|
||||
return parameters.repositoryProxy.findAllWithAnnotatedQueryByFirstname(PERSON_FIRSTNAME);
|
||||
}
|
||||
|
||||
@Benchmark
|
||||
public List<Person> stringBasedQueryDynamicSort(BenchmarkParameters parameters) {
|
||||
return parameters.repositoryProxy.findAllWithAnnotatedQueryByFirstname("first", Sort.by("firstname"));
|
||||
return parameters.repositoryProxy.findAllWithAnnotatedQueryByFirstname(PERSON_FIRSTNAME, Sort.by(COLUMN_PERSON_FIRSTNAME));
|
||||
}
|
||||
|
||||
@Benchmark
|
||||
public List<Person> stringBasedNativeQuery(BenchmarkParameters parameters) {
|
||||
return parameters.repositoryProxy.findAllWithNativeQueryByFirstname("first");
|
||||
return parameters.repositoryProxy.findAllWithNativeQueryByFirstname(PERSON_FIRSTNAME);
|
||||
}
|
||||
|
||||
@Benchmark
|
||||
public Long derivedCount(BenchmarkParameters parameters) {
|
||||
return parameters.repositoryProxy.countByFirstname(PERSON_FIRSTNAME);
|
||||
}
|
||||
|
||||
@Benchmark
|
||||
public Long stringBasedCount(BenchmarkParameters parameters) {
|
||||
return parameters.repositoryProxy.countWithAnnotatedQueryByFirstname(PERSON_FIRSTNAME);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,9 +17,8 @@ package org.springframework.data.jpa.repository.query;
|
||||
|
||||
import static org.springframework.data.jpa.repository.query.JpaQueryParsingToken.*;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.data.jpa.repository.query.QueryRenderer.QueryRendererBuilder;
|
||||
import org.springframework.data.jpa.repository.query.QueryTransformers.CountSelectionTokenStream;
|
||||
import org.springframework.lang.Nullable;
|
||||
|
||||
/**
|
||||
@@ -87,10 +86,10 @@ class EqlCountQueryTransformer extends EqlQueryRenderer {
|
||||
QueryRendererBuilder selectionListbuilder = QueryRendererBuilder.concat(ctx.select_item(), this::visit,
|
||||
TOKEN_COMMA);
|
||||
|
||||
List<JpaQueryParsingToken> countSelection = QueryTransformers
|
||||
.filterCountSelection(selectionListbuilder.build().stream().toList());
|
||||
CountSelectionTokenStream countSelection = QueryTransformers
|
||||
.filterCountSelection(selectionListbuilder);
|
||||
|
||||
if (countSelection.stream().anyMatch(eqlToken -> eqlToken.getToken().contains("new"))) {
|
||||
if (countSelection.requiresPrimaryAlias()) {
|
||||
// constructor
|
||||
nested.append(new JpaQueryParsingToken(primaryFromAlias));
|
||||
} else {
|
||||
|
||||
@@ -111,8 +111,7 @@ class EqlSortedQueryTransformer extends EqlQueryRenderer {
|
||||
QueryRendererBuilder builder = super.visitSelect_item(ctx);
|
||||
|
||||
if (ctx.result_variable() != null) {
|
||||
List<JpaQueryParsingToken> tokens = builder.build().stream().toList();
|
||||
transformerSupport.registerAlias(tokens.get(tokens.size() - 1).getToken());
|
||||
transformerSupport.registerAlias(builder.lastToken());
|
||||
}
|
||||
|
||||
return builder;
|
||||
@@ -122,9 +121,7 @@ class EqlSortedQueryTransformer extends EqlQueryRenderer {
|
||||
public QueryRendererBuilder visitJoin(EqlParser.JoinContext ctx) {
|
||||
|
||||
QueryRendererBuilder builder = super.visitJoin(ctx);
|
||||
|
||||
List<JpaQueryParsingToken> tokens = builder.build().stream().toList();
|
||||
transformerSupport.registerAlias(tokens.get(tokens.size() - 1).getToken());
|
||||
transformerSupport.registerAlias(builder.lastToken());
|
||||
|
||||
return builder;
|
||||
}
|
||||
|
||||
@@ -17,9 +17,8 @@ package org.springframework.data.jpa.repository.query;
|
||||
|
||||
import static org.springframework.data.jpa.repository.query.JpaQueryParsingToken.*;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.data.jpa.repository.query.QueryRenderer.QueryRendererBuilder;
|
||||
import org.springframework.data.jpa.repository.query.QueryTransformers.CountSelectionTokenStream;
|
||||
import org.springframework.lang.Nullable;
|
||||
|
||||
/**
|
||||
@@ -196,10 +195,10 @@ class HqlCountQueryTransformer extends HqlQueryRenderer {
|
||||
|
||||
if (ctx.DISTINCT() != null) {
|
||||
|
||||
List<JpaQueryParsingToken> countSelection = QueryTransformers
|
||||
.filterCountSelection(selectionListbuilder.build().stream().toList());
|
||||
CountSelectionTokenStream countSelection = QueryTransformers
|
||||
.filterCountSelection(selectionListbuilder);
|
||||
|
||||
if (countSelection.stream().anyMatch(hqlToken -> hqlToken.getToken().contains("new"))) {
|
||||
if (countSelection.requiresPrimaryAlias()) {
|
||||
// constructor
|
||||
nested.append(new JpaQueryParsingToken(primaryFromAlias));
|
||||
} else {
|
||||
|
||||
@@ -2291,21 +2291,21 @@ class HqlQueryRenderer extends HqlBaseVisitor<QueryRendererBuilder> {
|
||||
|
||||
if (ctx.IS() != null) {
|
||||
|
||||
builder.append(JpaQueryExpression.expression(ctx.IS()));
|
||||
builder.append(JpaExpressionToken.expression(ctx.IS()));
|
||||
|
||||
if (ctx.NOT() != null) {
|
||||
builder.append(JpaQueryParsingToken.expression(ctx.NOT()));
|
||||
}
|
||||
|
||||
builder.append(JpaQueryExpression.expression(ctx.EMPTY()));
|
||||
builder.append(JpaExpressionToken.expression(ctx.EMPTY()));
|
||||
} else if (ctx.MEMBER() != null) {
|
||||
|
||||
if (ctx.NOT() != null) {
|
||||
builder.append(JpaQueryParsingToken.expression(ctx.NOT()));
|
||||
}
|
||||
|
||||
builder.append(JpaQueryExpression.expression(ctx.MEMBER()));
|
||||
builder.append(JpaQueryExpression.expression(ctx.OF()));
|
||||
builder.append(JpaExpressionToken.expression(ctx.MEMBER()));
|
||||
builder.append(JpaExpressionToken.expression(ctx.OF()));
|
||||
builder.append(visit(ctx.path()));
|
||||
}
|
||||
|
||||
|
||||
@@ -101,8 +101,7 @@ class HqlSortedQueryTransformer extends HqlQueryRenderer {
|
||||
QueryRendererBuilder builder = super.visitJoinPath(ctx);
|
||||
|
||||
if (ctx.variable() != null) {
|
||||
List<JpaQueryParsingToken> tokens = builder.build().stream().toList();
|
||||
transformerSupport.registerAlias(tokens.get(tokens.size() - 1).getToken());
|
||||
transformerSupport.registerAlias(builder.lastToken());
|
||||
}
|
||||
|
||||
return builder;
|
||||
@@ -114,8 +113,7 @@ class HqlSortedQueryTransformer extends HqlQueryRenderer {
|
||||
QueryRendererBuilder builder = super.visitJoinSubquery(ctx);
|
||||
|
||||
if (ctx.variable() != null) {
|
||||
List<JpaQueryParsingToken> tokens = builder.build().stream().toList();
|
||||
transformerSupport.registerAlias(tokens.get(tokens.size() - 1).getToken());
|
||||
transformerSupport.registerAlias(builder.lastToken());
|
||||
}
|
||||
|
||||
return builder;
|
||||
@@ -127,8 +125,7 @@ class HqlSortedQueryTransformer extends HqlQueryRenderer {
|
||||
QueryRendererBuilder builder = super.visitVariable(ctx);
|
||||
|
||||
if (ctx.identifier() != null) {
|
||||
List<JpaQueryParsingToken> tokens = builder.build().stream().toList();
|
||||
transformerSupport.registerAlias(tokens.get(tokens.size() - 1).getToken());
|
||||
transformerSupport.registerAlias(builder.lastToken());
|
||||
}
|
||||
|
||||
return builder;
|
||||
|
||||
@@ -15,8 +15,6 @@
|
||||
*/
|
||||
package org.springframework.data.jpa.repository.query;
|
||||
|
||||
import static org.springframework.data.jpa.repository.query.JpaQueryParsingToken.*;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.function.BiFunction;
|
||||
@@ -31,7 +29,6 @@ import org.antlr.v4.runtime.ParserRuleContext;
|
||||
import org.antlr.v4.runtime.TokenStream;
|
||||
import org.antlr.v4.runtime.atn.PredictionMode;
|
||||
import org.antlr.v4.runtime.tree.ParseTreeVisitor;
|
||||
|
||||
import org.springframework.data.domain.Sort;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.Assert;
|
||||
@@ -65,7 +62,7 @@ class JpaQueryEnhancer implements QueryEnhancer {
|
||||
this.introspector.visit(context);
|
||||
|
||||
List<JpaQueryParsingToken> tokens = introspector.getProjection();
|
||||
this.projection = tokens.isEmpty() ? "" : render(tokens);
|
||||
this.projection = tokens.isEmpty() ? "" : QueryRenderer.TokenRenderer.render(tokens);
|
||||
}
|
||||
|
||||
static <P extends Parser> ParserRuleContext parse(String query, Function<CharStream, Lexer> lexerFactoryFunction,
|
||||
@@ -194,7 +191,7 @@ class JpaQueryEnhancer implements QueryEnhancer {
|
||||
*/
|
||||
@Override
|
||||
public String applySorting(Sort sort) {
|
||||
return render(sortFunction.apply(sort, detectAlias()).visit(context));
|
||||
return QueryRenderer.TokenRenderer.render(sortFunction.apply(sort, detectAlias()).visit(context));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -226,7 +223,7 @@ class JpaQueryEnhancer implements QueryEnhancer {
|
||||
*/
|
||||
@Override
|
||||
public String createCountQueryFor(@Nullable String countProjection) {
|
||||
return render(countQueryFunction.apply(countProjection, detectAlias()).visit(context));
|
||||
return QueryRenderer.TokenRenderer.render(countQueryFunction.apply(countProjection, detectAlias()).visit(context));
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -15,7 +15,6 @@
|
||||
*/
|
||||
package org.springframework.data.jpa.repository.query;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import org.antlr.v4.runtime.Token;
|
||||
@@ -26,41 +25,42 @@ import org.antlr.v4.runtime.tree.TerminalNode;
|
||||
* in the parsing process, so the text itself is wrapped in a {@link Supplier}.
|
||||
*
|
||||
* @author Greg Turnquist
|
||||
* @author Christoph Strobl
|
||||
* @since 3.1
|
||||
*/
|
||||
class JpaQueryParsingToken {
|
||||
class JpaQueryParsingToken implements QueryToken {
|
||||
|
||||
/**
|
||||
* Commonly use tokens.
|
||||
*/
|
||||
public static final JpaQueryParsingToken TOKEN_NONE = JpaQueryParsingToken.token("");
|
||||
public static final JpaQueryParsingToken TOKEN_COMMA = JpaQueryParsingToken.token(", ");
|
||||
public static final JpaQueryParsingToken TOKEN_SPACE = JpaQueryParsingToken.token(" ");
|
||||
public static final JpaQueryParsingToken TOKEN_DOT = JpaQueryParsingToken.token(".");
|
||||
public static final JpaQueryParsingToken TOKEN_EQUALS = JpaQueryParsingToken.token(" = ");
|
||||
public static final JpaQueryParsingToken TOKEN_OPEN_PAREN = JpaQueryParsingToken.token("(");
|
||||
public static final JpaQueryParsingToken TOKEN_CLOSE_PAREN = JpaQueryParsingToken.token(")");
|
||||
public static final JpaQueryParsingToken TOKEN_ORDER_BY = JpaQueryParsingToken.expression("order by");
|
||||
public static final JpaQueryParsingToken TOKEN_LOWER_FUNC = new JpaQueryParsingToken("lower(");
|
||||
public static final JpaQueryParsingToken TOKEN_SELECT_COUNT = JpaQueryParsingToken.token("select count(");
|
||||
public static final JpaQueryParsingToken TOKEN_COUNT_FUNC = JpaQueryParsingToken.token("count(");
|
||||
public static final JpaQueryParsingToken TOKEN_DOUBLE_PIPE = JpaQueryParsingToken.token(" || ");
|
||||
public static final JpaQueryParsingToken TOKEN_OPEN_SQUARE_BRACKET = JpaQueryParsingToken.token("[");
|
||||
public static final JpaQueryParsingToken TOKEN_CLOSE_SQUARE_BRACKET = new JpaQueryParsingToken("]");
|
||||
public static final JpaQueryParsingToken TOKEN_COLON = JpaQueryParsingToken.token(":");
|
||||
public static final JpaQueryParsingToken TOKEN_QUESTION_MARK = JpaQueryParsingToken.token("?");
|
||||
public static final JpaQueryParsingToken TOKEN_OPEN_BRACE = JpaQueryParsingToken.token("{");
|
||||
public static final JpaQueryParsingToken TOKEN_CLOSE_BRACE = new JpaQueryParsingToken("}");
|
||||
public static final JpaQueryParsingToken TOKEN_DOUBLE_UNDERSCORE = JpaQueryParsingToken.token("__");
|
||||
public static final JpaQueryParsingToken TOKEN_AS = JpaQueryParsingToken.expression("AS");
|
||||
public static final JpaQueryParsingToken TOKEN_DESC = JpaQueryParsingToken.expression("desc");
|
||||
public static final JpaQueryParsingToken TOKEN_ASC = JpaQueryParsingToken.expression("asc");
|
||||
public static final JpaQueryParsingToken TOKEN_WITH = JpaQueryParsingToken.expression("WITH");
|
||||
public static final JpaQueryParsingToken TOKEN_NOT = JpaQueryParsingToken.expression("NOT");
|
||||
public static final JpaQueryParsingToken TOKEN_MATERIALIZED = JpaQueryParsingToken.expression("materialized");
|
||||
public static final JpaQueryParsingToken TOKEN_NULLS = JpaQueryParsingToken.expression("NULLS");
|
||||
public static final JpaQueryParsingToken TOKEN_FIRST = JpaQueryParsingToken.expression("FIRST");
|
||||
public static final JpaQueryParsingToken TOKEN_LAST = JpaQueryParsingToken.expression("LAST");
|
||||
static final JpaQueryParsingToken TOKEN_NONE = JpaQueryParsingToken.token("");
|
||||
static final JpaQueryParsingToken TOKEN_COMMA = JpaQueryParsingToken.token(", ");
|
||||
static final JpaQueryParsingToken TOKEN_SPACE = JpaQueryParsingToken.token(" ");
|
||||
static final JpaQueryParsingToken TOKEN_DOT = JpaQueryParsingToken.token(".");
|
||||
static final JpaQueryParsingToken TOKEN_EQUALS = JpaQueryParsingToken.token(" = ");
|
||||
static final JpaQueryParsingToken TOKEN_OPEN_PAREN = JpaQueryParsingToken.token("(");
|
||||
static final JpaQueryParsingToken TOKEN_CLOSE_PAREN = JpaQueryParsingToken.token(")");
|
||||
static final JpaQueryParsingToken TOKEN_ORDER_BY = JpaQueryParsingToken.expression("order by");
|
||||
static final JpaQueryParsingToken TOKEN_LOWER_FUNC = JpaQueryParsingToken.token("lower(");
|
||||
static final JpaQueryParsingToken TOKEN_SELECT_COUNT = JpaQueryParsingToken.token("select count(");
|
||||
static final JpaQueryParsingToken TOKEN_COUNT_FUNC = JpaQueryParsingToken.token("count(");
|
||||
static final JpaQueryParsingToken TOKEN_DOUBLE_PIPE = JpaQueryParsingToken.token(" || ");
|
||||
static final JpaQueryParsingToken TOKEN_OPEN_SQUARE_BRACKET = JpaQueryParsingToken.token("[");
|
||||
static final JpaQueryParsingToken TOKEN_CLOSE_SQUARE_BRACKET = JpaQueryParsingToken.token("]");
|
||||
static final JpaQueryParsingToken TOKEN_COLON = JpaQueryParsingToken.token(":");
|
||||
static final JpaQueryParsingToken TOKEN_QUESTION_MARK = JpaQueryParsingToken.token("?");
|
||||
static final JpaQueryParsingToken TOKEN_OPEN_BRACE = JpaQueryParsingToken.token("{");
|
||||
static final JpaQueryParsingToken TOKEN_CLOSE_BRACE = JpaQueryParsingToken.token("}");
|
||||
static final JpaQueryParsingToken TOKEN_DOUBLE_UNDERSCORE = JpaQueryParsingToken.token("__");
|
||||
static final JpaQueryParsingToken TOKEN_AS = JpaQueryParsingToken.expression("AS");
|
||||
static final JpaQueryParsingToken TOKEN_DESC = JpaQueryParsingToken.expression("desc");
|
||||
static final JpaQueryParsingToken TOKEN_ASC = JpaQueryParsingToken.expression("asc");
|
||||
static final JpaQueryParsingToken TOKEN_WITH = JpaQueryParsingToken.expression("WITH");
|
||||
static final JpaQueryParsingToken TOKEN_NOT = JpaQueryParsingToken.expression("NOT");
|
||||
static final JpaQueryParsingToken TOKEN_MATERIALIZED = JpaQueryParsingToken.expression("materialized");
|
||||
static final JpaQueryParsingToken TOKEN_NULLS = JpaQueryParsingToken.expression("NULLS");
|
||||
static final JpaQueryParsingToken TOKEN_FIRST = JpaQueryParsingToken.expression("FIRST");
|
||||
static final JpaQueryParsingToken TOKEN_LAST = JpaQueryParsingToken.expression("LAST");
|
||||
|
||||
/**
|
||||
* The text value of the token.
|
||||
@@ -71,11 +71,11 @@ class JpaQueryParsingToken {
|
||||
this.token = token;
|
||||
}
|
||||
|
||||
public static JpaQueryParsingToken token(TerminalNode node) {
|
||||
static JpaQueryParsingToken token(TerminalNode node) {
|
||||
return token(node.getText());
|
||||
}
|
||||
|
||||
public static JpaQueryParsingToken token(Token token) {
|
||||
static JpaQueryParsingToken token(Token token) {
|
||||
return token(token.getText());
|
||||
}
|
||||
|
||||
@@ -84,22 +84,26 @@ class JpaQueryParsingToken {
|
||||
}
|
||||
|
||||
static JpaQueryParsingToken expression(String expression) {
|
||||
return new JpaQueryExpression(expression);
|
||||
return new JpaExpressionToken(expression);
|
||||
}
|
||||
|
||||
public static JpaQueryParsingToken expression(Token token) {
|
||||
static JpaQueryParsingToken expression(Token token) {
|
||||
return expression(token.getText());
|
||||
}
|
||||
|
||||
public static JpaQueryParsingToken expression(TerminalNode node) {
|
||||
static JpaQueryParsingToken expression(TerminalNode node) {
|
||||
return expression(node.getText());
|
||||
}
|
||||
|
||||
public static JpaQueryParsingToken ventilated(Token op) {
|
||||
static JpaQueryParsingToken ventilated(Token op) {
|
||||
return new JpaQueryParsingToken(" " + op.getText() + " ");
|
||||
}
|
||||
|
||||
String getToken() {
|
||||
return value();
|
||||
}
|
||||
|
||||
public String value() {
|
||||
return token;
|
||||
}
|
||||
|
||||
@@ -109,8 +113,8 @@ class JpaQueryParsingToken {
|
||||
* @param token must not be {@literal null}.
|
||||
* @return {@literal true} if both tokens are equals (using case-insensitive comparison).
|
||||
*/
|
||||
boolean isA(JpaQueryParsingToken token) {
|
||||
return token.getToken().equalsIgnoreCase(this.getToken());
|
||||
public boolean isA(QueryToken token) {
|
||||
return token.value().equalsIgnoreCase(this.value());
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -118,52 +122,14 @@ class JpaQueryParsingToken {
|
||||
return getToken();
|
||||
}
|
||||
|
||||
/**
|
||||
* Render a list of {@link JpaQueryParsingToken}s into a string.
|
||||
*
|
||||
* @param tokens
|
||||
* @return rendered string containing either a query or some subset of that query
|
||||
*/
|
||||
static String render(Object tokens) {
|
||||
static class JpaExpressionToken extends JpaQueryParsingToken {
|
||||
|
||||
if (tokens instanceof Collection tpr) {
|
||||
return render(tpr);
|
||||
}
|
||||
|
||||
return ((QueryRenderer.QueryRendererBuilder) tokens).build().render();
|
||||
}
|
||||
|
||||
/**
|
||||
* Render a list of {@link JpaQueryParsingToken}s into a string.
|
||||
*
|
||||
* @param tokens
|
||||
* @return rendered string containing either a query or some subset of that query
|
||||
*/
|
||||
static String render(Collection<JpaQueryParsingToken> tokens) {
|
||||
|
||||
StringBuilder results = new StringBuilder();
|
||||
|
||||
boolean previousExpression = false;
|
||||
|
||||
for (JpaQueryParsingToken jpaQueryParsingToken : tokens) {
|
||||
|
||||
if (previousExpression) {
|
||||
if (!results.isEmpty() && results.charAt(results.length() - 1) != ' ') {
|
||||
results.append(' ');
|
||||
}
|
||||
}
|
||||
|
||||
previousExpression = jpaQueryParsingToken instanceof JpaQueryExpression;
|
||||
results.append(jpaQueryParsingToken.getToken());
|
||||
}
|
||||
|
||||
return results.toString();
|
||||
}
|
||||
|
||||
static class JpaQueryExpression extends JpaQueryParsingToken {
|
||||
|
||||
JpaQueryExpression(String token) {
|
||||
JpaExpressionToken(String token) {
|
||||
super(token);
|
||||
}
|
||||
|
||||
public boolean isExpression() {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -41,6 +41,10 @@ class JpaQueryTransformerSupport {
|
||||
projectionAliases.add(token);
|
||||
}
|
||||
|
||||
void registerAlias(QueryToken token) {
|
||||
projectionAliases.add(token.value());
|
||||
}
|
||||
|
||||
/**
|
||||
* Using the primary {@literal FROM} clause's alias and a {@link Sort}, construct all the {@literal ORDER BY}
|
||||
* arguments.
|
||||
|
||||
@@ -17,9 +17,8 @@ package org.springframework.data.jpa.repository.query;
|
||||
|
||||
import static org.springframework.data.jpa.repository.query.JpaQueryParsingToken.*;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.data.jpa.repository.query.QueryRenderer.QueryRendererBuilder;
|
||||
import org.springframework.data.jpa.repository.query.QueryTransformers.CountSelectionTokenStream;
|
||||
import org.springframework.lang.Nullable;
|
||||
|
||||
/**
|
||||
@@ -87,10 +86,10 @@ class JpqlCountQueryTransformer extends JpqlQueryRenderer {
|
||||
QueryRendererBuilder selectionListbuilder = QueryRendererBuilder.concat(ctx.select_item(), this::visit,
|
||||
TOKEN_COMMA);
|
||||
|
||||
List<JpaQueryParsingToken> countSelection = QueryTransformers
|
||||
.filterCountSelection(selectionListbuilder.build().stream().toList());
|
||||
CountSelectionTokenStream countSelection = QueryTransformers
|
||||
.filterCountSelection(selectionListbuilder);
|
||||
|
||||
if (countSelection.stream().anyMatch(jpqlToken -> jpqlToken.getToken().contains("new"))) {
|
||||
if (countSelection.requiresPrimaryAlias()) {
|
||||
// constructor
|
||||
nested.append(new JpaQueryParsingToken(primaryFromAlias));
|
||||
} else {
|
||||
|
||||
@@ -105,8 +105,7 @@ class JpqlSortedQueryTransformer extends JpqlQueryRenderer {
|
||||
QueryRendererBuilder builder = super.visitSelect_item(ctx);
|
||||
|
||||
if (ctx.result_variable() != null) {
|
||||
List<JpaQueryParsingToken> tokens = builder.build().stream().toList();
|
||||
transformerSupport.registerAlias(tokens.get(tokens.size() - 1).getToken());
|
||||
transformerSupport.registerAlias(builder.lastToken());
|
||||
}
|
||||
|
||||
return builder;
|
||||
@@ -117,8 +116,7 @@ class JpqlSortedQueryTransformer extends JpqlQueryRenderer {
|
||||
|
||||
QueryRendererBuilder builder = super.visitJoin(ctx);
|
||||
|
||||
List<JpaQueryParsingToken> tokens = builder.build().stream().toList();
|
||||
transformerSupport.registerAlias(tokens.get(tokens.size() - 1).getToken());
|
||||
transformerSupport.registerAlias(builder.lastToken());
|
||||
|
||||
return builder;
|
||||
}
|
||||
|
||||
@@ -17,17 +17,22 @@ package org.springframework.data.jpa.repository.query;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.function.Function;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import org.antlr.v4.runtime.TokenStream;
|
||||
import org.springframework.util.CompositeIterator;
|
||||
|
||||
/**
|
||||
* Abstraction to encapsulate query expressions and render a query.
|
||||
* <p>
|
||||
* Query rendering consists of multiple building blocks:
|
||||
* <ul>
|
||||
* <li>{@link JpaQueryParsingToken tokens} and
|
||||
* {@link org.springframework.data.jpa.repository.query.JpaQueryParsingToken.JpaQueryExpression expression tokens}</li>
|
||||
* {@link org.springframework.data.jpa.repository.query.JpaQueryParsingToken.JpaExpressionToken expression tokens}</li>
|
||||
* <li>{@link QueryRenderer compositions} such as a composition of multiple tokens.</li>
|
||||
* <li>{@link QueryRenderer expressions} that are individual parts such as {@code SELECT} or {@code ORDER BY …}</li>
|
||||
* <li>{@link QueryRenderer inline expressions} such as composition of tokens and expressions such as function calls
|
||||
@@ -36,7 +41,7 @@ import java.util.stream.Stream;
|
||||
*
|
||||
* @author Mark Paluch
|
||||
*/
|
||||
abstract class QueryRenderer {
|
||||
abstract class QueryRenderer implements QueryTokenStream<QueryToken> {
|
||||
|
||||
/**
|
||||
* Creates a QueryRenderer from a collection of {@link JpaQueryParsingToken}.
|
||||
@@ -44,8 +49,8 @@ abstract class QueryRenderer {
|
||||
* @param tokens
|
||||
* @return
|
||||
*/
|
||||
static QueryRenderer from(Collection<JpaQueryParsingToken> tokens) {
|
||||
List<JpaQueryParsingToken> tokensToUse = new ArrayList<>(32);
|
||||
static QueryRenderer from(Collection<? extends QueryToken> tokens) {
|
||||
List<QueryToken> tokensToUse = new ArrayList<>(Math.max(tokens.size(), 32));
|
||||
tokensToUse.addAll(tokens);
|
||||
return new TokenRenderer(tokensToUse);
|
||||
}
|
||||
@@ -80,10 +85,7 @@ abstract class QueryRenderer {
|
||||
* @return
|
||||
*/
|
||||
QueryRenderer append(QueryRenderer renderer) {
|
||||
List<QueryRenderer> objects = new ArrayList<>(32);
|
||||
objects.add(this);
|
||||
objects.add(renderer);
|
||||
return new CompositeRenderer(objects);
|
||||
return CompositeRenderer.combine(this, renderer);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -93,13 +95,6 @@ abstract class QueryRenderer {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return stream of tokens.
|
||||
*/
|
||||
public Stream<JpaQueryParsingToken> stream() {
|
||||
return Stream.empty();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return render();
|
||||
@@ -111,9 +106,21 @@ abstract class QueryRenderer {
|
||||
static class CompositeRenderer extends QueryRenderer {
|
||||
|
||||
private final List<QueryRenderer> nested;
|
||||
private int size;
|
||||
|
||||
CompositeRenderer(List<QueryRenderer> nested) {
|
||||
this.nested = new ArrayList<>(nested);
|
||||
static CompositeRenderer combine(QueryRenderer root, QueryRenderer nested) {
|
||||
|
||||
List<QueryRenderer> queryRenderers = new ArrayList<>(32);
|
||||
queryRenderers.add(root);
|
||||
queryRenderers.add(nested);
|
||||
|
||||
return new CompositeRenderer(queryRenderers, root.estimatedSize() + nested.estimatedSize());
|
||||
}
|
||||
|
||||
private CompositeRenderer(List<QueryRenderer> nested, int size) {
|
||||
|
||||
this.nested = nested;
|
||||
this.size = size;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -140,6 +147,7 @@ abstract class QueryRenderer {
|
||||
QueryRenderer append(QueryRenderer renderer) {
|
||||
|
||||
nested.add(renderer);
|
||||
this.size += renderer.estimatedSize();
|
||||
return this;
|
||||
}
|
||||
|
||||
@@ -149,15 +157,18 @@ abstract class QueryRenderer {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Stream<JpaQueryParsingToken> stream() {
|
||||
|
||||
Stream<JpaQueryParsingToken> stream = Stream.empty();
|
||||
public Iterator<QueryToken> iterator() {
|
||||
|
||||
CompositeIterator<QueryToken> iterator = new CompositeIterator<>();
|
||||
for (QueryRenderer renderer : nested) {
|
||||
stream = Stream.concat(stream, renderer.stream());
|
||||
iterator.add(renderer.iterator());
|
||||
}
|
||||
return iterator;
|
||||
}
|
||||
|
||||
return stream;
|
||||
@Override
|
||||
public int estimatedSize() {
|
||||
return size;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -166,15 +177,15 @@ abstract class QueryRenderer {
|
||||
*/
|
||||
static class TokenRenderer extends QueryRenderer {
|
||||
|
||||
private final List<JpaQueryParsingToken> tokens;
|
||||
private final List<QueryToken> tokens;
|
||||
|
||||
TokenRenderer(List<JpaQueryParsingToken> tokens) {
|
||||
TokenRenderer(List<QueryToken> tokens) {
|
||||
this.tokens = tokens;
|
||||
}
|
||||
|
||||
@Override
|
||||
String render() {
|
||||
return JpaQueryParsingToken.render(tokens);
|
||||
return render(tokens);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -190,19 +201,80 @@ abstract class QueryRenderer {
|
||||
|
||||
@Override
|
||||
public boolean isExpression() {
|
||||
return !tokens.isEmpty() && tokens.get(tokens.size() - 1) instanceof JpaQueryParsingToken.JpaQueryExpression;
|
||||
return !tokens.isEmpty() && tokens.get(tokens.size() - 1).isExpression();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Stream<JpaQueryParsingToken> stream() {
|
||||
public Stream<QueryToken> stream() {
|
||||
return tokens.stream();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterator<QueryToken> iterator() {
|
||||
return tokens.iterator();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<QueryToken> toList() {
|
||||
return tokens;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int estimatedSize() {
|
||||
return tokens.size();
|
||||
}
|
||||
|
||||
/**
|
||||
* Render a list of {@link JpaQueryParsingToken}s into a string.
|
||||
*
|
||||
* @param tokens
|
||||
* @return rendered string containing either a query or some subset of that query
|
||||
*/
|
||||
static String render(Object tokens) {
|
||||
|
||||
if (tokens instanceof Collection tpr) {
|
||||
return render(tpr);
|
||||
}
|
||||
|
||||
if(tokens instanceof QueryRendererBuilder qrb) {
|
||||
return qrb.build().render();
|
||||
}
|
||||
|
||||
if(tokens instanceof QueryRenderer qr) {
|
||||
return qr.render();
|
||||
}
|
||||
|
||||
throw new IllegalArgumentException("Unknown token type %s".formatted(tokens));
|
||||
}
|
||||
|
||||
|
||||
static String render(Collection<QueryToken> tokens) {
|
||||
|
||||
StringBuilder results = new StringBuilder();
|
||||
|
||||
boolean previousExpression = false;
|
||||
|
||||
for (QueryToken jpaQueryParsingToken : tokens) {
|
||||
|
||||
if (previousExpression) {
|
||||
if (!results.isEmpty() && results.charAt(results.length() - 1) != ' ') {
|
||||
results.append(' ');
|
||||
}
|
||||
}
|
||||
|
||||
previousExpression = jpaQueryParsingToken.isExpression();
|
||||
results.append(jpaQueryParsingToken.value());
|
||||
}
|
||||
|
||||
return results.toString();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Builder for {@link QueryRenderer}.
|
||||
*/
|
||||
static class QueryRendererBuilder {
|
||||
static class QueryRendererBuilder implements QueryTokenStream<QueryToken> {
|
||||
|
||||
protected QueryRenderer current = QueryRenderer.empty();
|
||||
|
||||
@@ -217,7 +289,7 @@ abstract class QueryRenderer {
|
||||
* @param <T>
|
||||
*/
|
||||
public static <T> QueryRendererBuilder concat(Collection<T> elements, Function<T, QueryRendererBuilder> visitor,
|
||||
JpaQueryParsingToken separator) {
|
||||
QueryToken separator) {
|
||||
return concat(elements, visitor, QueryRendererBuilder::toInline, separator);
|
||||
}
|
||||
|
||||
@@ -232,7 +304,7 @@ abstract class QueryRenderer {
|
||||
* @param <T>
|
||||
*/
|
||||
public static <T> QueryRendererBuilder concatExpressions(Collection<T> elements,
|
||||
Function<T, QueryRendererBuilder> visitor, JpaQueryParsingToken separator) {
|
||||
Function<T, QueryRendererBuilder> visitor, QueryToken separator) {
|
||||
return concat(elements, visitor, QueryRendererBuilder::toExpression, separator);
|
||||
}
|
||||
|
||||
@@ -248,7 +320,7 @@ abstract class QueryRenderer {
|
||||
* @param <T>
|
||||
*/
|
||||
public static <T> QueryRendererBuilder concat(Collection<T> elements, Function<T, QueryRendererBuilder> visitor,
|
||||
Function<QueryRendererBuilder, QueryRenderer> postProcess, JpaQueryParsingToken separator) {
|
||||
Function<QueryRendererBuilder, QueryRenderer> postProcess, QueryToken separator) {
|
||||
|
||||
QueryRendererBuilder builder = new QueryRendererBuilder();
|
||||
for (T element : elements) {
|
||||
@@ -267,7 +339,7 @@ abstract class QueryRenderer {
|
||||
* @param token
|
||||
* @return
|
||||
*/
|
||||
public static QueryRendererBuilder from(JpaQueryParsingToken token) {
|
||||
public static QueryRendererBuilder from(QueryToken token) {
|
||||
return new QueryRendererBuilder().append(token);
|
||||
}
|
||||
|
||||
@@ -277,7 +349,7 @@ abstract class QueryRenderer {
|
||||
* @param token
|
||||
* @return {@code this} builder.
|
||||
*/
|
||||
QueryRendererBuilder append(JpaQueryParsingToken token) {
|
||||
QueryRendererBuilder append(QueryToken token) {
|
||||
return append(List.of(token));
|
||||
}
|
||||
|
||||
@@ -287,10 +359,14 @@ abstract class QueryRenderer {
|
||||
* @param tokens
|
||||
* @return {@code this} builder.
|
||||
*/
|
||||
QueryRendererBuilder append(Collection<JpaQueryParsingToken> tokens) {
|
||||
QueryRendererBuilder append(Collection<? extends QueryToken> tokens) {
|
||||
return append(QueryRenderer.from(tokens));
|
||||
}
|
||||
|
||||
QueryRendererBuilder append(QueryTokenStream<? extends QueryToken> tokens) {
|
||||
return append(QueryRenderer.from(tokens.toList()));
|
||||
}
|
||||
|
||||
/**
|
||||
* Append a QueryRendererBuilder.
|
||||
*
|
||||
@@ -378,7 +454,7 @@ abstract class QueryRenderer {
|
||||
* @return
|
||||
*/
|
||||
public boolean isEmpty() {
|
||||
return current instanceof EmptyQueryRenderer;
|
||||
return current.isEmpty();
|
||||
}
|
||||
|
||||
public QueryRenderer build() {
|
||||
@@ -402,6 +478,26 @@ abstract class QueryRenderer {
|
||||
public QueryRenderer toInline() {
|
||||
return new InlineRenderer(current);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<QueryToken> toList() {
|
||||
return current.toList();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Stream<QueryToken> stream() {
|
||||
return current.stream();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int estimatedSize() {
|
||||
return current.estimatedSize();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterator<QueryToken> iterator() {
|
||||
return current.iterator();
|
||||
}
|
||||
}
|
||||
|
||||
private static class InlineRenderer extends QueryRenderer {
|
||||
@@ -418,9 +514,24 @@ abstract class QueryRenderer {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Stream<JpaQueryParsingToken> stream() {
|
||||
public Stream<QueryToken> stream() {
|
||||
return delegate.stream();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<QueryToken> toList() {
|
||||
return delegate.toList();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterator<QueryToken> iterator() {
|
||||
return delegate.iterator();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int estimatedSize() {
|
||||
return delegate.estimatedSize();
|
||||
}
|
||||
}
|
||||
|
||||
private static class ExpressionRenderer extends QueryRenderer {
|
||||
@@ -442,9 +553,24 @@ abstract class QueryRenderer {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Stream<JpaQueryParsingToken> stream() {
|
||||
public Stream<QueryToken> stream() {
|
||||
return delegate.stream();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<QueryToken> toList() {
|
||||
return delegate.toList();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterator<QueryToken> iterator() {
|
||||
return delegate.iterator();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int estimatedSize() {
|
||||
return delegate.estimatedSize();
|
||||
}
|
||||
}
|
||||
|
||||
private static class EmptyQueryRenderer extends QueryRenderer {
|
||||
@@ -460,5 +586,30 @@ abstract class QueryRenderer {
|
||||
QueryRenderer append(QueryRenderer renderer) {
|
||||
return renderer;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEmpty() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<QueryToken> toList() {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Stream<QueryToken> stream() {
|
||||
return Stream.empty();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterator<QueryToken> iterator() {
|
||||
return Collections.emptyIterator();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int estimatedSize() {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
/*
|
||||
* Copyright 2024 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.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.data.jpa.repository.query;
|
||||
|
||||
/**
|
||||
* @author Christoph Strobl
|
||||
*/
|
||||
public interface QueryToken {
|
||||
|
||||
String value();
|
||||
|
||||
default boolean isExpression() {
|
||||
return false;
|
||||
}
|
||||
|
||||
boolean isA(QueryToken queryToken);
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
/*
|
||||
* Copyright 2024 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.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.data.jpa.repository.query;
|
||||
|
||||
import org.springframework.data.util.Streamable;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
|
||||
/**
|
||||
* @author Christoph Strobl
|
||||
*/
|
||||
public interface QueryTokenStream<T extends QueryToken> extends Streamable<T> {
|
||||
|
||||
@Nullable
|
||||
default T firstToken() {
|
||||
return CollectionUtils.firstElement(toList());
|
||||
}
|
||||
|
||||
@Nullable
|
||||
default T lastToken() {
|
||||
return CollectionUtils.lastElement(toList());
|
||||
}
|
||||
|
||||
int estimatedSize();
|
||||
}
|
||||
@@ -18,6 +18,7 @@ package org.springframework.data.jpa.repository.query;
|
||||
import static org.springframework.data.jpa.repository.query.JpaQueryParsingToken.*;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
@@ -28,19 +29,14 @@ import java.util.List;
|
||||
*/
|
||||
class QueryTransformers {
|
||||
|
||||
/**
|
||||
* Filter a token list from a {@code SELECT} clause to be used within a count query. That is, filter any {@code AS …}
|
||||
* aliases.
|
||||
*
|
||||
* @param selection the input selection.
|
||||
* @return filtered selection to be used with count queries.
|
||||
*/
|
||||
static List<JpaQueryParsingToken> filterCountSelection(List<JpaQueryParsingToken> selection) {
|
||||
static CountSelectionTokenStream filterCountSelection(QueryTokenStream<QueryToken> selection) {
|
||||
|
||||
List<JpaQueryParsingToken> target = new ArrayList<>(selection.size());
|
||||
List<QueryToken> target = new ArrayList<>(selection.estimatedSize());
|
||||
boolean skipNext = false;
|
||||
boolean containsNew = false;
|
||||
|
||||
for (QueryToken token : selection) {
|
||||
|
||||
for (JpaQueryParsingToken token : selection) {
|
||||
|
||||
if (skipNext) {
|
||||
skipNext = false;
|
||||
@@ -52,14 +48,48 @@ class QueryTransformers {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!token.isA(TOKEN_COMMA) && token instanceof JpaQueryExpression) {
|
||||
token = JpaQueryParsingToken.token(token.getToken());
|
||||
if (!token.isA(TOKEN_COMMA) && token.isExpression()) {
|
||||
token = JpaQueryParsingToken.token(token.value());
|
||||
}
|
||||
|
||||
if(!containsNew && token.value().contains("new")) {
|
||||
containsNew = true;
|
||||
}
|
||||
|
||||
target.add(token);
|
||||
}
|
||||
|
||||
return target;
|
||||
return new CountSelectionTokenStream(target, containsNew);
|
||||
}
|
||||
|
||||
static class CountSelectionTokenStream implements QueryTokenStream<QueryToken> {
|
||||
|
||||
private final List<QueryToken> tokens;
|
||||
private final boolean requiresPrimaryAlias;
|
||||
|
||||
public CountSelectionTokenStream(List<QueryToken> tokens, boolean containsNew) {
|
||||
this.tokens = tokens;
|
||||
this.requiresPrimaryAlias = containsNew;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int estimatedSize() {
|
||||
return tokens.size();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterator<QueryToken> iterator() {
|
||||
return tokens.iterator();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<QueryToken> toList() {
|
||||
return tokens;
|
||||
}
|
||||
|
||||
public boolean requiresPrimaryAlias() {
|
||||
return requiresPrimaryAlias;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -21,6 +21,7 @@ import static org.springframework.data.jpa.repository.query.JpaQueryParsingToken
|
||||
import org.antlr.v4.runtime.CharStreams;
|
||||
import org.antlr.v4.runtime.CommonTokenStream;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.data.jpa.repository.query.QueryRenderer.TokenRenderer;
|
||||
|
||||
/**
|
||||
* Tests built around examples of EQL found in the EclipseLink's docs at
|
||||
@@ -47,7 +48,7 @@ class EqlComplianceTests {
|
||||
|
||||
EqlParser.StartContext parsedQuery = parser.start();
|
||||
|
||||
return render(new EqlQueryRenderer().visit(parsedQuery));
|
||||
return TokenRenderer.render(new EqlQueryRenderer().visit(parsedQuery));
|
||||
}
|
||||
|
||||
private void assertQuery(String query) {
|
||||
|
||||
@@ -28,6 +28,7 @@ import org.junit.jupiter.params.ParameterizedTest;
|
||||
import org.junit.jupiter.params.provider.Arguments;
|
||||
import org.junit.jupiter.params.provider.MethodSource;
|
||||
import org.junit.jupiter.params.provider.ValueSource;
|
||||
import org.springframework.data.jpa.repository.query.QueryRenderer.TokenRenderer;
|
||||
|
||||
/**
|
||||
* Tests built around examples of EQL found in the JPA spec
|
||||
@@ -54,7 +55,7 @@ class EqlQueryRendererTests {
|
||||
|
||||
EqlParser.StartContext parsedQuery = parser.start();
|
||||
|
||||
return render(new EqlQueryRenderer().visit(parsedQuery));
|
||||
return TokenRenderer.render(new EqlQueryRenderer().visit(parsedQuery));
|
||||
}
|
||||
|
||||
static Stream<Arguments> reservedWords() {
|
||||
|
||||
@@ -22,6 +22,7 @@ import org.antlr.v4.runtime.CharStreams;
|
||||
import org.antlr.v4.runtime.CommonTokenStream;
|
||||
import org.junit.jupiter.api.Disabled;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.data.jpa.repository.query.QueryRenderer.TokenRenderer;
|
||||
|
||||
/**
|
||||
* Tests built around examples of EQL found in the JPA spec
|
||||
@@ -44,7 +45,7 @@ class EqlSpecificationTests {
|
||||
|
||||
EqlParser.StartContext parsedQuery = parser.start();
|
||||
|
||||
return render(new EqlQueryRenderer().visit(parsedQuery));
|
||||
return TokenRenderer.render(new EqlQueryRenderer().visit(parsedQuery));
|
||||
}
|
||||
|
||||
private void assertQuery(String query) {
|
||||
|
||||
@@ -22,6 +22,7 @@ import org.antlr.v4.runtime.CharStreams;
|
||||
import org.antlr.v4.runtime.CommonTokenStream;
|
||||
import org.junit.jupiter.api.Disabled;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.data.jpa.repository.query.QueryRenderer.TokenRenderer;
|
||||
|
||||
/**
|
||||
* Tests built around examples of HQL found in
|
||||
@@ -46,7 +47,7 @@ class HqlSpecificationTests {
|
||||
|
||||
HqlParser.StartContext parsedQuery = parser.start();
|
||||
|
||||
return render(new HqlQueryRenderer().visit(parsedQuery));
|
||||
return TokenRenderer.render(new HqlQueryRenderer().visit(parsedQuery));
|
||||
}
|
||||
|
||||
private void assertQuery(String query) {
|
||||
|
||||
@@ -21,6 +21,7 @@ import static org.springframework.data.jpa.repository.query.JpaQueryParsingToken
|
||||
import org.antlr.v4.runtime.CharStreams;
|
||||
import org.antlr.v4.runtime.CommonTokenStream;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.data.jpa.repository.query.QueryRenderer.TokenRenderer;
|
||||
|
||||
/**
|
||||
* Test to verify compliance of {@link JpqlParser} with standard SQL. Other than {@link JpqlSpecificationTests} tests in
|
||||
@@ -40,7 +41,7 @@ class JpqlComplianceTests {
|
||||
|
||||
JpqlParser.StartContext parsedQuery = parser.start();
|
||||
|
||||
return render(new JpqlQueryRenderer().visit(parsedQuery));
|
||||
return TokenRenderer.render(new JpqlQueryRenderer().visit(parsedQuery));
|
||||
}
|
||||
|
||||
private void assertQuery(String query) {
|
||||
|
||||
@@ -28,6 +28,7 @@ import org.junit.jupiter.params.ParameterizedTest;
|
||||
import org.junit.jupiter.params.provider.Arguments;
|
||||
import org.junit.jupiter.params.provider.MethodSource;
|
||||
import org.junit.jupiter.params.provider.ValueSource;
|
||||
import org.springframework.data.jpa.repository.query.QueryRenderer.TokenRenderer;
|
||||
|
||||
/**
|
||||
* Tests built around examples of JPQL found in the JPA spec
|
||||
@@ -55,7 +56,7 @@ class JpqlQueryRendererTests {
|
||||
|
||||
JpqlParser.StartContext parsedQuery = parser.start();
|
||||
|
||||
return render(new JpqlQueryRenderer().visit(parsedQuery));
|
||||
return TokenRenderer.render(new JpqlQueryRenderer().visit(parsedQuery));
|
||||
}
|
||||
|
||||
static Stream<Arguments> reservedWords() {
|
||||
|
||||
@@ -22,6 +22,7 @@ import org.antlr.v4.runtime.CharStreams;
|
||||
import org.antlr.v4.runtime.CommonTokenStream;
|
||||
import org.junit.jupiter.api.Disabled;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.data.jpa.repository.query.QueryRenderer.TokenRenderer;
|
||||
|
||||
/**
|
||||
* Tests built around examples of JPQL found in the JPA spec
|
||||
@@ -48,7 +49,7 @@ class JpqlSpecificationTests {
|
||||
|
||||
JpqlParser.StartContext parsedQuery = parser.start();
|
||||
|
||||
return render(new JpqlQueryRenderer().visit(parsedQuery));
|
||||
return TokenRenderer.render(new JpqlQueryRenderer().visit(parsedQuery));
|
||||
}
|
||||
|
||||
private void assertQuery(String query) {
|
||||
|
||||
Reference in New Issue
Block a user