From ca22db14bb3cd2d892b02fd3f1c5f49d21441901 Mon Sep 17 00:00:00 2001 From: Christian Beikov Date: Tue, 3 Aug 2021 09:56:49 +0200 Subject: [PATCH] Fix some issues with collection-path related HQL functions minindex/maxindex/minelement/maxelement/elements/indices and group by item rendering problems --- .../org/hibernate/userguide/hql/HQLTest.java | 2 +- .../org/hibernate/grammars/hql/HqlLexer.g4 | 1 + .../org/hibernate/grammars/hql/HqlParser.g4 | 44 +++-- .../java/org/hibernate/dialect/Dialect.java | 4 +- .../java/org/hibernate/dialect/H2Dialect.java | 4 +- .../org/hibernate/dialect/MySQLDialect.java | 4 +- .../hibernate/dialect/PostgreSQLDialect.java | 4 +- .../dialect/SelectItemReferenceStrategy.java | 27 +++ .../domain/internal/ListAttributeImpl.java | 1 - .../domain/internal/MapAttributeImpl.java | 1 - .../domain/internal/MappingMetamodelImpl.java | 4 + .../hql/internal/SemanticQueryBuilder.java | 181 +++++++++++++++--- .../sqm/StrictJpaComplianceViolation.java | 2 +- .../sqm/internal/SqmMappingModelHelper.java | 16 +- .../hibernate/query/sqm/internal/SqmUtil.java | 12 ++ .../sqm/sql/BaseSqmToSqlAstConverter.java | 53 +++-- .../EntityValuedPathInterpretation.java | 6 +- .../tree/domain/AbstractSqmSimplePath.java | 9 +- .../AbstractSqmSpecificPluralPartPath.java | 7 - .../sqm/tree/domain/SqmCorrelatedRoot.java | 7 +- .../sql/ast/spi/AbstractSqlAstTranslator.java | 94 +++++++-- .../org/hibernate/query/GroupByAliasTest.java | 5 +- .../org/hibernate/testing/DialectChecks.java | 6 - 23 files changed, 378 insertions(+), 116 deletions(-) create mode 100644 hibernate-core/src/main/java/org/hibernate/dialect/SelectItemReferenceStrategy.java diff --git a/documentation/src/test/java/org/hibernate/userguide/hql/HQLTest.java b/documentation/src/test/java/org/hibernate/userguide/hql/HQLTest.java index 40a6bf6063..9bff4d97de 100644 --- a/documentation/src/test/java/org/hibernate/userguide/hql/HQLTest.java +++ b/documentation/src/test/java/org/hibernate/userguide/hql/HQLTest.java @@ -2006,7 +2006,7 @@ public class HQLTest extends BaseEntityManagerFunctionalTestCase { "where p.person = ph.person ", Object[].class ) .getResultList(); //end::hql-relational-comparisons-example[] - assertEquals(3, phonePayments.size()); + assertEquals(2, phonePayments.size()); }); } diff --git a/hibernate-core/src/main/antlr/org/hibernate/grammars/hql/HqlLexer.g4 b/hibernate-core/src/main/antlr/org/hibernate/grammars/hql/HqlLexer.g4 index 79ab64a33e..48bd9b4ca3 100644 --- a/hibernate-core/src/main/antlr/org/hibernate/grammars/hql/HqlLexer.g4 +++ b/hibernate-core/src/main/antlr/org/hibernate/grammars/hql/HqlLexer.g4 @@ -197,6 +197,7 @@ HOUR : [hH] [oO] [uU] [rR]; IFNULL : [iI] [fF] [nN] [uU] [lL] [lL]; IN : [iI] [nN]; INDEX : [iI] [nN] [dD] [eE] [xX]; +INDICES : [iI] [nN] [dD] [iI] [cC] [eE] [sS]; INNER : [iI] [nN] [nN] [eE] [rR]; INSERT : [iI] [nN] [sS] [eE] [rR] [tT]; INSTANT : [iI] [nN] [sS] [tT] [aA] [nN] [tT]; diff --git a/hibernate-core/src/main/antlr/org/hibernate/grammars/hql/HqlParser.g4 b/hibernate-core/src/main/antlr/org/hibernate/grammars/hql/HqlParser.g4 index 2617b963a1..5523bd5086 100644 --- a/hibernate-core/src/main/antlr/org/hibernate/grammars/hql/HqlParser.g4 +++ b/hibernate-core/src/main/antlr/org/hibernate/grammars/hql/HqlParser.g4 @@ -255,6 +255,7 @@ pathContinuation * * * TREAT( path ) * * ELEMENTS( path ) + * * INDICES( path ) * * VALUE( path ) * * KEY( path ) * * path[ selector ] @@ -262,6 +263,7 @@ pathContinuation syntacticDomainPath : treatedNavigablePath | collectionElementNavigablePath + | collectionIndexNavigablePath | mapKeyNavigablePath | dotIdentifierSequence indexedPathAccessFragment ; @@ -289,6 +291,10 @@ collectionElementNavigablePath : (VALUE | ELEMENTS) LEFT_PAREN path RIGHT_PAREN pathContinuation? ; +collectionIndexNavigablePath + : INDICES LEFT_PAREN path RIGHT_PAREN + ; + mapKeyNavigablePath : KEY LEFT_PAREN path RIGHT_PAREN pathContinuation? ; @@ -394,19 +400,20 @@ whereClause predicate //highest to lowest precedence - : LEFT_PAREN predicate RIGHT_PAREN # GroupedPredicate - | expression IS (NOT)? NULL # IsNullPredicate - | expression IS (NOT)? EMPTY # IsEmptyPredicate - | expression (NOT)? IN inList # InPredicate - | expression (NOT)? BETWEEN expression AND expression # BetweenPredicate - | expression (NOT)? LIKE expression (likeEscape)? # LikePredicate - | expression comparisonOperator expression # ComparisonPredicate - | EXISTS expression # ExistsPredicate - | expression (NOT)? MEMBER OF path # MemberOfPredicate - | NOT predicate # NegatedPredicate - | predicate AND predicate # AndPredicate - | predicate OR predicate # OrPredicate - | expression # BooleanExpressionPredicate + : LEFT_PAREN predicate RIGHT_PAREN # GroupedPredicate + | expression IS (NOT)? NULL # IsNullPredicate + | expression IS (NOT)? EMPTY # IsEmptyPredicate + | expression (NOT)? IN inList # InPredicate + | expression (NOT)? BETWEEN expression AND expression # BetweenPredicate + | expression (NOT)? LIKE expression (likeEscape)? # LikePredicate + | expression comparisonOperator expression # ComparisonPredicate + | EXISTS (ELEMENTS|INDICES) LEFT_PAREN dotIdentifierSequence RIGHT_PAREN # ExistsCollectionPartPredicate + | EXISTS expression # ExistsPredicate + | expression (NOT)? MEMBER OF path # MemberOfPredicate + | NOT predicate # NegatedPredicate + | predicate AND predicate # AndPredicate + | predicate OR predicate # OrPredicate + | expression # BooleanExpressionPredicate ; comparisonOperator @@ -421,10 +428,10 @@ comparisonOperator ; inList - : ELEMENTS? LEFT_PAREN dotIdentifierSequence RIGHT_PAREN # PersistentCollectionReferenceInList - | LEFT_PAREN expression (COMMA expression)* RIGHT_PAREN # ExplicitTupleInList - | LEFT_PAREN subQuery RIGHT_PAREN # SubQueryInList - | parameter # ParamInList + : (ELEMENTS|INDICES) LEFT_PAREN dotIdentifierSequence RIGHT_PAREN # PersistentCollectionReferenceInList + | LEFT_PAREN expression (COMMA expression)* RIGHT_PAREN # ExplicitTupleInList + | LEFT_PAREN subQuery RIGHT_PAREN # SubQueryInList + | parameter # ParamInList ; likeEscape @@ -713,11 +720,13 @@ countFunction everyFunction : (EVERY|ALL) LEFT_PAREN predicate RIGHT_PAREN filterClause? | (EVERY|ALL) LEFT_PAREN subQuery RIGHT_PAREN + | (EVERY|ALL) (ELEMENTS|INDICES) LEFT_PAREN dotIdentifierSequence RIGHT_PAREN ; anyFunction : (ANY|SOME) LEFT_PAREN predicate RIGHT_PAREN filterClause? | (ANY|SOME) LEFT_PAREN subQuery RIGHT_PAREN + | (ANY|SOME) (ELEMENTS|INDICES) LEFT_PAREN dotIdentifierSequence RIGHT_PAREN ; filterClause @@ -1170,6 +1179,7 @@ identifier | IFNULL | IN | INDEX + | INDICES | INNER | INSERT | INSTANT diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/Dialect.java b/hibernate-core/src/main/java/org/hibernate/dialect/Dialect.java index 002c3b4d0c..02efe30ce8 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/Dialect.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/Dialect.java @@ -3810,8 +3810,8 @@ public abstract class Dialect implements ConversionContext { return null; } - public boolean supportsSelectAliasInGroupByClause() { - return false; + public SelectItemReferenceStrategy getGroupBySelectItemReferenceStrategy() { + return SelectItemReferenceStrategy.EXPRESSION; } public SizeStrategy getSizeStrategy() { diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/H2Dialect.java b/hibernate-core/src/main/java/org/hibernate/dialect/H2Dialect.java index b402aae627..b56ccf09c9 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/H2Dialect.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/H2Dialect.java @@ -429,8 +429,8 @@ public class H2Dialect extends Dialect { } @Override - public boolean supportsSelectAliasInGroupByClause() { - return true; + public SelectItemReferenceStrategy getGroupBySelectItemReferenceStrategy() { + return SelectItemReferenceStrategy.ALIAS; } @Override diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/MySQLDialect.java b/hibernate-core/src/main/java/org/hibernate/dialect/MySQLDialect.java index 35a261db30..808a92a701 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/MySQLDialect.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/MySQLDialect.java @@ -499,8 +499,8 @@ public class MySQLDialect extends Dialect { } @Override - public boolean supportsSelectAliasInGroupByClause() { - return true; + public SelectItemReferenceStrategy getGroupBySelectItemReferenceStrategy() { + return SelectItemReferenceStrategy.POSITION; } @Override diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/PostgreSQLDialect.java b/hibernate-core/src/main/java/org/hibernate/dialect/PostgreSQLDialect.java index c29d307385..67b1e91bb3 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/PostgreSQLDialect.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/PostgreSQLDialect.java @@ -646,8 +646,8 @@ public class PostgreSQLDialect extends Dialect { } @Override - public boolean supportsSelectAliasInGroupByClause() { - return true; + public SelectItemReferenceStrategy getGroupBySelectItemReferenceStrategy() { + return SelectItemReferenceStrategy.POSITION; } @Override diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/SelectItemReferenceStrategy.java b/hibernate-core/src/main/java/org/hibernate/dialect/SelectItemReferenceStrategy.java new file mode 100644 index 0000000000..aa19d0c91a --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/dialect/SelectItemReferenceStrategy.java @@ -0,0 +1,27 @@ +/* + * 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 . + */ +package org.hibernate.dialect; + +/** + * Strategies for referring to a select item. + * + * @author Christian Beikov + */ +public enum SelectItemReferenceStrategy { + /** + * The default strategy i.e. render the expression again. + */ + EXPRESSION, + /** + * Refer to the item via its alias. + */ + ALIAS, + /** + * Refer to the item via its position. + */ + POSITION; +} diff --git a/hibernate-core/src/main/java/org/hibernate/metamodel/model/domain/internal/ListAttributeImpl.java b/hibernate-core/src/main/java/org/hibernate/metamodel/model/domain/internal/ListAttributeImpl.java index 56499d2372..c30398c923 100644 --- a/hibernate-core/src/main/java/org/hibernate/metamodel/model/domain/internal/ListAttributeImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/metamodel/model/domain/internal/ListAttributeImpl.java @@ -29,7 +29,6 @@ class ListAttributeImpl extends AbstractPluralAttribute, E> imp //noinspection unchecked this.indexPathSource = (SqmPathSource) SqmMappingModelHelper.resolveSqmKeyPathSource( - getName(), builder.getListIndexOrMapKeyType(), BindableType.PLURAL_ATTRIBUTE ); diff --git a/hibernate-core/src/main/java/org/hibernate/metamodel/model/domain/internal/MapAttributeImpl.java b/hibernate-core/src/main/java/org/hibernate/metamodel/model/domain/internal/MapAttributeImpl.java index 03f5255559..ff69a31736 100644 --- a/hibernate-core/src/main/java/org/hibernate/metamodel/model/domain/internal/MapAttributeImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/metamodel/model/domain/internal/MapAttributeImpl.java @@ -30,7 +30,6 @@ class MapAttributeImpl extends AbstractPluralAttribute, V> super( xceBuilder, metadataContext ); this.keyPathSource = SqmMappingModelHelper.resolveSqmKeyPathSource( - CollectionPart.Nature.INDEX.getName(), xceBuilder.getListIndexOrMapKeyType(), BindableType.PLURAL_ATTRIBUTE ); diff --git a/hibernate-core/src/main/java/org/hibernate/metamodel/model/domain/internal/MappingMetamodelImpl.java b/hibernate-core/src/main/java/org/hibernate/metamodel/model/domain/internal/MappingMetamodelImpl.java index 5667fed52f..ae5c69d6f6 100755 --- a/hibernate-core/src/main/java/org/hibernate/metamodel/model/domain/internal/MappingMetamodelImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/metamodel/model/domain/internal/MappingMetamodelImpl.java @@ -747,6 +747,10 @@ public class MappingMetamodelImpl implements MappingMetamodel, MetamodelImplemen return (MappingModelExpressable) sqmExpressable; } + if ( sqmExpressable instanceof EntityDomainType ) { + return getEntityDescriptor( ( (EntityDomainType) sqmExpressable ).getHibernateEntityName() ); + } + throw new UnsupportedOperationException( "Cannot determine proper mapping model expressable for " + sqmExpressable ); } diff --git a/hibernate-core/src/main/java/org/hibernate/query/hql/internal/SemanticQueryBuilder.java b/hibernate-core/src/main/java/org/hibernate/query/hql/internal/SemanticQueryBuilder.java index 7c1ab61ac1..70cc016983 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/hql/internal/SemanticQueryBuilder.java +++ b/hibernate-core/src/main/java/org/hibernate/query/hql/internal/SemanticQueryBuilder.java @@ -1693,8 +1693,8 @@ public class SemanticQueryBuilder extends HqlParserBaseVisitor implem HqlParser.JpaCollectionJoinContext ctx, SqmRoot sqmRoot) { final HqlParser.IdentificationVariableDefContext identificationVariableDefContext; - if ( ctx.getChildCount() > 1 ) { - identificationVariableDefContext = (HqlParser.IdentificationVariableDefContext) ctx.getChild( 1 ); + if ( ctx.getChildCount() > 5 ) { + identificationVariableDefContext = (HqlParser.IdentificationVariableDefContext) ctx.getChild( 5 ); } else { identificationVariableDefContext = null; @@ -2047,13 +2047,35 @@ public class SemanticQueryBuilder extends HqlParserBaseVisitor implem creationContext.getNodeBuilder() ); } + else if ( inListContext instanceof HqlParser.PersistentCollectionReferenceInListContext ) { + if ( getCreationOptions().useStrictJpaCompliance() ) { + throw new StrictJpaComplianceViolation( StrictJpaComplianceViolation.Type.HQL_COLLECTION_FUNCTION ); + } + final HqlParser.PersistentCollectionReferenceInListContext collectionReferenceInListContext = (HqlParser.PersistentCollectionReferenceInListContext) inListContext; + return new SqmInSubQueryPredicate<>( + testExpression, + createCollectionReferenceSubQuery( + (HqlParser.DotIdentifierSequenceContext) collectionReferenceInListContext.getChild( 2 ), + (TerminalNode) collectionReferenceInListContext.getChild( 0 ) + ), + negated, + creationContext.getNodeBuilder() + ); + } else { - // todo : handle PersistentCollectionReferenceInList labeled branch - throw new ParsingException( "Unexpected IN predicate type [" + ctx.getClass().getSimpleName() + "] : " + ctx.getText() ); } } + @Override + public SqmPredicate visitExistsCollectionPartPredicate(HqlParser.ExistsCollectionPartPredicateContext ctx) { + final SqmSubQuery subQuery = createCollectionReferenceSubQuery( + (HqlParser.DotIdentifierSequenceContext) ctx.getChild( 3 ), + null + ); + return new SqmExistsPredicate( subQuery, creationContext.getNodeBuilder() ); + } + @Override public SqmPredicate visitExistsPredicate(HqlParser.ExistsPredicateContext ctx) { final SqmExpression expression = (SqmExpression) ctx.getChild( 1 ).accept( this ); @@ -3764,23 +3786,35 @@ public class SemanticQueryBuilder extends HqlParserBaseVisitor implem final SqmPredicate filterExpression = getFilterExpression( ctx ); final ParseTree argumentChild = ctx.getChild( 2 ); if ( argumentChild instanceof HqlParser.SubQueryContext ) { - SqmSubQuery subquery = (SqmSubQuery) argumentChild.accept(this); + final SqmSubQuery subquery = (SqmSubQuery) argumentChild.accept( this ); return new SqmEvery<>( subquery, creationContext.getNodeBuilder() ); } + else if ( argumentChild instanceof HqlParser.PredicateContext ) { + if ( getCreationOptions().useStrictJpaCompliance() ) { + throw new StrictJpaComplianceViolation( StrictJpaComplianceViolation.Type.FUNCTION_CALL ); + } + final SqmExpression argument = (SqmExpression) argumentChild.accept( this ); - final SqmExpression argument = (SqmExpression) argumentChild.accept( this ); - - if ( argument instanceof SqmSubQuery && ctx.getChild( ctx.getChildCount() - 1) instanceof HqlParser.FilterClauseContext ) { - throw new SemanticException( "Quantified expression cannot have a filter clause!" ); + return getFunctionDescriptor( "every" ).generateAggregateSqmExpression( + singletonList( argument ), + filterExpression, + resolveExpressableTypeBasic( Boolean.class ), + creationContext.getQueryEngine(), + creationContext.getJpaMetamodel().getTypeConfiguration() + ); + } + else { + if ( getCreationOptions().useStrictJpaCompliance() ) { + throw new StrictJpaComplianceViolation( StrictJpaComplianceViolation.Type.HQL_COLLECTION_FUNCTION ); + } + return new SqmEvery<>( + createCollectionReferenceSubQuery( + (HqlParser.DotIdentifierSequenceContext) ctx.getChild( 3 ), + (TerminalNode) ctx.getChild( 1 ) + ), + creationContext.getNodeBuilder() + ); } - - return getFunctionDescriptor("every").generateAggregateSqmExpression( - singletonList( argument ), - filterExpression, - resolveExpressableTypeBasic( Boolean.class ), - creationContext.getQueryEngine(), - creationContext.getJpaMetamodel().getTypeConfiguration() - ); } @Override @@ -3788,23 +3822,87 @@ public class SemanticQueryBuilder extends HqlParserBaseVisitor implem final SqmPredicate filterExpression = getFilterExpression( ctx ); final ParseTree argumentChild = ctx.getChild( 2 ); if ( argumentChild instanceof HqlParser.SubQueryContext ) { - SqmSubQuery subquery = (SqmSubQuery) argumentChild.accept(this); + final SqmSubQuery subquery = (SqmSubQuery) argumentChild.accept( this ); return new SqmAny<>( subquery, creationContext.getNodeBuilder() ); } + else if ( argumentChild instanceof HqlParser.PredicateContext ) { + if ( getCreationOptions().useStrictJpaCompliance() ) { + throw new StrictJpaComplianceViolation( StrictJpaComplianceViolation.Type.FUNCTION_CALL ); + } + final SqmExpression argument = (SqmExpression) argumentChild.accept( this ); - final SqmExpression argument = (SqmExpression) argumentChild.accept( this ); - - if ( argument instanceof SqmSubQuery && ctx.getChild( ctx.getChildCount() - 1) instanceof HqlParser.FilterClauseContext ) { - throw new SemanticException( "Quantified expression cannot have a filter clause!" ); + return getFunctionDescriptor( "any" ).generateAggregateSqmExpression( + singletonList( argument ), + filterExpression, + resolveExpressableTypeBasic( Boolean.class ), + creationContext.getQueryEngine(), + creationContext.getJpaMetamodel().getTypeConfiguration() + ); } + else { + if ( getCreationOptions().useStrictJpaCompliance() ) { + throw new StrictJpaComplianceViolation( StrictJpaComplianceViolation.Type.HQL_COLLECTION_FUNCTION ); + } + return new SqmAny<>( + createCollectionReferenceSubQuery( + (HqlParser.DotIdentifierSequenceContext) ctx.getChild( 3 ), + (TerminalNode) ctx.getChild( 1 ) + ), + creationContext.getNodeBuilder() + ); + } + } - return getFunctionDescriptor("any").generateAggregateSqmExpression( - singletonList( argument ), - filterExpression, - resolveExpressableTypeBasic( Boolean.class ), - creationContext.getQueryEngine(), - creationContext.getJpaMetamodel().getTypeConfiguration() + private SqmSubQuery createCollectionReferenceSubQuery( + HqlParser.DotIdentifierSequenceContext pathCtx, + TerminalNode collectionReferenceCtx) { + final SqmPath pluralAttributePath = consumeDomainPath( pathCtx ); + final SqmPathSource referencedPathSource = pluralAttributePath.getReferencedPathSource(); + + if ( !(referencedPathSource instanceof PluralPersistentAttribute ) ) { + throw new PathException( + "Illegal attempt to treat non-plural path as a plural path : " + pluralAttributePath.getNavigablePath() + ); + } + final PluralPersistentAttribute attribute = (PluralPersistentAttribute) referencedPathSource; + final SqmSubQuery subQuery = new SqmSubQuery<>( + processingStateStack.getCurrent().getProcessingQuery(), + creationContext.getNodeBuilder() ); + final SqmSelectClause selectClause = new SqmSelectClause( false, 1, creationContext.getNodeBuilder() ); + final SqmFromClause fromClause = new SqmFromClause( 1 ); + final SqmCorrelation correlation = ( (AbstractSqmFrom) pluralAttributePath.getLhs() ).createCorrelation(); + final SqmAttributeJoin collectionJoin = correlation.join( pluralAttributePath.getNavigablePath().getUnaliasedLocalName() ); + fromClause.addRoot( correlation.getCorrelatedRoot() ); + if ( collectionReferenceCtx == null ) { + final SqmLiteral literal = new SqmLiteral<>( + 1, + StandardBasicTypes.INTEGER, + creationContext.getNodeBuilder() + ); + subQuery.applyInferableType( literal.getNodeType() ); + selectClause.setSelection( literal ); + } + else { + final SqmPathSource pathSource; + switch ( collectionReferenceCtx.getSymbol().getType() ) { + case HqlParser.ELEMENTS: + pathSource = attribute.getElementPathSource(); + break; + case HqlParser.INDICES: + pathSource = attribute.getIndexPathSource(); + break; + default: + throw new ParsingException( "Unexpected collection reference : " + collectionReferenceCtx.getText() ); + } + subQuery.applyInferableType( pathSource.getSqmPathType() ); + selectClause.setSelection( pathSource.createSqmPath( collectionJoin ) ); + } + final SqmQuerySpec querySpec = subQuery.getQuerySpec(); + querySpec.setFromClause( fromClause ); + querySpec.setSelectClause( selectClause ); + //noinspection unchecked + return (SqmSubQuery) subQuery; } @Override @@ -4216,6 +4314,9 @@ public class SemanticQueryBuilder extends HqlParserBaseVisitor implem else if ( firstChild instanceof HqlParser.CollectionElementNavigablePathContext ) { return visitCollectionElementNavigablePath( (HqlParser.CollectionElementNavigablePathContext) firstChild ); } + else if ( firstChild instanceof HqlParser.CollectionIndexNavigablePathContext ) { + return visitCollectionIndexNavigablePath( (HqlParser.CollectionIndexNavigablePathContext) firstChild ); + } else if ( firstChild instanceof HqlParser.MapKeyNavigablePathContext ) { return visitMapKeyNavigablePath( (HqlParser.MapKeyNavigablePathContext) firstChild ); } @@ -4384,6 +4485,30 @@ public class SemanticQueryBuilder extends HqlParserBaseVisitor implem } + @Override + public SqmPath visitCollectionIndexNavigablePath(HqlParser.CollectionIndexNavigablePathContext ctx) { + final SqmPath pluralAttributePath = consumeDomainPath( (HqlParser.PathContext) ctx.getChild( 2 ) ); + final SqmPathSource referencedPathSource = pluralAttributePath.getReferencedPathSource(); + + if ( !(referencedPathSource instanceof PluralPersistentAttribute ) ) { + throw new PathException( + "Illegal attempt to treat non-plural path as a plural path : " + pluralAttributePath.getNavigablePath() + ); + } + + final PluralPersistentAttribute attribute = (PluralPersistentAttribute) referencedPathSource; + + if ( getCreationOptions().useStrictJpaCompliance() ) { + if ( attribute.getCollectionClassification() != CollectionClassification.MAP ) { + throw new StrictJpaComplianceViolation( StrictJpaComplianceViolation.Type.VALUE_FUNCTION_ON_NON_MAP ); + } + } + + return attribute.getIndexPathSource().createSqmPath( + pluralAttributePath + ); + } + @Override @SuppressWarnings({ "rawtypes" }) public SqmPath visitMapKeyNavigablePath(HqlParser.MapKeyNavigablePathContext ctx) { diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/StrictJpaComplianceViolation.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/StrictJpaComplianceViolation.java index c627040a9e..858b043862 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/StrictJpaComplianceViolation.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/StrictJpaComplianceViolation.java @@ -19,7 +19,7 @@ public class StrictJpaComplianceViolation extends SemanticException { ALIASED_FETCH_JOIN( "aliased fetch join" ), UNMAPPED_POLYMORPHISM( "unmapped polymorphic reference" ), FUNCTION_CALL( "improper non-standard function call" ), - HQL_COLLECTION_FUNCTION( "use of HQL collection functions (maxelement, minelement, maxindex, minindex)"), + HQL_COLLECTION_FUNCTION( "use of HQL collection functions (maxelement, minelement, maxindex, minindex, elements, indices)"), VALUE_FUNCTION_ON_NON_MAP( "use of value() function for non-Map type" ), RESERVED_WORD_USED_AS_ALIAS( "use of reserved word as alias (identification variable or result variable)" ), INDEXED_ELEMENT_REFERENCE( "use of HQL indexed element reference syntax" ), diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/internal/SqmMappingModelHelper.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/internal/SqmMappingModelHelper.java index 988ed8581f..583aade88c 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/internal/SqmMappingModelHelper.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/internal/SqmMappingModelHelper.java @@ -11,9 +11,11 @@ import javax.persistence.metamodel.Bindable; import org.hibernate.NotYetImplementedFor6Exception; import org.hibernate.engine.spi.SessionFactoryImplementor; +import org.hibernate.metamodel.mapping.CollectionPart; import org.hibernate.metamodel.mapping.EntityMappingType; import org.hibernate.metamodel.mapping.MappingModelExpressable; import org.hibernate.metamodel.mapping.ModelPart; +import org.hibernate.metamodel.mapping.ModelPartContainer; import org.hibernate.metamodel.model.domain.AnyMappingDomainType; import org.hibernate.metamodel.model.domain.BasicDomainType; import org.hibernate.metamodel.model.domain.DomainType; @@ -61,11 +63,13 @@ public class SqmMappingModelHelper { } public static SqmPathSource resolveSqmKeyPathSource( - String name, DomainType valueDomainType, Bindable.BindableType jpaBindableType) { - // todo (6.0): the key path source must create a special path for the key - return resolveSqmPathSource( name, valueDomainType, jpaBindableType ); + return resolveSqmPathSource( + CollectionPart.Nature.INDEX.getName(), + valueDomainType, + jpaBindableType + ); } public static SqmPathSource resolveSqmPathSource( @@ -149,7 +153,11 @@ public class SqmMappingModelHelper { // Plural path parts are not joined and thus also have no table group if ( sqmPath instanceof AbstractSqmSpecificPluralPartPath ) { final TableGroup lhsTableGroup = tableGroupLocator.apply( sqmPath.getLhs().getLhs().getNavigablePath() ); - return lhsTableGroup.getModelPart().findSubPart( sqmPath.getReferencedPathSource().getPathName(), null ); + final ModelPartContainer pluralPart = (ModelPartContainer) lhsTableGroup.getModelPart().findSubPart( + sqmPath.getLhs().getReferencedPathSource().getPathName(), + null + ); + return pluralPart.findSubPart( sqmPath.getReferencedPathSource().getPathName(), null ); } final TableGroup lhsTableGroup = tableGroupLocator.apply( sqmPath.getLhs().getNavigablePath() ); diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/internal/SqmUtil.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/internal/SqmUtil.java index 14182a3eab..210de27737 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/internal/SqmUtil.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/internal/SqmUtil.java @@ -20,6 +20,7 @@ import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.engine.spi.SharedSessionContractImplementor; import org.hibernate.metamodel.MappingMetamodel; import org.hibernate.metamodel.mapping.Bindable; +import org.hibernate.metamodel.mapping.CollectionPart; import org.hibernate.metamodel.mapping.ConvertibleModelPart; import org.hibernate.metamodel.mapping.EntityIdentifierMapping; import org.hibernate.metamodel.mapping.EntityMappingType; @@ -328,6 +329,9 @@ public class SqmUtil { if ( parameterType == null ) { throw new SqlTreeCreationException( "Unable to interpret mapping-model type for Query parameter : " + domainParam ); } + if ( parameterType instanceof CollectionPart && ( (CollectionPart) parameterType ).getPartMappingType() instanceof Bindable ) { + parameterType = (Bindable) ( (CollectionPart) parameterType ).getPartMappingType(); + } if ( parameterType instanceof EntityIdentifierMapping ) { final EntityIdentifierMapping identifierMapping = (EntityIdentifierMapping) parameterType; @@ -336,6 +340,14 @@ public class SqmUtil { bindValue = identifierMapping.getIdentifier( bindValue, session ); } } + else if ( parameterType instanceof EntityMappingType ) { + final EntityIdentifierMapping identifierMapping = ( (EntityMappingType) parameterType ).getIdentifierMapping(); + final EntityMappingType entityMapping = identifierMapping.findContainingEntityMapping(); + parameterType = identifierMapping; + if ( entityMapping.getRepresentationStrategy().getInstantiator().isInstance( bindValue, session.getFactory() ) ) { + bindValue = identifierMapping.getIdentifier( bindValue, session ); + } + } else if ( parameterType instanceof ToOneAttributeMapping ) { ToOneAttributeMapping association = (ToOneAttributeMapping) parameterType; bindValue = association.getForeignKeyDescriptor().getAssociationKeyFromSide( 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 9ada40d8e7..4827f23a6d 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 @@ -45,6 +45,7 @@ import org.hibernate.metamodel.MappingMetamodel; import org.hibernate.metamodel.mapping.Association; import org.hibernate.metamodel.mapping.AttributeMapping; import org.hibernate.metamodel.mapping.BasicValuedMapping; +import org.hibernate.metamodel.mapping.Bindable; import org.hibernate.metamodel.mapping.CollectionPart; import org.hibernate.metamodel.mapping.ConvertibleModelPart; import org.hibernate.metamodel.mapping.EmbeddableMappingType; @@ -52,6 +53,7 @@ import org.hibernate.metamodel.mapping.EmbeddableValuedModelPart; import org.hibernate.metamodel.mapping.EntityAssociationMapping; import org.hibernate.metamodel.mapping.EntityDiscriminatorMapping; import org.hibernate.metamodel.mapping.EntityIdentifierMapping; +import org.hibernate.metamodel.mapping.EntityMappingType; import org.hibernate.metamodel.mapping.EntityValuedModelPart; import org.hibernate.metamodel.mapping.EntityVersionMapping; import org.hibernate.metamodel.mapping.ForeignKeyDescriptor; @@ -2747,17 +2749,19 @@ public abstract class BaseSqmToSqlAstConverter extends Base MappingModelExpressable valueMapping, BiConsumer jdbcParameterConsumer) { sqmParameterMappingModelTypes.put( expression, valueMapping ); - + final Bindable bindable; if ( valueMapping instanceof Association ) { - ( (Association) valueMapping ).getForeignKeyDescriptor().forEachJdbcType( - (index, jdbcMapping) -> jdbcParameterConsumer.accept( index, new JdbcParameterImpl( jdbcMapping ) ) - ); + bindable = ( (Association) valueMapping ).getForeignKeyDescriptor(); + } + else if ( valueMapping instanceof EntityMappingType ) { + bindable = ( (EntityMappingType) valueMapping ).getIdentifierMapping(); } else { - valueMapping.forEachJdbcType( - (index, jdbcMapping) -> jdbcParameterConsumer.accept( index, new JdbcParameterImpl( jdbcMapping ) ) - ); + bindable = valueMapping; } + bindable.forEachJdbcType( + (index, jdbcMapping) -> jdbcParameterConsumer.accept( index, new JdbcParameterImpl( jdbcMapping ) ) + ); } @Override @@ -4098,26 +4102,47 @@ public abstract class BaseSqmToSqlAstConverter extends Base final CollectionPart collectionPart = index ? mappingModelExpressable.getIndexDescriptor() : mappingModelExpressable.getElementDescriptor(); - final List arguments = new ArrayList<>( 1 ); + final ModelPart modelPart; + if ( collectionPart instanceof EntityAssociationMapping ) { + modelPart = ( (EntityAssociationMapping) collectionPart ).getKeyTargetMatchPart(); + } + else { + modelPart = collectionPart; + } + final List arguments = new ArrayList<>( 1 ); final NavigablePath navigablePath = pluralPartPath.getNavigablePath(); - collectionPart.forEachSelectable( + final int jdbcTypeCount = modelPart.getJdbcTypeCount(); + final List tupleElements; + if ( jdbcTypeCount == 1 ) { + tupleElements = arguments; + } + else { + tupleElements = new ArrayList<>( jdbcTypeCount ); + } + modelPart.forEachSelectable( (selectionIndex, selectionMapping) -> { - arguments.add( + tupleElements.add( new ColumnReference( - tableGroup.getTableReference( navigablePath, selectionMapping.getContainingTableExpression() ), + tableGroup.getTableReference( + navigablePath, + selectionMapping.getContainingTableExpression() + ), selectionMapping, creationContext.getSessionFactory() ) ); } ); + if ( jdbcTypeCount != 1 ) { + arguments.add( new SqlTuple( tupleElements, modelPart ) ); + } final Expression expression = new SelfRenderingAggregateFunctionSqlAstExpression( functionDescriptor.getName(), functionDescriptor::render, - arguments, + (List) (List) arguments, null, - (AllowableFunctionReturnType) collectionPart.getJdbcMappings().get( 0 ), - collectionPart + (AllowableFunctionReturnType) modelPart.getJdbcMappings().get( 0 ), + modelPart ); subQuerySpec.getSelectClause().addSqlSelection( new SqlSelectionImpl( 1, 0, expression ) ); diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/sql/internal/EntityValuedPathInterpretation.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/sql/internal/EntityValuedPathInterpretation.java index 5e73c47282..3c1ddaffaf 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/sql/internal/EntityValuedPathInterpretation.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/sql/internal/EntityValuedPathInterpretation.java @@ -85,7 +85,7 @@ public class EntityValuedPathInterpretation extends AbstractSqmPathInterpreta final EntityIdentifierMapping identifierMapping = entityMappingType.getIdentifierMapping(); final EntityDiscriminatorMapping discriminatorMapping = entityMappingType.getDiscriminatorMapping(); final List expressions = new ArrayList<>( - mapping.getJdbcTypeCount() + identifierMapping.getJdbcTypeCount() + entityMappingType.getJdbcTypeCount() + identifierMapping.getJdbcTypeCount() + ( discriminatorMapping == null ? 0 : 1 ) ); final SelectableConsumer selectableConsumer = (selectionIndex, selectableMapping) -> { @@ -111,8 +111,8 @@ public class EntityValuedPathInterpretation extends AbstractSqmPathInterpreta if ( discriminatorMapping != null ) { discriminatorMapping.forEachSelectable( selectableConsumer ); } - mapping.forEachSelectable( selectableConsumer ); - sqlExpression = new SqlTuple( expressions, mapping ); + entityMappingType.forEachSelectable( selectableConsumer ); + sqlExpression = new SqlTuple( expressions, entityMappingType ); } else if ( mapping instanceof EntityAssociationMapping ) { final EntityAssociationMapping associationMapping = (EntityAssociationMapping) mapping; diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/AbstractSqmSimplePath.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/AbstractSqmSimplePath.java index 2dc40f401b..de680e515d 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/AbstractSqmSimplePath.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/AbstractSqmSimplePath.java @@ -14,7 +14,6 @@ import org.hibernate.query.sqm.SqmPathSource; * @author Steve Ebersole */ public abstract class AbstractSqmSimplePath extends AbstractSqmPath implements SqmSimplePath { - private final NavigablePath navigablePath; @SuppressWarnings("WeakerAccess") public AbstractSqmSimplePath( @@ -32,17 +31,11 @@ public abstract class AbstractSqmSimplePath extends AbstractSqmPath implem SqmPath lhs, String explicitAlias, NodeBuilder nodeBuilder) { - super( referencedPathSource, lhs, nodeBuilder ); - this.navigablePath = navigablePath; + super( navigablePath, referencedPathSource, lhs, nodeBuilder ); setExplicitAlias( explicitAlias ); } - @Override - public NavigablePath getNavigablePath() { - return navigablePath; - } - @Override public void appendHqlString(StringBuilder sb) { if ( getLhs() != null ) { diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/AbstractSqmSpecificPluralPartPath.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/AbstractSqmSpecificPluralPartPath.java index 40172a42d7..f30ad4b8a7 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/AbstractSqmSpecificPluralPartPath.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/AbstractSqmSpecificPluralPartPath.java @@ -16,7 +16,6 @@ import org.hibernate.query.PathException; * @author Steve Ebersole */ public abstract class AbstractSqmSpecificPluralPartPath extends AbstractSqmPath implements SqmPath { - private final NavigablePath navigablePath; private final SqmPath pluralDomainPath; private final PluralPersistentAttribute pluralAttribute; @@ -33,7 +32,6 @@ public abstract class AbstractSqmSpecificPluralPartPath extends AbstractSqmPa pluralDomainPath, pluralDomainPath.nodeBuilder() ); - this.navigablePath = navigablePath; this.pluralDomainPath = pluralDomainPath; this.pluralAttribute = referencedAttribute; } @@ -48,11 +46,6 @@ public abstract class AbstractSqmSpecificPluralPartPath extends AbstractSqmPa return pluralAttribute; } - @Override - public NavigablePath getNavigablePath() { - return navigablePath; - } - @Override public SqmPath getLhs() { return pluralDomainPath; diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmCorrelatedRoot.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmCorrelatedRoot.java index 9cf91922de..6d30fcc1c9 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmCorrelatedRoot.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmCorrelatedRoot.java @@ -17,7 +17,12 @@ public class SqmCorrelatedRoot extends SqmRoot implements SqmPathWrapper correlationParent; public SqmCorrelatedRoot(SqmRoot correlationParent) { - super( correlationParent.getReferencedPathSource(), null, correlationParent.nodeBuilder() ); + super( + correlationParent.getNavigablePath(), + correlationParent.getReferencedPathSource(), + null, + correlationParent.nodeBuilder() + ); this.correlationParent = correlationParent; } diff --git a/hibernate-core/src/main/java/org/hibernate/sql/ast/spi/AbstractSqlAstTranslator.java b/hibernate-core/src/main/java/org/hibernate/sql/ast/spi/AbstractSqlAstTranslator.java index 2b914ef0ed..a01d7962a1 100644 --- a/hibernate-core/src/main/java/org/hibernate/sql/ast/spi/AbstractSqlAstTranslator.java +++ b/hibernate-core/src/main/java/org/hibernate/sql/ast/spi/AbstractSqlAstTranslator.java @@ -24,6 +24,7 @@ import java.util.function.Function; import org.hibernate.LockMode; import org.hibernate.QueryException; import org.hibernate.dialect.RowLockStrategy; +import org.hibernate.dialect.SelectItemReferenceStrategy; import org.hibernate.metamodel.mapping.EntityAssociationMapping; import org.hibernate.metamodel.mapping.ModelPart; import org.hibernate.persister.entity.EntityPersister; @@ -1480,7 +1481,7 @@ public abstract class AbstractSqlAstTranslator implemen visitSelectClause( querySpec.getSelectClause() ); visitFromClause( querySpec.getFromClause() ); visitWhereClause( querySpec ); - visitGroupByClause( querySpec, dialect.supportsSelectAliasInGroupByClause() ); + visitGroupByClause( querySpec, dialect.getGroupBySelectItemReferenceStrategy() ); visitHavingClause( querySpec ); visitOrderBy( querySpec.getSortSpecifications() ); visitOffsetFetchClause( querySpec ); @@ -1556,13 +1557,38 @@ public abstract class AbstractSqlAstTranslator implemen return expression; } - protected final void visitGroupByClause(QuerySpec querySpec, boolean supportsSelectAliases) { + protected Expression resolveExpressionToAlias(Expression expression) { + int index = -1; + if ( expression instanceof SqlSelectionExpression ) { + index = ( (SqlSelectionExpression) expression ).getSelection().getValuesArrayPosition(); + } + else if ( expression instanceof SqmPathInterpretation ) { + final Expression sqlExpression = ( (SqmPathInterpretation) expression ).getSqlExpression(); + if ( sqlExpression instanceof SqlSelectionExpression ) { + index = ( (SqlSelectionExpression) sqlExpression ).getSelection().getValuesArrayPosition(); + } + } + if ( index == -1 ) { + return expression; + } + return new ColumnReference( + (String) null, + "c" + index, + false, + null, + null, + expression.getExpressionType().getJdbcMappings().get( 0 ), + sessionFactory + ); + } + + protected final void visitGroupByClause(QuerySpec querySpec, SelectItemReferenceStrategy referenceStrategy) { final List partitionExpressions = querySpec.getGroupByClauseExpressions(); if ( !partitionExpressions.isEmpty() ) { try { clauseStack.push( Clause.GROUP ); appendSql( " group by " ); - visitPartitionExpressions( partitionExpressions, supportsSelectAliases ); + visitPartitionExpressions( partitionExpressions, referenceStrategy ); } finally { clauseStack.pop(); @@ -1575,7 +1601,7 @@ public abstract class AbstractSqlAstTranslator implemen try { clauseStack.push( Clause.PARTITION ); appendSql( "partition by " ); - visitPartitionExpressions( partitionExpressions, false ); + visitPartitionExpressions( partitionExpressions, SelectItemReferenceStrategy.EXPRESSION ); } finally { clauseStack.pop(); @@ -1585,13 +1611,21 @@ public abstract class AbstractSqlAstTranslator implemen protected final void visitPartitionExpressions( List partitionExpressions, - boolean supportsSelectAliases) { - if ( supportsSelectAliases ) { - visitPartitionExpressions( partitionExpressions, expression -> expression ); - } - else { - visitPartitionExpressions( partitionExpressions, expression -> resolveAliasedExpression( expression ) ); + SelectItemReferenceStrategy referenceStrategy) { + final Function resolveAliasExpression; + switch ( referenceStrategy ) { + case POSITION: + resolveAliasExpression = Function.identity(); + break; + case ALIAS: + resolveAliasExpression = this::resolveExpressionToAlias; + break; + case EXPRESSION: + default: + resolveAliasExpression = this::resolveAliasedExpression; + break; } + visitPartitionExpressions( partitionExpressions, resolveAliasExpression ); } protected final void visitPartitionExpressions( @@ -2862,7 +2896,7 @@ public abstract class AbstractSqlAstTranslator implemen protected void visitSqlSelections(SelectClause selectClause) { final List sqlSelections = selectClause.getSqlSelections(); final int size = sqlSelections.size(); - if ( needsSelectAliases ) { + if ( needsSelectAliases || needsSelectAliasesForGroupOrOrderByClause() ) { String separator = NO_SEPARATOR; for ( int i = 0; i < size; i++ ) { final SqlSelection sqlSelection = sqlSelections.get( i ); @@ -2887,6 +2921,40 @@ public abstract class AbstractSqlAstTranslator implemen } } + private boolean needsSelectAliasesForGroupOrOrderByClause() { + if ( dialect.getGroupBySelectItemReferenceStrategy() == SelectItemReferenceStrategy.ALIAS ) { + final QuerySpec querySpec = (QuerySpec) getQueryPartStack().getCurrent(); + for ( Expression groupByClauseExpression : querySpec.getGroupByClauseExpressions() ) { + if ( isSelectItemReference( groupByClauseExpression ) ) { + return true; + } + } + if ( querySpec.hasSortSpecifications() ) { + for ( SortSpecification sortSpecification : querySpec.getSortSpecifications() ) { + if ( isSelectItemReference( sortSpecification.getSortExpression() ) ) { + return true; + } + } + } + } + return false; + } + + protected final boolean isSelectItemReference(Expression expression) { + final SqlTuple sqlTuple = SqlTupleContainer.getSqlTuple( expression ); + if ( sqlTuple != null ) { + for ( Expression e : sqlTuple.getExpressions() ) { + if ( e instanceof SqlSelectionExpression ) { + return true; + } + } + } + else if ( expression instanceof SqlSelectionExpression ) { + return true; + } + return false; + } + protected void renderRowNumberingSelectItems(SelectClause selectClause, QueryPart queryPart) { final FetchClauseType fetchClauseType = getFetchClauseTypeForRowNumbering( queryPart ); if ( fetchClauseType != null ) { @@ -4329,7 +4397,7 @@ public abstract class AbstractSqlAstTranslator implemen .isEmpty() || subQuery.getHavingClauseRestrictions() != null ) { // If we have a group by or having clause, we have to move the tuple comparison emulation to the HAVING clause visitWhereClause( subQuery ); - visitGroupByClause( subQuery, false ); + visitGroupByClause( subQuery, SelectItemReferenceStrategy.EXPRESSION ); appendSql( " having " ); clauseStack.push( Clause.HAVING ); @@ -4421,7 +4489,7 @@ public abstract class AbstractSqlAstTranslator implemen visitSelectClause( subQuery.getSelectClause() ); visitFromClause( subQuery.getFromClause() ); visitWhereClause( subQuery ); - visitGroupByClause( subQuery, dialect.supportsSelectAliasInGroupByClause() ); + visitGroupByClause( subQuery, dialect.getGroupBySelectItemReferenceStrategy() ); visitHavingClause( subQuery ); appendSql( " order by " ); diff --git a/hibernate-core/src/test/java/org/hibernate/query/GroupByAliasTest.java b/hibernate-core/src/test/java/org/hibernate/query/GroupByAliasTest.java index a4199263c0..e54353212f 100644 --- a/hibernate-core/src/test/java/org/hibernate/query/GroupByAliasTest.java +++ b/hibernate-core/src/test/java/org/hibernate/query/GroupByAliasTest.java @@ -7,8 +7,7 @@ package org.hibernate.query; import org.hibernate.jpa.test.BaseEntityManagerFunctionalTestCase; -import org.hibernate.testing.DialectChecks; -import org.hibernate.testing.RequiresDialectFeature; +import org.hibernate.testing.TestForIssue; import org.hibernate.testing.jdbc.SQLStatementInterceptor; import org.junit.Test; @@ -30,7 +29,7 @@ import static org.junit.Assert.assertNotNull; * @author Jan-Willem Gmelig Meyling * @author Sayra Ranjha */ -@RequiresDialectFeature(value = DialectChecks.SupportsSelectAliasInGroupByClause.class, jiraKey = "HHH-9301") +@TestForIssue( jiraKey = "HHH-9301" ) public class GroupByAliasTest extends BaseEntityManagerFunctionalTestCase { public static final int MAX_COUNT = 15; diff --git a/hibernate-testing/src/main/java/org/hibernate/testing/DialectChecks.java b/hibernate-testing/src/main/java/org/hibernate/testing/DialectChecks.java index 1ad4d7c3b2..a865aa1d76 100644 --- a/hibernate-testing/src/main/java/org/hibernate/testing/DialectChecks.java +++ b/hibernate-testing/src/main/java/org/hibernate/testing/DialectChecks.java @@ -281,12 +281,6 @@ abstract public class DialectChecks { } } - public static class SupportsSelectAliasInGroupByClause implements DialectCheck { - public boolean isMatch(Dialect dialect) { - return dialect.supportsSelectAliasInGroupByClause(); - } - } - public static class SupportsNClob implements DialectCheck { @Override public boolean isMatch(Dialect dialect) {