jamesfredley commented on code in PR #16559:
URL: https://github.com/apache/grails-core/pull/16559#discussion_r4340866366
##########
grails-data-hibernate7/core/src/main/groovy/org/grails/orm/hibernate/query/PredicateGenerator.java:
##########
@@ -305,20 +305,31 @@ private Predicate handleAssociationCriteria(
JpaQueryContext fromsByProvider,
DetachedAssociationCriteria<?> associationCriteria) {
String associationName = associationCriteria.getAssociationPath();
- From<?, ?> associationRoot = fromsByProvider.getFrom(associationName);
+ // The block joins from the entity its context queries, with the join
type recorded for it there. A join that
+ // an outer block or an outer query made for an association of the
same name, such as face in
+ // face { ... } owner { face { ... } } or owner in a subquery, is
another table, so only the joins of this
+ // context are reused.
+ From<?, ?> associationRoot =
fromsByProvider.getLocalFrom(associationName);
if (associationRoot == null) {
- // Check if we already have it in our parent or alias map
- Expression<?> expr =
fromsByProvider.getFullyQualifiedExpression(associationName);
- if (expr instanceof From<?, ?> from) {
- associationRoot = from;
- } else {
- associationRoot =
fromsByProvider.getRoot().join(associationName);
- fromsByProvider.addFrom(associationName, associationRoot);
- }
+ HibernateAlias definition =
fromsByProvider.getLocalAliasDefinition(associationName);
+ associationRoot = definition != null ?
+ fromsByProvider.getRoot().join(definition.path(),
definition.joinType()) :
+ fromsByProvider.getRoot().join(associationName,
fromsByProvider.getLocalJoinType(associationName));
+ fromsByProvider.addFrom(associationName, associationRoot);
+ // A later path through the association, such as face.name in
+ // eq('face.name', 'Santa') owner { face { ... } eq('face.name',
'Homer') }, is resolved against the aliases
+ // realized by the outer contexts before the joins of this one, so
the join is realized as an alias of this
+ // context too.
+ fromsByProvider.registerAlias(associationName, associationRoot);
Review Comment:
This makes the association name visible to nested queries.
`AliasRegistry.getRealized` (lines 74-78) returns the parent registry's
realized join when the name is absent locally.
`ExpressionResolver.resolveFromAlias` uses that lookup.
```groovy
DetachedCriteria.for(Pet).build {
owner { eq('active', true) }
in 'id', DetachedCriteria.for(Pet).build {
projections { property 'owner.firstName' }
}
}
```
The subquery should project its own owner's first name. After this change
`owner` resolves to the outer join, so the subquery is correlated and the rows
change with the order of the criteria.
Keep an automatically realized association path local to the context that
created it. Do not publish it as a correlation alias. An explicit alias that a
subquery is meant to see can stay on the parent lookup. Please add a regression
with an association block and an independent subquery projection of the same
path.
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: [email protected]
For queries about this service, please contact Infrastructure at:
[email protected]