From a1a85e2517c19daefafa3280e6423a71fdfc9435 Mon Sep 17 00:00:00 2001 From: gavinking Date: Mon, 17 Feb 2020 01:15:34 +0100 Subject: [PATCH] Fixes for subqueries in HQL select clause Allow subqueries to occur in the select list, and allow their aliases to occur in the order by clause. --- .../sqm/sql/BaseSqmToSqlAstConverter.java | 4 +- .../sql/ast/tree/expression/QueryLiteral.java | 9 ++++ .../sql/ast/tree/select/QuerySpec.java | 48 ++++++++++++++++++- .../test/query/hql/SubqueryOperatorsTest.java | 27 +++++++++++ 4 files changed, 85 insertions(+), 3 deletions(-) diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/sql/BaseSqmToSqlAstConverter.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/sql/BaseSqmToSqlAstConverter.java index 76f8988d41..b37f395374 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/sql/BaseSqmToSqlAstConverter.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/sql/BaseSqmToSqlAstConverter.java @@ -525,7 +525,9 @@ public Void visitFromClause(SqmFromClause sqmFromClause) { protected void consumeFromClauseRoot(SqmRoot sqmRoot) { log.tracef( "Resolving SqmRoot [%s] to TableGroup", sqmRoot ); - assert ! fromClauseIndex.isResolved( sqmRoot ); + if ( fromClauseIndex.isResolved( sqmRoot ) ) { + log.tracef( "Already resolved SqmRoot [%s] to TableGroup", sqmRoot ); + } final EntityPersister entityDescriptor = resolveEntityPersister( sqmRoot.getReferencedPathSource() ); diff --git a/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/expression/QueryLiteral.java b/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/expression/QueryLiteral.java index 75f5959606..bb711c55b9 100644 --- a/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/expression/QueryLiteral.java +++ b/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/expression/QueryLiteral.java @@ -103,4 +103,13 @@ public void bindParameterValue( executionContext.getSession() ); } + + @Override + public void applySqlSelections(DomainResultCreationState creationState) { + creationState.getSqlAstCreationState().getSqlExpressionResolver().resolveSqlSelection( + this, + type.getBasicType().getJavaTypeDescriptor(), + creationState.getSqlAstCreationState().getCreationContext().getDomainModel().getTypeConfiguration() + ); + } } diff --git a/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/select/QuerySpec.java b/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/select/QuerySpec.java index 204ee89194..c02b407501 100644 --- a/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/select/QuerySpec.java +++ b/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/select/QuerySpec.java @@ -11,19 +11,28 @@ import java.util.function.Consumer; import org.hibernate.metamodel.mapping.MappingModelExpressable; +import org.hibernate.query.sqm.sql.internal.DomainResultProducer; import org.hibernate.sql.ast.spi.SqlAstTreeHelper; import org.hibernate.sql.ast.SqlAstWalker; +import org.hibernate.sql.ast.spi.SqlExpressionResolver; +import org.hibernate.sql.ast.spi.SqlSelection; import org.hibernate.sql.ast.tree.SqlAstNode; import org.hibernate.sql.ast.tree.cte.CteConsumer; import org.hibernate.sql.ast.tree.expression.Expression; import org.hibernate.sql.ast.tree.from.FromClause; import org.hibernate.sql.ast.tree.predicate.Predicate; import org.hibernate.sql.ast.tree.predicate.PredicateContainer; +import org.hibernate.sql.results.graph.DomainResult; +import org.hibernate.sql.results.graph.DomainResultCreationState; +import org.hibernate.sql.results.graph.basic.BasicResult; +import org.hibernate.sql.results.internal.SqlSelectionImpl; +import org.hibernate.type.descriptor.java.JavaTypeDescriptor; +import org.hibernate.type.spi.TypeConfiguration; /** * @author Steve Ebersole */ -public class QuerySpec implements SqlAstNode, PredicateContainer, Expression, CteConsumer { +public class QuerySpec implements SqlAstNode, PredicateContainer, Expression, CteConsumer, DomainResultProducer { private final boolean isRoot; private final FromClause fromClause; @@ -112,6 +121,41 @@ public void accept(SqlAstWalker sqlTreeWalker) { @Override public MappingModelExpressable getExpressionType() { - return null; + SqlSelection first = selectClause.getSqlSelections().get(0); + return ( (SqlSelectionImpl) first ).getWrappedSqlExpression().getExpressionType(); + } + + @Override + public void applySqlSelections(DomainResultCreationState creationState) { + SqlSelection first = selectClause.getSqlSelections().get(0); + TypeConfiguration typeConfiguration = creationState.getSqlAstCreationState().getCreationContext().getDomainModel().getTypeConfiguration(); + JavaTypeDescriptor descriptor = ( (SqlSelectionImpl) first ).getWrappedSqlExpression().getExpressionType() + .getJdbcMappings( typeConfiguration ).get(0).getJavaTypeDescriptor(); + creationState.getSqlAstCreationState().getSqlExpressionResolver().resolveSqlSelection( + this, + descriptor, + typeConfiguration + ); + } + + @Override + public DomainResult createDomainResult(String resultVariable, DomainResultCreationState creationState) { + SqlSelection first = selectClause.getSqlSelections().get(0); + TypeConfiguration typeConfiguration = creationState.getSqlAstCreationState().getCreationContext().getDomainModel().getTypeConfiguration(); + JavaTypeDescriptor descriptor = ( (SqlSelectionImpl) first ).getWrappedSqlExpression().getExpressionType() + .getJdbcMappings( typeConfiguration ).get(0).getJavaTypeDescriptor(); + + final SqlExpressionResolver sqlExpressionResolver = creationState.getSqlAstCreationState().getSqlExpressionResolver(); + final SqlSelection sqlSelection = sqlExpressionResolver.resolveSqlSelection( + this, + descriptor, + typeConfiguration + ); + + return new BasicResult<>( + sqlSelection.getValuesArrayPosition(), + resultVariable, + descriptor + ); } } diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/query/hql/SubqueryOperatorsTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/query/hql/SubqueryOperatorsTest.java index b9ac22ceef..88b5c68970 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/query/hql/SubqueryOperatorsTest.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/query/hql/SubqueryOperatorsTest.java @@ -52,6 +52,33 @@ public void testAny() { } ); } + @Test + public void testSubqueryInVariousClauses() { + inTransaction( + session -> { + List res0 = session.createQuery( + "select (select 1) as one, (select 'foo') as foo order by one, foo, (select 2)" ) + .list(); + assertThat( res0.size(), is( 1 ) ); + List res1 = session.createQuery( + "select (select 1) as one, (select 'foo') as foo from SimpleEntity o order by one, foo, (select 2)" ) + .list(); + assertThat( res1.size(), is( 2 ) ); + List res2 = session.createQuery( + "select (select x.id from SimpleEntity x where x.id = o.id) as xid from SimpleEntity o order by xid" ) + .list(); + assertThat( res2.size(), is( 2 ) ); + List res3 = session.createQuery( + "from SimpleEntity o where o.someString = (select 'aaa') and o.id >= (select 0)" ) + .list(); + assertThat( res3.size(), is( 1 ) ); + List res4 = session.createQuery( + "from SimpleEntity o where o.id = (select y.id from SimpleEntity y where y.id = o.id)" ) + .list(); + assertThat( res4.size(), is( 2 ) ); + } ); + } + @Test public void testExists() { inTransaction(