mirror of
https://github.com/hibernate/hibernate-orm
synced 2025-02-18 17:15:02 +00:00
HHH-1615 Test and fix for group by entity
This commit is contained in:
parent
044b4b5345
commit
e13e0bc9d5
@ -44,6 +44,7 @@
|
|||||||
import org.hibernate.sql.ast.SqlAstTranslatorFactory;
|
import org.hibernate.sql.ast.SqlAstTranslatorFactory;
|
||||||
import org.hibernate.sql.ast.spi.StandardSqlAstTranslatorFactory;
|
import org.hibernate.sql.ast.spi.StandardSqlAstTranslatorFactory;
|
||||||
import org.hibernate.sql.ast.tree.Statement;
|
import org.hibernate.sql.ast.tree.Statement;
|
||||||
|
import org.hibernate.sql.ast.tree.expression.SqlTupleContainer;
|
||||||
import org.hibernate.sql.exec.spi.JdbcOperation;
|
import org.hibernate.sql.exec.spi.JdbcOperation;
|
||||||
import org.hibernate.tool.schema.extract.internal.SequenceInformationExtractorH2DatabaseImpl;
|
import org.hibernate.tool.schema.extract.internal.SequenceInformationExtractorH2DatabaseImpl;
|
||||||
import org.hibernate.tool.schema.extract.internal.SequenceInformationExtractorLegacyImpl;
|
import org.hibernate.tool.schema.extract.internal.SequenceInformationExtractorLegacyImpl;
|
||||||
@ -426,6 +427,11 @@ public boolean supportsTuplesInSubqueries() {
|
|||||||
return supportsTuplesInSubqueries;
|
return supportsTuplesInSubqueries;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean supportsSelectAliasInGroupByClause() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public IdentityColumnSupport getIdentityColumnSupport() {
|
public IdentityColumnSupport getIdentityColumnSupport() {
|
||||||
return new H2IdentityColumnSupport();
|
return new H2IdentityColumnSupport();
|
||||||
|
@ -419,6 +419,11 @@ public boolean supportsUnionAll() {
|
|||||||
return getMySQLVersion() >= 500;
|
return getMySQLVersion() >= 500;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean supportsSelectAliasInGroupByClause() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean supportsColumnCheck() {
|
public boolean supportsColumnCheck() {
|
||||||
return false;
|
return false;
|
||||||
|
@ -641,6 +641,11 @@ public boolean supportsRowValueConstructorSyntaxInQuantifiedPredicates() {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean supportsSelectAliasInGroupByClause() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public CallableStatementSupport getCallableStatementSupport() {
|
public CallableStatementSupport getCallableStatementSupport() {
|
||||||
return PostgresCallableStatementSupport.INSTANCE;
|
return PostgresCallableStatementSupport.INSTANCE;
|
||||||
|
@ -341,11 +341,12 @@ public abstract class BaseSqmToSqlAstConverter<T extends Statement> extends Base
|
|||||||
private int fetchDepth;
|
private int fetchDepth;
|
||||||
private boolean resolvingCircularFetch;
|
private boolean resolvingCircularFetch;
|
||||||
private ForeignKeyDescriptor.Nature currentlyResolvingForeignKeySide;
|
private ForeignKeyDescriptor.Nature currentlyResolvingForeignKeySide;
|
||||||
|
private SqmQueryPart<?> currentSqmQueryPart;
|
||||||
|
|
||||||
private Map<String, FilterPredicate> collectionFilterPredicates;
|
private Map<String, FilterPredicate> collectionFilterPredicates;
|
||||||
private OrderByFragmentConsumer orderByFragmentConsumer;
|
private OrderByFragmentConsumer orderByFragmentConsumer;
|
||||||
|
|
||||||
private Map<String, NavigablePath> joinPathBySqmJoinFullPath = new HashMap<>();
|
private final Map<String, NavigablePath> joinPathBySqmJoinFullPath = new HashMap<>();
|
||||||
|
|
||||||
private final SqlAliasBaseManager sqlAliasBaseManager = new SqlAliasBaseManager();
|
private final SqlAliasBaseManager sqlAliasBaseManager = new SqlAliasBaseManager();
|
||||||
private final Stack<SqlAstProcessingState> processingStateStack = new StandardStack<>();
|
private final Stack<SqlAstProcessingState> processingStateStack = new StandardStack<>();
|
||||||
@ -1315,6 +1316,8 @@ public QueryGroup visitQueryGroup(SqmQueryGroup<?> queryGroup) {
|
|||||||
);
|
);
|
||||||
final DelegatingSqmAliasedNodeCollector collector = (DelegatingSqmAliasedNodeCollector) processingState
|
final DelegatingSqmAliasedNodeCollector collector = (DelegatingSqmAliasedNodeCollector) processingState
|
||||||
.getSqlExpressionResolver();
|
.getSqlExpressionResolver();
|
||||||
|
final SqmQueryPart<?> sqmQueryPart = currentSqmQueryPart;
|
||||||
|
currentSqmQueryPart = queryGroup;
|
||||||
pushProcessingState( processingState );
|
pushProcessingState( processingState );
|
||||||
|
|
||||||
try {
|
try {
|
||||||
@ -1335,6 +1338,7 @@ public QueryGroup visitQueryGroup(SqmQueryGroup<?> queryGroup) {
|
|||||||
}
|
}
|
||||||
finally {
|
finally {
|
||||||
popProcessingStateStack();
|
popProcessingStateStack();
|
||||||
|
currentSqmQueryPart = sqmQueryPart;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1386,6 +1390,8 @@ else if ( sqmQuerySpec.hasPositionalGroupItem() ) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
final SqmQueryPart<?> sqmQueryPart = currentSqmQueryPart;
|
||||||
|
currentSqmQueryPart = sqmQuerySpec;
|
||||||
pushProcessingState( processingState );
|
pushProcessingState( processingState );
|
||||||
|
|
||||||
try {
|
try {
|
||||||
@ -1435,6 +1441,7 @@ else if ( sqmQuerySpec.hasPositionalGroupItem() ) {
|
|||||||
}
|
}
|
||||||
additionalRestrictions = originalAdditionalRestrictions;
|
additionalRestrictions = originalAdditionalRestrictions;
|
||||||
popProcessingStateStack();
|
popProcessingStateStack();
|
||||||
|
currentSqmQueryPart = sqmQueryPart;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1657,10 +1664,22 @@ protected Expression resolveGroupOrOrderByExpression(SqmExpression<?> groupByCla
|
|||||||
}
|
}
|
||||||
else if ( tableGroup.getModelPart() instanceof EntityValuedModelPart ) {
|
else if ( tableGroup.getModelPart() instanceof EntityValuedModelPart ) {
|
||||||
final EntityValuedModelPart mapping = (EntityValuedModelPart) tableGroup.getModelPart();
|
final EntityValuedModelPart mapping = (EntityValuedModelPart) tableGroup.getModelPart();
|
||||||
|
final boolean expandToAllColumns;
|
||||||
|
if ( currentClauseStack.getCurrent() == Clause.GROUP ) {
|
||||||
|
// When the table group is known to be fetched i.e. a fetch join
|
||||||
|
// but also when the from clause is part of the select clause
|
||||||
|
// we need to expand to all columns, as we also expand this to all columns in the select clause
|
||||||
|
expandToAllColumns = tableGroup.isFetched()
|
||||||
|
|| groupByClauseExpression instanceof SqmFrom<?, ?> && selectClauseContains( (SqmFrom<?, ?>) groupByClauseExpression );
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
expandToAllColumns = false;
|
||||||
|
}
|
||||||
return EntityValuedPathInterpretation.from(
|
return EntityValuedPathInterpretation.from(
|
||||||
tableGroup.getNavigablePath(),
|
tableGroup.getNavigablePath(),
|
||||||
tableGroup,
|
tableGroup,
|
||||||
mapping,
|
mapping,
|
||||||
|
expandToAllColumns,
|
||||||
this
|
this
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -1668,6 +1687,20 @@ else if ( tableGroup.getModelPart() instanceof EntityValuedModelPart ) {
|
|||||||
return expression;
|
return expression;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private boolean selectClauseContains(SqmFrom<?, ?> from) {
|
||||||
|
final SqmQuerySpec<?> sqmQuerySpec = (SqmQuerySpec<?>) currentSqmQueryPart;
|
||||||
|
final List<SqmSelection> selections = sqmQuerySpec.getSelectClause().getSelections();
|
||||||
|
if ( selections.isEmpty() && from instanceof SqmRoot<?> ) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
for ( SqmSelection selection : selections ) {
|
||||||
|
if ( selection.getSelectableNode() == from ) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<Expression> visitGroupByClause(List<SqmExpression<?>> groupByClauseExpressions) {
|
public List<Expression> visitGroupByClause(List<SqmExpression<?>> groupByClauseExpressions) {
|
||||||
if ( !groupByClauseExpressions.isEmpty() ) {
|
if ( !groupByClauseExpressions.isEmpty() ) {
|
||||||
|
@ -14,11 +14,13 @@
|
|||||||
import org.hibernate.metamodel.mapping.BasicValuedModelPart;
|
import org.hibernate.metamodel.mapping.BasicValuedModelPart;
|
||||||
import org.hibernate.metamodel.mapping.CollectionPart;
|
import org.hibernate.metamodel.mapping.CollectionPart;
|
||||||
import org.hibernate.metamodel.mapping.EntityAssociationMapping;
|
import org.hibernate.metamodel.mapping.EntityAssociationMapping;
|
||||||
|
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.EntityMappingType;
|
||||||
import org.hibernate.metamodel.mapping.EntityValuedModelPart;
|
import org.hibernate.metamodel.mapping.EntityValuedModelPart;
|
||||||
import org.hibernate.metamodel.mapping.ForeignKeyDescriptor;
|
import org.hibernate.metamodel.mapping.ForeignKeyDescriptor;
|
||||||
import org.hibernate.metamodel.mapping.ModelPart;
|
import org.hibernate.metamodel.mapping.ModelPart;
|
||||||
|
import org.hibernate.metamodel.mapping.SelectableConsumer;
|
||||||
import org.hibernate.metamodel.mapping.internal.SimpleForeignKeyDescriptor;
|
import org.hibernate.metamodel.mapping.internal.SimpleForeignKeyDescriptor;
|
||||||
import org.hibernate.query.NavigablePath;
|
import org.hibernate.query.NavigablePath;
|
||||||
import org.hibernate.query.sqm.sql.SqmToSqlAstConverter;
|
import org.hibernate.query.sqm.sql.SqmToSqlAstConverter;
|
||||||
@ -65,19 +67,54 @@ public static <T> EntityValuedPathInterpretation<T> from(
|
|||||||
.findTableGroup( sqmPath.getLhs().getNavigablePath() )
|
.findTableGroup( sqmPath.getLhs().getNavigablePath() )
|
||||||
.getModelPart()
|
.getModelPart()
|
||||||
.findSubPart( sqmPath.getReferencedPathSource().getPathName(), null );
|
.findSubPart( sqmPath.getReferencedPathSource().getPathName(), null );
|
||||||
return from( sqmPath.getNavigablePath(), tableGroup, mapping, sqlAstCreationState );
|
return from( sqmPath.getNavigablePath(), tableGroup, mapping, false, sqlAstCreationState );
|
||||||
}
|
}
|
||||||
|
|
||||||
public static <T> EntityValuedPathInterpretation<T> from(
|
public static <T> EntityValuedPathInterpretation<T> from(
|
||||||
NavigablePath navigablePath,
|
NavigablePath navigablePath,
|
||||||
TableGroup tableGroup,
|
TableGroup tableGroup,
|
||||||
EntityValuedModelPart mapping,
|
EntityValuedModelPart mapping,
|
||||||
|
boolean expandToAllColumns,
|
||||||
SqmToSqlAstConverter sqlAstCreationState) {
|
SqmToSqlAstConverter sqlAstCreationState) {
|
||||||
final SqlExpressionResolver sqlExprResolver = sqlAstCreationState.getSqlExpressionResolver();
|
final SqlExpressionResolver sqlExprResolver = sqlAstCreationState.getSqlExpressionResolver();
|
||||||
final SessionFactoryImplementor sessionFactory = sqlAstCreationState.getCreationContext().getSessionFactory();
|
final SessionFactoryImplementor sessionFactory = sqlAstCreationState.getCreationContext().getSessionFactory();
|
||||||
final Expression sqlExpression;
|
final Expression sqlExpression;
|
||||||
|
|
||||||
if ( mapping instanceof EntityAssociationMapping ) {
|
if ( expandToAllColumns ) {
|
||||||
|
final EntityMappingType entityMappingType = mapping.getEntityMappingType();
|
||||||
|
final EntityIdentifierMapping identifierMapping = entityMappingType.getIdentifierMapping();
|
||||||
|
final EntityDiscriminatorMapping discriminatorMapping = entityMappingType.getDiscriminatorMapping();
|
||||||
|
final List<Expression> expressions = new ArrayList<>(
|
||||||
|
mapping.getJdbcTypeCount() + identifierMapping.getJdbcTypeCount()
|
||||||
|
+ ( discriminatorMapping == null ? 0 : 1 )
|
||||||
|
);
|
||||||
|
final SelectableConsumer selectableConsumer = (selectionIndex, selectableMapping) -> {
|
||||||
|
final TableReference tableReference = tableGroup.resolveTableReference(
|
||||||
|
navigablePath,
|
||||||
|
selectableMapping.getContainingTableExpression()
|
||||||
|
);
|
||||||
|
expressions.add(
|
||||||
|
sqlExprResolver.resolveSqlExpression(
|
||||||
|
createColumnReferenceKey(
|
||||||
|
tableReference,
|
||||||
|
selectableMapping.getSelectionExpression()
|
||||||
|
),
|
||||||
|
processingState -> new ColumnReference(
|
||||||
|
tableReference,
|
||||||
|
selectableMapping,
|
||||||
|
sessionFactory
|
||||||
|
)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
};
|
||||||
|
identifierMapping.forEachSelectable( selectableConsumer );
|
||||||
|
if ( discriminatorMapping != null ) {
|
||||||
|
discriminatorMapping.forEachSelectable( selectableConsumer );
|
||||||
|
}
|
||||||
|
mapping.forEachSelectable( selectableConsumer );
|
||||||
|
sqlExpression = new SqlTuple( expressions, mapping );
|
||||||
|
}
|
||||||
|
else if ( mapping instanceof EntityAssociationMapping ) {
|
||||||
final EntityAssociationMapping associationMapping = (EntityAssociationMapping) mapping;
|
final EntityAssociationMapping associationMapping = (EntityAssociationMapping) mapping;
|
||||||
final ForeignKeyDescriptor fkDescriptor = associationMapping.getForeignKeyDescriptor();
|
final ForeignKeyDescriptor fkDescriptor = associationMapping.getForeignKeyDescriptor();
|
||||||
final String lhsTable;
|
final String lhsTable;
|
||||||
|
@ -1518,8 +1518,8 @@ protected final void visitPartitionExpressions(List<Expression> partitionExpress
|
|||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
for ( Expression partitionExpression : partitionExpressions ) {
|
for ( Expression partitionExpression : partitionExpressions ) {
|
||||||
if ( partitionExpression instanceof SqlTuple ) {
|
if ( partitionExpression instanceof SqlTupleContainer ) {
|
||||||
for ( Expression expression : ( (SqlTuple) partitionExpression ).getExpressions() ) {
|
for ( Expression expression : ( (SqlTupleContainer) partitionExpression ).getSqlTuple().getExpressions() ) {
|
||||||
appendSql( separator );
|
appendSql( separator );
|
||||||
renderPartitionItem( resolveAliasedExpression( expression ) );
|
renderPartitionItem( resolveAliasedExpression( expression ) );
|
||||||
separator = COMA_SEPARATOR;
|
separator = COMA_SEPARATOR;
|
||||||
|
@ -0,0 +1,52 @@
|
|||||||
|
/*
|
||||||
|
* Hibernate, Relational Persistence for Idiomatic Java
|
||||||
|
*
|
||||||
|
* License: GNU Lesser General Public License (LGPL), version 2.1 or later
|
||||||
|
* See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html
|
||||||
|
*/
|
||||||
|
package org.hibernate.orm.test.query.hql;
|
||||||
|
|
||||||
|
import javax.persistence.Tuple;
|
||||||
|
|
||||||
|
import org.hibernate.testing.TestForIssue;
|
||||||
|
import org.hibernate.testing.orm.domain.StandardDomainModel;
|
||||||
|
import org.hibernate.testing.orm.domain.contacts.Contact;
|
||||||
|
import org.hibernate.testing.orm.domain.gambit.EntityOfBasics;
|
||||||
|
import org.hibernate.testing.orm.junit.DomainModel;
|
||||||
|
import org.hibernate.testing.orm.junit.ServiceRegistry;
|
||||||
|
import org.hibernate.testing.orm.junit.SessionFactory;
|
||||||
|
import org.hibernate.testing.orm.junit.SessionFactoryScope;
|
||||||
|
import org.junit.jupiter.api.Assertions;
|
||||||
|
import org.junit.jupiter.api.BeforeAll;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Christian Beikov
|
||||||
|
*/
|
||||||
|
@ServiceRegistry
|
||||||
|
@DomainModel(standardModels = StandardDomainModel.CONTACTS)
|
||||||
|
@SessionFactory
|
||||||
|
public class GroupByTest {
|
||||||
|
|
||||||
|
@BeforeAll
|
||||||
|
public void prepareData(SessionFactoryScope scope) {
|
||||||
|
scope.inTransaction(
|
||||||
|
em -> {
|
||||||
|
Contact entity1 = new Contact();
|
||||||
|
entity1.setId( 123 );
|
||||||
|
em.persist( entity1 );
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@TestForIssue( jiraKey = "HHH-1615")
|
||||||
|
public void testGroupByEntity(SessionFactoryScope scope) {
|
||||||
|
scope.inTransaction(
|
||||||
|
session -> {
|
||||||
|
session.createQuery( "select e, count(*) from Contact e group by e", Tuple.class ).list();
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user