diff --git a/hibernate-core/src/main/java/org/hibernate/query/hql/internal/QualifiedJoinPathConsumer.java b/hibernate-core/src/main/java/org/hibernate/query/hql/internal/QualifiedJoinPathConsumer.java index 494c318ac491..a29f5f6537df 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/hql/internal/QualifiedJoinPathConsumer.java +++ b/hibernate-core/src/main/java/org/hibernate/query/hql/internal/QualifiedJoinPathConsumer.java @@ -302,7 +302,7 @@ public void consumeIdentifier(String identifier, boolean isTerminal, boolean all final String fullPath = path.toString(); final EntityDomainType joinedEntityType = creationState.getCreationContext() .getJpaMetamodel() - .resolveHqlEntityReference( fullPath ); + .getHqlEntityReference( fullPath ); if ( joinedEntityType == null ) { final SqmCteStatement cteStatement = creationState.findCteStatement( fullPath ); if ( cteStatement != null ) { diff --git a/hibernate-core/src/main/java/org/hibernate/query/hql/internal/SqmPathRegistryImpl.java b/hibernate-core/src/main/java/org/hibernate/query/hql/internal/SqmPathRegistryImpl.java index 25a7289096ae..fcafc230f362 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/hql/internal/SqmPathRegistryImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/query/hql/internal/SqmPathRegistryImpl.java @@ -23,6 +23,8 @@ 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; @@ -167,6 +169,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 ); } diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/SemanticQueryWalker.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/SemanticQueryWalker.java index 2bd2546aa460..9f878d8cfe42 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/SemanticQueryWalker.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/SemanticQueryWalker.java @@ -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; @@ -162,6 +164,14 @@ public interface SemanticQueryWalker { 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 ); } diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/AbstractSqmPath.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/AbstractSqmPath.java index d7d3a7f0ae95..21c3511246c5 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/AbstractSqmPath.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/AbstractSqmPath.java @@ -161,6 +161,7 @@ public SqmPathSource getModel() { public SqmPathSource getResolvedModel() { final DomainType lhsType; final SqmPathSource pathSource = getReferencedPathSource(); + if ( pathSource.isGeneric() && ( lhsType = getLhs().getResolvedModel().getSqmPathType() ) instanceof ManagedDomainType ) { final PersistentAttribute concreteAttribute = ( (ManagedDomainType) lhsType ).findConcreteGenericAttribute( pathSource.getPathName() diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmCorrelatedCteJoin.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmCorrelatedCteJoin.java new file mode 100644 index 000000000000..4c409e92625d --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmCorrelatedCteJoin.java @@ -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 extends SqmCteJoin implements SqmCorrelation { + + private final SqmCorrelatedRootJoin correlatedRootJoin; + private final SqmCteJoin correlationParent; + + public SqmCorrelatedCteJoin(SqmCteJoin correlationParent) { + //noinspection unchecked + super( + correlationParent.getCte(), + correlationParent.getExplicitAlias(), + correlationParent.getSqmJoinType(), + (SqmRoot) correlationParent.getRoot() + ); + this.correlatedRootJoin = SqmCorrelatedDerivedRootJoin.create( correlationParent, this ); + this.correlationParent = correlationParent; + } + + private SqmCorrelatedCteJoin( + NavigablePath navigablePath, + SqmCteStatement cte, + SqmPathSource pathSource, + String alias, + SqmJoinType joinType, + SqmRoot sqmRoot, + SqmCorrelatedRootJoin correlatedRootJoin, + SqmCteJoin correlationParent) { + super( navigablePath, cte, pathSource, alias, joinType, sqmRoot ); + this.correlatedRootJoin = correlatedRootJoin; + this.correlationParent = correlationParent; + } + + @Override + public SqmCorrelatedCteJoin copy(SqmCopyContext context) { + final SqmCorrelatedCteJoin existing = context.getCopy( this ); + if ( existing != null ) { + return existing; + } + final SqmCorrelatedCteJoin path = context.registerCopy( + this, + new SqmCorrelatedCteJoin<>( + getNavigablePath(), + getCte().copy( context ), + getReferencedPathSource(), + getExplicitAlias(), + getSqmJoinType(), + (SqmRoot) findRoot().copy( context ), + correlatedRootJoin.copy( context ), + correlationParent.copy( context ) + ) + ); + copyTo( path, context ); + return path; + } + + @Override + public SqmCteJoin getCorrelationParent() { + return correlationParent; + } + + @Override + public SqmPath getWrappedPath() { + return correlationParent; + } + + @Override + public boolean isCorrelated() { + return true; + } + + @Override + public SqmRoot getCorrelatedRoot() { + return correlatedRootJoin; + } + + @Override + public X accept(SemanticQueryWalker walker) { + return walker.visitCorrelatedCteJoin( this ); + } + +} diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmCorrelatedDerivedJoin.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmCorrelatedDerivedJoin.java new file mode 100644 index 000000000000..ecf42b319956 --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmCorrelatedDerivedJoin.java @@ -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 extends SqmDerivedJoin implements SqmCorrelation { + + private final SqmCorrelatedRootJoin correlatedRootJoin; + private final SqmDerivedJoin correlationParent; + + public SqmCorrelatedDerivedJoin(SqmDerivedJoin correlationParent) { + //noinspection unchecked + super( + correlationParent.getNavigablePath(), + correlationParent.getQueryPart(), + correlationParent.isLateral(), + correlationParent.getReferencedPathSource(), + correlationParent.getExplicitAlias(), + correlationParent.getSqmJoinType(), + (SqmRoot) correlationParent.getRoot() + ); + this.correlatedRootJoin = SqmCorrelatedDerivedRootJoin.create( correlationParent, this ); + this.correlationParent = correlationParent; + } + + private SqmCorrelatedDerivedJoin( + NavigablePath navigablePath, + SqmSubQuery subQuery, + boolean lateral, + SqmPathSource pathSource, + String alias, + SqmJoinType joinType, + SqmRoot sqmRoot, + SqmCorrelatedRootJoin correlatedRootJoin, + SqmDerivedJoin correlationParent) { + super( navigablePath, subQuery, lateral, pathSource, alias, joinType, sqmRoot ); + this.correlatedRootJoin = correlatedRootJoin; + this.correlationParent = correlationParent; + } + + @Override + public SqmCorrelatedDerivedJoin copy(SqmCopyContext context) { + final SqmCorrelatedDerivedJoin existing = context.getCopy( this ); + if ( existing != null ) { + return existing; + } + final SqmCorrelatedDerivedJoin path = context.registerCopy( + this, + new SqmCorrelatedDerivedJoin<>( + getNavigablePath(), + getQueryPart(), + isLateral(), + getReferencedPathSource(), + getExplicitAlias(), + getSqmJoinType(), + (SqmRoot) findRoot().copy( context ), + correlatedRootJoin.copy( context ), + correlationParent.copy( context ) + ) + ); + copyTo( path, context ); + return path; + } + + @Override + public SqmDerivedJoin getCorrelationParent() { + return correlationParent; + } + + @Override + public SqmPath getWrappedPath() { + return correlationParent; + } + + @Override + public boolean isCorrelated() { + return true; + } + + @Override + public SqmRoot getCorrelatedRoot() { + return correlatedRootJoin; + } + + @Override + public X accept(SemanticQueryWalker walker) { + return walker.visitCorrelatedDerivedJoin( this ); + } + +} diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmCorrelatedDerivedRoot.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmCorrelatedDerivedRoot.java new file mode 100644 index 000000000000..43e125f5d907 --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmCorrelatedDerivedRoot.java @@ -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 extends SqmCorrelatedRoot implements SqmPathWrapper, SqmCorrelation { + + public SqmCorrelatedDerivedRoot(SqmDerivedRoot correlationParent) { + this( (SqmRoot) correlationParent ); + } + + public SqmCorrelatedDerivedRoot(SqmCteRoot correlationParent) { + this( (SqmRoot) correlationParent ); + } + + private SqmCorrelatedDerivedRoot(SqmRoot correlationParent) { + super( + correlationParent.getNavigablePath(), + correlationParent.getReferencedPathSource(), + correlationParent.nodeBuilder(), + correlationParent + ); + } + + @Override + public SqmCorrelatedDerivedRoot copy(SqmCopyContext context) { + final SqmCorrelatedDerivedRoot existing = context.getCopy( this ); + if ( existing != null ) { + return existing; + } + final SqmCorrelatedDerivedRoot path = context.registerCopy( + this, + new SqmCorrelatedDerivedRoot<>( getCorrelationParent().copy( context ) ) + ); + copyTo( path, context ); + return path; + } + + // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + // JPA + + @Override + public EntityDomainType getModel() { + // Or should we throw an exception instead? + return null; + } + + @Override + public String getEntityName() { + return null; + } + + @Override + public SqmPathSource getResolvedModel() { + return getReferencedPathSource(); + } + +} diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmCorrelatedDerivedRootJoin.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmCorrelatedDerivedRootJoin.java new file mode 100644 index 000000000000..89d627eb8577 --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmCorrelatedDerivedRootJoin.java @@ -0,0 +1,93 @@ +/* + * 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.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 extends SqmCorrelatedRootJoin { + + public SqmCorrelatedDerivedRootJoin( + NavigablePath navigablePath, + SqmPathSource referencedNavigable, + NodeBuilder nodeBuilder) { + super( navigablePath, referencedNavigable, nodeBuilder ); + } + + @Override + public SqmCorrelatedDerivedRootJoin copy(SqmCopyContext context) { + final SqmCorrelatedDerivedRootJoin existing = context.getCopy( this ); + if ( existing != null ) { + return existing; + } + final SqmCorrelatedDerivedRootJoin path = context.registerCopy( + this, + new SqmCorrelatedDerivedRootJoin<>( + getNavigablePath(), + getReferencedPathSource(), + nodeBuilder() + ) + ); + copyTo( path, context ); + return path; + } + + @SuppressWarnings("unchecked") + public static > SqmCorrelatedDerivedRootJoin create(J correlationParent, J correlatedJoin) { + final SqmFrom parentPath = (SqmFrom) correlationParent.getParentPath(); + final SqmCorrelatedDerivedRootJoin rootJoin; + if ( parentPath == null ) { + rootJoin = new SqmCorrelatedDerivedRootJoin<>( + correlationParent.getNavigablePath(), + (SqmPathSource) 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 EntityDomainType getModel() { + // Or should we throw an exception instead? + return null; + } + + @Override + public String getEntityName() { + return null; + } + + @Override + public SqmPathSource getResolvedModel() { + return getReferencedPathSource(); + } +} diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmCorrelatedRoot.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmCorrelatedRoot.java index 46b6a2e6cccc..07421314dea6 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmCorrelatedRoot.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmCorrelatedRoot.java @@ -7,9 +7,14 @@ package org.hibernate.query.sqm.tree.domain; import org.hibernate.query.criteria.JpaSelection; +import org.hibernate.query.sqm.NodeBuilder; 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.from.SqmRoot; +import org.hibernate.spi.NavigablePath; + +import java.util.Objects; /** * @author Steve Ebersole @@ -28,6 +33,11 @@ public SqmCorrelatedRoot(SqmRoot correlationParent) { this.correlationParent = correlationParent; } + protected SqmCorrelatedRoot(NavigablePath navigablePath, SqmPathSource referencedNavigable, NodeBuilder nodeBuilder, SqmRoot correlationParent) { + super( navigablePath, referencedNavigable, nodeBuilder ); + this.correlationParent = correlationParent; + } + @Override public SqmCorrelatedRoot copy(SqmCopyContext context) { final SqmCorrelatedRoot existing = context.getCopy( this ); @@ -82,4 +92,16 @@ public SqmRoot getCorrelatedRoot() { public X accept(SemanticQueryWalker walker) { return walker.visitCorrelatedRoot( this ); } + + @Override + public boolean equals(Object object) { + return object instanceof SqmCorrelatedRoot + && super.equals( object ) + && Objects.equals( this.correlationParent, ((SqmCorrelatedRoot) object).correlationParent ); + } + + @Override + public int hashCode() { + return Objects.hash( super.hashCode(), correlationParent ); + } } diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmCteRoot.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmCteRoot.java index f4fc1553f6ff..f16ed137cc7e 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmCteRoot.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmCteRoot.java @@ -97,7 +97,7 @@ public SqmPathSource getResolvedModel() { @Override public SqmCorrelatedRoot createCorrelation() { - throw new UnsupportedOperationException(); + return new SqmCorrelatedDerivedRoot<>( this ); } @Override diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmDerivedRoot.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmDerivedRoot.java index c5ce71316ce3..9d31e5d4818a 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmDerivedRoot.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmDerivedRoot.java @@ -99,7 +99,7 @@ public SqmPathSource getResolvedModel() { @Override public SqmCorrelatedRoot createCorrelation() { - throw new UnsupportedOperationException(); + return new SqmCorrelatedDerivedRoot<>( this ); } @Override diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/from/SqmCteJoin.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/from/SqmCteJoin.java index 50554862b012..3187cacb7e9c 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/from/SqmCteJoin.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/from/SqmCteJoin.java @@ -17,7 +17,7 @@ import org.hibernate.query.sqm.tree.SqmJoinType; import org.hibernate.query.sqm.tree.cte.SqmCteStatement; import org.hibernate.query.sqm.tree.domain.AbstractSqmQualifiedJoin; -import org.hibernate.query.sqm.tree.domain.SqmCorrelatedEntityJoin; +import org.hibernate.query.sqm.tree.domain.SqmCorrelatedCteJoin; import org.hibernate.query.sqm.tree.domain.SqmPath; import org.hibernate.spi.NavigablePath; @@ -110,8 +110,8 @@ public X accept(SemanticQueryWalker walker) { // JPA @Override - public SqmCorrelatedEntityJoin createCorrelation() { - throw new UnsupportedOperationException(); + public SqmCorrelatedCteJoin createCorrelation() { + return new SqmCorrelatedCteJoin<>( this ); } @Override diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/from/SqmDerivedJoin.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/from/SqmDerivedJoin.java index fe1056c980bc..d86039443790 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/from/SqmDerivedJoin.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/from/SqmDerivedJoin.java @@ -12,6 +12,7 @@ import org.hibernate.query.criteria.JpaDerivedJoin; import org.hibernate.query.criteria.JpaExpression; import org.hibernate.query.criteria.JpaPredicate; +import org.hibernate.query.sqm.tree.domain.SqmCorrelatedDerivedJoin; import org.hibernate.query.derived.AnonymousTupleType; import org.hibernate.query.sqm.SemanticQueryWalker; import org.hibernate.query.sqm.SqmPathSource; @@ -19,7 +20,6 @@ import org.hibernate.query.sqm.tree.SqmCopyContext; import org.hibernate.query.sqm.tree.SqmJoinType; import org.hibernate.query.sqm.tree.domain.AbstractSqmQualifiedJoin; -import org.hibernate.query.sqm.tree.domain.SqmCorrelatedEntityJoin; import org.hibernate.query.sqm.tree.domain.SqmPath; import org.hibernate.query.sqm.tree.select.SqmSubQuery; import org.hibernate.spi.NavigablePath; @@ -161,8 +161,8 @@ public X accept(SemanticQueryWalker walker) { // JPA @Override - public SqmCorrelatedEntityJoin createCorrelation() { - throw new UnsupportedOperationException(); + public SqmCorrelatedDerivedJoin createCorrelation() { + return new SqmCorrelatedDerivedJoin<>( this ); } @Override diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/jpa/query/CorrelationCteDerivedJoinTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/jpa/query/CorrelationCteDerivedJoinTest.java new file mode 100644 index 000000000000..0d0e3c40d0a5 --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/jpa/query/CorrelationCteDerivedJoinTest.java @@ -0,0 +1,55 @@ +/* + * 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.jpa.query; + +import org.hibernate.testing.orm.domain.StandardDomainModel; +import org.hibernate.testing.orm.junit.*; +import org.junit.jupiter.api.Test; + +@SessionFactory +@DomainModel(standardModels = StandardDomainModel.GAMBIT) +@JiraKey("HHH-17522") +@RequiresDialectFeature(feature = DialectFeatureChecks.SupportsSubqueryInOnClause.class) +@RequiresDialectFeature(feature = DialectFeatureChecks.SupportsOrderByInCorrelatedSubquery.class) +public class CorrelationCteDerivedJoinTest { + + @Test + public void tesDerivedJoin(SessionFactoryScope scope) { + scope.inTransaction(s -> { + s.createSelectionQuery("select corrSub.id " + + "from EntityOfBasics e " + + "left join ( " + + " select 1 as id " + + " from EntityOfBasics eSub " + + ") sub on true " + + "left join lateral ( " + + " select 1 as id " + + " from EntityOfBasics eSub " + + " where eSub.id = sub.id " + + ") corrSub on true", Integer.class ).getResultList(); + }); + } + + @Test + public void tesCte(SessionFactoryScope scope) { + scope.inTransaction(s -> { + s.createSelectionQuery("with mycte as ( " + + " select 1 as id " + + " from EntityOfBasics eSub " + + ") " + + "select corrSub.id " + + "from EntityOfBasics e " + + "left join mycte sub on true " + + "left join lateral ( " + + " select 1 as id " + + " from EntityOfBasics eSub " + + " where eSub.id = sub.id " + + ") corrSub on true", Integer.class ).getResultList(); + }); + } + +} diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/jpa/query/CorrelationCteDerivedRootTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/jpa/query/CorrelationCteDerivedRootTest.java new file mode 100644 index 000000000000..8651ab42a97c --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/jpa/query/CorrelationCteDerivedRootTest.java @@ -0,0 +1,53 @@ +/* + * 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.jpa.query; + +import org.hibernate.testing.orm.domain.StandardDomainModel; +import org.hibernate.testing.orm.junit.*; +import org.junit.jupiter.api.Test; + +@SessionFactory +@DomainModel(standardModels = StandardDomainModel.GAMBIT) +@JiraKey("HHH-17522") +@RequiresDialectFeature(feature = DialectFeatureChecks.SupportsSubqueryInOnClause.class) +@RequiresDialectFeature(feature = DialectFeatureChecks.SupportsOrderByInCorrelatedSubquery.class) +public class CorrelationCteDerivedRootTest { + + @Test + public void tesDerivedRoot(SessionFactoryScope scope) { + scope.inTransaction(s -> { + s.createSelectionQuery("select corrSub.id " + + "from ( " + + " select 1 as id " + + " from EntityOfBasics eSub " + + ") sub " + + "left join lateral ( " + + " select 1 as id " + + " from EntityOfBasics eSub " + + " where eSub.id = sub.id " + + ") corrSub on true", Integer.class ).getResultList(); + }); + } + + @Test + public void tesCte(SessionFactoryScope scope) { + scope.inTransaction(s -> { + s.createSelectionQuery("with mycte as ( " + + " select 1 as id " + + " from EntityOfBasics eSub " + + ") " + + "select corrSub.id " + + "from mycte sub " + + "left join lateral ( " + + " select 1 as id " + + " from EntityOfBasics eSub " + + " where eSub.id = sub.id " + + ") corrSub on true", Integer.class ).getResultList(); + }); + } + +} diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/query/CteTests.java b/hibernate-core/src/test/java/org/hibernate/orm/test/query/CteTests.java index c628b9e595da..9e3d2d205363 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/query/CteTests.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/query/CteTests.java @@ -17,13 +17,16 @@ import org.hibernate.query.criteria.JpaCteCriteria; import org.hibernate.query.criteria.JpaEntityJoin; import org.hibernate.query.criteria.JpaJoin; +import org.hibernate.query.criteria.JpaJoinedFrom; import org.hibernate.query.criteria.JpaParameterExpression; import org.hibernate.query.criteria.JpaRoot; import org.hibernate.query.criteria.JpaSubQuery; import org.hibernate.query.spi.QueryImplementor; +import org.hibernate.query.sqm.tree.SqmJoinType; import org.hibernate.sql.ast.tree.cte.CteMaterialization; import org.hibernate.sql.ast.tree.cte.CteSearchClauseKind; +import org.hibernate.testing.orm.junit.Jira; import org.hibernate.testing.orm.junit.SkipForDialect; import org.hibernate.testing.orm.domain.StandardDomainModel; import org.hibernate.testing.orm.domain.contacts.Address; @@ -88,6 +91,47 @@ public void testBasic(SessionFactoryScope scope) { ); } + @Test + @Jira("https://hibernate.atlassian.net/browse/HHH-17897") + public void testBasicJoined(SessionFactoryScope scope) { + scope.inTransaction( + session -> { + final HibernateCriteriaBuilder cb = session.getCriteriaBuilder(); + final JpaCriteriaQuery cte = cb.createTupleQuery(); + final JpaRoot cteRoot = cte.from( Contact.class ); + cte.multiselect( cteRoot.get( "id" ).alias( "id" ), cteRoot.get( "name" ).alias( "name" ) ); + cte.where( cb.equal( cteRoot.get( "gender" ), Contact.Gender.FEMALE ) ); + + final JpaCriteriaQuery cq = cb.createTupleQuery(); + final JpaCteCriteria femaleContacts = cq.with( cte ); + + final JpaRoot root = cq.from( Contact.class ); + final JpaJoinedFrom join = root.join( femaleContacts ); + join.on( cb.equal( root.get( "id" ), join.get( "id" ) ) ); + + cq.multiselect( root.get( "id" ), root.get( "name" ) ); + cq.orderBy( cb.asc( root.get( "id" ) ) ); + + final QueryImplementor query = session.createQuery( + "with femaleContacts as (" + + "select c.id id, c.name name from Contact c where c.gender = FEMALE" + + ")" + + "select c.id, c.name from Contact c join femaleContacts f on c.id = f.id order by c.id", + Tuple.class + ); + verifySame( + session.createQuery( cq ).getResultList(), + query.getResultList(), + list -> { + assertEquals( 2, list.size() ); + assertEquals( "Jane", list.get( 0 ).get( 1, Contact.Name.class ).getFirst() ); + assertEquals( "Granny", list.get( 1 ).get( 1, Contact.Name.class ).getFirst() ); + } + ); + } + ); + } + @Test public void testNested(SessionFactoryScope scope) { scope.inTransaction(