Skip to content

Commit 17b8d2e

Browse files
beikovsebersole
authored andcommitted
HHH-11538 - Skip generating joins for entity references used in equality and nullness predicates
1 parent ce32b36 commit 17b8d2e

File tree

10 files changed

+94
-70
lines changed

10 files changed

+94
-70
lines changed

hibernate-core/src/main/antlr/hql-sql.g

Lines changed: 61 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -209,6 +209,8 @@ tokens
209209
210210
protected void resolve(AST node) throws SemanticException { }
211211
212+
protected void resolve(AST node, AST predicateNode) throws SemanticException { }
213+
212214
protected void resolveSelectExpression(AST dotNode) throws SemanticException { }
213215
214216
protected void processFunction(AST functionCall,boolean inSelect) throws SemanticException { }
@@ -335,7 +337,7 @@ assignment
335337
336338
// For now, just use expr. Revisit after ejb3 solidifies this.
337339
newValue
338-
: expr | query
340+
: expr [ null ] | query
339341
;
340342
341343
// The query / subquery rule. Pops the current 'from node' context
@@ -380,7 +382,7 @@ nullPrecedence
380382
381383
orderExpr
382384
: { isOrderExpressionResultVariableRef( _t ) }? resultVariableRef
383-
| expr
385+
| expr [ null ]
384386
;
385387
386388
resultVariableRef!
@@ -392,7 +394,7 @@ resultVariableRef!
392394
;
393395
394396
groupClause
395-
: #(GROUP { handleClauseStart( GROUP ); } (expr)+ ( #(HAVING logicalExpr) )? ) {
397+
: #(GROUP { handleClauseStart( GROUP ); } (expr [ null ])+ ( #(HAVING logicalExpr) )? ) {
396398
handleClauseEnd();
397399
}
398400
;
@@ -429,7 +431,7 @@ selectExpr
429431
| count
430432
| collectionFunction // elements() or indices()
431433
| constant
432-
| arithmeticExpr
434+
| arithmeticExpr [ null ]
433435
| logicalExpr
434436
| parameter
435437
| query
@@ -448,7 +450,7 @@ constructor
448450
;
449451
450452
aggregateExpr
451-
: expr //p:propertyRef { resolve(#p); }
453+
: expr [ null ] //p:propertyRef { resolve(#p); }
452454
| collectionFunction
453455
;
454456
@@ -572,36 +574,37 @@ logicalExpr
572574
;
573575
574576
// TODO: Add any other comparison operators here.
577+
// We pass through the comparisonExpr AST to the expressions so that joins can be avoided for EQ/IN/NULLNESS
575578
comparisonExpr
576579
:
577-
( #(EQ exprOrSubquery exprOrSubquery)
578-
| #(NE exprOrSubquery exprOrSubquery)
579-
| #(LT exprOrSubquery exprOrSubquery)
580-
| #(GT exprOrSubquery exprOrSubquery)
581-
| #(LE exprOrSubquery exprOrSubquery)
582-
| #(GE exprOrSubquery exprOrSubquery)
583-
| #(LIKE exprOrSubquery expr ( #(ESCAPE expr) )? )
584-
| #(NOT_LIKE exprOrSubquery expr ( #(ESCAPE expr) )? )
585-
| #(BETWEEN exprOrSubquery exprOrSubquery exprOrSubquery)
586-
| #(NOT_BETWEEN exprOrSubquery exprOrSubquery exprOrSubquery)
587-
| #(IN exprOrSubquery inRhs )
588-
| #(NOT_IN exprOrSubquery inRhs )
589-
| #(IS_NULL exprOrSubquery)
590-
| #(IS_NOT_NULL exprOrSubquery)
591-
// | #(IS_TRUE expr)
592-
// | #(IS_FALSE expr)
593-
| #(EXISTS ( expr | collectionFunctionOrSubselect ) )
580+
( #(EQ exprOrSubquery [ currentAST.root ] exprOrSubquery [ currentAST.root ])
581+
| #(NE exprOrSubquery [ currentAST.root ] exprOrSubquery [ currentAST.root ])
582+
| #(LT exprOrSubquery [ null ] exprOrSubquery [ null ])
583+
| #(GT exprOrSubquery [ null ] exprOrSubquery [ null ])
584+
| #(LE exprOrSubquery [ null ] exprOrSubquery [ null ])
585+
| #(GE exprOrSubquery [ null ] exprOrSubquery [ null ])
586+
| #(LIKE exprOrSubquery [ null ] expr [ null ] ( #(ESCAPE expr [ null ]) )? )
587+
| #(NOT_LIKE exprOrSubquery [ null ] expr [ null ] ( #(ESCAPE expr [ null ]) )? )
588+
| #(BETWEEN exprOrSubquery [ null ] exprOrSubquery [ null ] exprOrSubquery [ null ])
589+
| #(NOT_BETWEEN exprOrSubquery [ null ] exprOrSubquery [ null ] exprOrSubquery [ null ])
590+
| #(IN exprOrSubquery [ currentAST.root ] inRhs [ currentAST.root ] )
591+
| #(NOT_IN exprOrSubquery [ currentAST.root ] inRhs [ currentAST.root ] )
592+
| #(IS_NULL exprOrSubquery [ currentAST.root ])
593+
| #(IS_NOT_NULL exprOrSubquery [ currentAST.root ])
594+
// | #(IS_TRUE expr [ null ])
595+
// | #(IS_FALSE expr [ null ])
596+
| #(EXISTS ( expr [ null ] | collectionFunctionOrSubselect ) )
594597
) {
595598
prepareLogicOperator( #comparisonExpr );
596599
}
597600
;
598601
599-
inRhs
600-
: #(IN_LIST ( collectionFunctionOrSubselect | ( (expr)* ) ) )
602+
inRhs [ AST predicateNode ]
603+
: #(IN_LIST ( collectionFunctionOrSubselect | ( (expr [ predicateNode ])* ) ) )
601604
;
602605
603-
exprOrSubquery
604-
: expr
606+
exprOrSubquery [ AST predicateNode ]
607+
: expr [ predicateNode ]
605608
| query
606609
| #(ANY collectionFunctionOrSubselect)
607610
| #(ALL collectionFunctionOrSubselect)
@@ -613,55 +616,55 @@ collectionFunctionOrSubselect
613616
| query
614617
;
615618
616-
expr
617-
: ae:addrExpr [ true ] { resolve(#ae); } // Resolve the top level 'address expression'
618-
| #( VECTOR_EXPR (expr)* )
619+
expr [ AST predicateNode ]
620+
: ae:addrExpr [ true ] { resolve(#ae, predicateNode); } // Resolve the top level 'address expression'
621+
| #( VECTOR_EXPR (expr [ predicateNode ])* )
619622
| constant
620-
| arithmeticExpr
623+
| arithmeticExpr [ predicateNode ]
621624
| functionCall // Function call, not in the SELECT clause.
622625
| parameter
623626
| count // Count, not in the SELECT clause.
624627
;
625628
626-
arithmeticExpr
627-
: #(PLUS exprOrSubquery exprOrSubquery) { prepareArithmeticOperator( #arithmeticExpr ); }
628-
| #(MINUS exprOrSubquery exprOrSubquery) { prepareArithmeticOperator( #arithmeticExpr ); }
629-
| #(DIV exprOrSubquery exprOrSubquery) { prepareArithmeticOperator( #arithmeticExpr ); }
630-
| #(MOD exprOrSubquery exprOrSubquery) { prepareArithmeticOperator( #arithmeticExpr ); }
631-
| #(STAR exprOrSubquery exprOrSubquery) { prepareArithmeticOperator( #arithmeticExpr ); }
632-
// | #(CONCAT expr (expr)+ ) { prepareArithmeticOperator( #arithmeticExpr ); }
633-
| #(UNARY_MINUS expr) { prepareArithmeticOperator( #arithmeticExpr ); }
634-
| caseExpr
629+
arithmeticExpr [ AST predicateNode ]
630+
: #(PLUS exprOrSubquery [ null ] exprOrSubquery [ null ]) { prepareArithmeticOperator( #arithmeticExpr ); }
631+
| #(MINUS exprOrSubquery [ null ] exprOrSubquery [ null ]) { prepareArithmeticOperator( #arithmeticExpr ); }
632+
| #(DIV exprOrSubquery [ null ] exprOrSubquery [ null ]) { prepareArithmeticOperator( #arithmeticExpr ); }
633+
| #(MOD exprOrSubquery [ null ] exprOrSubquery [ null ]) { prepareArithmeticOperator( #arithmeticExpr ); }
634+
| #(STAR exprOrSubquery [ null ] exprOrSubquery [ null ]) { prepareArithmeticOperator( #arithmeticExpr ); }
635+
// | #(CONCAT expr [ null ] (expr [ null ])+ ) { prepareArithmeticOperator( #arithmeticExpr ); }
636+
| #(UNARY_MINUS expr [ null ]) { prepareArithmeticOperator( #arithmeticExpr ); }
637+
| caseExpr [ predicateNode ]
635638
;
636639
637-
caseExpr
638-
: simpleCaseExpression
639-
| searchedCaseExpression
640+
caseExpr [ AST predicateNode ]
641+
: simpleCaseExpression [ predicateNode ]
642+
| searchedCaseExpression [ predicateNode ]
640643
;
641644
642-
expressionOrSubQuery
643-
: expr
645+
expressionOrSubQuery [ AST predicateNode ]
646+
: expr [ predicateNode ]
644647
| query
645648
;
646649
647-
simpleCaseExpression
648-
: #(CASE2 {inCase=true;} expressionOrSubQuery (simpleCaseWhenClause)+ (elseClause)?) {inCase=false;}
650+
simpleCaseExpression [ AST predicateNode ]
651+
: #(CASE2 {inCase=true;} expressionOrSubQuery [ currentAST.root ] (simpleCaseWhenClause [ currentAST.root, predicateNode ])+ (elseClause [ predicateNode ])?) {inCase=false;}
649652
;
650653
651-
simpleCaseWhenClause
652-
: #(WHEN expressionOrSubQuery expressionOrSubQuery)
654+
simpleCaseWhenClause [ AST predicateNode, AST superPredicateNode ]
655+
: #(WHEN expressionOrSubQuery [ predicateNode ] expressionOrSubQuery [ superPredicateNode ])
653656
;
654657
655-
elseClause
656-
: #(ELSE expressionOrSubQuery)
658+
elseClause [ AST predicateNode ]
659+
: #(ELSE expressionOrSubQuery [ predicateNode ])
657660
;
658661
659-
searchedCaseExpression
660-
: #(CASE {inCase = true;} (searchedCaseWhenClause)+ (elseClause)?) {inCase = false;}
662+
searchedCaseExpression [ AST predicateNode ]
663+
: #(CASE {inCase = true;} (searchedCaseWhenClause [ predicateNode ])+ (elseClause [ predicateNode ])?) {inCase = false;}
661664
;
662665
663-
searchedCaseWhenClause
664-
: #(WHEN logicalExpr expressionOrSubQuery)
666+
searchedCaseWhenClause [ AST predicateNode ]
667+
: #(WHEN logicalExpr expressionOrSubQuery [ predicateNode ])
665668
;
666669
667670
@@ -675,11 +678,11 @@ collectionFunction
675678
;
676679

677680
functionCall
678-
: #(METHOD_CALL {inFunctionCall=true;} pathAsIdent ( #(EXPR_LIST (exprOrSubquery)* ) )? ) {
681+
: #(METHOD_CALL {inFunctionCall=true;} pathAsIdent ( #(EXPR_LIST (exprOrSubquery [ null ])* ) )? ) {
679682
processFunction( #functionCall, inSelect );
680683
inFunctionCall=false;
681684
}
682-
| #(CAST {inFunctionCall=true;} exprOrSubquery pathAsIdent) {
685+
| #(CAST {inFunctionCall=true;} exprOrSubquery [ null ] pathAsIdent) {
683686
processCastFunction( #functionCall, inSelect );
684687
inFunctionCall=false;
685688
}
@@ -715,7 +718,7 @@ addrExpr! [ boolean root ]
715718
#addrExpr = #(#d, #lhs, #rhs);
716719
#addrExpr = lookupProperty(#addrExpr,root,false);
717720
}
718-
| #(i:INDEX_OP lhs2:addrExprLhs rhs2:expr) {
721+
| #(i:INDEX_OP lhs2:addrExprLhs rhs2:expr [ null ]) {
719722
#addrExpr = #(#i, #lhs2, #rhs2);
720723
processIndex(#addrExpr);
721724
}
@@ -794,7 +797,7 @@ mapComponentReference
794797
;
795798

796799
mapPropertyExpression
797-
: e:expr {
800+
: e:expr [ null ] {
798801
validateMapPropertyExpression( #e );
799802
}
800803
;

hibernate-core/src/main/java/org/hibernate/hql/internal/ast/HqlSqlWalker.java

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1023,14 +1023,19 @@ private void createSelectClauseFromFromClause(QueryNode qn) throws SemanticExcep
10231023

10241024
@Override
10251025
protected void resolve(AST node) throws SemanticException {
1026+
resolve(node, null);
1027+
}
1028+
1029+
@Override
1030+
protected void resolve(AST node, AST predicateNode) throws SemanticException {
10261031
if ( node != null ) {
10271032
// This is called when it's time to fully resolve a path expression.
10281033
ResolvableNode r = (ResolvableNode) node;
10291034
if ( isInFunctionCall() ) {
10301035
r.resolveInFunctionCall( false, true );
10311036
}
10321037
else {
1033-
r.resolve( false, true ); // Generate implicit joins, only if necessary.
1038+
r.resolve( false, true, null, null, predicateNode ); // Generate implicit joins, only if necessary.
10341039
}
10351040
}
10361041
}

hibernate-core/src/main/java/org/hibernate/hql/internal/ast/tree/AbstractMapComponentNode.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,8 @@ public void resolve(
4545
boolean generateJoin,
4646
boolean implicitJoin,
4747
String classAlias,
48-
AST parent) throws SemanticException {
48+
AST parent,
49+
AST parentPredicate) throws SemanticException {
4950
if ( mapFromElement == null ) {
5051
final FromReferenceNode mapReference = getMapReference();
5152
mapReference.resolve( true, true );

hibernate-core/src/main/java/org/hibernate/hql/internal/ast/tree/DotNode.java

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -194,7 +194,7 @@ public void resolveIndex(AST parent) throws SemanticException {
194194
dereferenceCollection( (CollectionType) propertyType, true, true, null, parent );
195195
}
196196

197-
public void resolve(boolean generateJoin, boolean implicitJoin, String classAlias, AST parent)
197+
public void resolve(boolean generateJoin, boolean implicitJoin, String classAlias, AST parent, AST parentPredicate)
198198
throws SemanticException {
199199
// If this dot has already been resolved, stop now.
200200
if ( isResolved() ) {
@@ -227,7 +227,7 @@ public void resolve(boolean generateJoin, boolean implicitJoin, String classAlia
227227
else if ( propertyType.isEntityType() ) {
228228
// The property is another class..
229229
checkLhsIsNotCollection();
230-
dereferenceEntity( (EntityType) propertyType, implicitJoin, classAlias, generateJoin, parent );
230+
dereferenceEntity( (EntityType) propertyType, implicitJoin, classAlias, generateJoin, parent, parentPredicate );
231231
initText();
232232
}
233233
else if ( propertyType.isCollectionType() ) {
@@ -361,7 +361,8 @@ private void dereferenceEntity(
361361
boolean implicitJoin,
362362
String classAlias,
363363
boolean generateJoin,
364-
AST parent) throws SemanticException {
364+
AST parent,
365+
AST parentPredicate) throws SemanticException {
365366
checkForCorrelatedSubquery( "dereferenceEntity" );
366367
// three general cases we check here as to whether to render a physical SQL join:
367368
// 1) is our parent a DotNode as well? If so, our property reference is
@@ -403,6 +404,10 @@ else if ( regressionStyleJoinSuppression ) {
403404
// this is the regression style determination which matches the logic of the classic translator
404405
joinIsNeeded = generateJoin && ( !getWalker().isInSelect() || !getWalker().isShallowQuery() );
405406
}
407+
else if ( parentPredicate != null ) {
408+
// Never generate a join when we compare entities directly
409+
joinIsNeeded = generateJoin;
410+
}
406411
else {
407412
joinIsNeeded = generateJoin || ( getWalker().isInSelect() || getWalker().isInFrom() );
408413
}

hibernate-core/src/main/java/org/hibernate/hql/internal/ast/tree/FromReferenceNode.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,11 @@ public void resolve(boolean generateJoin, boolean implicitJoin, String classAlia
109109
resolve( generateJoin, implicitJoin, classAlias, null );
110110
}
111111

112+
public void resolve(boolean generateJoin, boolean implicitJoin, String classAlias, AST parent)
113+
throws SemanticException {
114+
resolve( generateJoin, implicitJoin, classAlias, parent, null );
115+
}
116+
112117
public void prepareForDot(String propertyName) throws SemanticException {
113118
}
114119

hibernate-core/src/main/java/org/hibernate/hql/internal/ast/tree/IdentNode.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -102,7 +102,7 @@ private void initText(String[] columns) {
102102
setText( text );
103103
}
104104

105-
public void resolve(boolean generateJoin, boolean implicitJoin, String classAlias, AST parent) {
105+
public void resolve(boolean generateJoin, boolean implicitJoin, String classAlias, AST parent, AST parentPredicate) {
106106
if (!isResolved()) {
107107
if ( getWalker().getCurrentFromClause().isFromElementAlias( getText() ) ) {
108108
FromElement fromElement = getWalker().getCurrentFromClause().getFromElement( getText() );

hibernate-core/src/main/java/org/hibernate/hql/internal/ast/tree/IndexNode.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@ public void resolveIndex(AST parent) throws SemanticException {
6565
}
6666

6767
@Override
68-
public void resolve(boolean generateJoin, boolean implicitJoin, String classAlias, AST parent)
68+
public void resolve(boolean generateJoin, boolean implicitJoin, String classAlias, AST parent, AST parentPredicate)
6969
throws SemanticException {
7070
if ( isResolved() ) {
7171
return;

hibernate-core/src/main/java/org/hibernate/hql/internal/ast/tree/MapEntryNode.java

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -65,11 +65,12 @@ public void resolve(
6565
boolean generateJoin,
6666
boolean implicitJoin,
6767
String classAlias,
68-
AST parent) throws SemanticException {
68+
AST parent,
69+
AST parentPredicate) throws SemanticException {
6970
if (parent != null) {
7071
throw new SemanticException( expressionDescription() + " expression cannot be further de-referenced" );
7172
}
72-
super.resolve(generateJoin, implicitJoin, classAlias, parent);
73+
super.resolve(generateJoin, implicitJoin, classAlias, parent, parentPredicate);
7374
}
7475

7576
@Override

hibernate-core/src/main/java/org/hibernate/hql/internal/ast/tree/ResolvableNode.java

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,15 +17,19 @@ public interface ResolvableNode {
1717
/**
1818
* Does the work of resolving an identifier or a dot
1919
*/
20+
void resolve(boolean generateJoin, boolean implicitJoin, String classAlias, AST parent, AST parentPredicate) throws SemanticException;
21+
/**
22+
* Does the work of resolving an identifier or a dot, but without a parent predicate node
23+
*/
2024
void resolve(boolean generateJoin, boolean implicitJoin, String classAlias, AST parent) throws SemanticException;
2125

2226
/**
23-
* Does the work of resolving an identifier or a dot, but without a parent node
27+
* Does the work of resolving an identifier or a dot, but without a parent predicate node or parent node
2428
*/
2529
void resolve(boolean generateJoin, boolean implicitJoin, String classAlias) throws SemanticException;
2630

2731
/**
28-
* Does the work of resolving an identifier or a dot, but without a parent node or alias
32+
* Does the work of resolving an identifier or a dot, but without a parent predicate node or parent node or alias
2933
*/
3034
void resolve(boolean generateJoin, boolean implicitJoin) throws SemanticException;
3135

hibernate-core/src/main/java/org/hibernate/hql/internal/ast/tree/SelectExpressionImpl.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ public void setScalarColumnText(int i) throws SemanticException {
2424
setText( text );
2525
}
2626

27-
public void resolve(boolean generateJoin, boolean implicitJoin, String classAlias, AST parent) throws SemanticException {
27+
public void resolve(boolean generateJoin, boolean implicitJoin, String classAlias, AST parent, AST parentPredicate) throws SemanticException {
2828
// Generated select expressions are already resolved, nothing to do.
2929
return;
3030
}

0 commit comments

Comments
 (0)