Skip to content

Commit

Permalink
Support Common Table Expressions in HQL parser.
Browse files Browse the repository at this point in the history
See #2981.
  • Loading branch information
gregturn committed May 26, 2023
1 parent 8e3c52c commit 3817e38
Show file tree
Hide file tree
Showing 4 changed files with 203 additions and 8 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -51,9 +51,37 @@ selectStatement
;

queryExpression
: orderedQuery (setOperator orderedQuery)*
: withClause? orderedQuery (setOperator orderedQuery)*
;

withClause
: WITH cte (',' cte)*
;

cte
: identifier AS (NOT? MATERIALIZED)? '(' queryExpression ')' searchClause? cycleClause?
;

searchClause
: SEARCH (BREADTH | DEPTH) FIRST BY searchSpecifications SET identifier
;

searchSpecifications
: searchSpecification (',' searchSpecification)*
;

searchSpecification
: identifier sortDirection? nullsPrecedence?
;

cycleClause
: CYCLE cteAttributes SET identifier (TO literal DEFAULT literal)? (USING identifier)?
;

cteAttributes
: identifier (',' identifier)*
;

orderedQuery
: (query | '(' queryExpression ')') queryOrder?
;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,10 @@ public List<JpaQueryParsingToken> visitQueryExpression(HqlParser.QueryExpression

List<JpaQueryParsingToken> tokens = new ArrayList<>();

if (ctx.withClause() != null) {
tokens.addAll(visit(ctx.withClause()));
}

tokens.addAll(visit(ctx.orderedQuery(0)));

for (int i = 1; i < ctx.orderedQuery().size(); i++) {
Expand All @@ -70,6 +74,150 @@ public List<JpaQueryParsingToken> visitQueryExpression(HqlParser.QueryExpression
return tokens;
}

@Override
public List<JpaQueryParsingToken> visitWithClause(HqlParser.WithClauseContext ctx) {

List<JpaQueryParsingToken> tokens = new ArrayList<>();

tokens.add(TOKEN_WITH);

ctx.cte().forEach(cteContext -> {

tokens.addAll(visit(cteContext));
tokens.add(TOKEN_COMMA);
});
CLIP(tokens);

return tokens;
}

@Override
public List<JpaQueryParsingToken> visitCte(HqlParser.CteContext ctx) {

List<JpaQueryParsingToken> tokens = new ArrayList<>();

tokens.addAll(visit(ctx.identifier()));
tokens.add(TOKEN_AS);
NOSPACE(tokens);

if (ctx.NOT() != null) {
tokens.add(TOKEN_NOT);
}
if (ctx.MATERIALIZED() != null) {
tokens.add(TOKEN_MATERIALIZED);
}

tokens.add(TOKEN_OPEN_PAREN);
tokens.addAll(visit(ctx.queryExpression()));
tokens.add(TOKEN_CLOSE_PAREN);

if (ctx.searchClause() != null) {
tokens.addAll(visit(ctx.searchClause()));
}
if (ctx.cycleClause() != null) {
tokens.addAll(visit(ctx.cycleClause()));
}

return tokens;
}

@Override
public List<JpaQueryParsingToken> visitSearchClause(HqlParser.SearchClauseContext ctx) {

List<JpaQueryParsingToken> tokens = new ArrayList<>();

tokens.add(new JpaQueryParsingToken(ctx.SEARCH().getText()));

if (ctx.BREADTH() != null) {
tokens.add(new JpaQueryParsingToken(ctx.BREADTH().getText()));
} else if (ctx.DEPTH() != null) {
tokens.add(new JpaQueryParsingToken(ctx.DEPTH().getText()));
}

tokens.add(new JpaQueryParsingToken(ctx.FIRST().getText()));
tokens.add(new JpaQueryParsingToken(ctx.BY().getText()));
tokens.addAll(visit(ctx.searchSpecifications()));
tokens.add(new JpaQueryParsingToken(ctx.SET().getText()));
tokens.addAll(visit(ctx.identifier()));

return tokens;
}

@Override
public List<JpaQueryParsingToken> visitSearchSpecifications(HqlParser.SearchSpecificationsContext ctx) {

List<JpaQueryParsingToken> tokens = new ArrayList<>();

ctx.searchSpecification().forEach(searchSpecificationContext -> {

tokens.addAll(visit(searchSpecificationContext));
tokens.add(TOKEN_COMMA);
});
CLIP(tokens);

return tokens;
}

@Override
public List<JpaQueryParsingToken> visitSearchSpecification(HqlParser.SearchSpecificationContext ctx) {

List<JpaQueryParsingToken> tokens = new ArrayList<>();

tokens.addAll(visit(ctx.identifier()));

if (ctx.sortDirection() != null) {
tokens.addAll(visit(ctx.sortDirection()));
}

if (ctx.nullsPrecedence() != null) {
tokens.addAll(visit(ctx.nullsPrecedence()));
}

return tokens;
}

@Override
public List<JpaQueryParsingToken> visitCycleClause(HqlParser.CycleClauseContext ctx) {

List<JpaQueryParsingToken> tokens = new ArrayList<>();

tokens.add(new JpaQueryParsingToken(ctx.CYCLE().getText()));
tokens.addAll(visit(ctx.cteAttributes()));
tokens.add(new JpaQueryParsingToken(ctx.SET().getText()));
tokens.addAll(visit(ctx.identifier(0)));

if (ctx.TO() != null) {

tokens.add(new JpaQueryParsingToken(ctx.TO().getText()));
tokens.addAll(visit(ctx.literal(0)));
tokens.add(new JpaQueryParsingToken(ctx.DEFAULT().getText()));
tokens.addAll(visit(ctx.literal(1)));
}

if (ctx.USING() != null) {

tokens.add(new JpaQueryParsingToken(ctx.USING().getText()));
tokens.addAll(visit(ctx.identifier(1)));
}

return tokens;
}

@Override
public List<JpaQueryParsingToken> visitCteAttributes(HqlParser.CteAttributesContext ctx) {

List<JpaQueryParsingToken> tokens = new ArrayList<>();

ctx.identifier().forEach(identifierContext -> {

tokens.addAll(visit(identifierContext));
tokens.add(TOKEN_COMMA);
});
CLIP(tokens);

return tokens;
}

@Override
public List<JpaQueryParsingToken> visitOrderedQuery(HqlParser.OrderedQueryContext ctx) {

Expand Down Expand Up @@ -1876,7 +2024,7 @@ public List<JpaQueryParsingToken> visitNotPredicate(HqlParser.NotPredicateContex

List<JpaQueryParsingToken> tokens = new ArrayList<>();

tokens.add(new JpaQueryParsingToken(ctx.NOT()));
tokens.add(TOKEN_NOT);
tokens.addAll(visit(ctx.predicate()));

return tokens;
Expand Down Expand Up @@ -1919,7 +2067,7 @@ public List<JpaQueryParsingToken> visitBetweenExpression(HqlParser.BetweenExpres
tokens.addAll(visit(ctx.expression(0)));

if (ctx.NOT() != null) {
tokens.add(new JpaQueryParsingToken(ctx.NOT()));
tokens.add(TOKEN_NOT);
}

tokens.add(new JpaQueryParsingToken(ctx.BETWEEN()));
Expand All @@ -1939,7 +2087,7 @@ public List<JpaQueryParsingToken> visitDealingWithNullExpression(HqlParser.Deali
tokens.add(new JpaQueryParsingToken(ctx.IS()));

if (ctx.NOT() != null) {
tokens.add(new JpaQueryParsingToken(ctx.NOT()));
tokens.add(TOKEN_NOT);
}

if (ctx.NULL() != null) {
Expand All @@ -1962,7 +2110,7 @@ public List<JpaQueryParsingToken> visitStringPatternMatching(HqlParser.StringPat
tokens.addAll(visit(ctx.expression(0)));

if (ctx.NOT() != null) {
tokens.add(new JpaQueryParsingToken(ctx.NOT()));
tokens.add(TOKEN_NOT);
}

if (ctx.LIKE() != null) {
Expand Down Expand Up @@ -1990,7 +2138,7 @@ public List<JpaQueryParsingToken> visitInExpression(HqlParser.InExpressionContex
tokens.addAll(visit(ctx.expression()));

if (ctx.NOT() != null) {
tokens.add(new JpaQueryParsingToken(ctx.NOT()));
tokens.add(TOKEN_NOT);
}

tokens.add(new JpaQueryParsingToken(ctx.IN()));
Expand Down Expand Up @@ -2081,14 +2229,14 @@ public List<JpaQueryParsingToken> visitCollectionExpression(HqlParser.Collection
tokens.add(new JpaQueryParsingToken(ctx.IS()));

if (ctx.NOT() != null) {
tokens.add(new JpaQueryParsingToken(ctx.NOT()));
tokens.add(TOKEN_NOT);
}

tokens.add(new JpaQueryParsingToken(ctx.EMPTY()));
} else if (ctx.MEMBER() != null) {

if (ctx.NOT() != null) {
tokens.add(new JpaQueryParsingToken(ctx.NOT()));
tokens.add(TOKEN_NOT);
}

tokens.add(new JpaQueryParsingToken(ctx.MEMBER()));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,13 @@ class JpaQueryParsingToken {
public static final JpaQueryParsingToken TOKEN_DESC = new JpaQueryParsingToken("desc", false);

public static final JpaQueryParsingToken TOKEN_ASC = new JpaQueryParsingToken("asc", false);

public static final JpaQueryParsingToken TOKEN_WITH = new JpaQueryParsingToken("WITH");

public static final JpaQueryParsingToken TOKEN_NOT = new JpaQueryParsingToken("NOT");

public static final JpaQueryParsingToken TOKEN_MATERIALIZED = new JpaQueryParsingToken("materialized");

/**
* The text value of the token.
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1487,4 +1487,16 @@ select round(count(ri) * 100 / max(ri.receipt.positions), 0) as perc
""");
});
}

@Test // GH-2981
void cteWithClauseShouldWork() {

assertQuery("""
WITH maxId AS(select max(sr.snapshot.id) snapshotId from SnapshotReference sr
where sr.id.selectionId = ?1 and sr.enabled
group by sr.userId
)
select sr from maxId m join SnapshotReference sr on sr.snapshot.id = m.snapshotId
""");
}
}

0 comments on commit 3817e38

Please sign in to comment.