Skip to content

HHH-17522 Test and fix correlation of CTE and derived roots and joins #10720

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 1 commit into from
Aug 8, 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 @@ -17,19 +17,18 @@
import org.hibernate.metamodel.model.domain.BasicDomainType;
import org.hibernate.metamodel.model.domain.spi.JpaMetamodelImplementor;
import org.hibernate.query.SemanticException;
import org.hibernate.query.criteria.JpaJoinedFrom;
import org.hibernate.query.hql.HqlLogging;
import org.hibernate.query.hql.spi.SqmCreationProcessingState;
import org.hibernate.query.hql.spi.SqmPathRegistry;
import org.hibernate.query.sqm.AliasCollisionException;
import org.hibernate.query.sqm.ParsingException;
import org.hibernate.query.sqm.SqmPathSource;
import org.hibernate.query.sqm.SqmTreeCreationLogger;
import org.hibernate.query.sqm.tree.domain.AbstractSqmFrom;
import org.hibernate.query.sqm.tree.domain.SqmCorrelation;
import org.hibernate.query.sqm.tree.domain.SqmPath;
import org.hibernate.query.sqm.tree.from.SqmCrossJoin;
import org.hibernate.query.sqm.tree.from.SqmEntityJoin;
import org.hibernate.query.sqm.tree.from.SqmFrom;
import org.hibernate.query.sqm.tree.from.SqmJoin;
import org.hibernate.query.sqm.tree.from.SqmRoot;
import org.hibernate.query.sqm.tree.from.*;
import org.hibernate.query.sqm.tree.select.SqmAliasedNode;
import org.hibernate.query.sqm.tree.select.SqmSubQuery;
import org.hibernate.spi.NavigablePath;
Expand Down Expand Up @@ -244,6 +243,12 @@ else if ( parentRegistered instanceof SqmCrossJoin<?> ) {
else if ( parentRegistered instanceof SqmEntityJoin<?> ) {
correlated = selectQuery.correlate( (SqmEntityJoin<?>) parentRegistered );
}
else if ( parentRegistered instanceof AbstractSqmFrom<?, ?>) {
final SqmCorrelation<?, ?> correlation =
((AbstractSqmFrom<?, ?>) parentRegistered).createCorrelation();
selectQuery.getQuerySpec().addRoot( correlation.getCorrelatedRoot() );
correlated = correlation;
}
else {
throw new UnsupportedOperationException( "Can't correlate from node: " + parentRegistered );
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@
import org.hibernate.query.sqm.tree.domain.SqmBasicValuedSimplePath;
import org.hibernate.query.sqm.tree.domain.SqmCorrelatedBagJoin;
import org.hibernate.query.sqm.tree.domain.SqmCorrelatedCrossJoin;
import org.hibernate.query.sqm.tree.domain.SqmCorrelatedCteJoin;
import org.hibernate.query.sqm.tree.domain.SqmCorrelatedDerivedJoin;
import org.hibernate.query.sqm.tree.domain.SqmCorrelatedEntityJoin;
import org.hibernate.query.sqm.tree.domain.SqmCorrelatedListJoin;
import org.hibernate.query.sqm.tree.domain.SqmCorrelatedMapJoin;
Expand Down Expand Up @@ -170,6 +172,14 @@ public interface SemanticQueryWalker<T> {

T visitQualifiedAttributeJoin(SqmAttributeJoin<?, ?> joinedFromElement);

default T visitCorrelatedCteJoin(SqmCorrelatedCteJoin<?> join){
return visitQualifiedCteJoin( join );
}

default T visitCorrelatedDerivedJoin(SqmCorrelatedDerivedJoin<?> join){
return visitQualifiedDerivedJoin( join );
}

default T visitCorrelatedCrossJoin(SqmCorrelatedCrossJoin<?> join) {
return visitCrossJoin( join );
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,7 @@ public SqmPathSource<T> getModel() {
public SqmPathSource<?> getResolvedModel() {
final DomainType<?> lhsType;
final SqmPathSource<T> pathSource = getReferencedPathSource();

if ( pathSource.isGeneric() && ( lhsType = getLhs().getResolvedModel().getSqmPathType() ) instanceof ManagedDomainType ) {
final PersistentAttribute<?, ?> concreteAttribute = ( (ManagedDomainType<?>) lhsType ).findConcreteGenericAttribute(
pathSource.getPathName()
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
/*
* 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.domain;

import org.hibernate.query.sqm.SemanticQueryWalker;
import org.hibernate.query.sqm.SqmPathSource;
import org.hibernate.query.sqm.tree.SqmCopyContext;
import org.hibernate.query.sqm.tree.SqmJoinType;
import org.hibernate.query.sqm.tree.cte.SqmCteStatement;
import org.hibernate.query.sqm.tree.from.SqmCteJoin;
import org.hibernate.query.sqm.tree.from.SqmRoot;
import org.hibernate.spi.NavigablePath;

/**
* @author Christian Beikov
*/
public class SqmCorrelatedCteJoin<T> extends SqmCteJoin<T> implements SqmCorrelation<T, T> {

private final SqmCorrelatedRootJoin<T> correlatedRootJoin;
private final SqmCteJoin<T> correlationParent;

public SqmCorrelatedCteJoin(SqmCteJoin<T> correlationParent) {
//noinspection unchecked
super(
correlationParent.getCte(),
correlationParent.getExplicitAlias(),
correlationParent.getSqmJoinType(),
(SqmRoot<T>) correlationParent.getRoot()
);
this.correlatedRootJoin = SqmCorrelatedDerivedRootJoin.create( correlationParent, this );
this.correlationParent = correlationParent;
}

private SqmCorrelatedCteJoin(
NavigablePath navigablePath,
SqmCteStatement<T> cte,
SqmPathSource<T> pathSource,
String alias,
SqmJoinType joinType,
SqmRoot<T> sqmRoot,
SqmCorrelatedRootJoin<T> correlatedRootJoin,
SqmCteJoin<T> correlationParent) {
super( navigablePath, cte, pathSource, alias, joinType, sqmRoot );
this.correlatedRootJoin = correlatedRootJoin;
this.correlationParent = correlationParent;
}

@Override
public SqmCorrelatedCteJoin<T> copy(SqmCopyContext context) {
final SqmCorrelatedCteJoin<T> existing = context.getCopy( this );
if ( existing != null ) {
return existing;
}
final SqmCorrelatedCteJoin<T> path = context.registerCopy(
this,
new SqmCorrelatedCteJoin<>(
getNavigablePath(),
getCte().copy( context ),
getReferencedPathSource(),
getExplicitAlias(),
getSqmJoinType(),
(SqmRoot<T>) findRoot().copy( context ),
correlatedRootJoin.copy( context ),
correlationParent.copy( context )
)
);
copyTo( path, context );
return path;
}

@Override
public SqmCteJoin<T> getCorrelationParent() {
return correlationParent;
}

@Override
public SqmPath<T> getWrappedPath() {
return correlationParent;
}

@Override
public boolean isCorrelated() {
return true;
}

@Override
public SqmRoot<T> getCorrelatedRoot() {
return correlatedRootJoin;
}

@Override
public <X> X accept(SemanticQueryWalker<X> walker) {
return walker.visitCorrelatedCteJoin( this );
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
/*
* 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.domain;

import org.hibernate.query.sqm.SemanticQueryWalker;
import org.hibernate.query.sqm.SqmPathSource;
import org.hibernate.query.sqm.tree.SqmCopyContext;
import org.hibernate.query.sqm.tree.SqmJoinType;
import org.hibernate.query.sqm.tree.from.SqmDerivedJoin;
import org.hibernate.query.sqm.tree.from.SqmRoot;
import org.hibernate.query.sqm.tree.select.SqmSubQuery;
import org.hibernate.spi.NavigablePath;

/**
* @author Christian Beikov
*/
public class SqmCorrelatedDerivedJoin<T> extends SqmDerivedJoin<T> implements SqmCorrelation<T, T> {

private final SqmCorrelatedRootJoin<T> correlatedRootJoin;
private final SqmDerivedJoin<T> correlationParent;

public SqmCorrelatedDerivedJoin(SqmDerivedJoin<T> correlationParent) {
//noinspection unchecked
super(
correlationParent.getNavigablePath(),
correlationParent.getQueryPart(),
correlationParent.isLateral(),
correlationParent.getReferencedPathSource(),
correlationParent.getExplicitAlias(),
correlationParent.getSqmJoinType(),
(SqmRoot<T>) correlationParent.getRoot()
);
this.correlatedRootJoin = SqmCorrelatedDerivedRootJoin.create( correlationParent, this );
this.correlationParent = correlationParent;
}

private SqmCorrelatedDerivedJoin(
NavigablePath navigablePath,
SqmSubQuery<T> subQuery,
boolean lateral,
SqmPathSource<T> pathSource,
String alias,
SqmJoinType joinType,
SqmRoot<T> sqmRoot,
SqmCorrelatedRootJoin<T> correlatedRootJoin,
SqmDerivedJoin<T> correlationParent) {
super( navigablePath, subQuery, lateral, pathSource, alias, joinType, sqmRoot );
this.correlatedRootJoin = correlatedRootJoin;
this.correlationParent = correlationParent;
}

@Override
public SqmCorrelatedDerivedJoin<T> copy(SqmCopyContext context) {
final SqmCorrelatedDerivedJoin<T> existing = context.getCopy( this );
if ( existing != null ) {
return existing;
}
final SqmCorrelatedDerivedJoin<T> path = context.registerCopy(
this,
new SqmCorrelatedDerivedJoin<>(
getNavigablePath(),
getQueryPart(),
isLateral(),
getReferencedPathSource(),
getExplicitAlias(),
getSqmJoinType(),
(SqmRoot<T>) findRoot().copy( context ),
correlatedRootJoin.copy( context ),
correlationParent.copy( context )
)
);
copyTo( path, context );
return path;
}

@Override
public SqmDerivedJoin<T> getCorrelationParent() {
return correlationParent;
}

@Override
public SqmPath<T> getWrappedPath() {
return correlationParent;
}

@Override
public boolean isCorrelated() {
return true;
}

@Override
public SqmRoot<T> getCorrelatedRoot() {
return correlatedRootJoin;
}

@Override
public <X> X accept(SemanticQueryWalker<X> walker) {
return walker.visitCorrelatedDerivedJoin( this );
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
/*
* 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.domain;

import org.hibernate.metamodel.model.domain.EntityDomainType;
import org.hibernate.query.sqm.SqmPathSource;
import org.hibernate.query.sqm.tree.SqmCopyContext;
import org.hibernate.query.sqm.tree.from.SqmRoot;

/**
* @author Steve Ebersole
*/
public class SqmCorrelatedDerivedRoot<T> extends SqmCorrelatedRoot<T> implements SqmPathWrapper<T, T>, SqmCorrelation<T, T> {

public SqmCorrelatedDerivedRoot(SqmDerivedRoot<T> correlationParent) {
this( (SqmRoot<T>) correlationParent );
}

public SqmCorrelatedDerivedRoot(SqmCteRoot<T> correlationParent) {
this( (SqmRoot<T>) correlationParent );
}

private SqmCorrelatedDerivedRoot(SqmRoot<T> correlationParent) {
super(
correlationParent.getNavigablePath(),
correlationParent.getReferencedPathSource(),
correlationParent.nodeBuilder(),
correlationParent
);
}

@Override
public SqmCorrelatedDerivedRoot<T> copy(SqmCopyContext context) {
final SqmCorrelatedDerivedRoot<T> existing = context.getCopy( this );
if ( existing != null ) {
return existing;
}
final SqmCorrelatedDerivedRoot<T> path = context.registerCopy(
this,
new SqmCorrelatedDerivedRoot<>( getCorrelationParent().copy( context ) )
);
copyTo( path, context );
return path;
}

// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// JPA

@Override
public EntityDomainType<T> getModel() {
// Or should we throw an exception instead?
return null;
}

@Override
public String getEntityName() {
return null;
}

@Override
public SqmPathSource<T> getResolvedModel() {
return getReferencedPathSource();
}

}
Loading
Loading