Skip to content

Commit a71df55

Browse files
committed
HHH-17231 Reintroduce support for entity path expansion in subqueries
1 parent 6edba01 commit a71df55

File tree

2 files changed

+39
-26
lines changed

2 files changed

+39
-26
lines changed

hibernate-core/src/main/java/org/hibernate/query/sqm/sql/internal/EntityValuedPathInterpretation.java

Lines changed: 32 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -257,18 +257,23 @@ public static <T> EntityValuedPathInterpretation<T> from(
257257
SqmToSqlAstConverter sqlAstCreationState) {
258258
final boolean expandToAllColumns;
259259
final Clause currentClause = sqlAstCreationState.getCurrentClauseStack().getCurrent();
260-
if ( sqlAstCreationState.getCurrentProcessingState().isTopLevel() &&
261-
( currentClause == Clause.GROUP || currentClause == Clause.ORDER ) ) {
262-
final SqmQuerySpec<?> querySpec = (SqmQuerySpec<?>) sqlAstCreationState.getCurrentSqmQueryPart();
260+
if ( currentClause == Clause.GROUP || currentClause == Clause.ORDER ) {
261+
assert sqlAstCreationState.getCurrentSqmQueryPart().isSimpleQueryPart();
262+
final SqmQuerySpec<?> querySpec = sqlAstCreationState.getCurrentSqmQueryPart().getFirstQuerySpec();
263263
if ( currentClause == Clause.ORDER && !querySpec.groupByClauseContains( navigablePath ) ) {
264264
// We must ensure that the order by expression be expanded but only if the group by
265265
// contained the same expression, and that was expanded as well
266266
expandToAllColumns = false;
267267
}
268268
else {
269269
// When the table group is selected and the navigablePath is selected we need to expand
270-
// to all columns, as we also expand this to all columns in the select clause
271-
expandToAllColumns = isSelected( tableGroup, navigablePath, querySpec );
270+
// to all columns, as we must make sure we include all columns present in the select clause
271+
expandToAllColumns = isSelected(
272+
tableGroup,
273+
navigablePath,
274+
querySpec,
275+
sqlAstCreationState.getCurrentProcessingState().isTopLevel()
276+
);
272277
}
273278
}
274279
else {
@@ -342,38 +347,43 @@ public static <T> EntityValuedPathInterpretation<T> from(
342347
);
343348
}
344349

345-
private static boolean isSelected(TableGroup tableGroup, NavigablePath path, SqmQuerySpec<?> sqmQuerySpec) {
346-
// If the table group is selected (initialized), check if the entity valued
347-
// navigable path or any child path appears in the select clause
348-
return tableGroup.isInitialized() && selectClauseContains( path, sqmQuerySpec );
349-
}
350-
351-
private static boolean selectClauseContains(NavigablePath path, SqmQuerySpec<?> sqmQuerySpec) {
352-
final List<SqmSelection<?>> selections = sqmQuerySpec.getSelectClause() == null
353-
? Collections.emptyList()
354-
: sqmQuerySpec.getSelectClause().getSelections();
355-
for ( SqmSelection<?> selection : selections ) {
356-
if ( selectionContains( selection.getSelectableNode(), path ) ) {
350+
private static boolean isSelected(
351+
TableGroup tableGroup,
352+
NavigablePath path,
353+
SqmQuerySpec<?> sqmQuerySpec,
354+
boolean isTopLevel) {
355+
// If the table group is not initialized, i.e. not selected, no need to check selections
356+
if ( !tableGroup.isInitialized() || sqmQuerySpec.getSelectClause() == null ) {
357+
return false;
358+
}
359+
final NavigablePath tableGroupPath = isTopLevel ? null : tableGroup.getNavigablePath();
360+
for ( SqmSelection<?> selection : sqmQuerySpec.getSelectClause().getSelections() ) {
361+
if ( selectionContains( selection.getSelectableNode(), path, tableGroupPath ) ) {
357362
return true;
358363
}
359364
}
360365
return false;
361366
}
362367

363-
private static boolean selectionContains(Selection<?> selection, NavigablePath path) {
364-
if ( selection instanceof SqmPath && path.isParentOrEqual( ( (SqmPath<?>) selection ).getNavigablePath() ) ) {
365-
return true;
368+
private static boolean selectionContains(Selection<?> selection, NavigablePath path, NavigablePath tableGroupPath) {
369+
if ( selection instanceof SqmPath<?> ) {
370+
final SqmPath<?> sqmPath = (SqmPath<?>) selection;
371+
// Expansion is needed if the table group is null, i.e. we're in a top level query where EVPs are always
372+
// expanded to all columns, or if the selection is on the same table (lhs) as the group by expression ...
373+
return ( tableGroupPath == null || sqmPath.getLhs().getNavigablePath().equals( tableGroupPath ) )
374+
// ... and if the entity valued path is selected or any of its columns are
375+
&& path.isParentOrEqual( sqmPath.getNavigablePath() );
366376
}
367377
else if ( selection.isCompoundSelection() ) {
368378
for ( Selection<?> compoundSelection : selection.getCompoundSelectionItems() ) {
369-
if ( selectionContains( compoundSelection, path ) ) {
379+
if ( selectionContains( compoundSelection, path, tableGroupPath ) ) {
370380
return true;
371381
}
372382
}
373383
}
374384
else if ( selection instanceof SqmDynamicInstantiation ) {
375385
for ( SqmDynamicInstantiationArgument<?> argument : ( (SqmDynamicInstantiation<?>) selection ).getArguments() ) {
376-
if ( selectionContains( argument.getSelectableNode(), path ) ) {
386+
if ( selectionContains( argument.getSelectableNode(), path, tableGroupPath ) ) {
377387
return true;
378388
}
379389
}

hibernate-core/src/main/java/org/hibernate/sql/ast/spi/AbstractSqlAstTranslator.java

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7072,12 +7072,13 @@ protected <X extends Expression> void emulateSubQueryRelationalRestrictionPredic
70727072
this.queryPartForRowNumberingClauseDepth = -1;
70737073
this.needsSelectAliases = false;
70747074
queryPartStack.push( subQuery );
7075-
appendSql( "exists (select 1" );
7076-
visitFromClause( subQuery.getFromClause() );
7077-
7075+
appendSql( "exists (" );
70787076
if ( !subQuery.getGroupByClauseExpressions().isEmpty()
70797077
|| subQuery.getHavingClauseRestrictions() != null ) {
7080-
// If we have a group by or having clause, we have to move the tuple comparison emulation to the HAVING clause
7078+
// If we have a group by or having clause, we have to move the tuple comparison emulation to the HAVING clause.
7079+
// Also, we need to explicitly include the selections to avoid 'invalid HAVING clause' errors
7080+
visitSelectClause( subQuery.getSelectClause() );
7081+
visitFromClause( subQuery.getFromClause() );
70817082
visitWhereClause( subQuery.getWhereClauseRestrictions() );
70827083
visitGroupByClause( subQuery, SelectItemReferenceStrategy.EXPRESSION );
70837084

@@ -7102,6 +7103,8 @@ protected <X extends Expression> void emulateSubQueryRelationalRestrictionPredic
71027103
}
71037104
else {
71047105
// If we have no group by or having clause, we can move the tuple comparison emulation to the WHERE clause
7106+
appendSql( "select 1" );
7107+
visitFromClause( subQuery.getFromClause() );
71057108
appendSql( " where " );
71067109
clauseStack.push( Clause.WHERE );
71077110
try {

0 commit comments

Comments
 (0)