JpqlQueryRenderer.java
/*
* Copyright 2022-2025 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 static org.springframework.data.jpa.repository.query.QueryTokens.*;
import java.util.ArrayList;
import java.util.List;
import org.antlr.v4.runtime.ParserRuleContext;
import org.antlr.v4.runtime.RuleContext;
import org.antlr.v4.runtime.tree.ParseTree;
import org.antlr.v4.runtime.tree.RuleNode;
import org.antlr.v4.runtime.tree.TerminalNode;
import org.springframework.data.jpa.repository.query.QueryRenderer.QueryRendererBuilder;
import org.springframework.util.CollectionUtils;
/**
* An ANTLR {@link org.antlr.v4.runtime.tree.ParseTreeVisitor} that renders a JPQL query without making any changes.
*
* @author Greg Turnquist
* @author Christoph Strobl
* @author Mark Paluch
* @author TaeHyun Kang
* @since 3.1
*/
@SuppressWarnings({ "ConstantConditions", "DuplicatedCode" })
class JpqlQueryRenderer extends JpqlBaseVisitor<QueryTokenStream> {
/**
* Is this AST tree a {@literal subquery}?
*
* @return {@literal true} is the query is a subquery; {@literal false} otherwise.
*/
static boolean isSubquery(ParserRuleContext ctx) {
while (ctx != null) {
if (ctx instanceof JpqlParser.SubqueryContext) {
return true;
}
if (ctx instanceof JpqlParser.Update_statementContext || ctx instanceof JpqlParser.Delete_statementContext) {
return false;
}
ctx = ctx.getParent();
}
return false;
}
/**
* Is this AST tree a {@literal set} query that has been added through {@literal UNION|INTERSECT|EXCEPT}?
*
* @return boolean
*/
static boolean isSetQuery(ParserRuleContext ctx) {
while (ctx != null) {
if (ctx instanceof JpqlParser.Set_fuctionContext) {
return true;
}
ctx = ctx.getParent();
}
return false;
}
@Override
public QueryTokenStream visitStart(JpqlParser.StartContext ctx) {
return visit(ctx.ql_statement());
}
@Override
public QueryTokenStream visitFrom_clause(JpqlParser.From_clauseContext ctx) {
QueryRendererBuilder builder = QueryRenderer.builder();
builder.append(QueryTokens.expression(ctx.FROM()));
builder.appendInline(visit(ctx.identification_variable_declaration()));
if (!ctx.identificationVariableDeclarationOrCollectionMemberDeclaration().isEmpty()) {
builder.append(TOKEN_COMMA);
}
builder.appendExpression(QueryTokenStream
.concat(ctx.identificationVariableDeclarationOrCollectionMemberDeclaration(), this::visit, TOKEN_COMMA));
return builder;
}
@Override
public QueryTokenStream visitIdentificationVariableDeclarationOrCollectionMemberDeclaration(
JpqlParser.IdentificationVariableDeclarationOrCollectionMemberDeclarationContext ctx) {
if (ctx.subquery() != null) {
QueryRendererBuilder nested = QueryRenderer.builder();
nested.append(TOKEN_OPEN_PAREN);
nested.appendInline(visit(ctx.subquery()));
nested.append(TOKEN_CLOSE_PAREN);
QueryRendererBuilder builder = QueryRenderer.builder();
builder.appendExpression(nested);
if (ctx.AS() != null) {
builder.append(QueryTokens.expression(ctx.AS()));
}
if (ctx.identification_variable() != null) {
builder.appendExpression(visit(ctx.identification_variable()));
}
return builder;
}
return super.visitIdentificationVariableDeclarationOrCollectionMemberDeclaration(ctx);
}
@Override
public QueryTokenStream visitJoin_association_path_expression(
JpqlParser.Join_association_path_expressionContext ctx) {
QueryRendererBuilder builder = QueryRenderer.builder();
if (ctx.TREAT() == null) {
if (ctx.join_collection_valued_path_expression() != null) {
builder.appendExpression(visit(ctx.join_collection_valued_path_expression()));
} else if (ctx.join_single_valued_path_expression() != null) {
builder.appendExpression(visit(ctx.join_single_valued_path_expression()));
}
} else {
QueryRendererBuilder nested = QueryRenderer.builder();
if (ctx.join_collection_valued_path_expression() != null) {
nested.appendExpression(visit(ctx.join_collection_valued_path_expression()));
nested.append(QueryTokens.expression(ctx.AS()));
nested.appendExpression(visit(ctx.subtype()));
} else if (ctx.join_single_valued_path_expression() != null) {
nested.appendExpression(visit(ctx.join_single_valued_path_expression()));
nested.append(QueryTokens.expression(ctx.AS()));
nested.appendExpression(visit(ctx.subtype()));
}
builder.append(QueryTokens.token(ctx.TREAT()));
builder.append(TOKEN_OPEN_PAREN);
builder.appendInline(nested);
builder.append(TOKEN_CLOSE_PAREN);
}
return builder;
}
@Override
public QueryTokenStream visitJoin_collection_valued_path_expression(
JpqlParser.Join_collection_valued_path_expressionContext ctx) {
List<ParseTree> items = new ArrayList<>(2 + ctx.single_valued_embeddable_object_field().size());
if (ctx.identification_variable() != null) {
items.add(ctx.identification_variable());
}
items.addAll(ctx.single_valued_embeddable_object_field());
items.add(ctx.collection_valued_field());
return QueryTokenStream.concat(items, this::visit, TOKEN_DOT);
}
@Override
public QueryTokenStream visitJoin_single_valued_path_expression(
JpqlParser.Join_single_valued_path_expressionContext ctx) {
List<ParseTree> items = new ArrayList<>(2 + ctx.single_valued_embeddable_object_field().size());
if (ctx.identification_variable() != null) {
items.add(ctx.identification_variable());
}
items.addAll(ctx.single_valued_embeddable_object_field());
items.add(ctx.single_valued_object_field());
return QueryTokenStream.concat(items, this::visit, TOKEN_DOT);
}
@Override
public QueryTokenStream visitCollection_member_declaration(JpqlParser.Collection_member_declarationContext ctx) {
QueryRendererBuilder nested = QueryRenderer.builder();
nested.append(QueryTokens.token(ctx.IN()));
nested.append(TOKEN_OPEN_PAREN);
nested.appendInline(visit(ctx.collection_valued_path_expression()));
nested.append(TOKEN_CLOSE_PAREN);
QueryRendererBuilder builder = QueryRenderer.builder();
builder.appendExpression(nested);
if (ctx.AS() != null) {
builder.append(QueryTokens.expression(ctx.AS()));
}
if (ctx.identification_variable() != null) {
builder.appendExpression(visit(ctx.identification_variable()));
}
return builder;
}
@Override
public QueryTokenStream visitQualified_identification_variable(
JpqlParser.Qualified_identification_variableContext ctx) {
QueryRendererBuilder builder = QueryRenderer.builder();
if (ctx.map_field_identification_variable() != null) {
builder.append(visit(ctx.map_field_identification_variable()));
} else if (ctx.identification_variable() != null) {
builder.append(QueryTokens.expression(ctx.ENTRY()));
builder.append(TOKEN_OPEN_PAREN);
builder.append(visit(ctx.identification_variable()));
builder.append(TOKEN_CLOSE_PAREN);
}
return builder;
}
@Override
public QueryTokenStream visitMap_field_identification_variable(
JpqlParser.Map_field_identification_variableContext ctx) {
QueryRendererBuilder builder = QueryRenderer.builder();
if (ctx.KEY() != null) {
builder.append(QueryTokens.token(ctx.KEY()));
builder.append(TOKEN_OPEN_PAREN);
builder.appendInline(visit(ctx.identification_variable()));
builder.append(TOKEN_CLOSE_PAREN);
} else if (ctx.VALUE() != null) {
builder.append(QueryTokens.token(ctx.VALUE()));
builder.append(TOKEN_OPEN_PAREN);
builder.appendInline(visit(ctx.identification_variable()));
builder.append(TOKEN_CLOSE_PAREN);
}
return builder;
}
@Override
public QueryTokenStream visitSingle_valued_path_expression(JpqlParser.Single_valued_path_expressionContext ctx) {
QueryRendererBuilder builder = QueryRenderer.builder();
if (ctx.qualified_identification_variable() != null) {
builder.append(visit(ctx.qualified_identification_variable()));
} else if (ctx.qualified_identification_variable() != null) {
builder.append(QueryTokens.token(ctx.TREAT()));
builder.append(TOKEN_OPEN_PAREN);
builder.appendInline(visit(ctx.qualified_identification_variable()));
builder.append(QueryTokens.expression(ctx.AS()));
builder.appendInline(visit(ctx.subtype()));
builder.append(TOKEN_CLOSE_PAREN);
} else if (ctx.state_field_path_expression() != null) {
builder.append(visit(ctx.state_field_path_expression()));
} else if (ctx.single_valued_object_path_expression() != null) {
builder.append(visit(ctx.single_valued_object_path_expression()));
}
return builder;
}
@Override
public QueryTokenStream visitGeneral_subpath(JpqlParser.General_subpathContext ctx) {
if (ctx.simple_subpath() != null) {
return visit(ctx.simple_subpath());
} else if (ctx.treated_subpath() != null) {
List<ParseTree> items = new ArrayList<>(1 + ctx.single_valued_object_field().size());
items.add(ctx.treated_subpath());
items.addAll(ctx.single_valued_object_field());
return QueryTokenStream.concat(items, this::visit, TOKEN_DOT);
}
return QueryTokenStream.empty();
}
@Override
public QueryTokenStream visitSimple_subpath(JpqlParser.Simple_subpathContext ctx) {
List<ParseTree> items = new ArrayList<>(1 + ctx.single_valued_object_field().size());
items.add(ctx.general_identification_variable());
items.addAll(ctx.single_valued_object_field());
return QueryTokenStream.concat(items, this::visit, TOKEN_DOT);
}
@Override
public QueryTokenStream visitTreated_subpath(JpqlParser.Treated_subpathContext ctx) {
QueryRendererBuilder builder = QueryRenderer.builder();
QueryRendererBuilder nested = QueryRenderer.builder();
nested.appendExpression(visit(ctx.general_subpath()));
nested.append(QueryTokens.expression(ctx.AS()));
nested.appendExpression(visit(ctx.subtype()));
builder.append(QueryTokens.token(ctx.TREAT()));
builder.append(TOKEN_OPEN_PAREN);
builder.appendInline(nested);
builder.append(TOKEN_CLOSE_PAREN);
return builder;
}
@Override
public QueryTokenStream visitState_field_path_expression(JpqlParser.State_field_path_expressionContext ctx) {
QueryRendererBuilder builder = QueryRenderer.builder();
builder.appendInline(visit(ctx.general_subpath()));
builder.append(TOKEN_DOT);
builder.appendInline(visit(ctx.state_field()));
return builder;
}
@Override
public QueryTokenStream visitSingle_valued_object_path_expression(
JpqlParser.Single_valued_object_path_expressionContext ctx) {
QueryRendererBuilder builder = QueryRenderer.builder();
builder.appendInline(visit(ctx.general_subpath()));
builder.append(TOKEN_DOT);
builder.appendInline(visit(ctx.single_valued_object_field()));
return builder;
}
@Override
public QueryTokenStream visitCollection_valued_path_expression(
JpqlParser.Collection_valued_path_expressionContext ctx) {
QueryRendererBuilder builder = QueryRenderer.builder();
builder.appendInline(visit(ctx.general_subpath()));
builder.append(TOKEN_DOT);
builder.appendInline(visit(ctx.collection_value_field()));
return builder;
}
@Override
public QueryTokenStream visitUpdate_clause(JpqlParser.Update_clauseContext ctx) {
QueryRendererBuilder builder = QueryRenderer.builder();
builder.append(QueryTokens.expression(ctx.UPDATE()));
builder.appendExpression(visit(ctx.entity_name()));
if (ctx.AS() != null) {
builder.append(QueryTokens.expression(ctx.AS()));
}
if (ctx.identification_variable() != null) {
builder.appendExpression(visit(ctx.identification_variable()));
}
builder.append(QueryTokens.expression(ctx.SET()));
builder.append(QueryTokenStream.concat(ctx.update_item(), this::visit, TOKEN_COMMA));
return builder;
}
@Override
public QueryTokenStream visitUpdate_item(JpqlParser.Update_itemContext ctx) {
QueryRendererBuilder builder = QueryRenderer.builder();
List<ParseTree> items = new ArrayList<>(3 + ctx.single_valued_embeddable_object_field().size());
if (ctx.identification_variable() != null) {
items.add(ctx.identification_variable());
}
items.addAll(ctx.single_valued_embeddable_object_field());
if (ctx.state_field() != null) {
items.add(ctx.state_field());
} else if (ctx.single_valued_object_field() != null) {
items.add(ctx.single_valued_object_field());
}
builder.appendInline(QueryTokenStream.concat(items, this::visit, TOKEN_DOT));
builder.append(TOKEN_EQUALS);
builder.append(visit(ctx.new_value()));
return builder;
}
@Override
public QueryTokenStream visitSelect_clause(JpqlParser.Select_clauseContext ctx) {
QueryRendererBuilder builder = prepareSelectClause(ctx);
builder.appendExpression(QueryTokenStream.concat(ctx.select_item(), this::visit, TOKEN_COMMA));
return builder;
}
QueryRendererBuilder prepareSelectClause(JpqlParser.Select_clauseContext ctx) {
QueryRendererBuilder builder = QueryRenderer.builder();
builder.append(QueryTokens.expression(ctx.SELECT()));
if (ctx.DISTINCT() != null) {
builder.append(QueryTokens.expression(ctx.DISTINCT()));
}
return builder;
}
@Override
public QueryTokenStream visitSelect_expression(JpqlParser.Select_expressionContext ctx) {
if (ctx.identification_variable() != null && ctx.OBJECT() != null) {
QueryRendererBuilder builder = QueryRenderer.builder();
builder.append(QueryTokens.token(ctx.OBJECT()));
builder.append(TOKEN_OPEN_PAREN);
builder.appendInline(visit(ctx.identification_variable()));
builder.append(TOKEN_CLOSE_PAREN);
return builder;
}
return super.visitSelect_expression(ctx);
}
@Override
public QueryTokenStream visitConstructor_expression(JpqlParser.Constructor_expressionContext ctx) {
QueryRendererBuilder builder = QueryRenderer.builder();
builder.append(QueryTokens.expression(ctx.NEW()));
builder.append(visit(ctx.constructor_name()));
builder.append(TOKEN_OPEN_PAREN);
builder.appendInline(QueryTokenStream.concat(ctx.constructor_item(), this::visit, TOKEN_COMMA));
builder.append(TOKEN_CLOSE_PAREN);
return builder;
}
@Override
public QueryTokenStream visitAggregate_expression(JpqlParser.Aggregate_expressionContext ctx) {
QueryRendererBuilder builder = QueryRenderer.builder();
if (ctx.AVG() != null || ctx.MAX() != null || ctx.MIN() != null || ctx.SUM() != null) {
if (ctx.AVG() != null) {
builder.append(QueryTokens.token(ctx.AVG()));
}
if (ctx.MAX() != null) {
builder.append(QueryTokens.token(ctx.MAX()));
}
if (ctx.MIN() != null) {
builder.append(QueryTokens.token(ctx.MIN()));
}
if (ctx.SUM() != null) {
builder.append(QueryTokens.token(ctx.SUM()));
}
builder.append(TOKEN_OPEN_PAREN);
if (ctx.DISTINCT() != null) {
builder.append(QueryTokens.expression(ctx.DISTINCT()));
}
builder.appendInline(visit(ctx.simple_select_expression()));
builder.append(TOKEN_CLOSE_PAREN);
} else if (ctx.COUNT() != null) {
builder.append(QueryTokens.token(ctx.COUNT()));
builder.append(TOKEN_OPEN_PAREN);
if (ctx.DISTINCT() != null) {
builder.append(QueryTokens.expression(ctx.DISTINCT()));
}
builder.appendInline(visit(ctx.simple_select_expression()));
builder.append(TOKEN_CLOSE_PAREN);
} else if (ctx.function_invocation() != null) {
builder.append(visit(ctx.function_invocation()));
}
return builder;
}
@Override
public QueryTokenStream visitGroupby_clause(JpqlParser.Groupby_clauseContext ctx) {
QueryRendererBuilder builder = QueryRenderer.builder();
builder.append(QueryTokens.expression(ctx.GROUP()));
builder.append(QueryTokens.expression(ctx.BY()));
builder.appendExpression(QueryTokenStream.concat(ctx.groupby_item(), this::visit, TOKEN_COMMA));
return builder;
}
@Override
public QueryTokenStream visitOrderby_clause(JpqlParser.Orderby_clauseContext ctx) {
QueryRendererBuilder builder = QueryRenderer.builder();
builder.append(QueryTokens.expression(ctx.ORDER()));
builder.append(QueryTokens.expression(ctx.BY()));
builder.append(QueryTokenStream.concat(ctx.orderby_item(), this::visit, TOKEN_COMMA));
return builder;
}
@Override
public QueryTokenStream visitSubquery_from_clause(JpqlParser.Subquery_from_clauseContext ctx) {
QueryRendererBuilder builder = QueryRenderer.builder();
builder.append(QueryTokens.expression(ctx.FROM()));
builder.appendExpression(
QueryTokenStream.concat(ctx.subselect_identification_variable_declaration(), this::visit, TOKEN_COMMA));
return builder;
}
@Override
public QueryTokenStream visitConditional_primary(JpqlParser.Conditional_primaryContext ctx) {
if (ctx.conditional_expression() != null) {
return QueryTokenStream.group(visit(ctx.conditional_expression()));
}
return super.visitConditional_primary(ctx);
}
@Override
public QueryTokenStream visitIn_expression(JpqlParser.In_expressionContext ctx) {
QueryRendererBuilder builder = QueryRenderer.builder();
if (ctx.string_expression() != null) {
builder.appendExpression(visit(ctx.string_expression()));
}
if (ctx.type_discriminator() != null) {
builder.appendExpression(visit(ctx.type_discriminator()));
}
if (ctx.NOT() != null) {
builder.append(QueryTokens.expression(ctx.NOT()));
}
if (ctx.IN() != null) {
builder.append(QueryTokens.expression(ctx.IN()));
}
if (ctx.in_item() != null && !ctx.in_item().isEmpty()) {
builder.append(QueryTokenStream.group(QueryTokenStream.concat(ctx.in_item(), this::visit, TOKEN_COMMA)));
} else if (ctx.subquery() != null) {
builder.append(QueryTokenStream.group(visit(ctx.subquery())));
} else if (ctx.collection_valued_input_parameter() != null) {
builder.append(visit(ctx.collection_valued_input_parameter()));
}
return builder;
}
@Override
public QueryTokenStream visitExists_expression(JpqlParser.Exists_expressionContext ctx) {
QueryRendererBuilder builder = QueryRenderer.builder();
if (ctx.NOT() != null) {
builder.append(QueryTokens.expression(ctx.NOT()));
}
builder.append(QueryTokens.expression(ctx.EXISTS()));
builder.append(QueryTokenStream.group(visit(ctx.subquery())));
return builder;
}
@Override
public QueryTokenStream visitAll_or_any_expression(JpqlParser.All_or_any_expressionContext ctx) {
QueryRendererBuilder builder = QueryRenderer.builder();
if (ctx.ALL() != null) {
builder.append(QueryTokens.expression(ctx.ALL()));
} else if (ctx.ANY() != null) {
builder.append(QueryTokens.expression(ctx.ANY()));
} else if (ctx.SOME() != null) {
builder.append(QueryTokens.expression(ctx.SOME()));
}
builder.append(QueryTokenStream.group(visit(ctx.subquery())));
return builder;
}
@Override
public QueryTokenStream visitArithmetic_factor(JpqlParser.Arithmetic_factorContext ctx) {
QueryRendererBuilder builder = QueryRenderer.builder();
if (ctx.op != null) {
builder.append(QueryTokens.token(ctx.op));
}
builder.append(visit(ctx.arithmetic_primary()));
return builder;
}
@Override
public QueryTokenStream visitArithmetic_primary(JpqlParser.Arithmetic_primaryContext ctx) {
if (ctx.arithmetic_expression() != null) {
return QueryTokenStream.group(visit(ctx.arithmetic_expression()));
} else if (ctx.subquery() != null) {
return QueryTokenStream.group(visit(ctx.subquery()));
}
return super.visitArithmetic_primary(ctx);
}
@Override
public QueryTokenStream visitString_expression(JpqlParser.String_expressionContext ctx) {
QueryRendererBuilder builder = QueryRenderer.builder();
if (ctx.subquery() != null) {
return QueryTokenStream.group(visit(ctx.subquery()));
}
return super.visitString_expression(ctx);
}
@Override
public QueryTokenStream visitDatetime_expression(JpqlParser.Datetime_expressionContext ctx) {
if (ctx.subquery() != null) {
return QueryTokenStream.group(visit(ctx.subquery()));
}
return super.visitDatetime_expression(ctx);
}
@Override
public QueryTokenStream visitBoolean_expression(JpqlParser.Boolean_expressionContext ctx) {
if (ctx.subquery() != null) {
return QueryTokenStream.group(visit(ctx.subquery()));
}
return super.visitBoolean_expression(ctx);
}
@Override
public QueryTokenStream visitEnum_expression(JpqlParser.Enum_expressionContext ctx) {
if (ctx.subquery() != null) {
return QueryTokenStream.group(visit(ctx.subquery()));
}
return super.visitEnum_expression(ctx);
}
@Override
public QueryTokenStream visitType_discriminator(JpqlParser.Type_discriminatorContext ctx) {
QueryRendererBuilder builder = QueryRenderer.builder();
if (ctx.general_identification_variable() != null) {
builder.append(visit(ctx.general_identification_variable()));
} else if (ctx.single_valued_object_path_expression() != null) {
builder.append(visit(ctx.single_valued_object_path_expression()));
} else if (ctx.input_parameter() != null) {
builder.append(visit(ctx.input_parameter()));
}
return QueryTokenStream.ofFunction(ctx.TYPE(), builder);
}
@Override
public QueryTokenStream visitFunctions_returning_numerics(JpqlParser.Functions_returning_numericsContext ctx) {
QueryRendererBuilder builder = QueryRenderer.builder();
if (ctx.LENGTH() != null) {
return QueryTokenStream.ofFunction(ctx.LENGTH(), visit(ctx.string_expression(0)));
} else if (ctx.LOCATE() != null) {
builder.appendInline(visit(ctx.string_expression(0)));
builder.append(TOKEN_COMMA);
builder.appendInline(visit(ctx.string_expression(1)));
if (ctx.arithmetic_expression() != null) {
builder.append(TOKEN_COMMA);
builder.appendInline(visit(ctx.arithmetic_expression(0)));
}
return QueryTokenStream.ofFunction(ctx.LOCATE(), builder);
} else if (ctx.ABS() != null) {
return QueryTokenStream.ofFunction(ctx.ABS(), visit(ctx.arithmetic_expression(0)));
} else if (ctx.CEILING() != null) {
return QueryTokenStream.ofFunction(ctx.CEILING(), visit(ctx.arithmetic_expression(0)));
} else if (ctx.EXP() != null) {
return QueryTokenStream.ofFunction(ctx.EXP(), visit(ctx.arithmetic_expression(0)));
} else if (ctx.FLOOR() != null) {
return QueryTokenStream.ofFunction(ctx.FLOOR(), visit(ctx.arithmetic_expression(0)));
} else if (ctx.LN() != null) {
return QueryTokenStream.ofFunction(ctx.LN(), visit(ctx.arithmetic_expression(0)));
} else if (ctx.SIGN() != null) {
return QueryTokenStream.ofFunction(ctx.SIGN(), visit(ctx.arithmetic_expression(0)));
} else if (ctx.SQRT() != null) {
return QueryTokenStream.ofFunction(ctx.SQRT(), visit(ctx.arithmetic_expression(0)));
} else if (ctx.MOD() != null) {
builder.appendInline(visit(ctx.arithmetic_expression(0)));
builder.append(TOKEN_COMMA);
builder.appendInline(visit(ctx.arithmetic_expression(1)));
return QueryTokenStream.ofFunction(ctx.MOD(), builder);
} else if (ctx.POWER() != null) {
builder.appendInline(visit(ctx.arithmetic_expression(0)));
builder.append(TOKEN_COMMA);
builder.appendInline(visit(ctx.arithmetic_expression(1)));
return QueryTokenStream.ofFunction(ctx.POWER(), builder);
} else if (ctx.ROUND() != null) {
builder.appendInline(visit(ctx.arithmetic_expression(0)));
builder.append(TOKEN_COMMA);
builder.appendInline(visit(ctx.arithmetic_expression(1)));
return QueryTokenStream.ofFunction(ctx.ROUND(), builder);
} else if (ctx.SIZE() != null) {
return QueryTokenStream.ofFunction(ctx.SIZE(), visit(ctx.collection_valued_path_expression()));
} else if (ctx.INDEX() != null) {
return QueryTokenStream.ofFunction(ctx.INDEX(), visit(ctx.identification_variable()));
} else if (ctx.extract_datetime_field() != null) {
builder.append(visit(ctx.extract_datetime_field()));
}
return builder;
}
@Override
public QueryTokenStream visitFunctions_returning_strings(JpqlParser.Functions_returning_stringsContext ctx) {
QueryRendererBuilder builder = QueryRenderer.builder();
if (ctx.CONCAT() != null) {
return QueryTokenStream.ofFunction(ctx.CONCAT(),
QueryTokenStream.concat(ctx.string_expression(), this::visit, TOKEN_COMMA));
} else if (ctx.SUBSTRING() != null) {
builder.append(visit(ctx.string_expression(0)));
builder.append(TOKEN_COMMA);
builder.appendInline(QueryTokenStream.concat(ctx.arithmetic_expression(), this::visit, TOKEN_COMMA));
return QueryTokenStream.ofFunction(ctx.SUBSTRING(), builder);
} else if (ctx.TRIM() != null) {
if (ctx.trim_specification() != null) {
builder.appendExpression(visit(ctx.trim_specification()));
}
if (ctx.trim_character() != null) {
builder.appendExpression(visit(ctx.trim_character()));
}
if (ctx.FROM() != null) {
builder.append(QueryTokens.expression(ctx.FROM()));
}
builder.append(visit(ctx.string_expression(0)));
return QueryTokenStream.ofFunction(ctx.TRIM(), builder);
} else if (ctx.LOWER() != null) {
return QueryTokenStream.ofFunction(ctx.LOWER(),
QueryTokenStream.concat(ctx.string_expression(), this::visit, TOKEN_COMMA));
} else if (ctx.UPPER() != null) {
return QueryTokenStream.ofFunction(ctx.UPPER(),
QueryTokenStream.concat(ctx.string_expression(), this::visit, TOKEN_COMMA));
} else if (ctx.LEFT() != null) {
builder.append(visit(ctx.string_expression(0)));
builder.append(TOKEN_COMMA);
builder.append(visit(ctx.arithmetic_expression(0)));
return QueryTokenStream.ofFunction(ctx.LEFT(), builder);
} else if (ctx.RIGHT() != null) {
builder.appendInline(visit(ctx.string_expression(0)));
builder.append(TOKEN_COMMA);
builder.append(visit(ctx.arithmetic_expression(0)));
return QueryTokenStream.ofFunction(ctx.RIGHT(), builder);
} else if (ctx.REPLACE() != null) {
return QueryTokenStream.ofFunction(ctx.REPLACE(),
QueryTokenStream.concat(ctx.string_expression(), this::visit, TOKEN_COMMA));
}
return builder;
}
@Override
public QueryTokenStream visitArithmetic_cast_function(JpqlParser.Arithmetic_cast_functionContext ctx) {
QueryRendererBuilder builder = QueryRenderer.builder();
builder.appendExpression(visit(ctx.string_expression()));
if (ctx.AS() != null) {
builder.append(QueryTokens.expression(ctx.AS()));
}
builder.append(QueryTokens.token(ctx.f));
return QueryTokenStream.ofFunction(ctx.CAST(), builder);
}
@Override
public QueryTokenStream visitType_cast_function(JpqlParser.Type_cast_functionContext ctx) {
QueryRendererBuilder builder = QueryRenderer.builder();
builder.appendExpression(visit(ctx.scalar_expression()));
if (ctx.AS() != null) {
builder.append(QueryTokens.expression(ctx.AS()));
}
builder.appendInline(visit(ctx.identification_variable()));
if (!CollectionUtils.isEmpty(ctx.numeric_literal())) {
builder.append(TOKEN_OPEN_PAREN);
builder.appendInline(QueryTokenStream.concat(ctx.numeric_literal(), this::visit, TOKEN_COMMA));
builder.append(TOKEN_CLOSE_PAREN);
}
return QueryTokenStream.ofFunction(ctx.CAST(), builder);
}
@Override
public QueryTokenStream visitString_cast_function(JpqlParser.String_cast_functionContext ctx) {
QueryRendererBuilder builder = QueryRenderer.builder();
builder.appendExpression(visit(ctx.scalar_expression()));
if (ctx.AS() != null) {
builder.append(QueryTokens.expression(ctx.AS()));
}
builder.append(QueryTokens.token(ctx.STRING()));
return QueryTokenStream.ofFunction(ctx.CAST(), builder);
}
@Override
public QueryTokenStream visitFunction_invocation(JpqlParser.Function_invocationContext ctx) {
QueryRendererBuilder builder = QueryRenderer.builder();
builder.append(QueryTokens.token(ctx.FUNCTION()));
builder.append(TOKEN_OPEN_PAREN);
builder.appendInline(visit(ctx.function_name()));
if (!ctx.function_arg().isEmpty()) {
builder.append(TOKEN_COMMA);
builder.appendInline(QueryTokenStream.concat(ctx.function_arg(), this::visit, TOKEN_COMMA));
}
builder.append(TOKEN_CLOSE_PAREN);
return builder;
}
@Override
public QueryTokenStream visitExtract_datetime_field(JpqlParser.Extract_datetime_fieldContext ctx) {
QueryRendererBuilder nested = QueryRenderer.builder();
nested.appendExpression(visit(ctx.datetime_field()));
nested.append(QueryTokens.expression(ctx.FROM()));
nested.appendExpression(visit(ctx.datetime_expression()));
return QueryTokenStream.ofFunction(ctx.EXTRACT(), nested);
}
@Override
public QueryTokenStream visitDatetime_field(JpqlParser.Datetime_fieldContext ctx) {
return visit(ctx.identification_variable());
}
@Override
public QueryTokenStream visitExtract_datetime_part(JpqlParser.Extract_datetime_partContext ctx) {
QueryRendererBuilder nested = QueryRenderer.builder();
nested.appendExpression(visit(ctx.datetime_part()));
nested.append(QueryTokens.expression(ctx.FROM()));
nested.appendExpression(visit(ctx.datetime_expression()));
return QueryTokenStream.ofFunction(ctx.EXTRACT(), nested);
}
@Override
public QueryTokenStream visitCoalesce_expression(JpqlParser.Coalesce_expressionContext ctx) {
return QueryTokenStream.ofFunction(ctx.COALESCE(),
QueryTokenStream.concat(ctx.scalar_expression(), this::visit, TOKEN_COMMA));
}
@Override
public QueryTokenStream visitNullif_expression(JpqlParser.Nullif_expressionContext ctx) {
return QueryTokenStream.ofFunction(ctx.NULLIF(),
QueryTokenStream.concat(ctx.scalar_expression(), this::visit, TOKEN_COMMA));
}
@Override
public QueryTokenStream visitInput_parameter(JpqlParser.Input_parameterContext ctx) {
QueryRendererBuilder builder = QueryRenderer.builder();
if (ctx.INTLITERAL() != null) {
builder.append(TOKEN_QUESTION_MARK);
builder.append(QueryTokens.token(ctx.INTLITERAL()));
} else if (ctx.identification_variable() != null) {
builder.append(TOKEN_COLON);
builder.appendInline(visit(ctx.identification_variable()));
}
return builder;
}
@Override
public QueryTokenStream visitEntity_name(JpqlParser.Entity_nameContext ctx) {
return QueryTokenStream.concat(ctx.reserved_word(), this::visit, TOKEN_DOT);
}
@Override
public QueryTokenStream visitChildren(RuleNode node) {
int childCount = node.getChildCount();
if (childCount == 1 && node.getChild(0) instanceof RuleContext t) {
return visit(t);
}
if (childCount == 1 && node.getChild(0) instanceof TerminalNode t) {
return QueryTokens.token(t);
}
return QueryTokenStream.concatExpressions(node, this::visit);
}
}