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.
This commit is contained in:
gavinking 2020-02-17 01:15:34 +01:00 committed by Steve Ebersole
parent 76000201eb
commit a1a85e2517
4 changed files with 85 additions and 3 deletions

View File

@ -525,7 +525,9 @@ public abstract class BaseSqmToSqlAstConverter
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() );

View File

@ -103,4 +103,13 @@ public class QueryLiteral<T> implements Literal, DomainResultProducer<T> {
executionContext.getSession()
);
}
@Override
public void applySqlSelections(DomainResultCreationState creationState) {
creationState.getSqlAstCreationState().getSqlExpressionResolver().resolveSqlSelection(
this,
type.getBasicType().getJavaTypeDescriptor(),
creationState.getSqlAstCreationState().getCreationContext().getDomainModel().getTypeConfiguration()
);
}
}

View File

@ -11,19 +11,28 @@ import java.util.List;
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 class QuerySpec implements SqlAstNode, PredicateContainer, Expression, Ct
@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
);
}
}

View File

@ -52,6 +52,33 @@ public class SubqueryOperatorsTest extends SessionFactoryBasedFunctionalTest {
} );
}
@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(