Skip to content

minor cleanups to SqmQueryGroup #10423

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Jun 27, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,11 @@
package org.hibernate.query.sqm.tree.select;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.Objects;

import org.hibernate.internal.util.collections.CollectionHelper;
import org.hibernate.AssertionFailure;
import org.hibernate.query.common.FetchClauseType;
import org.hibernate.query.SemanticException;
import org.hibernate.query.sqm.SetOperator;
Expand All @@ -27,6 +26,9 @@
import org.hibernate.query.sqm.tree.from.SqmJoin;
import org.hibernate.type.descriptor.java.JavaType;

import static java.util.Collections.unmodifiableList;
import static org.hibernate.internal.util.collections.CollectionHelper.listOf;

/**
* A grouped list of queries connected through a certain set operator.
*
Expand All @@ -38,7 +40,7 @@
private SetOperator setOperator;

public SqmQueryGroup(SqmQueryPart<T> queryPart) {
this( queryPart.nodeBuilder(), null, CollectionHelper.listOf( queryPart ) );
this( queryPart.nodeBuilder(), null, listOf( queryPart ) );
}

public SqmQueryGroup(
Expand All @@ -56,20 +58,17 @@
if ( existing != null ) {
return existing;
}
final List<SqmQueryPart<T>> queryParts = new ArrayList<>( this.queryParts.size() );
for ( SqmQueryPart<T> queryPart : this.queryParts ) {
queryParts.add( queryPart.copy( context ) );
else {
final List<SqmQueryPart<T>> queryParts = new ArrayList<>( this.queryParts.size() );

Check notice

Code scanning / CodeQL

Possible confusion of local and field Note

Potentially confusing name: method
copy
also refers to field
queryParts
(as this.queryParts).
for ( SqmQueryPart<T> queryPart : this.queryParts ) {
queryParts.add( queryPart.copy( context ) );
}
final SqmQueryGroup<T> queryGroup =
context.registerCopy( this,
new SqmQueryGroup<>( nodeBuilder(), setOperator, queryParts ) );
copyTo( queryGroup, context );
return queryGroup;
}
final SqmQueryGroup<T> queryGroup = context.registerCopy(
this,
new SqmQueryGroup<>(
nodeBuilder(),
setOperator,
queryParts
)
);
copyTo( queryGroup, context );
return queryGroup;
}

public List<SqmQueryPart<T>> queryParts() {
Expand All @@ -88,7 +87,9 @@

@Override
public boolean isSimpleQueryPart() {
return setOperator == null && queryParts.size() == 1 && queryParts.get( 0 ).isSimpleQueryPart();
return setOperator == null
&& queryParts.size() == 1
&& queryParts.get( 0 ).isSimpleQueryPart();
}

@Override
Expand All @@ -98,7 +99,7 @@

@Override
public List<SqmQueryPart<T>> getQueryParts() {
return Collections.unmodifiableList( queryParts );
return unmodifiableList( queryParts );
}

public SetOperator getSetOperator() {
Expand Down Expand Up @@ -156,12 +157,11 @@
final int firstSelectionSize = typedNodes.size();
for ( int i = 0; i < queryParts.size(); i++ ) {
final SqmQueryPart<T> queryPart = queryParts.get( i );
if ( queryPart instanceof SqmQueryGroup<?> ) {
( (SqmQueryGroup<?>) queryPart ).validateQueryGroupFetchStructure( typedNodes );
if ( queryPart instanceof SqmQueryGroup<?> queryGroup ) {
queryGroup.validateQueryGroupFetchStructure( typedNodes );
}
else {
final SqmQuerySpec<?> querySpec = (SqmQuerySpec<?>) queryPart;
final List<SqmSelection<?>> selections = querySpec.getSelectClause().getSelections();
else if ( queryPart instanceof SqmQuerySpec<?> querySpec ) {
final var selections = querySpec.getSelectClause().getSelections();
if ( firstSelectionSize != selections.size() ) {
throw new SemanticException( "All query parts in a query group must have the same arity" );
}
Expand All @@ -175,31 +175,25 @@
);
}
if ( firstSqmSelection instanceof SqmFrom<?, ?> firstFrom ) {
final SqmFrom<?, ?> from = (SqmFrom<?, ?>) selections.get( j ).getSelectableNode();
final var from = (SqmFrom<?, ?>) selections.get( j ).getSelectableNode();
validateFetchesMatch( firstFrom, from );
}
}
}
else {
throw new AssertionFailure( "Unrecognized SqmQueryPart subtype" );
}
}
}

private void validateFetchesMatch(SqmFrom<?, ?> firstFrom, SqmFrom<?, ?> from) {
final Iterator<? extends SqmJoin<?, ?>> firstJoinIter = firstFrom.getSqmJoins().iterator();
final Iterator<? extends SqmJoin<?, ?>> joinIter = from.getSqmJoins().iterator();
final var firstJoinIter = firstFrom.getSqmJoins().iterator();
final var joinIter = from.getSqmJoins().iterator();
while ( firstJoinIter.hasNext() ) {
final SqmJoin<?, ?> firstSqmJoin = firstJoinIter.next();
if ( firstSqmJoin instanceof SqmAttributeJoin<?, ?> firstAttrJoin ) {
if ( firstAttrJoin.isFetched() ) {
SqmAttributeJoin<?, ?> matchingAttrJoin = null;
while ( joinIter.hasNext() ) {
final SqmJoin<?, ?> sqmJoin = joinIter.next();
if ( sqmJoin instanceof SqmAttributeJoin<?, ?> attrJoin ) {
if ( attrJoin.isFetched() ) {
matchingAttrJoin = attrJoin;
break;
}
}
}
final var matchingAttrJoin = findFirstFetchJoin( joinIter );
if ( matchingAttrJoin == null || firstAttrJoin.getModel() != matchingAttrJoin.getModel() ) {
throw new SemanticException(
"All query parts in a query group must have the same join fetches in the same order"
Expand All @@ -211,15 +205,23 @@
}
// At this point, the other iterator should only contain non-fetch joins
while ( joinIter.hasNext() ) {
final SqmJoin<?, ?> sqmJoin = joinIter.next();
if ( sqmJoin instanceof SqmAttributeJoin<?, ?> attrJoin ) {
if ( attrJoin.isFetched() ) {
throw new SemanticException(
"All query parts in a query group must have the same join fetches in the same order"
);
}
if ( joinIter.next() instanceof SqmAttributeJoin<?, ?> attrJoin
&& attrJoin.isFetched() ) {
throw new SemanticException(
"All query parts in a query group must have the same join fetches in the same order"
);
}
}
}

private static SqmAttributeJoin<?, ?> findFirstFetchJoin(Iterator<? extends SqmJoin<?, ?>> joinIter) {
while ( joinIter.hasNext() ) {
if ( joinIter.next() instanceof SqmAttributeJoin<?, ?> attrJoin
&& attrJoin.isFetched() ) {
return attrJoin;
}
}
return null;
}

@Override
Expand Down
4 changes: 2 additions & 2 deletions hibernate-core/src/main/java/org/hibernate/sql/Template.java
Original file line number Diff line number Diff line change
Expand Up @@ -145,13 +145,13 @@ public static String renderWhereStringTemplate(

// IMPL NOTE: The basic process here is to tokenize the incoming string and to iterate over each token
// in turn. As we process each token, we set a series of flags used to indicate the type of context in
// which the tokens occur. Depending on the state of those flags we decide whether we need to qualify
// which the tokens occur. Depending on the state of those flags, we decide whether we need to qualify
// identifier references.

// WARNING TO MAINTAINERS: This is a simple scanner-based state machine. Please don't attempt to turn it into
// a parser for SQL, no matter how "special" your case is. What I mean by this is: don't write code which
// attempts to recognize the grammar of SQL, not even little bits of SQL. Previous "enhancements" to this
// function did not respect this concept, and resulted in code which was fragile and unmaintainable. If
// function did not respect this concept and resulted in code which was fragile and unmaintainable. If
// lookahead is truly necessary, use the lookahead() function provided below.

final String symbols = PUNCTUATION + WHITESPACE + dialect.openQuote() + dialect.closeQuote();
Expand Down
Loading