Skip to content

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

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 @@ -16,6 +16,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 @@ -173,6 +175,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 @@ -148,6 +148,7 @@ public SqmPathSource<T> getModel() {
@Override
public SqmPathSource<T> getResolvedModel() {
final SqmPathSource<T> pathSource = getReferencedPathSource();

if ( pathSource.isGeneric()
&& getLhs().getResolvedModel().getPathType() instanceof SqmManagedDomainType<?> lhsType ) {
final var concreteAttribute = lhsType.findConcreteGenericAttribute( pathSource.getPathName() );
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
/*
* SPDX-License-Identifier: Apache-2.0
* Copyright Red Hat Inc. and Hibernate Authors
*/
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>, SqmCorrelatedSingularValuedJoin<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,103 @@
/*
* SPDX-License-Identifier: Apache-2.0
* Copyright Red Hat Inc. and Hibernate Authors
*/
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>, SqmCorrelatedSingularValuedJoin<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,66 @@
/*
* SPDX-License-Identifier: Apache-2.0
* Copyright Red Hat Inc. and Hibernate Authors
*/
package org.hibernate.query.sqm.tree.domain;

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 SqmEntityDomainType<T> getModel() {
// Or should we throw an exception instead?
return null;
}

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

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

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
/*
* SPDX-License-Identifier: Apache-2.0
* Copyright Red Hat Inc. and Hibernate Authors
*/
package org.hibernate.query.sqm.tree.domain;

import org.hibernate.query.sqm.NodeBuilder;
import org.hibernate.query.sqm.SqmPathSource;
import org.hibernate.query.sqm.tree.SqmCopyContext;
import org.hibernate.query.sqm.tree.from.SqmFrom;
import org.hibernate.query.sqm.tree.from.SqmJoin;
import org.hibernate.spi.NavigablePath;

/**
* @author Steve Ebersole
*/
public class SqmCorrelatedDerivedRootJoin<T> extends SqmCorrelatedRootJoin<T> {

public SqmCorrelatedDerivedRootJoin(
NavigablePath navigablePath,
SqmPathSource<T> referencedNavigable,
NodeBuilder nodeBuilder) {
super( navigablePath, referencedNavigable, nodeBuilder );
}

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

@SuppressWarnings("unchecked")
public static <X, J extends SqmJoin<X, ?>> SqmCorrelatedDerivedRootJoin<X> create(J correlationParent, J correlatedJoin) {
final SqmFrom<?, X> parentPath = (SqmFrom<?, X>) correlationParent.getParentPath();
final SqmCorrelatedDerivedRootJoin<X> rootJoin;
if ( parentPath == null ) {
rootJoin = new SqmCorrelatedDerivedRootJoin<>(
correlationParent.getNavigablePath(),
(SqmPathSource<X>) correlationParent.getReferencedPathSource(),
correlationParent.nodeBuilder()
);
}
else {
rootJoin = new SqmCorrelatedDerivedRootJoin<>(
parentPath.getNavigablePath(),
parentPath.getReferencedPathSource(),
correlationParent.nodeBuilder()
);
}
rootJoin.addSqmJoin( correlatedJoin );
return rootJoin;
}

@Override
public boolean containsOnlyInnerJoins() {
// The derived join is just referenced, no need to create any table groups
return true;
}

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

@Override
public SqmEntityDomainType<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