Add support for JPA 3.2 additions to EQL.
See: #3136 Original Pull Request: #3695
This commit is contained in:
committed by
Mark Paluch
parent
ebda35d385
commit
262e05eda8
@@ -309,6 +309,7 @@ scalar_expression
|
||||
| datetime_expression
|
||||
| boolean_expression
|
||||
| case_expression
|
||||
| cast_function
|
||||
| entity_type_expression
|
||||
;
|
||||
|
||||
@@ -458,6 +459,7 @@ string_expression
|
||||
| string_cast_function
|
||||
| type_cast_function
|
||||
| '(' subquery ')'
|
||||
| string_expression '||' string_expression
|
||||
;
|
||||
|
||||
datetime_expression
|
||||
@@ -542,6 +544,9 @@ functions_returning_strings
|
||||
| TRIM '(' ((trim_specification)? (trim_character)? FROM)? string_expression ')'
|
||||
| LOWER '(' string_expression ')'
|
||||
| UPPER '(' string_expression ')'
|
||||
| REPLACE '(' string_expression ',' string_expression ',' string_expression ')'
|
||||
| LEFT '(' string_expression ',' arithmetic_expression ')'
|
||||
| RIGHT '(' string_expression ',' arithmetic_expression ')'
|
||||
;
|
||||
|
||||
trim_specification
|
||||
@@ -625,6 +630,14 @@ nullif_expression
|
||||
: NULLIF '(' scalar_expression ',' scalar_expression ')'
|
||||
;
|
||||
|
||||
type_literal
|
||||
: STRING
|
||||
| INTEGER
|
||||
| LONG
|
||||
| FLOAT
|
||||
| DOUBLE
|
||||
;
|
||||
|
||||
/*******************
|
||||
Gaps in the spec.
|
||||
*******************/
|
||||
@@ -637,6 +650,7 @@ trim_character
|
||||
identification_variable
|
||||
: IDENTIFICATION_VARIABLE
|
||||
| f=(COUNT
|
||||
| AS
|
||||
| DATE
|
||||
| FROM
|
||||
| INNER
|
||||
@@ -646,11 +660,13 @@ identification_variable
|
||||
| ORDER
|
||||
| OUTER
|
||||
| POWER
|
||||
| RIGHT
|
||||
| FLOOR
|
||||
| SIGN
|
||||
| TIME
|
||||
| TYPE
|
||||
| VALUE)
|
||||
| type_literal
|
||||
;
|
||||
|
||||
constructor_name
|
||||
@@ -832,6 +848,8 @@ reserved_word
|
||||
|OR
|
||||
|ORDER
|
||||
|OUTER
|
||||
|REPLACE
|
||||
|RIGHT
|
||||
|POWER
|
||||
|ROUND
|
||||
|SELECT
|
||||
@@ -928,6 +946,7 @@ EXTRACT : E X T R A C T;
|
||||
FALSE : F A L S E;
|
||||
FETCH : F E T C H;
|
||||
FIRST : F I R S T;
|
||||
FLOAT : F L O A T;
|
||||
FLOOR : F L O O R;
|
||||
FLOAT : F L O A T;
|
||||
FROM : F R O M;
|
||||
@@ -937,6 +956,7 @@ HAVING : H A V I N G;
|
||||
IN : I N;
|
||||
INDEX : I N D E X;
|
||||
INNER : I N N E R;
|
||||
INTEGER : I N T E G E R;
|
||||
INTERSECT : I N T E R S E C T;
|
||||
IS : I S;
|
||||
INTEGER : I N T E G E R;
|
||||
@@ -969,6 +989,8 @@ ORDER : O R D E R;
|
||||
OUTER : O U T E R;
|
||||
POWER : P O W E R;
|
||||
REGEXP : R E G E X P;
|
||||
REPLACE : R E P L A C E;
|
||||
RIGHT : R I G H T;
|
||||
ROUND : R O U N D;
|
||||
SELECT : S E L E C T;
|
||||
SET : S E T;
|
||||
@@ -976,6 +998,7 @@ SIGN : S I G N;
|
||||
SIZE : S I Z E;
|
||||
SOME : S O M E;
|
||||
SQRT : S Q R T;
|
||||
STRING : S T R I N G;
|
||||
SUBSTRING : S U B S T R I N G;
|
||||
STRING : S T R I N G;
|
||||
SUM : S U M;
|
||||
@@ -996,7 +1019,6 @@ WHERE : W H E R E;
|
||||
EQUAL : '=' ;
|
||||
NOT_EQUAL : '<>' | '!=' ;
|
||||
|
||||
|
||||
CHARACTER : '\'' (~ ('\'' | '\\')) '\'' ;
|
||||
IDENTIFICATION_VARIABLE : ('a' .. 'z' | 'A' .. 'Z' | '\u0080' .. '\ufffe' | '$' | '_') ('a' .. 'z' | 'A' .. 'Z' | '\u0080' .. '\ufffe' | '0' .. '9' | '$' | '_')* ;
|
||||
STRINGLITERAL : '\'' (~ ('\'' | '\\')|'\\')* '\'' ;
|
||||
|
||||
@@ -43,13 +43,25 @@ ql_statement
|
||||
;
|
||||
|
||||
select_statement
|
||||
: select_clause from_clause (where_clause)? (groupby_clause)? (having_clause)? (orderby_clause)? (setOperator_with_select_statement)*
|
||||
: select_query
|
||||
;
|
||||
|
||||
setOperator_with_select_statement
|
||||
: INTERSECT select_statement
|
||||
| UNION select_statement
|
||||
| EXCEPT select_statement
|
||||
select_query
|
||||
: select_clause from_clause (where_clause)? (groupby_clause)? (having_clause)? (orderby_clause)? (set_fuction)?
|
||||
;
|
||||
|
||||
setOperator
|
||||
: UNION ALL?
|
||||
| INTERSECT ALL?
|
||||
| EXCEPT ALL?
|
||||
;
|
||||
|
||||
set_fuction
|
||||
: setOperator set_function_select
|
||||
;
|
||||
|
||||
set_function_select
|
||||
: select_query
|
||||
;
|
||||
|
||||
update_statement
|
||||
@@ -303,6 +315,7 @@ scalar_expression
|
||||
| datetime_expression
|
||||
| boolean_expression
|
||||
| case_expression
|
||||
| cast_expression
|
||||
| entity_type_expression
|
||||
;
|
||||
|
||||
@@ -453,6 +466,7 @@ string_expression
|
||||
| string_cast_function
|
||||
| type_cast_function
|
||||
| '(' subquery ')'
|
||||
| string_expression '||' string_expression
|
||||
;
|
||||
|
||||
datetime_expression
|
||||
@@ -536,7 +550,10 @@ functions_returning_strings
|
||||
| SUBSTRING '(' string_expression ',' arithmetic_expression (',' arithmetic_expression)? ')'
|
||||
| TRIM '(' ((trim_specification)? (trim_character)? FROM)? string_expression ')'
|
||||
| LOWER '(' string_expression ')'
|
||||
| REPLACE '(' string_expression ',' string_expression ',' string_expression ')'
|
||||
| UPPER '(' string_expression ')'
|
||||
| LEFT '(' string_expression ',' arithmetic_expression ')'
|
||||
| RIGHT '(' string_expression ',' arithmetic_expression ')'
|
||||
;
|
||||
|
||||
trim_specification
|
||||
@@ -620,6 +637,10 @@ nullif_expression
|
||||
: NULLIF '(' scalar_expression ',' scalar_expression ')'
|
||||
;
|
||||
|
||||
cast_expression
|
||||
: CAST '(' string_expression AS type_literal ')'
|
||||
;
|
||||
|
||||
/*******************
|
||||
Gaps in the spec.
|
||||
*******************/
|
||||
@@ -641,6 +662,7 @@ identification_variable
|
||||
| ORDER
|
||||
| OUTER
|
||||
| POWER
|
||||
| RIGHT
|
||||
| FLOOR
|
||||
| SIGN
|
||||
| TIME
|
||||
@@ -691,6 +713,14 @@ numeric_literal
|
||||
| LONGLITERAL
|
||||
;
|
||||
|
||||
type_literal
|
||||
: STRING
|
||||
| INTEGER
|
||||
| LONG
|
||||
| FLOAT
|
||||
| DOUBLE
|
||||
;
|
||||
|
||||
boolean_literal
|
||||
: TRUE
|
||||
| FALSE
|
||||
@@ -827,6 +857,8 @@ reserved_word
|
||||
|ORDER
|
||||
|OUTER
|
||||
|POWER
|
||||
|REPLACE
|
||||
|RIGHT
|
||||
|ROUND
|
||||
|SELECT
|
||||
|SET
|
||||
@@ -922,6 +954,7 @@ EXTRACT : E X T R A C T;
|
||||
FALSE : F A L S E;
|
||||
FETCH : F E T C H;
|
||||
FIRST : F I R S T;
|
||||
FLOAT : F L O A T;
|
||||
FLOOR : F L O O R;
|
||||
FLOAT : F L O A T;
|
||||
FROM : F R O M;
|
||||
@@ -931,6 +964,7 @@ HAVING : H A V I N G;
|
||||
IN : I N;
|
||||
INDEX : I N D E X;
|
||||
INNER : I N N E R;
|
||||
INTEGER : I N T E G E R;
|
||||
INTERSECT : I N T E R S E C T;
|
||||
IS : I S;
|
||||
INTEGER : I N T E G E R;
|
||||
@@ -961,6 +995,8 @@ ON : O N;
|
||||
OR : O R;
|
||||
ORDER : O R D E R;
|
||||
OUTER : O U T E R;
|
||||
REPLACE : R E P L A C E;
|
||||
RIGHT : R I G H T;
|
||||
POWER : P O W E R;
|
||||
REGEXP : R E G E X P;
|
||||
ROUND : R O U N D;
|
||||
@@ -970,6 +1006,7 @@ SIGN : S I G N;
|
||||
SIZE : S I Z E;
|
||||
SOME : S O M E;
|
||||
SQRT : S Q R T;
|
||||
STRING : S T R I N G;
|
||||
SUBSTRING : S U B S T R I N G;
|
||||
STRING : S T R I N G;
|
||||
SUM : S U M;
|
||||
|
||||
@@ -24,6 +24,7 @@ import org.antlr.v4.runtime.tree.ParseTree;
|
||||
|
||||
import org.springframework.data.jpa.repository.query.QueryRenderer.QueryRendererBuilder;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
import org.springframework.util.ObjectUtils;
|
||||
|
||||
/**
|
||||
* An ANTLR {@link org.antlr.v4.runtime.tree.ParseTreeVisitor} that renders an EQL query without making any changes.
|
||||
@@ -1009,6 +1010,8 @@ class EqlQueryRenderer extends EqlBaseVisitor<QueryTokenStream> {
|
||||
builder.append(visit(ctx.case_expression()));
|
||||
} else if (ctx.entity_type_expression() != null) {
|
||||
builder.append(visit(ctx.entity_type_expression()));
|
||||
} else if (ctx.cast_function() != null) {
|
||||
return (visit(ctx.cast_function()));
|
||||
}
|
||||
|
||||
return builder;
|
||||
@@ -1610,6 +1613,11 @@ class EqlQueryRenderer extends EqlBaseVisitor<QueryTokenStream> {
|
||||
builder.append(TOKEN_OPEN_PAREN);
|
||||
builder.appendInline(visit(ctx.subquery()));
|
||||
builder.append(TOKEN_CLOSE_PAREN);
|
||||
} else if (!ObjectUtils.isEmpty(ctx.string_expression())) {
|
||||
|
||||
builder.appendInline(visit(ctx.string_expression(0)));
|
||||
builder.append(TOKEN_DOUBLE_PIPE);
|
||||
builder.appendExpression(visit(ctx.string_expression(1)));
|
||||
}
|
||||
|
||||
return builder;
|
||||
@@ -1941,6 +1949,32 @@ class EqlQueryRenderer extends EqlBaseVisitor<QueryTokenStream> {
|
||||
builder.append(TOKEN_OPEN_PAREN);
|
||||
builder.appendInline(visit(ctx.string_expression(0)));
|
||||
builder.append(TOKEN_CLOSE_PAREN);
|
||||
} else if (ctx.LEFT() != null) {
|
||||
|
||||
builder.append(QueryTokens.token(ctx.LEFT()));
|
||||
builder.append(TOKEN_OPEN_PAREN);
|
||||
builder.appendInline(visit(ctx.string_expression(0)));
|
||||
builder.append(TOKEN_COMMA);
|
||||
builder.appendInline(visit(ctx.arithmetic_expression(0)));
|
||||
builder.append(TOKEN_CLOSE_PAREN);
|
||||
} else if (ctx.RIGHT() != null) {
|
||||
|
||||
builder.append(QueryTokens.token(ctx.RIGHT()));
|
||||
builder.append(TOKEN_OPEN_PAREN);
|
||||
builder.appendInline(visit(ctx.string_expression(0)));
|
||||
builder.append(TOKEN_COMMA);
|
||||
builder.appendInline(visit(ctx.arithmetic_expression(0)));
|
||||
builder.append(TOKEN_CLOSE_PAREN);
|
||||
} else if (ctx.REPLACE() != null) {
|
||||
|
||||
builder.append(QueryTokens.token(ctx.REPLACE()));
|
||||
builder.append(TOKEN_OPEN_PAREN);
|
||||
builder.appendInline(visit(ctx.string_expression(0)));
|
||||
builder.append(TOKEN_COMMA);
|
||||
builder.appendInline(visit(ctx.string_expression(1)));
|
||||
builder.append(TOKEN_COMMA);
|
||||
builder.appendInline(visit(ctx.string_expression(2)));
|
||||
builder.append(TOKEN_CLOSE_PAREN);
|
||||
}
|
||||
|
||||
return builder;
|
||||
@@ -1986,7 +2020,7 @@ class EqlQueryRenderer extends EqlBaseVisitor<QueryTokenStream> {
|
||||
if (ctx.AS() != null) {
|
||||
builder.append(QueryTokens.expression(ctx.AS()));
|
||||
}
|
||||
builder.appendInline(visit(ctx.identification_variable()));
|
||||
builder.appendInline(QueryTokenStream.concat(ctx.identification_variable(), this::visit, TOKEN_SPACE));
|
||||
|
||||
if (!CollectionUtils.isEmpty(ctx.numeric_literal())) {
|
||||
|
||||
@@ -2106,6 +2140,14 @@ class EqlQueryRenderer extends EqlBaseVisitor<QueryTokenStream> {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public QueryRendererBuilder visitType_literal(EqlParser.Type_literalContext ctx) {
|
||||
|
||||
QueryRendererBuilder builder = QueryRenderer.builder();
|
||||
ctx.children.forEach(it -> builder.append(QueryTokens.expression(it.getText())));
|
||||
return builder;
|
||||
}
|
||||
|
||||
@Override
|
||||
public QueryTokenStream visitGeneral_case_expression(EqlParser.General_case_expressionContext ctx) {
|
||||
|
||||
@@ -2226,9 +2268,11 @@ class EqlQueryRenderer extends EqlBaseVisitor<QueryTokenStream> {
|
||||
return QueryRendererBuilder.from(QueryTokens.expression(ctx.IDENTIFICATION_VARIABLE()));
|
||||
} else if (ctx.f != null) {
|
||||
return QueryRendererBuilder.from(QueryTokens.expression(ctx.f));
|
||||
} else {
|
||||
return QueryRenderer.builder();
|
||||
} else if (ctx.type_literal() != null) {
|
||||
return visit(ctx.type_literal());
|
||||
}
|
||||
|
||||
return QueryRenderer.builder();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -42,7 +42,17 @@ class JpqlCountQueryTransformer extends JpqlQueryRenderer {
|
||||
}
|
||||
|
||||
@Override
|
||||
public QueryRenderer.QueryRendererBuilder visitSelect_statement(JpqlParser.Select_statementContext ctx) {
|
||||
public QueryTokenStream visitSelect_statement(JpqlParser.Select_statementContext ctx) {
|
||||
|
||||
if(ctx.select_query() != null) {
|
||||
return visitSelect_query(ctx.select_query());
|
||||
}
|
||||
|
||||
return QueryTokenStream.empty();
|
||||
}
|
||||
|
||||
@Override
|
||||
public QueryTokenStream visitSelect_query(JpqlParser.Select_queryContext ctx) {
|
||||
|
||||
QueryRendererBuilder builder = QueryRenderer.builder();
|
||||
|
||||
|
||||
@@ -15,17 +15,34 @@
|
||||
*/
|
||||
package org.springframework.data.jpa.repository.query;
|
||||
|
||||
import static org.springframework.data.jpa.repository.query.QueryTokens.*;
|
||||
import static org.springframework.data.jpa.repository.query.QueryTokens.TOKEN_CLOSE_PAREN;
|
||||
import static org.springframework.data.jpa.repository.query.QueryTokens.TOKEN_COLON;
|
||||
import static org.springframework.data.jpa.repository.query.QueryTokens.TOKEN_COMMA;
|
||||
import static org.springframework.data.jpa.repository.query.QueryTokens.TOKEN_DOT;
|
||||
import static org.springframework.data.jpa.repository.query.QueryTokens.TOKEN_EQUALS;
|
||||
import static org.springframework.data.jpa.repository.query.QueryTokens.TOKEN_OPEN_PAREN;
|
||||
import static org.springframework.data.jpa.repository.query.QueryTokens.TOKEN_QUESTION_MARK;
|
||||
import static org.springframework.data.jpa.repository.query.QueryTokens.TOKEN_DOUBLE_PIPE;
|
||||
import static org.springframework.data.jpa.repository.query.QueryTokens.TOKEN_SPACE;
|
||||
import static org.springframework.data.jpa.repository.query.QueryTokens.TOKEN_CLOSE_PAREN;
|
||||
import static org.springframework.data.jpa.repository.query.QueryTokens.TOKEN_OPEN_PAREN;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.antlr.v4.runtime.tree.ParseTree;
|
||||
|
||||
import org.springframework.data.jpa.repository.query.JpqlParser.Except_clauseContext;
|
||||
import org.springframework.data.jpa.repository.query.JpqlParser.Intersect_clauseContext;
|
||||
import org.springframework.data.jpa.repository.query.JpqlParser.Relation_fuctions_selectContext;
|
||||
import org.springframework.data.jpa.repository.query.JpqlParser.NullsPrecedenceContext;
|
||||
import org.springframework.data.jpa.repository.query.JpqlParser.Cast_expressionContext;
|
||||
import org.springframework.data.jpa.repository.query.JpqlParser.Reserved_wordContext;
|
||||
import org.springframework.data.jpa.repository.query.JpqlParser.Set_fuctionContext;
|
||||
import org.springframework.data.jpa.repository.query.JpqlParser.Type_literalContext;
|
||||
import org.springframework.data.jpa.repository.query.QueryRenderer.QueryRendererBuilder;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
import org.springframework.util.ObjectUtils;
|
||||
|
||||
/**
|
||||
* An ANTLR {@link org.antlr.v4.runtime.tree.ParseTreeVisitor} that renders a JPQL query without making any changes.
|
||||
@@ -56,8 +73,17 @@ class JpqlQueryRenderer extends JpqlBaseVisitor<QueryTokenStream> {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public QueryTokenStream visitSelect_statement(JpqlParser.Select_statementContext ctx) {
|
||||
@Override
|
||||
public QueryTokenStream visitSelect_statement(JpqlParser.Select_statementContext ctx) {
|
||||
|
||||
if(ctx.select_query() != null) {
|
||||
return visitSelect_query(ctx.select_query());
|
||||
}
|
||||
|
||||
return QueryTokenStream.empty();
|
||||
}
|
||||
|
||||
public QueryTokenStream visitSelect_query(JpqlParser.Select_queryContext ctx) {
|
||||
|
||||
QueryRendererBuilder builder = QueryRenderer.builder();
|
||||
|
||||
@@ -80,11 +106,11 @@ class JpqlQueryRenderer extends JpqlBaseVisitor<QueryTokenStream> {
|
||||
builder.appendExpression(visit(ctx.orderby_clause()));
|
||||
}
|
||||
|
||||
ctx.setOperator_with_select_statement().forEach(setOperatorWithSelectStatementContext -> {
|
||||
tokens.addAll(visit(setOperatorWithSelectStatementContext));
|
||||
});
|
||||
if(ctx.set_fuction() != null) {
|
||||
builder.appendExpression(visit(ctx.set_fuction()));
|
||||
}
|
||||
|
||||
return tokens;
|
||||
return builder;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -800,6 +826,19 @@ class JpqlQueryRenderer extends JpqlBaseVisitor<QueryTokenStream> {
|
||||
return builder;
|
||||
}
|
||||
|
||||
@Override
|
||||
public QueryTokenStream visitSet_fuction(Set_fuctionContext ctx) {
|
||||
|
||||
QueryRendererBuilder builder = QueryRenderer.builder();
|
||||
|
||||
builder.append(QueryTokens.expression(ctx.setOperator().getStart()));
|
||||
if(ctx.setOperator().ALL() != null) {
|
||||
builder.append(QueryTokens.expression(ctx.setOperator().ALL()));
|
||||
}
|
||||
builder.appendExpression(visit(ctx.set_function_select().select_query()));
|
||||
return builder;
|
||||
}
|
||||
|
||||
@Override
|
||||
public QueryTokenStream visitOrderby_item(JpqlParser.Orderby_itemContext ctx) {
|
||||
|
||||
@@ -974,6 +1013,8 @@ class JpqlQueryRenderer extends JpqlBaseVisitor<QueryTokenStream> {
|
||||
return visit(ctx.case_expression());
|
||||
} else if (ctx.entity_type_expression() != null) {
|
||||
return visit(ctx.entity_type_expression());
|
||||
} else if (ctx.cast_expression() != null) {
|
||||
return (visit(ctx.cast_expression()));
|
||||
}
|
||||
|
||||
return QueryTokenStream.empty();
|
||||
@@ -1570,6 +1611,11 @@ class JpqlQueryRenderer extends JpqlBaseVisitor<QueryTokenStream> {
|
||||
builder.append(TOKEN_OPEN_PAREN);
|
||||
builder.appendInline(visit(ctx.subquery()));
|
||||
builder.append(TOKEN_CLOSE_PAREN);
|
||||
} else if (!ObjectUtils.isEmpty(ctx.string_expression())) {
|
||||
|
||||
builder.appendInline(visit(ctx.string_expression(0)));
|
||||
builder.append(TOKEN_DOUBLE_PIPE);
|
||||
builder.appendExpression(visit(ctx.string_expression(1)));
|
||||
}
|
||||
|
||||
return builder;
|
||||
@@ -1890,6 +1936,29 @@ class JpqlQueryRenderer extends JpqlBaseVisitor<QueryTokenStream> {
|
||||
builder.append(TOKEN_OPEN_PAREN);
|
||||
builder.append(visit(ctx.string_expression(0)));
|
||||
builder.append(TOKEN_CLOSE_PAREN);
|
||||
} else if (ctx.LEFT() != null) {
|
||||
builder.append(QueryTokens.token(ctx.LEFT()));
|
||||
builder.append(TOKEN_OPEN_PAREN);
|
||||
builder.appendInline(visit(ctx.string_expression(0)));
|
||||
builder.append(TOKEN_COMMA);
|
||||
builder.appendInline(visit(ctx.arithmetic_expression(0)));
|
||||
builder.append(TOKEN_CLOSE_PAREN);
|
||||
} else if (ctx.RIGHT() != null) {
|
||||
builder.append(QueryTokens.token(ctx.RIGHT()));
|
||||
builder.append(TOKEN_OPEN_PAREN);
|
||||
builder.appendInline(visit(ctx.string_expression(0)));
|
||||
builder.append(TOKEN_COMMA);
|
||||
builder.appendInline(visit(ctx.arithmetic_expression(0)));
|
||||
builder.append(TOKEN_CLOSE_PAREN);
|
||||
} else if (ctx.REPLACE() != null) {
|
||||
builder.append(QueryTokens.token(ctx.REPLACE()));
|
||||
builder.append(TOKEN_OPEN_PAREN);
|
||||
builder.appendInline(visit(ctx.string_expression(0)));
|
||||
builder.append(TOKEN_COMMA);
|
||||
builder.appendInline(visit(ctx.string_expression(1)));
|
||||
builder.append(TOKEN_COMMA);
|
||||
builder.appendInline(visit(ctx.string_expression(2)));
|
||||
builder.append(TOKEN_CLOSE_PAREN);
|
||||
}
|
||||
|
||||
return builder;
|
||||
@@ -2048,6 +2117,26 @@ class JpqlQueryRenderer extends JpqlBaseVisitor<QueryTokenStream> {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public QueryRendererBuilder visitCast_expression(Cast_expressionContext ctx) {
|
||||
QueryRendererBuilder builder = QueryRenderer.builder();
|
||||
builder.append(QueryTokens.token(ctx.CAST()));
|
||||
builder.append(TOKEN_OPEN_PAREN);
|
||||
builder.appendInline(visit(ctx.string_expression()));
|
||||
builder.append(QueryTokens.expression(ctx.AS()));
|
||||
builder.appendInline(visit(ctx.type_literal()));
|
||||
builder.append(TOKEN_CLOSE_PAREN);
|
||||
return builder;
|
||||
}
|
||||
|
||||
@Override
|
||||
public QueryRendererBuilder visitType_literal(Type_literalContext ctx) {
|
||||
|
||||
QueryRendererBuilder builder = QueryRenderer.builder();
|
||||
ctx.children.forEach(it -> builder.append(QueryTokens.expression(it.getText())));
|
||||
return builder;
|
||||
}
|
||||
|
||||
@Override
|
||||
public QueryTokenStream visitGeneral_case_expression(JpqlParser.General_case_expressionContext ctx) {
|
||||
|
||||
|
||||
@@ -54,6 +54,16 @@ class JpqlSortedQueryTransformer extends JpqlQueryRenderer {
|
||||
@Override
|
||||
public QueryTokenStream visitSelect_statement(JpqlParser.Select_statementContext ctx) {
|
||||
|
||||
if(ctx.select_query() != null) {
|
||||
return visitSelect_query(ctx.select_query());
|
||||
}
|
||||
|
||||
return QueryTokenStream.empty();
|
||||
}
|
||||
|
||||
@Override
|
||||
public QueryTokenStream visitSelect_query(JpqlParser.Select_queryContext ctx) {
|
||||
|
||||
QueryRendererBuilder builder = QueryRenderer.builder();
|
||||
|
||||
builder.appendExpression(visit(ctx.select_clause()));
|
||||
@@ -96,7 +106,7 @@ class JpqlSortedQueryTransformer extends JpqlQueryRenderer {
|
||||
return builder.append(dtoDelegate.transformSelectionList(tokenStream));
|
||||
}
|
||||
|
||||
private void doVisitOrderBy(QueryRendererBuilder builder, JpqlParser.Select_statementContext ctx) {
|
||||
private void doVisitOrderBy(QueryRendererBuilder builder, JpqlParser.Select_queryContext ctx) {
|
||||
|
||||
if (ctx.orderby_clause() != null) {
|
||||
QueryTokenStream existingOrder = visit(ctx.orderby_clause());
|
||||
|
||||
@@ -18,6 +18,8 @@ package org.springframework.data.jpa.repository.query;
|
||||
import static org.assertj.core.api.Assertions.*;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.params.ParameterizedTest;
|
||||
import org.junit.jupiter.params.provider.ValueSource;
|
||||
|
||||
import org.springframework.data.jpa.repository.query.QueryRenderer.TokenRenderer;
|
||||
|
||||
@@ -412,4 +414,53 @@ class EqlComplianceTests {
|
||||
assertQuery("SELECT e FROM Employee e WHERE (e.active IS NOT null OR e.active = true)");
|
||||
assertQuery("SELECT e FROM Employee e WHERE (e.active IS NOT NULL OR e.active = true)");
|
||||
}
|
||||
|
||||
|
||||
@Test // GH-3496
|
||||
void lateralShouldBeAValidParameter() {
|
||||
|
||||
assertQuery("select e from Employee e where e.lateral = :_lateral");
|
||||
assertQuery("select te from TestEntity te where te.lateral = :lateral");
|
||||
}
|
||||
|
||||
@Test // GH-3136
|
||||
void intersect() {
|
||||
|
||||
assertQuery("""
|
||||
SELECT e FROM Employee e JOIN e.phones p WHERE p.areaCode = :areaCode1
|
||||
INTERSECT SELECT e FROM Employee e JOIN e.phones p WHERE p.areaCode = :areaCode2
|
||||
""");
|
||||
}
|
||||
|
||||
@Test // GH-3136
|
||||
void except() {
|
||||
|
||||
assertQuery("""
|
||||
SELECT e FROM Employee e
|
||||
EXCEPT SELECT e FROM Employee e WHERE e.salary > e.manager.salary
|
||||
""");
|
||||
}
|
||||
|
||||
@ParameterizedTest // GH-3136
|
||||
@ValueSource(strings = {"STRING", "INTEGER", "FLOAT", "DOUBLE"})
|
||||
void jpqlCast(String targetType) {
|
||||
assertQuery("SELECT CAST(e.salary AS %s) FROM Employee e".formatted(targetType));
|
||||
}
|
||||
|
||||
@ParameterizedTest // GH-3136
|
||||
@ValueSource(strings = {"LEFT", "RIGHT"})
|
||||
void leftRightStringFunctions(String keyword) {
|
||||
assertQuery("SELECT %s(e.name, 3) FROM Employee e".formatted(keyword));
|
||||
}
|
||||
|
||||
@Test // GH-3136
|
||||
void replaceStringFunctions() {
|
||||
assertQuery("SELECT REPLACE(e.name, 'o', 'a') FROM Employee e");
|
||||
assertQuery("SELECT REPLACE(e.name, ' ', '_') FROM Employee e");
|
||||
}
|
||||
|
||||
@Test // GH-3136
|
||||
void stringConcatWithPipes() {
|
||||
assertQuery("SELECT e.firstname || e.lastname AS name FROM Employee e");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1983,6 +1983,18 @@ class HqlQueryRendererTests {
|
||||
assertQuery(source);
|
||||
}
|
||||
|
||||
@ParameterizedTest // GH-3136
|
||||
@ValueSource(strings = {"LEFT", "RIGHT"})
|
||||
void leftRightStringFunctions(String keyword) {
|
||||
assertQuery("SELECT %s(e.name, 3) FROM Employee e".formatted(keyword));
|
||||
}
|
||||
|
||||
@Test // GH-3136
|
||||
void replaceStringFunctions() {
|
||||
assertQuery("SELECT REPLACE(e.name, 'o', 'a') FROM Employee e");
|
||||
assertQuery("SELECT REPLACE(e.name, ' ', '_') FROM Employee e");
|
||||
}
|
||||
|
||||
@Test
|
||||
void reservedWordsShouldWork() {
|
||||
|
||||
|
||||
@@ -18,6 +18,8 @@ package org.springframework.data.jpa.repository.query;
|
||||
import static org.assertj.core.api.Assertions.*;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.params.ParameterizedTest;
|
||||
import org.junit.jupiter.params.provider.ValueSource;
|
||||
|
||||
/**
|
||||
* Test to verify compliance of {@link JpqlParser} with standard SQL. Other than {@link JpqlSpecificationTests} tests in
|
||||
@@ -63,4 +65,54 @@ class JpqlComplianceTests {
|
||||
assertQuery("select new com.example.demo.SampleObject(se.id, se.sampleValue, \"java\") from SampleEntity se");
|
||||
}
|
||||
|
||||
@Test // GH-3136
|
||||
void union() {
|
||||
|
||||
assertQuery("""
|
||||
SELECT MAX(e.salary) FROM Employee e WHERE e.address.city = :city1
|
||||
UNION SELECT MAX(e.salary) FROM Employee e WHERE e.address.city = :city2
|
||||
""");
|
||||
}
|
||||
|
||||
@Test // GH-3136
|
||||
void intersect() {
|
||||
|
||||
assertQuery("""
|
||||
SELECT e FROM Employee e JOIN e.phones p WHERE p.areaCode = :areaCode1
|
||||
INTERSECT SELECT e FROM Employee e JOIN e.phones p WHERE p.areaCode = :areaCode2
|
||||
""");
|
||||
}
|
||||
|
||||
@Test // GH-3136
|
||||
void except() {
|
||||
|
||||
assertQuery("""
|
||||
SELECT e FROM Employee e
|
||||
EXCEPT SELECT e FROM Employee e WHERE e.salary > e.manager.salary
|
||||
""");
|
||||
}
|
||||
|
||||
@ParameterizedTest // GH-3136
|
||||
@ValueSource(strings = {"STRING", "INTEGER", "FLOAT", "DOUBLE"})
|
||||
void cast(String targetType) {
|
||||
assertQuery("SELECT CAST(e.salary AS %s) FROM Employee e".formatted(targetType));
|
||||
}
|
||||
|
||||
@ParameterizedTest // GH-3136
|
||||
@ValueSource(strings = {"LEFT", "RIGHT"})
|
||||
void leftRightStringFunctions(String keyword) {
|
||||
assertQuery("SELECT %s(e.name, 3) FROM Employee e".formatted(keyword));
|
||||
}
|
||||
|
||||
@Test // GH-3136
|
||||
void replaceStringFunctions() {
|
||||
assertQuery("SELECT REPLACE(e.name, 'o', 'a') FROM Employee e");
|
||||
assertQuery("SELECT REPLACE(e.name, ' ', '_') FROM Employee e");
|
||||
}
|
||||
|
||||
@Test // GH-3136
|
||||
void stringConcatWithPipes() {
|
||||
assertQuery("SELECT e.firstname || e.lastname AS name FROM Employee e");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user