6 - SQM based on JPA type system
- moving SQM-specific tests from wip/6.0
This commit is contained in:
parent
5e19aee4a1
commit
5e8be067ca
|
@ -7,6 +7,9 @@
|
|||
package org.hibernate.metamodel.model.domain.internal;
|
||||
|
||||
import org.hibernate.metamodel.model.domain.EntityDomainType;
|
||||
import org.hibernate.metamodel.model.domain.PersistentAttribute;
|
||||
import org.hibernate.metamodel.model.domain.SingularPersistentAttribute;
|
||||
import org.hibernate.query.SemanticException;
|
||||
import org.hibernate.query.sqm.NodeBuilder;
|
||||
import org.hibernate.query.sqm.SqmPathSource;
|
||||
import org.hibernate.query.sqm.produce.spi.SqmCreationState;
|
||||
|
@ -32,7 +35,17 @@ public class EntitySqmPathSource<J> extends AbstractSqmPathSource<J> {
|
|||
|
||||
@Override
|
||||
public SqmPathSource<?> findSubPathSource(String name) {
|
||||
return (SqmPathSource<?>) getSqmPathType().findAttribute( name );
|
||||
final PersistentAttribute<?,?> attribute = getSqmPathType().findAttribute( name );
|
||||
if ( attribute != null ) {
|
||||
return (SqmPathSource<?>) attribute;
|
||||
}
|
||||
|
||||
final SingularPersistentAttribute<J, ?> idAttribute = getSqmPathType().findIdAttribute();
|
||||
if ( idAttribute != null && idAttribute.getName().equals( name ) ) {
|
||||
return idAttribute;
|
||||
}
|
||||
|
||||
throw new SemanticException( "Unknown sub-path name : " + name );
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -13,10 +13,11 @@ import org.hibernate.graph.internal.SubGraphImpl;
|
|||
import org.hibernate.graph.spi.SubGraphImplementor;
|
||||
import org.hibernate.mapping.PersistentClass;
|
||||
import org.hibernate.metamodel.model.domain.AbstractIdentifiableType;
|
||||
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.JpaMetamodel;
|
||||
import org.hibernate.metamodel.model.domain.PersistentAttribute;
|
||||
import org.hibernate.metamodel.model.domain.SingularPersistentAttribute;
|
||||
import org.hibernate.query.sqm.SqmPathSource;
|
||||
import org.hibernate.query.sqm.produce.spi.SqmCreationState;
|
||||
import org.hibernate.query.sqm.tree.domain.SqmPath;
|
||||
|
@ -66,13 +67,41 @@ public class EntityTypeImpl<J>
|
|||
}
|
||||
|
||||
@Override
|
||||
public DomainType<?> getSqmPathType() {
|
||||
public EntityDomainType<J> getSqmPathType() {
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SqmPathSource<?> findSubPathSource(String name) {
|
||||
return (SqmPathSource<?>) findAttribute( name );
|
||||
final PersistentAttribute<?,?> attribute = findAttribute( name );
|
||||
if ( attribute != null ) {
|
||||
return (SqmPathSource<?>) attribute;
|
||||
}
|
||||
|
||||
if ( "id".equalsIgnoreCase( name ) ) {
|
||||
// todo (6.0) : probably need special handling here for non-aggregated composite ids
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public PersistentAttribute<? super J, ?> findAttribute(String name) {
|
||||
final PersistentAttribute<? super J, ?> attribute = super.findAttribute( name );
|
||||
if ( attribute != null ) {
|
||||
return attribute;
|
||||
}
|
||||
|
||||
if ( "id".equalsIgnoreCase( name ) ) {
|
||||
//noinspection unchecked
|
||||
final SingularPersistentAttribute<J, ?> idAttribute = findIdAttribute();
|
||||
//noinspection RedundantIfStatement
|
||||
if ( idAttribute != null ) {
|
||||
return idAttribute;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
package org.hibernate.query.hql.internal;
|
||||
|
||||
import org.hibernate.NotYetImplementedFor6Exception;
|
||||
import org.hibernate.query.SemanticException;
|
||||
import org.hibernate.query.hql.spi.SemanticPathPart;
|
||||
import org.hibernate.query.sqm.SqmPathSource;
|
||||
import org.hibernate.query.sqm.produce.spi.SqmCreationState;
|
||||
|
@ -33,6 +34,9 @@ public class DomainPathPart implements SemanticPathPart {
|
|||
SqmCreationState creationState) {
|
||||
final SqmPath<?> lhs = currentPath;
|
||||
final SqmPathSource subPathSource = lhs.getReferencedPathSource().findSubPathSource( name );
|
||||
if ( subPathSource == null ) {
|
||||
throw new SemanticException( "Cannot resolve path (`" + name + "`) relative to `" + lhs.getNavigablePath() + "`" );
|
||||
}
|
||||
//noinspection unchecked
|
||||
currentPath = subPathSource.createSqmPath( lhs, creationState );
|
||||
if ( isTerminal ) {
|
||||
|
|
|
@ -6,6 +6,8 @@
|
|||
*/
|
||||
package org.hibernate.query.hql.internal;
|
||||
|
||||
import org.hibernate.metamodel.model.domain.EntityDomainType;
|
||||
import org.hibernate.query.NavigablePath;
|
||||
import org.hibernate.query.SemanticException;
|
||||
import org.hibernate.query.hql.spi.DotIdentifierConsumer;
|
||||
import org.hibernate.query.hql.spi.SemanticPathPart;
|
||||
|
@ -15,16 +17,22 @@ import org.hibernate.query.sqm.SqmPathSource;
|
|||
import org.hibernate.query.sqm.produce.spi.SqmCreationProcessingState;
|
||||
import org.hibernate.query.sqm.produce.spi.SqmCreationState;
|
||||
import org.hibernate.query.sqm.tree.SqmJoinType;
|
||||
import org.hibernate.query.sqm.tree.domain.SqmPolymorphicRootDescriptor;
|
||||
import org.hibernate.query.sqm.tree.from.SqmAttributeJoin;
|
||||
import org.hibernate.query.sqm.tree.from.SqmEntityJoin;
|
||||
import org.hibernate.query.sqm.tree.from.SqmFrom;
|
||||
import org.hibernate.query.sqm.tree.from.SqmRoot;
|
||||
|
||||
import org.jboss.logging.Logger;
|
||||
|
||||
/**
|
||||
* Specialized "intermediate" SemanticPathPart for processing domain model paths
|
||||
*
|
||||
* @author Steve Ebersole
|
||||
*/
|
||||
public class QualifiedJoinPathConsumer implements DotIdentifierConsumer {
|
||||
private static final Logger log = Logger.getLogger( QualifiedJoinPathConsumer.class );
|
||||
|
||||
private final SqmCreationState creationState;
|
||||
private final SqmRoot sqmRoot;
|
||||
|
||||
|
@ -32,7 +40,7 @@ public class QualifiedJoinPathConsumer implements DotIdentifierConsumer {
|
|||
private final boolean fetch;
|
||||
private final String alias;
|
||||
|
||||
private SqmFrom currentPath;
|
||||
private ConsumerDelegate delegate;
|
||||
|
||||
public QualifiedJoinPathConsumer(
|
||||
SqmRoot<?> sqmRoot,
|
||||
|
@ -47,25 +55,28 @@ public class QualifiedJoinPathConsumer implements DotIdentifierConsumer {
|
|||
this.creationState = creationState;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SemanticPathPart getConsumedPart() {
|
||||
return delegate.getConsumedPart();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void consumeIdentifier(String identifier, boolean isBase, boolean isTerminal) {
|
||||
if ( isBase ) {
|
||||
assert currentPath == null;
|
||||
|
||||
this.currentPath = resolvePathBase( identifier, isTerminal, creationState );
|
||||
assert delegate == null;
|
||||
delegate = resolveBase( identifier, isTerminal );
|
||||
}
|
||||
else {
|
||||
assert currentPath != null;
|
||||
currentPath = createJoin( currentPath, identifier, isTerminal, creationState );
|
||||
assert delegate != null;
|
||||
delegate.consumeIdentifier( identifier, isTerminal );
|
||||
}
|
||||
}
|
||||
|
||||
private SqmFrom resolvePathBase(String identifier, boolean isTerminal, SqmCreationState creationState) {
|
||||
private ConsumerDelegate resolveBase(String identifier, boolean isTerminal) {
|
||||
final SqmCreationProcessingState processingState = creationState.getCurrentProcessingState();
|
||||
final SqmPathRegistry pathRegistry = processingState.getPathRegistry();
|
||||
|
||||
final SqmFrom pathRootByAlias = pathRegistry.findFromByAlias( identifier );
|
||||
|
||||
if ( pathRootByAlias != null ) {
|
||||
// identifier is an alias (identification variable)
|
||||
|
||||
|
@ -73,23 +84,61 @@ public class QualifiedJoinPathConsumer implements DotIdentifierConsumer {
|
|||
throw new SemanticException( "Cannot join to root : " + identifier );
|
||||
}
|
||||
|
||||
return pathRootByAlias;
|
||||
return new AttributeJoinDelegate(
|
||||
pathRootByAlias,
|
||||
joinType,
|
||||
fetch,
|
||||
alias,
|
||||
creationState
|
||||
);
|
||||
}
|
||||
|
||||
final SqmFrom pathRootByExposedNavigable = pathRegistry.findFromExposing( identifier );
|
||||
if ( pathRootByExposedNavigable != null ) {
|
||||
return createJoin( pathRootByExposedNavigable, identifier, isTerminal, creationState );
|
||||
return new AttributeJoinDelegate(
|
||||
createJoin( pathRootByExposedNavigable, identifier, isTerminal ),
|
||||
joinType,
|
||||
fetch,
|
||||
alias,
|
||||
creationState
|
||||
);
|
||||
}
|
||||
|
||||
// todo (6.0) : another alternative here is an entity-join (entity name as rhs rather than attribute path)
|
||||
// - need to account for that here, which may need delayed resolution in the case of a
|
||||
// qualified entity reference (FQN)
|
||||
// otherwise, assume we have a qualified entity name - delay resolution until we
|
||||
// process the final token
|
||||
|
||||
throw new SemanticException( "Could not determine how to resolve qualified join base : " + identifier );
|
||||
return new ExpectingEntityJoinDelegate(
|
||||
identifier,
|
||||
isTerminal,
|
||||
sqmRoot,
|
||||
joinType,
|
||||
alias,
|
||||
fetch,
|
||||
creationState
|
||||
);
|
||||
}
|
||||
|
||||
private SqmFrom createJoin(SqmFrom lhs, String identifier, boolean isTerminal, SqmCreationState creationState) {
|
||||
final SqmPathSource subPathSource = lhs.getReferencedPathSource().findSubPathSource( identifier );
|
||||
private SqmFrom createJoin(SqmFrom lhs, String identifier, boolean isTerminal) {
|
||||
return createJoin(
|
||||
lhs,
|
||||
identifier,
|
||||
joinType,
|
||||
alias,
|
||||
fetch,
|
||||
isTerminal,
|
||||
creationState
|
||||
);
|
||||
}
|
||||
|
||||
private static SqmFrom createJoin(
|
||||
SqmFrom lhs,
|
||||
String name,
|
||||
SqmJoinType joinType,
|
||||
String alias,
|
||||
boolean fetch,
|
||||
boolean isTerminal,
|
||||
SqmCreationState creationState) {
|
||||
final SqmPathSource subPathSource = lhs.getReferencedPathSource().findSubPathSource( name );
|
||||
final SqmAttributeJoin join = ( (SqmJoinable) subPathSource ).createSqmJoin(
|
||||
lhs,
|
||||
joinType,
|
||||
|
@ -97,13 +146,114 @@ public class QualifiedJoinPathConsumer implements DotIdentifierConsumer {
|
|||
fetch,
|
||||
creationState
|
||||
);
|
||||
//noinspection unchecked
|
||||
lhs.addSqmJoin( join );
|
||||
creationState.getCurrentProcessingState().getPathRegistry().register( join );
|
||||
return join;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SemanticPathPart getConsumedPart() {
|
||||
return currentPath;
|
||||
private interface ConsumerDelegate {
|
||||
void consumeIdentifier(String identifier, boolean isTerminal);
|
||||
SemanticPathPart getConsumedPart();
|
||||
}
|
||||
|
||||
private static class AttributeJoinDelegate implements ConsumerDelegate {
|
||||
private final SqmCreationState creationState;
|
||||
|
||||
private final SqmJoinType joinType;
|
||||
private final boolean fetch;
|
||||
private final String alias;
|
||||
|
||||
private SqmFrom currentPath;
|
||||
|
||||
public AttributeJoinDelegate(
|
||||
SqmFrom base,
|
||||
SqmJoinType joinType,
|
||||
boolean fetch,
|
||||
String alias,
|
||||
SqmCreationState creationState) {
|
||||
this.joinType = joinType;
|
||||
this.fetch = fetch;
|
||||
this.alias = alias;
|
||||
this.creationState = creationState;
|
||||
|
||||
this.currentPath = base;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void consumeIdentifier(String identifier, boolean isTerminal) {
|
||||
currentPath = createJoin(
|
||||
currentPath,
|
||||
identifier,
|
||||
joinType,
|
||||
alias,
|
||||
fetch,
|
||||
isTerminal,
|
||||
creationState
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public SemanticPathPart getConsumedPart() {
|
||||
return currentPath;
|
||||
}
|
||||
}
|
||||
|
||||
private static class ExpectingEntityJoinDelegate implements ConsumerDelegate {
|
||||
private final SqmCreationState creationState;
|
||||
private final SqmRoot sqmRoot;
|
||||
|
||||
private final SqmJoinType joinType;
|
||||
private final boolean fetch;
|
||||
private final String alias;
|
||||
|
||||
private NavigablePath path = new NavigablePath();
|
||||
|
||||
private SqmEntityJoin<?> join;
|
||||
|
||||
public ExpectingEntityJoinDelegate(
|
||||
String identifier,
|
||||
boolean isTerminal,
|
||||
SqmRoot sqmRoot,
|
||||
SqmJoinType joinType,
|
||||
String alias,
|
||||
boolean fetch,
|
||||
SqmCreationState creationState) {
|
||||
this.creationState = creationState;
|
||||
this.sqmRoot = sqmRoot;
|
||||
this.joinType = joinType;
|
||||
this.fetch = fetch;
|
||||
this.alias = alias;
|
||||
|
||||
consumeIdentifier( identifier, isTerminal );
|
||||
}
|
||||
|
||||
@Override
|
||||
public void consumeIdentifier(String identifier, boolean isTerminal) {
|
||||
path = path.append( identifier );
|
||||
|
||||
if ( isTerminal ) {
|
||||
final EntityDomainType<?> joinedEntityType = creationState.getCreationContext()
|
||||
.getJpaMetamodel()
|
||||
.resolveHqlEntityReference( path.getFullPath() );
|
||||
if ( joinedEntityType == null ) {
|
||||
throw new SemanticException( "Could not resolve join path - " + path.getFullPath() );
|
||||
}
|
||||
|
||||
assert ! ( joinedEntityType instanceof SqmPolymorphicRootDescriptor );
|
||||
|
||||
if ( fetch ) {
|
||||
log.debugf( "Ignoring fetch on entity join : %s(%s)", joinedEntityType.getHibernateEntityName(), alias );
|
||||
}
|
||||
|
||||
join = new SqmEntityJoin<>( joinedEntityType, alias, joinType, sqmRoot );
|
||||
creationState.getCurrentProcessingState().getPathRegistry().register( join );
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public SemanticPathPart getConsumedPart() {
|
||||
return join;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -22,15 +22,12 @@ import org.hibernate.query.sqm.tree.from.SqmRoot;
|
|||
* @author Steve Ebersole
|
||||
*/
|
||||
public class QualifiedJoinPredicatePathConsumer extends BasicDotIdentifierConsumer {
|
||||
private final SqmRoot sqmRoot;
|
||||
private final SqmQualifiedJoin sqmJoin;
|
||||
|
||||
public QualifiedJoinPredicatePathConsumer(
|
||||
SqmRoot<?> sqmRoot,
|
||||
SqmQualifiedJoin sqmJoin,
|
||||
SqmCreationState creationState) {
|
||||
super( creationState );
|
||||
this.sqmRoot = sqmRoot;
|
||||
this.sqmJoin = sqmJoin;
|
||||
}
|
||||
|
||||
|
|
|
@ -1,156 +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.hql.internal;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
|
||||
import org.hibernate.boot.registry.classloading.spi.ClassLoaderService;
|
||||
import org.hibernate.boot.registry.classloading.spi.ClassLoadingException;
|
||||
import org.hibernate.metamodel.model.domain.EntityDomainType;
|
||||
import org.hibernate.query.SemanticException;
|
||||
import org.hibernate.query.hql.spi.SemanticPathPart;
|
||||
import org.hibernate.query.sqm.produce.spi.SqmCreationContext;
|
||||
import org.hibernate.query.sqm.produce.spi.SqmCreationState;
|
||||
import org.hibernate.query.sqm.tree.domain.SqmPath;
|
||||
import org.hibernate.query.sqm.tree.expression.SqmEnumLiteral;
|
||||
import org.hibernate.query.sqm.tree.expression.SqmExpression;
|
||||
import org.hibernate.query.sqm.tree.expression.SqmFieldLiteral;
|
||||
import org.hibernate.query.sqm.tree.expression.SqmLiteralEntityType;
|
||||
import org.hibernate.type.descriptor.java.EnumJavaTypeDescriptor;
|
||||
|
||||
/**
|
||||
* A delayed resolution of a non-terminal path part
|
||||
*
|
||||
* @author Steve Ebersole
|
||||
*/
|
||||
public class SemanticPathPartDelayedResolution implements SemanticPathPart, FullyQualifiedReflectivePathSource {
|
||||
private final FullyQualifiedReflectivePathSource parent;
|
||||
|
||||
// todo (6.0) : consider reusing this PossiblePackageRoot instance, updating the state fields
|
||||
// - we'd still add to the stack, but we'd save the instantiations
|
||||
|
||||
private final String fullPath;
|
||||
private final String localName;
|
||||
|
||||
@SuppressWarnings("WeakerAccess")
|
||||
public SemanticPathPartDelayedResolution(String name) {
|
||||
this( null, name );
|
||||
}
|
||||
|
||||
@SuppressWarnings("WeakerAccess")
|
||||
public SemanticPathPartDelayedResolution(
|
||||
FullyQualifiedReflectivePathSource parent,
|
||||
String localName) {
|
||||
this.parent = parent;
|
||||
this.localName = localName;
|
||||
|
||||
this.fullPath = parent == null ? localName : parent.append( localName ).getFullPath();
|
||||
}
|
||||
|
||||
@Override
|
||||
public FullyQualifiedReflectivePathSource getParent() {
|
||||
return parent;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getLocalName() {
|
||||
return localName;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getFullPath() {
|
||||
return fullPath;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SemanticPathPartDelayedResolution append(String subPathName) {
|
||||
throw new UnsupportedOperationException( "Use #resolvePathPart instead" );
|
||||
}
|
||||
|
||||
@Override
|
||||
public SemanticPathPart resolvePathPart(
|
||||
String subName,
|
||||
boolean isTerminal,
|
||||
SqmCreationState creationState) {
|
||||
final String combinedName = this.fullPath + '.' + subName;
|
||||
|
||||
final SqmCreationContext creationContext = creationState.getCreationContext();
|
||||
|
||||
if ( isTerminal ) {
|
||||
final EntityDomainType entityTypeByName = creationContext.getJpaMetamodel().entity( combinedName );
|
||||
if ( entityTypeByName != null ) {
|
||||
//noinspection unchecked
|
||||
return new SqmLiteralEntityType( entityTypeByName, creationContext.getNodeBuilder() );
|
||||
}
|
||||
|
||||
// the incoming subName could be a field or enum reference relative to this combinedName
|
||||
// which would mean the combinedName must be a class name
|
||||
final ClassLoaderService classLoaderService = creationContext
|
||||
.getServiceRegistry()
|
||||
.getService( ClassLoaderService.class );
|
||||
|
||||
// todo (6.0) : would be nice to leverage imported names here
|
||||
try {
|
||||
final Class referencedClass = classLoaderService.classForName( combinedName );
|
||||
|
||||
if ( referencedClass.isEnum() ) {
|
||||
try {
|
||||
//noinspection unchecked
|
||||
final Enum<?> enumValue = Enum.valueOf( referencedClass, subName );
|
||||
//noinspection unchecked
|
||||
return new SqmEnumLiteral(
|
||||
enumValue,
|
||||
(EnumJavaTypeDescriptor) creationContext.getJpaMetamodel()
|
||||
.getTypeConfiguration()
|
||||
.getJavaTypeDescriptorRegistry()
|
||||
.resolveDescriptor( referencedClass ),
|
||||
subName,
|
||||
creationContext.getNodeBuilder()
|
||||
);
|
||||
}
|
||||
catch (Exception e) {
|
||||
// ignore - it could still potentially be a static field reference
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
final Field field = referencedClass.getDeclaredField( subName );
|
||||
//noinspection unchecked
|
||||
return new SqmFieldLiteral<>(
|
||||
field.get( null ),
|
||||
creationContext.getJpaMetamodel()
|
||||
.getTypeConfiguration()
|
||||
.getJavaTypeDescriptorRegistry()
|
||||
.resolveDescriptor( referencedClass ),
|
||||
subName,
|
||||
creationContext.getNodeBuilder()
|
||||
);
|
||||
}
|
||||
catch (Exception e) {
|
||||
// ignore - fall through to the exception below
|
||||
}
|
||||
}
|
||||
catch (ClassLoadingException e) {
|
||||
// ignore - we will hit the exception below
|
||||
}
|
||||
|
||||
throw new SemanticException( "Could not resolve path terminal : " + combinedName + '.' + subName );
|
||||
}
|
||||
else {
|
||||
|
||||
return new SemanticPathPartDelayedResolution( this, subName );
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public SqmPath<?> resolveIndexedAccess(
|
||||
SqmExpression selector,
|
||||
boolean isTerminal,
|
||||
SqmCreationState creationState) {
|
||||
return null;
|
||||
}
|
||||
}
|
|
@ -1,85 +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.hql.internal;
|
||||
|
||||
import java.util.Locale;
|
||||
|
||||
import org.hibernate.query.SemanticException;
|
||||
import org.hibernate.query.hql.spi.SemanticPathPart;
|
||||
import org.hibernate.query.hql.spi.SqmPathRegistry;
|
||||
import org.hibernate.query.sqm.produce.spi.SqmCreationProcessingState;
|
||||
import org.hibernate.query.sqm.produce.spi.SqmCreationState;
|
||||
import org.hibernate.query.sqm.tree.domain.SqmPath;
|
||||
import org.hibernate.query.sqm.tree.expression.SqmExpression;
|
||||
import org.hibernate.query.sqm.tree.from.SqmFrom;
|
||||
|
||||
/**
|
||||
* @author Steve Ebersole
|
||||
*/
|
||||
public class SemanticPathPartJoinPredicate implements SemanticPathPart {
|
||||
private final SqmFrom joinLhs;
|
||||
|
||||
@SuppressWarnings("WeakerAccess")
|
||||
public SemanticPathPartJoinPredicate(SqmFrom joinLhs) {
|
||||
super();
|
||||
this.joinLhs = joinLhs;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SemanticPathPart resolvePathPart(
|
||||
String name,
|
||||
boolean isTerminal,
|
||||
SqmCreationState creationState) {
|
||||
final SqmCreationProcessingState processingState = creationState.getCurrentProcessingState();
|
||||
final SqmPathRegistry pathRegistry = processingState.getPathRegistry();
|
||||
|
||||
// #1 - name is joinLhs alias
|
||||
if ( name.equals( joinLhs.getExplicitAlias() ) ) {
|
||||
return joinLhs;
|
||||
}
|
||||
|
||||
// #2 - name is alias for another SqmFrom
|
||||
final SqmFrom fromByAlias = pathRegistry.findFromByAlias( name );
|
||||
if ( fromByAlias != null ) {
|
||||
validatePathRoot( fromByAlias );
|
||||
return fromByAlias;
|
||||
}
|
||||
|
||||
// #3 - name is a unqualified attribute reference relative to the current processing state
|
||||
final SqmFrom fromExposing = pathRegistry.findFromExposing( name );
|
||||
if ( fromExposing != null ) {
|
||||
validatePathRoot( fromExposing );
|
||||
return fromExposing;
|
||||
}
|
||||
|
||||
if ( ! isTerminal ) {
|
||||
return new SemanticPathPartDelayedResolution( name );
|
||||
}
|
||||
|
||||
throw new SemanticException( "Could not resolve path root used in join predicate: " + name );
|
||||
}
|
||||
|
||||
private void validatePathRoot(SqmPath path) {
|
||||
if ( ! path.findRoot().equals( joinLhs.findRoot() ) ) {
|
||||
throw new SemanticException(
|
||||
String.format(
|
||||
Locale.ROOT,
|
||||
"Qualified join predicate path [%s] referred to from-clause root other that the join rhs",
|
||||
path.getNavigablePath().getFullPath()
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public SqmPath resolveIndexedAccess(
|
||||
SqmExpression selector,
|
||||
boolean isTerminal,
|
||||
SqmCreationState creationState) {
|
||||
throw new SemanticException( "Illegal index-access as join predicate root" );
|
||||
}
|
||||
}
|
|
@ -1,152 +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.hql.internal;
|
||||
|
||||
import org.hibernate.metamodel.model.domain.EntityDomainType;
|
||||
import org.hibernate.query.SemanticException;
|
||||
import org.hibernate.query.hql.spi.SemanticPathPart;
|
||||
import org.hibernate.query.hql.spi.SqmPathRegistry;
|
||||
import org.hibernate.query.sqm.SqmJoinable;
|
||||
import org.hibernate.query.sqm.produce.spi.SqmCreationProcessingState;
|
||||
import org.hibernate.query.sqm.produce.spi.SqmCreationState;
|
||||
import org.hibernate.query.sqm.tree.SqmJoinType;
|
||||
import org.hibernate.query.sqm.tree.domain.SqmPath;
|
||||
import org.hibernate.query.sqm.tree.expression.SqmExpression;
|
||||
import org.hibernate.query.sqm.tree.from.SqmAttributeJoin;
|
||||
import org.hibernate.query.sqm.tree.from.SqmEntityJoin;
|
||||
import org.hibernate.query.sqm.tree.from.SqmFrom;
|
||||
import org.hibernate.query.sqm.tree.from.SqmRoot;
|
||||
|
||||
/**
|
||||
* SemanticPathPart handling specific to processing a qualified join path
|
||||
*
|
||||
* @author Steve Ebersole
|
||||
*/
|
||||
public class SemanticPathPartQualifiedJoinPath implements SemanticPathPart {
|
||||
private final SqmRoot sqmRoot;
|
||||
|
||||
private final SqmJoinType joinType;
|
||||
private final boolean fetch;
|
||||
private final String alias;
|
||||
|
||||
@SuppressWarnings("WeakerAccess")
|
||||
public SemanticPathPartQualifiedJoinPath(SqmRoot sqmRoot, SqmJoinType joinType, boolean fetch, String alias) {
|
||||
this.sqmRoot = sqmRoot;
|
||||
this.joinType = joinType;
|
||||
this.fetch = fetch;
|
||||
this.alias = alias;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SemanticPathPart resolvePathPart(
|
||||
String name,
|
||||
boolean isTerminal,
|
||||
SqmCreationState creationState) {
|
||||
final SqmCreationProcessingState processingState = creationState.getCurrentProcessingState();
|
||||
final SqmPathRegistry pathRegistry = processingState.getPathRegistry();
|
||||
|
||||
final SqmFrom fromByAlias = pathRegistry.findFromByAlias( name );
|
||||
if ( fromByAlias != null ) {
|
||||
return fromByAlias;
|
||||
}
|
||||
|
||||
final SqmFrom fromExposing = pathRegistry.findFromExposing( name );
|
||||
if ( fromExposing != null ) {
|
||||
final SqmAttributeJoin join = ( (SqmJoinable) fromExposing.getReferencedPathSource().findSubPathSource(
|
||||
name ) ).createSqmJoin(
|
||||
fromExposing,
|
||||
joinType,
|
||||
isTerminal ? alias : null,
|
||||
fetch,
|
||||
creationState
|
||||
);
|
||||
pathRegistry.register( join );
|
||||
return join;
|
||||
}
|
||||
|
||||
// otherwise it has to be an entity join
|
||||
|
||||
if ( isTerminal ) {
|
||||
final EntityDomainType<?> entityType = processingState.getCreationState()
|
||||
.getCreationContext()
|
||||
.getJpaMetamodel()
|
||||
.entity( name );
|
||||
if ( entityType == null ) {
|
||||
throw new SemanticException( "Could not resolve qualified join path: " + name );
|
||||
}
|
||||
final SqmEntityJoin<?> entityJoin = new SqmEntityJoin<>( entityType, alias, joinType, sqmRoot );
|
||||
pathRegistry.register( entityJoin );
|
||||
return entityJoin;
|
||||
}
|
||||
|
||||
return new SemanticPathPartDelayedEntityJoinHandler( name, sqmRoot, joinType, alias );
|
||||
}
|
||||
|
||||
@Override
|
||||
public SqmPath resolveIndexedAccess(
|
||||
SqmExpression selector,
|
||||
boolean isTerminal,
|
||||
SqmCreationState creationState) {
|
||||
throw new SemanticException( "Illegal index-access as qualified join path" );
|
||||
}
|
||||
|
||||
private static class SemanticPathPartDelayedEntityJoinHandler implements SemanticPathPart {
|
||||
private final SqmRoot sqmRoot;
|
||||
|
||||
private final SqmJoinType joinType;
|
||||
private final String alias;
|
||||
|
||||
private String pathSoFar;
|
||||
|
||||
private SemanticPathPartDelayedEntityJoinHandler(
|
||||
String baseName,
|
||||
SqmRoot sqmRoot,
|
||||
SqmJoinType joinType,
|
||||
String alias) {
|
||||
this.sqmRoot = sqmRoot;
|
||||
this.joinType = joinType;
|
||||
this.alias = alias;
|
||||
|
||||
this.pathSoFar = baseName;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SemanticPathPart resolvePathPart(
|
||||
String name,
|
||||
boolean isTerminal,
|
||||
SqmCreationState creationState) {
|
||||
pathSoFar = pathSoFar + '.' + name;
|
||||
if ( ! isTerminal ) {
|
||||
return this;
|
||||
}
|
||||
|
||||
final SqmCreationProcessingState processingState = creationState.getCurrentProcessingState();
|
||||
final SqmPathRegistry pathRegistry = processingState.getPathRegistry();
|
||||
|
||||
final EntityDomainType<?> entityType = processingState.getCreationState()
|
||||
.getCreationContext()
|
||||
.getJpaMetamodel()
|
||||
.entity( name );
|
||||
if ( entityType == null ) {
|
||||
throw new SemanticException( "Could not resolve qualified join path: " + name );
|
||||
}
|
||||
final SqmEntityJoin<?> entityJoin = new SqmEntityJoin<>( entityType, alias, joinType, sqmRoot );
|
||||
pathRegistry.register( entityJoin );
|
||||
|
||||
return entityJoin;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SqmPath resolveIndexedAccess(
|
||||
SqmExpression selector,
|
||||
boolean isTerminal,
|
||||
SqmCreationState creationState) {
|
||||
throw new SemanticException( "Illegal index-access as qualified join path" );
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -1,77 +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.hql.internal;
|
||||
|
||||
import org.hibernate.metamodel.model.domain.EntityDomainType;
|
||||
import org.hibernate.query.SemanticException;
|
||||
import org.hibernate.query.hql.spi.SemanticPathPart;
|
||||
import org.hibernate.query.hql.spi.SqmPathRegistry;
|
||||
import org.hibernate.query.sqm.produce.spi.SqmCreationContext;
|
||||
import org.hibernate.query.sqm.produce.spi.SqmCreationState;
|
||||
import org.hibernate.query.sqm.tree.domain.SqmPath;
|
||||
import org.hibernate.query.sqm.tree.expression.SqmExpression;
|
||||
import org.hibernate.query.sqm.tree.expression.SqmLiteralEntityType;
|
||||
import org.hibernate.query.sqm.tree.from.SqmFrom;
|
||||
|
||||
/**
|
||||
* @author Steve Ebersole
|
||||
*/
|
||||
public class SemanticPathPartRoot implements SemanticPathPart {
|
||||
public SemanticPathPartRoot() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public SemanticPathPart resolvePathPart(
|
||||
String name,
|
||||
boolean isTerminal,
|
||||
SqmCreationState creationState) {
|
||||
// At this point we have a "root reference"... the first path part in
|
||||
// a potential series of path parts
|
||||
|
||||
final SqmPathRegistry pathRegistry = creationState.getProcessingStateStack().getCurrent().getPathRegistry();
|
||||
final SqmCreationContext creationContext = creationState.getCreationContext();
|
||||
|
||||
// this root reference could be any of:
|
||||
// 1) a from-element alias
|
||||
// 2) an unqualified attribute name exposed from one (and only one!) from-element
|
||||
// 3) an unqualified (imported) entity name
|
||||
|
||||
// #1
|
||||
final SqmFrom aliasedFromElement = pathRegistry.findFromByAlias( name );
|
||||
if ( aliasedFromElement != null ) {
|
||||
return aliasedFromElement;
|
||||
}
|
||||
|
||||
|
||||
// #2
|
||||
final SqmFrom unqualifiedAttributeOwner = pathRegistry.findFromExposing( name );
|
||||
if ( unqualifiedAttributeOwner != null ) {
|
||||
return unqualifiedAttributeOwner.resolvePathPart( name, false, creationState );
|
||||
}
|
||||
|
||||
// #3
|
||||
final EntityDomainType<?> entityTypeByName = creationContext.getJpaMetamodel().entity( name );
|
||||
if ( entityTypeByName != null ) {
|
||||
//noinspection unchecked
|
||||
return new SqmLiteralEntityType( entityTypeByName, creationState.getCreationContext().getNodeBuilder() );
|
||||
}
|
||||
|
||||
if ( ! isTerminal ) {
|
||||
return new SemanticPathPartDelayedResolution( name );
|
||||
}
|
||||
|
||||
throw new SemanticException( "Could not resolve path root : " + name );
|
||||
}
|
||||
|
||||
@Override
|
||||
public SqmPath resolveIndexedAccess(
|
||||
SqmExpression selector,
|
||||
boolean isTerminal,
|
||||
SqmCreationState creationState) {
|
||||
throw new SemanticException( "Path cannot start with index-access" );
|
||||
}
|
||||
}
|
|
@ -1004,7 +1004,7 @@ public class SemanticQueryBuilder extends HqlParserBaseVisitor implements SqmCre
|
|||
}
|
||||
|
||||
if ( parserJoin.qualifiedJoinPredicate() != null ) {
|
||||
dotIdentifierConsumerStack.push( new QualifiedJoinPredicatePathConsumer( sqmRoot, join, this ) );
|
||||
dotIdentifierConsumerStack.push( new QualifiedJoinPredicatePathConsumer( join, this ) );
|
||||
try {
|
||||
join.setJoinPredicate( (SqmPredicate) parserJoin.qualifiedJoinPredicate().predicate().accept( this ) );
|
||||
}
|
||||
|
|
|
@ -23,7 +23,7 @@ import org.hibernate.query.internal.QueryInterpretationCacheStandardImpl;
|
|||
import org.hibernate.query.sqm.internal.SqmCriteriaNodeBuilder;
|
||||
import org.hibernate.query.hql.SemanticQueryProducer;
|
||||
import org.hibernate.query.sqm.produce.function.SqmFunctionRegistry;
|
||||
import org.hibernate.query.sqm.produce.internal.SemanticQueryProducerImpl;
|
||||
import org.hibernate.query.hql.internal.SemanticQueryProducerImpl ;
|
||||
import org.hibernate.query.sqm.produce.internal.SqmCreationOptionsStandard;
|
||||
import org.hibernate.query.sqm.produce.spi.SqmCreationContext;
|
||||
import org.hibernate.query.sqm.produce.spi.SqmCreationOptions;
|
||||
|
|
|
@ -1,65 +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.sqm.produce.internal;
|
||||
|
||||
import org.hibernate.QueryException;
|
||||
import org.hibernate.query.hql.internal.HqlParseTreeBuilder;
|
||||
import org.hibernate.query.hql.internal.HqlParseTreePrinter;
|
||||
import org.hibernate.query.hql.internal.HqlParser;
|
||||
import org.hibernate.query.hql.internal.SemanticQueryBuilder;
|
||||
import org.hibernate.query.sqm.InterpretationException;
|
||||
import org.hibernate.query.hql.SemanticQueryProducer;
|
||||
import org.hibernate.query.sqm.produce.spi.SqmCreationContext;
|
||||
import org.hibernate.query.sqm.produce.spi.SqmCreationOptions;
|
||||
import org.hibernate.query.sqm.tree.SqmStatement;
|
||||
|
||||
/**
|
||||
* Standard implementation of SemanticQueryInterpreter
|
||||
*
|
||||
* @author Steve Ebersole
|
||||
*/
|
||||
public class SemanticQueryProducerImpl implements SemanticQueryProducer {
|
||||
private final SqmCreationContext sqmCreationContext;
|
||||
private final SqmCreationOptions sqmCreationOptions;
|
||||
|
||||
public SemanticQueryProducerImpl(
|
||||
SqmCreationContext sqmCreationContext,
|
||||
SqmCreationOptions sqmCreationOptions) {
|
||||
this.sqmCreationContext = sqmCreationContext;
|
||||
this.sqmCreationOptions = sqmCreationOptions;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SqmStatement interpret(String query) {
|
||||
// final ParsingContext parsingContext = ;
|
||||
|
||||
// first, ask Antlr to build the parse tree
|
||||
final HqlParser parser = HqlParseTreeBuilder.INSTANCE.parseHql( query );
|
||||
|
||||
// Log the parse tree (if enabled)
|
||||
HqlParseTreePrinter.logStatementParseTree( parser );
|
||||
|
||||
// then we perform semantic analysis and build the semantic representation...
|
||||
try {
|
||||
final SqmStatement sqmStatement = SemanticQueryBuilder.buildSemanticModel(
|
||||
parser.statement(),
|
||||
sqmCreationOptions,
|
||||
sqmCreationContext
|
||||
);
|
||||
|
||||
SqmTreePrinter.logTree( sqmStatement );
|
||||
|
||||
return sqmStatement;
|
||||
}
|
||||
catch (QueryException e) {
|
||||
throw e;
|
||||
}
|
||||
catch (Exception e) {
|
||||
throw new InterpretationException( query, e );
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue