re-enable tests

re-organize some tests
continuing with o.h.test.hql - order-by
This commit is contained in:
Steve Ebersole 2021-04-06 14:31:25 -05:00
parent e8a5506eb5
commit c85ec5a7e4
20 changed files with 459 additions and 398 deletions

View File

@ -28,14 +28,11 @@ import java.util.GregorianCalendar;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.function.Supplier;
import java.util.regex.Pattern;
import org.hibernate.query.FetchClauseType;
import org.hibernate.NotYetImplementedFor6Exception;
import org.hibernate.query.NullPrecedence;
import org.hibernate.QueryException;
import org.hibernate.query.SetOperator;
import org.hibernate.query.SortOrder;
import org.hibernate.boot.registry.classloading.spi.ClassLoaderService;
import org.hibernate.boot.registry.classloading.spi.ClassLoadingException;
import org.hibernate.grammars.hql.HqlLexer;
@ -50,13 +47,16 @@ import org.hibernate.metamodel.model.domain.DomainType;
import org.hibernate.metamodel.model.domain.EntityDomainType;
import org.hibernate.metamodel.model.domain.IdentifiableDomainType;
import org.hibernate.metamodel.model.domain.ManagedDomainType;
import org.hibernate.metamodel.model.domain.MapPersistentAttribute;
import org.hibernate.metamodel.model.domain.PluralPersistentAttribute;
import org.hibernate.metamodel.model.domain.SingularPersistentAttribute;
import org.hibernate.query.BinaryArithmeticOperator;
import org.hibernate.query.ComparisonOperator;
import org.hibernate.query.FetchClauseType;
import org.hibernate.query.NullPrecedence;
import org.hibernate.query.PathException;
import org.hibernate.query.SemanticException;
import org.hibernate.query.SetOperator;
import org.hibernate.query.SortOrder;
import org.hibernate.query.TemporalUnit;
import org.hibernate.query.TrimSpec;
import org.hibernate.query.UnaryArithmeticOperator;
@ -104,6 +104,7 @@ import org.hibernate.query.sqm.tree.domain.SqmPath;
import org.hibernate.query.sqm.tree.domain.SqmPluralValuedSimplePath;
import org.hibernate.query.sqm.tree.domain.SqmPolymorphicRootDescriptor;
import org.hibernate.query.sqm.tree.domain.SqmTreatedPath;
import org.hibernate.query.sqm.tree.expression.SqmAliasedNodeRef;
import org.hibernate.query.sqm.tree.expression.SqmAny;
import org.hibernate.query.sqm.tree.expression.SqmBinaryArithmetic;
import org.hibernate.query.sqm.tree.expression.SqmByUnit;
@ -161,6 +162,7 @@ import org.hibernate.query.sqm.tree.predicate.SqmOrPredicate;
import org.hibernate.query.sqm.tree.predicate.SqmPredicate;
import org.hibernate.query.sqm.tree.predicate.SqmWhereClause;
import org.hibernate.query.sqm.tree.select.AbstractSqmSelectQuery;
import org.hibernate.query.sqm.tree.select.SqmAliasedNode;
import org.hibernate.query.sqm.tree.select.SqmDynamicInstantiation;
import org.hibernate.query.sqm.tree.select.SqmDynamicInstantiationArgument;
import org.hibernate.query.sqm.tree.select.SqmOrderByClause;
@ -241,17 +243,19 @@ public class SemanticQueryBuilder<R> extends HqlParserBaseVisitor<Object> implem
private final Stack<SqmCreationProcessingState> processingStateStack = new StandardStack<>();
private ParameterCollector parameterCollector;
public Stack<SqmCreationProcessingState> getProcessingStateStack() {
return processingStateStack;
}
private BasicDomainType<Integer> integerDomainType;
private JavaTypeDescriptor<List<?>> listJavaTypeDescriptor;
private JavaTypeDescriptor<Map<?,?>> mapJavaTypeDescriptor;
@SuppressWarnings("WeakerAccess")
public SemanticQueryBuilder(SqmCreationOptions creationOptions, SqmCreationContext creationContext) {
this.creationOptions = creationOptions;
this.creationContext = creationContext;
this.dotIdentifierConsumerStack = new StandardStack<>( new BasicDotIdentifierConsumer( this ) );
this.integerDomainType = creationContext
.getNodeBuilder()
.getTypeConfiguration()
.standardBasicTypeForJavaType( Integer.class );
}
@Override
@ -264,6 +268,10 @@ public class SemanticQueryBuilder<R> extends HqlParserBaseVisitor<Object> implem
return creationOptions;
}
public Stack<SqmCreationProcessingState> getProcessingStateStack() {
return processingStateStack;
}
protected Stack<ParameterDeclarationContext> getParameterDeclarationContextStack() {
return parameterDeclarationContextStack;
}
@ -770,28 +778,41 @@ public class SemanticQueryBuilder<R> extends HqlParserBaseVisitor<Object> implem
final SqmSelectableNode selectableNode = visitSelectableNode( ctx );
//noinspection unchecked
final SqmSelection selection = new SqmSelection(
final SqmSelection<?> selection = new SqmSelection<>(
selectableNode,
// NOTE : SqmSelection forces the alias down to its selectableNode.
// - no need to do that here
resultIdentifier,
creationContext.getNodeBuilder()
);
getProcessingStateStack().getCurrent().getPathRegistry().register( selection );
// if the node is not a dynamic-instantiation, register it with
// the path-registry
//noinspection StatementWithEmptyBody
if ( selectableNode instanceof SqmDynamicInstantiation ) {
// nothing else to do (avoid kludgy `! ( instanceof )` syntax
}
else {
getCurrentProcessingState().getPathRegistry().register( selection );
}
return selection;
}
private SqmSelectableNode visitSelectableNode(HqlParser.SelectionContext ctx) {
private SqmSelectableNode<?> visitSelectableNode(HqlParser.SelectionContext ctx) {
if ( ctx.selectExpression().dynamicInstantiation() != null ) {
return visitDynamicInstantiation( ctx.selectExpression().dynamicInstantiation() );
}
else if ( ctx.selectExpression().jpaSelectObjectSyntax() != null ) {
if ( ctx.selectExpression().jpaSelectObjectSyntax() != null ) {
return visitJpaSelectObjectSyntax( ctx.selectExpression().jpaSelectObjectSyntax() );
}
else if ( ctx.selectExpression().mapEntrySelection() != null ) {
if ( ctx.selectExpression().mapEntrySelection() != null ) {
return visitMapEntrySelection( ctx.selectExpression().mapEntrySelection() );
}
else if ( ctx.selectExpression().expression() != null ) {
if ( ctx.selectExpression().expression() != null ) {
return (SqmExpression) ctx.selectExpression().expression().accept( this );
}
@ -829,12 +850,9 @@ public class SemanticQueryBuilder<R> extends HqlParserBaseVisitor<Object> implem
return null;
}
private JavaTypeDescriptor<List> listJavaTypeDescriptor;
private JavaTypeDescriptor<Map> mapJavaTypeDescriptor;
@Override
public SqmDynamicInstantiation visitDynamicInstantiation(HqlParser.DynamicInstantiationContext ctx) {
final SqmDynamicInstantiation dynamicInstantiation;
public SqmDynamicInstantiation<?> visitDynamicInstantiation(HqlParser.DynamicInstantiationContext ctx) {
final SqmDynamicInstantiation<?> dynamicInstantiation;
if ( ctx.dynamicInstantiationTarget().MAP() != null ) {
if ( mapJavaTypeDescriptor == null ) {
@ -863,7 +881,7 @@ public class SemanticQueryBuilder<R> extends HqlParserBaseVisitor<Object> implem
else {
final String className = ctx.dynamicInstantiationTarget().dotIdentifierSequence().getText();
try {
final JavaTypeDescriptor jtd = resolveInstantiationTargetJtd( className );
final JavaTypeDescriptor<?> jtd = resolveInstantiationTargetJtd( className );
dynamicInstantiation = SqmDynamicInstantiation.forClassInstantiation(
jtd,
creationContext.getNodeBuilder()
@ -881,7 +899,7 @@ public class SemanticQueryBuilder<R> extends HqlParserBaseVisitor<Object> implem
return dynamicInstantiation;
}
private JavaTypeDescriptor resolveInstantiationTargetJtd(String className) {
private JavaTypeDescriptor<?> resolveInstantiationTargetJtd(String className) {
final Class<?> targetJavaType = classForName( creationContext.getJpaMetamodel().qualifyImportableName( className ) );
return creationContext.getJpaMetamodel()
.getTypeConfiguration()
@ -889,18 +907,32 @@ public class SemanticQueryBuilder<R> extends HqlParserBaseVisitor<Object> implem
.resolveDescriptor( targetJavaType );
}
private Class classForName(String className) {
private Class<?> classForName(String className) {
return creationContext.getServiceRegistry().getService( ClassLoaderService.class ).classForName( className );
}
@Override
public SqmDynamicInstantiationArgument visitDynamicInstantiationArg(HqlParser.DynamicInstantiationArgContext ctx) {
public SqmDynamicInstantiationArgument<?> visitDynamicInstantiationArg(HqlParser.DynamicInstantiationArgContext ctx) {
final String alias = ctx.identifier() == null ? null : ctx.identifier().getText();
final SqmSelectableNode<?> argExpression = visitDynamicInstantiationArgExpression( ctx.dynamicInstantiationArgExpression() );
//noinspection unchecked
return new SqmDynamicInstantiationArgument(
visitDynamicInstantiationArgExpression( ctx.dynamicInstantiationArgExpression() ),
ctx.identifier() == null ? null : ctx.identifier().getText(),
final SqmDynamicInstantiationArgument<?> argument = new SqmDynamicInstantiationArgument<>(
argExpression,
alias,
creationContext.getNodeBuilder()
);
//noinspection StatementWithEmptyBody
if ( argExpression instanceof SqmDynamicInstantiation ) {
// nothing else to do (avoid kludgy `! ( instanceof )` syntax
}
else {
getCurrentProcessingState().getPathRegistry().register( argument );
}
return argument;
}
@Override
@ -930,56 +962,71 @@ public class SemanticQueryBuilder<R> extends HqlParserBaseVisitor<Object> implem
return visitExpressions( ctx.groupByExpression() );
}
@Override
public SqmExpression<?> visitGroupByExpression(HqlParser.GroupByExpressionContext ctx) {
if ( ctx.INTEGER_LITERAL() != null ) {
// This is syntactically disallowed
if ( ctx.collationSpecification() != null ) {
throw new ParsingException( "COLLATE is not allowed for position based group by items!" );
}
final int position = Integer.parseInt( ctx.INTEGER_LITERAL().getText() );
final SqmSelection<?> selection = getCurrentProcessingState().getPathRegistry().findSelectionByPosition( position );
if ( selection == null ) {
throw new ParsingException( "Invalid select item position " + position + " used for order by item!" );
private SqmExpression<?> resolveOrderByOrGroupByExpression(
Supplier<TerminalNode> positionLiteralAccess,
Supplier<HqlParser.IdentifierContext> identifierAccess,
Supplier<HqlParser.ExpressionContext> expressionContextAccess,
boolean definedCollate) {
if ( positionLiteralAccess.get() != null ) {
if ( definedCollate ) {
// This is syntactically disallowed
throw new ParsingException( "COLLATE is not allowed for position based order-by or group-by items" );
}
return new SqmLiteral<>(
position,
resolveExpressableTypeBasic( Integer.class ),
creationContext.getNodeBuilder()
);
final int position = Integer.parseInt( positionLiteralAccess.get().getText() );
// make sure this selection exists
final SqmAliasedNode<?> nodeByPosition = getCurrentProcessingState()
.getPathRegistry()
.findAliasedNodeByPosition( position );
if ( nodeByPosition == null ) {
throw new ParsingException( "Numeric literal `" + position + "` used in group-by does not match a registered select-item" );
}
return new SqmAliasedNodeRef( position, integerDomainType, creationContext.getNodeBuilder() );
}
if ( ctx.identifier() != null ) {
final HqlParser.CollationSpecificationContext collationSpecificationContext = ctx.collationSpecification();
final SqmSelection<?> selection = getCurrentProcessingState().getPathRegistry().findSelectionByAlias( ctx.identifier().getText() );
if ( selection != null ) {
// This is syntactically disallowed
if ( collationSpecificationContext != null ) {
throw new ParsingException( "COLLATE is not allowed for alias based group by items!" );
final HqlParser.IdentifierContext identifier = identifierAccess.get();
if ( identifier != null ) {
final String identifierText = identifier.getText();
final Integer correspondingPosition = getCurrentProcessingState()
.getPathRegistry()
.findAliasedNodePosition( identifierText );
if ( correspondingPosition != null ) {
if ( definedCollate ) {
// This is syntactically disallowed
throw new ParsingException( "COLLATE is not allowed for alias based order-by or group-by items" );
}
return new SqmLiteral<>(
getSelectionPosition( selection ),
resolveExpressableTypeBasic( Integer.class ),
creationContext.getNodeBuilder()
);
return new SqmAliasedNodeRef( correspondingPosition, integerDomainType, creationContext.getNodeBuilder() );
}
final SqmFrom<?, ?> sqmFrom = getCurrentProcessingState().getPathRegistry().findFromByAlias( ctx.identifier().getText() );
final SqmFrom<?, ?> sqmFrom = getCurrentProcessingState().getPathRegistry().findFromByAlias( identifierText );
if ( sqmFrom != null ) {
// This is syntactically disallowed
if ( collationSpecificationContext != null ) {
throw new ParsingException( "COLLATE is not allowed for alias based group by items!" );
if ( definedCollate ) {
// This is syntactically disallowed
throw new ParsingException( "COLLATE is not allowed for alias based order-by or group-by items" );
}
// this will group-by all of the sub-parts in the from-element's model part
return sqmFrom;
}
final DotIdentifierConsumer dotIdentifierConsumer = dotIdentifierConsumerStack.getCurrent();
dotIdentifierConsumer.consumeIdentifier( ctx.getText(), true, true );
dotIdentifierConsumer.consumeIdentifier( identifierText, true, true );
return (SqmExpression<?>) dotIdentifierConsumer.getConsumedPart();
}
return (SqmExpression<?>) ctx.expression().accept( this );
return (SqmExpression<?>) expressionContextAccess.get().accept( this );
}
@Override
public SqmExpression<?> visitGroupByExpression(HqlParser.GroupByExpressionContext ctx) {
return resolveOrderByOrGroupByExpression(
ctx::INTEGER_LITERAL,
ctx::identifier,
ctx::expression,
ctx.collationSpecification() != null
);
}
@Override
@ -1034,66 +1081,12 @@ public class SemanticQueryBuilder<R> extends HqlParserBaseVisitor<Object> implem
@Override
public SqmExpression<?> visitSortExpression(HqlParser.SortExpressionContext ctx) {
if ( ctx.INTEGER_LITERAL() != null ) {
// This is syntactically disallowed
if ( ctx.collationSpecification() != null ) {
throw new ParsingException( "COLLATE is not allowed for position based order by items!" );
}
final int position = Integer.parseInt( ctx.INTEGER_LITERAL().getText() );
SqmSelection<?> selection = getCurrentProcessingState().getPathRegistry().findSelectionByPosition( position );
if ( selection == null ) {
selection = currentQuerySpec().getSelectClause().getSelections().get( position - 1 );
}
if ( selection == null ) {
throw new ParsingException( "Invalid select item position " + position + " used for order by item!" );
}
return new SqmLiteral<>(
position,
resolveExpressableTypeBasic( Integer.class ),
creationContext.getNodeBuilder()
);
}
if ( ctx.identifier() != null ) {
final HqlParser.CollationSpecificationContext collationSpecificationContext = ctx.collationSpecification();
final String alias = ctx.identifier().getText();
SqmSelection<?> selection = getCurrentProcessingState().getPathRegistry().findSelectionByAlias( alias );
if ( selection == null ) {
for ( SqmSelection sqmSelection : currentQuerySpec().getSelectClause().getSelections() ) {
if ( alias.equals( sqmSelection.getAlias() ) ) {
selection = sqmSelection;
break;
}
}
}
if ( selection != null ) {
// This is syntactically disallowed
if ( collationSpecificationContext != null ) {
throw new ParsingException( "COLLATE is not allowed for alias based order by items!" );
}
return new SqmLiteral<>(
getSelectionPosition( selection ),
resolveExpressableTypeBasic( Integer.class ),
creationContext.getNodeBuilder()
);
}
final SqmFrom<?, ?> sqmFrom = getCurrentProcessingState().getPathRegistry().findFromByAlias( ctx.identifier().getText() );
if ( sqmFrom != null ) {
// This is syntactically disallowed
if ( collationSpecificationContext != null ) {
throw new ParsingException( "COLLATE is not allowed for alias based order by items!" );
}
return sqmFrom;
}
final DotIdentifierConsumer dotIdentifierConsumer = dotIdentifierConsumerStack.getCurrent();
dotIdentifierConsumer.consumeIdentifier( ctx.getText(), true, true );
return (SqmExpression<?>) dotIdentifierConsumer.getConsumedPart();
}
return (SqmExpression<?>) ctx.expression().accept( this );
return resolveOrderByOrGroupByExpression(
ctx::INTEGER_LITERAL,
ctx::identifier,
ctx::expression,
ctx.collationSpecification() != null
);
}
private SqmQuerySpec<?> currentQuerySpec() {

View File

@ -6,12 +6,15 @@
*/
package org.hibernate.query.hql.internal;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.function.Function;
import org.hibernate.internal.util.MutableInteger;
import org.hibernate.jpa.spi.JpaCompliance;
import org.hibernate.query.NavigablePath;
import org.hibernate.query.hql.HqlLogging;
@ -23,7 +26,7 @@ import org.hibernate.query.sqm.SqmPathSource;
import org.hibernate.query.sqm.SqmTreeCreationLogger;
import org.hibernate.query.sqm.tree.domain.SqmPath;
import org.hibernate.query.sqm.tree.from.SqmFrom;
import org.hibernate.query.sqm.tree.select.SqmSelection;
import org.hibernate.query.sqm.tree.select.SqmAliasedNode;
/**
* Container for indexing needed while building an SQM tree.
@ -39,7 +42,7 @@ public class SqmPathRegistryImpl implements SqmPathRegistry {
private final Map<String, SqmFrom> sqmFromByAlias = new HashMap<>();
private final LinkedHashMap<String, SqmSelection> sqmSelectionsByAlias = new LinkedHashMap<>();
private final List<SqmAliasedNode> simpleSelectionNodes = new ArrayList<>();
public SqmPathRegistryImpl(SqmCreationProcessingState associatedProcessingState) {
this.associatedProcessingState = associatedProcessingState;
@ -227,21 +230,13 @@ public class SqmPathRegistryImpl implements SqmPathRegistry {
}
@Override
public SqmSelection findSelectionByAlias(String alias) {
return sqmSelectionsByAlias.get( alias );
}
public SqmAliasedNode<?> findAliasedNodeByAlias(String alias) {
assert alias != null;
@Override
public SqmSelection findSelectionByPosition(int position) {
// NOTE : 1-based
// so incoming position must be between >= 1 and <= map.size
if ( position >= 1 && position <= sqmSelectionsByAlias.size() ) {
int i = 1;
for ( Map.Entry<String, SqmSelection> entry : sqmSelectionsByAlias.entrySet() ) {
if ( position == i++ ) {
return entry.getValue();
}
for ( int i = 0; i < simpleSelectionNodes.size(); i++ ) {
final SqmAliasedNode<?> node = simpleSelectionNodes.get( i );
if ( alias.equals( node.getAlias() ) ) {
return node;
}
}
@ -249,22 +244,50 @@ public class SqmPathRegistryImpl implements SqmPathRegistry {
}
@Override
public void register(SqmSelection selection) {
if ( selection.getAlias() != null ) {
checkResultVariable( selection );
sqmSelectionsByAlias.put( selection.getAlias(), selection );
public Integer findAliasedNodePosition(String alias) {
if ( alias == null ) {
return null;
}
// NOTE : 1-based
for ( int i = 0; i < simpleSelectionNodes.size(); i++ ) {
final SqmAliasedNode<?> node = simpleSelectionNodes.get( i );
if ( alias.equals( node.getAlias() ) ) {
return i + 1;
}
}
return null;
}
private void checkResultVariable(SqmSelection selection) {
final String alias = selection.getAlias();
@Override
public SqmAliasedNode<?> findAliasedNodeByPosition(int position) {
// NOTE : 1-based
if ( sqmSelectionsByAlias.containsKey( alias ) ) {
return simpleSelectionNodes.get( position - 1 );
}
@Override
public void register(SqmAliasedNode<?> node) {
checkResultVariable( node );
simpleSelectionNodes.add( node );
}
private void checkResultVariable(SqmAliasedNode selection) {
final String alias = selection.getAlias();
if ( alias == null ) {
return;
}
final Integer position = findAliasedNodePosition( alias );
if ( position != null ) {
throw new AliasCollisionException(
String.format(
Locale.ENGLISH,
"Alias [%s] is already used in same select clause",
alias
"Alias [%s] is already used in same select clause [position=%s]",
alias,
position
)
);
}

View File

@ -12,7 +12,7 @@ import org.hibernate.Incubating;
import org.hibernate.query.NavigablePath;
import org.hibernate.query.sqm.tree.domain.SqmPath;
import org.hibernate.query.sqm.tree.from.SqmFrom;
import org.hibernate.query.sqm.tree.select.SqmSelection;
import org.hibernate.query.sqm.tree.select.SqmAliasedNode;
/**
* Registry for SqmPath references providing the ability to access them
@ -76,17 +76,30 @@ public interface SqmPathRegistry {
// SqmSelection
/**
* Register an SqmSelection
* Register a node aliased within the select-clause
*/
void register(SqmSelection selection);
void register(SqmAliasedNode<?> aliasedNode);
/**
* Find an SqmSelection by the explicit alias assigned to it
* Find a node (if one) by the explicit alias assigned to it
* within the select-clause
*
* @return The matching node, or null
*/
SqmSelection findSelectionByAlias(String alias);
SqmAliasedNode<?> findAliasedNodeByAlias(String alias);
/**
* Find the position of a node with the given alias, relative to the
* underlying SQL select-list.
*
* @return The position, or null
*/
Integer findAliasedNodePosition(String alias);
/**
* Find an SqmSelection by its position in the SqmSelectClause
*
* @return The matching node, or null
*/
SqmSelection findSelectionByPosition(int position);
SqmAliasedNode<?> findAliasedNodeByPosition(int position);
}

View File

@ -8,7 +8,6 @@ package org.hibernate.query.hql.spi;
import org.hibernate.Incubating;
import org.hibernate.query.sqm.tree.select.SqmSelectQuery;
import org.hibernate.query.sqm.tree.select.SqmSelection;
/**
* SqmCreationProcessingState specialization for processing a SQM query-spec
@ -17,10 +16,6 @@ import org.hibernate.query.sqm.tree.select.SqmSelection;
*/
@Incubating
public interface SqmQuerySpecCreationProcessingState extends SqmCreationProcessingState {
void registerSelection(SqmSelection selection);
SqmSelection findSelectionByAlias(String alias);
SqmSelection findSelectionByPosition(int position);
@Override
SqmSelectQuery<?> getProcessingQuery();
}

View File

@ -1,122 +0,0 @@
/*
* 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.query.results;
import java.util.HashMap;
import java.util.Map;
import java.util.function.Consumer;
import java.util.function.Function;
import org.hibernate.Internal;
import org.hibernate.LockMode;
import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.sql.ast.spi.SqlAliasBaseGenerator;
import org.hibernate.sql.ast.spi.SqlAliasBaseManager;
import org.hibernate.sql.ast.spi.SqlAstCreationContext;
import org.hibernate.sql.ast.spi.SqlAstCreationState;
import org.hibernate.sql.ast.spi.SqlAstProcessingState;
import org.hibernate.sql.ast.spi.SqlExpressionResolver;
import org.hibernate.sql.ast.spi.SqlSelection;
import org.hibernate.sql.ast.tree.expression.Expression;
import org.hibernate.type.descriptor.java.JavaTypeDescriptor;
import org.hibernate.type.spi.TypeConfiguration;
/**
* SqlAstCreationState implementation for result-set mapping handling
*
* @author Steve Ebersole
*/
@Internal
public class SqlAstCreationStateImpl implements SqlAstCreationState, SqlAstProcessingState, SqlExpressionResolver {
private final FromClauseAccessImpl fromClauseAccess;
private final SqlAliasBaseManager sqlAliasBaseManager;
private final Consumer<SqlSelection> sqlSelectionConsumer;
private final Map<String, SqlSelectionImpl> sqlSelectionMap = new HashMap<>();
private final SessionFactoryImplementor sessionFactory;
public SqlAstCreationStateImpl(
FromClauseAccessImpl fromClauseAccess,
SqlAliasBaseManager sqlAliasBaseManager,
Consumer<SqlSelection> sqlSelectionConsumer,
SessionFactoryImplementor sessionFactory) {
this.fromClauseAccess = fromClauseAccess;
this.sqlAliasBaseManager = sqlAliasBaseManager;
this.sqlSelectionConsumer = sqlSelectionConsumer;
this.sessionFactory = sessionFactory;
}
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// SqlAstProcessingState
@Override
public SqlAstProcessingState getParentState() {
return null;
}
@Override
public SqlAstCreationState getSqlAstCreationState() {
return this;
}
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// SqlAstCreationState
@Override
public SqlAstCreationContext getCreationContext() {
return sessionFactory;
}
@Override
public SqlAstCreationStateImpl getCurrentProcessingState() {
return this;
}
@Override
public SqlAstCreationStateImpl getSqlExpressionResolver() {
return this;
}
@Override
public FromClauseAccessImpl getFromClauseAccess() {
return fromClauseAccess;
}
@Override
public SqlAliasBaseGenerator getSqlAliasBaseGenerator() {
return sqlAliasBaseManager;
}
@Override
public LockMode determineLockMode(String identificationVariable) {
return LockMode.READ;
}
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// SqlExpressionResolver
@Override
public Expression resolveSqlExpression(
String key,
Function<SqlAstProcessingState, Expression> creator) {
return null;
}
@Override
public SqlSelection resolveSqlSelection(
Expression expression,
JavaTypeDescriptor javaTypeDescriptor,
TypeConfiguration typeConfiguration) {
return null;
}
}

View File

@ -39,10 +39,6 @@ public class SqmCreationProcessingStateImpl implements SqmCreationProcessingStat
return processingQuery;
}
protected SqmPathRegistryImpl getProcessingIndex() {
return processingIndex;
}
@Override
public SqmCreationState getCreationState() {
return creationState;

View File

@ -7,10 +7,9 @@
package org.hibernate.query.sqm.internal;
import org.hibernate.query.hql.spi.SqmCreationProcessingState;
import org.hibernate.query.hql.spi.SqmQuerySpecCreationProcessingState;
import org.hibernate.query.hql.spi.SqmCreationState;
import org.hibernate.query.hql.spi.SqmQuerySpecCreationProcessingState;
import org.hibernate.query.sqm.tree.select.SqmSelectQuery;
import org.hibernate.query.sqm.tree.select.SqmSelection;
/**
* Models the state related to parsing a sqm spec. As a "linked list" to account for
@ -42,19 +41,4 @@ public class SqmQuerySpecCreationProcessingStateStandardImpl
public SqmSelectQuery<?> getProcessingQuery() {
return (SqmSelectQuery<?>) super.getProcessingQuery();
}
@Override
public void registerSelection(SqmSelection selection) {
getProcessingIndex().register( selection );
}
@Override
public SqmSelection findSelectionByAlias(String alias) {
return getProcessingIndex().findSelectionByAlias( alias );
}
@Override
public SqmSelection findSelectionByPosition(int position) {
return getProcessingIndex().findSelectionByPosition( position );
}
}

View File

@ -219,10 +219,9 @@ public class MultiTableSqmMutationConverter extends BaseSqmToSqlAstConverter<Sta
sqlQuerySpec,
rootProcessingState,
this,
r -> new SqlSelectionForSqmSelectionResolver(
r -> new SqmAliasedNodePositionTracker(
r,
sqmSelectClause.getSelectionItems()
.size()
sqmSelectClause.getSelectionItems().size()
),
getCurrentClauseStack()::getCurrent
) {

View File

@ -23,6 +23,7 @@ import java.util.function.Supplier;
import javax.persistence.TemporalType;
import org.hibernate.HibernateException;
import org.hibernate.Internal;
import org.hibernate.LockMode;
import org.hibernate.LockOptions;
import org.hibernate.NotYetImplementedFor6Exception;
@ -124,6 +125,7 @@ import org.hibernate.query.sqm.tree.domain.SqmPluralValuedSimplePath;
import org.hibernate.query.sqm.tree.domain.SqmTreatedPath;
import org.hibernate.query.sqm.tree.expression.Conversion;
import org.hibernate.query.sqm.tree.expression.JpaCriteriaParameter;
import org.hibernate.query.sqm.tree.expression.SqmAliasedNodeRef;
import org.hibernate.query.sqm.tree.expression.SqmAny;
import org.hibernate.query.sqm.tree.expression.SqmBinaryArithmetic;
import org.hibernate.query.sqm.tree.expression.SqmByUnit;
@ -361,11 +363,20 @@ public abstract class BaseSqmToSqlAstConverter<T extends Statement> extends Base
this.statement = statement;
if ( statement instanceof SqmSelectStatement<?> ) {
// NOTE: note the difference here between `JpaSelection#getSelectionItems`
// and `SqmSelectClause#getSelections`.
//
// - `#getSelectionItems` returns individual select-items. "grouped" selections,
// such as dynamic-instantiation, unwrap themselves (possibly recursively).
// It is a JPA-defined method
//
// - `#getSelections` returns top-level selections. These are ultimately the
// domain-results of the query
this.domainResults = new ArrayList<>(
( (SqmSelectStatement<?>) statement ).getQueryPart()
.getFirstQuerySpec()
.getSelectClause()
.getSelectionItems()
.getSelections()
.size()
);
@ -885,7 +896,7 @@ public abstract class BaseSqmToSqlAstConverter<T extends Statement> extends Base
new SqlAstProcessingStateImpl(
null,
this,
r -> new SqlSelectionForSqmSelectionResolver(
r -> new SqmAliasedNodePositionTracker(
r,
selectQueryPart.getFirstQuerySpec()
.getSelectClause()
@ -1072,12 +1083,18 @@ public abstract class BaseSqmToSqlAstConverter<T extends Statement> extends Base
);
for ( SqmDynamicInstantiationArgument<?> sqmArgument : sqmDynamicInstantiation.getArguments() ) {
final DomainResultProducer<?> argumentResultProducer = (DomainResultProducer<?>) sqmArgument.getSelectableNode()
//noinspection StatementWithEmptyBody
if ( sqmArgument.getSelectableNode() instanceof SqmDynamicInstantiation ) {
// see discussion on `#visitSelection` wrt dynamic-instantiation
}
else {
currentSqlSelectionCollector().next();
}
final DomainResultProducer<?> argumentResultProducer = (DomainResultProducer<?>) sqmArgument
.getSelectableNode()
.accept( this );
dynamicInstantiation.addArgument(
sqmArgument.getAlias(),
argumentResultProducer
);
dynamicInstantiation.addArgument( sqmArgument.getAlias(), argumentResultProducer, this );
}
dynamicInstantiation.complete();
@ -1086,14 +1103,14 @@ public abstract class BaseSqmToSqlAstConverter<T extends Statement> extends Base
}
@SuppressWarnings("unchecked")
private <T> JavaTypeDescriptor<T> interpretInstantiationTarget(SqmDynamicInstantiationTarget<?> instantiationTarget) {
final Class<T> targetJavaType;
private <X> JavaTypeDescriptor<X> interpretInstantiationTarget(SqmDynamicInstantiationTarget<?> instantiationTarget) {
final Class<X> targetJavaType;
if ( instantiationTarget.getNature() == DynamicInstantiationNature.LIST ) {
targetJavaType = (Class<T>) List.class;
targetJavaType = (Class<X>) List.class;
}
else if ( instantiationTarget.getNature() == DynamicInstantiationNature.MAP ) {
targetJavaType = (Class<T>) Map.class;
targetJavaType = (Class<X>) Map.class;
}
else {
targetJavaType = instantiationTarget.getJavaType();
@ -1250,6 +1267,8 @@ public abstract class BaseSqmToSqlAstConverter<T extends Statement> extends Base
return cteStatements;
}
private boolean trackSelectionsForGroup;
@Override
public QueryPart visitQueryPart(SqmQueryPart<?> queryPart) {
return (QueryPart) super.visitQueryPart( queryPart );
@ -1265,26 +1284,36 @@ public abstract class BaseSqmToSqlAstConverter<T extends Statement> extends Base
queryGroup.getSetOperator(),
newQueryParts
);
if ( queryGroup.getOrderByClause() != null && queryGroup.getOrderByClause().hasPositionalSortItem() ) {
trackSelectionsForGroup = true;
}
final SqlAstQueryPartProcessingStateImpl processingState = new SqlAstQueryPartProcessingStateImpl(
group,
getCurrentProcessingState(),
this,
DelegatingSqlSelectionForSqmSelectionCollector::new,
DelegatingSqmAliasedNodeCollector::new,
currentClauseStack::getCurrent
);
final DelegatingSqlSelectionForSqmSelectionCollector collector = (DelegatingSqlSelectionForSqmSelectionCollector) processingState
final DelegatingSqmAliasedNodeCollector collector = (DelegatingSqmAliasedNodeCollector) processingState
.getSqlExpressionResolver();
pushProcessingState( processingState );
try {
newQueryParts.add( visitQueryPart( queryParts.get( 0 ) ) );
collector.setSqlSelectionForSqmSelectionCollector(
(SqlSelectionForSqmSelectionCollector) lastPoppedProcessingState.getSqlExpressionResolver()
collector.setSqmAliasedNodeCollector(
(SqmAliasedNodeCollector) lastPoppedProcessingState.getSqlExpressionResolver()
);
visitOrderByOffsetAndFetch( queryGroup, group );
trackSelectionsForGroup = false;
for ( int i = 1; i < size; i++ ) {
newQueryParts.add( visitQueryPart( queryParts.get( i ) ) );
}
visitOrderByOffsetAndFetch( queryGroup, group );
return group;
}
finally {
@ -1294,8 +1323,9 @@ public abstract class BaseSqmToSqlAstConverter<T extends Statement> extends Base
@Override
public QuerySpec visitQuerySpec(SqmQuerySpec<?> sqmQuerySpec) {
final boolean topLevel = getProcessingStateStack().isEmpty();
final QuerySpec sqlQuerySpec = new QuerySpec(
getProcessingStateStack().isEmpty(),
topLevel,
sqmQuerySpec.getFromClause().getNumberOfRoots()
);
final SqmSelectClause selectClause = sqmQuerySpec.getSelectClause();
@ -1303,22 +1333,45 @@ public abstract class BaseSqmToSqlAstConverter<T extends Statement> extends Base
Predicate originalAdditionalRestrictions = additionalRestrictions;
additionalRestrictions = null;
pushProcessingState(
new SqlAstQueryPartProcessingStateImpl(
sqlQuerySpec,
getCurrentProcessingState(),
this,
r -> new SqlSelectionForSqmSelectionResolver(
r,
selectClause.getSelectionItems()
.size()
),
currentClauseStack::getCurrent
)
);
final boolean trackAliasedNodePositions;
if ( trackSelectionsForGroup ) {
trackAliasedNodePositions = true;
}
else if ( sqmQuerySpec.getOrderByClause() != null && sqmQuerySpec.getOrderByClause().hasPositionalSortItem() ) {
trackAliasedNodePositions = true;
}
else if ( sqmQuerySpec.hasPositionalGroupItem() ) {
trackAliasedNodePositions = true;
}
else {
trackAliasedNodePositions = false;
}
final SqlAstProcessingState processingState;
if ( trackAliasedNodePositions ) {
processingState = new SqlAstQueryPartProcessingStateImpl(
sqlQuerySpec,
getCurrentProcessingState(),
this,
r -> new SqmAliasedNodePositionTracker(
r,
selectClause.getSelectionItems().size()
),
currentClauseStack::getCurrent
);
}
else {
processingState = new SqlAstQueryPartProcessingStateImpl(
sqlQuerySpec,
getCurrentProcessingState(),
this,
currentClauseStack::getCurrent
);
}
pushProcessingState( processingState );
try {
final boolean topLevel = sqlQuerySpec.isRoot();
if ( topLevel ) {
orderByFragmentConsumer = new StandardOrderByFragmentConsumer();
}
@ -1464,12 +1517,13 @@ public abstract class BaseSqmToSqlAstConverter<T extends Statement> extends Base
@Override
public Void visitSelection(SqmSelection<?> sqmSelection) {
currentSqlSelectionCollector().next();
final Map<String, DomainResultProducer<?>> resultProducers;
if ( sqmSelection.getSelectableNode() instanceof SqmJpaCompoundSelection<?> ) {
SqmJpaCompoundSelection<?> selectableNode = (SqmJpaCompoundSelection<?>) sqmSelection.getSelectableNode();
resultProducers = new HashMap<>( selectableNode.getSelectionItems().size() );
for ( SqmSelectableNode<?> selectionItem : selectableNode.getSelectionItems() ) {
currentSqlSelectionCollector().next();
resultProducers.put(
selectionItem.getAlias(),
(DomainResultProducer<?>) selectionItem.accept( this )
@ -1478,6 +1532,18 @@ public abstract class BaseSqmToSqlAstConverter<T extends Statement> extends Base
}
else {
//noinspection StatementWithEmptyBody
if ( sqmSelection.getSelectableNode() instanceof SqmDynamicInstantiation ) {
// this `currentSqlSelectionCollector().next()` is meant solely for resolving
// literal reference to a selection-item in the order-by or group-by clause.
// in the case of `SqmDynamicInstantiation`, that ordering should ignore that
// level here. visiting the dynamic-instantiation will manage this for its
// arguments
}
else {
// otherwise, position the collector at the next index in prep for visitation
currentSqlSelectionCollector().next();
}
resultProducers = Collections.singletonMap(
sqmSelection.getAlias(),
(DomainResultProducer<?>) sqmSelection.getSelectableNode().accept( this )
@ -1524,31 +1590,32 @@ public abstract class BaseSqmToSqlAstConverter<T extends Statement> extends Base
}
protected Expression resolveGroupOrOrderByExpression(SqmExpression<?> groupByClauseExpression) {
if ( groupByClauseExpression instanceof SqmLiteral<?> ) {
Object literal = ( (SqmLiteral<?>) groupByClauseExpression ).getLiteralValue();
if ( literal instanceof Integer ) {
// Integer literals have a special meaning in the GROUP BY and ORDER BY clause i.e. they refer to a select item
final int sqmPosition = (Integer) literal;
final List<SqlSelection> selections = currentSqlSelectionCollector().getSelections( sqmPosition );
final List<Expression> expressions = new ArrayList<>( selections.size() );
OUTER: for ( int i = 0; i < selections.size(); i++ ) {
final SqlSelection selection = selections.get( i );
// We skip duplicate selections which can occur when grouping/ordering by an entity alias.
// Duplication happens because the primary key of an entity usually acts as FK target of collections
// which is, just like the identifier itself, also registered as selection
for ( int j = 0; j < i; j++ ) {
if ( selections.get( j ) == selection ) {
continue OUTER;
}
if ( groupByClauseExpression instanceof SqmAliasedNodeRef ) {
final int aliasedNodeOrdinal = ( (SqmAliasedNodeRef) groupByClauseExpression ).getPosition();
final int sqmPosition = aliasedNodeOrdinal - 1;
final List<SqlSelection> selections = currentSqlSelectionCollector().getSelections( sqmPosition );
assert selections != null : String.format( Locale.ROOT, "No SqlSelections for SQM position `%s`", sqmPosition );
final List<Expression> expressions = new ArrayList<>( selections.size() );
OUTER: for ( int i = 0; i < selections.size(); i++ ) {
final SqlSelection selection = selections.get( i );
// We skip duplicate selections which can occur when grouping/ordering by an entity alias.
// Duplication happens because the primary key of an entity usually acts as FK target of collections
// which is, just like the identifier itself, also registered as selection
for ( int j = 0; j < i; j++ ) {
if ( selections.get( j ) == selection ) {
continue OUTER;
}
expressions.add( new SqlSelectionExpression( selection ) );
}
if ( expressions.size() == 1 ) {
return expressions.get( 0 );
}
return new SqlTuple( expressions, null );
expressions.add( new SqlSelectionExpression( selection ) );
}
if ( expressions.size() == 1 ) {
return expressions.get( 0 );
}
return new SqlTuple( expressions, null );
}
return (Expression) groupByClauseExpression.accept( this );
}
@ -2064,8 +2131,8 @@ public abstract class BaseSqmToSqlAstConverter<T extends Statement> extends Base
return processingState.getInflightQueryPart();
}
protected SqlSelectionForSqmSelectionCollector currentSqlSelectionCollector() {
return (SqlSelectionForSqmSelectionCollector) getCurrentProcessingState().getSqlExpressionResolver();
protected SqmAliasedNodeCollector currentSqlSelectionCollector() {
return (SqmAliasedNodeCollector) getCurrentProcessingState().getSqlExpressionResolver();
}
@Override
@ -4502,17 +4569,18 @@ public abstract class BaseSqmToSqlAstConverter<T extends Statement> extends Base
}
}
protected interface SqlSelectionForSqmSelectionCollector {
@Internal
public interface SqmAliasedNodeCollector {
void next();
List<SqlSelection> getSelections(int position);
}
protected static class DelegatingSqlSelectionForSqmSelectionCollector implements SqlExpressionResolver, SqlSelectionForSqmSelectionCollector {
protected static class DelegatingSqmAliasedNodeCollector implements SqlExpressionResolver, SqmAliasedNodeCollector {
private final SqlExpressionResolver delegate;
private SqlSelectionForSqmSelectionCollector sqlSelectionForSqmSelectionCollector;
private SqmAliasedNodeCollector sqmAliasedNodeCollector;
public DelegatingSqlSelectionForSqmSelectionCollector(SqlExpressionResolver delegate) {
public DelegatingSqmAliasedNodeCollector(SqlExpressionResolver delegate) {
this.delegate = delegate;
}
@ -4523,7 +4591,7 @@ public abstract class BaseSqmToSqlAstConverter<T extends Statement> extends Base
@Override
public List<SqlSelection> getSelections(int position) {
return sqlSelectionForSqmSelectionCollector.getSelections( position );
return sqmAliasedNodeCollector.getSelections( position );
}
@Override
@ -4539,22 +4607,21 @@ public abstract class BaseSqmToSqlAstConverter<T extends Statement> extends Base
return delegate.resolveSqlSelection( expression, javaTypeDescriptor, typeConfiguration );
}
public SqlSelectionForSqmSelectionCollector getSqlSelectionForSqmSelectionCollector() {
return sqlSelectionForSqmSelectionCollector;
public SqmAliasedNodeCollector getSqmAliasedNodeCollector() {
return sqmAliasedNodeCollector;
}
public void setSqlSelectionForSqmSelectionCollector(SqlSelectionForSqmSelectionCollector sqlSelectionForSqmSelectionCollector) {
this.sqlSelectionForSqmSelectionCollector = sqlSelectionForSqmSelectionCollector;
public void setSqmAliasedNodeCollector(SqmAliasedNodeCollector sqmAliasedNodeCollector) {
this.sqmAliasedNodeCollector = sqmAliasedNodeCollector;
}
}
protected static class SqlSelectionForSqmSelectionResolver implements SqlExpressionResolver, SqlSelectionForSqmSelectionCollector {
protected static class SqmAliasedNodePositionTracker implements SqlExpressionResolver, SqmAliasedNodeCollector {
private final SqlExpressionResolver delegate;
private final List<SqlSelection>[] sqlSelectionsForSqmSelection;
private int index = -1;
public SqlSelectionForSqmSelectionResolver(SqlExpressionResolver delegate, int sqmSelectionCount) {
public SqmAliasedNodePositionTracker(SqlExpressionResolver delegate, int sqmSelectionCount) {
this.delegate = delegate;
sqlSelectionsForSqmSelection = new List[sqmSelectionCount];
}
@ -4566,7 +4633,7 @@ public abstract class BaseSqmToSqlAstConverter<T extends Statement> extends Base
@Override
public List<SqlSelection> getSelections(int position) {
return sqlSelectionsForSqmSelection[position - 1];
return sqlSelectionsForSqmSelection[ position ];
}
@Override

View File

@ -8,10 +8,13 @@ package org.hibernate.query.sqm.sql.internal;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.function.Function;
import java.util.function.Supplier;
import org.hibernate.query.sqm.sql.BaseSqmToSqlAstConverter;
import org.hibernate.query.sqm.sql.BaseSqmToSqlAstConverter.SqmAliasedNodeCollector;
import org.hibernate.query.sqm.sql.ConversionException;
import org.hibernate.sql.ast.Clause;
import org.hibernate.sql.ast.spi.SqlAstCreationState;
@ -29,7 +32,8 @@ import org.hibernate.type.spi.TypeConfiguration;
*
* @author Steve Ebersole
*/
public class SqlAstProcessingStateImpl implements SqlAstProcessingState, SqlExpressionResolver {
public class SqlAstProcessingStateImpl
implements SqlAstProcessingState, SqlExpressionResolver, SqmAliasedNodeCollector {
private final SqlAstProcessingState parentState;
private final SqlAstCreationState creationState;
private final SqlExpressionResolver expressionResolver;
@ -141,4 +145,15 @@ public class SqlAstProcessingStateImpl implements SqlAstProcessingState, SqlExpr
TypeConfiguration typeConfiguration) {
throw new ConversionException( "Unexpected call to resolve SqlSelection outside of QuerySpec processing" );
}
@Override
public void next() {
// nothing to do
int i = 1;
}
@Override
public List<SqlSelection> getSelections(int position) {
throw new UnsupportedOperationException();
}
}

View File

@ -0,0 +1,36 @@
/*
* 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.query.sqm.tree.expression;
import org.hibernate.query.sqm.NodeBuilder;
import org.hibernate.query.sqm.SemanticQueryWalker;
import org.hibernate.query.sqm.SqmExpressable;
/**
* Models a reference to a {@link org.hibernate.query.sqm.tree.select.SqmAliasedNode}
* used in the order-by or group-by clause by either position or alias,
* though the reference is normalized here to a positional ref
*/
public class SqmAliasedNodeRef extends AbstractSqmExpression<Integer> {
private final int position;
public SqmAliasedNodeRef(int position, SqmExpressable<Integer> intType, NodeBuilder criteriaBuilder) {
super( intType, criteriaBuilder );
this.position = position;
}
public int getPosition() {
return position;
}
@Override
public <X> X accept(SemanticQueryWalker<X> walker) {
// we expect this to be handled specially in
// `BaseSqmToSqlAstConverter#resolveGroupOrOrderByExpression`
throw new UnsupportedOperationException();
}
}

View File

@ -10,23 +10,32 @@ import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import org.hibernate.query.sqm.tree.expression.SqmAliasedNodeRef;
import org.hibernate.query.sqm.tree.expression.SqmExpression;
/**
* @author Steve Ebersole
*/
public class SqmOrderByClause {
private boolean hasPositionalSortItem;
private List<SqmSortSpecification> sortSpecifications;
public SqmOrderByClause() {
}
public boolean hasPositionalSortItem() {
return hasPositionalSortItem;
}
@SuppressWarnings("UnusedReturnValue")
public SqmOrderByClause addSortSpecification(SqmSortSpecification sortSpecification) {
if ( sortSpecifications == null ) {
sortSpecifications = new ArrayList<>();
}
sortSpecifications.add( sortSpecification );
if ( sortSpecification.getExpression() instanceof SqmAliasedNodeRef ) {
hasPositionalSortItem = true;
}
return this;
}
@ -49,5 +58,11 @@ public class SqmOrderByClause {
public void setSortSpecifications(List<SqmSortSpecification> sortSpecifications) {
this.sortSpecifications = new ArrayList<>();
this.sortSpecifications.addAll( sortSpecifications );
for ( int i = 0; i < sortSpecifications.size(); i++ ) {
final SqmSortSpecification sortSpecification = sortSpecifications.get( i );
if ( sortSpecification.getExpression() instanceof SqmAliasedNodeRef ) {
hasPositionalSortItem = true;
}
}
}
}

View File

@ -83,6 +83,7 @@ public class SqmQueryGroup<T> extends SqmQueryPart<T> implements JpaQueryGroup<T
this.setOperator = setOperator;
}
@Override
public SqmQueryGroup<T> setSortSpecifications(List<? extends JpaOrder> sortSpecifications) {
return (SqmQueryGroup<T>) super.setSortSpecifications( sortSpecifications );

View File

@ -24,6 +24,7 @@ import org.hibernate.query.criteria.JpaSelection;
import org.hibernate.query.sqm.NodeBuilder;
import org.hibernate.query.sqm.SemanticQueryWalker;
import org.hibernate.query.sqm.tree.SqmNode;
import org.hibernate.query.sqm.tree.expression.SqmAliasedNodeRef;
import org.hibernate.query.sqm.tree.expression.SqmExpression;
import org.hibernate.query.sqm.tree.from.SqmAttributeJoin;
import org.hibernate.query.sqm.tree.from.SqmFrom;
@ -46,6 +47,7 @@ public class SqmQuerySpec<T> extends SqmQueryPart<T>
private SqmSelectClause selectClause;
private SqmWhereClause whereClause;
private boolean hasPositionalGroupItem;
private List<SqmExpression<?>> groupByClauseExpressions = Collections.emptyList();
private SqmPredicate havingClausePredicate;
@ -129,6 +131,10 @@ public class SqmQuerySpec<T> extends SqmQueryPart<T>
whereClause.applyPredicate( predicate );
}
public boolean hasPositionalGroupItem() {
return hasPositionalGroupItem;
}
public List<SqmExpression<?>> getGroupByClauseExpressions() {
return groupByClauseExpressions;
}
@ -137,6 +143,13 @@ public class SqmQuerySpec<T> extends SqmQueryPart<T>
this.groupByClauseExpressions = groupByClauseExpressions == null
? Collections.emptyList()
: groupByClauseExpressions;
for ( int i = 0; i < groupByClauseExpressions.size(); i++ ) {
final SqmExpression<?> groupItem = groupByClauseExpressions.get( i );
if ( groupItem instanceof SqmAliasedNodeRef ) {
hasPositionalGroupItem = true;
}
}
}
public SqmPredicate getHavingClausePredicate() {

View File

@ -12,6 +12,7 @@ import java.util.function.Consumer;
import org.hibernate.query.FetchClauseType;
import org.hibernate.query.sqm.sql.internal.DomainResultProducer;
import org.hibernate.query.sqm.tree.expression.SqmAliasedNodeRef;
import org.hibernate.sql.ast.tree.SqlAstNode;
import org.hibernate.sql.ast.tree.expression.Expression;
@ -19,9 +20,11 @@ import org.hibernate.sql.ast.tree.expression.Expression;
* @author Christian Beikov
*/
public abstract class QueryPart implements SqlAstNode, Expression, DomainResultProducer {
private final boolean isRoot;
private boolean hasPositionalSortItem;
private List<SortSpecification> sortSpecifications;
private Expression offsetClauseExpression;
private Expression fetchClauseExpression;
private FetchClauseType fetchClauseType = FetchClauseType.ROWS_ONLY;
@ -48,6 +51,10 @@ public abstract class QueryPart implements SqlAstNode, Expression, DomainResultP
return sortSpecifications != null && !sortSpecifications.isEmpty();
}
public boolean hasPositionalSortItem() {
return hasPositionalSortItem;
}
public List<SortSpecification> getSortSpecifications() {
return sortSpecifications;
}
@ -63,6 +70,12 @@ public abstract class QueryPart implements SqlAstNode, Expression, DomainResultP
sortSpecifications = new ArrayList<>();
}
sortSpecifications.add( specification );
if ( isRoot ) {
if ( specification.getSortExpression() instanceof SqmAliasedNodeRef ) {
hasPositionalSortItem = true;
}
}
}
public boolean hasOffsetOrFetchClause() {

View File

@ -12,6 +12,7 @@ import java.util.function.Consumer;
import org.hibernate.metamodel.mapping.MappingModelExpressable;
import org.hibernate.query.sqm.sql.internal.DomainResultProducer;
import org.hibernate.query.sqm.tree.expression.SqmAliasedNodeRef;
import org.hibernate.sql.ast.SqlAstWalker;
import org.hibernate.sql.ast.spi.SqlAstTreeHelper;
import org.hibernate.sql.ast.spi.SqlExpressionResolver;
@ -37,6 +38,7 @@ public class QuerySpec extends QueryPart implements SqlAstNode, PredicateContain
private Predicate whereClauseRestrictions;
private boolean hasPositionalGroupItem;
private List<Expression> groupByClauseExpressions = Collections.emptyList();
private Predicate havingClauseRestrictions;
@ -86,8 +88,20 @@ public class QuerySpec extends QueryPart implements SqlAstNode, PredicateContain
return groupByClauseExpressions;
}
public boolean hasPositionalGroupItem() {
return hasPositionalGroupItem;
}
public void setGroupByClauseExpressions(List<Expression> groupByClauseExpressions) {
this.groupByClauseExpressions = groupByClauseExpressions == null ? Collections.emptyList() : groupByClauseExpressions;
if ( isRoot() ) {
for ( int i = 0; i < groupByClauseExpressions.size(); i++ ) {
final Expression groupItem = groupByClauseExpressions.get( i );
if ( groupItem instanceof SqmAliasedNodeRef ) {
hasPositionalGroupItem = true;
}
}
}
}
public Predicate getHavingClauseRestrictions() {

View File

@ -58,7 +58,7 @@ public class DynamicInstantiation<T> implements DomainResultProducer {
return getTargetJavaTypeDescriptor().getJavaTypeClass();
}
public void addArgument(String alias, DomainResultProducer argumentResultProducer) {
public void addArgument(String alias, DomainResultProducer<?> argumentResultProducer, DomainResultCreationState creationState) {
if ( argumentAdditionsComplete ) {
throw new ConversionException( "Unexpected call to DynamicInstantiation#addAgument after previously complete" );
}
@ -90,7 +90,7 @@ public class DynamicInstantiation<T> implements DomainResultProducer {
arguments = new ArrayList<>();
}
arguments.add( new DynamicInstantiationArgument( argumentResultProducer, alias ) );
arguments.add( new DynamicInstantiationArgument<>( alias, argumentResultProducer, creationState ) );
}
public void complete() {

View File

@ -7,18 +7,22 @@
package org.hibernate.sql.results.graph.instantiation.internal;
import org.hibernate.query.sqm.sql.internal.DomainResultProducer;
import org.hibernate.sql.results.graph.DomainResult;
import org.hibernate.sql.results.graph.DomainResultCreationState;
/**
* @author Steve Ebersole
*/
public class DynamicInstantiationArgument {
private final DomainResultProducer argumentResultProducer;
public class DynamicInstantiationArgument<T> {
private final ArgumentDomainResult<T> argumentResult;
private final String alias;
@SuppressWarnings("WeakerAccess")
public DynamicInstantiationArgument(DomainResultProducer argumentResultProducer, String alias) {
this.argumentResultProducer = argumentResultProducer;
public DynamicInstantiationArgument(
String alias,
DomainResultProducer<T> argumentResultProducer,
DomainResultCreationState creationState) {
this.argumentResult = new ArgumentDomainResult<>( argumentResultProducer.createDomainResult( alias, creationState ) );
this.alias = alias;
}
@ -26,9 +30,7 @@ public class DynamicInstantiationArgument {
return alias;
}
public ArgumentDomainResult buildArgumentDomainResult(DomainResultCreationState creationState) {
return new ArgumentDomainResult(
argumentResultProducer.createDomainResult( alias, creationState )
);
public ArgumentDomainResult<T> buildArgumentDomainResult(DomainResultCreationState creationState) {
return argumentResult;
}
}

View File

@ -1,10 +1,10 @@
/*
* 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>.
* 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.set;
package org.hibernate.orm.test.query.hql.org.hibernate.orm.test.query.hql.set;
import java.util.List;

View File

@ -612,18 +612,18 @@ public class ASTParserLoadingOrderByTest extends BaseCoreFunctionalTestCase {
// ordered by address, name:
// zoo3 Zoo 1312 Mockingbird Lane, Anywhere, IL USA
// zoo4 Duh Zoo 1312 Mockingbird Lane, Nowhere, IL USA
// zoo2 A Zoo 1313 Mockingbird Lane, Anywhere, IL USA
// zoo1 Zoo 1313 Mockingbird Lane, Anywhere, IL USA
// zoo4 Duh Zoo 1312 Mockingbird Lane, Nowhere, IL USA
list =
s.createQuery(
"select new Zoo( z.name as zname, z.address as zaddress) from Zoo z order by zaddress, zname"
).list();
assertEquals( 4, list.size() );
assertEquals( zoo3, list.get( 0 ) );
assertEquals( zoo4, list.get( 1 ) );
assertEquals( zoo2, list.get( 2 ) );
assertEquals( zoo1, list.get( 3 ) );
assertEquals( zoo2, list.get( 1 ) );
assertEquals( zoo1, list.get( 2 ) );
assertEquals( zoo4, list.get( 3 ) );
t.commit();
@ -660,22 +660,26 @@ public class ASTParserLoadingOrderByTest extends BaseCoreFunctionalTestCase {
// ordered by address, name:
// zoo3 Zoo 1312 Mockingbird Lane, Anywhere, IL USA
// zoo4 Duh Zoo 1312 Mockingbird Lane, Nowhere, IL USA
// zoo2 A Zoo 1313 Mockingbird Lane, Anywhere, IL USA
// zoo1 Zoo 1313 Mockingbird Lane, Anywhere, IL USA
// zoo4 Duh Zoo 1312 Mockingbird Lane, Nowhere, IL USA
list =
s.createQuery(
"select new map( z.name as zname, z.address as zaddress ) from Zoo z left join z.mammals m order by zaddress, zname"
).list();
assertEquals( 4, list.size() );
assertEquals( zoo3.getName(), ( ( Map ) list.get( 0 ) ).get( "zname" ) );
assertEquals( zoo3.getAddress(), ( ( Map ) list.get( 0 ) ).get( "zaddress" ) );
assertEquals( zoo4.getName(), ( ( Map ) list.get( 1 ) ).get( "zname" ) );
assertEquals( zoo4.getAddress(), ( ( Map ) list.get( 1 ) ).get( "zaddress" ) );
assertEquals( zoo2.getName(), ( ( Map ) list.get( 2 ) ).get( "zname" ) );
assertEquals( zoo2.getAddress(), ( ( Map ) list.get( 2 ) ).get( "zaddress" ) );
assertEquals( zoo1.getName(), ( ( Map ) list.get( 3 ) ).get( "zname" ) );
assertEquals( zoo1.getAddress(), ( ( Map ) list.get( 3 ) ).get( "zaddress" ) );
assertEquals( zoo2.getName(), ( ( Map ) list.get( 1 ) ).get( "zname" ) );
assertEquals( zoo2.getAddress(), ( ( Map ) list.get( 1 ) ).get( "zaddress" ) );
assertEquals( zoo1.getName(), ( ( Map ) list.get( 2 ) ).get( "zname" ) );
assertEquals( zoo1.getAddress(), ( ( Map ) list.get( 2 ) ).get( "zaddress" ) );
assertEquals( zoo4.getName(), ( ( Map ) list.get( 3 ) ).get( "zname" ) );
assertEquals( zoo4.getAddress(), ( ( Map ) list.get( 3 ) ).get( "zaddress" ) );
t.commit();
s.close();