HHH-14913 - sub-query modifiers are ignored

This commit is contained in:
Steve Ebersole 2021-11-04 14:53:14 -05:00
parent b70487546e
commit 0ea110aea2
12 changed files with 212 additions and 27 deletions

View File

@ -50,7 +50,7 @@ import org.hibernate.query.sqm.tree.domain.SqmPath;
import org.hibernate.query.sqm.tree.domain.SqmSetJoin;
import org.hibernate.query.sqm.tree.domain.SqmSingularJoin;
import org.hibernate.query.sqm.tree.expression.SqmExpression;
import org.hibernate.query.sqm.tree.expression.SqmRestrictedSubQueryExpression;
import org.hibernate.query.sqm.tree.expression.SqmModifiedSubQueryExpression;
import org.hibernate.query.sqm.tree.expression.SqmTuple;
import org.hibernate.query.sqm.tree.from.SqmRoot;
import org.hibernate.query.sqm.tree.insert.SqmInsertSelectStatement;
@ -354,13 +354,13 @@ public interface NodeBuilder extends HibernateCriteriaBuilder {
<T> SqmFunction<T> function(String name, Class<T> type, Expression<?>[] args);
@Override
<Y> SqmRestrictedSubQueryExpression<Y> all(Subquery<Y> subquery);
<Y> SqmModifiedSubQueryExpression<Y> all(Subquery<Y> subquery);
@Override
<Y> SqmRestrictedSubQueryExpression<Y> some(Subquery<Y> subquery);
<Y> SqmModifiedSubQueryExpression<Y> some(Subquery<Y> subquery);
@Override
<Y> SqmRestrictedSubQueryExpression<Y> any(Subquery<Y> subquery);
<Y> SqmModifiedSubQueryExpression<Y> any(Subquery<Y> subquery);
@Override
<K, M extends Map<K, ?>> SqmExpression<Set<K>> keys(M map);

View File

@ -9,7 +9,6 @@ package org.hibernate.query.sqm;
import java.util.List;
import org.hibernate.NotYetImplementedFor6Exception;
import org.hibernate.metamodel.model.domain.internal.DiscriminatorSqmPath;
import org.hibernate.query.sqm.sql.internal.SelfInterpretingSqmPath;
import org.hibernate.query.sqm.tree.cte.SqmCteContainer;
import org.hibernate.query.sqm.tree.cte.SqmCteStatement;
@ -42,6 +41,7 @@ import org.hibernate.query.sqm.tree.expression.SqmDurationUnit;
import org.hibernate.query.sqm.tree.expression.SqmEvery;
import org.hibernate.query.sqm.tree.expression.SqmFormat;
import org.hibernate.query.sqm.tree.expression.SqmFunction;
import org.hibernate.query.sqm.tree.expression.SqmModifiedSubQueryExpression;
import org.hibernate.query.sqm.tree.expression.SqmParameterizedEntityType;
import org.hibernate.query.sqm.tree.expression.SqmEnumLiteral;
import org.hibernate.query.sqm.tree.expression.SqmExpression;
@ -50,7 +50,6 @@ import org.hibernate.query.sqm.tree.expression.SqmLiteral;
import org.hibernate.query.sqm.tree.expression.SqmLiteralEntityType;
import org.hibernate.query.sqm.tree.expression.SqmNamedParameter;
import org.hibernate.query.sqm.tree.expression.SqmPositionalParameter;
import org.hibernate.query.sqm.tree.expression.SqmRestrictedSubQueryExpression;
import org.hibernate.query.sqm.tree.expression.SqmSummarization;
import org.hibernate.query.sqm.tree.expression.SqmToDuration;
import org.hibernate.query.sqm.tree.expression.SqmTuple;
@ -205,7 +204,7 @@ public interface SemanticQueryWalker<T> {
T visitSubQueryExpression(SqmSubQuery<?> expression);
T visitRestrictedSubQueryExpression(SqmRestrictedSubQueryExpression<?> sqmRestrictedSubQueryExpression);
T visitModifiedSubQueryExpression(SqmModifiedSubQueryExpression<?> expression);
T visitSimpleCaseExpression(SqmCaseSimple<?, ?> expression);

View File

@ -93,7 +93,7 @@ import org.hibernate.query.sqm.tree.expression.SqmExpression;
import org.hibernate.query.sqm.tree.expression.SqmFunction;
import org.hibernate.query.sqm.tree.expression.SqmLiteral;
import org.hibernate.query.sqm.tree.expression.SqmLiteralNull;
import org.hibernate.query.sqm.tree.expression.SqmRestrictedSubQueryExpression;
import org.hibernate.query.sqm.tree.expression.SqmModifiedSubQueryExpression;
import org.hibernate.query.sqm.tree.expression.SqmTrimSpecification;
import org.hibernate.query.sqm.tree.expression.SqmTuple;
import org.hibernate.query.sqm.tree.expression.SqmUnaryOperation;
@ -1344,28 +1344,28 @@ public class SqmCriteriaNodeBuilder implements NodeBuilder, SqmCreationContext,
}
@Override
public <Y> SqmRestrictedSubQueryExpression<Y> all(Subquery<Y> subquery) {
return new SqmRestrictedSubQueryExpression<>(
public <Y> SqmModifiedSubQueryExpression<Y> all(Subquery<Y> subquery) {
return new SqmModifiedSubQueryExpression<>(
(SqmSubQuery<Y>) subquery,
SqmRestrictedSubQueryExpression.Modifier.ALL,
SqmModifiedSubQueryExpression.Modifier.ALL,
this
);
}
@Override
public <Y> SqmRestrictedSubQueryExpression<Y> some(Subquery<Y> subquery) {
return new SqmRestrictedSubQueryExpression<>(
public <Y> SqmModifiedSubQueryExpression<Y> some(Subquery<Y> subquery) {
return new SqmModifiedSubQueryExpression<>(
(SqmSubQuery<Y>) subquery,
SqmRestrictedSubQueryExpression.Modifier.SOME,
SqmModifiedSubQueryExpression.Modifier.SOME,
this
);
}
@Override
public <Y> SqmRestrictedSubQueryExpression<Y> any(Subquery<Y> subquery) {
return new SqmRestrictedSubQueryExpression<>(
public <Y> SqmModifiedSubQueryExpression<Y> any(Subquery<Y> subquery) {
return new SqmModifiedSubQueryExpression<>(
(SqmSubQuery<Y>) subquery,
SqmRestrictedSubQueryExpression.Modifier.ANY,
SqmModifiedSubQueryExpression.Modifier.ANY,
this
);
}

View File

@ -55,7 +55,7 @@ import org.hibernate.query.sqm.tree.expression.SqmLiteralEntityType;
import org.hibernate.query.sqm.tree.expression.SqmNamedParameter;
import org.hibernate.query.sqm.tree.expression.SqmParameterizedEntityType;
import org.hibernate.query.sqm.tree.expression.SqmPositionalParameter;
import org.hibernate.query.sqm.tree.expression.SqmRestrictedSubQueryExpression;
import org.hibernate.query.sqm.tree.expression.SqmModifiedSubQueryExpression;
import org.hibernate.query.sqm.tree.expression.SqmStar;
import org.hibernate.query.sqm.tree.expression.SqmSummarization;
import org.hibernate.query.sqm.tree.expression.SqmToDuration;
@ -1026,7 +1026,7 @@ public class SqmTreePrinter implements SemanticQueryWalker<Object> {
}
@Override
public Object visitRestrictedSubQueryExpression(SqmRestrictedSubQueryExpression sqmRestrictedSubQueryExpression) {
public Object visitModifiedSubQueryExpression(SqmModifiedSubQueryExpression expression) {
return null;
}
}

View File

@ -53,7 +53,7 @@ import org.hibernate.query.sqm.tree.expression.SqmLiteralEntityType;
import org.hibernate.query.sqm.tree.expression.SqmNamedParameter;
import org.hibernate.query.sqm.tree.expression.SqmParameterizedEntityType;
import org.hibernate.query.sqm.tree.expression.SqmPositionalParameter;
import org.hibernate.query.sqm.tree.expression.SqmRestrictedSubQueryExpression;
import org.hibernate.query.sqm.tree.expression.SqmModifiedSubQueryExpression;
import org.hibernate.query.sqm.tree.expression.SqmSummarization;
import org.hibernate.query.sqm.tree.expression.SqmToDuration;
import org.hibernate.query.sqm.tree.expression.SqmTuple;
@ -588,8 +588,8 @@ public abstract class BaseSemanticQueryWalker implements SemanticQueryWalker<Obj
}
@Override
public Object visitRestrictedSubQueryExpression(SqmRestrictedSubQueryExpression<?> sqmRestrictedSubQueryExpression) {
return sqmRestrictedSubQueryExpression.getSubQuery().accept( this );
public Object visitModifiedSubQueryExpression(SqmModifiedSubQueryExpression<?> expression) {
return expression.getSubQuery().accept( this );
}
@Override

View File

@ -85,6 +85,8 @@ import org.hibernate.query.sqm.produce.function.internal.PatternRenderer;
import org.hibernate.query.sqm.tree.SqmJoinType;
import org.hibernate.query.sqm.tree.domain.SqmCorrelatedRootJoin;
import org.hibernate.query.sqm.tree.domain.SqmCorrelation;
import org.hibernate.query.sqm.tree.expression.SqmModifiedSubQueryExpression;
import org.hibernate.sql.ast.tree.expression.ModifiedSubQueryExpression;
import org.hibernate.sql.exec.internal.VersionTypeSeedParameterSpecification;
import org.hibernate.persister.collection.CollectionPersister;
import org.hibernate.persister.entity.EntityPersister;
@ -4270,6 +4272,27 @@ public abstract class BaseSqmToSqlAstConverter<T extends Statement> extends Base
return lhs;
}
@Override
public Object visitModifiedSubQueryExpression(SqmModifiedSubQueryExpression<?> expr) {
return new ModifiedSubQueryExpression(
visitSubQueryExpression( expr.getSubQuery() ),
convert( expr.getModifier() )
);
}
private ModifiedSubQueryExpression.Modifier convert(SqmModifiedSubQueryExpression.Modifier modifier) {
if ( modifier == SqmModifiedSubQueryExpression.Modifier.ALL ) {
return ModifiedSubQueryExpression.Modifier.ALL;
}
if ( modifier == SqmModifiedSubQueryExpression.Modifier.ANY ) {
return ModifiedSubQueryExpression.Modifier.ANY;
}
if ( modifier == SqmModifiedSubQueryExpression.Modifier.SOME ) {
return ModifiedSubQueryExpression.Modifier.SOME;
}
throw new IllegalStateException( "Unrecognized SqmModifiedSubQueryExpression.Modifier : " + modifier );
}
@Override
public QueryPart visitSubQueryExpression(SqmSubQuery<?> sqmSubQuery) {
// The only purpose for tracking the current join is to

View File

@ -17,7 +17,7 @@ import org.hibernate.query.sqm.tree.select.SqmSubQuery;
*
* @author Steve Ebersole
*/
public class SqmRestrictedSubQueryExpression<T> extends AbstractSqmExpression<T> {
public class SqmModifiedSubQueryExpression<T> extends AbstractSqmExpression<T> {
public enum Modifier {
ALL,
ANY,
@ -27,7 +27,7 @@ public class SqmRestrictedSubQueryExpression<T> extends AbstractSqmExpression<T>
private final SqmSubQuery<T> subQuery;
private final Modifier modifier;
public SqmRestrictedSubQueryExpression(
public SqmModifiedSubQueryExpression(
SqmSubQuery<T> subquery,
Modifier modifier,
NodeBuilder builder) {
@ -39,7 +39,7 @@ public class SqmRestrictedSubQueryExpression<T> extends AbstractSqmExpression<T>
);
}
public SqmRestrictedSubQueryExpression(
public SqmModifiedSubQueryExpression(
SqmSubQuery<T> subQuery,
Modifier modifier,
SqmExpressable<T> resultType,
@ -59,7 +59,7 @@ public class SqmRestrictedSubQueryExpression<T> extends AbstractSqmExpression<T>
@Override
public <X> X accept(SemanticQueryWalker<X> walker) {
return walker.visitRestrictedSubQueryExpression( this );
return walker.visitModifiedSubQueryExpression( this );
}
@Override

View File

@ -26,6 +26,7 @@ import org.hibernate.sql.ast.tree.expression.ExtractUnit;
import org.hibernate.sql.ast.tree.expression.Format;
import org.hibernate.sql.ast.tree.expression.JdbcLiteral;
import org.hibernate.sql.ast.tree.expression.JdbcParameter;
import org.hibernate.sql.ast.tree.expression.ModifiedSubQueryExpression;
import org.hibernate.sql.ast.tree.expression.QueryLiteral;
import org.hibernate.sql.ast.tree.expression.SelfRenderingExpression;
import org.hibernate.sql.ast.tree.expression.SqlSelectionExpression;
@ -145,6 +146,8 @@ public interface SqlAstWalker {
void visitUnaryOperationExpression(UnaryOperation unaryOperationExpression);
void visitModifiedSubQueryExpression(ModifiedSubQueryExpression expression);
void visitBooleanExpressionPredicate(BooleanExpressionPredicate booleanExpressionPredicate);
void visitBetweenPredicate(BetweenPredicate betweenPredicate);
@ -176,5 +179,4 @@ public interface SqlAstWalker {
void visitDuration(Duration duration);
void visitConversion(Conversion conversion);
}

View File

@ -94,6 +94,7 @@ import org.hibernate.sql.ast.tree.expression.JdbcLiteral;
import org.hibernate.sql.ast.tree.expression.JdbcParameter;
import org.hibernate.sql.ast.tree.expression.Literal;
import org.hibernate.sql.ast.tree.expression.LiteralAsParameter;
import org.hibernate.sql.ast.tree.expression.ModifiedSubQueryExpression;
import org.hibernate.sql.ast.tree.expression.QueryLiteral;
import org.hibernate.sql.ast.tree.expression.SelfRenderingExpression;
import org.hibernate.sql.ast.tree.expression.SqlSelectionExpression;
@ -4001,6 +4002,15 @@ public abstract class AbstractSqlAstTranslator<T extends JdbcOperation> implemen
unaryOperationExpression.getOperand().accept( this );
}
@Override
public void visitModifiedSubQueryExpression(ModifiedSubQueryExpression expression) {
final ModifiedSubQueryExpression.Modifier modifier = expression.getModifier();
appendSql( modifier.getSqlName() );
appendSql( " " );
expression.getSubQuery().accept( this );
}
@Override
public void visitSelfRenderingPredicate(SelfRenderingPredicate selfRenderingPredicate) {
// todo (6.0) render boolean expression as comparison predicate if necessary

View File

@ -30,6 +30,7 @@ import org.hibernate.sql.ast.tree.expression.Format;
import org.hibernate.sql.ast.tree.expression.FunctionExpression;
import org.hibernate.sql.ast.tree.expression.JdbcLiteral;
import org.hibernate.sql.ast.tree.expression.JdbcParameter;
import org.hibernate.sql.ast.tree.expression.ModifiedSubQueryExpression;
import org.hibernate.sql.ast.tree.expression.QueryLiteral;
import org.hibernate.sql.ast.tree.expression.SelfRenderingExpression;
import org.hibernate.sql.ast.tree.expression.SqlSelectionExpression;
@ -174,6 +175,11 @@ class AggregateFunctionChecker implements SqlAstWalker {
unaryOperationExpression.getOperand().accept( this );
}
@Override
public void visitModifiedSubQueryExpression(ModifiedSubQueryExpression expression) {
expression.getSubQuery().accept( this );
}
@Override
public void visitBooleanExpressionPredicate(BooleanExpressionPredicate booleanExpressionPredicate) {
booleanExpressionPredicate.getExpression().accept( this );

View File

@ -0,0 +1,58 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* License: GNU Lesser General Public License (LGPL), version 2.1 or later
* See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html
*/
package org.hibernate.sql.ast.tree.expression;
import org.hibernate.metamodel.mapping.JdbcMappingContainer;
import org.hibernate.sql.ast.SqlAstWalker;
import org.hibernate.sql.ast.tree.select.QueryPart;
/**
* @author Steve Ebersole
*/
public class ModifiedSubQueryExpression implements Expression {
public enum Modifier {
ALL( "all" ),
ANY( "any" ),
SOME( "some" );
private final String sqlName;
Modifier(String sqlName) {
this.sqlName = sqlName;
}
public String getSqlName() {
return sqlName;
}
}
private final QueryPart subQuery;
private final Modifier modifier;
public ModifiedSubQueryExpression(QueryPart subQuery, Modifier modifier) {
this.subQuery = subQuery;
this.modifier = modifier;
}
public QueryPart getSubQuery() {
return subQuery;
}
public Modifier getModifier() {
return modifier;
}
@Override
public void accept(SqlAstWalker sqlTreeWalker) {
sqlTreeWalker.visitModifiedSubQueryExpression( this );
}
@Override
public JdbcMappingContainer getExpressionType() {
return subQuery.getExpressionType();
}
}

View File

@ -0,0 +1,87 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* License: GNU Lesser General Public License (LGPL), version 2.1 or later
* See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html
*/
package org.hibernate.orm.test.query.criteria;
import org.hibernate.query.criteria.HibernateCriteriaBuilder;
import org.hibernate.testing.orm.domain.gambit.BasicEntity;
import org.hibernate.testing.orm.junit.DomainModel;
import org.hibernate.testing.orm.junit.SessionFactory;
import org.hibernate.testing.orm.junit.SessionFactoryScope;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.Test;
import jakarta.persistence.criteria.CriteriaQuery;
import jakarta.persistence.criteria.Path;
import jakarta.persistence.criteria.Root;
import jakarta.persistence.criteria.Subquery;
/**
* @author Steve Ebersole
*/
@DomainModel( annotatedClasses = BasicEntity.class )
@SessionFactory
public class SubQueryTests {
@Test
public void modifiedSubQueryTest(SessionFactoryScope scope) {
scope.inTransaction( (session) -> {
session.persist( new BasicEntity( 2368, "irrelevant" ) );
session.persist( new BasicEntity( 2578, "irrelevant" ) );
session.persist( new BasicEntity( 3000, "irrelevant" ) );
session.persist( new BasicEntity( 10000, "irrelevant" ) );
session.persist( new BasicEntity( 10050, "irrelevant" ) );
} );
scope.inTransaction( (session) -> {
final HibernateCriteriaBuilder criteriaBuilder = session.getCriteriaBuilder();
final CriteriaQuery<Integer> criteria = criteriaBuilder.createQuery( Integer.class );
final Root<BasicEntity> root = criteria.from(BasicEntity.class);
final Path<Integer> rootIdPath = root.get( "id" );
final Subquery<Integer> subCriteria = criteria.subquery(Integer.class);
final Root<BasicEntity> subRoot = subCriteria.from(BasicEntity.class);
final Path<Integer> subRootIdPath = subRoot.get( "id" );
subCriteria.select( subRootIdPath );
subCriteria.where( criteriaBuilder.lt( subRootIdPath, 10050 ) );
criteria.select( rootIdPath );
criteria.where(
criteriaBuilder.gt(
rootIdPath,
criteriaBuilder.all( subCriteria ) )
);
session.createQuery( criteria ).list();
} );
}
@Test
public void modifiedSubQueryHqlTest(SessionFactoryScope scope) {
scope.inTransaction( (session) -> {
session.persist( new BasicEntity( 2368, "irrelevant" ) );
session.persist( new BasicEntity( 2578, "irrelevant" ) );
session.persist( new BasicEntity( 3000, "irrelevant" ) );
session.persist( new BasicEntity( 10000, "irrelevant" ) );
session.persist( new BasicEntity( 10050, "irrelevant" ) );
} );
scope.inTransaction( (session) -> {
final String qry = "select e.id from BasicEntity e where e.id > all (select s.id from BasicEntity s where s.id > 1)";
session.createQuery( qry ).list();
} );
}
@AfterEach
public void cleanUpTestData(SessionFactoryScope scope) {
scope.inTransaction( (session) -> {
session.createQuery( "delete BasicEntity" ).executeUpdate();
} );
}
}