HHH-17898 Throw error if non-lateral from-clause subquery uses outer from node

This commit is contained in:
Christian Beikov 2024-03-28 09:59:51 +01:00
parent d6bc041dff
commit 7ecbc07d4d
2 changed files with 64 additions and 0 deletions

View File

@ -2593,6 +2593,9 @@ public abstract class BaseSqmToSqlAstConverter<T extends Statement> extends Base
final TableGroup parentTableGroup = fromClauseIndex.findTableGroup(
from.getCorrelationParent().getNavigablePath()
);
if ( parentTableGroup == null ) {
throw new InterpretationException( "Access to from node '" + from.getCorrelationParent() + "' is not possible in from-clause subqueries, unless the 'lateral' keyword is used for the subquery!" );
}
final SqlAliasBase sqlAliasBase = sqlAliasBaseManager.createSqlAliasBase( parentTableGroup.getGroupAlias() );
if ( parentTableGroup instanceof PluralTableGroup ) {
final PluralTableGroup pluralTableGroup = (PluralTableGroup) parentTableGroup;
@ -2744,7 +2747,11 @@ public abstract class BaseSqmToSqlAstConverter<T extends Statement> extends Base
final TableGroup tableGroup;
if ( sqmRoot instanceof SqmDerivedRoot<?> ) {
final SqmDerivedRoot<?> derivedRoot = (SqmDerivedRoot<?>) sqmRoot;
// Temporarily push an empty FromClauseIndex to disallow access to aliases from the top query
// Only lateral subqueries are allowed to see the aliases
fromClauseIndexStack.push( new FromClauseIndex( null ) );
final SelectStatement statement = (SelectStatement) derivedRoot.getQueryPart().accept( this );
fromClauseIndexStack.pop();
final AnonymousTupleType<?> tupleType = (AnonymousTupleType<?>) sqmRoot.getNodeType();
final List<SqlSelection> sqlSelections = statement.getQueryPart().getFirstQuerySpec().getSelectClause().getSqlSelections();
final AnonymousTupleTableGroupProducer tableGroupProducer = tupleType.resolveTableGroupProducer(
@ -3485,7 +3492,15 @@ public abstract class BaseSqmToSqlAstConverter<T extends Statement> extends Base
}
private TableGroup consumeDerivedJoin(SqmDerivedJoin<?> sqmJoin, TableGroup parentTableGroup, boolean transitive) {
if ( !sqmJoin.isLateral() ) {
// Temporarily push an empty FromClauseIndex to disallow access to aliases from the top query
// Only lateral subqueries are allowed to see the aliases
fromClauseIndexStack.push( new FromClauseIndex( null ) );
}
final SelectStatement statement = (SelectStatement) sqmJoin.getQueryPart().accept( this );
if ( !sqmJoin.isLateral() ) {
fromClauseIndexStack.pop();
}
final AnonymousTupleType<?> tupleType = (AnonymousTupleType<?>) sqmJoin.getNodeType();
final List<SqlSelection> sqlSelections = statement.getQueryPart().getFirstQuerySpec().getSelectClause().getSqlSelections();
final AnonymousTupleTableGroupProducer tableGroupProducer = tupleType.resolveTableGroupProducer(

View File

@ -17,9 +17,11 @@ import org.hibernate.query.criteria.JpaDerivedRoot;
import org.hibernate.query.criteria.JpaRoot;
import org.hibernate.query.criteria.JpaSubQuery;
import org.hibernate.query.spi.QueryImplementor;
import org.hibernate.query.sqm.InterpretationException;
import org.hibernate.query.sqm.tree.SqmJoinType;
import org.hibernate.query.sqm.tree.from.SqmAttributeJoin;
import org.hibernate.testing.orm.junit.Jira;
import org.hibernate.testing.orm.junit.RequiresDialectFeature;
import org.hibernate.testing.orm.domain.StandardDomainModel;
import org.hibernate.testing.orm.domain.contacts.Address;
@ -35,7 +37,9 @@ import org.junit.jupiter.api.Test;
import jakarta.persistence.Tuple;
import jakarta.persistence.criteria.Join;
import jakarta.persistence.criteria.Root;
import org.assertj.core.api.Assertions;
import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.jupiter.api.Assertions.assertEquals;
/**
@ -81,6 +85,51 @@ public class SubQueryInFromTests {
);
}
@Test
@Jira("https://hibernate.atlassian.net/browse/HHH-17898")
public void testJoinSubqueryUsingInvalidAlias1(SessionFactoryScope scope) {
scope.inTransaction(
session -> {
try {
session.createQuery(
"select c.name, a.name from Contact c " +
"join (" +
"select c2.name as name " +
"from Contact c2 " +
"where c2 = c" +
") a",
Tuple.class
).getResultList();
}
catch (InterpretationException ex) {
assertThat( ex.getMessage() ).contains( "lateral" );
}
}
);
}
@Test
@Jira("https://hibernate.atlassian.net/browse/HHH-17898")
public void testJoinSubqueryUsingInvalidAlias2(SessionFactoryScope scope) {
scope.inTransaction(
session -> {
try {
session.createQuery(
"select c.name, a.address from Contact c " +
"join (" +
"select address.line1 as address " +
"from c.addresses address " +
") a",
Tuple.class
).getResultList();
}
catch (InterpretationException ex) {
assertThat( ex.getMessage() ).contains( "lateral" );
}
}
);
}
@Test
@RequiresDialectFeature(feature = DialectFeatureChecks.SupportsSubqueryInOnClause.class)
@RequiresDialectFeature(feature = DialectFeatureChecks.SupportsOrderByInCorrelatedSubquery.class)