Fix some issues with collection-path related HQL functions minindex/maxindex/minelement/maxelement/elements/indices and group by item rendering problems

This commit is contained in:
Christian Beikov 2021-08-03 09:56:49 +02:00
parent 4acdf1ab3e
commit ca22db14bb
23 changed files with 378 additions and 116 deletions

View File

@ -2006,7 +2006,7 @@ public class HQLTest extends BaseEntityManagerFunctionalTestCase {
"where p.person = ph.person ", Object[].class ) "where p.person = ph.person ", Object[].class )
.getResultList(); .getResultList();
//end::hql-relational-comparisons-example[] //end::hql-relational-comparisons-example[]
assertEquals(3, phonePayments.size()); assertEquals(2, phonePayments.size());
}); });
} }

View File

@ -197,6 +197,7 @@ HOUR : [hH] [oO] [uU] [rR];
IFNULL : [iI] [fF] [nN] [uU] [lL] [lL]; IFNULL : [iI] [fF] [nN] [uU] [lL] [lL];
IN : [iI] [nN]; IN : [iI] [nN];
INDEX : [iI] [nN] [dD] [eE] [xX]; INDEX : [iI] [nN] [dD] [eE] [xX];
INDICES : [iI] [nN] [dD] [iI] [cC] [eE] [sS];
INNER : [iI] [nN] [nN] [eE] [rR]; INNER : [iI] [nN] [nN] [eE] [rR];
INSERT : [iI] [nN] [sS] [eE] [rR] [tT]; INSERT : [iI] [nN] [sS] [eE] [rR] [tT];
INSTANT : [iI] [nN] [sS] [tT] [aA] [nN] [tT]; INSTANT : [iI] [nN] [sS] [tT] [aA] [nN] [tT];

View File

@ -255,6 +255,7 @@ pathContinuation
* *
* * TREAT( path ) * * TREAT( path )
* * ELEMENTS( path ) * * ELEMENTS( path )
* * INDICES( path )
* * VALUE( path ) * * VALUE( path )
* * KEY( path ) * * KEY( path )
* * path[ selector ] * * path[ selector ]
@ -262,6 +263,7 @@ pathContinuation
syntacticDomainPath syntacticDomainPath
: treatedNavigablePath : treatedNavigablePath
| collectionElementNavigablePath | collectionElementNavigablePath
| collectionIndexNavigablePath
| mapKeyNavigablePath | mapKeyNavigablePath
| dotIdentifierSequence indexedPathAccessFragment | dotIdentifierSequence indexedPathAccessFragment
; ;
@ -289,6 +291,10 @@ collectionElementNavigablePath
: (VALUE | ELEMENTS) LEFT_PAREN path RIGHT_PAREN pathContinuation? : (VALUE | ELEMENTS) LEFT_PAREN path RIGHT_PAREN pathContinuation?
; ;
collectionIndexNavigablePath
: INDICES LEFT_PAREN path RIGHT_PAREN
;
mapKeyNavigablePath mapKeyNavigablePath
: KEY LEFT_PAREN path RIGHT_PAREN pathContinuation? : KEY LEFT_PAREN path RIGHT_PAREN pathContinuation?
; ;
@ -394,19 +400,20 @@ whereClause
predicate predicate
//highest to lowest precedence //highest to lowest precedence
: LEFT_PAREN predicate RIGHT_PAREN # GroupedPredicate : LEFT_PAREN predicate RIGHT_PAREN # GroupedPredicate
| expression IS (NOT)? NULL # IsNullPredicate | expression IS (NOT)? NULL # IsNullPredicate
| expression IS (NOT)? EMPTY # IsEmptyPredicate | expression IS (NOT)? EMPTY # IsEmptyPredicate
| expression (NOT)? IN inList # InPredicate | expression (NOT)? IN inList # InPredicate
| expression (NOT)? BETWEEN expression AND expression # BetweenPredicate | expression (NOT)? BETWEEN expression AND expression # BetweenPredicate
| expression (NOT)? LIKE expression (likeEscape)? # LikePredicate | expression (NOT)? LIKE expression (likeEscape)? # LikePredicate
| expression comparisonOperator expression # ComparisonPredicate | expression comparisonOperator expression # ComparisonPredicate
| EXISTS expression # ExistsPredicate | EXISTS (ELEMENTS|INDICES) LEFT_PAREN dotIdentifierSequence RIGHT_PAREN # ExistsCollectionPartPredicate
| expression (NOT)? MEMBER OF path # MemberOfPredicate | EXISTS expression # ExistsPredicate
| NOT predicate # NegatedPredicate | expression (NOT)? MEMBER OF path # MemberOfPredicate
| predicate AND predicate # AndPredicate | NOT predicate # NegatedPredicate
| predicate OR predicate # OrPredicate | predicate AND predicate # AndPredicate
| expression # BooleanExpressionPredicate | predicate OR predicate # OrPredicate
| expression # BooleanExpressionPredicate
; ;
comparisonOperator comparisonOperator
@ -421,10 +428,10 @@ comparisonOperator
; ;
inList inList
: ELEMENTS? LEFT_PAREN dotIdentifierSequence RIGHT_PAREN # PersistentCollectionReferenceInList : (ELEMENTS|INDICES) LEFT_PAREN dotIdentifierSequence RIGHT_PAREN # PersistentCollectionReferenceInList
| LEFT_PAREN expression (COMMA expression)* RIGHT_PAREN # ExplicitTupleInList | LEFT_PAREN expression (COMMA expression)* RIGHT_PAREN # ExplicitTupleInList
| LEFT_PAREN subQuery RIGHT_PAREN # SubQueryInList | LEFT_PAREN subQuery RIGHT_PAREN # SubQueryInList
| parameter # ParamInList | parameter # ParamInList
; ;
likeEscape likeEscape
@ -713,11 +720,13 @@ countFunction
everyFunction everyFunction
: (EVERY|ALL) LEFT_PAREN predicate RIGHT_PAREN filterClause? : (EVERY|ALL) LEFT_PAREN predicate RIGHT_PAREN filterClause?
| (EVERY|ALL) LEFT_PAREN subQuery RIGHT_PAREN | (EVERY|ALL) LEFT_PAREN subQuery RIGHT_PAREN
| (EVERY|ALL) (ELEMENTS|INDICES) LEFT_PAREN dotIdentifierSequence RIGHT_PAREN
; ;
anyFunction anyFunction
: (ANY|SOME) LEFT_PAREN predicate RIGHT_PAREN filterClause? : (ANY|SOME) LEFT_PAREN predicate RIGHT_PAREN filterClause?
| (ANY|SOME) LEFT_PAREN subQuery RIGHT_PAREN | (ANY|SOME) LEFT_PAREN subQuery RIGHT_PAREN
| (ANY|SOME) (ELEMENTS|INDICES) LEFT_PAREN dotIdentifierSequence RIGHT_PAREN
; ;
filterClause filterClause
@ -1170,6 +1179,7 @@ identifier
| IFNULL | IFNULL
| IN | IN
| INDEX | INDEX
| INDICES
| INNER | INNER
| INSERT | INSERT
| INSTANT | INSTANT

View File

@ -3810,8 +3810,8 @@ public abstract class Dialect implements ConversionContext {
return null; return null;
} }
public boolean supportsSelectAliasInGroupByClause() { public SelectItemReferenceStrategy getGroupBySelectItemReferenceStrategy() {
return false; return SelectItemReferenceStrategy.EXPRESSION;
} }
public SizeStrategy getSizeStrategy() { public SizeStrategy getSizeStrategy() {

View File

@ -429,8 +429,8 @@ public class H2Dialect extends Dialect {
} }
@Override @Override
public boolean supportsSelectAliasInGroupByClause() { public SelectItemReferenceStrategy getGroupBySelectItemReferenceStrategy() {
return true; return SelectItemReferenceStrategy.ALIAS;
} }
@Override @Override

View File

@ -499,8 +499,8 @@ public class MySQLDialect extends Dialect {
} }
@Override @Override
public boolean supportsSelectAliasInGroupByClause() { public SelectItemReferenceStrategy getGroupBySelectItemReferenceStrategy() {
return true; return SelectItemReferenceStrategy.POSITION;
} }
@Override @Override

View File

@ -646,8 +646,8 @@ public class PostgreSQLDialect extends Dialect {
} }
@Override @Override
public boolean supportsSelectAliasInGroupByClause() { public SelectItemReferenceStrategy getGroupBySelectItemReferenceStrategy() {
return true; return SelectItemReferenceStrategy.POSITION;
} }
@Override @Override

View File

@ -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 <http://www.gnu.org/licenses/lgpl-2.1.html>.
*/
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;
}

View File

@ -29,7 +29,6 @@ class ListAttributeImpl<X, E> extends AbstractPluralAttribute<X, List<E>, E> imp
//noinspection unchecked //noinspection unchecked
this.indexPathSource = (SqmPathSource<Integer>) SqmMappingModelHelper.resolveSqmKeyPathSource( this.indexPathSource = (SqmPathSource<Integer>) SqmMappingModelHelper.resolveSqmKeyPathSource(
getName(),
builder.getListIndexOrMapKeyType(), builder.getListIndexOrMapKeyType(),
BindableType.PLURAL_ATTRIBUTE BindableType.PLURAL_ATTRIBUTE
); );

View File

@ -30,7 +30,6 @@ class MapAttributeImpl<X, K, V> extends AbstractPluralAttribute<X, Map<K, V>, V>
super( xceBuilder, metadataContext ); super( xceBuilder, metadataContext );
this.keyPathSource = SqmMappingModelHelper.resolveSqmKeyPathSource( this.keyPathSource = SqmMappingModelHelper.resolveSqmKeyPathSource(
CollectionPart.Nature.INDEX.getName(),
xceBuilder.getListIndexOrMapKeyType(), xceBuilder.getListIndexOrMapKeyType(),
BindableType.PLURAL_ATTRIBUTE BindableType.PLURAL_ATTRIBUTE
); );

View File

@ -747,6 +747,10 @@ public class MappingMetamodelImpl implements MappingMetamodel, MetamodelImplemen
return (MappingModelExpressable) sqmExpressable; return (MappingModelExpressable) sqmExpressable;
} }
if ( sqmExpressable instanceof EntityDomainType<?> ) {
return getEntityDescriptor( ( (EntityDomainType<?>) sqmExpressable ).getHibernateEntityName() );
}
throw new UnsupportedOperationException( "Cannot determine proper mapping model expressable for " + sqmExpressable ); throw new UnsupportedOperationException( "Cannot determine proper mapping model expressable for " + sqmExpressable );
} }

View File

@ -1693,8 +1693,8 @@ public class SemanticQueryBuilder<R> extends HqlParserBaseVisitor<Object> implem
HqlParser.JpaCollectionJoinContext ctx, HqlParser.JpaCollectionJoinContext ctx,
SqmRoot<?> sqmRoot) { SqmRoot<?> sqmRoot) {
final HqlParser.IdentificationVariableDefContext identificationVariableDefContext; final HqlParser.IdentificationVariableDefContext identificationVariableDefContext;
if ( ctx.getChildCount() > 1 ) { if ( ctx.getChildCount() > 5 ) {
identificationVariableDefContext = (HqlParser.IdentificationVariableDefContext) ctx.getChild( 1 ); identificationVariableDefContext = (HqlParser.IdentificationVariableDefContext) ctx.getChild( 5 );
} }
else { else {
identificationVariableDefContext = null; identificationVariableDefContext = null;
@ -2047,13 +2047,35 @@ public class SemanticQueryBuilder<R> extends HqlParserBaseVisitor<Object> implem
creationContext.getNodeBuilder() 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 { else {
// todo : handle PersistentCollectionReferenceInList labeled branch
throw new ParsingException( "Unexpected IN predicate type [" + ctx.getClass().getSimpleName() + "] : " + ctx.getText() ); throw new ParsingException( "Unexpected IN predicate type [" + ctx.getClass().getSimpleName() + "] : " + ctx.getText() );
} }
} }
@Override
public SqmPredicate visitExistsCollectionPartPredicate(HqlParser.ExistsCollectionPartPredicateContext ctx) {
final SqmSubQuery<Object> subQuery = createCollectionReferenceSubQuery(
(HqlParser.DotIdentifierSequenceContext) ctx.getChild( 3 ),
null
);
return new SqmExistsPredicate( subQuery, creationContext.getNodeBuilder() );
}
@Override @Override
public SqmPredicate visitExistsPredicate(HqlParser.ExistsPredicateContext ctx) { public SqmPredicate visitExistsPredicate(HqlParser.ExistsPredicateContext ctx) {
final SqmExpression<?> expression = (SqmExpression<?>) ctx.getChild( 1 ).accept( this ); final SqmExpression<?> expression = (SqmExpression<?>) ctx.getChild( 1 ).accept( this );
@ -3764,23 +3786,35 @@ public class SemanticQueryBuilder<R> extends HqlParserBaseVisitor<Object> implem
final SqmPredicate filterExpression = getFilterExpression( ctx ); final SqmPredicate filterExpression = getFilterExpression( ctx );
final ParseTree argumentChild = ctx.getChild( 2 ); final ParseTree argumentChild = ctx.getChild( 2 );
if ( argumentChild instanceof HqlParser.SubQueryContext ) { if ( argumentChild instanceof HqlParser.SubQueryContext ) {
SqmSubQuery<?> subquery = (SqmSubQuery<?>) argumentChild.accept(this); final SqmSubQuery<?> subquery = (SqmSubQuery<?>) argumentChild.accept( this );
return new SqmEvery<>( subquery, creationContext.getNodeBuilder() ); 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 ); return getFunctionDescriptor( "every" ).generateAggregateSqmExpression(
singletonList( argument ),
if ( argument instanceof SqmSubQuery<?> && ctx.getChild( ctx.getChildCount() - 1) instanceof HqlParser.FilterClauseContext ) { filterExpression,
throw new SemanticException( "Quantified expression cannot have a filter clause!" ); 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 @Override
@ -3788,23 +3822,87 @@ public class SemanticQueryBuilder<R> extends HqlParserBaseVisitor<Object> implem
final SqmPredicate filterExpression = getFilterExpression( ctx ); final SqmPredicate filterExpression = getFilterExpression( ctx );
final ParseTree argumentChild = ctx.getChild( 2 ); final ParseTree argumentChild = ctx.getChild( 2 );
if ( argumentChild instanceof HqlParser.SubQueryContext ) { if ( argumentChild instanceof HqlParser.SubQueryContext ) {
SqmSubQuery<?> subquery = (SqmSubQuery<?>) argumentChild.accept(this); final SqmSubQuery<?> subquery = (SqmSubQuery<?>) argumentChild.accept( this );
return new SqmAny<>( subquery, creationContext.getNodeBuilder() ); 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 ); return getFunctionDescriptor( "any" ).generateAggregateSqmExpression(
singletonList( argument ),
if ( argument instanceof SqmSubQuery<?> && ctx.getChild( ctx.getChildCount() - 1) instanceof HqlParser.FilterClauseContext ) { filterExpression,
throw new SemanticException( "Quantified expression cannot have a filter clause!" ); 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( private <X> SqmSubQuery<X> createCollectionReferenceSubQuery(
singletonList( argument ), HqlParser.DotIdentifierSequenceContext pathCtx,
filterExpression, TerminalNode collectionReferenceCtx) {
resolveExpressableTypeBasic( Boolean.class ), final SqmPath<?> pluralAttributePath = consumeDomainPath( pathCtx );
creationContext.getQueryEngine(), final SqmPathSource<?> referencedPathSource = pluralAttributePath.getReferencedPathSource();
creationContext.getJpaMetamodel().getTypeConfiguration()
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<Integer> 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<X>) subQuery;
} }
@Override @Override
@ -4216,6 +4314,9 @@ public class SemanticQueryBuilder<R> extends HqlParserBaseVisitor<Object> implem
else if ( firstChild instanceof HqlParser.CollectionElementNavigablePathContext ) { else if ( firstChild instanceof HqlParser.CollectionElementNavigablePathContext ) {
return visitCollectionElementNavigablePath( (HqlParser.CollectionElementNavigablePathContext) firstChild ); return visitCollectionElementNavigablePath( (HqlParser.CollectionElementNavigablePathContext) firstChild );
} }
else if ( firstChild instanceof HqlParser.CollectionIndexNavigablePathContext ) {
return visitCollectionIndexNavigablePath( (HqlParser.CollectionIndexNavigablePathContext) firstChild );
}
else if ( firstChild instanceof HqlParser.MapKeyNavigablePathContext ) { else if ( firstChild instanceof HqlParser.MapKeyNavigablePathContext ) {
return visitMapKeyNavigablePath( (HqlParser.MapKeyNavigablePathContext) firstChild ); return visitMapKeyNavigablePath( (HqlParser.MapKeyNavigablePathContext) firstChild );
} }
@ -4384,6 +4485,30 @@ public class SemanticQueryBuilder<R> extends HqlParserBaseVisitor<Object> 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 @Override
@SuppressWarnings({ "rawtypes" }) @SuppressWarnings({ "rawtypes" })
public SqmPath visitMapKeyNavigablePath(HqlParser.MapKeyNavigablePathContext ctx) { public SqmPath visitMapKeyNavigablePath(HqlParser.MapKeyNavigablePathContext ctx) {

View File

@ -19,7 +19,7 @@ public class StrictJpaComplianceViolation extends SemanticException {
ALIASED_FETCH_JOIN( "aliased fetch join" ), ALIASED_FETCH_JOIN( "aliased fetch join" ),
UNMAPPED_POLYMORPHISM( "unmapped polymorphic reference" ), UNMAPPED_POLYMORPHISM( "unmapped polymorphic reference" ),
FUNCTION_CALL( "improper non-standard function call" ), 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" ), 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)" ), 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" ), INDEXED_ELEMENT_REFERENCE( "use of HQL indexed element reference syntax" ),

View File

@ -11,9 +11,11 @@ import javax.persistence.metamodel.Bindable;
import org.hibernate.NotYetImplementedFor6Exception; import org.hibernate.NotYetImplementedFor6Exception;
import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.metamodel.mapping.CollectionPart;
import org.hibernate.metamodel.mapping.EntityMappingType; import org.hibernate.metamodel.mapping.EntityMappingType;
import org.hibernate.metamodel.mapping.MappingModelExpressable; import org.hibernate.metamodel.mapping.MappingModelExpressable;
import org.hibernate.metamodel.mapping.ModelPart; 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.AnyMappingDomainType;
import org.hibernate.metamodel.model.domain.BasicDomainType; import org.hibernate.metamodel.model.domain.BasicDomainType;
import org.hibernate.metamodel.model.domain.DomainType; import org.hibernate.metamodel.model.domain.DomainType;
@ -61,11 +63,13 @@ public class SqmMappingModelHelper {
} }
public static <J> SqmPathSource<J> resolveSqmKeyPathSource( public static <J> SqmPathSource<J> resolveSqmKeyPathSource(
String name,
DomainType<J> valueDomainType, DomainType<J> valueDomainType,
Bindable.BindableType jpaBindableType) { Bindable.BindableType jpaBindableType) {
// todo (6.0): the key path source must create a special path for the key return resolveSqmPathSource(
return resolveSqmPathSource( name, valueDomainType, jpaBindableType ); CollectionPart.Nature.INDEX.getName(),
valueDomainType,
jpaBindableType
);
} }
public static <J> SqmPathSource<J> resolveSqmPathSource( public static <J> SqmPathSource<J> resolveSqmPathSource(
@ -149,7 +153,11 @@ public class SqmMappingModelHelper {
// Plural path parts are not joined and thus also have no table group // Plural path parts are not joined and thus also have no table group
if ( sqmPath instanceof AbstractSqmSpecificPluralPartPath<?> ) { if ( sqmPath instanceof AbstractSqmSpecificPluralPartPath<?> ) {
final TableGroup lhsTableGroup = tableGroupLocator.apply( sqmPath.getLhs().getLhs().getNavigablePath() ); 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() ); final TableGroup lhsTableGroup = tableGroupLocator.apply( sqmPath.getLhs().getNavigablePath() );

View File

@ -20,6 +20,7 @@ import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.engine.spi.SharedSessionContractImplementor; import org.hibernate.engine.spi.SharedSessionContractImplementor;
import org.hibernate.metamodel.MappingMetamodel; import org.hibernate.metamodel.MappingMetamodel;
import org.hibernate.metamodel.mapping.Bindable; import org.hibernate.metamodel.mapping.Bindable;
import org.hibernate.metamodel.mapping.CollectionPart;
import org.hibernate.metamodel.mapping.ConvertibleModelPart; import org.hibernate.metamodel.mapping.ConvertibleModelPart;
import org.hibernate.metamodel.mapping.EntityIdentifierMapping; import org.hibernate.metamodel.mapping.EntityIdentifierMapping;
import org.hibernate.metamodel.mapping.EntityMappingType; import org.hibernate.metamodel.mapping.EntityMappingType;
@ -328,6 +329,9 @@ public class SqmUtil {
if ( parameterType == null ) { if ( parameterType == null ) {
throw new SqlTreeCreationException( "Unable to interpret mapping-model type for Query parameter : " + domainParam ); 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 ) { if ( parameterType instanceof EntityIdentifierMapping ) {
final EntityIdentifierMapping identifierMapping = (EntityIdentifierMapping) parameterType; final EntityIdentifierMapping identifierMapping = (EntityIdentifierMapping) parameterType;
@ -336,6 +340,14 @@ public class SqmUtil {
bindValue = identifierMapping.getIdentifier( bindValue, session ); 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 ) { else if ( parameterType instanceof ToOneAttributeMapping ) {
ToOneAttributeMapping association = (ToOneAttributeMapping) parameterType; ToOneAttributeMapping association = (ToOneAttributeMapping) parameterType;
bindValue = association.getForeignKeyDescriptor().getAssociationKeyFromSide( bindValue = association.getForeignKeyDescriptor().getAssociationKeyFromSide(

View File

@ -45,6 +45,7 @@ import org.hibernate.metamodel.MappingMetamodel;
import org.hibernate.metamodel.mapping.Association; import org.hibernate.metamodel.mapping.Association;
import org.hibernate.metamodel.mapping.AttributeMapping; import org.hibernate.metamodel.mapping.AttributeMapping;
import org.hibernate.metamodel.mapping.BasicValuedMapping; import org.hibernate.metamodel.mapping.BasicValuedMapping;
import org.hibernate.metamodel.mapping.Bindable;
import org.hibernate.metamodel.mapping.CollectionPart; import org.hibernate.metamodel.mapping.CollectionPart;
import org.hibernate.metamodel.mapping.ConvertibleModelPart; import org.hibernate.metamodel.mapping.ConvertibleModelPart;
import org.hibernate.metamodel.mapping.EmbeddableMappingType; 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.EntityAssociationMapping;
import org.hibernate.metamodel.mapping.EntityDiscriminatorMapping; import org.hibernate.metamodel.mapping.EntityDiscriminatorMapping;
import org.hibernate.metamodel.mapping.EntityIdentifierMapping; import org.hibernate.metamodel.mapping.EntityIdentifierMapping;
import org.hibernate.metamodel.mapping.EntityMappingType;
import org.hibernate.metamodel.mapping.EntityValuedModelPart; import org.hibernate.metamodel.mapping.EntityValuedModelPart;
import org.hibernate.metamodel.mapping.EntityVersionMapping; import org.hibernate.metamodel.mapping.EntityVersionMapping;
import org.hibernate.metamodel.mapping.ForeignKeyDescriptor; import org.hibernate.metamodel.mapping.ForeignKeyDescriptor;
@ -2747,17 +2749,19 @@ public abstract class BaseSqmToSqlAstConverter<T extends Statement> extends Base
MappingModelExpressable valueMapping, MappingModelExpressable valueMapping,
BiConsumer<Integer,JdbcParameter> jdbcParameterConsumer) { BiConsumer<Integer,JdbcParameter> jdbcParameterConsumer) {
sqmParameterMappingModelTypes.put( expression, valueMapping ); sqmParameterMappingModelTypes.put( expression, valueMapping );
final Bindable bindable;
if ( valueMapping instanceof Association ) { if ( valueMapping instanceof Association ) {
( (Association) valueMapping ).getForeignKeyDescriptor().forEachJdbcType( bindable = ( (Association) valueMapping ).getForeignKeyDescriptor();
(index, jdbcMapping) -> jdbcParameterConsumer.accept( index, new JdbcParameterImpl( jdbcMapping ) ) }
); else if ( valueMapping instanceof EntityMappingType ) {
bindable = ( (EntityMappingType) valueMapping ).getIdentifierMapping();
} }
else { else {
valueMapping.forEachJdbcType( bindable = valueMapping;
(index, jdbcMapping) -> jdbcParameterConsumer.accept( index, new JdbcParameterImpl( jdbcMapping ) )
);
} }
bindable.forEachJdbcType(
(index, jdbcMapping) -> jdbcParameterConsumer.accept( index, new JdbcParameterImpl( jdbcMapping ) )
);
} }
@Override @Override
@ -4098,26 +4102,47 @@ public abstract class BaseSqmToSqlAstConverter<T extends Statement> extends Base
final CollectionPart collectionPart = index final CollectionPart collectionPart = index
? mappingModelExpressable.getIndexDescriptor() ? mappingModelExpressable.getIndexDescriptor()
: mappingModelExpressable.getElementDescriptor(); : mappingModelExpressable.getElementDescriptor();
final List<SqlAstNode> arguments = new ArrayList<>( 1 ); final ModelPart modelPart;
if ( collectionPart instanceof EntityAssociationMapping ) {
modelPart = ( (EntityAssociationMapping) collectionPart ).getKeyTargetMatchPart();
}
else {
modelPart = collectionPart;
}
final List<Expression> arguments = new ArrayList<>( 1 );
final NavigablePath navigablePath = pluralPartPath.getNavigablePath(); final NavigablePath navigablePath = pluralPartPath.getNavigablePath();
collectionPart.forEachSelectable( final int jdbcTypeCount = modelPart.getJdbcTypeCount();
final List<Expression> tupleElements;
if ( jdbcTypeCount == 1 ) {
tupleElements = arguments;
}
else {
tupleElements = new ArrayList<>( jdbcTypeCount );
}
modelPart.forEachSelectable(
(selectionIndex, selectionMapping) -> { (selectionIndex, selectionMapping) -> {
arguments.add( tupleElements.add(
new ColumnReference( new ColumnReference(
tableGroup.getTableReference( navigablePath, selectionMapping.getContainingTableExpression() ), tableGroup.getTableReference(
navigablePath,
selectionMapping.getContainingTableExpression()
),
selectionMapping, selectionMapping,
creationContext.getSessionFactory() creationContext.getSessionFactory()
) )
); );
} }
); );
if ( jdbcTypeCount != 1 ) {
arguments.add( new SqlTuple( tupleElements, modelPart ) );
}
final Expression expression = new SelfRenderingAggregateFunctionSqlAstExpression( final Expression expression = new SelfRenderingAggregateFunctionSqlAstExpression(
functionDescriptor.getName(), functionDescriptor.getName(),
functionDescriptor::render, functionDescriptor::render,
arguments, (List<SqlAstNode>) (List<?>) arguments,
null, null,
(AllowableFunctionReturnType<?>) collectionPart.getJdbcMappings().get( 0 ), (AllowableFunctionReturnType<?>) modelPart.getJdbcMappings().get( 0 ),
collectionPart modelPart
); );
subQuerySpec.getSelectClause().addSqlSelection( new SqlSelectionImpl( 1, 0, expression ) ); subQuerySpec.getSelectClause().addSqlSelection( new SqlSelectionImpl( 1, 0, expression ) );

View File

@ -85,7 +85,7 @@ public class EntityValuedPathInterpretation<T> extends AbstractSqmPathInterpreta
final EntityIdentifierMapping identifierMapping = entityMappingType.getIdentifierMapping(); final EntityIdentifierMapping identifierMapping = entityMappingType.getIdentifierMapping();
final EntityDiscriminatorMapping discriminatorMapping = entityMappingType.getDiscriminatorMapping(); final EntityDiscriminatorMapping discriminatorMapping = entityMappingType.getDiscriminatorMapping();
final List<Expression> expressions = new ArrayList<>( final List<Expression> expressions = new ArrayList<>(
mapping.getJdbcTypeCount() + identifierMapping.getJdbcTypeCount() entityMappingType.getJdbcTypeCount() + identifierMapping.getJdbcTypeCount()
+ ( discriminatorMapping == null ? 0 : 1 ) + ( discriminatorMapping == null ? 0 : 1 )
); );
final SelectableConsumer selectableConsumer = (selectionIndex, selectableMapping) -> { final SelectableConsumer selectableConsumer = (selectionIndex, selectableMapping) -> {
@ -111,8 +111,8 @@ public class EntityValuedPathInterpretation<T> extends AbstractSqmPathInterpreta
if ( discriminatorMapping != null ) { if ( discriminatorMapping != null ) {
discriminatorMapping.forEachSelectable( selectableConsumer ); discriminatorMapping.forEachSelectable( selectableConsumer );
} }
mapping.forEachSelectable( selectableConsumer ); entityMappingType.forEachSelectable( selectableConsumer );
sqlExpression = new SqlTuple( expressions, mapping ); sqlExpression = new SqlTuple( expressions, entityMappingType );
} }
else if ( mapping instanceof EntityAssociationMapping ) { else if ( mapping instanceof EntityAssociationMapping ) {
final EntityAssociationMapping associationMapping = (EntityAssociationMapping) mapping; final EntityAssociationMapping associationMapping = (EntityAssociationMapping) mapping;

View File

@ -14,7 +14,6 @@ import org.hibernate.query.sqm.SqmPathSource;
* @author Steve Ebersole * @author Steve Ebersole
*/ */
public abstract class AbstractSqmSimplePath<T> extends AbstractSqmPath<T> implements SqmSimplePath<T> { public abstract class AbstractSqmSimplePath<T> extends AbstractSqmPath<T> implements SqmSimplePath<T> {
private final NavigablePath navigablePath;
@SuppressWarnings("WeakerAccess") @SuppressWarnings("WeakerAccess")
public AbstractSqmSimplePath( public AbstractSqmSimplePath(
@ -32,17 +31,11 @@ public abstract class AbstractSqmSimplePath<T> extends AbstractSqmPath<T> implem
SqmPath lhs, SqmPath lhs,
String explicitAlias, String explicitAlias,
NodeBuilder nodeBuilder) { NodeBuilder nodeBuilder) {
super( referencedPathSource, lhs, nodeBuilder ); super( navigablePath, referencedPathSource, lhs, nodeBuilder );
this.navigablePath = navigablePath;
setExplicitAlias( explicitAlias ); setExplicitAlias( explicitAlias );
} }
@Override
public NavigablePath getNavigablePath() {
return navigablePath;
}
@Override @Override
public void appendHqlString(StringBuilder sb) { public void appendHqlString(StringBuilder sb) {
if ( getLhs() != null ) { if ( getLhs() != null ) {

View File

@ -16,7 +16,6 @@ import org.hibernate.query.PathException;
* @author Steve Ebersole * @author Steve Ebersole
*/ */
public abstract class AbstractSqmSpecificPluralPartPath<T> extends AbstractSqmPath<T> implements SqmPath<T> { public abstract class AbstractSqmSpecificPluralPartPath<T> extends AbstractSqmPath<T> implements SqmPath<T> {
private final NavigablePath navigablePath;
private final SqmPath pluralDomainPath; private final SqmPath pluralDomainPath;
private final PluralPersistentAttribute<?,?,T> pluralAttribute; private final PluralPersistentAttribute<?,?,T> pluralAttribute;
@ -33,7 +32,6 @@ public abstract class AbstractSqmSpecificPluralPartPath<T> extends AbstractSqmPa
pluralDomainPath, pluralDomainPath,
pluralDomainPath.nodeBuilder() pluralDomainPath.nodeBuilder()
); );
this.navigablePath = navigablePath;
this.pluralDomainPath = pluralDomainPath; this.pluralDomainPath = pluralDomainPath;
this.pluralAttribute = referencedAttribute; this.pluralAttribute = referencedAttribute;
} }
@ -48,11 +46,6 @@ public abstract class AbstractSqmSpecificPluralPartPath<T> extends AbstractSqmPa
return pluralAttribute; return pluralAttribute;
} }
@Override
public NavigablePath getNavigablePath() {
return navigablePath;
}
@Override @Override
public SqmPath getLhs() { public SqmPath getLhs() {
return pluralDomainPath; return pluralDomainPath;

View File

@ -17,7 +17,12 @@ public class SqmCorrelatedRoot<T> extends SqmRoot<T> implements SqmPathWrapper<T
private final SqmRoot<T> correlationParent; private final SqmRoot<T> correlationParent;
public SqmCorrelatedRoot(SqmRoot<T> correlationParent) { public SqmCorrelatedRoot(SqmRoot<T> correlationParent) {
super( correlationParent.getReferencedPathSource(), null, correlationParent.nodeBuilder() ); super(
correlationParent.getNavigablePath(),
correlationParent.getReferencedPathSource(),
null,
correlationParent.nodeBuilder()
);
this.correlationParent = correlationParent; this.correlationParent = correlationParent;
} }

View File

@ -24,6 +24,7 @@ import java.util.function.Function;
import org.hibernate.LockMode; import org.hibernate.LockMode;
import org.hibernate.QueryException; import org.hibernate.QueryException;
import org.hibernate.dialect.RowLockStrategy; import org.hibernate.dialect.RowLockStrategy;
import org.hibernate.dialect.SelectItemReferenceStrategy;
import org.hibernate.metamodel.mapping.EntityAssociationMapping; import org.hibernate.metamodel.mapping.EntityAssociationMapping;
import org.hibernate.metamodel.mapping.ModelPart; import org.hibernate.metamodel.mapping.ModelPart;
import org.hibernate.persister.entity.EntityPersister; import org.hibernate.persister.entity.EntityPersister;
@ -1480,7 +1481,7 @@ public abstract class AbstractSqlAstTranslator<T extends JdbcOperation> implemen
visitSelectClause( querySpec.getSelectClause() ); visitSelectClause( querySpec.getSelectClause() );
visitFromClause( querySpec.getFromClause() ); visitFromClause( querySpec.getFromClause() );
visitWhereClause( querySpec ); visitWhereClause( querySpec );
visitGroupByClause( querySpec, dialect.supportsSelectAliasInGroupByClause() ); visitGroupByClause( querySpec, dialect.getGroupBySelectItemReferenceStrategy() );
visitHavingClause( querySpec ); visitHavingClause( querySpec );
visitOrderBy( querySpec.getSortSpecifications() ); visitOrderBy( querySpec.getSortSpecifications() );
visitOffsetFetchClause( querySpec ); visitOffsetFetchClause( querySpec );
@ -1556,13 +1557,38 @@ public abstract class AbstractSqlAstTranslator<T extends JdbcOperation> implemen
return expression; 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<Expression> partitionExpressions = querySpec.getGroupByClauseExpressions(); final List<Expression> partitionExpressions = querySpec.getGroupByClauseExpressions();
if ( !partitionExpressions.isEmpty() ) { if ( !partitionExpressions.isEmpty() ) {
try { try {
clauseStack.push( Clause.GROUP ); clauseStack.push( Clause.GROUP );
appendSql( " group by " ); appendSql( " group by " );
visitPartitionExpressions( partitionExpressions, supportsSelectAliases ); visitPartitionExpressions( partitionExpressions, referenceStrategy );
} }
finally { finally {
clauseStack.pop(); clauseStack.pop();
@ -1575,7 +1601,7 @@ public abstract class AbstractSqlAstTranslator<T extends JdbcOperation> implemen
try { try {
clauseStack.push( Clause.PARTITION ); clauseStack.push( Clause.PARTITION );
appendSql( "partition by " ); appendSql( "partition by " );
visitPartitionExpressions( partitionExpressions, false ); visitPartitionExpressions( partitionExpressions, SelectItemReferenceStrategy.EXPRESSION );
} }
finally { finally {
clauseStack.pop(); clauseStack.pop();
@ -1585,13 +1611,21 @@ public abstract class AbstractSqlAstTranslator<T extends JdbcOperation> implemen
protected final void visitPartitionExpressions( protected final void visitPartitionExpressions(
List<Expression> partitionExpressions, List<Expression> partitionExpressions,
boolean supportsSelectAliases) { SelectItemReferenceStrategy referenceStrategy) {
if ( supportsSelectAliases ) { final Function<Expression, Expression> resolveAliasExpression;
visitPartitionExpressions( partitionExpressions, expression -> expression ); switch ( referenceStrategy ) {
} case POSITION:
else { resolveAliasExpression = Function.identity();
visitPartitionExpressions( partitionExpressions, expression -> resolveAliasedExpression( expression ) ); break;
case ALIAS:
resolveAliasExpression = this::resolveExpressionToAlias;
break;
case EXPRESSION:
default:
resolveAliasExpression = this::resolveAliasedExpression;
break;
} }
visitPartitionExpressions( partitionExpressions, resolveAliasExpression );
} }
protected final void visitPartitionExpressions( protected final void visitPartitionExpressions(
@ -2862,7 +2896,7 @@ public abstract class AbstractSqlAstTranslator<T extends JdbcOperation> implemen
protected void visitSqlSelections(SelectClause selectClause) { protected void visitSqlSelections(SelectClause selectClause) {
final List<SqlSelection> sqlSelections = selectClause.getSqlSelections(); final List<SqlSelection> sqlSelections = selectClause.getSqlSelections();
final int size = sqlSelections.size(); final int size = sqlSelections.size();
if ( needsSelectAliases ) { if ( needsSelectAliases || needsSelectAliasesForGroupOrOrderByClause() ) {
String separator = NO_SEPARATOR; String separator = NO_SEPARATOR;
for ( int i = 0; i < size; i++ ) { for ( int i = 0; i < size; i++ ) {
final SqlSelection sqlSelection = sqlSelections.get( i ); final SqlSelection sqlSelection = sqlSelections.get( i );
@ -2887,6 +2921,40 @@ public abstract class AbstractSqlAstTranslator<T extends JdbcOperation> 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) { protected void renderRowNumberingSelectItems(SelectClause selectClause, QueryPart queryPart) {
final FetchClauseType fetchClauseType = getFetchClauseTypeForRowNumbering( queryPart ); final FetchClauseType fetchClauseType = getFetchClauseTypeForRowNumbering( queryPart );
if ( fetchClauseType != null ) { if ( fetchClauseType != null ) {
@ -4329,7 +4397,7 @@ public abstract class AbstractSqlAstTranslator<T extends JdbcOperation> implemen
.isEmpty() || subQuery.getHavingClauseRestrictions() != null ) { .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 // If we have a group by or having clause, we have to move the tuple comparison emulation to the HAVING clause
visitWhereClause( subQuery ); visitWhereClause( subQuery );
visitGroupByClause( subQuery, false ); visitGroupByClause( subQuery, SelectItemReferenceStrategy.EXPRESSION );
appendSql( " having " ); appendSql( " having " );
clauseStack.push( Clause.HAVING ); clauseStack.push( Clause.HAVING );
@ -4421,7 +4489,7 @@ public abstract class AbstractSqlAstTranslator<T extends JdbcOperation> implemen
visitSelectClause( subQuery.getSelectClause() ); visitSelectClause( subQuery.getSelectClause() );
visitFromClause( subQuery.getFromClause() ); visitFromClause( subQuery.getFromClause() );
visitWhereClause( subQuery ); visitWhereClause( subQuery );
visitGroupByClause( subQuery, dialect.supportsSelectAliasInGroupByClause() ); visitGroupByClause( subQuery, dialect.getGroupBySelectItemReferenceStrategy() );
visitHavingClause( subQuery ); visitHavingClause( subQuery );
appendSql( " order by " ); appendSql( " order by " );

View File

@ -7,8 +7,7 @@
package org.hibernate.query; package org.hibernate.query;
import org.hibernate.jpa.test.BaseEntityManagerFunctionalTestCase; import org.hibernate.jpa.test.BaseEntityManagerFunctionalTestCase;
import org.hibernate.testing.DialectChecks; import org.hibernate.testing.TestForIssue;
import org.hibernate.testing.RequiresDialectFeature;
import org.hibernate.testing.jdbc.SQLStatementInterceptor; import org.hibernate.testing.jdbc.SQLStatementInterceptor;
import org.junit.Test; import org.junit.Test;
@ -30,7 +29,7 @@ import static org.junit.Assert.assertNotNull;
* @author Jan-Willem Gmelig Meyling * @author Jan-Willem Gmelig Meyling
* @author Sayra Ranjha * @author Sayra Ranjha
*/ */
@RequiresDialectFeature(value = DialectChecks.SupportsSelectAliasInGroupByClause.class, jiraKey = "HHH-9301") @TestForIssue( jiraKey = "HHH-9301" )
public class GroupByAliasTest extends BaseEntityManagerFunctionalTestCase { public class GroupByAliasTest extends BaseEntityManagerFunctionalTestCase {
public static final int MAX_COUNT = 15; public static final int MAX_COUNT = 15;

View File

@ -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 { public static class SupportsNClob implements DialectCheck {
@Override @Override
public boolean isMatch(Dialect dialect) { public boolean isMatch(Dialect dialect) {